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.
This commit is contained in:
Steven Masley
2023-07-26 10:33:48 -04:00
committed by GitHub
parent 8649a10441
commit 2089006fbc
31 changed files with 585 additions and 125 deletions

View File

@ -195,6 +195,11 @@ var (
}
)
// ResourceUserObject is a helper function to create a user object for authz checks.
func ResourceUserObject(userID uuid.UUID) Object {
return ResourceUser.WithID(userID).WithOwner(userID.String())
}
// Object is used to create objects for authz checks when you have none in
// hand to run the check on.
// An example is if you want to list all workspaces, you can create a Object

View File

@ -259,7 +259,7 @@ neq(input.object.owner, "");
},
VariableConverter: regosql.UserConverter(),
ExpectedSQL: p(
p("'10d03e62-7703-4df5-a358-4f76577d4e2f' = ''") + " AND " + p("'' != ''") + " AND " + p("'' = ''"),
p("'10d03e62-7703-4df5-a358-4f76577d4e2f' = id :: text") + " AND " + p("id :: text != ''") + " AND " + p("'' = ''"),
),
},
}

View File

@ -42,8 +42,8 @@ func UserConverter() *sqltypes.VariableConverter {
// Users are never owned by an organization, so always return the empty string
// for the org owner.
sqltypes.StringVarMatcher("''", []string{"input", "object", "org_owner"}),
// Users never have an owner, and are only owned site wide.
sqltypes.StringVarMatcher("''", []string{"input", "object", "owner"}),
// Users are always owned by themselves.
sqltypes.StringVarMatcher("id :: text", []string{"input", "object", "owner"}),
)
matcher.RegisterMatcher(
// No ACLs on the user type

View File

@ -145,14 +145,18 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
Name: member,
DisplayName: "",
Site: Permissions(map[string][]Action{
// All users can read all other users and know they exist.
ResourceUser.Type: {ActionRead},
ResourceRoleAssignment.Type: {ActionRead},
// All users can see the provisioner daemons.
ResourceProvisionerDaemon.Type: {ActionRead},
}),
Org: map[string][]Permission{},
User: allPermsExcept(ResourceWorkspaceLocked),
Org: map[string][]Permission{},
User: append(allPermsExcept(ResourceWorkspaceLocked, ResourceUser, ResourceOrganizationMember),
Permissions(map[string][]Action{
// Users cannot do create/update/delete on themselves, but they
// can read their own details.
ResourceUser.Type: {ActionRead},
})...,
),
}.withCachedRegoValue()
auditorRole := Role{
@ -163,6 +167,10 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
// are not in.
ResourceTemplate.Type: {ActionRead},
ResourceAuditLog.Type: {ActionRead},
ResourceUser.Type: {ActionRead},
ResourceGroup.Type: {ActionRead},
// Org roles are not really used yet, so grant the perm at the site level.
ResourceOrganizationMember.Type: {ActionRead},
}),
Org: map[string][]Permission{},
User: []Permission{},
@ -180,6 +188,10 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
ResourceProvisionerDaemon.Type: {ActionCreate, ActionRead, ActionUpdate, ActionDelete},
// Needs to read all organizations since
ResourceOrganization.Type: {ActionRead},
ResourceUser.Type: {ActionRead},
ResourceGroup.Type: {ActionRead},
// Org roles are not really used yet, so grant the perm at the site level.
ResourceOrganizationMember.Type: {ActionRead},
}),
Org: map[string][]Permission{},
User: []Permission{},
@ -249,11 +261,6 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
Site: []Permission{},
Org: map[string][]Permission{
organizationID: {
{
// All org members can read the other members in their org.
ResourceType: ResourceOrganizationMember.Type,
Action: ActionRead,
},
{
// All org members can read the organization
ResourceType: ResourceOrganization.Type,
@ -264,13 +271,14 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
ResourceType: ResourceOrgRoleAssignment.Type,
Action: ActionRead,
},
{
ResourceType: ResourceGroup.Type,
Action: ActionRead,
},
},
},
User: []Permission{},
User: []Permission{
{
ResourceType: ResourceOrganizationMember.Type,
Action: ActionRead,
},
},
}
},
}

View File

@ -106,10 +106,10 @@ func TestRolePermissions(t *testing.T) {
{
Name: "MyUser",
Actions: []rbac.Action{rbac.ActionRead},
Resource: rbac.ResourceUser.WithID(currentUser),
Resource: rbac.ResourceUserObject(currentUser),
AuthorizeMap: map[bool][]authSubject{
true: {owner, memberMe, orgMemberMe, orgAdmin, otherOrgMember, otherOrgAdmin, templateAdmin, userAdmin},
false: {},
true: {orgMemberMe, owner, memberMe, templateAdmin, userAdmin},
false: {otherOrgMember, otherOrgAdmin, orgAdmin},
},
},
{
@ -281,7 +281,7 @@ func TestRolePermissions(t *testing.T) {
{
Name: "ManageOrgMember",
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete},
Resource: rbac.ResourceOrganizationMember.WithID(currentUser).InOrg(orgID),
Resource: rbac.ResourceOrganizationMember.WithID(currentUser).InOrg(orgID).WithOwner(currentUser.String()),
AuthorizeMap: map[bool][]authSubject{
true: {owner, orgAdmin, userAdmin},
false: {orgMemberMe, memberMe, otherOrgAdmin, otherOrgMember, templateAdmin},
@ -290,10 +290,10 @@ func TestRolePermissions(t *testing.T) {
{
Name: "ReadOrgMember",
Actions: []rbac.Action{rbac.ActionRead},
Resource: rbac.ResourceOrganizationMember.WithID(currentUser).InOrg(orgID),
Resource: rbac.ResourceOrganizationMember.WithID(currentUser).InOrg(orgID).WithOwner(currentUser.String()),
AuthorizeMap: map[bool][]authSubject{
true: {owner, orgAdmin, orgMemberMe, userAdmin},
false: {memberMe, otherOrgAdmin, otherOrgMember, templateAdmin},
true: {owner, orgAdmin, userAdmin, orgMemberMe, templateAdmin},
false: {memberMe, otherOrgAdmin, otherOrgMember},
},
},
{
@ -314,8 +314,8 @@ func TestRolePermissions(t *testing.T) {
Actions: []rbac.Action{rbac.ActionRead},
Resource: rbac.ResourceGroup.WithID(groupID).InOrg(orgID),
AuthorizeMap: map[bool][]authSubject{
true: {owner, orgAdmin, userAdmin, orgMemberMe},
false: {memberMe, otherOrgAdmin, otherOrgMember, templateAdmin},
true: {owner, orgAdmin, userAdmin, templateAdmin},
false: {memberMe, otherOrgAdmin, orgMemberMe, otherOrgMember},
},
},
{