From 74b52de2aea1fb65cdc9c667604a73cf3b3435cb Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 15 Aug 2022 21:07:51 +0000 Subject: [PATCH 01/14] fix: Role asign ui --- coderd/rbac/builtin.go | 3 +- coderd/roles.go | 30 ++++++++++++++----- site/src/api/api.ts | 4 +-- site/src/api/typesGenerated.ts | 13 ++++---- site/src/components/RoleSelect/RoleSelect.tsx | 6 ++-- site/src/components/UsersTable/UsersTable.tsx | 2 +- .../components/UsersTable/UsersTableBody.tsx | 22 +++++++------- site/src/pages/UsersPage/UsersPageView.tsx | 2 +- site/src/xServices/roles/siteRolesXService.ts | 4 +-- 9 files changed, 50 insertions(+), 36 deletions(-) diff --git a/coderd/rbac/builtin.go b/coderd/rbac/builtin.go index 24f45a300d3b1..0ce2d14b53dbc 100644 --- a/coderd/rbac/builtin.go +++ b/coderd/rbac/builtin.go @@ -123,7 +123,8 @@ var ( Name: userAdmin, DisplayName: "User Admin", Site: permissions(map[Object][]Action{ - ResourceUser: {ActionCreate, ActionRead, ActionUpdate, ActionDelete}, + ResourceRoleAssignment: {ActionCreate, ActionRead, ActionUpdate, ActionDelete}, + ResourceUser: {ActionCreate, ActionRead, ActionUpdate, ActionDelete}, }), } }, diff --git a/coderd/roles.go b/coderd/roles.go index c8f3d95e112af..48d3b2b37ec85 100644 --- a/coderd/roles.go +++ b/coderd/roles.go @@ -20,14 +20,21 @@ func (api *API) assignableSiteRoles(rw http.ResponseWriter, r *http.Request) { } roles := rbac.SiteRoles() - assignable := make([]rbac.Role, 0) + assignable := make([]codersdk.AssignableRoles, 0) for _, role := range roles { - if rbac.CanAssignRole(actorRoles.Roles, role.Name) { - assignable = append(assignable, role) + if role.DisplayName == "" { + continue } + assignable = append(assignable, codersdk.AssignableRoles{ + Role: codersdk.Role{ + Name: role.Name, + DisplayName: role.DisplayName, + }, + Assignable: rbac.CanAssignRole(actorRoles.Roles, role.Name), + }) } - httpapi.Write(rw, http.StatusOK, convertRoles(assignable)) + httpapi.Write(rw, http.StatusOK, assignable) } // assignableSiteRoles returns all site wide roles that can be assigned. @@ -41,14 +48,21 @@ func (api *API) assignableOrgRoles(rw http.ResponseWriter, r *http.Request) { } roles := rbac.OrganizationRoles(organization.ID) - assignable := make([]rbac.Role, 0) + assignable := make([]codersdk.AssignableRoles, 0) for _, role := range roles { - if rbac.CanAssignRole(actorRoles.Roles, role.Name) { - assignable = append(assignable, role) + if role.DisplayName == "" { + continue } + assignable = append(assignable, codersdk.AssignableRoles{ + Role: codersdk.Role{ + Name: role.Name, + DisplayName: role.DisplayName, + }, + Assignable: rbac.CanAssignRole(actorRoles.Roles, role.Name), + }) } - httpapi.Write(rw, http.StatusOK, convertRoles(assignable)) + httpapi.Write(rw, http.StatusOK, assignable) } func (api *API) checkPermissions(rw http.ResponseWriter, r *http.Request) { diff --git a/site/src/api/api.ts b/site/src/api/api.ts index fc232faf87d16..5d40a668400f3 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -311,8 +311,8 @@ export const updateUserPassword = async ( updatePassword: TypesGen.UpdateUserPasswordRequest, ): Promise => axios.put(`/api/v2/users/${userId}/password`, updatePassword) -export const getSiteRoles = async (): Promise> => { - const response = await axios.get>(`/api/v2/users/roles`) +export const getSiteRoles = async (): Promise> => { + const response = await axios.get>(`/api/v2/users/roles`) return response.data } diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index def9cd07e894a..cbb8f016122a2 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -24,6 +24,11 @@ export interface AgentGitSSHKey { readonly private_key: string } +// From codersdk/roles.go +export interface AssignableRoles extends Role { + readonly assignable: boolean +} + // From codersdk/users.go export interface AuthMethods { readonly password: boolean @@ -552,13 +557,7 @@ export type ParameterSourceScheme = "data" | "none" export type ParameterTypeSystem = "hcl" | "none" // From codersdk/provisionerdaemons.go -export type ProvisionerJobStatus = - | "canceled" - | "canceling" - | "failed" - | "pending" - | "running" - | "succeeded" +export type ProvisionerJobStatus = "canceled" | "canceling" | "failed" | "pending" | "running" | "succeeded" // From codersdk/organizations.go export type ProvisionerStorageMethod = "file" diff --git a/site/src/components/RoleSelect/RoleSelect.tsx b/site/src/components/RoleSelect/RoleSelect.tsx index a0bfb49bb25ee..ead95312150fd 100644 --- a/site/src/components/RoleSelect/RoleSelect.tsx +++ b/site/src/components/RoleSelect/RoleSelect.tsx @@ -3,13 +3,13 @@ import MenuItem from "@material-ui/core/MenuItem" import Select from "@material-ui/core/Select" import { makeStyles, Theme } from "@material-ui/core/styles" import { FC } from "react" -import { Role } from "../../api/typesGenerated" +import { Role, AssignableRoles } from "../../api/typesGenerated" export const Language = { label: "Roles", } export interface RoleSelectProps { - roles: Role[] + roles: AssignableRoles[] selectedRoles: Role[] onChange: (roles: Role["name"][]) => void loading?: boolean @@ -46,7 +46,7 @@ export const RoleSelect: FC = ({ const isChecked = selectedRoles.some((selectedRole) => selectedRole.name === r.name) return ( - + {r.display_name} ) diff --git a/site/src/components/UsersTable/UsersTable.tsx b/site/src/components/UsersTable/UsersTable.tsx index fc49dca3b1b34..8dcb2d8fa8e25 100644 --- a/site/src/components/UsersTable/UsersTable.tsx +++ b/site/src/components/UsersTable/UsersTable.tsx @@ -18,7 +18,7 @@ export const Language = { export interface UsersTableProps { users?: TypesGen.User[] - roles?: TypesGen.Role[] + roles?: TypesGen.AssignableRoles[] isUpdatingUserRoles?: boolean canEditUsers?: boolean isLoading?: boolean diff --git a/site/src/components/UsersTable/UsersTableBody.tsx b/site/src/components/UsersTable/UsersTableBody.tsx index 3e021e6f7c79d..b71730bb5f1ec 100644 --- a/site/src/components/UsersTable/UsersTableBody.tsx +++ b/site/src/components/UsersTable/UsersTableBody.tsx @@ -20,7 +20,7 @@ export const Language = { interface UsersTableBodyProps { users?: TypesGen.User[] - roles?: TypesGen.Role[] + roles?: TypesGen.AssignableRoles[] isUpdatingUserRoles?: boolean canEditUsers?: boolean isLoading?: boolean @@ -106,17 +106,17 @@ export const UsersTableBody: FC = ({ // Return either suspend or activate depending on status (user.status === "active" ? [ - { - label: Language.suspendMenuItem, - onClick: onSuspendUser, - }, - ] + { + label: Language.suspendMenuItem, + onClick: onSuspendUser, + }, + ] : [ - { - label: Language.activateMenuItem, - onClick: onActivateUser, - }, - ] + { + label: Language.activateMenuItem, + onClick: onActivateUser, + }, + ] ).concat({ label: Language.resetPasswordMenuItem, onClick: onResetUserPassword, diff --git a/site/src/pages/UsersPage/UsersPageView.tsx b/site/src/pages/UsersPage/UsersPageView.tsx index d411282a89177..a11b68bcb4980 100644 --- a/site/src/pages/UsersPage/UsersPageView.tsx +++ b/site/src/pages/UsersPage/UsersPageView.tsx @@ -17,7 +17,7 @@ export const Language = { export interface UsersPageViewProps { users?: TypesGen.User[] - roles?: TypesGen.Role[] + roles?: TypesGen.AssignableRoles[] filter?: string error?: unknown isUpdatingUserRoles?: boolean diff --git a/site/src/xServices/roles/siteRolesXService.ts b/site/src/xServices/roles/siteRolesXService.ts index 2380150581fb4..ee9eaa47d8198 100644 --- a/site/src/xServices/roles/siteRolesXService.ts +++ b/site/src/xServices/roles/siteRolesXService.ts @@ -8,7 +8,7 @@ export const Language = { } type SiteRolesContext = { - roles?: TypesGen.Role[] + roles?: TypesGen.AssignableRoles[] getRolesError: Error | unknown } @@ -25,7 +25,7 @@ export const siteRolesMachine = createMachine( events: {} as SiteRolesEvent, services: { getRoles: { - data: {} as TypesGen.Role[], + data: {} as TypesGen.AssignableRoles[], }, }, }, From daf8a98a1ebfdf6f9e64a90972ca953f3fd3bcf7 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 15 Aug 2022 21:11:08 +0000 Subject: [PATCH 02/14] fixup! fix: Role asign ui --- codersdk/roles.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/codersdk/roles.go b/codersdk/roles.go index d6e34c7e48127..34e2800ac5a42 100644 --- a/codersdk/roles.go +++ b/codersdk/roles.go @@ -14,8 +14,13 @@ type Role struct { DisplayName string `json:"display_name"` } +type AssignableRoles struct { + Role + Assignable bool `json:"assignable"` +} + // ListSiteRoles lists all assignable site wide roles. -func (c *Client) ListSiteRoles(ctx context.Context) ([]Role, error) { +func (c *Client) ListSiteRoles(ctx context.Context) ([]AssignableRoles, error) { res, err := c.Request(ctx, http.MethodGet, "/api/v2/users/roles", nil) if err != nil { return nil, err @@ -24,12 +29,12 @@ func (c *Client) ListSiteRoles(ctx context.Context) ([]Role, error) { if res.StatusCode != http.StatusOK { return nil, readBodyAsError(res) } - var roles []Role + var roles []AssignableRoles return roles, json.NewDecoder(res.Body).Decode(&roles) } // ListOrganizationRoles lists all assignable roles for a given organization. -func (c *Client) ListOrganizationRoles(ctx context.Context, org uuid.UUID) ([]Role, error) { +func (c *Client) ListOrganizationRoles(ctx context.Context, org uuid.UUID) ([]AssignableRoles, error) { res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s/members/roles", org.String()), nil) if err != nil { return nil, err @@ -38,7 +43,7 @@ func (c *Client) ListOrganizationRoles(ctx context.Context, org uuid.UUID) ([]Ro if res.StatusCode != http.StatusOK { return nil, readBodyAsError(res) } - var roles []Role + var roles []AssignableRoles return roles, json.NewDecoder(res.Body).Decode(&roles) } From ef93908469152489f200a04c6fb12ff096415f5c Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 15 Aug 2022 21:15:47 +0000 Subject: [PATCH 03/14] fix: Give user admin ability to make org members --- coderd/rbac/builtin.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/coderd/rbac/builtin.go b/coderd/rbac/builtin.go index 0ce2d14b53dbc..bfa6ded0a5250 100644 --- a/coderd/rbac/builtin.go +++ b/coderd/rbac/builtin.go @@ -125,6 +125,8 @@ var ( Site: permissions(map[Object][]Action{ ResourceRoleAssignment: {ActionCreate, ActionRead, ActionUpdate, ActionDelete}, ResourceUser: {ActionCreate, ActionRead, ActionUpdate, ActionDelete}, + // Full perms to manage org members + ResourceOrganizationMember: {ActionCreate, ActionRead, ActionUpdate, ActionDelete}, }), } }, @@ -197,6 +199,10 @@ var ( templateAdmin: true, userAdmin: true, }, + userAdmin: { + member: true, + orgMember: true, + }, orgAdmin: { orgAdmin: true, orgMember: true, From f49870e29d3ced99c5ae30410ad0b0e1d3b4ea0a Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 15 Aug 2022 21:17:11 +0000 Subject: [PATCH 04/14] Make fmt --- site/src/api/typesGenerated.ts | 8 +++++++- site/src/components/RoleSelect/RoleSelect.tsx | 2 +- .../components/UsersTable/UsersTableBody.tsx | 20 +++++++++---------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index cbb8f016122a2..e849b1a1f051f 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -557,7 +557,13 @@ export type ParameterSourceScheme = "data" | "none" export type ParameterTypeSystem = "hcl" | "none" // From codersdk/provisionerdaemons.go -export type ProvisionerJobStatus = "canceled" | "canceling" | "failed" | "pending" | "running" | "succeeded" +export type ProvisionerJobStatus = + | "canceled" + | "canceling" + | "failed" + | "pending" + | "running" + | "succeeded" // From codersdk/organizations.go export type ProvisionerStorageMethod = "file" diff --git a/site/src/components/RoleSelect/RoleSelect.tsx b/site/src/components/RoleSelect/RoleSelect.tsx index ead95312150fd..1a77eb02784ef 100644 --- a/site/src/components/RoleSelect/RoleSelect.tsx +++ b/site/src/components/RoleSelect/RoleSelect.tsx @@ -3,7 +3,7 @@ import MenuItem from "@material-ui/core/MenuItem" import Select from "@material-ui/core/Select" import { makeStyles, Theme } from "@material-ui/core/styles" import { FC } from "react" -import { Role, AssignableRoles } from "../../api/typesGenerated" +import { AssignableRoles, Role } from "../../api/typesGenerated" export const Language = { label: "Roles", diff --git a/site/src/components/UsersTable/UsersTableBody.tsx b/site/src/components/UsersTable/UsersTableBody.tsx index b71730bb5f1ec..441c13e372fd9 100644 --- a/site/src/components/UsersTable/UsersTableBody.tsx +++ b/site/src/components/UsersTable/UsersTableBody.tsx @@ -106,17 +106,17 @@ export const UsersTableBody: FC = ({ // Return either suspend or activate depending on status (user.status === "active" ? [ - { - label: Language.suspendMenuItem, - onClick: onSuspendUser, - }, - ] + { + label: Language.suspendMenuItem, + onClick: onSuspendUser, + }, + ] : [ - { - label: Language.activateMenuItem, - onClick: onActivateUser, - }, - ] + { + label: Language.activateMenuItem, + onClick: onActivateUser, + }, + ] ).concat({ label: Language.resetPasswordMenuItem, onClick: onResetUserPassword, From 42ff3638dbcf3178a00300cf36ffc9ee4f0c36f4 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 15 Aug 2022 16:27:53 -0500 Subject: [PATCH 05/14] Update unit test --- coderd/roles.go | 47 ++++++++---------------------- coderd/roles_test.go | 69 ++++++++++++++++++++++++++++++-------------- 2 files changed, 59 insertions(+), 57 deletions(-) diff --git a/coderd/roles.go b/coderd/roles.go index 48d3b2b37ec85..3370d2248b99b 100644 --- a/coderd/roles.go +++ b/coderd/roles.go @@ -20,21 +20,7 @@ func (api *API) assignableSiteRoles(rw http.ResponseWriter, r *http.Request) { } roles := rbac.SiteRoles() - assignable := make([]codersdk.AssignableRoles, 0) - for _, role := range roles { - if role.DisplayName == "" { - continue - } - assignable = append(assignable, codersdk.AssignableRoles{ - Role: codersdk.Role{ - Name: role.Name, - DisplayName: role.DisplayName, - }, - Assignable: rbac.CanAssignRole(actorRoles.Roles, role.Name), - }) - } - - httpapi.Write(rw, http.StatusOK, assignable) + httpapi.Write(rw, http.StatusOK, assignableRoles(actorRoles.Roles, roles)) } // assignableSiteRoles returns all site wide roles that can be assigned. @@ -48,21 +34,7 @@ func (api *API) assignableOrgRoles(rw http.ResponseWriter, r *http.Request) { } roles := rbac.OrganizationRoles(organization.ID) - assignable := make([]codersdk.AssignableRoles, 0) - for _, role := range roles { - if role.DisplayName == "" { - continue - } - assignable = append(assignable, codersdk.AssignableRoles{ - Role: codersdk.Role{ - Name: role.Name, - DisplayName: role.DisplayName, - }, - Assignable: rbac.CanAssignRole(actorRoles.Roles, role.Name), - }) - } - - httpapi.Write(rw, http.StatusOK, assignable) + httpapi.Write(rw, http.StatusOK, assignableRoles(actorRoles.Roles, roles)) } func (api *API) checkPermissions(rw http.ResponseWriter, r *http.Request) { @@ -116,14 +88,19 @@ func convertRole(role rbac.Role) codersdk.Role { } } -func convertRoles(roles []rbac.Role) []codersdk.Role { - converted := make([]codersdk.Role, 0, len(roles)) +func assignableRoles(actorRoles []string, roles []rbac.Role) []codersdk.AssignableRoles { + assignable := make([]codersdk.AssignableRoles, 0) for _, role := range roles { - // Roles without display names should never be shown to the ui. if role.DisplayName == "" { continue } - converted = append(converted, convertRole(role)) + assignable = append(assignable, codersdk.AssignableRoles{ + Role: codersdk.Role{ + Name: role.Name, + DisplayName: role.DisplayName, + }, + Assignable: rbac.CanAssignRole(actorRoles, role.Name), + }) } - return converted + return assignable } diff --git a/coderd/roles_test.go b/coderd/roles_test.go index 7dcc5354d7355..ecdcbcf96aa21 100644 --- a/coderd/roles_test.go +++ b/coderd/roles_test.go @@ -120,35 +120,42 @@ func TestListRoles(t *testing.T) { require.NoError(t, err, "create org") const forbidden = "Forbidden" - siteRoles := convertRoles(rbac.RoleOwner(), "auditor", "template-admin", "user-admin") - orgRoles := convertRoles(rbac.RoleOrgAdmin(admin.OrganizationID)) + //siteRoles := convertRoles(rbac.RoleOwner(), "auditor", "template-admin", "user-admin") + //orgRoles := convertRoles(rbac.RoleOrgAdmin(admin.OrganizationID)) testCases := []struct { Name string Client *codersdk.Client - APICall func(context.Context) ([]codersdk.Role, error) - ExpectedRoles []codersdk.Role + APICall func(context.Context) ([]codersdk.AssignableRoles, error) + ExpectedRoles []codersdk.AssignableRoles AuthorizedError string }{ { // Members cannot assign any roles Name: "MemberListSite", - APICall: func(ctx context.Context) ([]codersdk.Role, error) { + APICall: func(ctx context.Context) ([]codersdk.AssignableRoles, error) { x, err := member.ListSiteRoles(ctx) return x, err }, - ExpectedRoles: []codersdk.Role{}, + ExpectedRoles: convertRoles(map[string]bool{ + "owner": false, + "auditor": false, + "template-admin": false, + "user-admin": false, + }), }, { Name: "OrgMemberListOrg", - APICall: func(ctx context.Context) ([]codersdk.Role, error) { + APICall: func(ctx context.Context) ([]codersdk.AssignableRoles, error) { return member.ListOrganizationRoles(ctx, admin.OrganizationID) }, - ExpectedRoles: []codersdk.Role{}, + ExpectedRoles: convertRoles(map[string]bool{ + rbac.RoleOrgAdmin(admin.OrganizationID): false, + }), }, { Name: "NonOrgMemberListOrg", - APICall: func(ctx context.Context) ([]codersdk.Role, error) { + APICall: func(ctx context.Context) ([]codersdk.AssignableRoles, error) { return member.ListOrganizationRoles(ctx, otherOrg.ID) }, AuthorizedError: forbidden, @@ -156,21 +163,28 @@ func TestListRoles(t *testing.T) { // Org admin { Name: "OrgAdminListSite", - APICall: func(ctx context.Context) ([]codersdk.Role, error) { + APICall: func(ctx context.Context) ([]codersdk.AssignableRoles, error) { return orgAdmin.ListSiteRoles(ctx) }, - ExpectedRoles: []codersdk.Role{}, + ExpectedRoles: convertRoles(map[string]bool{ + "owner": false, + "auditor": false, + "template-admin": false, + "user-admin": false, + }), }, { Name: "OrgAdminListOrg", - APICall: func(ctx context.Context) ([]codersdk.Role, error) { + APICall: func(ctx context.Context) ([]codersdk.AssignableRoles, error) { return orgAdmin.ListOrganizationRoles(ctx, admin.OrganizationID) }, - ExpectedRoles: orgRoles, + ExpectedRoles: convertRoles(map[string]bool{ + rbac.RoleOrgAdmin(admin.OrganizationID): true, + }), }, { Name: "OrgAdminListOtherOrg", - APICall: func(ctx context.Context) ([]codersdk.Role, error) { + APICall: func(ctx context.Context) ([]codersdk.AssignableRoles, error) { return orgAdmin.ListOrganizationRoles(ctx, otherOrg.ID) }, AuthorizedError: forbidden, @@ -178,17 +192,24 @@ func TestListRoles(t *testing.T) { // Admin { Name: "AdminListSite", - APICall: func(ctx context.Context) ([]codersdk.Role, error) { + APICall: func(ctx context.Context) ([]codersdk.AssignableRoles, error) { return client.ListSiteRoles(ctx) }, - ExpectedRoles: siteRoles, + ExpectedRoles: convertRoles(map[string]bool{ + "owner": true, + "auditor": true, + "template-admin": true, + "user-admin": true, + }), }, { Name: "AdminListOrg", - APICall: func(ctx context.Context) ([]codersdk.Role, error) { + APICall: func(ctx context.Context) ([]codersdk.AssignableRoles, error) { return client.ListOrganizationRoles(ctx, admin.OrganizationID) }, - ExpectedRoles: orgRoles, + ExpectedRoles: convertRoles(map[string]bool{ + rbac.RoleOrgAdmin(admin.OrganizationID): true, + }), }, } @@ -222,10 +243,14 @@ func convertRole(roleName string) codersdk.Role { } } -func convertRoles(roleNames ...string) []codersdk.Role { - converted := make([]codersdk.Role, 0, len(roleNames)) - for _, roleName := range roleNames { - converted = append(converted, convertRole(roleName)) +func convertRoles(assignableRoles map[string]bool) []codersdk.AssignableRoles { + converted := make([]codersdk.AssignableRoles, 0, len(assignableRoles)) + for roleName, assignable := range assignableRoles { + role := convertRole(roleName) + converted = append(converted, codersdk.AssignableRoles{ + Role: role, + Assignable: assignable, + }) } return converted } From bf7464bd3cd97e71dbd8e786038758d4ba920157 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 15 Aug 2022 16:32:13 -0500 Subject: [PATCH 06/14] Update unit test of what roles can do --- coderd/rbac/builtin_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/coderd/rbac/builtin_test.go b/coderd/rbac/builtin_test.go index 9936c2e1385cb..f68bcecb89ca4 100644 --- a/coderd/rbac/builtin_test.go +++ b/coderd/rbac/builtin_test.go @@ -251,8 +251,8 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete}, Resource: rbac.ResourceRoleAssignment, AuthorizeMap: map[bool][]authSubject{ - true: {admin}, - false: {orgAdmin, orgMemberMe, otherOrgAdmin, otherOrgMember, memberMe, templateAdmin, userAdmin}, + true: {admin, userAdmin}, + false: {orgAdmin, orgMemberMe, otherOrgAdmin, otherOrgMember, memberMe, templateAdmin}, }, }, { @@ -305,8 +305,8 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete}, Resource: rbac.ResourceOrganizationMember.InOrg(orgID), AuthorizeMap: map[bool][]authSubject{ - true: {admin, orgAdmin}, - false: {orgMemberMe, memberMe, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin}, + true: {admin, orgAdmin, userAdmin}, + false: {orgMemberMe, memberMe, otherOrgAdmin, otherOrgMember, templateAdmin}, }, }, { @@ -314,8 +314,8 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionRead}, Resource: rbac.ResourceOrganizationMember.InOrg(orgID), AuthorizeMap: map[bool][]authSubject{ - true: {admin, orgAdmin, orgMemberMe}, - false: {memberMe, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin}, + true: {admin, orgAdmin, orgMemberMe, userAdmin}, + false: {memberMe, otherOrgAdmin, otherOrgMember, templateAdmin}, }, }, } From 88a6f6be5b53ef2da099edc7363ad0d3a7304d05 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 16 Aug 2022 08:58:03 -0500 Subject: [PATCH 07/14] PR cleanup --- coderd/rbac/builtin_test.go | 42 ++++++++++++++++++------------------- coderd/roles_test.go | 3 --- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/coderd/rbac/builtin_test.go b/coderd/rbac/builtin_test.go index f68bcecb89ca4..bbedc1e48527c 100644 --- a/coderd/rbac/builtin_test.go +++ b/coderd/rbac/builtin_test.go @@ -119,7 +119,7 @@ func TestRolePermissions(t *testing.T) { memberMe := authSubject{Name: "member_me", UserID: currentUser.String(), Roles: []string{rbac.RoleMember()}} orgMemberMe := authSubject{Name: "org_member_me", UserID: currentUser.String(), Roles: []string{rbac.RoleMember(), rbac.RoleOrgMember(orgID)}} - admin := authSubject{Name: "admin", UserID: adminID.String(), Roles: []string{rbac.RoleMember(), rbac.RoleOwner()}} + owner := authSubject{Name: "owner", UserID: adminID.String(), Roles: []string{rbac.RoleMember(), rbac.RoleOwner()}} orgAdmin := authSubject{Name: "org_admin", UserID: adminID.String(), Roles: []string{rbac.RoleMember(), rbac.RoleOrgMember(orgID), rbac.RoleOrgAdmin(orgID)}} otherOrgMember := authSubject{Name: "org_member_other", UserID: uuid.NewString(), Roles: []string{rbac.RoleMember(), rbac.RoleOrgMember(otherOrg)}} @@ -130,7 +130,7 @@ func TestRolePermissions(t *testing.T) { // requiredSubjects are required to be asserted in each test case. This is // to make sure one is not forgotten. - requiredSubjects := []authSubject{memberMe, admin, orgMemberMe, orgAdmin, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin} + requiredSubjects := []authSubject{memberMe, owner, orgMemberMe, orgAdmin, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin} testCases := []struct { // Name the test case to better locate the failing test case. @@ -150,7 +150,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionRead}, Resource: rbac.ResourceUser, AuthorizeMap: map[bool][]authSubject{ - true: {admin, memberMe, orgMemberMe, orgAdmin, otherOrgMember, otherOrgAdmin, templateAdmin, userAdmin}, + true: {owner, memberMe, orgMemberMe, orgAdmin, otherOrgMember, otherOrgAdmin, templateAdmin, userAdmin}, false: {}, }, }, @@ -159,7 +159,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete}, Resource: rbac.ResourceUser, AuthorizeMap: map[bool][]authSubject{ - true: {admin, userAdmin}, + true: {owner, userAdmin}, false: {memberMe, orgMemberMe, orgAdmin, otherOrgMember, otherOrgAdmin, templateAdmin}, }, }, @@ -169,7 +169,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete}, Resource: rbac.ResourceWorkspace.InOrg(orgID).WithOwner(currentUser.String()), AuthorizeMap: map[bool][]authSubject{ - true: {admin, orgMemberMe, orgAdmin, templateAdmin}, + true: {owner, orgMemberMe, orgAdmin, templateAdmin}, false: {memberMe, otherOrgAdmin, otherOrgMember, userAdmin}, }, }, @@ -179,7 +179,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete}, Resource: rbac.ResourceWorkspaceExecution.InOrg(orgID).WithOwner(currentUser.String()), AuthorizeMap: map[bool][]authSubject{ - true: {admin, orgAdmin, orgMemberMe}, + true: {owner, orgAdmin, orgMemberMe}, false: {memberMe, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin}, }, }, @@ -188,7 +188,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete}, Resource: rbac.ResourceTemplate.InOrg(orgID), AuthorizeMap: map[bool][]authSubject{ - true: {admin, orgAdmin, templateAdmin}, + true: {owner, orgAdmin, templateAdmin}, false: {memberMe, orgMemberMe, otherOrgAdmin, otherOrgMember, userAdmin}, }, }, @@ -197,7 +197,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionRead}, Resource: rbac.ResourceTemplate.InOrg(orgID), AuthorizeMap: map[bool][]authSubject{ - true: {admin, orgMemberMe, orgAdmin, templateAdmin}, + true: {owner, orgMemberMe, orgAdmin, templateAdmin}, false: {memberMe, otherOrgAdmin, otherOrgMember, userAdmin}, }, }, @@ -206,7 +206,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionCreate}, Resource: rbac.ResourceFile, AuthorizeMap: map[bool][]authSubject{ - true: {admin, templateAdmin}, + true: {owner, templateAdmin}, false: {orgMemberMe, orgAdmin, memberMe, otherOrgAdmin, otherOrgMember, userAdmin}, }, }, @@ -215,7 +215,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete}, Resource: rbac.ResourceFile.WithOwner(currentUser.String()), AuthorizeMap: map[bool][]authSubject{ - true: {admin, memberMe, orgMemberMe, templateAdmin}, + true: {owner, memberMe, orgMemberMe, templateAdmin}, false: {orgAdmin, otherOrgAdmin, otherOrgMember, userAdmin}, }, }, @@ -224,7 +224,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionCreate}, Resource: rbac.ResourceOrganization, AuthorizeMap: map[bool][]authSubject{ - true: {admin}, + true: {owner}, false: {orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin}, }, }, @@ -233,7 +233,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionUpdate, rbac.ActionDelete}, Resource: rbac.ResourceOrganization.InOrg(orgID), AuthorizeMap: map[bool][]authSubject{ - true: {admin, orgAdmin}, + true: {owner, orgAdmin}, false: {otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin}, }, }, @@ -242,7 +242,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionRead}, Resource: rbac.ResourceOrganization.InOrg(orgID), AuthorizeMap: map[bool][]authSubject{ - true: {admin, orgAdmin, orgMemberMe}, + true: {owner, orgAdmin, orgMemberMe}, false: {otherOrgAdmin, otherOrgMember, memberMe, templateAdmin, userAdmin}, }, }, @@ -251,7 +251,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete}, Resource: rbac.ResourceRoleAssignment, AuthorizeMap: map[bool][]authSubject{ - true: {admin, userAdmin}, + true: {owner, userAdmin}, false: {orgAdmin, orgMemberMe, otherOrgAdmin, otherOrgMember, memberMe, templateAdmin}, }, }, @@ -260,7 +260,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionRead}, Resource: rbac.ResourceRoleAssignment, AuthorizeMap: map[bool][]authSubject{ - true: {admin, orgAdmin, orgMemberMe, otherOrgAdmin, otherOrgMember, memberMe, templateAdmin, userAdmin}, + true: {owner, orgAdmin, orgMemberMe, otherOrgAdmin, otherOrgMember, memberMe, templateAdmin, userAdmin}, false: {}, }, }, @@ -269,7 +269,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete}, Resource: rbac.ResourceOrgRoleAssignment.InOrg(orgID), AuthorizeMap: map[bool][]authSubject{ - true: {admin, orgAdmin}, + true: {owner, orgAdmin}, false: {orgMemberMe, otherOrgAdmin, otherOrgMember, memberMe, templateAdmin, userAdmin}, }, }, @@ -278,7 +278,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionRead}, Resource: rbac.ResourceOrgRoleAssignment.InOrg(orgID), AuthorizeMap: map[bool][]authSubject{ - true: {admin, orgAdmin, orgMemberMe}, + true: {owner, orgAdmin, orgMemberMe}, false: {otherOrgAdmin, otherOrgMember, memberMe, templateAdmin, userAdmin}, }, }, @@ -287,7 +287,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete}, Resource: rbac.ResourceAPIKey.WithOwner(currentUser.String()), AuthorizeMap: map[bool][]authSubject{ - true: {admin, orgMemberMe, memberMe}, + true: {owner, orgMemberMe, memberMe}, false: {orgAdmin, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin}, }, }, @@ -296,7 +296,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete}, Resource: rbac.ResourceUserData.WithOwner(currentUser.String()), AuthorizeMap: map[bool][]authSubject{ - true: {admin, orgMemberMe, memberMe}, + true: {owner, orgMemberMe, memberMe}, false: {orgAdmin, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin}, }, }, @@ -305,7 +305,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete}, Resource: rbac.ResourceOrganizationMember.InOrg(orgID), AuthorizeMap: map[bool][]authSubject{ - true: {admin, orgAdmin, userAdmin}, + true: {owner, orgAdmin, userAdmin}, false: {orgMemberMe, memberMe, otherOrgAdmin, otherOrgMember, templateAdmin}, }, }, @@ -314,7 +314,7 @@ func TestRolePermissions(t *testing.T) { Actions: []rbac.Action{rbac.ActionRead}, Resource: rbac.ResourceOrganizationMember.InOrg(orgID), AuthorizeMap: map[bool][]authSubject{ - true: {admin, orgAdmin, orgMemberMe, userAdmin}, + true: {owner, orgAdmin, orgMemberMe, userAdmin}, false: {memberMe, otherOrgAdmin, otherOrgMember, templateAdmin}, }, }, diff --git a/coderd/roles_test.go b/coderd/roles_test.go index ecdcbcf96aa21..034be6a6bb43a 100644 --- a/coderd/roles_test.go +++ b/coderd/roles_test.go @@ -120,9 +120,6 @@ func TestListRoles(t *testing.T) { require.NoError(t, err, "create org") const forbidden = "Forbidden" - //siteRoles := convertRoles(rbac.RoleOwner(), "auditor", "template-admin", "user-admin") - //orgRoles := convertRoles(rbac.RoleOrgAdmin(admin.OrganizationID)) - testCases := []struct { Name string Client *codersdk.Client From 1c7bae311cde42f323587be0f682fa6241d00f3a Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 16 Aug 2022 09:19:35 -0500 Subject: [PATCH 08/14] Add frontend tests --- .../RoleSelect/RoleSelect.stories.tsx | 30 ++++++++++--- .../components/RoleSelect/RoleSelect.test.tsx | 40 +++++++++++++++++ .../UserDropdownContent.test.tsx | 4 +- site/src/testHelpers/entities.ts | 44 ++++++++++++++++--- 4 files changed, 106 insertions(+), 12 deletions(-) create mode 100644 site/src/components/RoleSelect/RoleSelect.test.tsx diff --git a/site/src/components/RoleSelect/RoleSelect.stories.tsx b/site/src/components/RoleSelect/RoleSelect.stories.tsx index ab3949baa63ac..b8fac39bba592 100644 --- a/site/src/components/RoleSelect/RoleSelect.stories.tsx +++ b/site/src/components/RoleSelect/RoleSelect.stories.tsx @@ -1,5 +1,10 @@ import { ComponentMeta, Story } from "@storybook/react" -import { MockAdminRole, MockMemberRole, MockSiteRoles } from "../../testHelpers/renderHelpers" +import { + assignableRole, + MockAssignableSiteRoles, MockAuditorRole, + MockMemberRole, MockOwnerRole, MockTemplateAdminRole, + MockUserAdminRole +} from "../../testHelpers/renderHelpers" import { RoleSelect, RoleSelectProps } from "./RoleSelect" export default { @@ -9,15 +14,30 @@ export default { const Template: Story = (args) => +// Include 4 roles: +// - owner (disabled, not checked) +// - template admin (disabled, checked) +// - auditor (enabled, not checked) +// - user admin (enabled, checked) export const Close = Template.bind({}) Close.args = { - roles: MockSiteRoles, - selectedRoles: [MockAdminRole, MockMemberRole], + roles: [ + assignableRole(MockOwnerRole, false), + assignableRole(MockTemplateAdminRole, false), + assignableRole(MockAuditorRole, true), + assignableRole(MockUserAdminRole, true), + ], + selectedRoles: [MockUserAdminRole, MockTemplateAdminRole, MockMemberRole], } export const Open = Template.bind({}) Open.args = { open: true, - roles: MockSiteRoles, - selectedRoles: [MockAdminRole, MockMemberRole], + roles: [ + assignableRole(MockOwnerRole, false), + assignableRole(MockTemplateAdminRole, false), + assignableRole(MockAuditorRole, true), + assignableRole(MockUserAdminRole, true), + ], + selectedRoles: [MockUserAdminRole, MockTemplateAdminRole, MockMemberRole], } diff --git a/site/src/components/RoleSelect/RoleSelect.test.tsx b/site/src/components/RoleSelect/RoleSelect.test.tsx new file mode 100644 index 0000000000000..7afd8a771ae90 --- /dev/null +++ b/site/src/components/RoleSelect/RoleSelect.test.tsx @@ -0,0 +1,40 @@ +import { screen } from "@testing-library/react" +import { + assignableRole, + MockAssignableSiteRoles, + MockAuditorRole, MockMemberRole, + MockOwnerRole, MockTemplateAdminRole, + MockUserAdminRole, + render +} from "../../testHelpers/renderHelpers" +import { RoleSelect } from "./RoleSelect" + +describe("UserRoleSelect", () => { + it("renders content", async () => { + // When + render() + + + // Then + const owner = await screen.findByText(MockOwnerRole.display_name) + const templateAdmin = await screen.findByText(MockTemplateAdminRole.display_name) + const auditor = await screen.findByText(MockAuditorRole.display_name) + const userAdmin = await screen.findByText(MockUserAdminRole.display_name) + + expect(owner).toHaveProperty('disabled', true) + expect(templateAdmin).toHaveProperty('disabled', true) + expect(auditor).toHaveProperty('disabled', true) + expect(userAdmin).toHaveProperty('disabled', true) + }) +}) diff --git a/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx b/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx index 48b123ebdd06c..652341cd81ad8 100644 --- a/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx +++ b/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx @@ -1,5 +1,5 @@ import { screen } from "@testing-library/react" -import { MockAdminRole, MockUser } from "../../testHelpers/entities" +import { MockOwnerRole, MockUser } from "../../testHelpers/entities" import { render } from "../../testHelpers/renderHelpers" import { Language, UserDropdownContent } from "./UserDropdownContent" @@ -26,7 +26,7 @@ describe("UserDropdownContent", () => { it("displays the user's roles", () => { render() - expect(screen.getByText(MockAdminRole.display_name)).toBeDefined() + expect(screen.getByText(MockOwnerRole.display_name)).toBeDefined() }) it("has the correct link for the account item", () => { diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 1e08d88f4eeb8..ab1284c176626 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -15,9 +15,30 @@ export const MockBuildInfo: TypesGen.BuildInfoResponse = { version: "v99.999.9999+c9cdf14", } -export const MockAdminRole: TypesGen.Role = { - name: "admin", - display_name: "Admin", +export const MockOwnerRole: TypesGen.Role = { + name: "owner", + display_name: "Owner", +} + +export const MockAssignableOwnerRole: TypesGen.AssignableRoles = { + ...MockOwnerRole, + assignable: false, +} + +export const MockUserAdminRole: TypesGen.Role = { + name: "user_admin", + display_name: "User Admin", +} + +export const MockTemplateAdminRole: TypesGen.Role = { + name: "template_admin", + display_name: "Template Admin", +} + + +export const MockAssignableUserAdminRole: TypesGen.AssignableRoles = { + ...MockUserAdminRole, + assignable: true, } export const MockMemberRole: TypesGen.Role = { @@ -30,7 +51,20 @@ export const MockAuditorRole: TypesGen.Role = { display_name: "Auditor", } -export const MockSiteRoles = [MockAdminRole, MockAuditorRole] +export const MockAssignableAuditorRole: TypesGen.AssignableRoles = { + ...MockAuditorRole, + assignable: true, +} + +export const MockSiteRoles = [MockUserAdminRole, MockAuditorRole] +export const MockAssignableSiteRoles = [MockAssignableOwnerRole, MockAssignableUserAdminRole, MockAssignableAuditorRole] + +export function assignableRole(role: TypesGen.Role, assignable: boolean): TypesGen.AssignableRoles { + return { + ...role, + assignable: assignable, + } +} export const MockUser: TypesGen.User = { id: "test-user", @@ -39,7 +73,7 @@ export const MockUser: TypesGen.User = { created_at: "", status: "active", organization_ids: ["fc0774ce-cc9e-48d4-80ae-88f7a4d4a8b0"], - roles: [MockAdminRole], + roles: [MockOwnerRole], } export const MockUser2: TypesGen.User = { From bad62770a7a420d8b3f36fe4333311e36087e3a5 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 16 Aug 2022 09:45:58 -0500 Subject: [PATCH 09/14] Make fmt --- .../RoleSelect/RoleSelect.stories.tsx | 8 ++-- .../components/RoleSelect/RoleSelect.test.tsx | 44 ++++++++++--------- site/src/testHelpers/entities.ts | 7 ++- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/site/src/components/RoleSelect/RoleSelect.stories.tsx b/site/src/components/RoleSelect/RoleSelect.stories.tsx index b8fac39bba592..7cb6717873f8d 100644 --- a/site/src/components/RoleSelect/RoleSelect.stories.tsx +++ b/site/src/components/RoleSelect/RoleSelect.stories.tsx @@ -1,9 +1,11 @@ import { ComponentMeta, Story } from "@storybook/react" import { assignableRole, - MockAssignableSiteRoles, MockAuditorRole, - MockMemberRole, MockOwnerRole, MockTemplateAdminRole, - MockUserAdminRole + MockAuditorRole, + MockMemberRole, + MockOwnerRole, + MockTemplateAdminRole, + MockUserAdminRole, } from "../../testHelpers/renderHelpers" import { RoleSelect, RoleSelectProps } from "./RoleSelect" diff --git a/site/src/components/RoleSelect/RoleSelect.test.tsx b/site/src/components/RoleSelect/RoleSelect.test.tsx index 7afd8a771ae90..98034eead56ec 100644 --- a/site/src/components/RoleSelect/RoleSelect.test.tsx +++ b/site/src/components/RoleSelect/RoleSelect.test.tsx @@ -1,30 +1,32 @@ import { screen } from "@testing-library/react" import { assignableRole, - MockAssignableSiteRoles, - MockAuditorRole, MockMemberRole, - MockOwnerRole, MockTemplateAdminRole, + MockAuditorRole, + MockMemberRole, + MockOwnerRole, + MockTemplateAdminRole, MockUserAdminRole, - render + render, } from "../../testHelpers/renderHelpers" import { RoleSelect } from "./RoleSelect" describe("UserRoleSelect", () => { it("renders content", async () => { // When - render() - + render( + , + ) // Then const owner = await screen.findByText(MockOwnerRole.display_name) @@ -32,9 +34,9 @@ describe("UserRoleSelect", () => { const auditor = await screen.findByText(MockAuditorRole.display_name) const userAdmin = await screen.findByText(MockUserAdminRole.display_name) - expect(owner).toHaveProperty('disabled', true) - expect(templateAdmin).toHaveProperty('disabled', true) - expect(auditor).toHaveProperty('disabled', true) - expect(userAdmin).toHaveProperty('disabled', true) + expect(owner).toHaveProperty("disabled", true) + expect(templateAdmin).toHaveProperty("disabled", true) + expect(auditor).toHaveProperty("disabled", true) + expect(userAdmin).toHaveProperty("disabled", true) }) }) diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index ab1284c176626..aa14196220fe0 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -35,7 +35,6 @@ export const MockTemplateAdminRole: TypesGen.Role = { display_name: "Template Admin", } - export const MockAssignableUserAdminRole: TypesGen.AssignableRoles = { ...MockUserAdminRole, assignable: true, @@ -57,7 +56,11 @@ export const MockAssignableAuditorRole: TypesGen.AssignableRoles = { } export const MockSiteRoles = [MockUserAdminRole, MockAuditorRole] -export const MockAssignableSiteRoles = [MockAssignableOwnerRole, MockAssignableUserAdminRole, MockAssignableAuditorRole] +export const MockAssignableSiteRoles = [ + MockAssignableOwnerRole, + MockAssignableUserAdminRole, + MockAssignableAuditorRole, +] export function assignableRole(role: TypesGen.Role, assignable: boolean): TypesGen.AssignableRoles { return { From 9994ce44428c4febf1c63e94a31cf3b5b1b61228 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 16 Aug 2022 09:49:10 -0500 Subject: [PATCH 10/14] Remove unused code --- .../components/RoleSelect/RoleSelect.test.tsx | 2 +- site/src/testHelpers/entities.ts | 22 ++----------------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/site/src/components/RoleSelect/RoleSelect.test.tsx b/site/src/components/RoleSelect/RoleSelect.test.tsx index 98034eead56ec..e0eedee4e24cd 100644 --- a/site/src/components/RoleSelect/RoleSelect.test.tsx +++ b/site/src/components/RoleSelect/RoleSelect.test.tsx @@ -7,7 +7,7 @@ import { MockTemplateAdminRole, MockUserAdminRole, render, -} from "../../testHelpers/renderHelpers" +} from "testHelpers/renderHelpers" import { RoleSelect } from "./RoleSelect" describe("UserRoleSelect", () => { diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index aa14196220fe0..738c29459ff4a 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -20,11 +20,6 @@ export const MockOwnerRole: TypesGen.Role = { display_name: "Owner", } -export const MockAssignableOwnerRole: TypesGen.AssignableRoles = { - ...MockOwnerRole, - assignable: false, -} - export const MockUserAdminRole: TypesGen.Role = { name: "user_admin", display_name: "User Admin", @@ -35,11 +30,6 @@ export const MockTemplateAdminRole: TypesGen.Role = { display_name: "Template Admin", } -export const MockAssignableUserAdminRole: TypesGen.AssignableRoles = { - ...MockUserAdminRole, - assignable: true, -} - export const MockMemberRole: TypesGen.Role = { name: "member", display_name: "Member", @@ -50,18 +40,10 @@ export const MockAuditorRole: TypesGen.Role = { display_name: "Auditor", } -export const MockAssignableAuditorRole: TypesGen.AssignableRoles = { - ...MockAuditorRole, - assignable: true, -} - export const MockSiteRoles = [MockUserAdminRole, MockAuditorRole] -export const MockAssignableSiteRoles = [ - MockAssignableOwnerRole, - MockAssignableUserAdminRole, - MockAssignableAuditorRole, -] +// assignableRole takes a role and a boolean. The boolean implies if the +// actor can assign (add/remove) the role from other users. export function assignableRole(role: TypesGen.Role, assignable: boolean): TypesGen.AssignableRoles { return { ...role, From e45aec6bc9b66f9bbe927e1181b10626dc46a833 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 16 Aug 2022 09:55:31 -0500 Subject: [PATCH 11/14] Update site/src/components/RoleSelect/RoleSelect.test.tsx Co-authored-by: Kira Pilot --- site/src/components/RoleSelect/RoleSelect.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/components/RoleSelect/RoleSelect.test.tsx b/site/src/components/RoleSelect/RoleSelect.test.tsx index e0eedee4e24cd..f6510ea8e3a0a 100644 --- a/site/src/components/RoleSelect/RoleSelect.test.tsx +++ b/site/src/components/RoleSelect/RoleSelect.test.tsx @@ -24,7 +24,7 @@ describe("UserRoleSelect", () => { selectedRoles={[MockUserAdminRole, MockTemplateAdminRole, MockMemberRole]} loading={false} onChange={jest.fn()} - open={true} + open />, ) From 04cfad976861840f4619a400dd5fa658726eb068 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 16 Aug 2022 10:00:41 -0500 Subject: [PATCH 12/14] Test updates --- site/src/components/RoleSelect/RoleSelect.test.tsx | 5 +++-- site/src/pages/UsersPage/UsersPage.test.tsx | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/site/src/components/RoleSelect/RoleSelect.test.tsx b/site/src/components/RoleSelect/RoleSelect.test.tsx index f6510ea8e3a0a..dc5d1cad36690 100644 --- a/site/src/components/RoleSelect/RoleSelect.test.tsx +++ b/site/src/components/RoleSelect/RoleSelect.test.tsx @@ -36,7 +36,8 @@ describe("UserRoleSelect", () => { expect(owner).toHaveProperty("disabled", true) expect(templateAdmin).toHaveProperty("disabled", true) - expect(auditor).toHaveProperty("disabled", true) - expect(userAdmin).toHaveProperty("disabled", true) + + expect(auditor).toHaveProperty("disabled", false) + expect(userAdmin).toHaveProperty("disabled", false) }) }) diff --git a/site/src/pages/UsersPage/UsersPage.test.tsx b/site/src/pages/UsersPage/UsersPage.test.tsx index 35f681da8fd4d..507b7b48dffe7 100644 --- a/site/src/pages/UsersPage/UsersPage.test.tsx +++ b/site/src/pages/UsersPage/UsersPage.test.tsx @@ -311,7 +311,7 @@ describe("Users Page", () => { }, MockAuditorRole) // Check if the select text was updated with the Auditor role - await waitFor(() => expect(rolesMenuTrigger).toHaveTextContent("Admin, Auditor")) + await waitFor(() => expect(rolesMenuTrigger).toHaveTextContent("Owner, Auditor")) // Check if the API was called correctly const currentRoles = MockUser.roles.map((r) => r.name) From 74dbb33d7b5f10c1691e44bba6ae90ae95059f96 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 16 Aug 2022 10:12:29 -0500 Subject: [PATCH 13/14] Fix js test assertions --- site/src/components/RoleSelect/RoleSelect.test.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/site/src/components/RoleSelect/RoleSelect.test.tsx b/site/src/components/RoleSelect/RoleSelect.test.tsx index dc5d1cad36690..7e5c9843a36ec 100644 --- a/site/src/components/RoleSelect/RoleSelect.test.tsx +++ b/site/src/components/RoleSelect/RoleSelect.test.tsx @@ -34,10 +34,10 @@ describe("UserRoleSelect", () => { const auditor = await screen.findByText(MockAuditorRole.display_name) const userAdmin = await screen.findByText(MockUserAdminRole.display_name) - expect(owner).toHaveProperty("disabled", true) - expect(templateAdmin).toHaveProperty("disabled", true) + expect(owner.getAttribute("aria-disabled")).toBeTruthy() + expect(templateAdmin.getAttribute("aria-disabled")).toBeTruthy() - expect(auditor).toHaveProperty("disabled", false) - expect(userAdmin).toHaveProperty("disabled", false) + expect(auditor.getAttribute("aria-disabled")).toBeUndefined() + expect(userAdmin.getAttribute("aria-disabled")).toBeUndefined() }) }) From 7212d7e09ca2b8e58c057d61363d5d3a3381fac7 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 16 Aug 2022 10:27:32 -0500 Subject: [PATCH 14/14] Attribute is string not boolean --- site/src/components/RoleSelect/RoleSelect.test.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/site/src/components/RoleSelect/RoleSelect.test.tsx b/site/src/components/RoleSelect/RoleSelect.test.tsx index 7e5c9843a36ec..51eb4e050701c 100644 --- a/site/src/components/RoleSelect/RoleSelect.test.tsx +++ b/site/src/components/RoleSelect/RoleSelect.test.tsx @@ -34,10 +34,11 @@ describe("UserRoleSelect", () => { const auditor = await screen.findByText(MockAuditorRole.display_name) const userAdmin = await screen.findByText(MockUserAdminRole.display_name) - expect(owner.getAttribute("aria-disabled")).toBeTruthy() - expect(templateAdmin.getAttribute("aria-disabled")).toBeTruthy() + // The attributes are "strings", not boolean types. + expect(owner.getAttribute("aria-disabled")).toBe("true") + expect(templateAdmin.getAttribute("aria-disabled")).toBe("true") - expect(auditor.getAttribute("aria-disabled")).toBeUndefined() - expect(userAdmin.getAttribute("aria-disabled")).toBeUndefined() + expect(userAdmin.getAttribute("aria-disabled")).toBe("false") + expect(auditor.getAttribute("aria-disabled")).toBe("false") }) })