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"
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: