Skip to content

Commit 2443e19

Browse files
chore(coderd/rbac): add Action{Create,Delete}Agent to ResourceWorkspace
This PR adds two new actions: `ActionCreateAgent` and `ActionDeleteAgent`. The former is used in this PR for `InsertWorkspaceAgent`, with the latter to be used in a follow-up PR for Dev Container Agents. A note has been left in `dbauthz.go` for `InsertWorkspaceAgent` detailing why it is allowed to insert a workspace agent when no workspace could be found.
1 parent e5758a1 commit 2443e19

File tree

19 files changed

+252
-11
lines changed

19 files changed

+252
-11
lines changed

coderd/apidoc/docs.go

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

coderd/apidoc/swagger.json

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

coderd/database/dbauthz/dbauthz.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ var (
177177
// Unsure why provisionerd needs update and read personal
178178
rbac.ResourceUser.Type: {policy.ActionRead, policy.ActionReadPersonal, policy.ActionUpdatePersonal},
179179
rbac.ResourceWorkspaceDormant.Type: {policy.ActionDelete, policy.ActionRead, policy.ActionUpdate, policy.ActionWorkspaceStop},
180-
rbac.ResourceWorkspace.Type: {policy.ActionDelete, policy.ActionRead, policy.ActionUpdate, policy.ActionWorkspaceStart, policy.ActionWorkspaceStop},
180+
rbac.ResourceWorkspace.Type: {policy.ActionDelete, policy.ActionRead, policy.ActionUpdate, policy.ActionWorkspaceStart, policy.ActionWorkspaceStop, policy.ActionCreateAgent},
181181
rbac.ResourceApiKey.Type: {policy.WildcardSymbol},
182182
// When org scoped provisioner credentials are implemented,
183183
// this can be reduced to read a specific org.
@@ -338,7 +338,7 @@ var (
338338
rbac.ResourceProvisionerDaemon.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate},
339339
rbac.ResourceUser.Type: rbac.ResourceUser.AvailableActions(),
340340
rbac.ResourceWorkspaceDormant.Type: {policy.ActionUpdate, policy.ActionDelete, policy.ActionWorkspaceStop},
341-
rbac.ResourceWorkspace.Type: {policy.ActionUpdate, policy.ActionDelete, policy.ActionWorkspaceStart, policy.ActionWorkspaceStop, policy.ActionSSH},
341+
rbac.ResourceWorkspace.Type: {policy.ActionUpdate, policy.ActionDelete, policy.ActionWorkspaceStart, policy.ActionWorkspaceStop, policy.ActionSSH, policy.ActionCreateAgent, policy.ActionDeleteAgent},
342342
rbac.ResourceWorkspaceProxy.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete},
343343
rbac.ResourceDeploymentConfig.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete},
344344
rbac.ResourceNotificationMessage.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
@@ -3162,6 +3162,10 @@ func (q *querier) GetWorkspaceByOwnerIDAndName(ctx context.Context, arg database
31623162
return fetch(q.log, q.auth, q.db.GetWorkspaceByOwnerIDAndName)(ctx, arg)
31633163
}
31643164

3165+
func (q *querier) GetWorkspaceByResourceID(ctx context.Context, resourceID uuid.UUID) (database.Workspace, error) {
3166+
return fetch(q.log, q.auth, q.db.GetWorkspaceByResourceID)(ctx, resourceID)
3167+
}
3168+
31653169
func (q *querier) GetWorkspaceByWorkspaceAppID(ctx context.Context, workspaceAppID uuid.UUID) (database.Workspace, error) {
31663170
return fetch(q.log, q.auth, q.db.GetWorkspaceByWorkspaceAppID)(ctx, workspaceAppID)
31673171
}
@@ -3700,9 +3704,24 @@ func (q *querier) InsertWorkspace(ctx context.Context, arg database.InsertWorksp
37003704
}
37013705

