Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 418a8a7

Browse files
authored
chore: Skip authz on various functions used for api data building (#6366)
* chore: Skip authz on various functions used for api data building API already fetches the parent object and does the rbac check. Until these functions are optimized, skipping authz is better. It leaves us no worse off than the status quo
1 parent d30da81 commit 418a8a7

File tree

5 files changed

+65
-127
lines changed

5 files changed

+65
-127
lines changed

coderd/database/dbauthz/querier.go

Lines changed: 0 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -251,12 +251,6 @@ func (q *querier) GetProvisionerJobByID(ctx context.Context, id uuid.UUID) (data
251251
return job, nil
252252
}
253253

254-
func (q *querier) GetProvisionerJobsByIDs(ctx context.Context, ids []uuid.UUID) ([]database.ProvisionerJob, error) {
255-
// TODO: This is missing authorization and is incorrect. This call is used by telemetry, and by 1 http route.
256-
// That http handler should find a better way to fetch these jobs with easier rbac authz.
257-
return q.db.GetProvisionerJobsByIDs(ctx, ids)
258-
}
259-
260254
func (q *querier) GetProvisionerLogsByIDBetween(ctx context.Context, arg database.GetProvisionerLogsByIDBetweenParams) ([]database.ProvisionerJobLog, error) {
261255
// Authorized read on job lets the actor also read the logs.
262256
_, err := q.GetProvisionerJobByID(ctx, arg.JobID)
@@ -725,35 +719,6 @@ func (q *querier) GetTemplateVersionVariables(ctx context.Context, templateVersi
725719
return q.db.GetTemplateVersionVariables(ctx, templateVersionID)
726720
}
727721

728-
func (q *querier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UUID) ([]database.TemplateVersion, error) {
729-
// TODO: This is so inefficient
730-
versions, err := q.db.GetTemplateVersionsByIDs(ctx, ids)
731-
if err != nil {
732-
return nil, err
733-
}
734-
checked := make(map[uuid.UUID]bool)
735-
for _, v := range versions {
736-
if _, ok := checked[v.TemplateID.UUID]; ok {
737-
continue
738-
}
739-
740-
obj := v.RBACObjectNoTemplate()
741-
template, err := q.db.GetTemplateByID(ctx, v.TemplateID.UUID)
742-
if err == nil {
743-
obj = v.RBACObject(template)
744-
}
745-
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
746-
return nil, err
747-
}
748-
if err := q.authorizeContext(ctx, rbac.ActionRead, obj); err != nil {
749-
return nil, err
750-
}
751-
checked[v.TemplateID.UUID] = true
752-
}
753-
754-
return versions, nil
755-
}
756-
757722
func (q *querier) GetTemplateVersionsByTemplateID(ctx context.Context, arg database.GetTemplateVersionsByTemplateIDParams) ([]database.TemplateVersion, error) {
758723
// An actor can read template versions if they can read the related template.
759724
template, err := q.db.GetTemplateByID(ctx, arg.TemplateID)
@@ -1013,11 +978,6 @@ func (q *querier) GetUsersWithCount(ctx context.Context, arg database.GetUsersPa
1013978
return users, rowUsers[0].Count, nil
1014979
}
1015980

1016-
// TODO: Remove this and use a filter on GetUsers
1017-
func (q *querier) GetUsersByIDs(ctx context.Context, ids []uuid.UUID) ([]database.User, error) {
1018-
return fetchWithPostFilter(q.auth, q.db.GetUsersByIDs)(ctx, ids)
1019-
}
1020-
1021981
func (q *querier) InsertUser(ctx context.Context, arg database.InsertUserParams) (database.User, error) {
1022982
// Always check if the assigned roles can actually be assigned by this actor.
1023983
impliedRoles := append([]string{rbac.RoleMember()}, arg.RBACRoles...)
@@ -1222,37 +1182,6 @@ func (q *querier) GetWorkspaceAgentByInstanceID(ctx context.Context, authInstanc
12221182
return agent, nil
12231183
}
12241184

1225-
// GetWorkspaceAgentsByResourceIDs is an all or nothing call. If the user cannot read
1226-
// a single agent, the entire call will fail.
1227-
func (q *querier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAgent, error) {
1228-
if _, ok := ActorFromContext(ctx); !ok {
1229-
return nil, NoActorError
1230-
}
1231-
// TODO: Make this more efficient. This is annoying because all these resources should be owned by the same workspace.
1232-
// So the authz check should just be 1 check, but we cannot do that easily here. We should see if all callers can
1233-
// instead do something like GetWorkspaceAgentsByWorkspaceID.
1234-
agents, err := q.db.GetWorkspaceAgentsByResourceIDs(ctx, ids)
1235-
if err != nil {
1236-
return nil, err
1237-
}
1238-
1239-
for _, a := range agents {
1240-
// Check if we can fetch the workspace by the agent ID.
1241-
_, err := q.GetWorkspaceByAgentID(ctx, a.ID)
1242-
if err == nil {
1243-
continue
1244-
}
1245-
if errors.Is(err, sql.ErrNoRows) && !errors.As(err, &NotAuthorizedError{}) {
1246-
// The agent is not tied to a workspace, likely from an orphaned template version.
1247-
// Just return it.
1248-
continue
1249-
}
1250-
// Otherwise, we cannot read the workspace, so we cannot read the agent.
1251-
return nil, err
1252-
}
1253-
return agents, nil
1254-
}
1255-
12561185
func (q *querier) UpdateWorkspaceAgentLifecycleStateByID(ctx context.Context, arg database.UpdateWorkspaceAgentLifecycleStateByIDParams) error {
12571186
agent, err := q.db.GetWorkspaceAgentByID(ctx, arg.ID)
12581187
if err != nil {
@@ -1305,20 +1234,6 @@ func (q *querier) GetWorkspaceAppsByAgentID(ctx context.Context, agentID uuid.UU
13051234
return q.db.GetWorkspaceAppsByAgentID(ctx, agentID)
13061235
}
13071236

1308-
// GetWorkspaceAppsByAgentIDs is an all or nothing call. If the user cannot read a single app, the entire call will fail.
1309-
func (q *querier) GetWorkspaceAppsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceApp, error) {
1310-
// TODO: This should be reworked. All these apps are likely owned by the same workspace, so we should be able to
1311-
// do 1 authz call. We should refactor this to be GetWorkspaceAppsByWorkspaceID.
1312-
for _, id := range ids {
1313-
_, err := q.GetWorkspaceAgentByID(ctx, id)
1314-
if err != nil {
1315-
return nil, err
1316-
}
1317-
}
1318-
1319-
return q.db.GetWorkspaceAppsByAgentIDs(ctx, ids)
1320-
}
1321-
13221237
func (q *querier) GetWorkspaceBuildByID(ctx context.Context, buildID uuid.UUID) (database.WorkspaceBuild, error) {
13231238
build, err := q.db.GetWorkspaceBuildByID(ctx, buildID)
13241239
if err != nil {
@@ -1395,21 +1310,6 @@ func (q *querier) GetWorkspaceResourceByID(ctx context.Context, id uuid.UUID) (d
13951310
return resource, nil
13961311
}
13971312

1398-
// GetWorkspaceResourceMetadataByResourceIDs is an all or nothing call. If a single resource is not authorized, then
1399-
// an error is returned.
1400-
func (q *querier) GetWorkspaceResourceMetadataByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceResourceMetadatum, error) {
1401-
// TODO: This is very inefficient. Since all these resources are likely asscoiated with the same workspace.
1402-
for _, id := range ids {
1403-
// If we can read the resource, we can read the metadata.
1404-
_, err := q.GetWorkspaceResourceByID(ctx, id)
1405-
if err != nil {
1406-
return nil, err
1407-
}
1408-
}
1409-
1410-
return q.db.GetWorkspaceResourceMetadataByResourceIDs(ctx, ids)
1411-
}
1412-
14131313
func (q *querier) GetWorkspaceResourcesByJobID(ctx context.Context, jobID uuid.UUID) ([]database.WorkspaceResource, error) {
14141314
job, err := q.db.GetProvisionerJobByID(ctx, jobID)
14151315
if err != nil {
@@ -1455,21 +1355,6 @@ func (q *querier) GetWorkspaceResourcesByJobID(ctx context.Context, jobID uuid.U
14551355
return q.db.GetWorkspaceResourcesByJobID(ctx, jobID)
14561356
}
14571357

1458-
// GetWorkspaceResourcesByJobIDs is an all or nothing call. If a single resource is not authorized, then
1459-
// an error is returned.
1460-
func (q *querier) GetWorkspaceResourcesByJobIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceResource, error) {
1461-
// TODO: This is very inefficient. Since all these resources are likely asscoiated with the same workspace.
1462-
for _, id := range ids {
1463-
// If we can read the resource, we can read the metadata.
1464-
_, err := q.GetProvisionerJobByID(ctx, id)
1465-
if err != nil {
1466-
return nil, err
1467-
}
1468-
}
1469-
1470-
return q.db.GetWorkspaceResourcesByJobIDs(ctx, ids)
1471-
}
1472-
14731358
func (q *querier) InsertWorkspace(ctx context.Context, arg database.InsertWorkspaceParams) (database.Workspace, error) {
14741359
obj := rbac.ResourceWorkspace.WithOwner(arg.OwnerID.String()).InOrg(arg.OrganizationID)
14751360
return insert(q.log, q.auth, obj, q.db.InsertWorkspace)(ctx, arg)

coderd/database/dbauthz/querier_test.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ func (s *MethodTestSuite) TestTemplate() {
622622
TemplateID: uuid.NullUUID{UUID: t2.ID, Valid: true},
623623
})
624624
check.Args([]uuid.UUID{tv1.ID, tv2.ID, tv3.ID}).
625-
Asserts(t1, rbac.ActionRead, t2, rbac.ActionRead).
625+
Asserts( /*t1, rbac.ActionRead, t2, rbac.ActionRead*/ ).
626626
Returns(slice.New(tv1, tv2, tv3))
627627
}))
628628
s.Run("GetTemplateVersionsByTemplateID", s.Subtest(func(db database.Store, check *expects) {
@@ -797,7 +797,7 @@ func (s *MethodTestSuite) TestUser() {
797797
a := dbgen.User(s.T(), db, database.User{CreatedAt: database.Now().Add(-time.Hour)})
798798
b := dbgen.User(s.T(), db, database.User{CreatedAt: database.Now()})
799799
check.Args([]uuid.UUID{a.ID, b.ID}).
800-
Asserts(a, rbac.ActionRead, b, rbac.ActionRead).
800+
Asserts( /*a, rbac.ActionRead, b, rbac.ActionRead*/ ).
801801
Returns(slice.New(a, b))
802802
}))
803803
s.Run("InsertUser", s.Subtest(func(db database.Store, check *expects) {
@@ -972,7 +972,7 @@ func (s *MethodTestSuite) TestWorkspace() {
972972
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
973973
res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
974974
agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID})
975-
check.Args([]uuid.UUID{res.ID}).Asserts(ws, rbac.ActionRead).
975+
check.Args([]uuid.UUID{res.ID}).Asserts( /*ws, rbac.ActionRead*/ ).
976976
Returns([]database.WorkspaceAgent{agt})
977977
}))
978978
s.Run("UpdateWorkspaceAgentLifecycleStateByID", s.Subtest(func(db database.Store, check *expects) {
@@ -1030,7 +1030,7 @@ func (s *MethodTestSuite) TestWorkspace() {
10301030
b := dbgen.WorkspaceApp(s.T(), db, database.WorkspaceApp{AgentID: bAgt.ID})
10311031

10321032
check.Args([]uuid.UUID{a.AgentID, b.AgentID}).
1033-
Asserts(aWs, rbac.ActionRead, bWs, rbac.ActionRead).
1033+
Asserts( /*aWs, rbac.ActionRead, bWs, rbac.ActionRead*/ ).
10341034
Returns([]database.WorkspaceApp{a, b})
10351035
}))
10361036
s.Run("GetWorkspaceBuildByID", s.Subtest(func(db database.Store, check *expects) {
@@ -1093,7 +1093,7 @@ func (s *MethodTestSuite) TestWorkspace() {
10931093
a := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
10941094
b := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
10951095
check.Args([]uuid.UUID{a.ID, b.ID}).
1096-
Asserts(ws, []rbac.Action{rbac.ActionRead, rbac.ActionRead})
1096+
Asserts( /*ws, []rbac.Action{rbac.ActionRead, rbac.ActionRead}*/ )
10971097
}))
10981098
s.Run("Build/GetWorkspaceResourcesByJobID", s.Subtest(func(db database.Store, check *expects) {
10991099
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
@@ -1115,7 +1115,9 @@ func (s *MethodTestSuite) TestWorkspace() {
11151115
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
11161116
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
11171117
wJob := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild})
1118-
check.Args([]uuid.UUID{tJob.ID, wJob.ID}).Asserts(v.RBACObject(tpl), rbac.ActionRead, ws, rbac.ActionRead).Returns([]database.WorkspaceResource{})
1118+
check.Args([]uuid.UUID{tJob.ID, wJob.ID}).
1119+
Asserts( /*v.RBACObject(tpl), rbac.ActionRead, ws, rbac.ActionRead*/ ).
1120+
Returns([]database.WorkspaceResource{})
11191121
}))
11201122
s.Run("InsertWorkspace", s.Subtest(func(db database.Store, check *expects) {
11211123
u := dbgen.User(s.T(), db, database.User{})

coderd/database/dbauthz/system.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,56 @@ import (
1414
// to these objects. Might need a negative permission on the `Owner` role to
1515
// prevent owners.
1616

17+
// GetWorkspaceAppsByAgentIDs
18+
// The workspace/job is already fetched.
19+
// TODO: This function should be removed/replaced with something with proper auth.
20+
func (q *querier) GetWorkspaceAppsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceApp, error) {
21+
return q.db.GetWorkspaceAppsByAgentIDs(ctx, ids)
22+
}
23+
24+
// GetWorkspaceAgentsByResourceIDs
25+
// The workspace/job is already fetched.
26+
// TODO: This function should be removed/replaced with something with proper auth.
27+
func (q *querier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAgent, error) {
28+
return q.db.GetWorkspaceAgentsByResourceIDs(ctx, ids)
29+
}
30+
31+
// GetWorkspaceResourceMetadataByResourceIDs is only used for build data.
32+
// The workspace/job is already fetched.
33+
// TODO: This function should be removed/replaced with something with proper auth.
34+
func (q *querier) GetWorkspaceResourceMetadataByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceResourceMetadatum, error) {
35+
return q.db.GetWorkspaceResourceMetadataByResourceIDs(ctx, ids)
36+
}
37+
38+
// GetUsersByIDs is only used for usernames on workspace return data.
39+
// This function should be replaced by joining this data to the workspace query
40+
// itself.
41+
// TODO: This function should be removed/replaced with something with proper auth.
42+
// A SQL compiled filter is an option.
43+
func (q *querier) GetUsersByIDs(ctx context.Context, ids []uuid.UUID) ([]database.User, error) {
44+
return q.db.GetUsersByIDs(ctx, ids)
45+
}
46+
47+
func (q *querier) GetProvisionerJobsByIDs(ctx context.Context, ids []uuid.UUID) ([]database.ProvisionerJob, error) {
48+
// TODO: This is missing authorization and is incorrect. This call is used by telemetry, and by 1 http route.
49+
// That http handler should find a better way to fetch these jobs with easier rbac authz.
50+
return q.db.GetProvisionerJobsByIDs(ctx, ids)
51+
}
52+
53+
// GetTemplateVersionsByIDs is only used for workspace build data.
54+
// The workspace is already fetched.
55+
// TODO: Find a way to replace this with proper authz.
56+
func (q *querier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UUID) ([]database.TemplateVersion, error) {
57+
return q.db.GetTemplateVersionsByIDs(ctx, ids)
58+
}
59+
60+
// GetWorkspaceResourcesByJobIDs is only used for workspace build data.
61+
// The workspace is already fetched.
62+
// TODO: Find a way to replace this with proper authz.
63+
func (q *querier) GetWorkspaceResourcesByJobIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceResource, error) {
64+
return q.db.GetWorkspaceResourcesByJobIDs(ctx, ids)
65+
}
66+
1767
func (q *querier) UpdateUserLinkedID(ctx context.Context, arg database.UpdateUserLinkedIDParams) (database.UserLink, error) {
1868
return q.db.UpdateUserLinkedID(ctx, arg)
1969
}

coderd/templates.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
"github.com/coder/coder/coderd/audit"
1717
"github.com/coder/coder/coderd/database"
18+
"github.com/coder/coder/coderd/database/dbauthz"
1819
"github.com/coder/coder/coderd/httpapi"
1920
"github.com/coder/coder/coderd/httpmw"
2021
"github.com/coder/coder/coderd/rbac"
@@ -82,11 +83,10 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
8283
return
8384
}
8485

85-
// TODO: This just returns the workspaces a user can view. We should use
86-
// a system function to get all workspaces that use this template.
87-
// This data should never be exposed to the user aside from a non-zero count.
88-
// Or we move this into a postgres constraint.
89-
workspaces, err := api.Database.GetWorkspaces(ctx, database.GetWorkspacesParams{
86+
// This is just to get the workspace count, so we use a system context to
87+
// return ALL workspaces. Not just workspaces the user can view.
88+
// nolint:gocritic
89+
workspaces, err := api.Database.GetWorkspaces(dbauthz.AsSystemRestricted(ctx), database.GetWorkspacesParams{
9090
TemplateIds: []uuid.UUID{template.ID},
9191
})
9292
if err != nil && !errors.Is(err, sql.ErrNoRows) {

coderd/workspaces.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
570570
}
571571
aReq.New = workspace
572572

573-
users, err := api.Database.GetUsersByIDs(ctx, []uuid.UUID{user.ID, workspaceBuild.InitiatorID})
573+
initiator, err := api.Database.GetUserByID(ctx, workspaceBuild.InitiatorID)
574574
if err != nil {
575575
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
576576
Message: "Internal error fetching user.",
@@ -584,6 +584,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
584584
WorkspaceBuilds: []telemetry.WorkspaceBuild{telemetry.ConvertWorkspaceBuild(workspaceBuild)},
585585
})
586586

587+
users := []database.User{user, initiator}
587588
apiBuild, err := api.convertWorkspaceBuild(
588589
workspaceBuild,
589590
workspace,

0 commit comments

Comments
 (0)