1
0
mirror of https://github.com/mattermost/focalboard.git synced 2024-12-21 13:38:56 +02:00

fixing role dropdown for admins

This commit is contained in:
Benjamin Cooke 2023-03-06 16:26:37 -05:00
parent 098868387e
commit 84614b8f4f
4 changed files with 71 additions and 190 deletions

View File

@ -22,6 +22,7 @@ const linkBoardMessage = "@%s linked the board [%s](%s) with this channel"
const unlinkBoardMessage = "@%s unlinked the board [%s](%s) with this channel" const unlinkBoardMessage = "@%s unlinked the board [%s](%s) with this channel"
var errNoDefaultCategoryFound = errors.New("no default category found for user") var errNoDefaultCategoryFound = errors.New("no default category found for user")
var errMemberRoleCannotBeChanged = errors.New("cannot change the role of this member")
func (a *App) GetBoard(boardID string) (*model.Board, error) { func (a *App) GetBoard(boardID string) (*model.Board, error) {
board, err := a.store.GetBoard(boardID) board, err := a.store.GetBoard(boardID)
@ -604,6 +605,10 @@ func (a *App) UpdateBoardMember(member *model.BoardMember) (*model.BoardMember,
return nil, bErr return nil, bErr
} }
if a.permissions.HasPermissionToTeam(member.UserID, board.TeamID, model.PermissionManageTeam) {
return nil, errMemberRoleCannotBeChanged
}
oldMember, err := a.store.GetMemberForBoard(member.BoardID, member.UserID) oldMember, err := a.store.GetMemberForBoard(member.BoardID, member.UserID)
if model.IsErrNotFound(err) { if model.IsErrNotFound(err) {
return nil, nil return nil, nil

View File

@ -1598,6 +1598,34 @@ func TestUpdateMember(t *testing.T) {
require.True(t, members[0].SchemeAdmin) require.True(t, members[0].SchemeAdmin)
}) })
t.Run("should not update a member if they are a team admin or higher", func(t *testing.T) {
th := SetupTestHelper(t).InitBasic()
defer th.TearDown()
newBoard := &model.Board{
Title: "title",
Type: model.BoardTypePrivate,
TeamID: teamID,
}
board, err := th.Server.App().CreateBoard(newBoard, th.GetUser1().ID, true)
require.NoError(t, err)
memberUpdate := &model.BoardMember{
UserID: th.GetUser1().ID,
BoardID: board.ID,
SchemeEditor: true,
}
updatedUser1Member, resp := th.Client.UpdateBoardMember(memberUpdate)
th.CheckBadRequest(resp)
require.Nil(t, updatedUser1Member)
members, err := th.Server.App().GetMembersForBoard(board.ID)
require.NoError(t, err)
require.Len(t, members, 1)
require.True(t, members[0].SchemeAdmin)
})
t.Run("should always disable the admin role on update member if the user is a guest", func(t *testing.T) { t.Run("should always disable the admin role on update member if the user is a guest", func(t *testing.T) {
th := SetupTestHelperPluginMode(t) th := SetupTestHelperPluginMode(t)
defer th.TearDown() defer th.TearDown()

View File

@ -78,8 +78,6 @@ exports[`src/components/shareBoard/userPermissionsRow should match snapshot 1`]
class="noicon" class="noicon"
/> />
</div> </div>
</div>
<div>
<div <div
aria-label="Commenter" aria-label="Commenter"
class="MenuOption TextOption menu-option" class="MenuOption TextOption menu-option"
@ -109,8 +107,6 @@ exports[`src/components/shareBoard/userPermissionsRow should match snapshot 1`]
class="noicon" class="noicon"
/> />
</div> </div>
</div>
<div>
<div <div
aria-label="Editor" aria-label="Editor"
class="MenuOption TextOption menu-option" class="MenuOption TextOption menu-option"
@ -140,8 +136,6 @@ exports[`src/components/shareBoard/userPermissionsRow should match snapshot 1`]
class="noicon" class="noicon"
/> />
</div> </div>
</div>
<div>
<div <div
aria-label="Admin" aria-label="Admin"
class="MenuOption TextOption menu-option" class="MenuOption TextOption menu-option"
@ -177,8 +171,6 @@ exports[`src/components/shareBoard/userPermissionsRow should match snapshot 1`]
class="noicon" class="noicon"
/> />
</div> </div>
</div>
<div>
<div <div
class="MenuOption MenuSeparator menu-separator" class="MenuOption MenuSeparator menu-separator"
/> />
@ -332,8 +324,6 @@ exports[`src/components/shareBoard/userPermissionsRow should match snapshot in p
class="noicon" class="noicon"
/> />
</div> </div>
</div>
<div>
<div <div
aria-label="Commenter" aria-label="Commenter"
class="MenuOption TextOption menu-option" class="MenuOption TextOption menu-option"
@ -363,8 +353,6 @@ exports[`src/components/shareBoard/userPermissionsRow should match snapshot in p
class="noicon" class="noicon"
/> />
</div> </div>
</div>
<div>
<div <div
aria-label="Editor" aria-label="Editor"
class="MenuOption TextOption menu-option" class="MenuOption TextOption menu-option"
@ -394,8 +382,6 @@ exports[`src/components/shareBoard/userPermissionsRow should match snapshot in p
class="noicon" class="noicon"
/> />
</div> </div>
</div>
<div>
<div <div
aria-label="Admin" aria-label="Admin"
class="MenuOption TextOption menu-option" class="MenuOption TextOption menu-option"
@ -431,8 +417,6 @@ exports[`src/components/shareBoard/userPermissionsRow should match snapshot in p
class="noicon" class="noicon"
/> />
</div> </div>
</div>
<div>
<div <div
class="MenuOption MenuSeparator menu-separator" class="MenuOption MenuSeparator menu-separator"
/> />
@ -586,9 +570,6 @@ exports[`src/components/shareBoard/userPermissionsRow should match snapshot in t
class="noicon" class="noicon"
/> />
</div> </div>
</div>
<div />
<div>
<div <div
aria-label="Editor" aria-label="Editor"
class="MenuOption TextOption menu-option" class="MenuOption TextOption menu-option"
@ -618,8 +599,6 @@ exports[`src/components/shareBoard/userPermissionsRow should match snapshot in t
class="noicon" class="noicon"
/> />
</div> </div>
</div>
<div>
<div <div
aria-label="Admin" aria-label="Admin"
class="MenuOption TextOption menu-option" class="MenuOption TextOption menu-option"
@ -655,8 +634,6 @@ exports[`src/components/shareBoard/userPermissionsRow should match snapshot in t
class="noicon" class="noicon"
/> />
</div> </div>
</div>
<div>
<div <div
class="MenuOption MenuSeparator menu-separator" class="MenuOption MenuSeparator menu-separator"
/> />
@ -786,141 +763,7 @@ exports[`src/components/shareBoard/userPermissionsRow should match snapshot-admi
<div <div
class="menu-options" class="menu-options"
> >
<div> <div />
<div
aria-label="Viewer"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex menu-option__check"
>
<div
class="menu-option__icon"
>
<div
class="empty-icon"
/>
</div>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Viewer
</div>
</div>
<div
class="noicon"
/>
</div>
</div>
<div>
<div
aria-label="Commenter"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex menu-option__check"
>
<div
class="menu-option__icon"
>
<div
class="empty-icon"
/>
</div>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Commenter
</div>
</div>
<div
class="noicon"
/>
</div>
</div>
<div>
<div
aria-label="Editor"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex menu-option__check"
>
<div
class="menu-option__icon"
>
<div
class="empty-icon"
/>
</div>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Editor
</div>
</div>
<div
class="noicon"
/>
</div>
</div>
<div>
<div
aria-label="Admin"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex menu-option__check"
>
<div
class="menu-option__icon"
>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
</div>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Admin
</div>
</div>
<div
class="noicon"
/>
</div>
</div>
<div>
<div
class="MenuOption MenuSeparator menu-separator"
/>
</div>
<div> <div>
<div <div
aria-label="Remove member" aria-label="Remove member"

View File

@ -83,38 +83,43 @@ const UserPermissionsRow = (props: Props): JSX.Element => {
position='left' position='left'
parentRef={menuWrapperRef} parentRef={menuWrapperRef}
> >
{(board.minimumRole === MemberRole.Viewer || board.minimumRole === MemberRole.None) && {
<Menu.Text (!user.permissions || user.permissions?.find((s) => s !== 'manage_system' && s !== 'manage_team')) &&
id={MemberRole.Viewer} <>
check={true} {(board.minimumRole === MemberRole.Viewer || board.minimumRole === MemberRole.None) &&
icon={currentRole === MemberRole.Viewer ? <CheckIcon/> : <div className='empty-icon'/>} <Menu.Text
name={intl.formatMessage({id: 'BoardMember.schemeViewer', defaultMessage: 'Viewer'})} id={MemberRole.Viewer}
onClick={() => props.onUpdateBoardMember(member, MemberRole.Viewer)} check={true}
/>} icon={currentRole === MemberRole.Viewer ? <CheckIcon/> : <div className='empty-icon'/>}
{!board.isTemplate && (board.minimumRole === MemberRole.None || board.minimumRole === MemberRole.Commenter || board.minimumRole === MemberRole.Viewer) && name={intl.formatMessage({id: 'BoardMember.schemeViewer', defaultMessage: 'Viewer'})}
<Menu.Text onClick={() => props.onUpdateBoardMember(member, MemberRole.Viewer)}
id={MemberRole.Commenter} />}
check={true} {!board.isTemplate && (board.minimumRole === MemberRole.None || board.minimumRole === MemberRole.Commenter || board.minimumRole === MemberRole.Viewer) &&
icon={currentRole === MemberRole.Commenter ? <CheckIcon/> : <div className='empty-icon'/>} <Menu.Text
name={intl.formatMessage({id: 'BoardMember.schemeCommenter', defaultMessage: 'Commenter'})} id={MemberRole.Commenter}
onClick={() => props.onUpdateBoardMember(member, MemberRole.Commenter)} check={true}
/>} icon={currentRole === MemberRole.Commenter ? <CheckIcon/> : <div className='empty-icon'/>}
<Menu.Text name={intl.formatMessage({id: 'BoardMember.schemeCommenter', defaultMessage: 'Commenter'})}
id={MemberRole.Editor} onClick={() => props.onUpdateBoardMember(member, MemberRole.Commenter)}
check={true} />}
icon={currentRole === MemberRole.Editor ? <CheckIcon/> : <div className='empty-icon'/>} <Menu.Text
name={intl.formatMessage({id: 'BoardMember.schemeEditor', defaultMessage: 'Editor'})} id={MemberRole.Editor}
onClick={() => props.onUpdateBoardMember(member, MemberRole.Editor)} check={true}
/> icon={currentRole === MemberRole.Editor ? <CheckIcon/> : <div className='empty-icon'/>}
{user.is_guest !== true && name={intl.formatMessage({id: 'BoardMember.schemeEditor', defaultMessage: 'Editor'})}
<Menu.Text onClick={() => props.onUpdateBoardMember(member, MemberRole.Editor)}
id={MemberRole.Admin} />
check={true} {user.is_guest !== true &&
icon={currentRole === MemberRole.Admin ? <CheckIcon/> : <div className='empty-icon'/>} <Menu.Text
name={intl.formatMessage({id: 'BoardMember.schemeAdmin', defaultMessage: 'Admin'})} id={MemberRole.Admin}
onClick={() => props.onUpdateBoardMember(member, MemberRole.Admin)} check={true}
/>} icon={currentRole === MemberRole.Admin ? <CheckIcon/> : <div className='empty-icon'/>}
<Menu.Separator/> name={intl.formatMessage({id: 'BoardMember.schemeAdmin', defaultMessage: 'Admin'})}
onClick={() => props.onUpdateBoardMember(member, MemberRole.Admin)}
/>}
<Menu.Separator/>
</>
}
<Menu.Text <Menu.Text
id='Remove' id='Remove'
name={intl.formatMessage({id: 'ShareBoard.userPermissionsRemoveMemberText', defaultMessage: 'Remove member'})} name={intl.formatMessage({id: 'ShareBoard.userPermissionsRemoveMemberText', defaultMessage: 'Remove member'})}