Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions coderd/database/dbauthz/dbauthz.go
Original file line number Diff line number Diff line change
Expand Up @@ -3940,6 +3940,24 @@ func (q *querier) GetWorkspaceResourceMetadataCreatedAfter(ctx context.Context,
return q.db.GetWorkspaceResourceMetadataCreatedAfter(ctx, createdAt)
}

// GetWorkspaceResourceWithJobByID is an optimized version that fetches both
// the resource and job information in a single query. This is specifically
// designed for system-restricted contexts (like agent authentication) where
// we can bypass the authorization cascade that would normally call
// GetWorkspaceBuildByJobID.
func (q *querier) GetWorkspaceResourceWithJobByID(ctx context.Context, id uuid.UUID) (database.GetWorkspaceResourceWithJobByIDRow, error) {
// Authorize for system resource access. This will only succeed for
// system-restricted contexts, ensuring this optimized path is only used
// in appropriate scenarios.
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
return database.GetWorkspaceResourceWithJobByIDRow{}, err
}

// With system-restricted context, we can safely bypass the authorization
// cascade and call the database directly.
return q.db.GetWorkspaceResourceWithJobByID(ctx, id)
}

func (q *querier) GetWorkspaceResourcesByJobID(ctx context.Context, jobID uuid.UUID) ([]database.WorkspaceResource, error) {
job, err := q.db.GetProvisionerJobByID(ctx, jobID)
if err != nil {
Expand Down
20 changes: 20 additions & 0 deletions coderd/database/dbauthz/dbauthz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1990,6 +1990,26 @@ func (s *MethodTestSuite) TestWorkspace() {
dbm.EXPECT().GetWorkspaceByID(gomock.Any(), build.WorkspaceID).Return(ws, nil).AnyTimes()
check.Args(res.ID).Asserts(ws, policy.ActionRead).Returns(res)
}))
s.Run("GetWorkspaceResourceWithJobByID", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
res := testutil.Fake(s.T(), faker, database.WorkspaceResource{})
resWithJob := database.GetWorkspaceResourceWithJobByIDRow{
ID: res.ID,
CreatedAt: res.CreatedAt,
JobID: res.JobID,
Transition: res.Transition,
Type: res.Type,
Name: res.Name,
Hide: res.Hide,
Icon: res.Icon,
InstanceType: res.InstanceType,
DailyCost: res.DailyCost,
ModulePath: res.ModulePath,
JobType: database.ProvisionerJobTypeWorkspaceBuild,
JobInput: []byte(`{}`),
}
dbm.EXPECT().GetWorkspaceResourceWithJobByID(gomock.Any(), res.ID).Return(resWithJob, nil).AnyTimes()
check.Args(res.ID).Asserts(rbac.ResourceSystem, policy.ActionRead).Returns(resWithJob)
}))
s.Run("Build/GetWorkspaceResourcesByJobID", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
ws := testutil.Fake(s.T(), faker, database.Workspace{})
build := testutil.Fake(s.T(), faker, database.WorkspaceBuild{WorkspaceID: ws.ID})
Expand Down
7 changes: 7 additions & 0 deletions coderd/database/dbmetrics/querymetrics.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions coderd/database/dbmock/dbmock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions coderd/database/querier.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 60 additions & 0 deletions coderd/database/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions coderd/database/queries/workspaceresources.sql
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,25 @@ SELECT
SELECT * FROM workspace_resource_metadata WHERE workspace_resource_id = ANY(
SELECT id FROM workspace_resources WHERE created_at > $1
);

-- name: GetWorkspaceResourceWithJobByID :one
SELECT
wr.id,
wr.created_at,
wr.job_id,
wr.transition,
wr.type,
wr.name,
wr.hide,
wr.icon,
wr.instance_type,
wr.daily_cost,
wr.module_path,
pj.type AS job_type,
pj.input AS job_input
FROM
workspace_resources wr
JOIN
provisioner_jobs pj ON wr.job_id = pj.id
WHERE
wr.id = $1;
17 changes: 4 additions & 13 deletions coderd/workspaceresourceauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,31 +143,22 @@ func (api *API) handleAuthInstanceID(rw http.ResponseWriter, r *http.Request, in
return
}
//nolint:gocritic // needed for auth instance id
resource, err := api.Database.GetWorkspaceResourceByID(dbauthz.AsSystemRestricted(ctx), agent.ResourceID)
resourceWithJob, err := api.Database.GetWorkspaceResourceWithJobByID(dbauthz.AsSystemRestricted(ctx), agent.ResourceID)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error fetching provisioner job resource.",
Detail: err.Error(),
})
return
}
//nolint:gocritic // needed for auth instance id
job, err := api.Database.GetProvisionerJobByID(dbauthz.AsSystemRestricted(ctx), resource.JobID)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error fetching provisioner job.",
Detail: err.Error(),
})
return
}
if job.Type != database.ProvisionerJobTypeWorkspaceBuild {
if resourceWithJob.JobType != database.ProvisionerJobTypeWorkspaceBuild {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: fmt.Sprintf("%q jobs cannot be authenticated.", job.Type),
Message: fmt.Sprintf("%q jobs cannot be authenticated.", resourceWithJob.JobType),
})
return
}
var jobData provisionerdserver.WorkspaceProvisionJob
err = json.Unmarshal(job.Input, &jobData)
err = json.Unmarshal(resourceWithJob.JobInput, &jobData)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error extracting job data.",
Expand Down
Loading