Skip to content

Commit d210298

Browse files
authored
refactor: consolidate audit logging into internal/audit provider (#541)
* refactor: consolidate audit logging into internal/audit provider Replace two duplicate audit log helpers (graphql/audit_log_helper.go and http_handlers/audit_log_helper.go) with a single audit.Provider in internal/audit/ following the project's standard provider pattern. - Create internal/audit/provider.go with Provider interface + LogEvent - Inject AuditProvider into graphql.Dependencies and http_handlers.Dependencies - Initialize audit provider in cmd/root.go - Update all 32 call sites across 29 files to use audit.Event - Delete the two old helper files - Callers now extract IP/UserAgent and pass in Event struct directly * chore: fix test
1 parent b8dbe62 commit d210298

38 files changed

Lines changed: 256 additions & 130 deletions

cmd/root.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/spf13/cobra"
1212
"golang.org/x/sync/errgroup"
1313

14+
"github.com/authorizerdev/authorizer/internal/audit"
1415
"github.com/authorizerdev/authorizer/internal/authenticators"
1516
"github.com/authorizerdev/authorizer/internal/config"
1617
"github.com/authorizerdev/authorizer/internal/constants"
@@ -397,8 +398,14 @@ func runRoot(c *cobra.Command, args []string) {
397398
log.Fatal().Msg("client secret missing in rootArgs")
398399
}
399400

401+
auditProvider := audit.New(&audit.Dependencies{
402+
Log: &log,
403+
StorageProvider: storageProvider,
404+
})
405+
400406
httpProvider, err := http_handlers.New(&rootArgs.config, &http_handlers.Dependencies{
401407
Log: &log,
408+
AuditProvider: auditProvider,
402409
AuthenticatorProvider: authenticatorProvider,
403410
EmailProvider: emailProvider,
404411
EventsProvider: eventsProvider,

internal/audit/provider.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package audit
2+
3+
import (
4+
"context"
5+
6+
"github.com/rs/zerolog"
7+
8+
"github.com/authorizerdev/authorizer/internal/storage"
9+
"github.com/authorizerdev/authorizer/internal/storage/schemas"
10+
)
11+
12+
// Dependencies for the audit provider.
13+
type Dependencies struct {
14+
Log *zerolog.Logger
15+
StorageProvider storage.Provider
16+
}
17+
18+
// Event represents an audit event to be logged.
19+
type Event struct {
20+
ActorID string
21+
ActorType string
22+
ActorEmail string
23+
Action string
24+
ResourceType string
25+
ResourceID string
26+
IPAddress string
27+
UserAgent string
28+
Metadata string
29+
}
30+
31+
// Provider is the interface for audit logging.
32+
type Provider interface {
33+
// LogEvent asynchronously records an audit log entry.
34+
// It is fire-and-forget: errors are logged but not propagated.
35+
LogEvent(event Event)
36+
}
37+
38+
type provider struct {
39+
deps *Dependencies
40+
}
41+
42+
// Ensure provider implements Provider.
43+
var _ Provider = &provider{}
44+
45+
// New creates a new audit provider.
46+
func New(deps *Dependencies) Provider {
47+
return &provider{deps: deps}
48+
}
49+
50+
// LogEvent asynchronously records an audit log entry.
51+
func (p *provider) LogEvent(event Event) {
52+
go func() {
53+
log := p.deps.Log.With().Str("func", "LogEvent").Logger()
54+
auditLog := &schemas.AuditLog{
55+
ActorID: event.ActorID,
56+
ActorType: event.ActorType,
57+
ActorEmail: event.ActorEmail,
58+
Action: event.Action,
59+
ResourceType: event.ResourceType,
60+
ResourceID: event.ResourceID,
61+
IPAddress: event.IPAddress,
62+
UserAgent: event.UserAgent,
63+
Metadata: event.Metadata,
64+
}
65+
if err := p.deps.StorageProvider.AddAuditLog(context.Background(), auditLog); err != nil {
66+
log.Debug().Err(err).Str("action", event.Action).Msg("Failed to add audit log")
67+
}
68+
}()
69+
}

internal/graphql/add_email_template.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"strings"
77

8+
"github.com/authorizerdev/authorizer/internal/audit"
89
"github.com/authorizerdev/authorizer/internal/constants"
910
"github.com/authorizerdev/authorizer/internal/graph/model"
1011
"github.com/authorizerdev/authorizer/internal/refs"
@@ -59,10 +60,13 @@ func (g *graphqlProvider) AddEmailTemplate(ctx context.Context, params *model.Ad
5960
return nil, err
6061
}
6162

62-
g.logAuditEvent(ctx, constants.AuditAdminEmailTemplateCreatedEvent, AuditLogOpts{
63+
g.AuditProvider.LogEvent(audit.Event{
64+
Action: constants.AuditAdminEmailTemplateCreatedEvent,
6365
ActorType: constants.AuditActorTypeAdmin,
6466
ResourceType: constants.AuditResourceTypeEmailTemplate,
6567
ResourceID: emailTemplate.ID,
68+
IPAddress: utils.GetIP(gc.Request),
69+
UserAgent: utils.GetUserAgent(gc.Request),
6670
})
6771
return &model.Response{
6872
Message: `Email template added successfully`,

internal/graphql/add_webhook.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"strings"
88

9+
"github.com/authorizerdev/authorizer/internal/audit"
910
"github.com/authorizerdev/authorizer/internal/constants"
1011
"github.com/authorizerdev/authorizer/internal/graph/model"
1112
"github.com/authorizerdev/authorizer/internal/refs"
@@ -55,10 +56,13 @@ func (g *graphqlProvider) AddWebhook(ctx context.Context, params *model.AddWebho
5556
return nil, err
5657
}
5758

58-
g.logAuditEvent(ctx, constants.AuditAdminWebhookCreatedEvent, AuditLogOpts{
59+
g.AuditProvider.LogEvent(audit.Event{
60+
Action: constants.AuditAdminWebhookCreatedEvent,
5961
ActorType: constants.AuditActorTypeAdmin,
6062
ResourceType: constants.AuditResourceTypeWebhook,
6163
ResourceID: webhook.ID,
64+
IPAddress: utils.GetIP(gc.Request),
65+
UserAgent: utils.GetUserAgent(gc.Request),
6266
})
6367
return &model.Response{
6468
Message: `Webhook added successfully`,

internal/graphql/admin_login.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66

7+
"github.com/authorizerdev/authorizer/internal/audit"
78
"github.com/authorizerdev/authorizer/internal/constants"
89
"github.com/authorizerdev/authorizer/internal/cookie"
910
"github.com/authorizerdev/authorizer/internal/crypto"
@@ -23,9 +24,12 @@ func (g *graphqlProvider) AdminLogin(ctx context.Context, params *model.AdminLog
2324
}
2425
if params.AdminSecret != g.Config.AdminSecret {
2526
log.Debug().Msg("Invalid admin secret")
26-
g.logAuditEvent(ctx, constants.AuditAdminLoginFailedEvent, AuditLogOpts{
27+
g.AuditProvider.LogEvent(audit.Event{
28+
Action: constants.AuditAdminLoginFailedEvent,
2729
ActorType: constants.AuditActorTypeAdmin,
2830
ResourceType: constants.AuditResourceTypeAdminSession,
31+
IPAddress: utils.GetIP(gc.Request),
32+
UserAgent: utils.GetUserAgent(gc.Request),
2933
})
3034
return res, fmt.Errorf(`invalid admin secret`)
3135
}
@@ -36,9 +40,12 @@ func (g *graphqlProvider) AdminLogin(ctx context.Context, params *model.AdminLog
3640
}
3741
cookie.SetAdminCookie(gc, hashedKey, g.Config.AdminCookieSecure)
3842

39-
g.logAuditEvent(ctx, constants.AuditAdminLoginSuccessEvent, AuditLogOpts{
43+
g.AuditProvider.LogEvent(audit.Event{
44+
Action: constants.AuditAdminLoginSuccessEvent,
4045
ActorType: constants.AuditActorTypeAdmin,
4146
ResourceType: constants.AuditResourceTypeAdminSession,
47+
IPAddress: utils.GetIP(gc.Request),
48+
UserAgent: utils.GetUserAgent(gc.Request),
4249
})
4350
res = &model.Response{
4451
Message: "admin logged in successfully",

internal/graphql/admin_logout.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66

7+
"github.com/authorizerdev/authorizer/internal/audit"
78
"github.com/authorizerdev/authorizer/internal/constants"
89
"github.com/authorizerdev/authorizer/internal/cookie"
910
"github.com/authorizerdev/authorizer/internal/graph/model"
@@ -25,9 +26,12 @@ func (g *graphqlProvider) AdminLogout(ctx context.Context) (*model.Response, err
2526
}
2627

2728
cookie.DeleteAdminCookie(gc, g.Config.AdminCookieSecure)
28-
g.logAuditEvent(ctx, constants.AuditAdminLogoutEvent, AuditLogOpts{
29+
g.AuditProvider.LogEvent(audit.Event{
30+
Action: constants.AuditAdminLogoutEvent,
2931
ActorType: constants.AuditActorTypeAdmin,
3032
ResourceType: constants.AuditResourceTypeAdminSession,
33+
IPAddress: utils.GetIP(gc.Request),
34+
UserAgent: utils.GetUserAgent(gc.Request),
3135
})
3236

3337
res := &model.Response{

internal/graphql/audit_log_helper.go

Lines changed: 0 additions & 50 deletions
This file was deleted.

internal/graphql/deactivate_account.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"time"
66

7+
"github.com/authorizerdev/authorizer/internal/audit"
78
"github.com/authorizerdev/authorizer/internal/constants"
89
"github.com/authorizerdev/authorizer/internal/graph/model"
910
"github.com/authorizerdev/authorizer/internal/refs"
@@ -43,12 +44,15 @@ func (g *graphqlProvider) DeactivateAccount(ctx context.Context) (*model.Respons
4344
g.MemoryStoreProvider.DeleteAllUserSessions(user.ID)
4445
g.EventsProvider.RegisterEvent(ctx, constants.UserDeactivatedWebhookEvent, "", user)
4546
}()
46-
g.logAuditEvent(ctx, constants.AuditUserDeactivatedEvent, AuditLogOpts{
47+
g.AuditProvider.LogEvent(audit.Event{
48+
Action: constants.AuditUserDeactivatedEvent,
4749
ActorID: user.ID,
4850
ActorType: constants.AuditActorTypeUser,
4951
ActorEmail: refs.StringValue(user.Email),
5052
ResourceType: constants.AuditResourceTypeUser,
5153
ResourceID: user.ID,
54+
IPAddress: utils.GetIP(gc.Request),
55+
UserAgent: utils.GetUserAgent(gc.Request),
5256
})
5357
res = &model.Response{
5458
Message: `user account deactivated successfully`,

internal/graphql/delete_email_template.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"strings"
77

8+
"github.com/authorizerdev/authorizer/internal/audit"
89
"github.com/authorizerdev/authorizer/internal/constants"
910
"github.com/authorizerdev/authorizer/internal/graph/model"
1011
"github.com/authorizerdev/authorizer/internal/utils"
@@ -43,10 +44,13 @@ func (g *graphqlProvider) DeleteEmailTemplate(ctx context.Context, params *model
4344
return nil, err
4445
}
4546

46-
g.logAuditEvent(ctx, constants.AuditAdminEmailTemplateDeletedEvent, AuditLogOpts{
47+
g.AuditProvider.LogEvent(audit.Event{
48+
Action: constants.AuditAdminEmailTemplateDeletedEvent,
4749
ActorType: constants.AuditActorTypeAdmin,
4850
ResourceType: constants.AuditResourceTypeEmailTemplate,
4951
ResourceID: params.ID,
52+
IPAddress: utils.GetIP(gc.Request),
53+
UserAgent: utils.GetUserAgent(gc.Request),
5054
})
5155
return &model.Response{
5256
Message: "Email templated deleted successfully",

internal/graphql/delete_user.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66

7+
"github.com/authorizerdev/authorizer/internal/audit"
78
"github.com/authorizerdev/authorizer/internal/constants"
89
"github.com/authorizerdev/authorizer/internal/graph/model"
910
"github.com/authorizerdev/authorizer/internal/refs"
@@ -89,10 +90,13 @@ func (g *graphqlProvider) DeleteUser(ctx context.Context, params *model.DeleteUs
8990
g.MemoryStoreProvider.DeleteAllUserSessions(user.ID)
9091
g.EventsProvider.RegisterEvent(ctx, constants.UserDeletedWebhookEvent, "", user)
9192
}()
92-
g.logAuditEvent(ctx, constants.AuditAdminUserDeletedEvent, AuditLogOpts{
93+
g.AuditProvider.LogEvent(audit.Event{
94+
Action: constants.AuditAdminUserDeletedEvent,
9395
ActorType: constants.AuditActorTypeAdmin,
9496
ResourceType: constants.AuditResourceTypeUser,
9597
ResourceID: user.ID,
98+
IPAddress: utils.GetIP(gc.Request),
99+
UserAgent: utils.GetUserAgent(gc.Request),
96100
})
97101

98102
return res, nil

0 commit comments

Comments
 (0)