From e54324d880c275415df1cd0ff916a3d8e3beb048 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Mon, 9 May 2022 11:38:14 -0500 Subject: [PATCH] refactor: Add roles into the user response (#1347) --- coderd/roles.go | 19 ++++++++++-- coderd/roles_test.go | 21 +++++++++++-- coderd/users.go | 10 +++++- codersdk/roles.go | 12 ------- codersdk/users.go | 11 ++++--- site/src/api/types.ts | 1 + site/src/api/typesGenerated.ts | 31 ++++++++++++------- .../ResetPasswordDialog.stories.tsx | 3 +- .../AccountPage/AccountPage.test.tsx | 1 + site/src/testHelpers/entities.ts | 2 ++ 10 files changed, 74 insertions(+), 37 deletions(-) diff --git a/coderd/roles.go b/coderd/roles.go index 470d726252..cad7430dcb 100644 --- a/coderd/roles.go +++ b/coderd/roles.go @@ -15,7 +15,7 @@ func (*api) assignableSiteRoles(rw http.ResponseWriter, _ *http.Request) { // TODO: @emyrk in the future, allow granular subsets of roles to be returned based on the // role of the user. roles := rbac.SiteRoles() - httpapi.Write(rw, http.StatusOK, codersdk.ConvertRoles(roles)) + httpapi.Write(rw, http.StatusOK, convertRoles(roles)) } // assignableSiteRoles returns all site wide roles that can be assigned. @@ -24,5 +24,20 @@ func (*api) assignableOrgRoles(rw http.ResponseWriter, r *http.Request) { // role of the user. organization := httpmw.OrganizationParam(r) roles := rbac.OrganizationRoles(organization.ID) - httpapi.Write(rw, http.StatusOK, codersdk.ConvertRoles(roles)) + httpapi.Write(rw, http.StatusOK, convertRoles(roles)) +} + +func convertRole(role rbac.Role) codersdk.Role { + return codersdk.Role{ + DisplayName: role.DisplayName, + Name: role.Name, + } +} + +func convertRoles(roles []rbac.Role) []codersdk.Role { + converted := make([]codersdk.Role, 0, len(roles)) + for _, role := range roles { + converted = append(converted, convertRole(role)) + } + return converted } diff --git a/coderd/roles_test.go b/coderd/roles_test.go index 6254ffa73d..2c42d129ce 100644 --- a/coderd/roles_test.go +++ b/coderd/roles_test.go @@ -84,7 +84,7 @@ func TestListRoles(t *testing.T) { APICall: func() ([]codersdk.Role, error) { return orgAdmin.ListOrganizationRoles(ctx, admin.OrganizationID) }, - ExpectedRoles: codersdk.ConvertRoles(rbac.OrganizationRoles(admin.OrganizationID)), + ExpectedRoles: convertRoles(rbac.OrganizationRoles(admin.OrganizationID)), }, { Name: "OrgAdminListOtherOrg", @@ -99,14 +99,14 @@ func TestListRoles(t *testing.T) { APICall: func() ([]codersdk.Role, error) { return client.ListSiteRoles(ctx) }, - ExpectedRoles: codersdk.ConvertRoles(rbac.SiteRoles()), + ExpectedRoles: convertRoles(rbac.SiteRoles()), }, { Name: "AdminListOrg", APICall: func() ([]codersdk.Role, error) { return client.ListOrganizationRoles(ctx, admin.OrganizationID) }, - ExpectedRoles: codersdk.ConvertRoles(rbac.OrganizationRoles(admin.OrganizationID)), + ExpectedRoles: convertRoles(rbac.OrganizationRoles(admin.OrganizationID)), }, } @@ -127,3 +127,18 @@ func TestListRoles(t *testing.T) { }) } } + +func convertRole(role rbac.Role) codersdk.Role { + return codersdk.Role{ + DisplayName: role.DisplayName, + Name: role.Name, + } +} + +func convertRoles(roles []rbac.Role) []codersdk.Role { + converted := make([]codersdk.Role, 0, len(roles)) + for _, role := range roles { + converted = append(converted, convertRole(role)) + } + return converted +} diff --git a/coderd/users.go b/coderd/users.go index 7841198558..b2e3ab5deb 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -807,14 +807,22 @@ func (api *api) createUser(ctx context.Context, req codersdk.CreateUserRequest) } func convertUser(user database.User, organizationIDs []uuid.UUID) codersdk.User { - return codersdk.User{ + convertedUser := codersdk.User{ ID: user.ID, Email: user.Email, CreatedAt: user.CreatedAt, Username: user.Username, Status: codersdk.UserStatus(user.Status), OrganizationIDs: organizationIDs, + Roles: make([]codersdk.Role, 0), } + + for _, roleName := range user.RBACRoles { + rbacRole, _ := rbac.RoleByName(roleName) + convertedUser.Roles = append(convertedUser.Roles, convertRole(rbacRole)) + } + + return convertedUser } func convertUsers(users []database.User, organizationIDsByUserID map[uuid.UUID][]uuid.UUID) []codersdk.User { diff --git a/codersdk/roles.go b/codersdk/roles.go index cc2234505f..727c78e225 100644 --- a/codersdk/roles.go +++ b/codersdk/roles.go @@ -6,7 +6,6 @@ import ( "fmt" "net/http" - "github.com/coder/coder/coderd/rbac" "github.com/google/uuid" ) @@ -44,14 +43,3 @@ func (c *Client) ListOrganizationRoles(ctx context.Context, org uuid.UUID) ([]Ro var roles []Role return roles, json.NewDecoder(res.Body).Decode(&roles) } - -func ConvertRoles(roles []rbac.Role) []Role { - converted := make([]Role, 0, len(roles)) - for _, role := range roles { - converted = append(converted, Role{ - DisplayName: role.DisplayName, - Name: role.Name, - }) - } - return converted -} diff --git a/codersdk/users.go b/codersdk/users.go index dd0ad207d9..79667a38e7 100644 --- a/codersdk/users.go +++ b/codersdk/users.go @@ -45,6 +45,7 @@ type User struct { Username string `json:"username" validate:"required"` Status UserStatus `json:"status"` OrganizationIDs []uuid.UUID `json:"organization_ids"` + Roles []Role `json:"roles"` } type CreateFirstUserRequest struct { @@ -216,17 +217,17 @@ func (c *Client) UpdateUserRoles(ctx context.Context, userID uuid.UUID, req Upda // UpdateOrganizationMemberRoles grants the userID the specified roles in an org. // Include ALL roles the user has. -func (c *Client) UpdateOrganizationMemberRoles(ctx context.Context, organizationID, userID uuid.UUID, req UpdateRoles) (User, error) { +func (c *Client) UpdateOrganizationMemberRoles(ctx context.Context, organizationID, userID uuid.UUID, req UpdateRoles) (OrganizationMember, error) { res, err := c.request(ctx, http.MethodPut, fmt.Sprintf("/api/v2/organizations/%s/members/%s/roles", organizationID, uuidOrMe(userID)), req) if err != nil { - return User{}, err + return OrganizationMember{}, err } defer res.Body.Close() if res.StatusCode != http.StatusOK { - return User{}, readBodyAsError(res) + return OrganizationMember{}, readBodyAsError(res) } - var user User - return user, json.NewDecoder(res.Body).Decode(&user) + var member OrganizationMember + return member, json.NewDecoder(res.Body).Decode(&member) } // GetUserRoles returns all roles the user has diff --git a/site/src/api/types.ts b/site/src/api/types.ts index 380859514e..bf20e794b8 100644 --- a/site/src/api/types.ts +++ b/site/src/api/types.ts @@ -24,6 +24,7 @@ export interface UserResponse { readonly created_at: string readonly status: "active" | "suspended" readonly organization_ids: string[] + readonly roles: { name: string; display_name: string }[] } /** diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index e3fa4e53fc..846211a109 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -12,7 +12,7 @@ export interface AgentGitSSHKey { readonly private_key: string } -// From codersdk/users.go:109:6 +// From codersdk/users.go:110:6 export interface AuthMethods { readonly password: boolean readonly github: boolean @@ -30,7 +30,7 @@ export interface BuildInfoResponse { readonly version: string } -// From codersdk/users.go:50:6 +// From codersdk/users.go:51:6 export interface CreateFirstUserRequest { readonly email: string readonly username: string @@ -38,13 +38,13 @@ export interface CreateFirstUserRequest { readonly organization: string } -// From codersdk/users.go:58:6 +// From codersdk/users.go:59:6 export interface CreateFirstUserResponse { readonly user_id: string readonly organization_id: string } -// From codersdk/users.go:104:6 +// From codersdk/users.go:105:6 export interface CreateOrganizationRequest { readonly name: string } @@ -77,7 +77,7 @@ export interface CreateTemplateVersionRequest { readonly parameter_values: CreateParameterRequest[] } -// From codersdk/users.go:63:6 +// From codersdk/users.go:64:6 export interface CreateUserRequest { readonly email: string readonly username: string @@ -101,7 +101,7 @@ export interface CreateWorkspaceRequest { readonly parameter_values: CreateParameterRequest[] } -// From codersdk/users.go:100:6 +// From codersdk/users.go:101:6 export interface GenerateAPIKeyResponse { readonly key: string } @@ -119,13 +119,13 @@ export interface GoogleInstanceIdentityToken { readonly json_web_token: string } -// From codersdk/users.go:89:6 +// From codersdk/users.go:90:6 export interface LoginWithPasswordRequest { readonly email: string readonly password: string } -// From codersdk/users.go:95:6 +// From codersdk/users.go:96:6 export interface LoginWithPasswordResponse { readonly session_token: string } @@ -195,6 +195,12 @@ export interface ProvisionerJobLog { readonly output: string } +// From codersdk/roles.go:13:6 +export interface Role { + readonly name: string + readonly display_name: string +} + // From codersdk/templates.go:17:6 export interface Template { readonly id: string @@ -255,17 +261,17 @@ export interface UpdateActiveTemplateVersion { readonly id: string } -// From codersdk/users.go:79:6 +// From codersdk/users.go:80:6 export interface UpdateRoles { readonly roles: string[] } -// From codersdk/users.go:75:6 +// From codersdk/users.go:76:6 export interface UpdateUserPasswordRequest { readonly password: string } -// From codersdk/users.go:70:6 +// From codersdk/users.go:71:6 export interface UpdateUserProfileRequest { readonly email: string readonly username: string @@ -294,9 +300,10 @@ export interface User { readonly username: string readonly status: UserStatus readonly organization_ids: string[] + readonly roles: Role[] } -// From codersdk/users.go:83:6 +// From codersdk/users.go:84:6 export interface UserRoles { readonly roles: string[] readonly organization_roles: Record diff --git a/site/src/components/ResetPasswordDialog/ResetPasswordDialog.stories.tsx b/site/src/components/ResetPasswordDialog/ResetPasswordDialog.stories.tsx index 8a0c1f19a6..170d280988 100644 --- a/site/src/components/ResetPasswordDialog/ResetPasswordDialog.stories.tsx +++ b/site/src/components/ResetPasswordDialog/ResetPasswordDialog.stories.tsx @@ -1,7 +1,6 @@ import { Story } from "@storybook/react" import React from "react" import { MockUser } from "../../testHelpers" -import { generateRandomString } from "../../util/random" import { ResetPasswordDialog, ResetPasswordDialogProps } from "./ResetPasswordDialog" export default { @@ -19,5 +18,5 @@ export const Example = Template.bind({}) Example.args = { open: true, user: MockUser, - newPassword: generateRandomString(12), + newPassword: "somerandomstringhere", } diff --git a/site/src/pages/PreferencesPages/AccountPage/AccountPage.test.tsx b/site/src/pages/PreferencesPages/AccountPage/AccountPage.test.tsx index 180b0ac69d..115e2f7a3d 100644 --- a/site/src/pages/PreferencesPages/AccountPage/AccountPage.test.tsx +++ b/site/src/pages/PreferencesPages/AccountPage/AccountPage.test.tsx @@ -37,6 +37,7 @@ describe("AccountPage", () => { created_at: new Date().toString(), status: "active", organization_ids: ["123"], + roles: [], ...data, }), ) diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index e7c3e91274..1ac27313d6 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -28,6 +28,7 @@ export const MockUser: UserResponse = { created_at: "", status: "active", organization_ids: ["fc0774ce-cc9e-48d4-80ae-88f7a4d4a8b0"], + roles: [], } export const MockUser2: UserResponse = { @@ -37,6 +38,7 @@ export const MockUser2: UserResponse = { created_at: "", status: "active", organization_ids: ["fc0774ce-cc9e-48d4-80ae-88f7a4d4a8b0"], + roles: [], } export const MockOrganization: Organization = {