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/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..76d78754255ca 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, policy.ActionDelete}, + }), + Org: map[string][]rbac.Permission{}, + User: []rbac.Permission{}, + }, + }), + Scope: rbac.ScopeAll, + }.WithCachedASTValue() + subjectSystemRestricted = rbac.Subject{ FriendlyName: "System", ID: uuid.Nil.String(), @@ -287,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}, @@ -327,6 +345,12 @@ 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/reading/updating/deleting 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 +974,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) @@ -1001,14 +1025,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) @@ -1185,7 +1209,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) @@ -1307,7 +1331,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 +1345,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) @@ -1686,7 +1710,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/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..61caf0945b245 100644 --- a/coderd/rbac/object_gen.go +++ b/coderd/rbac/object_gen.go @@ -129,6 +129,16 @@ var ( Type: "license", } + // ResourceNotificationMessage + // Valid Actions + // - "ActionCreate" :: create notification messages + // - "ActionDelete" :: delete notification messages + // - "ActionRead" :: read notification messages + // - "ActionUpdate" :: update notification messages + ResourceNotificationMessage = Object{ + Type: "notification_message", + } + // ResourceNotificationPreference // Valid Actions // - "ActionRead" :: read notification preferences @@ -318,6 +328,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..d70dd69821429 100644 --- a/coderd/rbac/policy/policy.go +++ b/coderd/rbac/policy/policy.go @@ -262,6 +262,14 @@ 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"), + ActionDelete: actDef("delete 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..954b5e9788c53 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, policy.ActionUpdate, policy.ActionDelete}, + 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..c903d5f8a02ff 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, ActionDelete, 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..79a80ef593d90 100644 --- a/site/src/api/rbacresourcesGenerated.ts +++ b/site/src/api/rbacresourcesGenerated.ts @@ -70,6 +70,12 @@ export const RBACResourceActions: Partial< delete: "delete license", read: "read licenses", }, + notification_message: { + create: "create notification messages", + delete: "delete 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"