fixed issue failed to revoke grant access of the user
This commit is contained in:
parent
e5b910238f
commit
7476c939c3
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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 => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue