fixed issue failed to revoke grant access of the user

This commit is contained in:
Yinyin Liu 2026-02-27 09:04:54 +01:00
parent e5b910238f
commit 7476c939c3
4 changed files with 216 additions and 271 deletions

View File

@ -363,6 +363,38 @@ public class Controller : ControllerBase
return user.AccessibleInstallations().ToList(); return user.AccessibleInstallations().ToList();
} }
[HttpGet(nameof(GetDirectInstallationAccessForUser))]
public ActionResult<IEnumerable<Object>> GetDirectInstallationAccessForUser(Int64 userId, Token authToken)
{
var sessionUser = Db.GetSession(authToken)?.User;
if (sessionUser == null)
return Unauthorized();
var user = Db.GetUserById(userId);
if (user == null)
return Unauthorized();
return user.DirectlyAccessibleInstallations()
.Select(i => new { i.Id, i.Name })
.ToList<Object>();
}
[HttpGet(nameof(GetDirectFolderAccessForUser))]
public ActionResult<IEnumerable<Object>> GetDirectFolderAccessForUser(Int64 userId, Token authToken)
{
var sessionUser = Db.GetSession(authToken)?.User;
if (sessionUser == null)
return Unauthorized();
var user = Db.GetUserById(userId);
if (user == null)
return Unauthorized();
return user.DirectlyAccessibleFolders()
.Select(f => new { f.Id, f.Name })
.ToList<Object>();
}
[HttpGet(nameof(GetUsersWithInheritedAccessToInstallation))] [HttpGet(nameof(GetUsersWithInheritedAccessToInstallation))]
public ActionResult<IEnumerable<Object>> GetUsersWithInheritedAccessToInstallation(Int64 id, Token authToken) public ActionResult<IEnumerable<Object>> GetUsersWithInheritedAccessToInstallation(Int64 id, Token authToken)
{ {

View File

@ -473,13 +473,25 @@ public static class SessionMethods
{ {
var sessionUser = session?.User; var sessionUser = session?.User;
return sessionUser is not null if (sessionUser is null || installation is null || user is null)
&& installation is not null return false;
&& user is not null
&& (user.IsDescendantOf(sessionUser) || sessionUser.UserType == 2) if (!(user.IsDescendantOf(sessionUser) || sessionUser.UserType == 2))
&& sessionUser.HasAccessTo(installation) return false;
&& user.HasAccessTo(installation)
&& Db.InstallationAccess.Delete(a => a.UserId == user.Id && a.InstallationId == installation.Id) > 0; if (!sessionUser.HasAccessTo(installation) || !user.HasAccessTo(installation))
return false;
// Try direct InstallationAccess record first
if (Db.InstallationAccess.Delete(a => a.UserId == user.Id && a.InstallationId == installation.Id) > 0)
return true;
// No direct record — access is inherited via a folder; revoke that folder access
var accessFolder = installation.Ancestors()
.FirstOrDefault(f => user.HasDirectAccessTo(f));
return accessFolder is not null
&& Db.FolderAccess.Delete(a => a.UserId == user.Id && a.FolderId == accessFolder.Id) > 0;
} }
public static Boolean RevokeUserAccessTo(this Session? session, User? user, Folder? folder) public static Boolean RevokeUserAccessTo(this Session? session, User? user, Folder? folder)

View File

@ -15,9 +15,11 @@ import {
IconButton, IconButton,
InputLabel, InputLabel,
ListItem, ListItem,
ListSubheader,
MenuItem, MenuItem,
Modal, Modal,
Select, Select,
Typography,
useTheme useTheme
} from '@mui/material'; } from '@mui/material';
import { TokenContext } from 'src/contexts/tokenContext'; import { TokenContext } from 'src/contexts/tokenContext';
@ -26,6 +28,7 @@ import ListItemAvatar from '@mui/material/ListItemAvatar';
import Avatar from '@mui/material/Avatar'; import Avatar from '@mui/material/Avatar';
import ListItemText from '@mui/material/ListItemText'; import ListItemText from '@mui/material/ListItemText';
import PersonIcon from '@mui/icons-material/Person'; import PersonIcon from '@mui/icons-material/Person';
import FolderIcon from '@mui/icons-material/Folder';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
import { Close as CloseIcon } from '@mui/icons-material'; import { Close as CloseIcon } from '@mui/icons-material';
import { AccessContext } from 'src/contexts/AccessContextProvider'; import { AccessContext } from 'src/contexts/AccessContextProvider';
@ -52,22 +55,24 @@ function UserAccess(props: UserAccessProps) {
const tokencontext = useContext(TokenContext); const tokencontext = useContext(TokenContext);
const { removeToken } = tokencontext; const { removeToken } = tokencontext;
const context = useContext(UserContext); const context = useContext(UserContext);
const { currentUser, setUser } = context; const { currentUser } = context;
const [openFolder, setOpenFolder] = useState(false); const [openFolder, setOpenFolder] = useState(false);
const [openInstallation, setOpenInstallation] = useState(false); const [openInstallation, setOpenInstallation] = useState(false);
const [openModal, setOpenModal] = useState(false); const [openModal, setOpenModal] = useState(false);
const [selectedFolderNames, setSelectedFolderNames] = useState<string[]>([]); const [selectedFolderNames, setSelectedFolderNames] = useState<string[]>([]);
const [selectedInstallationNames, setSelectedInstallationNames] = useState< const [selectedInstallationNames, setSelectedInstallationNames] = useState<string[]>([]);
string[]
>([]); // Available choices for grant modal
const [availableFolders, setAvailableFolders] = useState<I_Folder[]>([]);
const [availableInstallations, setAvailableInstallations] = useState<I_Installation[]>([]);
// Direct grants for this user
const [directFolders, setDirectFolders] = useState<{ id: number; name: string }[]>([]);
const [directInstallations, setDirectInstallations] = useState<{ id: number; name: string }[]>([]);
const [folders, setFolders] = useState<I_Folder[]>([]);
const [installations, setInstallations] = useState<I_Installation[]>([]);
const accessContext = useContext(AccessContext); const accessContext = useContext(AccessContext);
const { const {
fetchInstallationsForUser,
accessibleInstallationsForUser,
error, error,
setError, setError,
updated, updated,
@ -75,130 +80,118 @@ function UserAccess(props: UserAccessProps) {
updatedmessage, updatedmessage,
errormessage, errormessage,
setErrorMessage, setErrorMessage,
setUpdatedMessage, setUpdatedMessage
RevokeAccessFromResource
} = accessContext; } = accessContext;
const fetchFolders = useCallback(async () => { const fetchDirectGrants = useCallback(async () => {
try {
const [foldersRes, installationsRes] = await Promise.all([
axiosConfig.get(`/GetDirectFolderAccessForUser?userId=${props.current_user.id}`),
axiosConfig.get(`/GetDirectInstallationAccessForUser?userId=${props.current_user.id}`)
]);
setDirectFolders(foldersRes.data);
setDirectInstallations(installationsRes.data);
} catch (err) {
if (err.response && err.response.status === 401) removeToken();
}
}, [props.current_user.id]);
const fetchAvailableFolders = useCallback(async () => {
return axiosConfig return axiosConfig
.get('/GetAllFolders') .get('/GetAllFolders')
.then((res) => { .then((res) => setAvailableFolders(res.data))
setFolders(res.data);
})
.catch((err) => { .catch((err) => {
if (err.response && err.response.status == 401) { if (err.response && err.response.status == 401) removeToken();
removeToken();
}
}); });
}, [setFolders]); }, []);
const fetchInstallations = useCallback(async () => { const fetchAvailableInstallations = useCallback(async () => {
try { try {
// fetch product 0 const [res0, res1, res2, res3] = await Promise.all([
const res0 = await axiosConfig.get( axiosConfig.get(`/GetAllInstallationsFromProduct?product=0`),
`/GetAllInstallationsFromProduct?product=0` axiosConfig.get(`/GetAllInstallationsFromProduct?product=1`),
); axiosConfig.get(`/GetAllInstallationsFromProduct?product=2`),
const installations0 = res0.data; axiosConfig.get(`/GetAllInstallationsFromProduct?product=3`)
]);
// fetch product 1 setAvailableInstallations([...res0.data, ...res1.data, ...res2.data, ...res3.data]);
const res1 = await axiosConfig.get(
`/GetAllInstallationsFromProduct?product=3`
);
const installations1 = res1.data;
// aggregate
const combined = [...installations0, ...installations1];
// update
setInstallations(combined);
} catch (err) { } catch (err) {
if (err.response && err.response.status === 401) { if (err.response && err.response.status === 401) removeToken();
removeToken();
} }
} finally { }, []);
}
}, [setInstallations]);
useEffect(() => { useEffect(() => {
fetchInstallationsForUser(props.current_user.id); fetchDirectGrants();
}, [props.current_user]); }, [props.current_user]);
const handleGrantAccess = () => { const handleGrantAccess = () => {
fetchFolders(); fetchAvailableFolders();
fetchInstallations(); fetchAvailableInstallations();
setOpenModal(true); setOpenModal(true);
setSelectedFolderNames([]); setSelectedFolderNames([]);
setSelectedInstallationNames([]); setSelectedInstallationNames([]);
}; };
const handleFolderChange = (event) => { const handleRevokeFolder = async (folderId: number, folderName: string) => {
setSelectedFolderNames(event.target.value); axiosConfig
.post(`/RevokeUserAccessToFolder?UserId=${props.current_user.id}&FolderId=${folderId}`)
.then(() => {
setUpdatedMessage(intl.formatMessage({ id: 'revokedAccessFromUser' }) + ' ' + props.current_user.name);
setUpdated(true);
setTimeout(() => setUpdated(false), 3000);
fetchDirectGrants();
})
.catch(() => {
setErrorMessage(intl.formatMessage({ id: 'unableToRevokeAccess' }));
setError(true);
});
}; };
const handleInstallationChange = (event) => { const handleRevokeInstallation = async (installationId: number) => {
setSelectedInstallationNames(event.target.value); axiosConfig
}; .post(`/RevokeUserAccessToInstallation?UserId=${props.current_user.id}&InstallationId=${installationId}`)
const handleOpenFolder = () => { .then(() => {
setOpenFolder(true); setUpdatedMessage(intl.formatMessage({ id: 'revokedAccessFromUser' }) + ' ' + props.current_user.name);
}; setUpdated(true);
setTimeout(() => setUpdated(false), 3000);
const handleCloseFolder = () => { fetchDirectGrants();
setOpenFolder(false); })
}; .catch(() => {
const handleCancel = () => { setErrorMessage(intl.formatMessage({ id: 'unableToRevokeAccess' }));
setOpenModal(false); setError(true);
}; });
const handleOpenInstallation = () => {
setOpenInstallation(true);
};
const handleCloseInstallation = () => {
setOpenInstallation(false);
}; };
const handleSubmit = async () => { const handleSubmit = async () => {
for (const folderName of selectedFolderNames) { for (const folderName of selectedFolderNames) {
const folder = folders.find((folder) => folder.name === folderName); const folder = availableFolders.find((f) => f.name === folderName);
await axiosConfig await axiosConfig
.post( .post(`/GrantUserAccessToFolder?UserId=${props.current_user.id}&FolderId=${folder.id}`)
`/GrantUserAccessToFolder?UserId=${props.current_user.id}&FolderId=${folder.id}` .then(() => {
)
.then((response) => {
if (response) {
setUpdatedMessage(intl.formatMessage({ id: 'grantedAccessToUser' }, { name: props.current_user.name })); setUpdatedMessage(intl.formatMessage({ id: 'grantedAccessToUser' }, { name: props.current_user.name }));
setUpdated(true); setUpdated(true);
}
}) })
.catch((err) => { .catch(() => {
setErrorMessage(intl.formatMessage({ id: 'errorOccured' })); setErrorMessage(intl.formatMessage({ id: 'errorOccured' }));
setError(true); setError(true);
}); });
} }
for (const installationName of selectedInstallationNames) { for (const installationName of selectedInstallationNames) {
const installation = installations.find( const installation = availableInstallations.find((i) => i.name === installationName);
(installation) => installation.name === installationName
);
await axiosConfig await axiosConfig
.post( .post(`/GrantUserAccessToInstallation?UserId=${props.current_user.id}&InstallationId=${installation.id}`)
`/GrantUserAccessToInstallation?UserId=${props.current_user.id}&InstallationId=${installation.id}` .then(() => {
)
.then((response) => {
if (response) {
setUpdatedMessage(intl.formatMessage({ id: 'grantedAccessToUser' }, { name: props.current_user.name })); setUpdatedMessage(intl.formatMessage({ id: 'grantedAccessToUser' }, { name: props.current_user.name }));
setUpdated(true); setUpdated(true);
}
}) })
.catch((err) => { .catch(() => {
setErrorMessage(intl.formatMessage({ id: 'errorOccured' })); setErrorMessage(intl.formatMessage({ id: 'errorOccured' }));
setError(true); setError(true);
}); });
} }
setOpenModal(false); setOpenModal(false);
fetchInstallationsForUser(props.current_user.id); fetchDirectGrants();
}; };
return ( return (
@ -206,51 +199,25 @@ function UserAccess(props: UserAccessProps) {
<Grid container> <Grid container>
<Grid item xs={12} md={12}> <Grid item xs={12} md={12}>
{updated && ( {updated && (
<Alert <Alert severity="success" sx={{ mt: 1 }}>
severity="success"
sx={{
mt: 1
}}
>
{updatedmessage} {updatedmessage}
<IconButton <IconButton color="inherit" size="small" onClick={() => setUpdated(false)}>
color="inherit"
size="small"
onClick={() => setUpdated(false)}
>
<CloseIcon fontSize="small" /> <CloseIcon fontSize="small" />
</IconButton> </IconButton>
</Alert> </Alert>
)} )}
{error && ( {error && (
<Alert <Alert severity="error" sx={{ marginTop: '20px', marginBottom: '20px' }}>
severity="error"
sx={{
marginTop: '20px',
marginBottom: '20px'
}}
>
{errormessage} {errormessage}
<IconButton <IconButton color="inherit" size="small" onClick={() => setError(false)} sx={{ marginLeft: '10px' }}>
color="inherit"
size="small"
onClick={() => setError(false)}
sx={{
marginLeft: '10px'
}}
>
<CloseIcon fontSize="small" /> <CloseIcon fontSize="small" />
</IconButton> </IconButton>
</Alert> </Alert>
)} )}
<Modal {/* Grant Access Modal */}
open={openModal} <Modal open={openModal} onClose={() => {}} aria-labelledby="grant-modal">
onClose={() => {}}
aria-labelledby="error-modal"
aria-describedby="error-modal-description"
>
<Box <Box
sx={{ sx={{
position: 'absolute', position: 'absolute',
@ -264,59 +231,29 @@ function UserAccess(props: UserAccessProps) {
p: 4 p: 4
}} }}
> >
<Box <Box component="form" sx={{ textAlign: 'center' }} noValidate autoComplete="off">
component="form"
sx={{
textAlign: 'center'
}}
noValidate
autoComplete="off"
>
<div> <div>
<FormControl fullWidth sx={{ marginTop: 1, width: 390 }}> <FormControl fullWidth sx={{ marginTop: 1, width: 390 }}>
<InputLabel <InputLabel sx={{ fontSize: 14, backgroundColor: 'white' }}>
sx={{ <FormattedMessage id="grantAccessToFolders" defaultMessage="Grant access to folders" />
fontSize: 14,
backgroundColor: 'white'
}}
>
<FormattedMessage
id="grantAccessToFolders"
defaultMessage="Grant access to folders"
/>
</InputLabel> </InputLabel>
<Select <Select
multiple multiple
value={selectedFolderNames} value={selectedFolderNames}
onChange={handleFolderChange} onChange={(e) => setSelectedFolderNames(e.target.value as string[])}
open={openFolder} open={openFolder}
onClose={handleCloseFolder} onClose={() => setOpenFolder(false)}
onOpen={handleOpenFolder} onOpen={() => setOpenFolder(true)}
renderValue={(selected) => ( renderValue={(selected) => (
<div> <div>{selected.map((f) => <span key={f}>{f}, </span>)}</div>
{selected.map((folder) => (
<span key={folder}>{folder}, </span>
))}
</div>
)} )}
> >
{folders.map((folder) => ( {availableFolders.map((folder) => (
<MenuItem key={folder.id} value={folder.name}> <MenuItem key={folder.id} value={folder.name}>{folder.name}</MenuItem>
{folder.name}
</MenuItem>
))} ))}
<Button <Button
sx={{ sx={{ marginLeft: '150px', marginTop: '10px', backgroundColor: theme.colors.primary.main, color: 'white', '&:hover': { backgroundColor: theme.colors.primary.dark }, padding: '6px 8px' }}
marginLeft: '150px', onClick={() => setOpenFolder(false)}
marginTop: '10px',
backgroundColor: theme.colors.primary.main,
color: 'white',
'&:hover': {
backgroundColor: theme.colors.primary.dark
},
padding: '6px 8px'
}}
onClick={handleCloseFolder}
> >
<FormattedMessage id="submit" defaultMessage="Submit" /> <FormattedMessage id="submit" defaultMessage="Submit" />
</Button> </Button>
@ -326,52 +263,26 @@ function UserAccess(props: UserAccessProps) {
<div> <div>
<FormControl fullWidth sx={{ marginTop: 2, width: 390 }}> <FormControl fullWidth sx={{ marginTop: 2, width: 390 }}>
<InputLabel <InputLabel sx={{ fontSize: 14, backgroundColor: 'white' }}>
sx={{ <FormattedMessage id="grantAccessToInstallations" defaultMessage="Grant access to installations" />
fontSize: 14,
backgroundColor: 'white'
}}
>
<FormattedMessage
id="grantAccessToInstallations"
defaultMessage="Grant access to installations"
/>
</InputLabel> </InputLabel>
<Select <Select
multiple multiple
value={selectedInstallationNames} value={selectedInstallationNames}
onChange={handleInstallationChange} onChange={(e) => setSelectedInstallationNames(e.target.value as string[])}
open={openInstallation} open={openInstallation}
onClose={handleCloseInstallation} onClose={() => setOpenInstallation(false)}
onOpen={handleOpenInstallation} onOpen={() => setOpenInstallation(true)}
renderValue={(selected) => ( renderValue={(selected) => (
<div> <div>{selected.map((i) => <span key={i}>{i}, </span>)}</div>
{selected.map((installation) => (
<span key={installation}>{installation}, </span>
))}
</div>
)} )}
> >
{installations.map((installation) => ( {availableInstallations.map((installation) => (
<MenuItem <MenuItem key={installation.id} value={installation.name}>{installation.name}</MenuItem>
key={installation.id}
value={installation.name}
>
{installation.name}
</MenuItem>
))} ))}
<Button <Button
sx={{ sx={{ marginLeft: '150px', marginTop: '10px', backgroundColor: theme.colors.primary.main, color: 'white', '&:hover': { backgroundColor: theme.colors.primary.dark }, padding: '6px 8px' }}
marginLeft: '150px', onClick={() => setOpenInstallation(false)}
marginTop: '10px',
backgroundColor: theme.colors.primary.main,
color: 'white',
'&:hover': {
backgroundColor: theme.colors.primary.dark
},
padding: '6px 8px'
}}
onClick={handleCloseInstallation}
> >
<FormattedMessage id="submit" defaultMessage="Submit" /> <FormattedMessage id="submit" defaultMessage="Submit" />
</Button> </Button>
@ -380,32 +291,15 @@ function UserAccess(props: UserAccessProps) {
</div> </div>
<Button <Button
sx={{ sx={{ marginTop: '20px', backgroundColor: theme.colors.primary.main, color: 'white', '&:hover': { backgroundColor: theme.colors.primary.dark }, padding: '6px 8px' }}
marginTop: '20px',
backgroundColor: theme.colors.primary.main,
color: 'white',
'&:hover': {
backgroundColor: theme.colors.primary.dark
},
padding: '6px 8px'
}}
onClick={handleSubmit} onClick={handleSubmit}
> >
<FormattedMessage id="submit" defaultMessage="Submit" /> <FormattedMessage id="submit" defaultMessage="Submit" />
</Button> </Button>
<Button <Button
variant="contained" variant="contained"
onClick={handleCancel} onClick={() => setOpenModal(false)}
sx={{ sx={{ marginTop: '20px', marginLeft: '10px', backgroundColor: theme.colors.primary.main, color: 'white', '&:hover': { backgroundColor: theme.colors.primary.dark }, padding: '6px 8px' }}
marginTop: '20px',
marginLeft: '10px',
backgroundColor: theme.colors.primary.main,
color: 'white',
'&:hover': {
backgroundColor: theme.colors.primary.dark
},
padding: '6px 8px'
}}
> >
<FormattedMessage id="cancel" defaultMessage="Cancel" /> <FormattedMessage id="cancel" defaultMessage="Cancel" />
</Button> </Button>
@ -416,43 +310,63 @@ function UserAccess(props: UserAccessProps) {
<Button <Button
variant="contained" variant="contained"
onClick={handleGrantAccess} onClick={handleGrantAccess}
sx={{ sx={{ marginTop: '20px', marginBottom: '20px', backgroundColor: '#ffc04d', color: '#000000', '&:hover': { bgcolor: '#f7b34d' } }}
marginTop: '20px',
marginBottom: '20px',
backgroundColor: '#ffc04d',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
> >
<FormattedMessage id="grantAccess" defaultMessage="Grant Access" /> <FormattedMessage id="grantAccess" defaultMessage="Grant Access" />
</Button> </Button>
</Grid> </Grid>
<Grid item xs={12} md={12}>
{accessibleInstallationsForUser.map((installation, index) => {
const isLast = index === accessibleInstallationsForUser.length - 1;
{/* Folder Access Section */}
<Grid item xs={12} md={12}>
<Typography variant="subtitle2" sx={{ mt: 1, mb: 0.5, color: 'text.secondary', fontWeight: 600 }}>
<FormattedMessage id="folderAccess" defaultMessage="Folder Access" />
</Typography>
{directFolders.map((folder, index) => {
const isLast = index === directFolders.length - 1;
return ( return (
<Fragment key={installation.name}> <Fragment key={folder.id}>
<ListItem <ListItem
sx={{ sx={{ mb: isLast ? 1 : 0 }}
mb: isLast ? 4 : 0 // Apply margin-bottom to the last item only
}}
secondaryAction={ secondaryAction={
currentUser.userType === UserType.admin && ( currentUser.userType === UserType.admin && (
<IconButton <IconButton onClick={() => handleRevokeFolder(folder.id, folder.name)} edge="end">
onClick={() => { <PersonRemoveIcon />
RevokeAccessFromResource( </IconButton>
'ToInstallation', )
props.current_user.id, }
'InstallationId',
installation.id,
props.current_user.name
);
fetchInstallationsForUser(props.current_user.id);
}}
edge="end"
> >
<ListItemAvatar>
<Avatar>
<FolderIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={folder.name} />
</ListItem>
<Divider />
</Fragment>
);
})}
{directFolders.length === 0 && (
<Alert severity="info" sx={{ mb: 1 }}>
<FormattedMessage id="noDirectFolderAccess" defaultMessage="No folder access grants" />
</Alert>
)}
</Grid>
{/* Direct Installation Access Section */}
<Grid item xs={12} md={12}>
<Typography variant="subtitle2" sx={{ mt: 2, mb: 0.5, color: 'text.secondary', fontWeight: 600 }}>
<FormattedMessage id="directInstallationAccess" defaultMessage="Direct Installation Access" />
</Typography>
{directInstallations.map((installation, index) => {
const isLast = index === directInstallations.length - 1;
return (
<Fragment key={installation.id}>
<ListItem
sx={{ mb: isLast ? 4 : 0 }}
secondaryAction={
currentUser.userType === UserType.admin && (
<IconButton onClick={() => handleRevokeInstallation(installation.id)} edge="end">
<PersonRemoveIcon /> <PersonRemoveIcon />
</IconButton> </IconButton>
) )
@ -469,22 +383,9 @@ function UserAccess(props: UserAccessProps) {
</Fragment> </Fragment>
); );
})} })}
{directInstallations.length === 0 && (
{accessibleInstallationsForUser.length == 0 && ( <Alert severity="info" sx={{ mb: 4 }}>
<Alert <FormattedMessage id="noDirectInstallationAccess" defaultMessage="No direct installation access grants" />
severity="error"
sx={{
display: 'flex',
alignItems: 'center',
marginTop: '20px',
marginBottom: '20px'
}}
>
<FormattedMessage
id="theUserDoesNOtHaveAccessToAnyInstallation"
defaultMessage="The user does not have access to any installation "
/>
<IconButton color="inherit" size="small"></IconButton>
</Alert> </Alert>
)} )}
</Grid> </Grid>

View File

@ -44,12 +44,12 @@ function Folder(props: singleFolderProps) {
value: 'information', value: 'information',
label: <FormattedMessage id="information" defaultMessage="Information" /> label: <FormattedMessage id="information" defaultMessage="Information" />
}, },
{ ...(currentUser.userType === UserType.admin ? [{
value: 'manage', value: 'manage',
label: ( label: (
<FormattedMessage id="manageAccess" defaultMessage="Manage Access" /> <FormattedMessage id="manageAccess" defaultMessage="Manage Access" />
) )
} }] : [])
]; ];
const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => { const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => {