Skip to content

refactor(dbauthz): add authz for system-level functions #6513

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Mar 10, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
use dbauthz system in workspaceBuildsData
  • Loading branch information
johnstcn committed Mar 9, 2023
commit 018b804fe552c9f6fb6ab6e597e49b5ac00d45d9
34 changes: 23 additions & 11 deletions coderd/workspaceapps/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"cdr.dev/slog"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/database/dbauthz"
"github.com/coder/coder/coderd/httpapi"
"github.com/coder/coder/coderd/httpmw"
"github.com/coder/coder/coderd/rbac"
Expand All @@ -38,6 +39,12 @@ const (
//
// Upstream code should avoid any database calls ever.
func (p *Provider) ResolveRequest(rw http.ResponseWriter, r *http.Request, appReq Request) (*Ticket, bool) {
// nolint:gocritic // We need to make a number of database calls. Setting a system context here
// // is simpler than calling dbauthz.AsSystemRestricted on every call.
// // dangerousSystemCtx is only used for database calls. The actual authentication
// // logic is handled in Provider.authorizeWorkspaceApp which directly checks the actor's
// // permissions.
dangerousSystemCtx := dbauthz.AsSystemRestricted(r.Context())
err := appReq.Validate()
if err != nil {
p.writeWorkspaceApp500(rw, r, &appReq, err, "invalid app request")
Expand Down Expand Up @@ -108,13 +115,14 @@ func (p *Provider) ResolveRequest(rw http.ResponseWriter, r *http.Request, appRe
userErr error
)
if userID, uuidErr := uuid.Parse(appReq.UsernameOrID); uuidErr == nil {
user, userErr = p.Database.GetUserByID(r.Context(), userID)
user, userErr = p.Database.GetUserByID(dangerousSystemCtx, userID)
} else {
user, userErr = p.Database.GetUserByEmailOrUsername(r.Context(), database.GetUserByEmailOrUsernameParams{
user, userErr = p.Database.GetUserByEmailOrUsername(dangerousSystemCtx, database.GetUserByEmailOrUsernameParams{
Username: appReq.UsernameOrID,
})
}
if xerrors.Is(userErr, sql.ErrNoRows) {
// TODO: add coverage
p.writeWorkspaceApp404(rw, r, &appReq, fmt.Sprintf("user %q not found", appReq.UsernameOrID))
return nil, false
} else if userErr != nil {
Expand All @@ -129,9 +137,9 @@ func (p *Provider) ResolveRequest(rw http.ResponseWriter, r *http.Request, appRe
workspaceErr error
)
if workspaceID, uuidErr := uuid.Parse(appReq.WorkspaceNameOrID); uuidErr == nil {
workspace, workspaceErr = p.Database.GetWorkspaceByID(r.Context(), workspaceID)
workspace, workspaceErr = p.Database.GetWorkspaceByID(dangerousSystemCtx, workspaceID)
} else {
workspace, workspaceErr = p.Database.GetWorkspaceByOwnerIDAndName(r.Context(), database.GetWorkspaceByOwnerIDAndNameParams{
workspace, workspaceErr = p.Database.GetWorkspaceByOwnerIDAndName(dangerousSystemCtx, database.GetWorkspaceByOwnerIDAndNameParams{
OwnerID: user.ID,
Name: appReq.WorkspaceNameOrID,
Deleted: false,
Expand All @@ -153,15 +161,16 @@ func (p *Provider) ResolveRequest(rw http.ResponseWriter, r *http.Request, appRe
trustAgent = false
)
if agentID, uuidErr := uuid.Parse(appReq.AgentNameOrID); uuidErr == nil {
agent, agentErr = p.Database.GetWorkspaceAgentByID(r.Context(), agentID)
agent, agentErr = p.Database.GetWorkspaceAgentByID(dangerousSystemCtx, agentID)
} else {
build, err := p.Database.GetLatestWorkspaceBuildByWorkspaceID(r.Context(), workspace.ID)
build, err := p.Database.GetLatestWorkspaceBuildByWorkspaceID(dangerousSystemCtx, workspace.ID)
if err != nil {
p.writeWorkspaceApp500(rw, r, &appReq, err, "get latest workspace build")
return nil, false
}

resources, err := p.Database.GetWorkspaceResourcesByJobID(r.Context(), build.JobID)
// nolint:gocritic // We need to fetch the agent to authenticate the request. This is a system function.
resources, err := p.Database.GetWorkspaceResourcesByJobID(dangerousSystemCtx, build.JobID)
if err != nil {
p.writeWorkspaceApp500(rw, r, &appReq, err, "get workspace resources")
return nil, false
Expand All @@ -171,7 +180,8 @@ func (p *Provider) ResolveRequest(rw http.ResponseWriter, r *http.Request, appRe
resourcesIDs = append(resourcesIDs, resource.ID)
}

agents, err := p.Database.GetWorkspaceAgentsByResourceIDs(r.Context(), resourcesIDs)
// nolint:gocritic // We need to fetch the agent to authenticate the request. This is a system function.
agents, err := p.Database.GetWorkspaceAgentsByResourceIDs(dangerousSystemCtx, resourcesIDs)
if err != nil {
p.writeWorkspaceApp500(rw, r, &appReq, err, "get workspace agents")
return nil, false
Expand Down Expand Up @@ -209,12 +219,13 @@ func (p *Provider) ResolveRequest(rw http.ResponseWriter, r *http.Request, appRe

// Verify the agent belongs to the workspace.
if !trustAgent {
agentResource, err := p.Database.GetWorkspaceResourceByID(r.Context(), agent.ResourceID)
//nolint:gocritic // We need to fetch the agent to authenticate the request. This is a system function.
agentResource, err := p.Database.GetWorkspaceResourceByID(dangerousSystemCtx, agent.ResourceID)
if err != nil {
p.writeWorkspaceApp500(rw, r, &appReq, err, "get agent resource")
return nil, false
}
build, err := p.Database.GetWorkspaceBuildByJobID(r.Context(), agentResource.JobID)
build, err := p.Database.GetWorkspaceBuildByJobID(dangerousSystemCtx, agentResource.JobID)
if err != nil {
p.writeWorkspaceApp500(rw, r, &appReq, err, "get agent workspace build")
return nil, false
Expand Down Expand Up @@ -324,7 +335,8 @@ func (p *Provider) ResolveRequest(rw http.ResponseWriter, r *http.Request, appRe
// error while looking it up, an HTML error page is returned and false is
// returned so the caller can return early.
func (p *Provider) lookupWorkspaceApp(rw http.ResponseWriter, r *http.Request, agentID uuid.UUID, appSlug string) (database.WorkspaceApp, bool) {
app, err := p.Database.GetWorkspaceAppByAgentIDAndSlug(r.Context(), database.GetWorkspaceAppByAgentIDAndSlugParams{
// nolint:gocritic // We need to fetch the workspace app to authorize the request.
app, err := p.Database.GetWorkspaceAppByAgentIDAndSlug(dbauthz.AsSystemRestricted(r.Context()), database.GetWorkspaceAppByAgentIDAndSlugParams{
AgentID: agentID,
Slug: appSlug,
})
Expand Down
17 changes: 12 additions & 5 deletions coderd/workspacebuilds.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"golang.org/x/xerrors"

"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/database/dbauthz"
"github.com/coder/coder/coderd/httpapi"
"github.com/coder/coder/coderd/httpmw"
"github.com/coder/coder/coderd/provisionerdserver"
Expand Down Expand Up @@ -967,12 +968,15 @@ func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.W
for _, build := range workspaceBuilds {
templateVersionIDs = append(templateVersionIDs, build.TemplateVersionID)
}
templateVersions, err := api.Database.GetTemplateVersionsByIDs(ctx, templateVersionIDs)

// nolint:gocritic // Getting template versions by ID is a system function.
templateVersions, err := api.Database.GetTemplateVersionsByIDs(dbauthz.AsSystemRestricted(ctx), templateVersionIDs)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return workspaceBuildsData{}, xerrors.Errorf("get template versions: %w", err)
}

resources, err := api.Database.GetWorkspaceResourcesByJobIDs(ctx, jobIDs)
// nolint:gocritic // Getting workspace resources by job ID is a system function.
resources, err := api.Database.GetWorkspaceResourcesByJobIDs(dbauthz.AsSystemRestricted(ctx), jobIDs)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return workspaceBuildsData{}, xerrors.Errorf("get workspace resources by job: %w", err)
}
Expand All @@ -990,12 +994,14 @@ func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.W
resourceIDs = append(resourceIDs, resource.ID)
}

metadata, err := api.Database.GetWorkspaceResourceMetadataByResourceIDs(ctx, resourceIDs)
// nolint:gocritic // Getting workspace resource metadata by resource ID is a system function.
metadata, err := api.Database.GetWorkspaceResourceMetadataByResourceIDs(dbauthz.AsSystemRestricted(ctx), resourceIDs)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return workspaceBuildsData{}, xerrors.Errorf("fetching resource metadata: %w", err)
}

agents, err := api.Database.GetWorkspaceAgentsByResourceIDs(ctx, resourceIDs)
// nolint:gocritic // Getting workspace agents by resource IDs is a system function.
agents, err := api.Database.GetWorkspaceAgentsByResourceIDs(dbauthz.AsSystemRestricted(ctx), resourceIDs)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return workspaceBuildsData{}, xerrors.Errorf("get workspace agents: %w", err)
}
Expand All @@ -1015,7 +1021,8 @@ func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.W
agentIDs = append(agentIDs, agent.ID)
}

apps, err := api.Database.GetWorkspaceAppsByAgentIDs(ctx, agentIDs)
// nolint:gocritic // Getting workspace apps by agent IDs is a system function.
apps, err := api.Database.GetWorkspaceAppsByAgentIDs(dbauthz.AsSystemRestricted(ctx), agentIDs)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return workspaceBuildsData{}, xerrors.Errorf("fetching workspace apps: %w", err)
}
Expand Down