Skip to content

Commit 0d65143

Browse files
authored
chore: implement audit log for custom role edits (coder#13494)
* chore: implement audit log for custom role edits
1 parent 056a697 commit 0d65143

File tree

21 files changed

+122
-16
lines changed

21 files changed

+122
-16
lines changed

coderd/apidoc/docs.go

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/audit/diff.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ type Auditable interface {
2121
database.AuditOAuthConvertState |
2222
database.HealthSettings |
2323
database.OAuth2ProviderApp |
24-
database.OAuth2ProviderAppSecret
24+
database.OAuth2ProviderAppSecret |
25+
database.CustomRole
2526
}
2627

2728
// Map is a map of changed fields in an audited resource. It maps field names to

coderd/audit/request.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ func ResourceTarget[T Auditable](tgt T) string {
103103
return typed.Name
104104
case database.OAuth2ProviderAppSecret:
105105
return typed.DisplaySecret
106+
case database.CustomRole:
107+
return typed.Name
106108
default:
107109
panic(fmt.Sprintf("unknown resource %T for ResourceTarget", tgt))
108110
}
@@ -140,6 +142,8 @@ func ResourceID[T Auditable](tgt T) uuid.UUID {
140142
return typed.ID
141143
case database.OAuth2ProviderAppSecret:
142144
return typed.ID
145+
case database.CustomRole:
146+
return typed.ID
143147
default:
144148
panic(fmt.Sprintf("unknown resource %T for ResourceID", tgt))
145149
}
@@ -175,6 +179,8 @@ func ResourceType[T Auditable](tgt T) database.ResourceType {
175179
return database.ResourceTypeOauth2ProviderApp
176180
case database.OAuth2ProviderAppSecret:
177181
return database.ResourceTypeOauth2ProviderAppSecret
182+
case database.CustomRole:
183+
return database.ResourceTypeCustomRole
178184
default:
179185
panic(fmt.Sprintf("unknown resource %T for ResourceType", typed))
180186
}
@@ -211,6 +217,8 @@ func ResourceRequiresOrgID[T Auditable]() bool {
211217
return false
212218
case database.OAuth2ProviderAppSecret:
213219
return false
220+
case database.CustomRole:
221+
return true
214222
default:
215223
panic(fmt.Sprintf("unknown resource %T for ResourceRequiresOrgID", tgt))
216224
}

coderd/coderdtest/coderdtest.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,8 @@ func createAnotherUserRetry(t testing.TB, client *codersdk.Client, organizationI
758758
roleName, _, err = rbac.RoleSplit(roleName)
759759
require.NoError(t, err, "split org role name")
760760
if ok {
761+
roleName, _, err = rbac.RoleSplit(roleName)
762+
require.NoError(t, err, "split rolename")
761763
orgRoles[orgID] = append(orgRoles[orgID], roleName)
762764
} else {
763765
siteRoles = append(siteRoles, roleName)

coderd/database/dbauthz/customroles_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ func TestUpsertCustomRoles(t *testing.T) {
244244
} else {
245245
require.NoError(t, err)
246246

247-
// Verify we can fetch the role
247+
// Verify the role is fetched with the lookup filter.
248248
roles, err := az.CustomRoles(ctx, database.CustomRolesParams{
249249
LookupRoles: []database.NameOrganizationPair{
250250
{

coderd/database/dbmem/dbmem.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8415,6 +8415,7 @@ func (q *FakeQuerier) UpsertCustomRole(_ context.Context, arg database.UpsertCus
84158415
}
84168416

84178417
role := database.CustomRole{
8418+
ID: uuid.New(),
84188419
Name: arg.Name,
84198420
DisplayName: arg.DisplayName,
84208421
OrganizationID: arg.OrganizationID,

coderd/database/dump.sql

Lines changed: 8 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DROP INDEX idx_custom_roles_id;
2+
ALTER TABLE custom_roles DROP COLUMN id;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-- (name) is the primary key, this column is almost exclusively for auditing.
2+
-- Audit logs require a uuid as the unique identifier for a resource.
3+
ALTER TABLE custom_roles ADD COLUMN id uuid DEFAULT gen_random_uuid() NOT NULL;
4+
COMMENT ON COLUMN custom_roles.id IS 'Custom roles ID is used purely for auditing purposes. Name is a better unique identifier.';
5+
6+
-- Ensure unique uuids.
7+
CREATE INDEX idx_custom_roles_id ON custom_roles (id);
8+
ALTER TYPE resource_type ADD VALUE IF NOT EXISTS 'custom_role';

coderd/database/models.go

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/roles.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ import (
2020
// roles. Ideally only included in the enterprise package, but the routes are
2121
// intermixed with AGPL endpoints.
2222
type CustomRoleHandler interface {
23-
PatchOrganizationRole(ctx context.Context, db database.Store, rw http.ResponseWriter, orgID uuid.UUID, role codersdk.Role) (codersdk.Role, bool)
23+
PatchOrganizationRole(ctx context.Context, rw http.ResponseWriter, r *http.Request, orgID uuid.UUID, role codersdk.Role) (codersdk.Role, bool)
2424
}
2525

2626
type agplCustomRoleHandler struct{}
2727

28-
func (agplCustomRoleHandler) PatchOrganizationRole(ctx context.Context, _ database.Store, rw http.ResponseWriter, _ uuid.UUID, _ codersdk.Role) (codersdk.Role, bool) {
28+
func (agplCustomRoleHandler) PatchOrganizationRole(ctx context.Context, rw http.ResponseWriter, _ *http.Request, _ uuid.UUID, _ codersdk.Role) (codersdk.Role, bool) {
2929
httpapi.Write(ctx, rw, http.StatusForbidden, codersdk.Response{
3030
Message: "Creating and updating custom roles is an Enterprise feature. Contact sales!",
3131
})
@@ -54,7 +54,7 @@ func (api *API) patchOrgRoles(rw http.ResponseWriter, r *http.Request) {
5454
return
5555
}
5656

57-
updated, ok := handler.PatchOrganizationRole(ctx, api.Database, rw, organization.ID, req)
57+
updated, ok := handler.PatchOrganizationRole(ctx, rw, r, organization.ID, req)
5858
if !ok {
5959
return
6060
}

codersdk/audit.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const (
3030
ResourceTypeOAuth2ProviderApp ResourceType = "oauth2_provider_app"
3131
// nolint:gosec // This is not a secret.
3232
ResourceTypeOAuth2ProviderAppSecret ResourceType = "oauth2_provider_app_secret"
33+
ResourceTypeCustomRole ResourceType = "custom_role"
3334
)
3435

3536
func (r ResourceType) FriendlyString() string {
@@ -66,6 +67,8 @@ func (r ResourceType) FriendlyString() string {
6667
return "oauth2 app"
6768
case ResourceTypeOAuth2ProviderAppSecret:
6869
return "oauth2 app secret"
70+
case ResourceTypeCustomRole:
71+
return "custom role"
6972
default:
7073
return "unknown"
7174
}

0 commit comments

Comments
 (0)