Skip to content

Commit af5ddb1

Browse files
committed
WIP: org-scope for frobulators
Signed-off-by: Danny Kopping <danny@coder.com>
1 parent 9e749bc commit af5ddb1

22 files changed

+469
-256
lines changed

coderd/coderd.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,17 @@ func New(options *Options) *API {
909909
})
910910
})
911911
})
912+
r.Route("/frobulators", func(r chi.Router) {
913+
r.Use(apiKeyMiddleware)
914+
r.Route("/{user}", func(r chi.Router) {
915+
r.Use(
916+
httpmw.ExtractUserParam(options.Database),
917+
)
918+
r.Get("/", api.listFrobulators)
919+
r.Post("/", api.createFrobulator)
920+
r.Delete("/{id}", api.deleteFrobulator)
921+
})
922+
})
912923
})
913924
})
914925
r.Route("/templates", func(r chi.Router) {
@@ -1257,17 +1268,6 @@ func New(options *Options) *API {
12571268
})
12581269
r.Get("/dispatch-methods", api.notificationDispatchMethods)
12591270
})
1260-
r.Route("/frobulators", func(r chi.Router) {
1261-
r.Use(apiKeyMiddleware)
1262-
r.Get("/", api.listAllFrobulators)
1263-
r.Route("/{user}", func(r chi.Router) {
1264-
r.Use(
1265-
httpmw.ExtractUserParam(options.Database),
1266-
)
1267-
r.Get("/", api.listUserFrobulators)
1268-
r.Post("/", api.createFrobulator)
1269-
})
1270-
})
12711271
})
12721272

12731273
if options.SwaggerEndpoint {

coderd/database/dbauthz/dbauthz.go

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,13 @@ func (q *querier) DeleteExternalAuthLink(ctx context.Context, arg database.Delet
10591059
}, q.db.DeleteExternalAuthLink)(ctx, arg)
10601060
}
10611061

1062+
func (q *querier) DeleteFrobulator(ctx context.Context, args database.DeleteFrobulatorParams) error {
1063+
if err := q.authorizeContext(ctx, policy.ActionDelete, rbac.ResourceFrobulator.WithID(args.ID).WithOwner(args.UserID.String()).InOrg(args.OrgID)); err != nil {
1064+
return err
1065+
}
1066+
return q.db.DeleteFrobulator(ctx, args)
1067+
}
1068+
10621069
func (q *querier) DeleteGitSSHKey(ctx context.Context, userID uuid.UUID) error {
10631070
return fetchAndExec(q.log, q.auth, policy.ActionUpdatePersonal, q.db.GetGitSSHKey, q.db.DeleteGitSSHKey)(ctx, userID)
10641071
}
@@ -1298,13 +1305,6 @@ func (q *querier) GetActiveWorkspaceBuildsByTemplateID(ctx context.Context, temp
12981305
return q.db.GetActiveWorkspaceBuildsByTemplateID(ctx, templateID)
12991306
}
13001307

1301-
func (q *querier) GetAllFrobulators(ctx context.Context) ([]database.Frobulator, error) {
1302-
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceFrobulator); err != nil {
1303-
return nil, err
1304-
}
1305-
return q.db.GetAllFrobulators(ctx)
1306-
}
1307-
13081308
func (q *querier) GetAllTailnetAgents(ctx context.Context) ([]database.TailnetAgent, error) {
13091309
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceTailnetCoordinator); err != nil {
13101310
return []database.TailnetAgent{}, err
@@ -1464,6 +1464,13 @@ func (q *querier) GetFileTemplates(ctx context.Context, fileID uuid.UUID) ([]dat
14641464
return q.db.GetFileTemplates(ctx, fileID)
14651465
}
14661466

1467+
func (q *querier) GetFrobulators(ctx context.Context, arg database.GetFrobulatorsParams) ([]database.Frobulator, error) {
1468+
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceFrobulator.WithOwner(arg.UserID.String()).InOrg(arg.OrgID)); err != nil {
1469+
return nil, err
1470+
}
1471+
return q.db.GetFrobulators(ctx, arg)
1472+
}
1473+
14671474
func (q *querier) GetGitSSHKey(ctx context.Context, userID uuid.UUID) (database.GitSSHKey, error) {
14681475
return fetchWithAction(q.log, q.auth, policy.ActionReadPersonal, q.db.GetGitSSHKey)(ctx, userID)
14691476
}
@@ -2172,19 +2179,6 @@ func (q *querier) GetUserCount(ctx context.Context) (int64, error) {
21722179
return q.db.GetUserCount(ctx)
21732180
}
21742181

