mirror of
https://github.com/sasjs/server.git
synced 2025-12-12 11:54:35 +00:00
feat: add multiple permission for same combination of type and principal at once
This commit is contained in:
@@ -32,7 +32,7 @@ const BootstrapDialog = styled(Dialog)(({ theme }) => ({
|
|||||||
type AddPermissionModalProps = {
|
type AddPermissionModalProps = {
|
||||||
open: boolean
|
open: boolean
|
||||||
handleOpen: Dispatch<SetStateAction<boolean>>
|
handleOpen: Dispatch<SetStateAction<boolean>>
|
||||||
addPermission: (addPermissionPayload: RegisterPermissionPayload) => void
|
addPermission: (permissions: RegisterPermissionPayload[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const AddPermissionModal = ({
|
const AddPermissionModal = ({
|
||||||
@@ -42,9 +42,9 @@ const AddPermissionModal = ({
|
|||||||
}: AddPermissionModalProps) => {
|
}: AddPermissionModalProps) => {
|
||||||
const [paths, setPaths] = useState<string[]>([])
|
const [paths, setPaths] = useState<string[]>([])
|
||||||
const [loadingPaths, setLoadingPaths] = useState(false)
|
const [loadingPaths, setLoadingPaths] = useState(false)
|
||||||
const [path, setPath] = useState<string>()
|
const [selectedPaths, setSelectedPaths] = useState<string[]>([])
|
||||||
const [permissionType, setPermissionType] = useState('Route')
|
const [permissionType, setPermissionType] = useState('Route')
|
||||||
const [principalType, setPrincipalType] = useState('group')
|
const [principalType, setPrincipalType] = useState('Group')
|
||||||
const [userPrincipal, setUserPrincipal] = useState<UserResponse>()
|
const [userPrincipal, setUserPrincipal] = useState<UserResponse>()
|
||||||
const [groupPrincipal, setGroupPrincipal] = useState<GroupResponse>()
|
const [groupPrincipal, setGroupPrincipal] = useState<GroupResponse>()
|
||||||
const [permissionSetting, setPermissionSetting] = useState('Grant')
|
const [permissionSetting, setPermissionSetting] = useState('Grant')
|
||||||
@@ -72,10 +72,10 @@ const AddPermissionModal = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoadingPrincipals(true)
|
setLoadingPrincipals(true)
|
||||||
axios
|
axios
|
||||||
.get(`/SASjsApi/${principalType}`)
|
.get(`/SASjsApi/${principalType.toLowerCase()}`)
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
if (principalType === 'user') {
|
if (principalType.toLowerCase() === 'user') {
|
||||||
const users: UserResponse[] = res.data
|
const users: UserResponse[] = res.data
|
||||||
const nonAdminUsers = users.filter((user) => !user.isAdmin)
|
const nonAdminUsers = users.filter((user) => !user.isAdmin)
|
||||||
setUserPrincipals(nonAdminUsers)
|
setUserPrincipals(nonAdminUsers)
|
||||||
@@ -93,22 +93,29 @@ const AddPermissionModal = ({
|
|||||||
}, [principalType])
|
}, [principalType])
|
||||||
|
|
||||||
const handleAddPermission = () => {
|
const handleAddPermission = () => {
|
||||||
const addPermissionPayload: any = {
|
const permissions: RegisterPermissionPayload[] = []
|
||||||
path,
|
|
||||||
type: permissionType,
|
selectedPaths.forEach((path) => {
|
||||||
setting: permissionSetting,
|
const addPermissionPayload: any = {
|
||||||
principalType
|
path,
|
||||||
}
|
type: permissionType,
|
||||||
if (principalType === 'user' && userPrincipal) {
|
setting: permissionSetting,
|
||||||
addPermissionPayload.principalId = userPrincipal.id
|
principalType: principalType.toLowerCase(),
|
||||||
} else if (principalType === 'group' && groupPrincipal) {
|
principalId:
|
||||||
addPermissionPayload.principalId = groupPrincipal.groupId
|
principalType.toLowerCase() === 'user'
|
||||||
}
|
? userPrincipal?.id
|
||||||
addPermission(addPermissionPayload)
|
: groupPrincipal?.groupId
|
||||||
|
}
|
||||||
|
|
||||||
|
permissions.push(addPermissionPayload)
|
||||||
|
})
|
||||||
|
|
||||||
|
addPermission(permissions)
|
||||||
}
|
}
|
||||||
|
|
||||||
const addButtonDisabled =
|
const addButtonDisabled =
|
||||||
!path || (principalType === 'user' ? !userPrincipal : !groupPrincipal)
|
!selectedPaths.length ||
|
||||||
|
(principalType.toLowerCase() === 'user' ? !userPrincipal : !groupPrincipal)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BootstrapDialog onClose={() => handleOpen(false)} open={open}>
|
<BootstrapDialog onClose={() => handleOpen(false)} open={open}>
|
||||||
@@ -122,17 +129,14 @@ const AddPermissionModal = ({
|
|||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
|
multiple
|
||||||
options={paths}
|
options={paths}
|
||||||
disableClearable
|
filterSelectedOptions
|
||||||
value={path}
|
value={selectedPaths}
|
||||||
onChange={(event: any, newValue: string) => setPath(newValue)}
|
onChange={(event: any, newValue: string[]) => {
|
||||||
renderInput={(params) =>
|
setSelectedPaths(newValue)
|
||||||
loadingPaths ? (
|
}}
|
||||||
<CircularProgress />
|
renderInput={(params) => <TextField {...params} label="Paths" />}
|
||||||
) : (
|
|
||||||
<TextField {...params} autoFocus label="Path" />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
@@ -154,8 +158,7 @@ const AddPermissionModal = ({
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
options={['group', 'user']}
|
options={['Group', 'User']}
|
||||||
getOptionLabel={(option) => option.toUpperCase()}
|
|
||||||
disableClearable
|
disableClearable
|
||||||
value={principalType}
|
value={principalType}
|
||||||
onChange={(event: any, newValue: string) =>
|
onChange={(event: any, newValue: string) =>
|
||||||
@@ -167,7 +170,7 @@ const AddPermissionModal = ({
|
|||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
{principalType === 'user' ? (
|
{principalType.toLowerCase() === 'user' ? (
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
options={userPrincipals}
|
options={userPrincipals}
|
||||||
getOptionLabel={(option) => option.displayName}
|
getOptionLabel={(option) => option.displayName}
|
||||||
|
|||||||
99
web/src/containers/Settings/addPermissionResponseModal.tsx
Normal file
99
web/src/containers/Settings/addPermissionResponseModal.tsx
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import {
|
||||||
|
Paper,
|
||||||
|
Typography,
|
||||||
|
DialogContent,
|
||||||
|
TableContainer,
|
||||||
|
Table,
|
||||||
|
TableHead,
|
||||||
|
TableBody,
|
||||||
|
TableRow,
|
||||||
|
TableCell
|
||||||
|
} from '@mui/material'
|
||||||
|
|
||||||
|
import { BootstrapDialog } from '../../components/modal'
|
||||||
|
import { BootstrapDialogTitle } from '../../components/dialogTitle'
|
||||||
|
import { PermissionResponse } from '../../utils/types'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
open: boolean
|
||||||
|
setOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||||
|
permissionResponses: PermissionResponse[]
|
||||||
|
errorResponses: any[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const PermissionResponseModal = ({
|
||||||
|
open,
|
||||||
|
setOpen,
|
||||||
|
permissionResponses,
|
||||||
|
errorResponses
|
||||||
|
}: Props) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<BootstrapDialog onClose={() => setOpen(false)} open={open}>
|
||||||
|
<BootstrapDialogTitle
|
||||||
|
id="permission-response-modal"
|
||||||
|
handleOpen={setOpen}
|
||||||
|
>
|
||||||
|
Permission Response
|
||||||
|
</BootstrapDialogTitle>
|
||||||
|
<DialogContent dividers>
|
||||||
|
{permissionResponses.length > 0 && (
|
||||||
|
<>
|
||||||
|
<Typography gutterBottom>Added Permissions</Typography>
|
||||||
|
{permissionResponses.length > 0 && (
|
||||||
|
<TableContainer component={Paper}>
|
||||||
|
<Table size="small">
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Path</TableCell>
|
||||||
|
<TableCell>Type</TableCell>
|
||||||
|
<TableCell>Setting</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{permissionResponses.map((permission, index) => {
|
||||||
|
return (
|
||||||
|
<TableRow key={index}>
|
||||||
|
<TableCell>{permission.path}</TableCell>
|
||||||
|
<TableCell>{permission.type}</TableCell>
|
||||||
|
<TableCell>{permission.setting}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{errorResponses.length > 0 && (
|
||||||
|
<>
|
||||||
|
<Typography style={{ color: 'red', marginTop: '10px' }}>
|
||||||
|
Errors
|
||||||
|
</Typography>
|
||||||
|
<ul>
|
||||||
|
{errorResponses.map((err, index) => (
|
||||||
|
<li key={index}>
|
||||||
|
<Typography>
|
||||||
|
Error occurred for Path: {err.permission.path}
|
||||||
|
</Typography>
|
||||||
|
<Typography>
|
||||||
|
{typeof err.error.response.data === 'object'
|
||||||
|
? JSON.stringify(err.error.response.data)
|
||||||
|
: err.error.response.data}
|
||||||
|
</Typography>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</DialogContent>
|
||||||
|
</BootstrapDialog>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PermissionResponseModal
|
||||||
@@ -27,6 +27,7 @@ import { styled } from '@mui/material/styles'
|
|||||||
import Modal from '../../components/modal'
|
import Modal from '../../components/modal'
|
||||||
import PermissionFilterModal from './permissionFilterModal'
|
import PermissionFilterModal from './permissionFilterModal'
|
||||||
import AddPermissionModal from './addPermissionModal'
|
import AddPermissionModal from './addPermissionModal'
|
||||||
|
import PermissionResponseModal from './addPermissionResponseModal'
|
||||||
import UpdatePermissionModal from './updatePermissionModal'
|
import UpdatePermissionModal from './updatePermissionModal'
|
||||||
import DeleteConfirmationModal from '../../components/deleteConfirmationModal'
|
import DeleteConfirmationModal from '../../components/deleteConfirmationModal'
|
||||||
import BootstrapSnackbar, { AlertSeverityType } from '../../components/snackbar'
|
import BootstrapSnackbar, { AlertSeverityType } from '../../components/snackbar'
|
||||||
@@ -59,6 +60,13 @@ const Permission = () => {
|
|||||||
AlertSeverityType.Success
|
AlertSeverityType.Success
|
||||||
)
|
)
|
||||||
const [addPermissionModalOpen, setAddPermissionModalOpen] = useState(false)
|
const [addPermissionModalOpen, setAddPermissionModalOpen] = useState(false)
|
||||||
|
const [openPermissionResponseModal, setOpenPermissionResponseModal] =
|
||||||
|
useState(false)
|
||||||
|
const [addedPermissions, setAddedPermission] = useState<PermissionResponse[]>(
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
const [errorResponses, setErrorResponses] = useState<any[]>([])
|
||||||
|
|
||||||
const [updatePermissionModalOpen, setUpdatePermissionModalOpen] =
|
const [updatePermissionModalOpen, setUpdatePermissionModalOpen] =
|
||||||
useState(false)
|
useState(false)
|
||||||
const [deleteConfirmationModalOpen, setDeleteConfirmationModalOpen] =
|
const [deleteConfirmationModalOpen, setDeleteConfirmationModalOpen] =
|
||||||
@@ -181,29 +189,31 @@ const Permission = () => {
|
|||||||
setFilterApplied(false)
|
setFilterApplied(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const addPermission = (addPermissionPayload: RegisterPermissionPayload) => {
|
const addPermission = (permissions: RegisterPermissionPayload[]) => {
|
||||||
setAddPermissionModalOpen(false)
|
setAddPermissionModalOpen(false)
|
||||||
|
setAddedPermission([])
|
||||||
|
setErrorResponses([])
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
axios
|
|
||||||
.post('/SASjsApi/permission', addPermissionPayload)
|
const permissionResponses: PermissionResponse[] = []
|
||||||
.then((res: any) => {
|
const errors: any = []
|
||||||
fetchPermissions()
|
|
||||||
setSnackbarMessage('Permission added!')
|
permissions.forEach(async (permission) => {
|
||||||
setSnackbarSeverity(AlertSeverityType.Success)
|
await axios
|
||||||
setOpenSnackbar(true)
|
.post('/SASjsApi/permission', permission)
|
||||||
})
|
.then((res) => {
|
||||||
.catch((err) => {
|
permissionResponses.push(res.data)
|
||||||
setModalTitle('Abort')
|
})
|
||||||
setModalPayload(
|
.catch((error) => {
|
||||||
typeof err.response.data === 'object'
|
errors.push({ error, permission })
|
||||||
? JSON.stringify(err.response.data)
|
})
|
||||||
: err.response.data
|
})
|
||||||
)
|
|
||||||
setOpenModal(true)
|
fetchPermissions()
|
||||||
})
|
setIsLoading(false)
|
||||||
.finally(() => {
|
setOpenPermissionResponseModal(true)
|
||||||
setIsLoading(false)
|
setAddedPermission(permissionResponses)
|
||||||
})
|
setErrorResponses(errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleUpdatePermissionClick = (permission: PermissionResponse) => {
|
const handleUpdatePermissionClick = (permission: PermissionResponse) => {
|
||||||
@@ -340,6 +350,12 @@ const Permission = () => {
|
|||||||
handleOpen={setAddPermissionModalOpen}
|
handleOpen={setAddPermissionModalOpen}
|
||||||
addPermission={addPermission}
|
addPermission={addPermission}
|
||||||
/>
|
/>
|
||||||
|
<PermissionResponseModal
|
||||||
|
open={openPermissionResponseModal}
|
||||||
|
setOpen={setOpenPermissionResponseModal}
|
||||||
|
permissionResponses={addedPermissions}
|
||||||
|
errorResponses={errorResponses}
|
||||||
|
/>
|
||||||
<UpdatePermissionModal
|
<UpdatePermissionModal
|
||||||
open={updatePermissionModalOpen}
|
open={updatePermissionModalOpen}
|
||||||
handleOpen={setUpdatePermissionModalOpen}
|
handleOpen={setUpdatePermissionModalOpen}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ const PermissionFilterModal = ({
|
|||||||
onChange={(event: any, newValue: string[]) => {
|
onChange={(event: any, newValue: string[]) => {
|
||||||
setPathFilter(newValue)
|
setPathFilter(newValue)
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => <TextField {...params} label="URIs" />}
|
renderInput={(params) => <TextField {...params} label="Paths" />}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
|
|||||||
Reference in New Issue
Block a user