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

- Introduces rbac.ResourceSystem
- Grants system.* to system and provisionerd rbac subjects
- Updates dbauthz system queries where applicable
- coderd: Avoid index out of bounds in api.workspaceBuilds
- dbauthz: move GetUsersByIDs out of system, modify RBAC check to ResourceUser
- workspaceapps: Add test case for when owner of app is not found
This commit is contained in:
Cian Johnston
2023-03-10 18:09:28 +00:00
committed by GitHub
parent 1db2b12b8e
commit 144f374f60
17 changed files with 470 additions and 200 deletions
+2 -1
View File
@@ -795,7 +795,8 @@ func (api *API) CreateInMemoryProvisionerDaemon(ctx context.Context, debounce ti
}()
name := namesgenerator.GetRandomName(1)
daemon, err := api.Database.InsertProvisionerDaemon(ctx, database.InsertProvisionerDaemonParams{
// nolint:gocritic // Inserting a provisioner daemon is a system function.
daemon, err := api.Database.InsertProvisionerDaemon(dbauthz.AsSystemRestricted(ctx), database.InsertProvisionerDaemonParams{
ID: uuid.New(),
CreatedAt: database.Now(),
Name: name,
+29 -22
View File
@@ -115,17 +115,17 @@ func ActorFromContext(ctx context.Context) (rbac.Subject, bool) {
return a, ok
}
// AsProvisionerd returns a context with an actor that has permissions required
// for provisionerd to function.
func AsProvisionerd(ctx context.Context) context.Context {
return context.WithValue(ctx, authContextKey{}, rbac.Subject{
var (
subjectProvisionerd = rbac.Subject{
ID: uuid.Nil.String(),
Roles: rbac.Roles([]rbac.Role{
{
Name: "provisionerd",
DisplayName: "Provisioner Daemon",
Site: rbac.Permissions(map[string][]rbac.Action{
// TODO: Add ProvisionerJob resource type.
rbac.ResourceFile.Type: {rbac.ActionRead},
rbac.ResourceSystem.Type: {rbac.WildcardSymbol},
rbac.ResourceTemplate.Type: {rbac.ActionRead, rbac.ActionUpdate},
rbac.ResourceUser.Type: {rbac.ActionRead},
rbac.ResourceWorkspace.Type: {rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete},
@@ -135,20 +135,15 @@ func AsProvisionerd(ctx context.Context) context.Context {
},
}),
Scope: rbac.ScopeAll,
},
)
}
// AsAutostart returns a context with an actor that has permissions required
// for autostart to function.
func AsAutostart(ctx context.Context) context.Context {
return context.WithValue(ctx, authContextKey{}, rbac.Subject{
}
subjectAutostart = rbac.Subject{
ID: uuid.Nil.String(),
Roles: rbac.Roles([]rbac.Role{
{
Name: "autostart",
DisplayName: "Autostart Daemon",
Site: rbac.Permissions(map[string][]rbac.Action{
rbac.ResourceSystem.Type: {rbac.WildcardSymbol},
rbac.ResourceTemplate.Type: {rbac.ActionRead, rbac.ActionUpdate},
rbac.ResourceWorkspace.Type: {rbac.ActionRead, rbac.ActionUpdate},
}),
@@ -157,14 +152,8 @@ func AsAutostart(ctx context.Context) context.Context {
},
}),
Scope: rbac.ScopeAll,
},
)
}
// AsSystemRestricted returns a context with an actor that has permissions
// required for various system operations (login, logout, metrics cache).
func AsSystemRestricted(ctx context.Context) context.Context {
return context.WithValue(ctx, authContextKey{}, rbac.Subject{
}
subjectSystemRestricted = rbac.Subject{
ID: uuid.Nil.String(),
Roles: rbac.Roles([]rbac.Role{
{
@@ -175,6 +164,7 @@ func AsSystemRestricted(ctx context.Context) context.Context {
rbac.ResourceAPIKey.Type: {rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete},
rbac.ResourceGroup.Type: {rbac.ActionCreate, rbac.ActionUpdate},
rbac.ResourceRoleAssignment.Type: {rbac.ActionCreate},
rbac.ResourceSystem.Type: {rbac.WildcardSymbol},
rbac.ResourceOrganization.Type: {rbac.ActionCreate},
rbac.ResourceOrganizationMember.Type: {rbac.ActionCreate},
rbac.ResourceOrgRoleAssignment.Type: {rbac.ActionCreate},
@@ -187,8 +177,25 @@ func AsSystemRestricted(ctx context.Context) context.Context {
},
}),
Scope: rbac.ScopeAll,
},
)
}
)
// AsProvisionerd returns a context with an actor that has permissions required
// for provisionerd to function.
func AsProvisionerd(ctx context.Context) context.Context {
return context.WithValue(ctx, authContextKey{}, subjectProvisionerd)
}
// AsAutostart returns a context with an actor that has permissions required
// for autostart to function.
func AsAutostart(ctx context.Context) context.Context {
return context.WithValue(ctx, authContextKey{}, subjectAutostart)
}
// AsSystemRestricted returns a context with an actor that has permissions
// required for various system operations (login, logout, metrics cache).
func AsSystemRestricted(ctx context.Context) context.Context {
return context.WithValue(ctx, authContextKey{}, subjectSystemRestricted)
}
var AsRemoveActor = rbac.Subject{
+12
View File
@@ -963,6 +963,18 @@ func (q *querier) GetUserByID(ctx context.Context, id uuid.UUID) (database.User,
return fetch(q.log, q.auth, q.db.GetUserByID)(ctx, id)
}
// GetUsersByIDs is only used for usernames on workspace return data.
// This function should be replaced by joining this data to the workspace query
// itself.
func (q *querier) GetUsersByIDs(ctx context.Context, ids []uuid.UUID) ([]database.User, error) {
for _, uid := range ids {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceUser.WithID(uid)); err != nil {
return nil, err
}
}
return q.db.GetUsersByIDs(ctx, ids)
}
func (q *querier) GetAuthorizedUserCount(ctx context.Context, arg database.GetFilteredUserCountParams, prepared rbac.PreparedAuthorized) (int64, error) {
return q.db.GetAuthorizedUserCount(ctx, arg, prepared)
}
+7 -74
View File
@@ -282,11 +282,6 @@ func (s *MethodTestSuite) TestProvsionerJob() {
check.Args(database.UpdateProvisionerJobWithCancelByIDParams{ID: j.ID}).
Asserts(v.RBACObject(tpl), []rbac.Action{rbac.ActionRead, rbac.ActionUpdate}).Returns()
}))
s.Run("GetProvisionerJobsByIDs", s.Subtest(func(db database.Store, check *expects) {
a := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{})
b := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{})
check.Args([]uuid.UUID{a.ID, b.ID}).Asserts().Returns(slice.New(a, b))
}))
s.Run("GetProvisionerLogsByIDBetween", s.Subtest(func(db database.Store, check *expects) {
w := dbgen.Workspace(s.T(), db, database.Workspace{})
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{
@@ -619,22 +614,6 @@ func (s *MethodTestSuite) TestTemplate() {
})
check.Args(tv.ID).Asserts(t1, rbac.ActionRead).Returns(tv)
}))
s.Run("GetTemplateVersionsByIDs", s.Subtest(func(db database.Store, check *expects) {
t1 := dbgen.Template(s.T(), db, database.Template{})
t2 := dbgen.Template(s.T(), db, database.Template{})
tv1 := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
TemplateID: uuid.NullUUID{UUID: t1.ID, Valid: true},
})
tv2 := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
TemplateID: uuid.NullUUID{UUID: t2.ID, Valid: true},
})
tv3 := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
TemplateID: uuid.NullUUID{UUID: t2.ID, Valid: true},
})
check.Args([]uuid.UUID{tv1.ID, tv2.ID, tv3.ID}).
Asserts( /*t1, rbac.ActionRead, t2, rbac.ActionRead*/ ).
Returns(slice.New(tv1, tv2, tv3))
}))
s.Run("GetTemplateVersionsByTemplateID", s.Subtest(func(db database.Store, check *expects) {
t1 := dbgen.Template(s.T(), db, database.Template{})
a := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
@@ -784,6 +763,13 @@ func (s *MethodTestSuite) TestUser() {
u := dbgen.User(s.T(), db, database.User{})
check.Args(u.ID).Asserts(u, rbac.ActionRead).Returns(u)
}))
s.Run("GetUsersByIDs", s.Subtest(func(db database.Store, check *expects) {
a := dbgen.User(s.T(), db, database.User{CreatedAt: database.Now().Add(-time.Hour)})
b := dbgen.User(s.T(), db, database.User{CreatedAt: database.Now()})
check.Args([]uuid.UUID{a.ID, b.ID}).
Asserts(a, rbac.ActionRead, b, rbac.ActionRead).
Returns(slice.New(a, b))
}))
s.Run("GetAuthorizedUserCount", s.Subtest(func(db database.Store, check *expects) {
_ = dbgen.User(s.T(), db, database.User{})
check.Args(database.GetFilteredUserCountParams{}, emptyPreparedAuthorized{}).Asserts().Returns(int64(1))
@@ -803,13 +789,6 @@ func (s *MethodTestSuite) TestUser() {
b := dbgen.User(s.T(), db, database.User{CreatedAt: database.Now()})
check.Args(database.GetUsersParams{}).Asserts(a, rbac.ActionRead, b, rbac.ActionRead)
}))
s.Run("GetUsersByIDs", s.Subtest(func(db database.Store, check *expects) {
a := dbgen.User(s.T(), db, database.User{CreatedAt: database.Now().Add(-time.Hour)})
b := dbgen.User(s.T(), db, database.User{CreatedAt: database.Now()})
check.Args([]uuid.UUID{a.ID, b.ID}).
Asserts( /*a, rbac.ActionRead, b, rbac.ActionRead*/ ).
Returns(slice.New(a, b))
}))
s.Run("InsertUser", s.Subtest(func(db database.Store, check *expects) {
check.Args(database.InsertUserParams{
ID: uuid.New(),
@@ -977,14 +956,6 @@ func (s *MethodTestSuite) TestWorkspace() {
agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID})
check.Args(agt.AuthInstanceID.String).Asserts(ws, rbac.ActionRead).Returns(agt)
}))
s.Run("GetWorkspaceAgentsByResourceIDs", s.Subtest(func(db database.Store, check *expects) {
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID})
check.Args([]uuid.UUID{res.ID}).Asserts( /*ws, rbac.ActionRead*/ ).
Returns([]database.WorkspaceAgent{agt})
}))
s.Run("UpdateWorkspaceAgentLifecycleStateByID", s.Subtest(func(db database.Store, check *expects) {
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
@@ -1026,23 +997,6 @@ func (s *MethodTestSuite) TestWorkspace() {
check.Args(agt.ID).Asserts(ws, rbac.ActionRead).Returns(slice.New(a, b))
}))
s.Run("GetWorkspaceAppsByAgentIDs", s.Subtest(func(db database.Store, check *expects) {
aWs := dbgen.Workspace(s.T(), db, database.Workspace{})
aBuild := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: aWs.ID, JobID: uuid.New()})
aRes := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: aBuild.JobID})
aAgt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: aRes.ID})
a := dbgen.WorkspaceApp(s.T(), db, database.WorkspaceApp{AgentID: aAgt.ID})
bWs := dbgen.Workspace(s.T(), db, database.Workspace{})
bBuild := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: bWs.ID, JobID: uuid.New()})
bRes := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: bBuild.JobID})
bAgt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: bRes.ID})
b := dbgen.WorkspaceApp(s.T(), db, database.WorkspaceApp{AgentID: bAgt.ID})
check.Args([]uuid.UUID{a.AgentID, b.AgentID}).
Asserts( /*aWs, rbac.ActionRead, bWs, rbac.ActionRead*/ ).
Returns([]database.WorkspaceApp{a, b})
}))
s.Run("GetWorkspaceBuildByID", s.Subtest(func(db database.Store, check *expects) {
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID})
@@ -1096,15 +1050,6 @@ func (s *MethodTestSuite) TestWorkspace() {
res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
check.Args(res.ID).Asserts(ws, rbac.ActionRead).Returns(res)
}))
s.Run("GetWorkspaceResourceMetadataByResourceIDs", s.Subtest(func(db database.Store, check *expects) {
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
_ = dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild})
a := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
b := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
check.Args([]uuid.UUID{a.ID, b.ID}).
Asserts( /*ws, []rbac.Action{rbac.ActionRead, rbac.ActionRead}*/ )
}))
s.Run("Build/GetWorkspaceResourcesByJobID", s.Subtest(func(db database.Store, check *expects) {
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
@@ -1117,18 +1062,6 @@ func (s *MethodTestSuite) TestWorkspace() {
job := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: v.JobID, Type: database.ProvisionerJobTypeTemplateVersionImport})
check.Args(job.ID).Asserts(v.RBACObject(tpl), []rbac.Action{rbac.ActionRead, rbac.ActionRead}).Returns([]database.WorkspaceResource{})
}))
s.Run("GetWorkspaceResourcesByJobIDs", s.Subtest(func(db database.Store, check *expects) {
tpl := dbgen.Template(s.T(), db, database.Template{})
v := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true}, JobID: uuid.New()})
tJob := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: v.JobID, Type: database.ProvisionerJobTypeTemplateVersionImport})
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
wJob := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild})
check.Args([]uuid.UUID{tJob.ID, wJob.ID}).
Asserts( /*v.RBACObject(tpl), rbac.ActionRead, ws, rbac.ActionRead*/ ).
Returns([]database.WorkspaceResource{})
}))
s.Run("InsertWorkspace", s.Subtest(func(db database.Store, check *expects) {
u := dbgen.User(s.T(), db, database.User{})
o := dbgen.Organization(s.T(), db, database.Organization{})
+159 -28
View File
@@ -7,53 +7,50 @@ import (
"github.com/google/uuid"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/rbac"
)
// TODO: All these system functions should have rbac objects created to allow
// only system roles to call them. No user roles should ever have the permission
// to these objects. Might need a negative permission on the `Owner` role to
// prevent owners.
// GetWorkspaceAppsByAgentIDs
// The workspace/job is already fetched.
// TODO: This function should be removed/replaced with something with proper auth.
func (q *querier) GetWorkspaceAppsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceApp, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetWorkspaceAppsByAgentIDs(ctx, ids)
}
// GetWorkspaceAgentsByResourceIDs
// The workspace/job is already fetched.
// TODO: This function should be removed/replaced with something with proper auth.
func (q *querier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAgent, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetWorkspaceAgentsByResourceIDs(ctx, ids)
}
// GetWorkspaceResourceMetadataByResourceIDs is only used for build data.
// The workspace/job is already fetched.
// TODO: This function should be removed/replaced with something with proper auth.
func (q *querier) GetWorkspaceResourceMetadataByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceResourceMetadatum, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetWorkspaceResourceMetadataByResourceIDs(ctx, ids)
}
// GetUsersByIDs is only used for usernames on workspace return data.
// This function should be replaced by joining this data to the workspace query
// itself.
// TODO: This function should be removed/replaced with something with proper auth.
// A SQL compiled filter is an option.
func (q *querier) GetUsersByIDs(ctx context.Context, ids []uuid.UUID) ([]database.User, error) {
return q.db.GetUsersByIDs(ctx, ids)
}
// TODO: we need to add a provisioner job resource
func (q *querier) GetProvisionerJobsByIDs(ctx context.Context, ids []uuid.UUID) ([]database.ProvisionerJob, error) {
// TODO: This is missing authorization and is incorrect. This call is used by telemetry, and by 1 http route.
// That http handler should find a better way to fetch these jobs with easier rbac authz.
// if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
// return nil, err
// }
return q.db.GetProvisionerJobsByIDs(ctx, ids)
}
// GetTemplateVersionsByIDs is only used for workspace build data.
// The workspace is already fetched.
// TODO: Find a way to replace this with proper authz.
func (q *querier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UUID) ([]database.TemplateVersion, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetTemplateVersionsByIDs(ctx, ids)
}
@@ -61,18 +58,30 @@ func (q *querier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UUID)
// The workspace is already fetched.
// TODO: Find a way to replace this with proper authz.
func (q *querier) GetWorkspaceResourcesByJobIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceResource, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetWorkspaceResourcesByJobIDs(ctx, ids)
}
func (q *querier) UpdateUserLinkedID(ctx context.Context, arg database.UpdateUserLinkedIDParams) (database.UserLink, error) {
if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceSystem); err != nil {
return database.UserLink{}, err
}
return q.db.UpdateUserLinkedID(ctx, arg)
}
func (q *querier) GetUserLinkByLinkedID(ctx context.Context, linkedID string) (database.UserLink, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return database.UserLink{}, err
}
return q.db.GetUserLinkByLinkedID(ctx, linkedID)
}
func (q *querier) GetUserLinkByUserIDLoginType(ctx context.Context, arg database.GetUserLinkByUserIDLoginTypeParams) (database.UserLink, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return database.UserLink{}, err
}
return q.db.GetUserLinkByUserIDLoginType(ctx, arg)
}
@@ -81,96 +90,148 @@ func (q *querier) GetLatestWorkspaceBuilds(ctx context.Context) ([]database.Work
// This is because we need to query for all related workspaces to the returned builds.
// This is a very inefficient method of fetching the latest workspace builds.
// We should just join the rbac properties.
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetLatestWorkspaceBuilds(ctx)
}
// GetWorkspaceAgentByAuthToken is used in http middleware to get the workspace agent.
// This should only be used by a system user in that middleware.
func (q *querier) GetWorkspaceAgentByAuthToken(ctx context.Context, authToken uuid.UUID) (database.WorkspaceAgent, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return database.WorkspaceAgent{}, err
}
return q.db.GetWorkspaceAgentByAuthToken(ctx, authToken)
}
func (q *querier) GetActiveUserCount(ctx context.Context) (int64, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return 0, err
}
return q.db.GetActiveUserCount(ctx)
}
func (q *querier) GetUnexpiredLicenses(ctx context.Context) ([]database.License, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetUnexpiredLicenses(ctx)
}
func (q *querier) GetAuthorizationUserRoles(ctx context.Context, userID uuid.UUID) (database.GetAuthorizationUserRolesRow, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return database.GetAuthorizationUserRolesRow{}, err
}
return q.db.GetAuthorizationUserRoles(ctx, userID)
}
func (q *querier) GetDERPMeshKey(ctx context.Context) (string, error) {
// TODO Implement authz check for system user.
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return "", err
}
return q.db.GetDERPMeshKey(ctx)
}
func (q *querier) InsertDERPMeshKey(ctx context.Context, value string) error {
// TODO Implement authz check for system user.
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil {
return err
}
return q.db.InsertDERPMeshKey(ctx, value)
}
func (q *querier) InsertDeploymentID(ctx context.Context, value string) error {
// TODO Implement authz check for system user.
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil {
return err
}
return q.db.InsertDeploymentID(ctx, value)
}
func (q *querier) InsertReplica(ctx context.Context, arg database.InsertReplicaParams) (database.Replica, error) {
// TODO Implement authz check for system user.
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil {
return database.Replica{}, err
}
return q.db.InsertReplica(ctx, arg)
}
func (q *querier) UpdateReplica(ctx context.Context, arg database.UpdateReplicaParams) (database.Replica, error) {
// TODO Implement authz check for system user.
if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceSystem); err != nil {
return database.Replica{}, err
}
return q.db.UpdateReplica(ctx, arg)
}
func (q *querier) DeleteReplicasUpdatedBefore(ctx context.Context, updatedAt time.Time) error {
// TODO Implement authz check for system user.
if err := q.authorizeContext(ctx, rbac.ActionDelete, rbac.ResourceSystem); err != nil {
return err
}
return q.db.DeleteReplicasUpdatedBefore(ctx, updatedAt)
}
func (q *querier) GetReplicasUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]database.Replica, error) {
// TODO Implement authz check for system user.
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetReplicasUpdatedAfter(ctx, updatedAt)
}
func (q *querier) GetUserCount(ctx context.Context) (int64, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return 0, err
}
return q.db.GetUserCount(ctx)
}
func (q *querier) GetTemplates(ctx context.Context) ([]database.Template, error) {
// TODO Implement authz check for system user.
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetTemplates(ctx)
}
// Only used by metrics cache.
func (q *querier) GetTemplateAverageBuildTime(ctx context.Context, arg database.GetTemplateAverageBuildTimeParams) (database.GetTemplateAverageBuildTimeRow, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return database.GetTemplateAverageBuildTimeRow{}, err
}
return q.db.GetTemplateAverageBuildTime(ctx, arg)
}
// Only used by metrics cache.
func (q *querier) GetTemplateDAUs(ctx context.Context, templateID uuid.UUID) ([]database.GetTemplateDAUsRow, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetTemplateDAUs(ctx, templateID)
}
// Only used by metrics cache.
func (q *querier) GetDeploymentDAUs(ctx context.Context) ([]database.GetDeploymentDAUsRow, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetDeploymentDAUs(ctx)
}
// UpdateWorkspaceBuildCostByID is used by the provisioning system to update the cost of a workspace build.
func (q *querier) UpdateWorkspaceBuildCostByID(ctx context.Context, arg database.UpdateWorkspaceBuildCostByIDParams) (database.WorkspaceBuild, error) {
if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceSystem); err != nil {
return database.WorkspaceBuild{}, err
}
return q.db.UpdateWorkspaceBuildCostByID(ctx, arg)
}
func (q *querier) InsertOrUpdateLastUpdateCheck(ctx context.Context, value string) error {
if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceSystem); err != nil {
return err
}
return q.db.InsertOrUpdateLastUpdateCheck(ctx, value)
}
func (q *querier) GetLastUpdateCheck(ctx context.Context) (string, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return "", err
}
return q.db.GetLastUpdateCheck(ctx)
}
@@ -178,26 +239,44 @@ func (q *querier) GetLastUpdateCheck(ctx context.Context) (string, error) {
// telemetry data. Never called by a user.
func (q *querier) GetWorkspaceBuildsCreatedAfter(ctx context.Context, createdAt time.Time) ([]database.WorkspaceBuild, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetWorkspaceBuildsCreatedAfter(ctx, createdAt)
}
func (q *querier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt time.Time) ([]database.WorkspaceAgent, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetWorkspaceAgentsCreatedAfter(ctx, createdAt)
}
func (q *querier) GetWorkspaceAppsCreatedAfter(ctx context.Context, createdAt time.Time) ([]database.WorkspaceApp, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetWorkspaceAppsCreatedAfter(ctx, createdAt)
}
func (q *querier) GetWorkspaceResourcesCreatedAfter(ctx context.Context, createdAt time.Time) ([]database.WorkspaceResource, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetWorkspaceResourcesCreatedAfter(ctx, createdAt)
}
func (q *querier) GetWorkspaceResourceMetadataCreatedAfter(ctx context.Context, createdAt time.Time) ([]database.WorkspaceResourceMetadatum, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetWorkspaceResourceMetadataCreatedAfter(ctx, createdAt)
}
func (q *querier) DeleteOldWorkspaceAgentStats(ctx context.Context) error {
if err := q.authorizeContext(ctx, rbac.ActionDelete, rbac.ResourceSystem); err != nil {
return err
}
return q.db.DeleteOldWorkspaceAgentStats(ctx)
}
@@ -210,63 +289,115 @@ func (q *querier) GetDeploymentWorkspaceStats(ctx context.Context) (database.Get
}
func (q *querier) GetParameterSchemasCreatedAfter(ctx context.Context, createdAt time.Time) ([]database.ParameterSchema, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.GetParameterSchemasCreatedAfter(ctx, createdAt)
}
// TODO: We need to create a ProvisionerJob resource type
func (q *querier) GetProvisionerJobsCreatedAfter(ctx context.Context, createdAt time.Time) ([]database.ProvisionerJob, error) {
// if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
// return nil, err
// }
return q.db.GetProvisionerJobsCreatedAfter(ctx, createdAt)
}
// Provisionerd server functions
func (q *querier) InsertWorkspaceAgent(ctx context.Context, arg database.InsertWorkspaceAgentParams) (database.WorkspaceAgent, error) {
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil {
return database.WorkspaceAgent{}, err
}
return q.db.InsertWorkspaceAgent(ctx, arg)
}
func (q *querier) InsertWorkspaceApp(ctx context.Context, arg database.InsertWorkspaceAppParams) (database.WorkspaceApp, error) {
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil {
return database.WorkspaceApp{}, err
}
return q.db.InsertWorkspaceApp(ctx, arg)
}
func (q *querier) InsertWorkspaceResourceMetadata(ctx context.Context, arg database.InsertWorkspaceResourceMetadataParams) ([]database.WorkspaceResourceMetadatum, error) {
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil {
return nil, err
}
return q.db.InsertWorkspaceResourceMetadata(ctx, arg)
}
// TODO: We need to create a ProvisionerJob resource type
func (q *querier) AcquireProvisionerJob(ctx context.Context, arg database.AcquireProvisionerJobParams) (database.ProvisionerJob, error) {
// if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceSystem); err != nil {
// return database.ProvisionerJob{}, err
// }
return q.db.AcquireProvisionerJob(ctx, arg)
}
// TODO: We need to create a ProvisionerJob resource type
func (q *querier) UpdateProvisionerJobWithCompleteByID(ctx context.Context, arg database.UpdateProvisionerJobWithCompleteByIDParams) error {
// if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceSystem); err != nil {
// return err
// }
return q.db.UpdateProvisionerJobWithCompleteByID(ctx, arg)
}
// TODO: We need to create a ProvisionerJob resource type
func (q *querier) UpdateProvisionerJobByID(ctx context.Context, arg database.UpdateProvisionerJobByIDParams) error {
// if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceSystem); err != nil {
// return err
// }
return q.db.UpdateProvisionerJobByID(ctx, arg)
}
// TODO: We need to create a ProvisionerJob resource type
func (q *querier) InsertProvisionerJob(ctx context.Context, arg database.InsertProvisionerJobParams) (database.ProvisionerJob, error) {
// if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil {
// return database.ProvisionerJob{}, err
// }
return q.db.InsertProvisionerJob(ctx, arg)
}
// TODO: We need to create a ProvisionerJob resource type
func (q *querier) InsertProvisionerJobLogs(ctx context.Context, arg database.InsertProvisionerJobLogsParams) ([]database.ProvisionerJobLog, error) {
// if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil {
// return nil, err
// }
return q.db.InsertProvisionerJobLogs(ctx, arg)
}
// TODO: We need to create a ProvisionerDaemon resource type
func (q *querier) InsertProvisionerDaemon(ctx context.Context, arg database.InsertProvisionerDaemonParams) (database.ProvisionerDaemon, error) {
// if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil {
// return database.ProvisionerDaemon{}, err
// }
return q.db.InsertProvisionerDaemon(ctx, arg)
}
func (q *querier) InsertTemplateVersionParameter(ctx context.Context, arg database.InsertTemplateVersionParameterParams) (database.TemplateVersionParameter, error) {
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil {
return database.TemplateVersionParameter{}, err
}
return q.db.InsertTemplateVersionParameter(ctx, arg)
}
func (q *querier) InsertTemplateVersionVariable(ctx context.Context, arg database.InsertTemplateVersionVariableParams) (database.TemplateVersionVariable, error) {
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil {
return database.TemplateVersionVariable{}, err
}
return q.db.InsertTemplateVersionVariable(ctx, arg)
}
func (q *querier) InsertWorkspaceResource(ctx context.Context, arg database.InsertWorkspaceResourceParams) (database.WorkspaceResource, error) {
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil {
return database.WorkspaceResource{}, err
}
return q.db.InsertWorkspaceResource(ctx, arg)
}
func (q *querier) InsertParameterSchema(ctx context.Context, arg database.InsertParameterSchemaParams) (database.ParameterSchema, error) {
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil {
return database.ParameterSchema{}, err
}
return q.db.InsertParameterSchema(ctx, arg)
}
+120 -40
View File
@@ -10,6 +10,8 @@ import (
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/database/dbgen"
"github.com/coder/coder/coderd/rbac"
"github.com/coder/coder/coderd/util/slice"
)
func (s *MethodTestSuite) TestSystemFunctions() {
@@ -20,51 +22,51 @@ func (s *MethodTestSuite) TestSystemFunctions() {
UserID: u.ID,
LinkedID: l.LinkedID,
LoginType: database.LoginTypeGithub,
}).Asserts().Returns(l)
}).Asserts(rbac.ResourceSystem, rbac.ActionUpdate).Returns(l)
}))
s.Run("GetUserLinkByLinkedID", s.Subtest(func(db database.Store, check *expects) {
l := dbgen.UserLink(s.T(), db, database.UserLink{})
check.Args(l.LinkedID).Asserts().Returns(l)
check.Args(l.LinkedID).Asserts(rbac.ResourceSystem, rbac.ActionRead).Returns(l)
}))
s.Run("GetUserLinkByUserIDLoginType", s.Subtest(func(db database.Store, check *expects) {
l := dbgen.UserLink(s.T(), db, database.UserLink{})
check.Args(database.GetUserLinkByUserIDLoginTypeParams{
UserID: l.UserID,
LoginType: l.LoginType,
}).Asserts().Returns(l)
}).Asserts(rbac.ResourceSystem, rbac.ActionRead).Returns(l)
}))
s.Run("GetLatestWorkspaceBuilds", s.Subtest(func(db database.Store, check *expects) {
dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{})
dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{})
check.Args().Asserts()
check.Args().Asserts(rbac.ResourceSystem, rbac.ActionRead)
}))
s.Run("GetWorkspaceAgentByAuthToken", s.Subtest(func(db database.Store, check *expects) {
agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{})
check.Args(agt.AuthToken).Asserts().Returns(agt)
check.Args(agt.AuthToken).Asserts(rbac.ResourceSystem, rbac.ActionRead).Returns(agt)
}))
s.Run("GetActiveUserCount", s.Subtest(func(db database.Store, check *expects) {
check.Args().Asserts().Returns(int64(0))
check.Args().Asserts(rbac.ResourceSystem, rbac.ActionRead).Returns(int64(0))
}))
s.Run("GetUnexpiredLicenses", s.Subtest(func(db database.Store, check *expects) {
check.Args().Asserts()
check.Args().Asserts(rbac.ResourceSystem, rbac.ActionRead)
}))
s.Run("GetAuthorizationUserRoles", s.Subtest(func(db database.Store, check *expects) {
u := dbgen.User(s.T(), db, database.User{})
check.Args(u.ID).Asserts()
check.Args(u.ID).Asserts(rbac.ResourceSystem, rbac.ActionRead)
}))
s.Run("GetDERPMeshKey", s.Subtest(func(db database.Store, check *expects) {
check.Args().Asserts()
check.Args().Asserts(rbac.ResourceSystem, rbac.ActionRead)
}))
s.Run("InsertDERPMeshKey", s.Subtest(func(db database.Store, check *expects) {
check.Args("value").Asserts().Returns()
check.Args("value").Asserts(rbac.ResourceSystem, rbac.ActionCreate).Returns()
}))
s.Run("InsertDeploymentID", s.Subtest(func(db database.Store, check *expects) {
check.Args("value").Asserts().Returns()
check.Args("value").Asserts(rbac.ResourceSystem, rbac.ActionCreate).Returns()
}))
s.Run("InsertReplica", s.Subtest(func(db database.Store, check *expects) {
check.Args(database.InsertReplicaParams{
ID: uuid.New(),
}).Asserts()
}).Asserts(rbac.ResourceSystem, rbac.ActionCreate)
}))
s.Run("UpdateReplica", s.Subtest(func(db database.Store, check *expects) {
replica, err := db.InsertReplica(context.Background(), database.InsertReplicaParams{ID: uuid.New()})
@@ -72,24 +74,24 @@ func (s *MethodTestSuite) TestSystemFunctions() {
check.Args(database.UpdateReplicaParams{
ID: replica.ID,
DatabaseLatency: 100,
}).Asserts()
}).Asserts(rbac.ResourceSystem, rbac.ActionUpdate)
}))
s.Run("DeleteReplicasUpdatedBefore", s.Subtest(func(db database.Store, check *expects) {
_, err := db.InsertReplica(context.Background(), database.InsertReplicaParams{ID: uuid.New(), UpdatedAt: time.Now()})
require.NoError(s.T(), err)
check.Args(time.Now().Add(time.Hour)).Asserts()
check.Args(time.Now().Add(time.Hour)).Asserts(rbac.ResourceSystem, rbac.ActionDelete)
}))
s.Run("GetReplicasUpdatedAfter", s.Subtest(func(db database.Store, check *expects) {
_, err := db.InsertReplica(context.Background(), database.InsertReplicaParams{ID: uuid.New(), UpdatedAt: time.Now()})
require.NoError(s.T(), err)
check.Args(time.Now().Add(time.Hour * -1)).Asserts()
check.Args(time.Now().Add(time.Hour*-1)).Asserts(rbac.ResourceSystem, rbac.ActionRead)
}))
s.Run("GetUserCount", s.Subtest(func(db database.Store, check *expects) {
check.Args().Asserts().Returns(int64(0))
check.Args().Asserts(rbac.ResourceSystem, rbac.ActionRead).Returns(int64(0))
}))
s.Run("GetTemplates", s.Subtest(func(db database.Store, check *expects) {
_ = dbgen.Template(s.T(), db, database.Template{})
check.Args().Asserts()
check.Args().Asserts(rbac.ResourceSystem, rbac.ActionRead)
}))
s.Run("UpdateWorkspaceBuildCostByID", s.Subtest(func(db database.Store, check *expects) {
b := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{})
@@ -98,115 +100,193 @@ func (s *MethodTestSuite) TestSystemFunctions() {
check.Args(database.UpdateWorkspaceBuildCostByIDParams{
ID: b.ID,
DailyCost: 10,
}).Asserts().Returns(o)
}).Asserts(rbac.ResourceSystem, rbac.ActionUpdate).Returns(o)
}))
s.Run("InsertOrUpdateLastUpdateCheck", s.Subtest(func(db database.Store, check *expects) {
check.Args("value").Asserts()
check.Args("value").Asserts(rbac.ResourceSystem, rbac.ActionUpdate)
}))
s.Run("GetLastUpdateCheck", s.Subtest(func(db database.Store, check *expects) {
err := db.InsertOrUpdateLastUpdateCheck(context.Background(), "value")
require.NoError(s.T(), err)
check.Args().Asserts()
check.Args().Asserts(rbac.ResourceSystem, rbac.ActionRead)
}))
s.Run("GetWorkspaceBuildsCreatedAfter", s.Subtest(func(db database.Store, check *expects) {
_ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{CreatedAt: time.Now().Add(-time.Hour)})
check.Args(time.Now()).Asserts()
check.Args(time.Now()).Asserts(rbac.ResourceSystem, rbac.ActionRead)
}))
s.Run("GetWorkspaceAgentsCreatedAfter", s.Subtest(func(db database.Store, check *expects) {
_ = dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{CreatedAt: time.Now().Add(-time.Hour)})
check.Args(time.Now()).Asserts()
check.Args(time.Now()).Asserts(rbac.ResourceSystem, rbac.ActionRead)
}))
s.Run("GetWorkspaceAppsCreatedAfter", s.Subtest(func(db database.Store, check *expects) {
_ = dbgen.WorkspaceApp(s.T(), db, database.WorkspaceApp{CreatedAt: time.Now().Add(-time.Hour)})
check.Args(time.Now()).Asserts()
check.Args(time.Now()).Asserts(rbac.ResourceSystem, rbac.ActionRead)
}))
s.Run("GetWorkspaceResourcesCreatedAfter", s.Subtest(func(db database.Store, check *expects) {
_ = dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{CreatedAt: time.Now().Add(-time.Hour)})
check.Args(time.Now()).Asserts()
check.Args(time.Now()).Asserts(rbac.ResourceSystem, rbac.ActionRead)
}))
s.Run("GetWorkspaceResourceMetadataCreatedAfter", s.Subtest(func(db database.Store, check *expects) {
_ = dbgen.WorkspaceResourceMetadatums(s.T(), db, database.WorkspaceResourceMetadatum{})
check.Args(time.Now()).Asserts()
check.Args(time.Now()).Asserts(rbac.ResourceSystem, rbac.ActionRead)
}))
s.Run("DeleteOldWorkspaceAgentStats", s.Subtest(func(db database.Store, check *expects) {
check.Args().Asserts()
check.Args().Asserts(rbac.ResourceSystem, rbac.ActionDelete)
}))
s.Run("GetParameterSchemasCreatedAfter", s.Subtest(func(db database.Store, check *expects) {
_ = dbgen.ParameterSchema(s.T(), db, database.ParameterSchema{CreatedAt: time.Now().Add(-time.Hour)})
check.Args(time.Now()).Asserts()
check.Args(time.Now()).Asserts(rbac.ResourceSystem, rbac.ActionRead)
}))
s.Run("GetProvisionerJobsCreatedAfter", s.Subtest(func(db database.Store, check *expects) {
// TODO: add provisioner job resource type
_ = dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{CreatedAt: time.Now().Add(-time.Hour)})
check.Args(time.Now()).Asserts()
check.Args(time.Now()).Asserts( /*rbac.ResourceSystem, rbac.ActionRead*/ )
}))
s.Run("GetTemplateVersionsByIDs", s.Subtest(func(db database.Store, check *expects) {
t1 := dbgen.Template(s.T(), db, database.Template{})
t2 := dbgen.Template(s.T(), db, database.Template{})
tv1 := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
TemplateID: uuid.NullUUID{UUID: t1.ID, Valid: true},
})
tv2 := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
TemplateID: uuid.NullUUID{UUID: t2.ID, Valid: true},
})
tv3 := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
TemplateID: uuid.NullUUID{UUID: t2.ID, Valid: true},
})
check.Args([]uuid.UUID{tv1.ID, tv2.ID, tv3.ID}).
Asserts(rbac.ResourceSystem, rbac.ActionRead).
Returns(slice.New(tv1, tv2, tv3))
}))
s.Run("GetWorkspaceAppsByAgentIDs", s.Subtest(func(db database.Store, check *expects) {
aWs := dbgen.Workspace(s.T(), db, database.Workspace{})
aBuild := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: aWs.ID, JobID: uuid.New()})
aRes := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: aBuild.JobID})
aAgt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: aRes.ID})
a := dbgen.WorkspaceApp(s.T(), db, database.WorkspaceApp{AgentID: aAgt.ID})
bWs := dbgen.Workspace(s.T(), db, database.Workspace{})
bBuild := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: bWs.ID, JobID: uuid.New()})
bRes := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: bBuild.JobID})
bAgt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: bRes.ID})
b := dbgen.WorkspaceApp(s.T(), db, database.WorkspaceApp{AgentID: bAgt.ID})
check.Args([]uuid.UUID{a.AgentID, b.AgentID}).
Asserts(rbac.ResourceSystem, rbac.ActionRead).
Returns([]database.WorkspaceApp{a, b})
}))
s.Run("GetWorkspaceResourcesByJobIDs", s.Subtest(func(db database.Store, check *expects) {
tpl := dbgen.Template(s.T(), db, database.Template{})
v := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true}, JobID: uuid.New()})
tJob := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: v.JobID, Type: database.ProvisionerJobTypeTemplateVersionImport})
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
wJob := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild})
check.Args([]uuid.UUID{tJob.ID, wJob.ID}).
Asserts(rbac.ResourceSystem, rbac.ActionRead).
Returns([]database.WorkspaceResource{})
}))
s.Run("GetWorkspaceResourceMetadataByResourceIDs", s.Subtest(func(db database.Store, check *expects) {
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
_ = dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild})
a := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
b := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
check.Args([]uuid.UUID{a.ID, b.ID}).
Asserts(rbac.ResourceSystem, rbac.ActionRead)
}))
s.Run("GetWorkspaceAgentsByResourceIDs", s.Subtest(func(db database.Store, check *expects) {
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID})
check.Args([]uuid.UUID{res.ID}).
Asserts(rbac.ResourceSystem, rbac.ActionRead).
Returns([]database.WorkspaceAgent{agt})
}))
s.Run("GetProvisionerJobsByIDs", s.Subtest(func(db database.Store, check *expects) {
// TODO: add a ProvisionerJob resource type
a := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{})
b := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{})
check.Args([]uuid.UUID{a.ID, b.ID}).
Asserts( /*rbac.ResourceSystem, rbac.ActionRead*/ ).
Returns(slice.New(a, b))
}))
s.Run("InsertWorkspaceAgent", s.Subtest(func(db database.Store, check *expects) {
check.Args(database.InsertWorkspaceAgentParams{
ID: uuid.New(),
}).Asserts()
}).Asserts(rbac.ResourceSystem, rbac.ActionCreate)
}))
s.Run("InsertWorkspaceApp", s.Subtest(func(db database.Store, check *expects) {
check.Args(database.InsertWorkspaceAppParams{
ID: uuid.New(),
Health: database.WorkspaceAppHealthDisabled,
SharingLevel: database.AppSharingLevelOwner,
}).Asserts()
}).Asserts(rbac.ResourceSystem, rbac.ActionCreate)
}))
s.Run("InsertWorkspaceResourceMetadata", s.Subtest(func(db database.Store, check *expects) {
check.Args(database.InsertWorkspaceResourceMetadataParams{
WorkspaceResourceID: uuid.New(),
}).Asserts()
}).Asserts(rbac.ResourceSystem, rbac.ActionCreate)
}))
s.Run("AcquireProvisionerJob", s.Subtest(func(db database.Store, check *expects) {
// TODO: we need to create a ProvisionerJob resource
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{
StartedAt: sql.NullTime{Valid: false},
})
check.Args(database.AcquireProvisionerJobParams{Types: []database.ProvisionerType{j.Provisioner}}).
Asserts()
Asserts( /*rbac.ResourceSystem, rbac.ActionUpdate*/ )
}))
s.Run("UpdateProvisionerJobWithCompleteByID", s.Subtest(func(db database.Store, check *expects) {
// TODO: we need to create a ProvisionerJob resource
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{})
check.Args(database.UpdateProvisionerJobWithCompleteByIDParams{
ID: j.ID,
}).Asserts()
}).Asserts( /*rbac.ResourceSystem, rbac.ActionUpdate*/ )
}))
s.Run("UpdateProvisionerJobByID", s.Subtest(func(db database.Store, check *expects) {
// TODO: we need to create a ProvisionerJob resource
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{})
check.Args(database.UpdateProvisionerJobByIDParams{
ID: j.ID,
UpdatedAt: time.Now(),
}).Asserts()
}).Asserts( /*rbac.ResourceSystem, rbac.ActionUpdate*/ )
}))
s.Run("InsertProvisionerJob", s.Subtest(func(db database.Store, check *expects) {
// TODO: we need to create a ProvisionerJob resource
check.Args(database.InsertProvisionerJobParams{
ID: uuid.New(),
Provisioner: database.ProvisionerTypeEcho,
StorageMethod: database.ProvisionerStorageMethodFile,
Type: database.ProvisionerJobTypeWorkspaceBuild,
}).Asserts()
}).Asserts( /*rbac.ResourceSystem, rbac.ActionCreate*/ )
}))
s.Run("InsertProvisionerJobLogs", s.Subtest(func(db database.Store, check *expects) {
// TODO: we need to create a ProvisionerJob resource
j := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{})
check.Args(database.InsertProvisionerJobLogsParams{
JobID: j.ID,
}).Asserts()
}).Asserts( /*rbac.ResourceSystem, rbac.ActionCreate*/ )
}))
s.Run("InsertProvisionerDaemon", s.Subtest(func(db database.Store, check *expects) {
// TODO: we need to create a ProvisionerDaemon resource
check.Args(database.InsertProvisionerDaemonParams{
ID: uuid.New(),
}).Asserts()
}).Asserts( /*rbac.ResourceSystem, rbac.ActionCreate*/ )
}))
s.Run("InsertTemplateVersionParameter", s.Subtest(func(db database.Store, check *expects) {
v := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{})
check.Args(database.InsertTemplateVersionParameterParams{
TemplateVersionID: v.ID,
}).Asserts()
}).Asserts(rbac.ResourceSystem, rbac.ActionCreate)
}))
s.Run("InsertWorkspaceResource", s.Subtest(func(db database.Store, check *expects) {
r := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{})
check.Args(database.InsertWorkspaceResourceParams{
ID: r.ID,
Transition: database.WorkspaceTransitionStart,
}).Asserts()
}).Asserts(rbac.ResourceSystem, rbac.ActionCreate)
}))
s.Run("InsertParameterSchema", s.Subtest(func(db database.Store, check *expects) {
check.Args(database.InsertParameterSchemaParams{
@@ -214,6 +294,6 @@ func (s *MethodTestSuite) TestSystemFunctions() {
DefaultSourceScheme: database.ParameterSourceSchemeNone,
DefaultDestinationScheme: database.ParameterDestinationSchemeNone,
ValidationTypeSystem: database.ParameterTypeSystemNone,
}).Asserts()
}).Asserts(rbac.ResourceSystem, rbac.ActionCreate)
}))
}
+11 -3
View File
@@ -201,7 +201,9 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request,
})
return
}
resources, err := api.Database.GetWorkspaceResourcesByJobID(ctx, job.ID)
// nolint:gocritic // GetWorkspaceResourcesByJobID is a system function.
resources, err := api.Database.GetWorkspaceResourcesByJobID(dbauthz.AsSystemRestricted(ctx), job.ID)
if errors.Is(err, sql.ErrNoRows) {
err = nil
}
@@ -216,7 +218,9 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request,
for _, resource := range resources {
resourceIDs = append(resourceIDs, resource.ID)
}
resourceAgents, err := api.Database.GetWorkspaceAgentsByResourceIDs(ctx, resourceIDs)
// nolint:gocritic // GetWorkspaceAgentsByResourceIDs is a system function.
resourceAgents, err := api.Database.GetWorkspaceAgentsByResourceIDs(dbauthz.AsSystemRestricted(ctx), resourceIDs)
if errors.Is(err, sql.ErrNoRows) {
err = nil
}
@@ -231,6 +235,8 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request,
for _, agent := range resourceAgents {
resourceAgentIDs = append(resourceAgentIDs, agent.ID)
}
// nolint:gocritic // GetWorkspaceAppsByAgentIDs is a system function.
apps, err := api.Database.GetWorkspaceAppsByAgentIDs(ctx, resourceAgentIDs)
if errors.Is(err, sql.ErrNoRows) {
err = nil
@@ -242,7 +248,9 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request,
})
return
}
resourceMetadata, err := api.Database.GetWorkspaceResourceMetadataByResourceIDs(ctx, resourceIDs)
// nolint:gocritic // GetWorkspaceResourceMetadataByResourceIDs is a system function.
resourceMetadata, err := api.Database.GetWorkspaceResourceMetadataByResourceIDs(dbauthz.AsSystemRestricted(ctx), resourceIDs)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error fetching workspace metadata.",
+5
View File
@@ -159,6 +159,11 @@ var (
ResourceDebugInfo = Object{
Type: "debug_info",
}
// ResourceSystem is a pseudo-resource only used for system-level actions.
ResourceSystem = Object{
Type: "system",
}
)
// Object is used to create objects for authz checks when you have none in
+5 -2
View File
@@ -20,6 +20,7 @@ import (
"cdr.dev/slog"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/database/dbauthz"
)
const (
@@ -209,7 +210,8 @@ func (c *Checker) update() (r Result, err error) {
return r, xerrors.Errorf("json marshal result: %w", err)
}
err = c.db.InsertOrUpdateLastUpdateCheck(ctx, string(b))
// nolint:gocritic // Inserting the last update check is a system function.
err = c.db.InsertOrUpdateLastUpdateCheck(dbauthz.AsSystemRestricted(ctx), string(b))
if err != nil {
return r, err
}
@@ -224,7 +226,8 @@ func (c *Checker) notifyIfNewer(prev, next Result) {
}
func (c *Checker) lastUpdateCheck(ctx context.Context) (r Result, err error) {
s, err := c.db.GetLastUpdateCheck(ctx)
// nolint:gocritic // Getting the last update check is a system function.
s, err := c.db.GetLastUpdateCheck(dbauthz.AsSystemRestricted(ctx))
if err != nil {
return r, err
}
+4 -3
View File
@@ -37,7 +37,8 @@ import (
// @Router /users/first [get]
func (api *API) firstUser(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
userCount, err := api.Database.GetUserCount(ctx)
// nolint:gocritic // Getting user count is a system function.
userCount, err := api.Database.GetUserCount(dbauthz.AsSystemRestricted(ctx))
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error fetching user count.",
@@ -70,7 +71,6 @@ func (api *API) firstUser(rw http.ResponseWriter, r *http.Request) {
// @Success 201 {object} codersdk.CreateFirstUserResponse
// @Router /users/first [post]
func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
// TODO: Should this admin system context be in a middleware?
ctx := r.Context()
var createUser codersdk.CreateFirstUserRequest
if !httpapi.Read(ctx, rw, r, &createUser) {
@@ -78,7 +78,8 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
}
// This should only function for the first user.
userCount, err := api.Database.GetUserCount(ctx)
// nolint:gocritic // Getting user count is a system function.
userCount, err := api.Database.GetUserCount(dbauthz.AsSystemRestricted(ctx))
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error fetching user count.",
+22 -11
View File
@@ -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"
@@ -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")
@@ -108,9 +115,9 @@ 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,
})
}
@@ -129,9 +136,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,
@@ -153,15 +160,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
@@ -171,7 +179,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
@@ -209,12 +218,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
@@ -324,7 +334,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,
})
+20
View File
@@ -540,6 +540,26 @@ func Test_ResolveRequest(t *testing.T) {
require.Nil(t, ticket)
})
t.Run("UserNotFound", func(t *testing.T) {
t.Parallel()
req := workspaceapps.Request{
AccessMethod: workspaceapps.AccessMethodPath,
BasePath: "/app",
UsernameOrID: "thisuserdoesnotexist",
WorkspaceNameOrID: workspace.Name,
AgentNameOrID: agentName,
AppSlugOrPort: appNameOwner,
}
rw := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/app", nil)
r.Header.Set(codersdk.SessionTokenHeader, client.SessionToken())
ticket, ok := api.WorkspaceAppsProvider.ResolveRequest(rw, r, req)
require.False(t, ok)
require.Nil(t, ticket)
})
t.Run("RedirectSubdomainAuth", func(t *testing.T) {
t.Parallel()
+2
View File
@@ -1309,6 +1309,8 @@ func TestAppSharing(t *testing.T) {
// Verify that the apps have the correct sharing levels set.
workspaceBuild, err := client.WorkspaceBuild(ctx, workspace.LatestBuild.ID)
require.NoError(t, err)
require.NotEmpty(t, workspaceBuild.Resources, "workspace build has no resources")
require.NotEmpty(t, workspaceBuild.Resources[0].Agents, "workspace build has no agents")
agnt = workspaceBuild.Resources[0].Agents[0]
found := map[string]codersdk.WorkspaceAppSharingLevel{}
expected := map[string]codersdk.WorkspaceAppSharingLevel{
+30 -5
View File
@@ -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"
@@ -50,6 +51,24 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) {
return
}
// Ensure we have the job and template version for the workspace build.
// Otherwise we risk a panic in the api.convertWorkspaceBuild call below.
if len(data.jobs) == 0 {
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
Message: "Internal error getting workspace build data.",
Detail: "No job found for workspace build.",
})
return
}
if len(data.templateVersions) == 0 {
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
Message: "Internal error getting workspace build data.",
Detail: "No template version found for workspace build.",
})
return
}
apiBuild, err := api.convertWorkspaceBuild(
workspaceBuild,
workspace,
@@ -949,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)
}
@@ -972,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)
}
@@ -997,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)
}
+5 -2
View File
@@ -12,6 +12,7 @@ import (
"cdr.dev/slog"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/database/dbauthz"
"github.com/coder/coder/codersdk"
)
@@ -39,12 +40,14 @@ func Entitlements(
}
}
licenses, err := db.GetUnexpiredLicenses(ctx)
// nolint:gocritic // Getting unexpired licenses is a system function.
licenses, err := db.GetUnexpiredLicenses(dbauthz.AsSystemRestricted(ctx))
if err != nil {
return entitlements, err
}
activeUserCount, err := db.GetActiveUserCount(ctx)
// nolint:gocritic // Getting active user count is a system function.
activeUserCount, err := db.GetActiveUserCount(dbauthz.AsSystemRestricted(ctx))
if err != nil {
return entitlements, xerrors.Errorf("query active user count: %w", err)
}
+26 -4
View File
@@ -11,6 +11,7 @@ import (
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/coderd/provisionerdserver"
"github.com/coder/coder/coderd/rbac"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/enterprise/coderd/coderdenttest"
"github.com/coder/coder/enterprise/coderd/license"
@@ -20,6 +21,22 @@ import (
func TestProvisionerDaemonServe(t *testing.T) {
t.Parallel()
t.Run("OK", func(t *testing.T) {
t.Parallel()
client := coderdenttest.New(t, nil)
user := coderdtest.CreateFirstUser(t, client)
coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureExternalProvisionerDaemons: 1,
},
})
srv, err := client.ServeProvisionerDaemon(context.Background(), user.OrganizationID, []codersdk.ProvisionerType{
codersdk.ProvisionerTypeEcho,
}, map[string]string{})
require.NoError(t, err)
srv.DRPCConn().Close()
})
t.Run("NoLicense", func(t *testing.T) {
t.Parallel()
client := coderdenttest.New(t, nil)
@@ -42,11 +59,16 @@ func TestProvisionerDaemonServe(t *testing.T) {
codersdk.FeatureExternalProvisionerDaemons: 1,
},
})
srv, err := client.ServeProvisionerDaemon(context.Background(), user.OrganizationID, []codersdk.ProvisionerType{
another, _ := coderdtest.CreateAnotherUser(t, client, user.OrganizationID, rbac.RoleOrgAdmin(user.OrganizationID))
_, err := another.ServeProvisionerDaemon(context.Background(), user.OrganizationID, []codersdk.ProvisionerType{
codersdk.ProvisionerTypeEcho,
}, map[string]string{})
require.NoError(t, err)
srv.DRPCConn().Close()
}, map[string]string{
provisionerdserver.TagScope: provisionerdserver.ScopeOrganization,
})
require.Error(t, err)
var apiError *codersdk.Error
require.ErrorAs(t, err, &apiError)
require.Equal(t, http.StatusForbidden, apiError.StatusCode())
})
t.Run("OrganizationNoPerms", func(t *testing.T) {
+11 -5
View File
@@ -19,6 +19,7 @@ import (
"github.com/coder/coder/buildinfo"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/database/dbauthz"
)
var PubsubEvent = "replica"
@@ -61,7 +62,8 @@ func New(ctx context.Context, logger slog.Logger, db database.Store, pubsub data
if err != nil {
return nil, xerrors.Errorf("ping database: %w", err)
}
replica, err := db.InsertReplica(ctx, database.InsertReplicaParams{
// nolint:gocritic // Inserting a replica is a system function.
replica, err := db.InsertReplica(dbauthz.AsSystemRestricted(ctx), database.InsertReplicaParams{
ID: options.ID,
CreatedAt: database.Now(),
StartedAt: database.Now(),
@@ -141,7 +143,8 @@ func (m *Manager) loop(ctx context.Context) {
case <-ctx.Done():
return
case <-deleteTicker.C:
err := m.db.DeleteReplicasUpdatedBefore(ctx, m.updateInterval())
// nolint:gocritic // Deleting a replica is a system function
err := m.db.DeleteReplicasUpdatedBefore(dbauthz.AsSystemRestricted(ctx), m.updateInterval())
if err != nil {
m.logger.Warn(ctx, "delete old replicas", slog.Error(err))
}
@@ -218,7 +221,8 @@ func (m *Manager) syncReplicas(ctx context.Context) error {
defer m.closeWait.Done()
// Expect replicas to update once every three times the interval...
// If they don't, assume death!
replicas, err := m.db.GetReplicasUpdatedAfter(ctx, m.updateInterval())
// nolint:gocritic // Reading replicas is a system function
replicas, err := m.db.GetReplicasUpdatedAfter(dbauthz.AsSystemRestricted(ctx), m.updateInterval())
if err != nil {
return xerrors.Errorf("get replicas: %w", err)
}
@@ -276,7 +280,8 @@ func (m *Manager) syncReplicas(ctx context.Context) error {
m.mutex.Lock()
defer m.mutex.Unlock()
replica, err := m.db.UpdateReplica(ctx, database.UpdateReplicaParams{
// nolint:gocritic // Updating a replica is a system function.
replica, err := m.db.UpdateReplica(dbauthz.AsSystemRestricted(ctx), database.UpdateReplicaParams{
ID: m.self.ID,
UpdatedAt: database.Now(),
StartedAt: m.self.StartedAt,
@@ -366,7 +371,8 @@ func (m *Manager) Close() error {
defer m.mutex.Unlock()
ctx, cancelFunc := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelFunc()
_, err := m.db.UpdateReplica(ctx, database.UpdateReplicaParams{
// nolint:gocritic // Updating a replica is a sytsem function.
_, err := m.db.UpdateReplica(dbauthz.AsSystemRestricted(ctx), database.UpdateReplicaParams{
ID: m.self.ID,
UpdatedAt: database.Now(),
StartedAt: m.self.StartedAt,