diff --git a/coderd/authorize.go b/coderd/authorize.go index 802cb5ea15e9b..575bb5e98baf6 100644 --- a/coderd/authorize.go +++ b/coderd/authorize.go @@ -19,7 +19,7 @@ import ( // objects that the user is authorized to perform the given action on. // This is faster than calling Authorize() on each object. func AuthorizeFilter[O rbac.Objecter](h *HTTPAuthorizer, r *http.Request, action policy.Action, objects []O) ([]O, error) { - roles := httpmw.UserAuthorization(r) + roles := httpmw.UserAuthorization(r.Context()) objects, err := rbac.Filter(r.Context(), h.Authorizer, roles, action, objects) if err != nil { // Log the error as Filter should not be erroring. @@ -65,7 +65,7 @@ func (api *API) Authorize(r *http.Request, action policy.Action, object rbac.Obj // return // } func (h *HTTPAuthorizer) Authorize(r *http.Request, action policy.Action, object rbac.Objecter) bool { - roles := httpmw.UserAuthorization(r) + roles := httpmw.UserAuthorization(r.Context()) err := h.Authorizer.Authorize(r.Context(), roles, action, object.RBACObject()) if err != nil { // Log the errors for debugging @@ -97,7 +97,7 @@ func (h *HTTPAuthorizer) Authorize(r *http.Request, action policy.Action, object // call 'Authorize()' on the returned objects. // Note the authorization is only for the given action and object type. func (h *HTTPAuthorizer) AuthorizeSQLFilter(r *http.Request, action policy.Action, objectType string) (rbac.PreparedAuthorized, error) { - roles := httpmw.UserAuthorization(r) + roles := httpmw.UserAuthorization(r.Context()) prepared, err := h.Authorizer.Prepare(r.Context(), roles, action, objectType) if err != nil { return nil, xerrors.Errorf("prepare filter: %w", err) @@ -120,7 +120,7 @@ func (h *HTTPAuthorizer) AuthorizeSQLFilter(r *http.Request, action policy.Actio // @Router /authcheck [post] func (api *API) checkAuthorization(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() - auth := httpmw.UserAuthorization(r) + auth := httpmw.UserAuthorization(r.Context()) var params codersdk.AuthorizationRequest if !httpapi.Read(ctx, rw, r, ¶ms) { diff --git a/coderd/coderd.go b/coderd/coderd.go index 8cc5435542189..24b34ea4db91a 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -572,7 +572,7 @@ func New(options *Options) *API { TemplateScheduleStore: options.TemplateScheduleStore, UserQuietHoursScheduleStore: options.UserQuietHoursScheduleStore, AccessControlStore: options.AccessControlStore, - FileCache: files.NewFromStore(options.Database, options.PrometheusRegistry), + FileCache: files.NewFromStore(options.Database, options.PrometheusRegistry, options.Authorizer), Experiments: experiments, WebpushDispatcher: options.WebPushDispatcher, healthCheckGroup: &singleflight.Group[string, *healthsdk.HealthcheckReport]{}, diff --git a/coderd/coderdtest/authorize.go b/coderd/coderdtest/authorize.go index 279405c4e6a21..67551d0e3d2dd 100644 --- a/coderd/coderdtest/authorize.go +++ b/coderd/coderdtest/authorize.go @@ -234,6 +234,10 @@ func (r *RecordingAuthorizer) AssertOutOfOrder(t *testing.T, actor rbac.Subject, // AssertActor asserts in order. If the order of authz calls does not match, // this will fail. func (r *RecordingAuthorizer) AssertActor(t *testing.T, actor rbac.Subject, did ...ActionObjectPair) { + r.AssertActorID(t, actor.ID, did...) +} + +func (r *RecordingAuthorizer) AssertActorID(t *testing.T, id string, did ...ActionObjectPair) { r.Lock() defer r.Unlock() ptr := 0 @@ -242,7 +246,7 @@ func (r *RecordingAuthorizer) AssertActor(t *testing.T, actor rbac.Subject, did // Finished all assertions return } - if call.Actor.ID == actor.ID { + if call.Actor.ID == id { action, object := did[ptr].Action, did[ptr].Object assert.Equalf(t, action, call.Action, "assert action %d", ptr) assert.Equalf(t, object, call.Object, "assert object %d", ptr) diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index ee11b7ea95edf..52a54df80532a 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -432,6 +432,25 @@ var ( }), Scope: rbac.ScopeAll, }.WithCachedASTValue() + + subjectFileReader = rbac.Subject{ + Type: rbac.SubjectTypeFileReader, + FriendlyName: "Can Read All Files", + // Arbitrary uuid to have a unique ID for this subject. + ID: rbac.SubjectTypeFileReaderID, + Roles: rbac.Roles([]rbac.Role{ + { + Identifier: rbac.RoleIdentifier{Name: "file-reader"}, + DisplayName: "FileReader", + Site: rbac.Permissions(map[string][]policy.Action{ + rbac.ResourceFile.Type: {policy.ActionRead}, + }), + Org: map[string][]rbac.Permission{}, + User: []rbac.Permission{}, + }, + }), + Scope: rbac.ScopeAll, + }.WithCachedASTValue() ) // AsProvisionerd returns a context with an actor that has permissions required @@ -498,6 +517,10 @@ func AsPrebuildsOrchestrator(ctx context.Context) context.Context { return As(ctx, subjectPrebuildsOrchestrator) } +func AsFileReader(ctx context.Context) context.Context { + return As(ctx, subjectFileReader) +} + var AsRemoveActor = rbac.Subject{ ID: "remove-actor", } diff --git a/coderd/files/cache.go b/coderd/files/cache.go index 92b8ea33ed52f..484507d2ac5b0 100644 --- a/coderd/files/cache.go +++ b/coderd/files/cache.go @@ -13,33 +13,41 @@ import ( archivefs "github.com/coder/coder/v2/archive/fs" "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/database/dbauthz" + "github.com/coder/coder/v2/coderd/rbac" + "github.com/coder/coder/v2/coderd/rbac/policy" "github.com/coder/coder/v2/coderd/util/lazy" ) // NewFromStore returns a file cache that will fetch files from the provided // database. -func NewFromStore(store database.Store, registerer prometheus.Registerer) *Cache { - fetch := func(ctx context.Context, fileID uuid.UUID) (cacheEntryValue, error) { - file, err := store.GetFileByID(ctx, fileID) +func NewFromStore(store database.Store, registerer prometheus.Registerer, authz rbac.Authorizer) *Cache { + fetch := func(ctx context.Context, fileID uuid.UUID) (CacheEntryValue, error) { + // Make sure the read does not fail due to authorization issues. + // Authz is checked on the Acquire call, so this is safe. + //nolint:gocritic + file, err := store.GetFileByID(dbauthz.AsFileReader(ctx), fileID) if err != nil { - return cacheEntryValue{}, xerrors.Errorf("failed to read file from database: %w", err) + return CacheEntryValue{}, xerrors.Errorf("failed to read file from database: %w", err) } content := bytes.NewBuffer(file.Data) - return cacheEntryValue{ - FS: archivefs.FromTarReader(content), - size: int64(content.Len()), + return CacheEntryValue{ + Object: file.RBACObject(), + FS: archivefs.FromTarReader(content), + Size: int64(content.Len()), }, nil } - return New(fetch, registerer) + return New(fetch, registerer, authz) } -func New(fetch fetcher, registerer prometheus.Registerer) *Cache { +func New(fetch fetcher, registerer prometheus.Registerer, authz rbac.Authorizer) *Cache { return (&Cache{ lock: sync.Mutex{}, data: make(map[uuid.UUID]*cacheEntry), fetcher: fetch, + authz: authz, }).registerMetrics(registerer) } @@ -101,6 +109,7 @@ type Cache struct { lock sync.Mutex data map[uuid.UUID]*cacheEntry fetcher + authz rbac.Authorizer // metrics cacheMetrics @@ -117,18 +126,19 @@ type cacheMetrics struct { totalCacheSize prometheus.Counter } -type cacheEntryValue struct { +type CacheEntryValue struct { fs.FS - size int64 + Object rbac.Object + Size int64 } type cacheEntry struct { // refCount must only be accessed while the Cache lock is held. refCount int - value *lazy.ValueWithError[cacheEntryValue] + value *lazy.ValueWithError[CacheEntryValue] } -type fetcher func(context.Context, uuid.UUID) (cacheEntryValue, error) +type fetcher func(context.Context, uuid.UUID) (CacheEntryValue, error) // Acquire will load the fs.FS for the given file. It guarantees that parallel // calls for the same fileID will only result in one fetch, and that parallel @@ -146,22 +156,33 @@ func (c *Cache) Acquire(ctx context.Context, fileID uuid.UUID) (fs.FS, error) { c.Release(fileID) return nil, err } + + subject, ok := dbauthz.ActorFromContext(ctx) + if !ok { + return nil, dbauthz.ErrNoActor + } + // Always check the caller can actually read the file. + if err := c.authz.Authorize(ctx, subject, policy.ActionRead, it.Object); err != nil { + c.Release(fileID) + return nil, err + } + return it.FS, err } -func (c *Cache) prepare(ctx context.Context, fileID uuid.UUID) *lazy.ValueWithError[cacheEntryValue] { +func (c *Cache) prepare(ctx context.Context, fileID uuid.UUID) *lazy.ValueWithError[CacheEntryValue] { c.lock.Lock() defer c.lock.Unlock() entry, ok := c.data[fileID] if !ok { - value := lazy.NewWithError(func() (cacheEntryValue, error) { + value := lazy.NewWithError(func() (CacheEntryValue, error) { val, err := c.fetcher(ctx, fileID) // Always add to the cache size the bytes of the file loaded. if err == nil { - c.currentCacheSize.Add(float64(val.size)) - c.totalCacheSize.Add(float64(val.size)) + c.currentCacheSize.Add(float64(val.Size)) + c.totalCacheSize.Add(float64(val.Size)) } return val, err @@ -206,7 +227,7 @@ func (c *Cache) Release(fileID uuid.UUID) { ev, err := entry.value.Load() if err == nil { - c.currentCacheSize.Add(-1 * float64(ev.size)) + c.currentCacheSize.Add(-1 * float64(ev.Size)) } delete(c.data, fileID) diff --git a/coderd/files/cache_internal_test.go b/coderd/files/cache_test.go similarity index 58% rename from coderd/files/cache_internal_test.go rename to coderd/files/cache_test.go index 6ad84185b44b6..469520b4139fe 100644 --- a/coderd/files/cache_internal_test.go +++ b/coderd/files/cache_test.go @@ -1,4 +1,4 @@ -package files +package files_test import ( "context" @@ -12,28 +12,114 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" + "cdr.dev/slog/sloggers/slogtest" + "github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/coderd/coderdtest/promhelp" + "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/files" + "github.com/coder/coder/v2/coderd/rbac" + "github.com/coder/coder/v2/coderd/rbac/policy" "github.com/coder/coder/v2/testutil" ) +// nolint:paralleltest,tparallel // Serially testing is easier +func TestCacheRBAC(t *testing.T) { + t.Parallel() + + db, cache, rec := cacheAuthzSetup(t) + ctx := testutil.Context(t, testutil.WaitMedium) + + file := dbgen.File(t, db, database.File{}) + + nobodyID := uuid.New() + nobody := dbauthz.As(ctx, rbac.Subject{ + ID: nobodyID.String(), + Roles: rbac.Roles{}, + Scope: rbac.ScopeAll, + }) + + userID := uuid.New() + userReader := dbauthz.As(ctx, rbac.Subject{ + ID: userID.String(), + Roles: rbac.Roles{ + must(rbac.RoleByName(rbac.RoleTemplateAdmin())), + }, + Scope: rbac.ScopeAll, + }) + + //nolint:gocritic // Unit testing + cacheReader := dbauthz.AsFileReader(ctx) + + t.Run("NoRolesOpen", func(t *testing.T) { + // Ensure start is clean + require.Equal(t, 0, cache.Count()) + rec.Reset() + + _, err := cache.Acquire(nobody, file.ID) + require.Error(t, err) + require.True(t, rbac.IsUnauthorizedError(err)) + + // Ensure that the cache is empty + require.Equal(t, 0, cache.Count()) + + // Check the assertions + rec.AssertActorID(t, nobodyID.String(), rec.Pair(policy.ActionRead, file)) + rec.AssertActorID(t, rbac.SubjectTypeFileReaderID, rec.Pair(policy.ActionRead, file)) + }) + + t.Run("CacheHasFile", func(t *testing.T) { + rec.Reset() + require.Equal(t, 0, cache.Count()) + + // Read the file with a file reader to put it into the cache. + _, err := cache.Acquire(cacheReader, file.ID) + require.NoError(t, err) + require.Equal(t, 1, cache.Count()) + + // "nobody" should not be able to read the file. + _, err = cache.Acquire(nobody, file.ID) + require.Error(t, err) + require.True(t, rbac.IsUnauthorizedError(err)) + require.Equal(t, 1, cache.Count()) + + // UserReader can + _, err = cache.Acquire(userReader, file.ID) + require.NoError(t, err) + require.Equal(t, 1, cache.Count()) + + cache.Release(file.ID) + cache.Release(file.ID) + require.Equal(t, 0, cache.Count()) + + rec.AssertActorID(t, nobodyID.String(), rec.Pair(policy.ActionRead, file)) + rec.AssertActorID(t, rbac.SubjectTypeFileReaderID, rec.Pair(policy.ActionRead, file)) + rec.AssertActorID(t, userID.String(), rec.Pair(policy.ActionRead, file)) + }) +} + func cachePromMetricName(metric string) string { return "coderd_file_cache_" + metric } func TestConcurrency(t *testing.T) { t.Parallel() + //nolint:gocritic // Unit testing + ctx := dbauthz.AsFileReader(t.Context()) const fileSize = 10 emptyFS := afero.NewIOFS(afero.NewReadOnlyFs(afero.NewMemMapFs())) var fetches atomic.Int64 reg := prometheus.NewRegistry() - c := New(func(_ context.Context, _ uuid.UUID) (cacheEntryValue, error) { + c := files.New(func(_ context.Context, _ uuid.UUID) (files.CacheEntryValue, error) { fetches.Add(1) // Wait long enough before returning to make sure that all of the goroutines // will be waiting in line, ensuring that no one duplicated a fetch. time.Sleep(testutil.IntervalMedium) - return cacheEntryValue{FS: emptyFS, size: fileSize}, nil - }, reg) + return files.CacheEntryValue{FS: emptyFS, Size: fileSize}, nil + }, reg, &coderdtest.FakeAuthorizer{}) batches := 1000 groups := make([]*errgroup.Group, 0, batches) @@ -51,7 +137,7 @@ func TestConcurrency(t *testing.T) { g.Go(func() error { // We don't bother to Release these references because the Cache will be // released at the end of the test anyway. - _, err := c.Acquire(t.Context(), id) + _, err := c.Acquire(ctx, id) return err }) } @@ -74,16 +160,18 @@ func TestConcurrency(t *testing.T) { func TestRelease(t *testing.T) { t.Parallel() + //nolint:gocritic // Unit testing + ctx := dbauthz.AsFileReader(t.Context()) const fileSize = 10 emptyFS := afero.NewIOFS(afero.NewReadOnlyFs(afero.NewMemMapFs())) reg := prometheus.NewRegistry() - c := New(func(_ context.Context, _ uuid.UUID) (cacheEntryValue, error) { - return cacheEntryValue{ + c := files.New(func(_ context.Context, _ uuid.UUID) (files.CacheEntryValue, error) { + return files.CacheEntryValue{ FS: emptyFS, - size: fileSize, + Size: fileSize, }, nil - }, reg) + }, reg, &coderdtest.FakeAuthorizer{}) batches := 100 ids := make([]uuid.UUID, 0, batches) @@ -95,7 +183,7 @@ func TestRelease(t *testing.T) { batchSize := 10 for openedIdx, id := range ids { for batchIdx := range batchSize { - it, err := c.Acquire(t.Context(), id) + it, err := c.Acquire(ctx, id) require.NoError(t, err) require.Equal(t, emptyFS, it) @@ -112,7 +200,7 @@ func TestRelease(t *testing.T) { } // Make sure cache is fully loaded - require.Equal(t, len(c.data), batches) + require.Equal(t, c.Count(), batches) // Now release all of the references for closedIdx, id := range ids { @@ -136,7 +224,7 @@ func TestRelease(t *testing.T) { } // ...and make sure that the cache has emptied itself. - require.Equal(t, len(c.data), 0) + require.Equal(t, c.Count(), 0) // Verify all the counts & metrics are correct. // All existing files are closed @@ -150,3 +238,29 @@ func TestRelease(t *testing.T) { require.Equal(t, batches, promhelp.CounterValue(t, reg, cachePromMetricName("open_files_total"), nil)) require.Equal(t, batches*batchSize, promhelp.CounterValue(t, reg, cachePromMetricName("open_file_refs_total"), nil)) } + +func cacheAuthzSetup(t *testing.T) (database.Store, *files.Cache, *coderdtest.RecordingAuthorizer) { + t.Helper() + + logger := slogtest.Make(t, &slogtest.Options{}) + reg := prometheus.NewRegistry() + + db, _ := dbtestutil.NewDB(t) + authz := rbac.NewAuthorizer(reg) + rec := &coderdtest.RecordingAuthorizer{ + Called: nil, + Wrapped: authz, + } + + // Dbauthz wrap the db + db = dbauthz.New(db, rec, logger, coderdtest.AccessControlStorePointer()) + c := files.NewFromStore(db, reg, rec) + return db, c, rec +} + +func must[T any](t T, err error) T { + if err != nil { + panic(err) + } + return t +} diff --git a/coderd/httpmw/apikey.go b/coderd/httpmw/apikey.go index 4b92848b773e2..a70dc30ec903b 100644 --- a/coderd/httpmw/apikey.go +++ b/coderd/httpmw/apikey.go @@ -47,14 +47,14 @@ func APIKey(r *http.Request) database.APIKey { // UserAuthorizationOptional may return the roles and scope used for // authorization. Depends on the ExtractAPIKey handler. -func UserAuthorizationOptional(r *http.Request) (rbac.Subject, bool) { - return dbauthz.ActorFromContext(r.Context()) +func UserAuthorizationOptional(ctx context.Context) (rbac.Subject, bool) { + return dbauthz.ActorFromContext(ctx) } // UserAuthorization returns the roles and scope used for authorization. Depends // on the ExtractAPIKey handler. -func UserAuthorization(r *http.Request) rbac.Subject { - auth, ok := UserAuthorizationOptional(r) +func UserAuthorization(ctx context.Context) rbac.Subject { + auth, ok := UserAuthorizationOptional(ctx) if !ok { panic("developer error: ExtractAPIKey middleware not provided") } diff --git a/coderd/httpmw/apikey_test.go b/coderd/httpmw/apikey_test.go index 06ee93422bbf9..85f36959476b3 100644 --- a/coderd/httpmw/apikey_test.go +++ b/coderd/httpmw/apikey_test.go @@ -58,7 +58,7 @@ func TestAPIKey(t *testing.T) { assert.NoError(t, err, "actor rego ok") } - auth, ok := httpmw.UserAuthorizationOptional(r) + auth, ok := httpmw.UserAuthorizationOptional(r.Context()) assert.True(t, ok, "httpmw auth ok") if ok { _, err := auth.Roles.Expand() @@ -904,7 +904,7 @@ func TestAPIKey(t *testing.T) { })(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { assertActorOk(t, r) - auth := httpmw.UserAuthorization(r) + auth := httpmw.UserAuthorization(r.Context()) roles, err := auth.Roles.Expand() assert.NoError(t, err, "expand user roles") @@ -968,7 +968,7 @@ func TestAPIKey(t *testing.T) { RedirectToLogin: false, })(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { assertActorOk(t, r) - auth := httpmw.UserAuthorization(r) + auth := httpmw.UserAuthorization(r.Context()) roles, err := auth.Roles.Expand() assert.NoError(t, err, "expand user roles") diff --git a/coderd/httpmw/authorize_test.go b/coderd/httpmw/authorize_test.go index 5d04c5afacdb3..3ee9d92742252 100644 --- a/coderd/httpmw/authorize_test.go +++ b/coderd/httpmw/authorize_test.go @@ -125,7 +125,7 @@ func TestExtractUserRoles(t *testing.T) { }), ) rtr.Get("/", func(_ http.ResponseWriter, r *http.Request) { - roles := httpmw.UserAuthorization(r) + roles := httpmw.UserAuthorization(r.Context()) require.Equal(t, user.ID.String(), roles.ID) require.ElementsMatch(t, expRoles, roles.Roles.Names()) }) diff --git a/coderd/httpmw/ratelimit.go b/coderd/httpmw/ratelimit.go index 932373b5bacd9..ad1ecf3d6bbd9 100644 --- a/coderd/httpmw/ratelimit.go +++ b/coderd/httpmw/ratelimit.go @@ -43,7 +43,7 @@ func RateLimit(count int, window time.Duration) func(http.Handler) http.Handler // Allow Owner to bypass rate limiting for load tests // and automation. - auth := UserAuthorization(r) + auth := UserAuthorization(r.Context()) // We avoid using rbac.Authorizer since rego is CPU-intensive // and undermines the DoS-prevention goal of the rate limiter. diff --git a/coderd/identityprovider/middleware.go b/coderd/identityprovider/middleware.go index 1704ab2270f49..632e5a53c0319 100644 --- a/coderd/identityprovider/middleware.go +++ b/coderd/identityprovider/middleware.go @@ -36,7 +36,7 @@ func authorizeMW(accessURL *url.URL) func(next http.Handler) http.Handler { } app := httpmw.OAuth2ProviderApp(r) - ua := httpmw.UserAuthorization(r) + ua := httpmw.UserAuthorization(r.Context()) // url.Parse() allows empty URLs, which is fine because the origin is not // always set by browsers (or other tools like cURL). If the origin does diff --git a/coderd/parameters.go b/coderd/parameters.go index 48cccc27e6727..c88199956392d 100644 --- a/coderd/parameters.go +++ b/coderd/parameters.go @@ -133,7 +133,7 @@ func (api *API) handleDynamicParameters(listen bool, rw http.ResponseWriter, r * // nolint:gocritic // We need to fetch the templates files for the Terraform // evaluator, and the user likely does not have permission. - fileCtx := dbauthz.AsProvisionerd(ctx) + fileCtx := dbauthz.AsFileReader(ctx) fileID, err := api.Database.GetFileIDByTemplateVersionID(fileCtx, templateVersion.ID) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ diff --git a/coderd/rbac/authz.go b/coderd/rbac/authz.go index 9e3a0536279ae..a7f77d57ab253 100644 --- a/coderd/rbac/authz.go +++ b/coderd/rbac/authz.go @@ -74,6 +74,11 @@ const ( SubjectTypeSystemRestricted SubjectType = "system_restricted" SubjectTypeNotifier SubjectType = "notifier" SubjectTypeSubAgentAPI SubjectType = "sub_agent_api" + SubjectTypeFileReader SubjectType = "file_reader" +) + +const ( + SubjectTypeFileReaderID = "acbf0be6-6fed-47b6-8c43-962cb5cab994" ) // Subject is a struct that contains all the elements of a subject in an rbac diff --git a/coderd/roles.go b/coderd/roles.go index ed650f41fd6c9..3814cd36d29ad 100644 --- a/coderd/roles.go +++ b/coderd/roles.go @@ -26,7 +26,7 @@ import ( // @Router /users/roles [get] func (api *API) AssignableSiteRoles(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() - actorRoles := httpmw.UserAuthorization(r) + actorRoles := httpmw.UserAuthorization(r.Context()) if !api.Authorize(r, policy.ActionRead, rbac.ResourceAssignRole) { httpapi.Forbidden(rw) return @@ -59,7 +59,7 @@ func (api *API) AssignableSiteRoles(rw http.ResponseWriter, r *http.Request) { func (api *API) assignableOrgRoles(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() organization := httpmw.OrganizationParam(r) - actorRoles := httpmw.UserAuthorization(r) + actorRoles := httpmw.UserAuthorization(r.Context()) if !api.Authorize(r, policy.ActionRead, rbac.ResourceAssignOrgRole.InOrg(organization.ID)) { httpapi.ResourceNotFound(rw) diff --git a/coderd/users.go b/coderd/users.go index ad1ba8a018743..e2f6fd79c7d75 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -525,7 +525,7 @@ func (api *API) deleteUser(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() auditor := *api.Auditor.Load() user := httpmw.UserParam(r) - auth := httpmw.UserAuthorization(r) + auth := httpmw.UserAuthorization(r.Context()) aReq, commitAudit := audit.InitRequest[database.User](rw, &audit.RequestParams{ Audit: auditor, Log: api.Logger, diff --git a/enterprise/coderd/provisionerdaemons.go b/enterprise/coderd/provisionerdaemons.go index 30f4ddd66d91c..c8304952781d1 100644 --- a/enterprise/coderd/provisionerdaemons.go +++ b/enterprise/coderd/provisionerdaemons.go @@ -133,7 +133,7 @@ func (p *provisionerDaemonAuth) authorize(r *http.Request, org database.Organiza tags: tags, }, nil } - ua := httpmw.UserAuthorization(r) + ua := httpmw.UserAuthorization(r.Context()) err = p.authorizer.Authorize(ctx, ua, policy.ActionCreate, rbac.ResourceProvisionerDaemon.InOrg(org.ID)) if err != nil { return provisiionerDaemonAuthResponse{}, xerrors.New("user unauthorized")
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: