Skip to content

Commit 3e7ff9d

Browse files
chore(coderd/rbac): add Action{Create,Delete}Agent to ResourceWorkspace (#17932)
1 parent d2d2189 commit 3e7ff9d

File tree

19 files changed

+253
-11
lines changed

19 files changed

+253
-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.
@@ -339,7 +339,7 @@ var (
339339
rbac.ResourceProvisionerDaemon.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate},
340340
rbac.ResourceUser.Type: rbac.ResourceUser.AvailableActions(),
341341
rbac.ResourceWorkspaceDormant.Type: {policy.ActionUpdate, policy.ActionDelete, policy.ActionWorkspaceStop},
342-
rbac.ResourceWorkspace.Type: {policy.ActionUpdate, policy.ActionDelete, policy.ActionWorkspaceStart, policy.ActionWorkspaceStop, policy.ActionSSH},
342+
rbac.ResourceWorkspace.Type: {policy.ActionUpdate, policy.ActionDelete, policy.ActionWorkspaceStart, policy.ActionWorkspaceStop, policy.ActionSSH, policy.ActionCreateAgent, policy.ActionDeleteAgent},
343343
rbac.ResourceWorkspaceProxy.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete},
344344
rbac.ResourceDeploymentConfig.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete},
345345
rbac.ResourceNotificationMessage.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
@@ -3180,6 +3180,10 @@ func (q *querier) GetWorkspaceByOwnerIDAndName(ctx context.Context, arg database
31803180
return fetch(q.log, q.auth, q.db.GetWorkspaceByOwnerIDAndName)(ctx, arg)
31813181
}
31823182

3183+
func (q *querier) GetWorkspaceByResourceID(ctx context.Context, resourceID uuid.UUID) (database.Workspace, error) {
3184+
return fetch(q.log, q.auth, q.db.GetWorkspaceByResourceID)(ctx, resourceID)
3185+
}
3186+
31833187
func (q *querier) GetWorkspaceByWorkspaceAppID(ctx context.Context, workspaceAppID uuid.UUID) (database.Workspace, error) {
31843188
return fetch(q.log, q.auth, q.db.GetWorkspaceByWorkspaceAppID)(ctx, workspaceAppID)
31853189
}
@@ -3713,9 +3717,24 @@ func (q *querier) InsertWorkspace(ctx context.Context, arg database.InsertWorksp
37133717
}
37143718