37023706
func (q *querier) InsertWorkspaceAgent(ctx context.Context, arg database.InsertWorkspaceAgentParams) (database.WorkspaceAgent, error) {
3703-
if err := q.authorizeContext(ctx, policy.ActionCreate, rbac.ResourceSystem); err != nil {
3707+
// NOTE(DanielleMaywood):
3708+
// Currently, the only way to link a Resource back to a Workspace is by following this chain:
3709+
//
3710+
// WorkspaceResource -> WorkspaceBuild -> Workspace
3711+
//
3712+
// It is possible for this function to be called without there existing
3713+
// a `WorkspaceBuild` to link back to. This means that we want to allow
3714+
// execution to continue if there isn't a workspace found to allow this
3715+
// behavior to continue.
3716+
workspace, err := q.db.GetWorkspaceByResourceID(ctx, arg.ResourceID)
3717+
if err != nil && !errors.Is(err, sql.ErrNoRows) {
37043718
return database.WorkspaceAgent{}, err
37053719
}
3720+
3721+
if err := q.authorizeContext(ctx, policy.ActionCreateAgent, workspace); err != nil {
3722+
return database.WorkspaceAgent{}, err
3723+
}
3724+
37063725
return q.db.InsertWorkspaceAgent(ctx, arg)
37073726
}
37083727

coderd/database/dbauthz/dbauthz_test.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1925,6 +1925,22 @@ func (s *MethodTestSuite) TestWorkspace() {
19251925
})
19261926
check.Args(ws.ID).Asserts(ws, policy.ActionRead)
19271927
}))
1928+
s.Run("GetWorkspaceByResourceID", s.Subtest(func(db database.Store, check *expects) {
1929+
u := dbgen.User(s.T(), db, database.User{})
1930+
o := dbgen.Organization(s.T(), db, database.Organization{})
1931+
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{Type: database.ProvisionerJobTypeWorkspaceBuild})
1932+
tpl := dbgen.Template(s.T(), db, database.Template{CreatedBy: u.ID, OrganizationID: o.ID})
1933+
tv := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
1934+
TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true},
1935+
JobID: j.ID,
1936+
OrganizationID: o.ID,
1937+
CreatedBy: u.ID,
1938+
})
1939+
ws := dbgen.Workspace(s.T(), db, database.WorkspaceTable{OwnerID: u.ID, TemplateID: tpl.ID, OrganizationID: o.ID})
1940+
_ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: j.ID, TemplateVersionID: tv.ID})
1941+
res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: j.ID})
1942+
check.Args(res.ID).Asserts(ws, policy.ActionRead)
1943+
}))
19281944
s.Run("GetWorkspaces", s.Subtest(func(_ database.Store, check *expects) {
19291945
// No asserts here because SQLFilter.
19301946
check.Args(database.GetWorkspacesParams{}).Asserts()
@@ -4016,12 +4032,24 @@ func (s *MethodTestSuite) TestSystemFunctions() {
40164032
Returns(slice.New(a, b))
40174033
}))
40184034
s.Run("InsertWorkspaceAgent", s.Subtest(func(db database.Store, check *expects) {
4019-
dbtestutil.DisableForeignKeysAndTriggers(s.T(), db)
4035+
u := dbgen.User(s.T(), db, database.User{})
4036+
o := dbgen.Organization(s.T(), db, database.Organization{})
4037+
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{Type: database.ProvisionerJobTypeWorkspaceBuild})
4038+
tpl := dbgen.Template(s.T(), db, database.Template{CreatedBy: u.ID, OrganizationID: o.ID})
4039+
tv := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
4040+
TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true},
4041+
JobID: j.ID,
4042+
OrganizationID: o.ID,
4043+
CreatedBy: u.ID,
4044+
})
4045+
ws := dbgen.Workspace(s.T(), db, database.WorkspaceTable{OwnerID: u.ID, TemplateID: tpl.ID, OrganizationID: o.ID})
4046+
_ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: j.ID, TemplateVersionID: tv.ID})
4047+
res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: j.ID})
40204048
check.Args(database.InsertWorkspaceAgentParams{
40214049
ID: uuid.New(),
40224050
Name: "dev",
40234051
APIKeyScope: database.AgentKeyScopeEnumAll,
4024-
}).Asserts(rbac.ResourceSystem, policy.ActionCreate)
4052+
}).Asserts(res, policy.ActionCreateAgent)
40254053
}))
40264054
s.Run("InsertWorkspaceApp", s.Subtest(func(db database.Store, check *expects) {
40274055
dbtestutil.DisableForeignKeysAndTriggers(s.T(), db)

coderd/database/dbmem/dbmem.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8035,6 +8035,33 @@ func (q *FakeQuerier) GetWorkspaceByOwnerIDAndName(_ context.Context, arg databa
80358035
return database.Workspace{}, sql.ErrNoRows
80368036
}
80378037

8038+
func (q *FakeQuerier) GetWorkspaceByResourceID(ctx context.Context, resourceID uuid.UUID) (database.Workspace, error) {
8039+
q.mutex.RLock()
8040+
defer q.mutex.RUnlock()
8041+
8042+
for _, resource := range q.workspaceResources {
8043+
if resource.ID != resourceID {
8044+
continue
8045+
}
8046+
8047+
for _, build := range q.workspaceBuilds {
8048+
if build.JobID != resource.JobID {
8049+
continue
8050+
}
8051+
8052+
for _, workspace := range q.workspaces {
8053+
if workspace.ID != build.WorkspaceID {
8054+
continue
8055+
}
8056+
8057+
return q.extendWorkspace(workspace), nil
8058+
}
8059+
}
8060+
}
8061+
8062+
return database.Workspace{}, sql.ErrNoRows
8063+
}
8064+
80388065
func (q *FakeQuerier) GetWorkspaceByWorkspaceAppID(_ context.Context, workspaceAppID uuid.UUID) (database.Workspace, error) {
80398066
if err := validateDatabaseType(workspaceAppID); err != nil {
80408067
return database.Workspace{}, err

coderd/database/dbmetrics/querymetrics.go

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

coderd/database/dbmock/dbmock.go

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

coderd/database/querier.go

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

coderd/database/queries.sql.go

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

coderd/database/queries/workspaces.sql

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,30 @@ WHERE
88
LIMIT
99
1;
1010

11+
-- name: GetWorkspaceByResourceID :one
12+
SELECT
13+
*
14+
FROM
15+
workspaces_expanded as workspaces
16+
WHERE
17+
workspaces.id = (
18+
SELECT
19+
workspace_id
20+
FROM
21+
workspace_builds
22+
WHERE
23+
workspace_builds.job_id = (
24+
SELECT
25+
job_id
26+
FROM
27+
workspace_resources
28+
WHERE
29+
workspace_resources.id = @resource_id
30+
)
31+
)
32+
LIMIT
33+
1;
34+
1135
-- name: GetWorkspaceByWorkspaceAppID :one
1236
SELECT
1337
*

0 commit comments

Comments
 (0)