Skip to content

Commit

Permalink
Add search (Autocomplete) in environment/group dropdown in Share requ…
Browse files Browse the repository at this point in the history
…est modal (#1335)

### Feature or Bugfix
- Feature

### Detail
In the following view, instead of using a fixed list in the environment,
team and consumption roles dropdowns; this PR introduces search
capabilities as requested in #1012. There are still many other dropdowns
to change, where we will be able to extract a common frontend component.
This one however, is a bit particular so I implemented it separately.


![image](https://github.com/data-dot-all/dataall/assets/71252798/971b8c95-1211-4aa1-a714-0b1548f2ccd9)


### Relates
- #1012 

### Security
Please answer the questions below briefly where applicable, or write
`N/A`. Based on
[OWASP 10](https://owasp.org/Top10/en/).

- Does this PR introduce or modify any input fields or queries - this
includes
fetching data from storage outside the application (e.g. a database, an
S3 bucket)?
  - Is the input sanitized?
- What precautions are you taking before deserializing the data you
consume?
  - Is injection prevented by parametrizing queries?
  - Have you ensured no `eval` or similar functions are used?
- Does this PR introduce any functionality or component that requires
authorization?
- How have you ensured it respects the existing AuthN/AuthZ mechanisms?
  - Are you logging failed auth attempts?
- Are you using or adding any cryptographic features?
  - Do you use a standard proven implementations?
  - Are the used keys controlled by the customer? Where are they stored?
- Are you introducing any new policies/roles/users?
  - Have you used the least-privilege principle? How?


By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
  • Loading branch information
dlpzx authored Jun 20, 2024
1 parent dda4153 commit cfa1e31
Showing 1 changed file with 133 additions and 123 deletions.
256 changes: 133 additions & 123 deletions frontend/src/modules/Catalog/components/RequestAccessModal.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import SendIcon from '@mui/icons-material/Send';
import { LoadingButton } from '@mui/lab';
import Autocomplete from '@mui/lab/Autocomplete';
import {
Box,
Button,
Expand All @@ -8,7 +9,6 @@ import {
Dialog,
FormControlLabel,
FormHelperText,
MenuItem,
Switch,
TextField,
Typography
Expand Down Expand Up @@ -42,6 +42,7 @@ export const RequestAccessModal = (props) => {
const [loadingEnvs, setLoadingEnvs] = useState(false);
const [groupOptions, setGroupOptions] = useState([]);
const [loadingRoles, setLoadingRoles] = useState(false);
const [loadingPolicies, setLoadingPolicies] = useState(false);
const [roleOptions, setRoleOptions] = useState([]);
const [isSharePolicyAttached, setIsSharePolicyAttached] = useState(true);
const [policyName, setPolicyName] = useState('');
Expand Down Expand Up @@ -94,7 +95,7 @@ export const RequestAccessModal = (props) => {
const response = await client.query(
listEnvironmentGroups({
filter: Defaults.selectListFilter,
environmentUri
environmentUri: environmentUri
})
);
if (!response.errors) {
Expand Down Expand Up @@ -143,7 +144,7 @@ export const RequestAccessModal = (props) => {
};

const fetchRolePolicies = async (environmentUri, IAMRoleName) => {
setLoadingRoles(true);
setLoadingPolicies(true);
try {
const response = await client.query(
getConsumptionRolePolicies({
Expand All @@ -167,7 +168,7 @@ export const RequestAccessModal = (props) => {
} catch (e) {
dispatch({ type: SET_ERROR, error: e.message });
} finally {
setLoadingRoles(false);
setLoadingPolicies(false);
}
};

Expand Down Expand Up @@ -201,12 +202,12 @@ export const RequestAccessModal = (props) => {

const formRequestObject = (values) => {
let type = values.consumptionRole ? 'ConsumptionRole' : 'Group';
let principal = values.consumptionRole
? values.consumptionRole
let principal = values.consumptionRole.value
? values.consumptionRole.value
: values.groupUri;

let inputObject = {
environmentUri: values.environment.environmentUri,
environmentUri: values.environmentUri,
groupUri: values.groupUri,
principalId: principal,
principalType: type,
Expand Down Expand Up @@ -300,14 +301,16 @@ export const RequestAccessModal = (props) => {
<Box sx={{ p: 3 }}>
<Formik
initialValues={{
environment: '',
environmentUri: '',
comment: '',
attachMissingPolicies: false
}}
validationSchema={Yup.object().shape({
environment: Yup.object().required('*Environment is required'),
environmentUri: Yup.string().required(
'*Environment is required'
),
groupUri: Yup.string().required('*Team is required'),
consumptionRole: Yup.string(),
consumptionRole: Yup.object(),
comment: Yup.string().max(5000)
})}
onSubmit={async (
Expand Down Expand Up @@ -374,85 +377,93 @@ export const RequestAccessModal = (props) => {
{hit.resourceKind !== 'dashboard' && (
<Box>
<CardContent>
<TextField
fullWidth
error={Boolean(
touched.environment && errors.environment
)}
helperText={
touched.environment && errors.environment
}
label="Environment"
name="environment"
onChange={(event) => {
<Autocomplete
id="environment"
disablePortal
options={environmentOptions.map((option) => option)}
onChange={(event, value) => {
setFieldValue('groupUri', '');
setFieldValue('consumptionRole', '');
fetchGroups(
event.target.value.environmentUri
).catch((e) =>
dispatch({ type: SET_ERROR, error: e.message })
);
setFieldValue('environment', event.target.value);
if (value && value.environmentUri) {
setFieldValue(
'environmentUri',
value.environmentUri
);
fetchGroups(value.environmentUri).catch((e) =>
dispatch({
type: SET_ERROR,
error: e.message
})
);
} else {
setFieldValue('environmentUri', '');
setGroupOptions([]);
setRoleOptions([]);
}
}}
select
value={values.environment}
variant="outlined"
>
{environmentOptions.map((environment) => (
<MenuItem
key={environment.environmentUri}
value={environment}
>
{environment.label}
</MenuItem>
))}
</TextField>
renderInput={(params) => (
<TextField
{...params}
fullWidth
error={Boolean(
touched.environmentUri &&
errors.environmentUri
)}
helperText={
touched.environmentUri &&
errors.environmentUri
}
label="Environment"
onChange={handleChange}
variant="outlined"
/>
)}
/>
</CardContent>
<CardContent>
{loadingGroups ? (
<CircularProgress size={10} />
) : (
<Box>
{groupOptions.length > 0 ? (
<TextField
error={Boolean(
touched.groupUri && errors.groupUri
)}
helperText={
touched.groupUri && errors.groupUri
}
fullWidth
label="Requesters Team"
name="groupUri"
onChange={(event) => {
<Autocomplete
id="group"
disablePortal
options={groupOptions.map((option) => option)}
onChange={(event, value) => {
setFieldValue('consumptionRole', '');
fetchRoles(
values.environment.environmentUri,
event.target.value
).catch((e) =>
dispatch({
type: SET_ERROR,
error: e.message
})
);
setFieldValue(
'groupUri',
event.target.value
);
if (value && value.value) {
setFieldValue('groupUri', value.value);
fetchRoles(
values.environmentUri,
value.value
).catch((e) =>
dispatch({
type: SET_ERROR,
error: e.message
})
);
} else {
setFieldValue('groupUri', '');
setRoleOptions([]);
}
}}
select
value={values.groupUri}
variant="outlined"
>
{groupOptions.map((group) => (
<MenuItem
key={group.value}
value={group.value}
>
{group.label}
</MenuItem>
))}
</TextField>
renderInput={(params) => (
<TextField
{...params}
fullWidth
error={Boolean(
touched.groupUri && errors.groupUri
)}
helperText={
touched.groupUri && errors.groupUri
}
label="Team"
onChange={handleChange}
variant="outlined"
/>
)}
/>
) : (
<TextField
error={Boolean(
Expand All @@ -477,47 +488,46 @@ export const RequestAccessModal = (props) => {
) : (
<Box>
{roleOptions.length > 0 ? (
<TextField
error={Boolean(
touched.consumptionRole &&
errors.consumptionRole
)}
helperText={
touched.consumptionRole &&
errors.consumptionRole
}
fullWidth
label="Consumption Role (optional)"
name="consumptionRole"
onChange={(event) => {
setFieldValue(
'consumptionRole',
event.target.value.value
);
setFieldValue(
'consumptionRoleObj',
event.target.value
);
fetchRolePolicies(
values.environment.environmentUri,
event.target.value.IAMRoleName
).catch((e) =>
dispatch({
type: SET_ERROR,
error: e.message
})
);
<Autocomplete
id="consumptionRole"
disablePortal
options={roleOptions.map((option) => option)}
getOptionLabel={(option) => option.label}
onChange={(event, value) => {
setFieldValue('consumptionRole', value);
if (value && value.IAMRoleName) {
fetchRolePolicies(
values.environmentUri,
value.IAMRoleName
).catch((e) =>
dispatch({
type: SET_ERROR,
error: e.message
})
);
} else {
setFieldValue('consumptionRole', '');
setPolicyName('');
}
}}
select
value={values.consumptionRoleObj}
variant="outlined"
>
{roleOptions.map((role) => (
<MenuItem key={role.value} value={role}>
{role.label}
</MenuItem>
))}
</TextField>
renderInput={(params) => (
<TextField
{...params}
fullWidth
error={Boolean(
touched.consumptionRole &&
errors.consumptionRole
)}
helperText={
touched.consumptionRole &&
errors.consumptionRole
}
label="Consumption Role (optional)"
onChange={handleChange}
variant="outlined"
/>
)}
/>
) : (
<TextField
error={Boolean(
Expand All @@ -541,7 +551,7 @@ export const RequestAccessModal = (props) => {
</Box>
)}
{!values.consumptionRole ||
values.consumptionRoleObj.dataallManaged ||
values.consumptionRole.dataallManaged ||
isSharePolicyAttached ? (
<Box />
) : (
Expand All @@ -564,9 +574,9 @@ export const RequestAccessModal = (props) => {
component="p"
variant="caption"
></Typography>
{values.consumptionRoleObj &&
{values.consumptionRole &&
!(
values.consumptionRoleObj.dataallManaged ||
values.consumptionRole.dataallManaged ||
isSharePolicyAttached ||
values.attachMissingPolicies
) ? (
Expand All @@ -587,7 +597,7 @@ export const RequestAccessModal = (props) => {
</CardContent>
)}
</Box>
{isSubmitting || loading ? (
{isSubmitting || loading || loadingPolicies ? (
<CardContent>
<CircularProgress sx={{ ml: '45%' }} size={50} />
</CardContent>
Expand All @@ -600,9 +610,9 @@ export const RequestAccessModal = (props) => {
disabled={
isSubmitting ||
loading ||
(values.consumptionRoleObj &&
(values.consumptionRole &&
!(
values.consumptionRoleObj.dataallManaged ||
values.consumptionRole.dataallManaged ||
isSharePolicyAttached ||
values.attachMissingPolicies
))
Expand Down

0 comments on commit cfa1e31

Please sign in to comment.