chore: create ResourceNotificationMessage and AsNotifier (#15301)

Closes https://github.com/coder/coder/issues/15213

This PR enables sending notifications without requiring the auth system
context, instead using a new auth notifier context.
This commit is contained in:
Danielle Maywood
2024-10-31 17:01:51 +00:00
committed by GitHub
parent 9d03e0429f
commit 330acd1270
16 changed files with 123 additions and 52 deletions
+2 -2
View File
@@ -916,8 +916,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())
+2
View File
@@ -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",
+2
View File
@@ -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",
+31 -7
View File
@@ -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)
+13 -20
View File
@@ -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
+19 -17
View File
@@ -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
@@ -1376,7 +1378,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)
@@ -1412,7 +1414,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)
@@ -1469,7 +1471,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)
@@ -1573,7 +1575,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
@@ -1610,7 +1612,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)
+11
View File
@@ -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,
+8
View File
@@ -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"),
+15
View File
@@ -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
+2 -2
View File
@@ -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{
+2 -2
View File
@@ -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},
+2
View File
@@ -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},
+5
View File
@@ -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` |
+1
View File
@@ -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` |
+6
View File
@@ -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",
+2 -2
View File
@@ -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"