From 5b96e502ac6d9f2d0a2697d862be828d431c0a83 Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Wed, 30 Oct 2024 17:20:39 +0000 Subject: [PATCH 1/5] chore: begin impl of AsNotifier with ResourceNotificationMessage --- coderd/apidoc/docs.go | 2 ++ coderd/apidoc/swagger.json | 2 ++ coderd/database/dbauthz/dbauthz.go | 28 +++++++++++++++-- coderd/notifications/notifications_test.go | 36 ++++++++++++---------- coderd/rbac/object_gen.go | 10 ++++++ coderd/rbac/policy/policy.go | 7 +++++ coderd/rbac/roles_test.go | 15 +++++++++ coderd/templates.go | 4 +-- coderd/userauth.go | 4 +-- codersdk/rbacresources_gen.go | 2 ++ docs/reference/api/members.md | 5 +++ docs/reference/api/schemas.md | 1 + site/src/api/rbacresourcesGenerated.ts | 5 +++ site/src/api/typesGenerated.ts | 4 +-- 14 files changed, 99 insertions(+), 26 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 83d1fdc2c492a..750011cbbd1e1 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -12265,6 +12265,7 @@ const docTemplate = `{ "group_member", "idpsync_settings", "license", + "notification_message", "notification_preference", "notification_template", "oauth2_app", @@ -12298,6 +12299,7 @@ const docTemplate = `{ "ResourceGroupMember", "ResourceIdpsyncSettings", "ResourceLicense", + "ResourceNotificationMessage", "ResourceNotificationPreference", "ResourceNotificationTemplate", "ResourceOauth2App", diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 9861e195b7a69..7849778302ea5 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -11073,6 +11073,7 @@ "group_member", "idpsync_settings", "license", + "notification_message", "notification_preference", "notification_template", "oauth2_app", @@ -11106,6 +11107,7 @@ "ResourceGroupMember", "ResourceIdpsyncSettings", "ResourceLicense", + "ResourceNotificationMessage", "ResourceNotificationPreference", "ResourceNotificationTemplate", "ResourceOauth2App", diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index ae6b307b3e7d3..d852de6ef8a57 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -264,6 +264,23 @@ var ( Scope: rbac.ScopeAll, }.WithCachedASTValue() + subjectNotifier = rbac.Subject{ + FriendlyName: "Notifier", + ID: uuid.Nil.String(), + Roles: rbac.Roles([]rbac.Role{ + { + Identifier: rbac.RoleIdentifier{Name: "notifier"}, + DisplayName: "Notifier", + Site: rbac.Permissions(map[string][]policy.Action{ + rbac.ResourceNotificationMessage.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate}, + }), + Org: map[string][]rbac.Permission{}, + User: []rbac.Permission{}, + }, + }), + Scope: rbac.ScopeAll, + }.WithCachedASTValue() + subjectSystemRestricted = rbac.Subject{ FriendlyName: "System", ID: uuid.Nil.String(), @@ -327,6 +344,11 @@ func AsKeyReader(ctx context.Context) context.Context { return context.WithValue(ctx, authContextKey{}, subjectCryptoKeyReader) } +// AsNotifier returns a context with an actor that has permissions required for creating notifications. +func AsNotifier(ctx context.Context) context.Context { + return context.WithValue(ctx, authContextKey{}, subjectNotifier) +} + // AsSystemRestricted returns a context with an actor that has permissions // required for various system operations (login, logout, metrics cache). func AsSystemRestricted(ctx context.Context) context.Context { @@ -950,7 +972,7 @@ func (q *querier) AcquireLock(ctx context.Context, id int64) error { } func (q *querier) AcquireNotificationMessages(ctx context.Context, arg database.AcquireNotificationMessagesParams) ([]database.AcquireNotificationMessagesRow, error) { - if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceSystem); err != nil { + if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceNotificationMessage); err != nil { return nil, err } return q.db.AcquireNotificationMessages(ctx, arg) @@ -1307,7 +1329,7 @@ func (q *querier) DeleteWorkspaceAgentPortSharesByTemplate(ctx context.Context, } func (q *querier) EnqueueNotificationMessage(ctx context.Context, arg database.EnqueueNotificationMessageParams) error { - if err := q.authorizeContext(ctx, policy.ActionCreate, rbac.ResourceSystem); err != nil { + if err := q.authorizeContext(ctx, policy.ActionCreate, rbac.ResourceNotificationMessage); err != nil { return err } return q.db.EnqueueNotificationMessage(ctx, arg) @@ -1321,7 +1343,7 @@ func (q *querier) FavoriteWorkspace(ctx context.Context, id uuid.UUID) error { } func (q *querier) FetchNewMessageMetadata(ctx context.Context, arg database.FetchNewMessageMetadataParams) (database.FetchNewMessageMetadataRow, error) { - if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil { + if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceNotificationMessage); err != nil { return database.FetchNewMessageMetadataRow{}, err } return q.db.FetchNewMessageMetadata(ctx, arg) diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go index 86ed14fe90957..afe415226f1cd 100644 --- a/coderd/notifications/notifications_test.go +++ b/coderd/notifications/notifications_test.go @@ -71,7 +71,7 @@ func TestBasicNotificationRoundtrip(t *testing.T) { } // nolint:gocritic // Unit test. - ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong)) store, _ := dbtestutil.NewDB(t) logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) method := database.NotificationMethodSmtp @@ -135,7 +135,7 @@ func TestSMTPDispatch(t *testing.T) { // SETUP // nolint:gocritic // Unit test. - ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong)) store, _ := dbtestutil.NewDB(t) logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) @@ -197,7 +197,7 @@ func TestWebhookDispatch(t *testing.T) { // SETUP // nolint:gocritic // Unit test. - ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong)) store, _ := dbtestutil.NewDB(t) logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) @@ -281,7 +281,7 @@ func TestBackpressure(t *testing.T) { store, _ := dbtestutil.NewDB(t) logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) // nolint:gocritic // Unit test. - ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitShort)) + ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitShort)) const method = database.NotificationMethodWebhook cfg := defaultNotificationsConfig(method) @@ -407,7 +407,7 @@ func TestRetries(t *testing.T) { const maxAttempts = 3 // nolint:gocritic // Unit test. - ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong)) store, _ := dbtestutil.NewDB(t) logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) @@ -501,7 +501,7 @@ func TestExpiredLeaseIsRequeued(t *testing.T) { } // nolint:gocritic // Unit test. - ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong)) store, _ := dbtestutil.NewDB(t) logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) @@ -521,7 +521,7 @@ func TestExpiredLeaseIsRequeued(t *testing.T) { noopInterceptor := newNoopStoreSyncer(store) // nolint:gocritic // Unit test. - mgrCtx, cancelManagerCtx := context.WithCancel(dbauthz.AsSystemRestricted(context.Background())) + mgrCtx, cancelManagerCtx := context.WithCancel(dbauthz.AsNotifier(context.Background())) t.Cleanup(cancelManagerCtx) mgr, err := notifications.NewManager(cfg, noopInterceptor, defaultHelpers(), createMetrics(), logger.Named("manager")) @@ -626,7 +626,7 @@ func TestNotifierPaused(t *testing.T) { // Setup. // nolint:gocritic // Unit test. - ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong)) store, _ := dbtestutil.NewDB(t) logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) @@ -1081,7 +1081,7 @@ func TestNotificationTemplates_Golden(t *testing.T) { }() // nolint:gocritic // Unit test. - ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong)) // smtp config shared between client and server smtpConfig := codersdk.NotificationsEmailConfig{ @@ -1160,12 +1160,14 @@ func TestNotificationTemplates_Golden(t *testing.T) { // as appearance changes are enterprise features and we do not want to mix those // can't use the api if tc.appName != "" { - err = (*db).UpsertApplicationName(ctx, "Custom Application") + // nolint:gocritic // Unit test. + err = (*db).UpsertApplicationName(dbauthz.AsSystemRestricted(ctx), "Custom Application") require.NoError(t, err) } if tc.logoURL != "" { - err = (*db).UpsertLogoURL(ctx, "https://custom.application/logo.png") + // nolint:gocritic // Unit test. + err = (*db).UpsertLogoURL(dbauthz.AsSystemRestricted(ctx), "https://custom.application/logo.png") require.NoError(t, err) } @@ -1248,7 +1250,7 @@ func TestNotificationTemplates_Golden(t *testing.T) { }() // nolint:gocritic // Unit test. - ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong)) // Spin up the mock webhook server var body []byte @@ -1377,7 +1379,7 @@ func TestDisabledBeforeEnqueue(t *testing.T) { } // nolint:gocritic // Unit test. - ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong)) store, _ := dbtestutil.NewDB(t) logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) @@ -1413,7 +1415,7 @@ func TestDisabledAfterEnqueue(t *testing.T) { } // nolint:gocritic // Unit test. - ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong)) store, _ := dbtestutil.NewDB(t) logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) @@ -1470,7 +1472,7 @@ func TestCustomNotificationMethod(t *testing.T) { } // nolint:gocritic // Unit test. - ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong)) store, _ := dbtestutil.NewDB(t) logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) @@ -1574,7 +1576,7 @@ func TestNotificationsTemplates(t *testing.T) { } // nolint:gocritic // Unit test. - ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong)) api := coderdtest.New(t, createOpts(t)) // GIVEN: the first user (owner) and a regular member @@ -1611,7 +1613,7 @@ func TestNotificationDuplicates(t *testing.T) { } // nolint:gocritic // Unit test. - ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong)) store, _ := dbtestutil.NewDB(t) logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) diff --git a/coderd/rbac/object_gen.go b/coderd/rbac/object_gen.go index efe798d4ae4ac..ec9ff61776b3e 100644 --- a/coderd/rbac/object_gen.go +++ b/coderd/rbac/object_gen.go @@ -129,6 +129,15 @@ var ( Type: "license", } + // ResourceNotificationMessage + // Valid Actions + // - "ActionCreate" :: create notification messages + // - "ActionRead" :: read notification messages + // - "ActionUpdate" :: update notification messages + ResourceNotificationMessage = Object{ + Type: "notification_message", + } + // ResourceNotificationPreference // Valid Actions // - "ActionRead" :: read notification preferences @@ -318,6 +327,7 @@ func AllResources() []Objecter { ResourceGroupMember, ResourceIdpsyncSettings, ResourceLicense, + ResourceNotificationMessage, ResourceNotificationPreference, ResourceNotificationTemplate, ResourceOauth2App, diff --git a/coderd/rbac/policy/policy.go b/coderd/rbac/policy/policy.go index c553ac31cd6e3..71b0f6854fc7a 100644 --- a/coderd/rbac/policy/policy.go +++ b/coderd/rbac/policy/policy.go @@ -262,6 +262,13 @@ var RBACPermissions = map[string]PermissionDefinition{ ActionDelete: actDef(""), }, }, + "notification_message": { + Actions: map[Action]ActionDefinition{ + ActionCreate: actDef("create notification messages"), + ActionRead: actDef("read notification messages"), + ActionUpdate: actDef("update notification messages"), + }, + }, "notification_template": { Actions: map[Action]ActionDefinition{ ActionRead: actDef("read notification templates"), diff --git a/coderd/rbac/roles_test.go b/coderd/rbac/roles_test.go index c5a759f4d1da6..3cffd3a4bc96b 100644 --- a/coderd/rbac/roles_test.go +++ b/coderd/rbac/roles_test.go @@ -647,6 +647,21 @@ func TestRolePermissions(t *testing.T) { }, }, }, + { + Name: "NotificationMessages", + Actions: []policy.Action{policy.ActionCreate, policy.ActionRead}, + Resource: rbac.ResourceNotificationMessage, + AuthorizeMap: map[bool][]hasAuthSubjects{ + true: {owner}, + false: { + memberMe, orgMemberMe, otherOrgMember, + orgAdmin, otherOrgAdmin, + orgAuditor, otherOrgAuditor, + templateAdmin, orgTemplateAdmin, otherOrgTemplateAdmin, + userAdmin, orgUserAdmin, otherOrgUserAdmin, + }, + }, + }, { // Notification preferences are currently not organization-scoped // Any owner/admin may access any users' preferences diff --git a/coderd/templates.go b/coderd/templates.go index de47b5225a973..82f805f5a09c0 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -878,8 +878,8 @@ func (api *API) notifyUsersOfTemplateDeprecation(ctx context.Context, template d for userID := range users { _, err = api.NotificationsEnqueuer.Enqueue( - //nolint:gocritic // We need the system auth context to be able to send the deprecation notification. - dbauthz.AsSystemRestricted(ctx), + //nolint:gocritic // We need the notifier auth context to be able to send the deprecation notification. + dbauthz.AsNotifier(ctx), userID, notifications.TemplateTemplateDeprecated, map[string]string{ diff --git a/coderd/userauth.go b/coderd/userauth.go index 13f9b088d731f..2c83072d3b8f0 100644 --- a/coderd/userauth.go +++ b/coderd/userauth.go @@ -298,8 +298,8 @@ func (api *API) postRequestOneTimePasscode(rw http.ResponseWriter, r *http.Reque func (api *API) notifyUserRequestedOneTimePasscode(ctx context.Context, user database.User, passcode string) error { _, err := api.NotificationsEnqueuer.Enqueue( - //nolint:gocritic // We need the system auth context to be able to send the user their one-time passcode. - dbauthz.AsSystemRestricted(ctx), + //nolint:gocritic // We need the notifier auth context to be able to send the user their one-time passcode. + dbauthz.AsNotifier(ctx), user.ID, notifications.TemplateUserRequestedOneTimePasscode, map[string]string{"one_time_passcode": passcode}, diff --git a/codersdk/rbacresources_gen.go b/codersdk/rbacresources_gen.go index 8c3ced0946223..538a646af4c3d 100644 --- a/codersdk/rbacresources_gen.go +++ b/codersdk/rbacresources_gen.go @@ -18,6 +18,7 @@ const ( ResourceGroupMember RBACResource = "group_member" ResourceIdpsyncSettings RBACResource = "idpsync_settings" ResourceLicense RBACResource = "license" + ResourceNotificationMessage RBACResource = "notification_message" ResourceNotificationPreference RBACResource = "notification_preference" ResourceNotificationTemplate RBACResource = "notification_template" ResourceOauth2App RBACResource = "oauth2_app" @@ -72,6 +73,7 @@ var RBACResourceActions = map[RBACResource][]RBACAction{ ResourceGroupMember: {ActionRead}, ResourceIdpsyncSettings: {ActionRead, ActionUpdate}, ResourceLicense: {ActionCreate, ActionDelete, ActionRead}, + ResourceNotificationMessage: {ActionCreate, ActionRead, ActionUpdate}, ResourceNotificationPreference: {ActionRead, ActionUpdate}, ResourceNotificationTemplate: {ActionRead, ActionUpdate}, ResourceOauth2App: {ActionCreate, ActionDelete, ActionRead, ActionUpdate}, diff --git a/docs/reference/api/members.md b/docs/reference/api/members.md index 517ac51807c06..6ac07aa21fd5d 100644 --- a/docs/reference/api/members.md +++ b/docs/reference/api/members.md @@ -193,6 +193,7 @@ Status Code **200** | `resource_type` | `group_member` | | `resource_type` | `idpsync_settings` | | `resource_type` | `license` | +| `resource_type` | `notification_message` | | `resource_type` | `notification_preference` | | `resource_type` | `notification_template` | | `resource_type` | `oauth2_app` | @@ -353,6 +354,7 @@ Status Code **200** | `resource_type` | `group_member` | | `resource_type` | `idpsync_settings` | | `resource_type` | `license` | +| `resource_type` | `notification_message` | | `resource_type` | `notification_preference` | | `resource_type` | `notification_template` | | `resource_type` | `oauth2_app` | @@ -513,6 +515,7 @@ Status Code **200** | `resource_type` | `group_member` | | `resource_type` | `idpsync_settings` | | `resource_type` | `license` | +| `resource_type` | `notification_message` | | `resource_type` | `notification_preference` | | `resource_type` | `notification_template` | | `resource_type` | `oauth2_app` | @@ -642,6 +645,7 @@ Status Code **200** | `resource_type` | `group_member` | | `resource_type` | `idpsync_settings` | | `resource_type` | `license` | +| `resource_type` | `notification_message` | | `resource_type` | `notification_preference` | | `resource_type` | `notification_template` | | `resource_type` | `oauth2_app` | @@ -901,6 +905,7 @@ Status Code **200** | `resource_type` | `group_member` | | `resource_type` | `idpsync_settings` | | `resource_type` | `license` | +| `resource_type` | `notification_message` | | `resource_type` | `notification_preference` | | `resource_type` | `notification_template` | | `resource_type` | `oauth2_app` | diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index f4e683305029b..5dbb8cb5df97f 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -4491,6 +4491,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `group_member` | | `idpsync_settings` | | `license` | +| `notification_message` | | `notification_preference` | | `notification_template` | | `oauth2_app` | diff --git a/site/src/api/rbacresourcesGenerated.ts b/site/src/api/rbacresourcesGenerated.ts index 34b2ddf021ace..16f53c60d6303 100644 --- a/site/src/api/rbacresourcesGenerated.ts +++ b/site/src/api/rbacresourcesGenerated.ts @@ -70,6 +70,11 @@ export const RBACResourceActions: Partial< delete: "delete license", read: "read licenses", }, + notification_message: { + create: "create notification messages", + read: "read notification messages", + update: "update notification messages", + }, notification_preference: { read: "read notification preferences", update: "update notification preferences", diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index d687fb68ec61f..9b68a64b02521 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -2198,8 +2198,8 @@ export type RBACAction = "application_connect" | "assign" | "create" | "delete" export const RBACActions: RBACAction[] = ["application_connect", "assign", "create", "delete", "read", "read_personal", "ssh", "start", "stop", "update", "update_personal", "use", "view_insights"] // From codersdk/rbacresources_gen.go -export type RBACResource = "*" | "api_key" | "assign_org_role" | "assign_role" | "audit_log" | "crypto_key" | "debug_info" | "deployment_config" | "deployment_stats" | "file" | "group" | "group_member" | "idpsync_settings" | "license" | "notification_preference" | "notification_template" | "oauth2_app" | "oauth2_app_code_token" | "oauth2_app_secret" | "organization" | "organization_member" | "provisioner_daemon" | "provisioner_keys" | "replicas" | "system" | "tailnet_coordinator" | "template" | "user" | "workspace" | "workspace_dormant" | "workspace_proxy" -export const RBACResources: RBACResource[] = ["*", "api_key", "assign_org_role", "assign_role", "audit_log", "crypto_key", "debug_info", "deployment_config", "deployment_stats", "file", "group", "group_member", "idpsync_settings", "license", "notification_preference", "notification_template", "oauth2_app", "oauth2_app_code_token", "oauth2_app_secret", "organization", "organization_member", "provisioner_daemon", "provisioner_keys", "replicas", "system", "tailnet_coordinator", "template", "user", "workspace", "workspace_dormant", "workspace_proxy"] +export type RBACResource = "*" | "api_key" | "assign_org_role" | "assign_role" | "audit_log" | "crypto_key" | "debug_info" | "deployment_config" | "deployment_stats" | "file" | "group" | "group_member" | "idpsync_settings" | "license" | "notification_message" | "notification_preference" | "notification_template" | "oauth2_app" | "oauth2_app_code_token" | "oauth2_app_secret" | "organization" | "organization_member" | "provisioner_daemon" | "provisioner_keys" | "replicas" | "system" | "tailnet_coordinator" | "template" | "user" | "workspace" | "workspace_dormant" | "workspace_proxy" +export const RBACResources: RBACResource[] = ["*", "api_key", "assign_org_role", "assign_role", "audit_log", "crypto_key", "debug_info", "deployment_config", "deployment_stats", "file", "group", "group_member", "idpsync_settings", "license", "notification_message", "notification_preference", "notification_template", "oauth2_app", "oauth2_app_code_token", "oauth2_app_secret", "organization", "organization_member", "provisioner_daemon", "provisioner_keys", "replicas", "system", "tailnet_coordinator", "template", "user", "workspace", "workspace_dormant", "workspace_proxy"] // From codersdk/audit.go export type ResourceType = "api_key" | "convert_login" | "custom_role" | "git_ssh_key" | "group" | "health_settings" | "license" | "notifications_settings" | "oauth2_provider_app" | "oauth2_provider_app_secret" | "organization" | "template" | "template_version" | "user" | "workspace" | "workspace_build" | "workspace_proxy" From 72a2e9d5ae8ddd43c8ad19c7d9af29dbb576f94e Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Thu, 31 Oct 2024 10:45:03 +0000 Subject: [PATCH 2/5] fix: do todo tasks --- cli/server.go | 4 +-- coderd/database/dbauthz/dbauthz.go | 8 +++--- coderd/database/dbauthz/dbauthz_test.go | 33 ++++++++++--------------- coderd/rbac/roles_test.go | 2 +- 4 files changed, 20 insertions(+), 27 deletions(-) diff --git a/cli/server.go b/cli/server.go index d0282004a2aa1..44f3bb5164f94 100644 --- a/cli/server.go +++ b/cli/server.go @@ -910,8 +910,8 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. return xerrors.Errorf("failed to instantiate notification manager: %w", err) } - // nolint:gocritic // TODO: create own role. - notificationsManager.Run(dbauthz.AsSystemRestricted(ctx)) + // nolint:gocritic // We need to run the manager in a notifier context. + notificationsManager.Run(dbauthz.AsNotifier(ctx)) // Run report generator to distribute periodic reports. notificationReportGenerator := reports.NewReportGenerator(ctx, logger.Named("notifications.report_generator"), options.Database, options.NotificationsEnqueuer, quartz.NewReal()) diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index d852de6ef8a57..bb88183396692 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1023,14 +1023,14 @@ func (q *querier) BatchUpdateWorkspaceLastUsedAt(ctx context.Context, arg databa } func (q *querier) BulkMarkNotificationMessagesFailed(ctx context.Context, arg database.BulkMarkNotificationMessagesFailedParams) (int64, error) { - if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceSystem); err != nil { + if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceNotificationMessage); err != nil { return 0, err } return q.db.BulkMarkNotificationMessagesFailed(ctx, arg) } func (q *querier) BulkMarkNotificationMessagesSent(ctx context.Context, arg database.BulkMarkNotificationMessagesSentParams) (int64, error) { - if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceSystem); err != nil { + if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceNotificationMessage); err != nil { return 0, err } return q.db.BulkMarkNotificationMessagesSent(ctx, arg) @@ -1207,7 +1207,7 @@ func (q *querier) DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx context.Contex } func (q *querier) DeleteOldNotificationMessages(ctx context.Context) error { - if err := q.authorizeContext(ctx, policy.ActionDelete, rbac.ResourceSystem); err != nil { + if err := q.authorizeContext(ctx, policy.ActionDelete, rbac.ResourceNotificationMessage); err != nil { return err } return q.db.DeleteOldNotificationMessages(ctx) @@ -1708,7 +1708,7 @@ func (q *querier) GetLogoURL(ctx context.Context) (string, error) { } func (q *querier) GetNotificationMessagesByStatus(ctx context.Context, arg database.GetNotificationMessagesByStatusParams) ([]database.NotificationMessage, error) { - if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil { + if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceNotificationMessage); err != nil { return nil, err } return q.db.GetNotificationMessagesByStatus(ctx, arg) diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 439cf1bdaec19..73403a95b7859 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -2888,40 +2888,33 @@ func (s *MethodTestSuite) TestSystemFunctions() { func (s *MethodTestSuite) TestNotifications() { // System functions - s.Run("AcquireNotificationMessages", s.Subtest(func(db database.Store, check *expects) { - // TODO: update this test once we have a specific role for notifications - check.Args(database.AcquireNotificationMessagesParams{}).Asserts(rbac.ResourceSystem, policy.ActionUpdate) + s.Run("AcquireNotificationMessages", s.Subtest(func(_ database.Store, check *expects) { + check.Args(database.AcquireNotificationMessagesParams{}).Asserts(rbac.ResourceNotificationMessage, policy.ActionUpdate) })) - s.Run("BulkMarkNotificationMessagesFailed", s.Subtest(func(db database.Store, check *expects) { - // TODO: update this test once we have a specific role for notifications - check.Args(database.BulkMarkNotificationMessagesFailedParams{}).Asserts(rbac.ResourceSystem, policy.ActionUpdate) + s.Run("BulkMarkNotificationMessagesFailed", s.Subtest(func(_ database.Store, check *expects) { + check.Args(database.BulkMarkNotificationMessagesFailedParams{}).Asserts(rbac.ResourceNotificationMessage, policy.ActionUpdate) })) - s.Run("BulkMarkNotificationMessagesSent", s.Subtest(func(db database.Store, check *expects) { - // TODO: update this test once we have a specific role for notifications - check.Args(database.BulkMarkNotificationMessagesSentParams{}).Asserts(rbac.ResourceSystem, policy.ActionUpdate) + s.Run("BulkMarkNotificationMessagesSent", s.Subtest(func(_ database.Store, check *expects) { + check.Args(database.BulkMarkNotificationMessagesSentParams{}).Asserts(rbac.ResourceNotificationMessage, policy.ActionUpdate) })) - s.Run("DeleteOldNotificationMessages", s.Subtest(func(db database.Store, check *expects) { - // TODO: update this test once we have a specific role for notifications - check.Args().Asserts(rbac.ResourceSystem, policy.ActionDelete) + s.Run("DeleteOldNotificationMessages", s.Subtest(func(_ database.Store, check *expects) { + check.Args().Asserts(rbac.ResourceNotificationMessage, policy.ActionDelete) })) - s.Run("EnqueueNotificationMessage", s.Subtest(func(db database.Store, check *expects) { - // TODO: update this test once we have a specific role for notifications + s.Run("EnqueueNotificationMessage", s.Subtest(func(_ database.Store, check *expects) { check.Args(database.EnqueueNotificationMessageParams{ Method: database.NotificationMethodWebhook, Payload: []byte("{}"), - }).Asserts(rbac.ResourceSystem, policy.ActionCreate) + }).Asserts(rbac.ResourceNotificationMessage, policy.ActionCreate) })) s.Run("FetchNewMessageMetadata", s.Subtest(func(db database.Store, check *expects) { - // TODO: update this test once we have a specific role for notifications u := dbgen.User(s.T(), db, database.User{}) - check.Args(database.FetchNewMessageMetadataParams{UserID: u.ID}).Asserts(rbac.ResourceSystem, policy.ActionRead) + check.Args(database.FetchNewMessageMetadataParams{UserID: u.ID}).Asserts(rbac.ResourceNotificationMessage, policy.ActionRead) })) - s.Run("GetNotificationMessagesByStatus", s.Subtest(func(db database.Store, check *expects) { - // TODO: update this test once we have a specific role for notifications + s.Run("GetNotificationMessagesByStatus", s.Subtest(func(_ database.Store, check *expects) { check.Args(database.GetNotificationMessagesByStatusParams{ Status: database.NotificationMessageStatusLeased, Limit: 10, - }).Asserts(rbac.ResourceSystem, policy.ActionRead) + }).Asserts(rbac.ResourceNotificationMessage, policy.ActionRead) })) // Notification templates diff --git a/coderd/rbac/roles_test.go b/coderd/rbac/roles_test.go index 3cffd3a4bc96b..0514bca82af3b 100644 --- a/coderd/rbac/roles_test.go +++ b/coderd/rbac/roles_test.go @@ -649,7 +649,7 @@ func TestRolePermissions(t *testing.T) { }, { Name: "NotificationMessages", - Actions: []policy.Action{policy.ActionCreate, policy.ActionRead}, + Actions: []policy.Action{policy.ActionCreate, policy.ActionRead, policy.ActionUpdate}, Resource: rbac.ResourceNotificationMessage, AuthorizeMap: map[bool][]hasAuthSubjects{ true: {owner}, From 68687deb7c097a9bafe9ccffaeb3056016dc7b8c Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Thu, 31 Oct 2024 11:02:49 +0000 Subject: [PATCH 3/5] fix: add delete policy --- coderd/database/dbauthz/dbauthz.go | 3 ++- coderd/rbac/object_gen.go | 1 + coderd/rbac/policy/policy.go | 1 + codersdk/rbacresources_gen.go | 2 +- site/src/api/rbacresourcesGenerated.ts | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index bb88183396692..3efea5cef1c49 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -272,7 +272,7 @@ var ( Identifier: rbac.RoleIdentifier{Name: "notifier"}, DisplayName: "Notifier", Site: rbac.Permissions(map[string][]policy.Action{ - rbac.ResourceNotificationMessage.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate}, + rbac.ResourceNotificationMessage.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete}, }), Org: map[string][]rbac.Permission{}, User: []rbac.Permission{}, @@ -304,6 +304,7 @@ var ( rbac.ResourceWorkspace.Type: {policy.ActionUpdate, policy.ActionDelete, policy.ActionWorkspaceStart, policy.ActionWorkspaceStop, policy.ActionSSH}, rbac.ResourceWorkspaceProxy.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete}, rbac.ResourceDeploymentConfig.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete}, + rbac.ResourceNotificationMessage.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete}, rbac.ResourceNotificationPreference.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete}, rbac.ResourceNotificationTemplate.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete}, rbac.ResourceCryptoKey.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete}, diff --git a/coderd/rbac/object_gen.go b/coderd/rbac/object_gen.go index ec9ff61776b3e..61caf0945b245 100644 --- a/coderd/rbac/object_gen.go +++ b/coderd/rbac/object_gen.go @@ -132,6 +132,7 @@ var ( // ResourceNotificationMessage // Valid Actions // - "ActionCreate" :: create notification messages + // - "ActionDelete" :: delete notification messages // - "ActionRead" :: read notification messages // - "ActionUpdate" :: update notification messages ResourceNotificationMessage = Object{ diff --git a/coderd/rbac/policy/policy.go b/coderd/rbac/policy/policy.go index 71b0f6854fc7a..d70dd69821429 100644 --- a/coderd/rbac/policy/policy.go +++ b/coderd/rbac/policy/policy.go @@ -267,6 +267,7 @@ var RBACPermissions = map[string]PermissionDefinition{ ActionCreate: actDef("create notification messages"), ActionRead: actDef("read notification messages"), ActionUpdate: actDef("update notification messages"), + ActionDelete: actDef("delete notification messages"), }, }, "notification_template": { diff --git a/codersdk/rbacresources_gen.go b/codersdk/rbacresources_gen.go index 538a646af4c3d..c903d5f8a02ff 100644 --- a/codersdk/rbacresources_gen.go +++ b/codersdk/rbacresources_gen.go @@ -73,7 +73,7 @@ var RBACResourceActions = map[RBACResource][]RBACAction{ ResourceGroupMember: {ActionRead}, ResourceIdpsyncSettings: {ActionRead, ActionUpdate}, ResourceLicense: {ActionCreate, ActionDelete, ActionRead}, - ResourceNotificationMessage: {ActionCreate, ActionRead, ActionUpdate}, + ResourceNotificationMessage: {ActionCreate, ActionDelete, ActionRead, ActionUpdate}, ResourceNotificationPreference: {ActionRead, ActionUpdate}, ResourceNotificationTemplate: {ActionRead, ActionUpdate}, ResourceOauth2App: {ActionCreate, ActionDelete, ActionRead, ActionUpdate}, diff --git a/site/src/api/rbacresourcesGenerated.ts b/site/src/api/rbacresourcesGenerated.ts index 16f53c60d6303..79a80ef593d90 100644 --- a/site/src/api/rbacresourcesGenerated.ts +++ b/site/src/api/rbacresourcesGenerated.ts @@ -72,6 +72,7 @@ export const RBACResourceActions: Partial< }, notification_message: { create: "create notification messages", + delete: "delete notification messages", read: "read notification messages", update: "update notification messages", }, From da84f4ba909dc074a34eac2cd4aab84d27b2b483 Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Thu, 31 Oct 2024 13:02:58 +0000 Subject: [PATCH 4/5] fix: roles_test --- coderd/rbac/roles_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/rbac/roles_test.go b/coderd/rbac/roles_test.go index 0514bca82af3b..954b5e9788c53 100644 --- a/coderd/rbac/roles_test.go +++ b/coderd/rbac/roles_test.go @@ -649,7 +649,7 @@ func TestRolePermissions(t *testing.T) { }, { Name: "NotificationMessages", - Actions: []policy.Action{policy.ActionCreate, policy.ActionRead, policy.ActionUpdate}, + Actions: []policy.Action{policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete}, Resource: rbac.ResourceNotificationMessage, AuthorizeMap: map[bool][]hasAuthSubjects{ true: {owner}, From c12407ab320cff02ba454b7de0d3270a390b025d Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Thu, 31 Oct 2024 15:20:58 +0000 Subject: [PATCH 5/5] fix: update inaccurate comment --- coderd/database/dbauthz/dbauthz.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 3efea5cef1c49..76d78754255ca 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -345,7 +345,8 @@ func AsKeyReader(ctx context.Context) context.Context { return context.WithValue(ctx, authContextKey{}, subjectCryptoKeyReader) } -// AsNotifier returns a context with an actor that has permissions required for creating notifications. +// AsNotifier returns a context with an actor that has permissions required for +// creating/reading/updating/deleting notifications. func AsNotifier(ctx context.Context) context.Context { return context.WithValue(ctx, authContextKey{}, subjectNotifier) } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy