diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index f40a9f4322507..28812b4eae026 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1489,13 +1489,13 @@ func (q *querier) GetNotificationTemplateByID(ctx context.Context, id uuid.UUID) } func (q *querier) GetNotificationTemplatesByKind(ctx context.Context, kind database.NotificationTemplateKind) ([]database.NotificationTemplate, error) { - // TODO: restrict 'system' kind to admins only? - // All notification templates share the same rbac.Object, so there is no need - // to authorize them individually. If this passes, all notification templates can be read. - if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceNotificationTemplate); err != nil { - return nil, err + // Anyone can read the system notification templates. + if kind == database.NotificationTemplateKindSystem { + return q.db.GetNotificationTemplatesByKind(ctx, kind) } - return q.db.GetNotificationTemplatesByKind(ctx, kind) + + // TODO(dannyk): handle template ownership when we support user-default notification templates. + return nil, sql.ErrNoRows } func (q *querier) GetNotificationsSettings(ctx context.Context) (string, error) { diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index bc14d7a1d42ae..282143df5e505 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -2610,8 +2610,10 @@ func (s *MethodTestSuite) TestNotifications() { })) s.Run("GetNotificationTemplatesByKind", s.Subtest(func(db database.Store, check *expects) { check.Args(database.NotificationTemplateKindSystem). - Asserts(rbac.ResourceNotificationTemplate, policy.ActionRead). + Asserts(). Errors(dbmem.ErrUnimplemented) + + // TODO(dannyk): add support for other database.NotificationTemplateKind types once implemented. })) s.Run("UpdateNotificationTemplateMethodByID", s.Subtest(func(db database.Store, check *expects) { check.Args(database.UpdateNotificationTemplateMethodByIDParams{ diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go index d73edbf7c453b..13ea5b65b96cc 100644 --- a/coderd/notifications/notifications_test.go +++ b/coderd/notifications/notifications_test.go @@ -24,6 +24,7 @@ import ( "github.com/coder/serpent" + "github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/dbgen" "github.com/coder/coder/v2/coderd/database/dbtestutil" @@ -31,6 +32,7 @@ import ( "github.com/coder/coder/v2/coderd/notifications/dispatch" "github.com/coder/coder/v2/coderd/notifications/render" "github.com/coder/coder/v2/coderd/notifications/types" + "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/coderd/util/syncmap" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/testutil" @@ -893,6 +895,43 @@ func TestCustomNotificationMethod(t *testing.T) { }, testutil.WaitLong, testutil.IntervalFast) } +func TestNotificationsTemplates(t *testing.T) { + t.Parallel() + + // SETUP + if !dbtestutil.WillUsePostgres() { + // Notification system templates are only served from the database and not dbmem at this time. + t.Skip("This test requires postgres; it relies on business-logic only implemented in the database") + } + + ctx := testutil.Context(t, testutil.WaitLong) + api := coderdtest.New(t, createOpts(t)) + + // GIVEN: the first user (owner) and a regular member + firstUser := coderdtest.CreateFirstUser(t, api) + memberClient, _ := coderdtest.CreateAnotherUser(t, api, firstUser.OrganizationID, rbac.RoleMember()) + + // WHEN: requesting system notification templates as owner should work + templates, err := api.GetSystemNotificationTemplates(ctx) + require.NoError(t, err) + require.True(t, len(templates) > 1) + + // WHEN: requesting system notification templates as member should work + templates, err = memberClient.GetSystemNotificationTemplates(ctx) + require.NoError(t, err) + require.True(t, len(templates) > 1) +} + +func createOpts(t *testing.T) *coderdtest.Options { + t.Helper() + + dt := coderdtest.DeploymentValues(t) + dt.Experiments = []string{string(codersdk.ExperimentNotifications)} + return &coderdtest.Options{ + DeploymentValues: dt, + } +} + type fakeHandler struct { mu sync.RWMutex succeeded, failed []string