diff --git a/site/src/pages/UsersPage/UsersPage.test.tsx b/site/src/pages/UsersPage/UsersPage.test.tsx index d371c9fa58d60..ea57bbb978970 100644 --- a/site/src/pages/UsersPage/UsersPage.test.tsx +++ b/site/src/pages/UsersPage/UsersPage.test.tsx @@ -1,4 +1,5 @@ import { fireEvent, screen, waitFor, within } from "@testing-library/react" +import { rest } from "msw" import React from "react" import * as API from "../../api/api" import { Role } from "../../api/typesGenerated" @@ -7,8 +8,11 @@ import { Language as ResetPasswordDialogLanguage } from "../../components/ResetP import { Language as RoleSelectLanguage } from "../../components/RoleSelect/RoleSelect" import { Language as UsersTableLanguage } from "../../components/UsersTable/UsersTable" import { MockAuditorRole, MockUser, MockUser2, render } from "../../testHelpers/renderHelpers" +import { server } from "../../testHelpers/server" +import { permissionsToCheck } from "../../xServices/auth/authXService" import { Language as usersXServiceLanguage } from "../../xServices/users/usersXService" import { Language as UsersPageLanguage, UsersPage } from "./UsersPage" +import { Language as UsersViewLanguage } from "./UsersPageView" const suspendUser = async (setupActionSpies: () => void) => { // Get the first user in the table @@ -99,6 +103,32 @@ describe("Users Page", () => { expect(users.length).toEqual(2) }) + it("shows 'New user' button to an authorized user", () => { + render() + const newUserButton = screen.queryByText(UsersViewLanguage.newUserButton) + expect(newUserButton).toBeDefined() + }) + + it("does not show 'New user' button to unauthorized user", () => { + server.use( + rest.post("/api/v2/users/:userId/authorization", async (req, res, ctx) => { + const permissions = Object.keys(permissionsToCheck) + const response = permissions.reduce((obj, permission) => { + return { + ...obj, + [permission]: true, + createUser: false, + } + }, {}) + + return res(ctx.status(200), ctx.json(response)) + }), + ) + render() + const newUserButton = screen.queryByText(UsersViewLanguage.newUserButton) + expect(newUserButton).toBeNull() + }) + describe("suspend user", () => { describe("when it is success", () => { it("shows a success message and refresh the page", async () => { diff --git a/site/src/pages/UsersPage/UsersPage.tsx b/site/src/pages/UsersPage/UsersPage.tsx index b52cf1c69aa62..e28b962b2b634 100644 --- a/site/src/pages/UsersPage/UsersPage.tsx +++ b/site/src/pages/UsersPage/UsersPage.tsx @@ -23,6 +23,7 @@ export const UsersPage: React.FC = () => { const userToResetPassword = users?.find((u) => u.id === userIdToResetPassword) const permissions = useSelector(xServices.authXService, selectPermissions) const canEditUsers = permissions && permissions.updateUsers + const canCreateUser = permissions && permissions.createUser const { roles } = rolesState.context // Is loading if // - permissions are not loaded or @@ -70,6 +71,7 @@ export const UsersPage: React.FC = () => { isUpdatingUserRoles={usersState.matches("updatingUserRoles")} isLoading={isLoading} canEditUsers={canEditUsers} + canCreateUser={canCreateUser} /> = (args) => -export const Ready = Template.bind({}) -Ready.args = { +export const Admin = Template.bind({}) +Admin.args = { users: [MockUser, MockUser2], roles: MockSiteRoles, + canCreateUser: true, + canEditUsers: true, } + +export const Member = Template.bind({}) +Member.args = { ...Admin.args, canCreateUser: false, canEditUsers: false } + export const Empty = Template.bind({}) -Empty.args = { - users: [], - roles: MockSiteRoles, -} +Empty.args = { ...Admin.args, users: [] } diff --git a/site/src/pages/UsersPage/UsersPageView.tsx b/site/src/pages/UsersPage/UsersPageView.tsx index 8bb637c16c4aa..be5367d748ab5 100644 --- a/site/src/pages/UsersPage/UsersPageView.tsx +++ b/site/src/pages/UsersPage/UsersPageView.tsx @@ -17,6 +17,7 @@ export interface UsersPageViewProps { error?: unknown isUpdatingUserRoles?: boolean canEditUsers?: boolean + canCreateUser?: boolean isLoading?: boolean openUserCreationDialog: () => void onSuspendUser: (user: TypesGen.User) => void @@ -34,11 +35,13 @@ export const UsersPageView: React.FC = ({ error, isUpdatingUserRoles, canEditUsers, + canCreateUser, isLoading, }) => { + const newUserAction = canCreateUser ? { text: Language.newUserButton, onClick: openUserCreationDialog } : undefined return ( -
+
{error ? ( diff --git a/site/src/xServices/auth/authXService.ts b/site/src/xServices/auth/authXService.ts index deba485615995..3e634fc8b58d8 100644 --- a/site/src/xServices/auth/authXService.ts +++ b/site/src/xServices/auth/authXService.ts @@ -12,6 +12,7 @@ export const Language = { export const checks = { readAllUsers: "readAllUsers", updateUsers: "updateUsers", + createUser: "createUser", createTemplates: "createTemplates", } as const @@ -28,6 +29,12 @@ export const permissionsToCheck = { }, action: "update", }, + [checks.createUser]: { + object: { + resource_type: "user", + }, + action: "create", + }, [checks.createTemplates]: { object: { resource_type: "template",