mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
chore: UI SCIM guard for groups (#6866)
https://linear.app/unleash/issue/2-2113/ui-should-not-allow-manual-management-of-scim-managed-groups-in Adds a UI SCIM guard when trying to manage groups. The condition for the guard is: - Enterprise - SCIM flag enabled - SCIM setting enabled - SCIM group Similar to https://github.com/Unleash/unleash/pull/6859
This commit is contained in:
parent
8b25ebf792
commit
bc0704581b
@ -6,7 +6,7 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { useGroupApi } from 'hooks/api/actions/useGroupApi/useGroupApi';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { Button } from '@mui/material';
|
||||
import { Button, Tooltip } from '@mui/material';
|
||||
import { EDIT } from 'constants/misc';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import { useGroup } from 'hooks/api/getters/useGroup/useGroup';
|
||||
@ -14,6 +14,8 @@ import { UG_SAVE_BTN_ID } from 'utils/testIds';
|
||||
import { GO_BACK } from 'constants/navigate';
|
||||
import { useGroups } from 'hooks/api/getters/useGroups/useGroups';
|
||||
import type { IGroup } from 'interfaces/group';
|
||||
import { scimGroupTooltip } from '../group-constants';
|
||||
import { useScimSettings } from 'hooks/api/getters/useScimSettings/useScimSettings';
|
||||
|
||||
export const EditGroupContainer = () => {
|
||||
const groupId = Number(useRequiredPathParam('groupId'));
|
||||
@ -46,6 +48,11 @@ export const EditGroup = ({
|
||||
const { uiConfig } = useUiConfig();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const {
|
||||
settings: { enabled: scimEnabled },
|
||||
} = useScimSettings();
|
||||
const isScimGroup = scimEnabled && Boolean(group?.scimId);
|
||||
|
||||
const {
|
||||
name,
|
||||
setName,
|
||||
@ -143,15 +150,19 @@ export const EditGroup = ({
|
||||
handleCancel={handleCancel}
|
||||
mode={EDIT}
|
||||
>
|
||||
<Button
|
||||
type='submit'
|
||||
variant='contained'
|
||||
color='primary'
|
||||
disabled={!isValid}
|
||||
data-testid={UG_SAVE_BTN_ID}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<Tooltip title={isScimGroup ? scimGroupTooltip : ''} arrow>
|
||||
<div>
|
||||
<Button
|
||||
type='submit'
|
||||
variant='contained'
|
||||
color='primary'
|
||||
disabled={isScimGroup || !isValid}
|
||||
data-testid={UG_SAVE_BTN_ID}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</GroupForm>
|
||||
</FormTemplate>
|
||||
);
|
||||
|
@ -41,6 +41,8 @@ import {
|
||||
UG_EDIT_USERS_BTN_ID,
|
||||
UG_REMOVE_USER_BTN_ID,
|
||||
} from 'utils/testIds';
|
||||
import { useScimSettings } from 'hooks/api/getters/useScimSettings/useScimSettings';
|
||||
import { scimGroupTooltip } from '../group-constants';
|
||||
|
||||
export const groupUsersPlaceholder: IGroupUser[] = Array(15).fill({
|
||||
name: 'Name of the user',
|
||||
@ -68,6 +70,11 @@ export const Group: VFC = () => {
|
||||
const [removeUserOpen, setRemoveUserOpen] = useState(false);
|
||||
const [selectedUser, setSelectedUser] = useState<IGroupUser>();
|
||||
|
||||
const {
|
||||
settings: { enabled: scimEnabled },
|
||||
} = useScimSettings();
|
||||
const isScimGroup = scimEnabled && Boolean(group?.scimId);
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
@ -127,7 +134,11 @@ export const Group: VFC = () => {
|
||||
Cell: ({ row: { original: rowUser } }: any) => (
|
||||
<ActionCell>
|
||||
<Tooltip
|
||||
title='Remove user from group'
|
||||
title={
|
||||
isScimGroup
|
||||
? scimGroupTooltip
|
||||
: 'Remove user from group'
|
||||
}
|
||||
arrow
|
||||
describeChild
|
||||
>
|
||||
@ -138,6 +149,7 @@ export const Group: VFC = () => {
|
||||
setSelectedUser(rowUser);
|
||||
setRemoveUserOpen(true);
|
||||
}}
|
||||
disabled={isScimGroup}
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
@ -245,8 +257,11 @@ export const Group: VFC = () => {
|
||||
data-loading
|
||||
permission={ADMIN}
|
||||
tooltipProps={{
|
||||
title: 'Edit group',
|
||||
title: isScimGroup
|
||||
? scimGroupTooltip
|
||||
: 'Edit group',
|
||||
}}
|
||||
disabled={isScimGroup}
|
||||
>
|
||||
<Edit />
|
||||
</PermissionIconButton>
|
||||
@ -256,8 +271,11 @@ export const Group: VFC = () => {
|
||||
onClick={() => setRemoveOpen(true)}
|
||||
permission={ADMIN}
|
||||
tooltipProps={{
|
||||
title: 'Delete group',
|
||||
title: isScimGroup
|
||||
? scimGroupTooltip
|
||||
: 'Delete group',
|
||||
}}
|
||||
disabled={isScimGroup}
|
||||
>
|
||||
<Delete />
|
||||
</PermissionIconButton>
|
||||
@ -304,6 +322,12 @@ export const Group: VFC = () => {
|
||||
maxWidth='700px'
|
||||
Icon={Add}
|
||||
permission={ADMIN}
|
||||
disabled={isScimGroup}
|
||||
tooltipProps={{
|
||||
title: isScimGroup
|
||||
? scimGroupTooltip
|
||||
: '',
|
||||
}}
|
||||
>
|
||||
Edit users
|
||||
</ResponsiveButton>
|
||||
|
@ -7,6 +7,7 @@ import { Badge } from 'component/common/Badge/Badge';
|
||||
import { GroupCardActions } from './GroupCardActions/GroupCardActions';
|
||||
import TopicOutlinedIcon from '@mui/icons-material/TopicOutlined';
|
||||
import { RoleBadge } from 'component/common/RoleBadge/RoleBadge';
|
||||
import { useScimSettings } from 'hooks/api/getters/useScimSettings/useScimSettings';
|
||||
|
||||
const StyledLink = styled(Link)(({ theme }) => ({
|
||||
textDecoration: 'none',
|
||||
@ -96,6 +97,12 @@ export const GroupCard = ({
|
||||
onRemoveGroup,
|
||||
}: IGroupCardProps) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const {
|
||||
settings: { enabled: scimEnabled },
|
||||
} = useScimSettings();
|
||||
const isScimGroup = scimEnabled && Boolean(group.scimId);
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledLink key={group.id} to={`/admin/groups/${group.id}`}>
|
||||
@ -107,6 +114,7 @@ export const GroupCard = ({
|
||||
groupId={group.id}
|
||||
onEditUsers={() => onEditUsers(group)}
|
||||
onRemove={() => onRemoveGroup(group)}
|
||||
isScimGroup={isScimGroup}
|
||||
/>
|
||||
</StyledHeaderActions>
|
||||
</StyledTitleRow>
|
||||
|
@ -15,6 +15,7 @@ import Edit from '@mui/icons-material/Edit';
|
||||
import GroupRounded from '@mui/icons-material/GroupRounded';
|
||||
import MoreVert from '@mui/icons-material/MoreVert';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { scimGroupTooltip } from 'component/admin/groups/group-constants';
|
||||
|
||||
const StyledActions = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
@ -31,12 +32,14 @@ interface IGroupCardActions {
|
||||
groupId: number;
|
||||
onEditUsers: () => void;
|
||||
onRemove: () => void;
|
||||
isScimGroup?: boolean;
|
||||
}
|
||||
|
||||
export const GroupCardActions: FC<IGroupCardActions> = ({
|
||||
groupId,
|
||||
onEditUsers,
|
||||
onRemove,
|
||||
isScimGroup,
|
||||
}) => {
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||
|
||||
@ -58,17 +61,24 @@ export const GroupCardActions: FC<IGroupCardActions> = ({
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<Tooltip title='Group actions' arrow describeChild>
|
||||
<IconButton
|
||||
id={id}
|
||||
aria-controls={open ? menuId : undefined}
|
||||
aria-haspopup='true'
|
||||
aria-expanded={open ? 'true' : undefined}
|
||||
onClick={handleClick}
|
||||
type='button'
|
||||
>
|
||||
<MoreVert />
|
||||
</IconButton>
|
||||
<Tooltip
|
||||
title={isScimGroup ? scimGroupTooltip : 'Group actions'}
|
||||
arrow
|
||||
describeChild
|
||||
>
|
||||
<div>
|
||||
<IconButton
|
||||
id={id}
|
||||
aria-controls={open ? menuId : undefined}
|
||||
aria-haspopup='true'
|
||||
aria-expanded={open ? 'true' : undefined}
|
||||
onClick={handleClick}
|
||||
type='button'
|
||||
disabled={isScimGroup}
|
||||
>
|
||||
<MoreVert />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<StyledPopover
|
||||
id={menuId}
|
||||
|
2
frontend/src/component/admin/groups/group-constants.ts
Normal file
2
frontend/src/component/admin/groups/group-constants.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export const scimGroupTooltip =
|
||||
'This group is managed by your SCIM provider and cannot be changed manually';
|
@ -20,7 +20,7 @@ interface IUsersActionsCellProps {
|
||||
onChangePassword: (event: React.SyntheticEvent) => void;
|
||||
onResetPassword: (event: React.SyntheticEvent) => void;
|
||||
onDelete: (event: React.SyntheticEvent) => void;
|
||||
scimEnabled?: boolean;
|
||||
isScimUser?: boolean;
|
||||
}
|
||||
|
||||
export const UsersActionsCell: VFC<IUsersActionsCellProps> = ({
|
||||
@ -29,7 +29,7 @@ export const UsersActionsCell: VFC<IUsersActionsCellProps> = ({
|
||||
onChangePassword,
|
||||
onResetPassword,
|
||||
onDelete,
|
||||
scimEnabled,
|
||||
isScimUser,
|
||||
}) => {
|
||||
const scimTooltip =
|
||||
'This user is managed by your SCIM provider and cannot be changed manually';
|
||||
@ -41,9 +41,9 @@ export const UsersActionsCell: VFC<IUsersActionsCellProps> = ({
|
||||
onClick={onEdit}
|
||||
permission={ADMIN}
|
||||
tooltipProps={{
|
||||
title: scimEnabled ? scimTooltip : 'Edit user',
|
||||
title: isScimUser ? scimTooltip : 'Edit user',
|
||||
}}
|
||||
disabled={scimEnabled}
|
||||
disabled={isScimUser}
|
||||
>
|
||||
<Edit />
|
||||
</PermissionIconButton>
|
||||
@ -69,9 +69,9 @@ export const UsersActionsCell: VFC<IUsersActionsCellProps> = ({
|
||||
onClick={onChangePassword}
|
||||
permission={ADMIN}
|
||||
tooltipProps={{
|
||||
title: scimEnabled ? scimTooltip : 'Change password',
|
||||
title: isScimUser ? scimTooltip : 'Change password',
|
||||
}}
|
||||
disabled={scimEnabled}
|
||||
disabled={isScimUser}
|
||||
>
|
||||
<Lock />
|
||||
</PermissionIconButton>
|
||||
@ -80,9 +80,9 @@ export const UsersActionsCell: VFC<IUsersActionsCellProps> = ({
|
||||
onClick={onResetPassword}
|
||||
permission={ADMIN}
|
||||
tooltipProps={{
|
||||
title: scimEnabled ? scimTooltip : 'Reset password',
|
||||
title: isScimUser ? scimTooltip : 'Reset password',
|
||||
}}
|
||||
disabled={scimEnabled}
|
||||
disabled={isScimUser}
|
||||
>
|
||||
<LockReset />
|
||||
</PermissionIconButton>
|
||||
@ -91,9 +91,9 @@ export const UsersActionsCell: VFC<IUsersActionsCellProps> = ({
|
||||
onClick={onDelete}
|
||||
permission={ADMIN}
|
||||
tooltipProps={{
|
||||
title: scimEnabled ? scimTooltip : 'Remove user',
|
||||
title: isScimUser ? scimTooltip : 'Remove user',
|
||||
}}
|
||||
disabled={scimEnabled}
|
||||
disabled={isScimUser}
|
||||
>
|
||||
<Delete />
|
||||
</PermissionIconButton>
|
||||
|
@ -58,10 +58,8 @@ const UsersList = () => {
|
||||
});
|
||||
const userAccessUIEnabled = useUiFlag('userAccessUIEnabled');
|
||||
const {
|
||||
settings: { enabled: scimSettingEnabled },
|
||||
settings: { enabled: scimEnabled },
|
||||
} = useScimSettings();
|
||||
const scimFlagEnabled = useUiFlag('scimApi');
|
||||
const scimEnabled = isEnterprise() && scimSettingEnabled && scimFlagEnabled;
|
||||
const [delDialog, setDelDialog] = useState(false);
|
||||
const [showConfirm, setShowConfirm] = useState(false);
|
||||
const [emailSent, setEmailSent] = useState(false);
|
||||
@ -218,7 +216,7 @@ const UsersList = () => {
|
||||
onChangePassword={openPwDialog(user)}
|
||||
onResetPassword={openResetPwDialog(user)}
|
||||
onDelete={openDelDialog(user)}
|
||||
scimEnabled={scimEnabled && Boolean(user.scimId)}
|
||||
isScimUser={scimEnabled && Boolean(user.scimId)}
|
||||
/>
|
||||
),
|
||||
width: userAccessUIEnabled ? 240 : 200,
|
||||
|
Loading…
Reference in New Issue
Block a user