37153719
func (q *querier) InsertWorkspaceAgent(ctx context.Context, arg database.InsertWorkspaceAgentParams) (database.WorkspaceAgent, error) {
3716-
if err := q.authorizeContext(ctx, policy.ActionCreate, rbac.ResourceSystem); err != nil {
3720+
// NOTE(DanielleMaywood):
3721+
// Currently, the only way to link a Resource back to a Workspace is by following this chain:
3722+
//
3723+
// WorkspaceResource -> WorkspaceBuild -> Workspace
3724+
//
3725+
// It is possible for this function to be called without there existing
3726+
// a `WorkspaceBuild` to link back to. This means that we want to allow
3727+
// execution to continue if there isn't a workspace found to allow this
3728+
// behavior to continue.
3729+
workspace, err := q.db.GetWorkspaceByResourceID(ctx, arg.ResourceID)
3730+
if err != nil && !errors.Is(err, sql.ErrNoRows) {
37173731
return database.WorkspaceAgent{}, err
37183732
}
3733+
3734+
if err := q.authorizeContext(ctx, policy.ActionCreateAgent, workspace); err != nil {
3735+
return database.WorkspaceAgent{}, err
3736+
}
3737+
37193738
return q.db.InsertWorkspaceAgent(ctx, arg)
37203739
}
37213740

coderd/database/dbauthz/dbauthz_test.go

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,22 @@ func (s *MethodTestSuite) TestWorkspace() {
19281928
})
19291929
check.Args(ws.ID).Asserts(ws, policy.ActionRead)
19301930
}))
1931+
s.Run("GetWorkspaceByResourceID", s.Subtest(func(db database.Store, check *expects) {
1932+
u := dbgen.User(s.T(), db, database.User{})
1933+
o := dbgen.Organization(s.T(), db, database.Organization{})
1934+
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{Type: database.ProvisionerJobTypeWorkspaceBuild})
1935+
tpl := dbgen.Template(s.T(), db, database.Template{CreatedBy: u.ID, OrganizationID: o.ID})
1936+
tv := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
1937+
TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true},
1938+
JobID: j.ID,
1939+
OrganizationID: o.ID,
1940+
CreatedBy: u.ID,
1941+
})
1942+
ws := dbgen.Workspace(s.T(), db, database.WorkspaceTable{OwnerID: u.ID, TemplateID: tpl.ID, OrganizationID: o.ID})
1943+
_ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: j.ID, TemplateVersionID: tv.ID})
1944+
res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: j.ID})
1945+
check.Args(res.ID).Asserts(ws, policy.ActionRead)
1946+
}))
19311947
s.Run("GetWorkspaces", s.Subtest(func(_ database.Store, check *expects) {
19321948
// No asserts here because SQLFilter.
19331949
check.Args(database.GetWorkspacesParams{}).Asserts()
@@ -4018,12 +4034,25 @@ func (s *MethodTestSuite) TestSystemFunctions() {
40184034
Returns(slice.New(a, b))
40194035
}))
40204036
s.Run("InsertWorkspaceAgent", s.Subtest(func(db database.Store, check *expects) {
4021-
dbtestutil.DisableForeignKeysAndTriggers(s.T(), db)
4037+
u := dbgen.User(s.T(), db, database.User{})
4038+
o := dbgen.Organization(s.T(), db, database.Organization{})
4039+
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{Type: database.ProvisionerJobTypeWorkspaceBuild})
4040+
tpl := dbgen.Template(s.T(), db, database.Template{CreatedBy: u.ID, OrganizationID: o.ID})
4041+
tv := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
4042+
TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true},
4043+
JobID: j.ID,
4044+
OrganizationID: o.ID,
4045+
CreatedBy: u.ID,
4046+
})
4047+
ws := dbgen.Workspace(s.T(), db, database.WorkspaceTable{OwnerID: u.ID, TemplateID: tpl.ID, OrganizationID: o.ID})
4048+
_ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: j.ID, TemplateVersionID: tv.ID})
4049+
res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: j.ID})
40224050
check.Args(database.InsertWorkspaceAgentParams{
40234051
ID: uuid.New(),
4052+
ResourceID: res.ID,
40244053
Name: "dev",
40254054
APIKeyScope: database.AgentKeyScopeEnumAll,
4026-
}).Asserts(rbac.ResourceSystem, policy.ActionCreate)
4055+
}).Asserts(ws, policy.ActionCreateAgent)
40274056
}))
40284057
s.Run("InsertWorkspaceApp", s.Subtest(func(db database.Store, check *expects) {
40294058
dbtestutil.DisableForeignKeysAndTriggers(s.T(), db)

coderd/database/dbmem/dbmem.go

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

8056+
func (q *FakeQuerier) GetWorkspaceByResourceID(ctx context.Context, resourceID uuid.UUID) (database.Workspace, error) {
8057+
q.mutex.RLock()
8058+
defer q.mutex.RUnlock()
8059+
8060+
for _, resource := range q.workspaceResources {
8061+
if resource.ID != resourceID {
8062+
continue
8063+
}
8064+
8065+
for _, build := range q.workspaceBuilds {
8066+
if build.JobID != resource.JobID {
8067+
continue
8068+
}
8069+
8070+
for _, workspace := range q.workspaces {
8071+
if workspace.ID != build.WorkspaceID {
8072+
continue
8073+
}
8074+
8075+
return q.extendWorkspace(workspace), nil
8076+
}
8077+
}
8078+
}
8079+
8080+
return database.Workspace{}, sql.ErrNoRows
8081+
}
8082+
80568083
func (q *FakeQuerier) GetWorkspaceByWorkspaceAppID(_ context.Context, workspaceAppID uuid.UUID) (database.Workspace, error) {
80578084
if err := validateDatabaseType(workspaceAppID); err != nil {
80588085
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
*

coderd/rbac/object_gen.go

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

coderd/rbac/policy/policy.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ const (
2424

2525
ActionReadPersonal Action = "read_personal"
2626
ActionUpdatePersonal Action = "update_personal"
27+
28+
ActionCreateAgent Action = "create_agent"
29+
ActionDeleteAgent Action = "delete_agent"
2730
)
2831

2932
type PermissionDefinition struct {
@@ -67,6 +70,9 @@ var workspaceActions = map[Action]ActionDefinition{
6770
// Running a workspace
6871
ActionSSH: actDef("ssh into a given workspace"),
6972
ActionApplicationConnect: actDef("connect to workspace apps via browser"),
73+
74+
ActionCreateAgent: actDef("create a new workspace agent"),
75+
ActionDeleteAgent: actDef("delete an existing workspace agent"),
7076
}
7177

7278
// RBACPermissions is indexed by the type

0 commit comments

Comments
 (0)