mirror of
https://github.com/coder/coder.git
synced 2025-07-13 21:36:50 +00:00
feat: differentiate new user registration from user login in the audit log (#7096)
* auditing register events * fix tests * update docs * update comments * Update coderd/audit/request.go Co-authored-by: Colin Adler <colin1adler@gmail.com> --------- Co-authored-by: Colin Adler <colin1adler@gmail.com>
This commit is contained in:
6
coderd/apidoc/docs.go
generated
6
coderd/apidoc/docs.go
generated
@ -6117,7 +6117,8 @@ const docTemplate = `{
|
||||
"start",
|
||||
"stop",
|
||||
"login",
|
||||
"logout"
|
||||
"logout",
|
||||
"register"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"AuditActionCreate",
|
||||
@ -6126,7 +6127,8 @@ const docTemplate = `{
|
||||
"AuditActionStart",
|
||||
"AuditActionStop",
|
||||
"AuditActionLogin",
|
||||
"AuditActionLogout"
|
||||
"AuditActionLogout",
|
||||
"AuditActionRegister"
|
||||
]
|
||||
},
|
||||
"codersdk.AuditDiff": {
|
||||
|
14
coderd/apidoc/swagger.json
generated
14
coderd/apidoc/swagger.json
generated
@ -5433,7 +5433,16 @@
|
||||
},
|
||||
"codersdk.AuditAction": {
|
||||
"type": "string",
|
||||
"enum": ["create", "write", "delete", "start", "stop", "login", "logout"],
|
||||
"enum": [
|
||||
"create",
|
||||
"write",
|
||||
"delete",
|
||||
"start",
|
||||
"stop",
|
||||
"login",
|
||||
"logout",
|
||||
"register"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"AuditActionCreate",
|
||||
"AuditActionWrite",
|
||||
@ -5441,7 +5450,8 @@
|
||||
"AuditActionStart",
|
||||
"AuditActionStop",
|
||||
"AuditActionLogin",
|
||||
"AuditActionLogout"
|
||||
"AuditActionLogout",
|
||||
"AuditActionRegister"
|
||||
]
|
||||
},
|
||||
"codersdk.AuditDiff": {
|
||||
|
@ -247,9 +247,9 @@ func auditLogDescription(alog database.GetAuditLogsOffsetRow) string {
|
||||
)
|
||||
|
||||
// API Key resources (used for authentication) do not have targets and follow the below format:
|
||||
// "User {logged in | logged out}"
|
||||
// "User {logged in | logged out | registered}"
|
||||
if alog.ResourceType == database.ResourceTypeApiKey &&
|
||||
(alog.Action == database.AuditActionLogin || alog.Action == database.AuditActionLogout) {
|
||||
(alog.Action == database.AuditActionLogin || alog.Action == database.AuditActionLogout || alog.Action == database.AuditActionRegister) {
|
||||
return str
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,10 @@ type Request[T Auditable] struct {
|
||||
// This optional field can be passed in when the userID cannot be determined from the API Key
|
||||
// such as in the case of login, when the audit log is created prior the API Key's existence.
|
||||
UserID uuid.UUID
|
||||
|
||||
// This optional field can be passed in if the AuditAction must be overridden
|
||||
// such as in the case of new user authentication when the Audit Action is 'register', not 'login'.
|
||||
Action database.AuditAction
|
||||
}
|
||||
|
||||
type BuildAuditParams[T Auditable] struct {
|
||||
@ -198,6 +202,11 @@ func InitRequest[T Auditable](w http.ResponseWriter, p *RequestParams) (*Request
|
||||
return
|
||||
}
|
||||
|
||||
action := p.Action
|
||||
if req.Action != "" {
|
||||
action = req.Action
|
||||
}
|
||||
|
||||
ip := parseIP(p.Request.RemoteAddr)
|
||||
auditLog := database.AuditLog{
|
||||
ID: uuid.New(),
|
||||
@ -208,7 +217,7 @@ func InitRequest[T Auditable](w http.ResponseWriter, p *RequestParams) (*Request
|
||||
ResourceType: either(req.Old, req.New, ResourceType[T], req.params.Action),
|
||||
ResourceID: either(req.Old, req.New, ResourceID[T], req.params.Action),
|
||||
ResourceTarget: either(req.Old, req.New, ResourceTarget[T], req.params.Action),
|
||||
Action: p.Action,
|
||||
Action: action,
|
||||
Diff: diffRaw,
|
||||
StatusCode: int32(sw.Status),
|
||||
RequestID: httpmw.RequestID(p.Request),
|
||||
|
3
coderd/database/dump.sql
generated
3
coderd/database/dump.sql
generated
@ -18,7 +18,8 @@ CREATE TYPE audit_action AS ENUM (
|
||||
'start',
|
||||
'stop',
|
||||
'login',
|
||||
'logout'
|
||||
'logout',
|
||||
'register'
|
||||
);
|
||||
|
||||
CREATE TYPE build_reason AS ENUM (
|
||||
|
@ -0,0 +1,2 @@
|
||||
-- It's not possible to drop enum values from enum types, so the UP has "IF NOT
|
||||
-- EXISTS".
|
@ -0,0 +1,2 @@
|
||||
ALTER TYPE audit_action
|
||||
ADD VALUE IF NOT EXISTS 'register';
|
@ -139,13 +139,14 @@ func AllAppSharingLevelValues() []AppSharingLevel {
|
||||
type AuditAction string
|
||||
|
||||
const (
|
||||
AuditActionCreate AuditAction = "create"
|
||||
AuditActionWrite AuditAction = "write"
|
||||
AuditActionDelete AuditAction = "delete"
|
||||
AuditActionStart AuditAction = "start"
|
||||
AuditActionStop AuditAction = "stop"
|
||||
AuditActionLogin AuditAction = "login"
|
||||
AuditActionLogout AuditAction = "logout"
|
||||
AuditActionCreate AuditAction = "create"
|
||||
AuditActionWrite AuditAction = "write"
|
||||
AuditActionDelete AuditAction = "delete"
|
||||
AuditActionStart AuditAction = "start"
|
||||
AuditActionStop AuditAction = "stop"
|
||||
AuditActionLogin AuditAction = "login"
|
||||
AuditActionLogout AuditAction = "logout"
|
||||
AuditActionRegister AuditAction = "register"
|
||||
)
|
||||
|
||||
func (e *AuditAction) Scan(src interface{}) error {
|
||||
@ -191,7 +192,8 @@ func (e AuditAction) Valid() bool {
|
||||
AuditActionStart,
|
||||
AuditActionStop,
|
||||
AuditActionLogin,
|
||||
AuditActionLogout:
|
||||
AuditActionLogout,
|
||||
AuditActionRegister:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -206,6 +208,7 @@ func AllAuditActionValues() []AuditAction {
|
||||
AuditActionStop,
|
||||
AuditActionLogin,
|
||||
AuditActionLogout,
|
||||
AuditActionRegister,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,6 +395,12 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// If a new user is authenticating for the first time
|
||||
// the audit action is 'register', not 'login'
|
||||
if user.ID == uuid.Nil {
|
||||
aReq.Action = database.AuditActionRegister
|
||||
}
|
||||
|
||||
cookie, key, err := api.oauthLogin(r, oauthLoginParams{
|
||||
User: user,
|
||||
Link: link,
|
||||
@ -712,6 +718,12 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// If a new user is authenticating for the first time
|
||||
// the audit action is 'register', not 'login'
|
||||
if user.ID == uuid.Nil {
|
||||
aReq.Action = database.AuditActionRegister
|
||||
}
|
||||
|
||||
cookie, key, err := api.oauthLogin(r, oauthLoginParams{
|
||||
User: user,
|
||||
Link: link,
|
||||
|
@ -261,7 +261,7 @@ func TestUserOAuth2Github(t *testing.T) {
|
||||
|
||||
require.Len(t, auditor.AuditLogs(), numLogs)
|
||||
require.NotEqual(t, auditor.AuditLogs()[numLogs-1].UserID, uuid.Nil)
|
||||
require.Equal(t, database.AuditActionLogin, auditor.AuditLogs()[numLogs-1].Action)
|
||||
require.Equal(t, database.AuditActionRegister, auditor.AuditLogs()[numLogs-1].Action)
|
||||
})
|
||||
t.Run("SignupAllowedTeam", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
@ -305,7 +305,7 @@ func TestUserOAuth2Github(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode)
|
||||
require.Len(t, auditor.AuditLogs(), numLogs)
|
||||
require.Equal(t, database.AuditActionLogin, auditor.AuditLogs()[numLogs-1].Action)
|
||||
require.Equal(t, database.AuditActionRegister, auditor.AuditLogs()[numLogs-1].Action)
|
||||
})
|
||||
t.Run("SignupAllowedTeamInFirstOrganization", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
@ -357,7 +357,7 @@ func TestUserOAuth2Github(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode)
|
||||
require.Len(t, auditor.AuditLogs(), numLogs)
|
||||
require.Equal(t, database.AuditActionLogin, auditor.AuditLogs()[numLogs-1].Action)
|
||||
require.Equal(t, database.AuditActionRegister, auditor.AuditLogs()[numLogs-1].Action)
|
||||
})
|
||||
t.Run("SignupAllowedTeamInSecondOrganization", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
@ -409,7 +409,7 @@ func TestUserOAuth2Github(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode)
|
||||
require.Len(t, auditor.AuditLogs(), numLogs)
|
||||
require.Equal(t, database.AuditActionLogin, auditor.AuditLogs()[numLogs-1].Action)
|
||||
require.Equal(t, database.AuditActionRegister, auditor.AuditLogs()[numLogs-1].Action)
|
||||
})
|
||||
t.Run("SignupAllowEveryone", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
@ -447,7 +447,7 @@ func TestUserOAuth2Github(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode)
|
||||
require.Len(t, auditor.AuditLogs(), numLogs)
|
||||
require.Equal(t, database.AuditActionLogin, auditor.AuditLogs()[numLogs-1].Action)
|
||||
require.Equal(t, database.AuditActionRegister, auditor.AuditLogs()[numLogs-1].Action)
|
||||
})
|
||||
t.Run("SignupFailedInactiveInOrg", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
@ -721,7 +721,7 @@ func TestUserOIDC(t *testing.T) {
|
||||
|
||||
require.Len(t, auditor.AuditLogs(), numLogs)
|
||||
require.NotEqual(t, auditor.AuditLogs()[numLogs-1].UserID, uuid.Nil)
|
||||
require.Equal(t, database.AuditActionLogin, auditor.AuditLogs()[numLogs-1].Action)
|
||||
require.Equal(t, database.AuditActionRegister, auditor.AuditLogs()[numLogs-1].Action)
|
||||
}
|
||||
|
||||
if tc.AvatarURL != "" {
|
||||
@ -731,7 +731,7 @@ func TestUserOIDC(t *testing.T) {
|
||||
require.Equal(t, tc.AvatarURL, user.AvatarURL)
|
||||
|
||||
require.Len(t, auditor.AuditLogs(), numLogs)
|
||||
require.Equal(t, database.AuditActionLogin, auditor.AuditLogs()[numLogs-1].Action)
|
||||
require.Equal(t, database.AuditActionRegister, auditor.AuditLogs()[numLogs-1].Action)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -782,7 +782,7 @@ func TestUserOIDC(t *testing.T) {
|
||||
require.True(t, strings.HasPrefix(user.Username, "jon-"), "username %q should have prefix %q", user.Username, "jon-")
|
||||
|
||||
require.Len(t, auditor.AuditLogs(), numLogs)
|
||||
require.Equal(t, database.AuditActionLogin, auditor.AuditLogs()[numLogs-1].Action)
|
||||
require.Equal(t, database.AuditActionRegister, auditor.AuditLogs()[numLogs-1].Action)
|
||||
})
|
||||
|
||||
t.Run("Disabled", func(t *testing.T) {
|
||||
|
Reference in New Issue
Block a user