Skip to content

Commit 2089006

Browse files
authored
feat!: drop reading other 'user' permission (#8650)
* feat: drop reading other 'user' permission Members of the platform can no longer read or list other users. Resources that have "created_by" or "initiated_by" still retain user context, but only include username and avatar url. Attempting to read a user found via those means will result in a 404. * Hide /users page for regular users * make groups a privledged endpoint * Permissions page for template perms * Admin for a given template enables an endpoint for listing users/groups.
1 parent 8649a10 commit 2089006

File tree

31 files changed

+579
-119
lines changed

31 files changed

+579
-119
lines changed

coderd/apidoc/docs.go

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

coderd/apidoc/swagger.json

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

coderd/authorize_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func TestCheckPermissions(t *testing.T) {
103103
Client: orgAdminClient,
104104
UserID: orgAdminUser.ID,
105105
Check: map[string]bool{
106-
readAllUsers: true,
106+
readAllUsers: false,
107107
readMyself: true,
108108
readOwnWorkspaces: true,
109109
readOrgWorkspaces: true,
@@ -115,7 +115,7 @@ func TestCheckPermissions(t *testing.T) {
115115
Client: memberClient,
116116
UserID: memberUser.ID,
117117
Check: map[string]bool{
118-
readAllUsers: true,
118+
readAllUsers: false,
119119
readMyself: true,
120120
readOwnWorkspaces: true,
121121
readOrgWorkspaces: false,

coderd/coderdtest/authorize.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ func (RBACAsserter) convertObjects(t *testing.T, objs ...interface{}) []rbac.Obj
116116
case codersdk.TemplateVersion:
117117
robj = rbac.ResourceTemplate.InOrg(obj.OrganizationID)
118118
case codersdk.User:
119-
robj = rbac.ResourceUser.WithID(obj.ID)
119+
robj = rbac.ResourceUserObject(obj.ID)
120120
case codersdk.Workspace:
121121
robj = rbac.ResourceWorkspace.WithID(obj.ID).InOrg(obj.OrganizationID).WithOwner(obj.OwnerID.String())
122122
default:

coderd/database/dbauthz/dbauthz.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,15 +1108,15 @@ func (q *querier) GetProvisionerLogsAfterID(ctx context.Context, arg database.Ge
11081108
}
11091109

11101110
func (q *querier) GetQuotaAllowanceForUser(ctx context.Context, userID uuid.UUID) (int64, error) {
1111-
err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceUser.WithID(userID))
1111+
err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceUserObject(userID))
11121112
if err != nil {
11131113
return -1, err
11141114
}
11151115
return q.db.GetQuotaAllowanceForUser(ctx, userID)
11161116
}
11171117

11181118
func (q *querier) GetQuotaConsumedForUser(ctx context.Context, userID uuid.UUID) (int64, error) {
1119-
err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceUser.WithID(userID))
1119+
err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceUserObject(userID))
11201120
if err != nil {
11211121
return -1, err
11221122
}
@@ -1390,7 +1390,7 @@ func (q *querier) GetUsers(ctx context.Context, arg database.GetUsersParams) ([]
13901390
// itself.
13911391
func (q *querier) GetUsersByIDs(ctx context.Context, ids []uuid.UUID) ([]database.User, error) {
13921392
for _, uid := range ids {
1393-
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceUser.WithID(uid)); err != nil {
1393+
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceUserObject(uid)); err != nil {
13941394
return nil, err
13951395
}
13961396
}
@@ -1899,7 +1899,7 @@ func (q *querier) InsertUserGroupsByName(ctx context.Context, arg database.Inser
18991899

19001900
// TODO: Should this be in system.go?
19011901
func (q *querier) InsertUserLink(ctx context.Context, arg database.InsertUserLinkParams) (database.UserLink, error) {
1902-
if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceUser.WithID(arg.UserID)); err != nil {
1902+
if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceUserObject(arg.UserID)); err != nil {
19031903
return database.UserLink{}, err
19041904
}
19051905
return q.db.InsertUserLink(ctx, arg)
@@ -2614,24 +2614,24 @@ func (q *querier) GetAuthorizedTemplates(ctx context.Context, arg database.GetTe
26142614
}
26152615

26162616
func (q *querier) GetTemplateGroupRoles(ctx context.Context, id uuid.UUID) ([]database.TemplateGroup, error) {
2617-
// An actor is authorized to read template group roles if they are authorized to read the template.
2617+
// An actor is authorized to read template group roles if they are authorized to update the template.
26182618
template, err := q.db.GetTemplateByID(ctx, id)
26192619
if err != nil {
26202620
return nil, err
26212621
}
2622-
if err := q.authorizeContext(ctx, rbac.ActionRead, template); err != nil {
2622+
if err := q.authorizeContext(ctx, rbac.ActionUpdate, template); err != nil {
26232623
return nil, err
26242624
}
26252625
return q.db.GetTemplateGroupRoles(ctx, id)
26262626
}
26272627

26282628
func (q *querier) GetTemplateUserRoles(ctx context.Context, id uuid.UUID) ([]database.TemplateUser, error) {
2629-
// An actor is authorized to query template user roles if they are authorized to read the template.
2629+
// An actor is authorized to query template user roles if they are authorized to update the template.
26302630
template, err := q.db.GetTemplateByID(ctx, id)
26312631
if err != nil {
26322632
return nil, err
26332633
}
2634-
if err := q.authorizeContext(ctx, rbac.ActionRead, template); err != nil {
2634+
if err := q.authorizeContext(ctx, rbac.ActionUpdate, template); err != nil {
26352635
return nil, err
26362636
}
26372637
return q.db.GetTemplateUserRoles(ctx, id)

coderd/database/dbauthz/dbauthz_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ func (s *MethodTestSuite) TestOrganization() {
521521
ma := dbgen.OrganizationMember(s.T(), db, database.OrganizationMember{OrganizationID: oa.ID})
522522
mb := dbgen.OrganizationMember(s.T(), db, database.OrganizationMember{OrganizationID: ob.ID})
523523
check.Args([]uuid.UUID{ma.UserID, mb.UserID}).
524-
Asserts(rbac.ResourceUser.WithID(ma.UserID), rbac.ActionRead, rbac.ResourceUser.WithID(mb.UserID), rbac.ActionRead)
524+
Asserts(rbac.ResourceUserObject(ma.UserID), rbac.ActionRead, rbac.ResourceUserObject(mb.UserID), rbac.ActionRead)
525525
}))
526526
s.Run("GetOrganizationMemberByUserID", s.Subtest(func(db database.Store, check *expects) {
527527
mem := dbgen.OrganizationMember(s.T(), db, database.OrganizationMember{})
@@ -698,11 +698,11 @@ func (s *MethodTestSuite) TestTemplate() {
698698
}))
699699
s.Run("GetTemplateGroupRoles", s.Subtest(func(db database.Store, check *expects) {
700700
t1 := dbgen.Template(s.T(), db, database.Template{})
701-
check.Args(t1.ID).Asserts(t1, rbac.ActionRead)
701+
check.Args(t1.ID).Asserts(t1, rbac.ActionUpdate)
702702
}))
703703
s.Run("GetTemplateUserRoles", s.Subtest(func(db database.Store, check *expects) {
704704
t1 := dbgen.Template(s.T(), db, database.Template{})
705-
check.Args(t1.ID).Asserts(t1, rbac.ActionRead)
705+
check.Args(t1.ID).Asserts(t1, rbac.ActionUpdate)
706706
}))
707707
s.Run("GetTemplateVersionByID", s.Subtest(func(db database.Store, check *expects) {
708708
t1 := dbgen.Template(s.T(), db, database.Template{})

coderd/database/modelmethods.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,14 +194,15 @@ func (w Workspace) LockedRBAC() rbac.Object {
194194
func (m OrganizationMember) RBACObject() rbac.Object {
195195
return rbac.ResourceOrganizationMember.
196196
WithID(m.UserID).
197-
InOrg(m.OrganizationID)
197+
InOrg(m.OrganizationID).
198+
WithOwner(m.UserID.String())
198199
}
199200

200201
func (m GetOrganizationIDsByMemberIDsRow) RBACObject() rbac.Object {
201202
// TODO: This feels incorrect as we are really returning a list of orgmembers.
202203
// This return type should be refactored to return a list of orgmembers, not this
203204
// special type.
204-
return rbac.ResourceUser.WithID(m.UserID)
205+
return rbac.ResourceUserObject(m.UserID)
205206
}
206207

207208
func (o Organization) RBACObject() rbac.Object {
@@ -233,15 +234,15 @@ func (f File) RBACObject() rbac.Object {
233234
// If you are trying to get the RBAC object for the UserData, use
234235
// u.UserDataRBACObject() instead.
235236
func (u User) RBACObject() rbac.Object {
236-
return rbac.ResourceUser.WithID(u.ID)
237+
return rbac.ResourceUserObject(u.ID)
237238
}
238239

239240
func (u User) UserDataRBACObject() rbac.Object {
240241
return rbac.ResourceUserData.WithID(u.ID).WithOwner(u.ID.String())
241242
}
242243

243244
func (u GetUsersRow) RBACObject() rbac.Object {
244-
return rbac.ResourceUser.WithID(u.ID)
245+
return rbac.ResourceUserObject(u.ID)
245246
}
246247

247248
func (u GitSSHKey) RBACObject() rbac.Object {

coderd/rbac/object.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,11 @@ var (
195195
}
196196
)
197197

198+
// ResourceUserObject is a helper function to create a user object for authz checks.
199+
func ResourceUserObject(userID uuid.UUID) Object {
200+
return ResourceUser.WithID(userID).WithOwner(userID.String())
201+
}
202+
198203
// Object is used to create objects for authz checks when you have none in
199204
// hand to run the check on.
200205
// An example is if you want to list all workspaces, you can create a Object

coderd/rbac/regosql/compile_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ neq(input.object.owner, "");
259259
},
260260
VariableConverter: regosql.UserConverter(),
261261
ExpectedSQL: p(
262-
p("'10d03e62-7703-4df5-a358-4f76577d4e2f' = ''") + " AND " + p("'' != ''") + " AND " + p("'' = ''"),
262+
p("'10d03e62-7703-4df5-a358-4f76577d4e2f' = id :: text") + " AND " + p("id :: text != ''") + " AND " + p("'' = ''"),
263263
),
264264
},
265265
}

coderd/rbac/regosql/configs.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ func UserConverter() *sqltypes.VariableConverter {
4242
// Users are never owned by an organization, so always return the empty string
4343
// for the org owner.
4444
sqltypes.StringVarMatcher("''", []string{"input", "object", "org_owner"}),
45-
// Users never have an owner, and are only owned site wide.
46-
sqltypes.StringVarMatcher("''", []string{"input", "object", "owner"}),
45+
// Users are always owned by themselves.
46+
sqltypes.StringVarMatcher("id :: text", []string{"input", "object", "owner"}),
4747
)
4848
matcher.RegisterMatcher(
4949
// No ACLs on the user type

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy