Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3f770e1

Browse files
fix: User permissions on UI (#1570)
1 parent4eb0bb6 commit3f770e1

File tree

7 files changed

+88
-60
lines changed

7 files changed

+88
-60
lines changed

‎site/src/components/Navbar/Navbar.test.tsx‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ beforeEach(() => {
1111
})
1212

1313
describe("Navbar",()=>{
14-
describe("when user has permission toread all users",()=>{
14+
describe("when user has permission toupdate users",()=>{
1515
it("displays the admin menu",async()=>{
1616
constcheckUserPermissionsSpy=jest.spyOn(API,"checkUserPermissions").mockResolvedValueOnce({
17-
[checks.readAllUsers]:true,
17+
[checks.updateUsers]:true,
1818
})
1919

2020
renderWithAuth(<Navbar/>)
@@ -25,10 +25,10 @@ describe("Navbar", () => {
2525
})
2626
})
2727

28-
describe("when user has NO permission toread all users",()=>{
28+
describe("when user has NO permission toupdate users",()=>{
2929
it("does not display the admin menu",async()=>{
3030
constcheckUserPermissionsSpy=jest.spyOn(API,"checkUserPermissions").mockResolvedValueOnce({
31-
[checks.readAllUsers]:false,
31+
[checks.updateUsers]:false,
3232
})
3333
renderWithAuth(<Navbar/>)
3434

‎site/src/components/Navbar/Navbar.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const Navbar: React.FC = () => {
1111
constpermissions=useSelector(xServices.authXService,selectPermissions)
1212
// When we have more options in the admin dropdown we may want to check this
1313
// for more permissions
14-
constdisplayAdminDropdown=!!permissions?.readAllUsers
14+
constdisplayAdminDropdown=!!permissions?.updateUsers
1515
constonSignOut=()=>authSend("SIGN_OUT")
1616

1717
return<NavbarViewuser={me}onSignOut={onSignOut}displayAdminDropdown={displayAdminDropdown}/>

‎site/src/components/UsersTable/UsersTable.stories.tsx‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ export const Example = Template.bind({})
1414
Example.args={
1515
users:[MockUser,MockUser2],
1616
roles:MockSiteRoles,
17+
canEditUsers:false,
18+
}
19+
20+
exportconstEditable=Template.bind({})
21+
Editable.args={
22+
users:[MockUser,MockUser2],
23+
roles:MockSiteRoles,
24+
canEditUsers:true,
1725
}
1826

1927
exportconstEmpty=Template.bind({})

‎site/src/components/UsersTable/UsersTable.tsx‎

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@ import React from "react"
88
import*asTypesGenfrom"../../api/typesGenerated"
99
import{EmptyState}from"../EmptyState/EmptyState"
1010
import{RoleSelect}from"../RoleSelect/RoleSelect"
11-
import{TableHeaderRow}from"../TableHeaders/TableHeaders"
1211
import{TableLoader}from"../TableLoader/TableLoader"
1312
import{TableRowMenu}from"../TableRowMenu/TableRowMenu"
14-
import{TableTitle}from"../TableTitle/TableTitle"
1513
import{UserCell}from"../UserCell/UserCell"
1614

1715
exportconstLanguage={
@@ -28,6 +26,8 @@ export interface UsersTableProps {
2826
users?:TypesGen.User[]
2927
roles?:TypesGen.Role[]
3028
isUpdatingUserRoles?:boolean
29+
canEditUsers?:boolean
30+
isLoading?:boolean
3131
onSuspendUser:(user:TypesGen.User)=>void
3232
onResetUserPassword:(user:TypesGen.User)=>void
3333
onUpdateUserRoles:(user:TypesGen.User,roles:TypesGen.Role["name"][])=>void
@@ -40,52 +40,57 @@ export const UsersTable: React.FC<UsersTableProps> = ({
4040
onResetUserPassword,
4141
onUpdateUserRoles,
4242
isUpdatingUserRoles,
43+
canEditUsers,
44+
isLoading,
4345
})=>{
44-
constisLoading=!users||!roles
45-
4646
return(
4747
<Table>
4848
<TableHead>
49-
<TableTitletitle={Language.usersTitle}/>
50-
<TableHeaderRow>
51-
<TableCellsize="small">{Language.usernameLabel}</TableCell>
52-
<TableCellsize="small">{Language.rolesLabel}</TableCell>
49+
<TableRow>
50+
<TableCell>{Language.usernameLabel}</TableCell>
51+
<TableCell>{Language.rolesLabel}</TableCell>
5352
{/* 1% is a trick to make the table cell width fit the content */}
54-
<TableCellsize="small"width="1%"/>
55-
</TableHeaderRow>
53+
{canEditUsers&&<TableCellwidth="1%"/>}
54+
</TableRow>
5655
</TableHead>
5756
<TableBody>
5857
{isLoading&&<TableLoader/>}
59-
{users&&
60-
roles&&
58+
{!isLoading&&
59+
users&&
6160
users.map((u)=>(
6261
<TableRowkey={u.id}>
6362
<TableCell>
6463
<UserCellAvatar={{username:u.username}}primaryText={u.username}caption={u.email}/>{" "}
6564
</TableCell>
6665
<TableCell>
67-
<RoleSelect
68-
roles={roles}
69-
selectedRoles={u.roles}
70-
loading={isUpdatingUserRoles}
71-
onChange={(roles)=>onUpdateUserRoles(u,roles)}
72-
/>
73-
</TableCell>
74-
<TableCell>
75-
<TableRowMenu
76-
data={u}
77-
menuItems={[
78-
{
79-
label:Language.suspendMenuItem,
80-
onClick:onSuspendUser,
81-
},
82-
{
83-
label:Language.resetPasswordMenuItem,
84-
onClick:onResetUserPassword,
85-
},
86-
]}
87-
/>
66+
{canEditUsers ?(
67+
<RoleSelect
68+
roles={roles??[]}
69+
selectedRoles={u.roles}
70+
loading={isUpdatingUserRoles}
71+
onChange={(roles)=>onUpdateUserRoles(u,roles)}
72+
/>
73+
) :(
74+
<>{u.roles.map((r)=>r.display_name).join(", ")}</>
75+
)}
8876
</TableCell>
77+
{canEditUsers&&(
78+
<TableCell>
79+
<TableRowMenu
80+
data={u}
81+
menuItems={[
82+
{
83+
label:Language.suspendMenuItem,
84+
onClick:onSuspendUser,
85+
},
86+
{
87+
label:Language.resetPasswordMenuItem,
88+
onClick:onResetUserPassword,
89+
},
90+
]}
91+
/>
92+
</TableCell>
93+
)}
8994
</TableRow>
9095
))}
9196

‎site/src/pages/UsersPage/UsersPage.tsx‎

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import{useActor}from"@xstate/react"
1+
import{useActor,useSelector}from"@xstate/react"
22
importReact,{useContext,useEffect}from"react"
33
import{useNavigate}from"react-router"
44
import{ConfirmDialog}from"../../components/ConfirmDialog/ConfirmDialog"
55
import{ResetPasswordDialog}from"../../components/ResetPasswordDialog/ResetPasswordDialog"
6+
import{selectPermissions}from"../../xServices/auth/authSelectors"
67
import{XServiceContext}from"../../xServices/StateContext"
78
import{UsersPageView}from"./UsersPageView"
89

@@ -12,39 +13,38 @@ export const Language = {
1213
suspendDialogMessagePrefix:"Do you want to suspend the user",
1314
}
1415

15-
constuseRoles=()=>{
16-
constxServices=useContext(XServiceContext)
17-
const[rolesState,rolesSend]=useActor(xServices.siteRolesXService)
18-
const{ roles}=rolesState.context
19-
20-
/**
21-
* Fetch roles on component mount
22-
*/
23-
useEffect(()=>{
24-
rolesSend({
25-
type:"GET_ROLES",
26-
})
27-
},[rolesSend])
28-
29-
returnroles
30-
}
31-
3216
exportconstUsersPage:React.FC=()=>{
3317
constxServices=useContext(XServiceContext)
3418
const[usersState,usersSend]=useActor(xServices.usersXService)
19+
const[rolesState,rolesSend]=useActor(xServices.siteRolesXService)
3520
const{ users, getUsersError, userIdToSuspend, userIdToResetPassword, newUserPassword}=usersState.context
3621
constnavigate=useNavigate()
3722
constuserToBeSuspended=users?.find((u)=>u.id===userIdToSuspend)
3823
constuserToResetPassword=users?.find((u)=>u.id===userIdToResetPassword)
39-
constroles=useRoles()
24+
constpermissions=useSelector(xServices.authXService,selectPermissions)
25+
constcanEditUsers=permissions&&permissions.updateUsers
26+
const{ roles}=rolesState.context
27+
// Is loading if
28+
// - permissions are not loaded or
29+
// - users are not loaded or
30+
// - the user can edit the users but the roles are not loaded yet
31+
constisLoading=!permissions||!users||(canEditUsers&&!roles)
4032

41-
/**
42-
* Fetch users on component mount
43-
*/
33+
// Fetch users on component mount
4434
useEffect(()=>{
4535
usersSend("GET_USERS")
4636
},[usersSend])
4737

38+
// Fetch roles on component mount
39+
useEffect(()=>{
40+
// Only fetch the roles if the user has permission for it
41+
if(canEditUsers){
42+
rolesSend({
43+
type:"GET_ROLES",
44+
})
45+
}
46+
},[canEditUsers,rolesSend])
47+
4848
return(
4949
<>
5050
<UsersPageView
@@ -68,6 +68,8 @@ export const UsersPage: React.FC = () => {
6868
}}
6969
error={getUsersError}
7070
isUpdatingUserRoles={usersState.matches("updatingUserRoles")}
71+
isLoading={isLoading}
72+
canEditUsers={canEditUsers}
7173
/>
7274

7375
<ConfirmDialog

‎site/src/pages/UsersPage/UsersPageView.tsx‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export interface UsersPageViewProps {
1616
roles?:TypesGen.Role[]
1717
error?:unknown
1818
isUpdatingUserRoles?:boolean
19+
canEditUsers?:boolean
20+
isLoading?:boolean
1921
openUserCreationDialog:()=>void
2022
onSuspendUser:(user:TypesGen.User)=>void
2123
onResetUserPassword:(user:TypesGen.User)=>void
@@ -31,6 +33,8 @@ export const UsersPageView: React.FC<UsersPageViewProps> = ({
3133
onUpdateUserRoles,
3234
error,
3335
isUpdatingUserRoles,
36+
canEditUsers,
37+
isLoading,
3438
})=>{
3539
return(
3640
<Stackspacing={4}>
@@ -46,6 +50,8 @@ export const UsersPageView: React.FC<UsersPageViewProps> = ({
4650
onResetUserPassword={onResetUserPassword}
4751
onUpdateUserRoles={onUpdateUserRoles}
4852
isUpdatingUserRoles={isUpdatingUserRoles}
53+
canEditUsers={canEditUsers}
54+
isLoading={isLoading}
4955
/>
5056
)}
5157
</Margins>

‎site/src/xServices/auth/authXService.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const Language = {
1111

1212
exportconstchecks={
1313
readAllUsers:"readAllUsers",
14+
updateUsers:"updateUsers",
1415
createTemplates:"createTemplates",
1516
}asconst
1617

@@ -21,6 +22,12 @@ export const permissionsToCheck = {
2122
},
2223
action:"read",
2324
},
25+
[checks.updateUsers]:{
26+
object:{
27+
resource_type:"user",
28+
},
29+
action:"update",
30+
},
2431
[checks.createTemplates]:{
2532
object:{
2633
resource_type:"template",

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp