-
Notifications
You must be signed in to change notification settings - Fork 28
/
audit.go
157 lines (130 loc) · 4.44 KB
/
audit.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package service
import (
"context"
"fmt"
"net/http"
"github.com/authgear/authgear-server/pkg/api/apierrors"
"github.com/authgear/authgear-server/pkg/api/event"
"github.com/authgear/authgear-server/pkg/lib/config"
libevent "github.com/authgear/authgear-server/pkg/lib/event"
"github.com/authgear/authgear-server/pkg/lib/infra/db/auditdb"
"github.com/authgear/authgear-server/pkg/lib/infra/db/globaldb"
"github.com/authgear/authgear-server/pkg/lib/uiparam"
portalconfig "github.com/authgear/authgear-server/pkg/portal/config"
"github.com/authgear/authgear-server/pkg/portal/model"
portalsession "github.com/authgear/authgear-server/pkg/portal/session"
"github.com/authgear/authgear-server/pkg/util/clock"
"github.com/authgear/authgear-server/pkg/util/httputil"
"github.com/authgear/authgear-server/pkg/util/intl"
"github.com/authgear/authgear-server/pkg/util/log"
"github.com/authgear/authgear-server/pkg/util/sentry"
)
type AuditServiceAppService interface {
Get(id string) (*model.App, error)
}
type AuditService struct {
Context context.Context
RemoteIP httputil.RemoteIP
UserAgentString httputil.UserAgentString
Request *http.Request
Apps AuditServiceAppService
Authgear *portalconfig.AuthgearConfig
DenoEndpoint config.DenoEndpoint
SQLBuilder *globaldb.SQLBuilder
SQLExecutor *globaldb.SQLExecutor
AuditDatabase *auditdb.WriteHandle
Clock clock.Clock
LoggerFactory *log.Factory
}
func (s *AuditService) Log(app *model.App, payload event.NonBlockingPayload) (err error) {
if s.AuditDatabase == nil {
return
}
authgearApp, err := s.Apps.Get(s.Authgear.AppID)
if err != nil {
return
}
cfg := app.Context.Config
loggerFactory := s.LoggerFactory.ReplaceHooks(
apierrors.SkipLoggingHook{},
log.NewDefaultMaskLogHook(),
config.NewSecretMaskLogHook(cfg.SecretConfig),
sentry.NewLogHookFromContext(s.Context),
)
loggerFactory.DefaultFields["app"] = cfg.AppConfig.ID
// AuditSink is app specific.
// The records MUST have correct app_id.
// We have construct audit sink with the target app.
auditSink := newAuditSink(s.Context, app, s.AuditDatabase, loggerFactory)
// The portal uses its Authgear to deliver hooks.
// We have construct hook sink with the Authgear app.
hookSink := newHookSink(s.Context, authgearApp, s.DenoEndpoint, loggerFactory)
// Use the target app ID.
e, err := s.resolveNonBlockingEvent(app.ID, payload)
if err != nil {
return err
}
err = auditSink.ReceiveNonBlockingEvent(e)
if err != nil {
return err
}
err = hookSink.ReceiveNonBlockingEvent(e)
if err != nil {
return err
}
return nil
}
func (s *AuditService) nextSeq() (seq int64, err error) {
builder := s.SQLBuilder.
Select(fmt.Sprintf("nextval('%s')", s.SQLBuilder.TableName("_auth_event_sequence")))
row, err := s.SQLExecutor.QueryRowWith(builder)
if err != nil {
return
}
err = row.Scan(&seq)
return
}
func (s *AuditService) makeContext(appID string, payload event.Payload) event.Context {
var userIDStr string
portalSession := portalsession.GetValidSessionInfo(s.Context)
if portalSession != nil {
userIDStr = portalSession.UserID
}
preferredLanguageTags := intl.GetPreferredLanguageTags(s.Context)
// Initialize this to an empty slice so that it is always present in the JSON.
if preferredLanguageTags == nil {
preferredLanguageTags = []string{}
}
triggeredBy := payload.GetTriggeredBy()
uiParam := uiparam.GetUIParam(s.Context)
clientID := uiParam.ClientID
// This audit context must be constructed here.
// We cannot use GetAdminAuthzAudit because that is for Admin API to audit context.
auditCtx := PortalAdminAPIAuthContext{
Usage: UsageInternal,
ActorUserID: userIDStr,
HTTPReferer: s.Request.Header.Get("Referer"),
}
ctx := &event.Context{
Timestamp: s.Clock.NowUTC().Unix(),
// We do not populate UserID because the event is not about UserID.
TriggeredBy: triggeredBy,
AuditContext: auditCtx,
PreferredLanguages: preferredLanguageTags,
Language: "",
IPAddress: string(s.RemoteIP),
UserAgent: string(s.UserAgentString),
ClientID: clientID,
AppID: appID,
}
payload.FillContext(ctx)
return *ctx
}
func (s *AuditService) resolveNonBlockingEvent(appID string, payload event.NonBlockingPayload) (*event.Event, error) {
eventContext := s.makeContext(appID, payload)
seq, err := s.nextSeq()
if err != nil {
return nil, err
}
return libevent.NewNonBlockingEvent(seq, payload, eventContext), nil
}