mirror of
https://github.com/coder/coder.git
synced 2025-07-08 11:39:50 +00:00
feat: add audit log filter for autostarted and autostopped workspace builds (#5830)
* added query * fixed query * added example to dropdown * added documentation * added test * fixed formatting * fixed format
This commit is contained in:
12
coderd/apidoc/docs.go
generated
12
coderd/apidoc/docs.go
generated
@ -5583,6 +5583,18 @@ const docTemplate = `{
|
||||
}
|
||||
]
|
||||
},
|
||||
"build_reason": {
|
||||
"enum": [
|
||||
"autostart",
|
||||
"autostop",
|
||||
"initiator"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/codersdk.BuildReason"
|
||||
}
|
||||
]
|
||||
},
|
||||
"resource_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
|
8
coderd/apidoc/swagger.json
generated
8
coderd/apidoc/swagger.json
generated
@ -4943,6 +4943,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"build_reason": {
|
||||
"enum": ["autostart", "autostop", "initiator"],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/codersdk.BuildReason"
|
||||
}
|
||||
]
|
||||
},
|
||||
"resource_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
|
@ -67,6 +67,7 @@ func (api *API) auditLogs(rw http.ResponseWriter, r *http.Request) {
|
||||
Email: filter.Email,
|
||||
DateFrom: filter.DateFrom,
|
||||
DateTo: filter.DateTo,
|
||||
BuildReason: filter.BuildReason,
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
@ -443,6 +444,7 @@ func auditSearchQuery(query string) (database.GetAuditLogsOffsetParams, []coders
|
||||
Email: parser.String(searchParams, "", "email"),
|
||||
DateFrom: parsedDateFrom,
|
||||
DateTo: parsedDateTo,
|
||||
BuildReason: buildReasonFromString(parser.String(searchParams, "", "build_reason")),
|
||||
}
|
||||
|
||||
return filter, parser.Errors
|
||||
@ -488,3 +490,16 @@ func actionFromString(actionString string) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func buildReasonFromString(buildReasonString string) string {
|
||||
switch codersdk.BuildReason(buildReasonString) {
|
||||
case codersdk.BuildReasonInitiator:
|
||||
return buildReasonString
|
||||
case codersdk.BuildReasonAutostart:
|
||||
return buildReasonString
|
||||
case codersdk.BuildReasonAutostop:
|
||||
return buildReasonString
|
||||
default:
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
@ -179,6 +179,11 @@ func TestAuditLogsFilter(t *testing.T) {
|
||||
SearchQuery: "resource_type:workspace_build action:stop",
|
||||
ExpectedResult: 1,
|
||||
},
|
||||
{
|
||||
Name: "FilterOnWorkspaceBuildStartByInitiator",
|
||||
SearchQuery: "resource_type:workspace_build action:start build_reason:start",
|
||||
ExpectedResult: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
|
@ -3680,6 +3680,12 @@ func (q *fakeQuerier) GetAuditLogsOffset(ctx context.Context, arg database.GetAu
|
||||
continue
|
||||
}
|
||||
}
|
||||
if arg.BuildReason != "" {
|
||||
workspaceBuild, err := q.GetWorkspaceBuildByID(context.Background(), alog.ResourceID)
|
||||
if err == nil && !strings.EqualFold(arg.BuildReason, string(workspaceBuild.Reason)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
user, err := q.GetUserByID(ctx, alog.UserID)
|
||||
userValid := err == nil
|
||||
|
@ -367,18 +367,41 @@ func (q *sqlQuerier) UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDP
|
||||
|
||||
const getAuditLogsOffset = `-- name: GetAuditLogsOffset :many
|
||||
SELECT
|
||||
audit_logs.id, audit_logs.time, audit_logs.user_id, audit_logs.organization_id, audit_logs.ip, audit_logs.user_agent, audit_logs.resource_type, audit_logs.resource_id, audit_logs.resource_target, audit_logs.action, audit_logs.diff, audit_logs.status_code, audit_logs.additional_fields, audit_logs.request_id, audit_logs.resource_icon,
|
||||
audit_logs.id, audit_logs.time, audit_logs.user_id, audit_logs.organization_id, audit_logs.ip, audit_logs.user_agent, audit_logs.resource_type, audit_logs.resource_id, audit_logs.resource_target, audit_logs.action, audit_logs.diff, audit_logs.status_code, audit_logs.additional_fields, audit_logs.request_id, audit_logs.resource_icon,
|
||||
users.username AS user_username,
|
||||
users.email AS user_email,
|
||||
users.created_at AS user_created_at,
|
||||
users.status AS user_status,
|
||||
users.rbac_roles AS user_roles,
|
||||
users.avatar_url AS user_avatar_url,
|
||||
COUNT(audit_logs.*) OVER() AS count
|
||||
COUNT(audit_logs.*) OVER () AS count
|
||||
FROM
|
||||
audit_logs
|
||||
LEFT JOIN
|
||||
users ON audit_logs.user_id = users.id
|
||||
audit_logs
|
||||
LEFT JOIN users ON audit_logs.user_id = users.id
|
||||
LEFT JOIN
|
||||
-- First join on workspaces to get the initial workspace create
|
||||
-- to workspace build 1 id. This is because the first create is
|
||||
-- is a different audit log than subsequent starts.
|
||||
workspaces ON
|
||||
audit_logs.resource_type = 'workspace' AND
|
||||
audit_logs.resource_id = workspaces.id
|
||||
LEFT JOIN
|
||||
workspace_builds ON
|
||||
-- Get the reason from the build if the resource type
|
||||
-- is a workspace_build
|
||||
(
|
||||
audit_logs.resource_type = 'workspace_build'
|
||||
AND audit_logs.resource_id = workspace_builds.id
|
||||
)
|
||||
OR
|
||||
-- Get the reason from the build #1 if this is the first
|
||||
-- workspace create.
|
||||
(
|
||||
audit_logs.resource_type = 'workspace' AND
|
||||
audit_logs.action = 'create' AND
|
||||
workspaces.id = workspace_builds.workspace_id AND
|
||||
workspace_builds.build_number = 1
|
||||
)
|
||||
WHERE
|
||||
-- Filter resource_type
|
||||
CASE
|
||||
@ -428,6 +451,12 @@ WHERE
|
||||
"time" <= $10
|
||||
ELSE true
|
||||
END
|
||||
-- Filter by build_reason
|
||||
AND CASE
|
||||
WHEN $11::text != '' THEN
|
||||
workspace_builds.reason::text = $11
|
||||
ELSE true
|
||||
END
|
||||
ORDER BY
|
||||
"time" DESC
|
||||
LIMIT
|
||||
@ -447,6 +476,7 @@ type GetAuditLogsOffsetParams struct {
|
||||
Email string `db:"email" json:"email"`
|
||||
DateFrom time.Time `db:"date_from" json:"date_from"`
|
||||
DateTo time.Time `db:"date_to" json:"date_to"`
|
||||
BuildReason string `db:"build_reason" json:"build_reason"`
|
||||
}
|
||||
|
||||
type GetAuditLogsOffsetRow struct {
|
||||
@ -488,6 +518,7 @@ func (q *sqlQuerier) GetAuditLogsOffset(ctx context.Context, arg GetAuditLogsOff
|
||||
arg.Email,
|
||||
arg.DateFrom,
|
||||
arg.DateTo,
|
||||
arg.BuildReason,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -2,18 +2,41 @@
|
||||
-- ID.
|
||||
-- name: GetAuditLogsOffset :many
|
||||
SELECT
|
||||
audit_logs.*,
|
||||
audit_logs.*,
|
||||
users.username AS user_username,
|
||||
users.email AS user_email,
|
||||
users.created_at AS user_created_at,
|
||||
users.status AS user_status,
|
||||
users.rbac_roles AS user_roles,
|
||||
users.avatar_url AS user_avatar_url,
|
||||
COUNT(audit_logs.*) OVER() AS count
|
||||
COUNT(audit_logs.*) OVER () AS count
|
||||
FROM
|
||||
audit_logs
|
||||
LEFT JOIN
|
||||
users ON audit_logs.user_id = users.id
|
||||
audit_logs
|
||||
LEFT JOIN users ON audit_logs.user_id = users.id
|
||||
LEFT JOIN
|
||||
-- First join on workspaces to get the initial workspace create
|
||||
-- to workspace build 1 id. This is because the first create is
|
||||
-- is a different audit log than subsequent starts.
|
||||
workspaces ON
|
||||
audit_logs.resource_type = 'workspace' AND
|
||||
audit_logs.resource_id = workspaces.id
|
||||
LEFT JOIN
|
||||
workspace_builds ON
|
||||
-- Get the reason from the build if the resource type
|
||||
-- is a workspace_build
|
||||
(
|
||||
audit_logs.resource_type = 'workspace_build'
|
||||
AND audit_logs.resource_id = workspace_builds.id
|
||||
)
|
||||
OR
|
||||
-- Get the reason from the build #1 if this is the first
|
||||
-- workspace create.
|
||||
(
|
||||
audit_logs.resource_type = 'workspace' AND
|
||||
audit_logs.action = 'create' AND
|
||||
workspaces.id = workspace_builds.workspace_id AND
|
||||
workspace_builds.build_number = 1
|
||||
)
|
||||
WHERE
|
||||
-- Filter resource_type
|
||||
CASE
|
||||
@ -63,6 +86,12 @@ WHERE
|
||||
"time" <= @date_to
|
||||
ELSE true
|
||||
END
|
||||
-- Filter by build_reason
|
||||
AND CASE
|
||||
WHEN @build_reason::text != '' THEN
|
||||
workspace_builds.reason::text = @build_reason
|
||||
ELSE true
|
||||
END
|
||||
ORDER BY
|
||||
"time" DESC
|
||||
LIMIT
|
||||
|
Reference in New Issue
Block a user