2175-
func (q *querier) GetUserFrobulators(ctx context.Context, userID uuid.UUID) ([]database.Frobulator, error) {
2176-
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetUserFrobulators)(ctx, userID)
2177-
// Alternatively: just check if you can read *a* Frobulator owned by your ID.
2178-
// This is technically incorrect, as if Frobulators later become org-scoped, this will no longer be correct!
2179-
// But it's **much, much faster** .
2180-
/*
2181-
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceFrobulator.WithOwner(userID.String())); err != nil {
2182-
return nil, err
2183-
}
2184-
return q.db.GetUserFrobulators(ctx, userID)
2185-
*/
2186-
}
2187-
21882182
func (q *querier) GetUserLatencyInsights(ctx context.Context, arg database.GetUserLatencyInsightsParams) ([]database.GetUserLatencyInsightsRow, error) {
21892183
// Used by insights endpoints. Need to check both for auditors and for regular users with template acl perms.
21902184
if err := q.authorizeContext(ctx, policy.ActionViewInsights, rbac.ResourceTemplate); err != nil {
@@ -2705,9 +2699,9 @@ func (q *querier) InsertFile(ctx context.Context, arg database.InsertFileParams)
27052699
return insert(q.log, q.auth, rbac.ResourceFile.WithOwner(arg.CreatedBy.String()), q.db.InsertFile)(ctx, arg)
27062700
}
27072701

2708-
func (q *querier) InsertFrobulator(ctx context.Context, arg database.InsertFrobulatorParams) error {
2709-
if err := q.authorizeContext(ctx, policy.ActionCreate, rbac.ResourceFrobulator.WithOwner(arg.UserID.String())); err != nil {
2710-
return err
2702+
func (q *querier) InsertFrobulator(ctx context.Context, arg database.InsertFrobulatorParams) (database.Frobulator, error) {
2703+
if err := q.authorizeContext(ctx, policy.ActionCreate, rbac.ResourceFrobulator.WithOwner(arg.UserID.String()).InOrg(arg.OrgID)); err != nil {
2704+
return database.Frobulator{}, err
27112705
}
27122706

27132707
return q.db.InsertFrobulator(ctx, arg)

coderd/database/dbauthz/dbauthz_test.go

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2751,18 +2751,40 @@ func (s *MethodTestSuite) TestNotifications() {
27512751
}
27522752

27532753
func (s *MethodTestSuite) TestFrobulators() {
2754-
s.Run("GetAllFrobulators", s.Subtest(func(db database.Store, check *expects) {
2755-
check.Args().Asserts(rbac.ResourceFrobulator, policy.ActionRead)
2756-
}))
2757-
s.Run("GetUserFrobulators", s.Subtest(func(db database.Store, check *expects) {
2754+
s.Run("GetFrobulators", s.Subtest(func(db database.Store, check *expects) {
27582755
user := dbgen.User(s.T(), db, database.User{})
2759-
check.Args(user.ID).Asserts(rbac.ResourceFrobulator.WithOwner(user.ID.String()), policy.ActionRead)
2756+
org := dbgen.Organization(s.T(), db, database.Organization{})
2757+
check.Args(database.GetFrobulatorsParams{
2758+
UserID: user.ID,
2759+
OrgID: org.ID,
2760+
}).Asserts(
2761+
rbac.ResourceFrobulator.
2762+
WithOwner(user.ID.String()).
2763+
InOrg(org.ID),
2764+
policy.ActionRead,
2765+
)
27602766
}))
27612767
s.Run("InsertFrobulator", s.Subtest(func(db database.Store, check *expects) {
27622768
user := dbgen.User(s.T(), db, database.User{})
2769+
org := dbgen.Organization(s.T(), db, database.Organization{})
27632770
check.Args(database.InsertFrobulatorParams{
27642771
UserID: user.ID,
2765-
}).Asserts(rbac.ResourceFrobulator.WithOwner(user.ID.String()), policy.ActionCreate)
2772+
OrgID: org.ID,
2773+
}).Asserts(rbac.ResourceFrobulator.WithOwner(user.ID.String()).InOrg(org.ID), policy.ActionCreate)
2774+
}))
2775+
s.Run("DeleteFrobulator", s.Subtest(func(db database.Store, check *expects) {
2776+
user := dbgen.User(s.T(), db, database.User{})
2777+
org := dbgen.Organization(s.T(), db, database.Organization{})
2778+
frob := dbgen.Frobulator(s.T(), db, database.Frobulator{
2779+
UserID: user.ID,
2780+
OrgID: org.ID,
2781+
ModelNumber: "warhead-1",
2782+
})
2783+
check.Args(database.DeleteFrobulatorParams{
2784+
ID: frob.ID,
2785+
UserID: frob.UserID,
2786+
OrgID: frob.OrgID,
2787+
}).Asserts(rbac.ResourceFrobulator.WithOwner(frob.UserID.String()).InOrg(frob.OrgID).WithID(frob.ID), policy.ActionDelete)
27662788
}))
27672789
}
27682790

coderd/database/dbgen/dbgen.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,16 @@ func CustomRole(t testing.TB, db database.Store, seed database.CustomRole) datab
892892
return role
893893
}
894894

895+
func Frobulator(t testing.TB, db database.Store, orig database.Frobulator) database.Frobulator {
896+
frob, err := db.InsertFrobulator(genCtx, database.InsertFrobulatorParams{
897+
UserID: orig.UserID,
898+
OrgID: orig.OrgID,
899+
ModelNumber: orig.ModelNumber,
900+
})
901+
require.NoError(t, err, "insert frobulator")
902+
return frob
903+
}
904+
895905
func must[V any](v V, err error) V {
896906
if err != nil {
897907
panic(err)

coderd/database/dbmem/dbmem.go

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,6 +1485,20 @@ func (q *FakeQuerier) DeleteExternalAuthLink(_ context.Context, arg database.Del
14851485
return sql.ErrNoRows
14861486
}
14871487

1488+
func (q *FakeQuerier) DeleteFrobulator(_ context.Context, args database.DeleteFrobulatorParams) error {
1489+
q.mutex.Lock()
1490+
defer q.mutex.Unlock()
1491+
1492+
for i, frob := range q.frobulators {
1493+
if frob.ID == args.ID && frob.UserID == args.UserID && frob.OrgID == args.OrgID {
1494+
q.frobulators = append(q.frobulators[:i], q.frobulators[i+1:]...)
1495+
return nil
1496+
}
1497+
}
1498+
1499+
return sql.ErrNoRows
1500+
}
1501+
14881502
func (q *FakeQuerier) DeleteGitSSHKey(_ context.Context, userID uuid.UUID) error {
14891503
q.mutex.Lock()
14901504
defer q.mutex.Unlock()
@@ -2130,13 +2144,6 @@ func (q *FakeQuerier) GetActiveWorkspaceBuildsByTemplateID(ctx context.Context,
21302144
return filteredBuilds, nil
21312145
}
21322146

2133-
func (q *FakeQuerier) GetAllFrobulators(_ context.Context) ([]database.Frobulator, error) {
2134-
q.mutex.RLock()
2135-
defer q.mutex.RUnlock()
2136-
2137-
return q.frobulators, nil
2138-
}
2139-
21402147
func (*FakeQuerier) GetAllTailnetAgents(_ context.Context) ([]database.TailnetAgent, error) {
21412148
return nil, ErrUnimplemented
21422149
}
@@ -2508,6 +2515,27 @@ func (q *FakeQuerier) GetFileTemplates(_ context.Context, id uuid.UUID) ([]datab
25082515
return rows, nil
25092516
}
25102517

2518+
func (q *FakeQuerier) GetFrobulators(_ context.Context, arg database.GetFrobulatorsParams) ([]database.Frobulator, error) {
2519+
err := validateDatabaseType(arg)
2520+
if err != nil {
2521+
return nil, err
2522+
}
2523+
2524+
q.mutex.RLock()
2525+
defer q.mutex.RUnlock()
2526+
2527+
out := make([]database.Frobulator, 0, len(q.frobulators))
2528+
for _, frob := range q.frobulators {
2529+
if frob.UserID != arg.UserID || frob.OrgID != arg.OrgID {
2530+
continue
2531+
}
2532+
2533+
out = append(out, frob)
2534+
}
2535+
2536+
return out, nil
2537+
}
2538+
25112539
func (q *FakeQuerier) GetGitSSHKey(_ context.Context, userID uuid.UUID) (database.GitSSHKey, error) {
25122540
q.mutex.RLock()
25132541
defer q.mutex.RUnlock()
@@ -4865,22 +4893,6 @@ func (q *FakeQuerier) GetUserCount(_ context.Context) (int64, error) {
48654893
return existing, nil
48664894
}
48674895

4868-
func (q *FakeQuerier) GetUserFrobulators(_ context.Context, userID uuid.UUID) ([]database.Frobulator, error) {
4869-
q.mutex.RLock()
4870-
defer q.mutex.RUnlock()
4871-
4872-
out := make([]database.Frobulator, 0, len(q.frobulators))
4873-
for _, frob := range q.frobulators {
4874-
if frob.UserID != userID {
4875-
continue
4876-
}
4877-
4878-
out = append(out, frob)
4879-
}
4880-
4881-
return out, nil
4882-
}
4883-
48844896
func (q *FakeQuerier) GetUserLatencyInsights(_ context.Context, arg database.GetUserLatencyInsightsParams) ([]database.GetUserLatencyInsightsRow, error) {
48854897
err := validateDatabaseType(arg)
48864898
if err != nil {
@@ -6307,23 +6319,25 @@ func (q *FakeQuerier) InsertFile(_ context.Context, arg database.InsertFileParam
63076319
return file, nil
63086320
}
63096321

6310-
func (q *FakeQuerier) InsertFrobulator(_ context.Context, arg database.InsertFrobulatorParams) error {
6322+
func (q *FakeQuerier) InsertFrobulator(_ context.Context, arg database.InsertFrobulatorParams) (database.Frobulator, error) {
63116323
err := validateDatabaseType(arg)
63126324
if err != nil {
6313-
return err
6325+
return database.Frobulator{}, err
63146326
}
63156327

63166328
q.mutex.Lock()
63176329
defer q.mutex.Unlock()
63186330

63196331
// nolint:gosimple // This is fine as it is.
6320-
q.frobulators = append(q.frobulators, database.Frobulator{
6321-
ID: arg.ID,
6332+
frob := database.Frobulator{
6333+
ID: uuid.New(),
63226334
UserID: arg.UserID,
6335+
OrgID: arg.OrgID,
63236336
ModelNumber: arg.ModelNumber,
6324-
})
6337+
}
6338+
q.frobulators = append(q.frobulators, frob)
63256339

6326-
return nil
6340+
return frob, nil
63276341
}
63286342

63296343
func (q *FakeQuerier) InsertGitSSHKey(_ context.Context, arg database.InsertGitSSHKeyParams) (database.GitSSHKey, error) {

coderd/database/dbmetrics/dbmetrics.go

Lines changed: 15 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)