fix(coderd): ensure correct RBAC when enqueueing notifications (#15478)

- Assert rbac in fake notifications enqueuer
- Move fake notifications enqueuer to separate notificationstest package
- Update dbauthz rbac policy to allow provisionerd and autostart to create and read notification messages
- Update tests as required
This commit is contained in:
Cian Johnston
2024-11-12 12:40:46 +00:00
committed by GitHub
parent bb5c3a2dd8
commit 30e6fbd35c
18 changed files with 323 additions and 242 deletions

View File

@ -208,7 +208,8 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S
for _, ws := range markedForDeletion {
dormantTime := dbtime.Now().Add(opts.TimeTilDormantAutoDelete)
_, err = s.enqueuer.Enqueue(
ctx,
// nolint:gocritic // Need actor to enqueue notification
dbauthz.AsNotifier(ctx),
ws.OwnerID,
notifications.TemplateWorkspaceMarkedForDeletion,
map[string]string{

View File

@ -16,9 +16,11 @@ import (
"cdr.dev/slog/sloggers/slogtest"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbauthz"
"github.com/coder/coder/v2/coderd/database/dbgen"
"github.com/coder/coder/v2/coderd/database/dbtestutil"
"github.com/coder/coder/v2/coderd/notifications"
"github.com/coder/coder/v2/coderd/notifications/notificationstest"
agplschedule "github.com/coder/coder/v2/coderd/schedule"
"github.com/coder/coder/v2/coderd/util/ptr"
"github.com/coder/coder/v2/cryptorand"
@ -673,7 +675,7 @@ func TestNotifications(t *testing.T) {
}
// Setup dependencies
notifyEnq := testutil.FakeNotificationsEnqueuer{}
notifyEnq := notificationstest.FakeEnqueuer{}
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
const userQuietHoursSchedule = "CRON_TZ=UTC 0 0 * * *" // midnight UTC
userQuietHoursStore, err := schedule.NewEnterpriseUserQuietHoursScheduleStore(userQuietHoursSchedule, true)
@ -685,21 +687,23 @@ func TestNotifications(t *testing.T) {
// Lower the dormancy TTL to ensure the schedule recalculates deadlines and
// triggers notifications.
_, err = templateScheduleStore.Set(ctx, db, template, agplschedule.TemplateScheduleOptions{
// nolint:gocritic // Need an actor in the context.
_, err = templateScheduleStore.Set(dbauthz.AsNotifier(ctx), db, template, agplschedule.TemplateScheduleOptions{
TimeTilDormant: timeTilDormant / 2,
TimeTilDormantAutoDelete: timeTilDormant / 2,
})
require.NoError(t, err)
// We should expect a notification for each dormant workspace.
require.Len(t, notifyEnq.Sent, len(dormantWorkspaces))
sent := notifyEnq.Sent()
require.Len(t, sent, len(dormantWorkspaces))
for i, dormantWs := range dormantWorkspaces {
require.Equal(t, notifyEnq.Sent[i].UserID, dormantWs.OwnerID)
require.Equal(t, notifyEnq.Sent[i].TemplateID, notifications.TemplateWorkspaceMarkedForDeletion)
require.Contains(t, notifyEnq.Sent[i].Targets, template.ID)
require.Contains(t, notifyEnq.Sent[i].Targets, dormantWs.ID)
require.Contains(t, notifyEnq.Sent[i].Targets, dormantWs.OrganizationID)
require.Contains(t, notifyEnq.Sent[i].Targets, dormantWs.OwnerID)
require.Equal(t, sent[i].UserID, dormantWs.OwnerID)
require.Equal(t, sent[i].TemplateID, notifications.TemplateWorkspaceMarkedForDeletion)
require.Contains(t, sent[i].Targets, template.ID)
require.Contains(t, sent[i].Targets, dormantWs.ID)
require.Contains(t, sent[i].Targets, dormantWs.OrganizationID)
require.Contains(t, sent[i].Targets, dormantWs.OwnerID)
}
})
}

View File

@ -16,6 +16,7 @@ import (
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/coderdtest/oidctest"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/notifications/notificationstest"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/cryptorand"
"github.com/coder/coder/v2/enterprise/coderd"
@ -122,7 +123,7 @@ func TestScim(t *testing.T) {
// given
scimAPIKey := []byte("hi")
mockAudit := audit.NewMock()
notifyEnq := &testutil.FakeNotificationsEnqueuer{}
notifyEnq := &notificationstest.FakeEnqueuer{}
client, _ := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{
Auditor: mockAudit,
@ -172,7 +173,7 @@ func TestScim(t *testing.T) {
assert.Len(t, userRes.Users[0].OrganizationIDs, 1)
// Expect zero notifications (SkipNotifications = true)
require.Empty(t, notifyEnq.Sent)
require.Empty(t, notifyEnq.Sent())
})
t.Run("OK_Bearer", func(t *testing.T) {
@ -184,7 +185,7 @@ func TestScim(t *testing.T) {
// given
scimAPIKey := []byte("hi")
mockAudit := audit.NewMock()
notifyEnq := &testutil.FakeNotificationsEnqueuer{}
notifyEnq := &notificationstest.FakeEnqueuer{}
client, _ := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{
Auditor: mockAudit,
@ -228,7 +229,7 @@ func TestScim(t *testing.T) {
assert.Len(t, userRes.Users[0].OrganizationIDs, 1)
// Expect zero notifications (SkipNotifications = true)
require.Empty(t, notifyEnq.Sent)
require.Empty(t, notifyEnq.Sent())
})
t.Run("OKNoDefault", func(t *testing.T) {
@ -240,7 +241,7 @@ func TestScim(t *testing.T) {
// given
scimAPIKey := []byte("hi")
mockAudit := audit.NewMock()
notifyEnq := &testutil.FakeNotificationsEnqueuer{}
notifyEnq := &notificationstest.FakeEnqueuer{}
dv := coderdtest.DeploymentValues(t)
dv.OIDC.OrganizationAssignDefault = false
client, _ := coderdenttest.New(t, &coderdenttest.Options{
@ -287,7 +288,7 @@ func TestScim(t *testing.T) {
assert.Len(t, userRes.Users[0].OrganizationIDs, 0)
// Expect zero notifications (SkipNotifications = true)
require.Empty(t, notifyEnq.Sent)
require.Empty(t, notifyEnq.Sent())
})
t.Run("Duplicate", func(t *testing.T) {

View File

@ -19,6 +19,7 @@ import (
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/notifications"
"github.com/coder/coder/v2/coderd/notifications/notificationstest"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/util/ptr"
"github.com/coder/coder/v2/codersdk"
@ -39,7 +40,7 @@ func TestTemplates(t *testing.T) {
t.Run("Deprecated", func(t *testing.T) {
t.Parallel()
notifyEnq := &testutil.FakeNotificationsEnqueuer{}
notifyEnq := &notificationstest.FakeEnqueuer{}
owner, user := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{
IncludeProvisionerDaemon: true,
@ -81,8 +82,8 @@ func TestTemplates(t *testing.T) {
assert.True(t, updated.Deprecated)
assert.NotEmpty(t, updated.DeprecationMessage)
notifs := []*testutil.Notification{}
for _, notif := range notifyEnq.Sent {
notifs := []*notificationstest.FakeNotification{}
for _, notif := range notifyEnq.Sent() {
if notif.TemplateID == notifications.TemplateTemplateDeprecated {
notifs = append(notifs, notif)
}