Skip to content

Commit

Permalink
Implemented multi level nested conditions warning
Browse files Browse the repository at this point in the history
Signed-off-by: Yi Cai <yicai@redhat.com>
  • Loading branch information
ciiay committed Jul 1, 2024
1 parent 9806587 commit b13cae2
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,40 @@ describe('ConditionsForm', () => {

expect(getByTestId('save-conditions')).toBeDisabled();
});

it('shows Multiple levels of nested conditions warning', () => {
const { getByTestId } = renderComponent({
conditionsFormVal: {
anyOf: [
{
rule: 'HAS_ANOTTATION',
resourceType: selPluginResourceType,
params: {},
},
{
allOf: [
{
rule: 'HAS_ANOTTATION',
resourceType: selPluginResourceType,
params: {},
},
{
not: {
rule: 'HAS_LABEL',
resourceType: selPluginResourceType,
params: {
label: 'temp',
},
},
},
],
},
],
},
});

expect(
getByTestId('multi-level-nested-conditions-warning'),
).toBeInTheDocument();
});
});
50 changes: 50 additions & 0 deletions plugins/rbac/src/components/ConditionalAccess/ConditionsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import React from 'react';
import { PermissionCondition } from '@backstage/plugin-permission-common';

import { makeStyles } from '@material-ui/core';
import { Alert, AlertTitle } from '@material-ui/lab';
import WarningIcon from '@mui/icons-material/Warning';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import { RJSFValidationError } from '@rjsf/utils';
Expand Down Expand Up @@ -169,6 +171,32 @@ export const ConditionsForm = ({
);
};

const hasMultiLevelNestedConditions = (): boolean => {
if (!Array.isArray(conditions[criteria as keyof ConditionsData])) {
return false;
}

return (conditions[criteria as keyof ConditionsData] as Condition[])
.filter(condition => !('rule' in condition))
.some((firstLevelNestedCondition: Condition) => {
const nestedConditionCriteria = Object.keys(
firstLevelNestedCondition,
)[0];
return (
Array.isArray(
firstLevelNestedCondition[
nestedConditionCriteria as keyof Condition
],
) &&
(
firstLevelNestedCondition[
nestedConditionCriteria as keyof Condition
] as Condition[]
).some((con: Condition) => !('rule' in con))
);
});
};

return (
<>
<Box className={classes.form}>
Expand All @@ -182,6 +210,28 @@ export const ConditionsForm = ({
handleSetErrors={handleSetErrors}
setRemoveAllClicked={setRemoveAllClicked}
/>
{hasMultiLevelNestedConditions() && (
<Alert
icon={<WarningIcon />}
style={{ margin: '1.5rem 0 1rem 0' }}
severity="warning"
data-testid="multi-level-nested-conditions-warning"
>
<AlertTitle data-testid="multi-level-nested-conditions-warning-title">
Multiple levels of nested conditions are not supported
</AlertTitle>
Only one level is displayed. Please use the{' '}
<a
href="https://github.com/redhat-developer/red-hat-developers-documentation-rhdh/blob/main/modules/admin/proc-rbac-send-request-rbac-rest-api.adoc"
// TODO: Update link with the official documentation when RHIDP-3078 is resolved
target="blank"
style={{ textDecoration: 'underline' }}
>
CLI
</a>{' '}
to view all nested conditions.
</Alert>
)}
</Box>
<Box className={classes.footer}>
<Button
Expand Down
183 changes: 82 additions & 101 deletions plugins/rbac/src/components/ConditionalAccess/ConditionsFormRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ export const ConditionsFormRow = ({
</Box>
);
};
let showMultilevelNestedConditionWarning = false;

return (
<Box className={classes.conditionRow} data-testid="conditions-row">
<ToggleButtonGroup
Expand Down Expand Up @@ -748,101 +748,89 @@ export const ConditionsFormRow = ({
</div>
<Box>
{Object.keys(nc)[0] === criterias.allOf &&
nc.allOf?.map((c, index) => {
if ((c as PermissionCondition).rule === undefined) {
showMultilevelNestedConditionWarning = true;
}
return (
(c as PermissionCondition).rule !== undefined && (
<div
style={{
display:
(c as PermissionCondition).rule !== undefined
? 'flex'
: 'none',
}}
key={`condition-${index}`}
>
<ConditionsFormRowFields
oldCondition={c}
index={index}
onRuleChange={onRuleChange}
conditionRow={conditionRow}
criteria={criteria}
conditionRulesData={conditionRulesData}
handleSetErrors={handleSetErrors}
setRemoveAllClicked={setRemoveAllClicked}
nestedConditionRow={nestedConditionRow}
nestedConditionCriteria={Object.keys(nc)[0]}
nestedConditionIndex={nestedConditionIndex}
ruleIndex={index}
updateRules={updateRules}
/>
<IconButton
title="Remove"
className={classes.removeRuleButton}
disabled={index === 0}
onClick={() =>
handleRemoveNestedConditionRule(
criterias.allOf,
nestedConditionIndex,
index,
)
}
>
<RemoveIcon />
</IconButton>
</div>
)
);
})}
nc.allOf?.map((c, index) => (
<div
style={{
display:
(c as PermissionCondition).rule !== undefined
? 'flex'
: 'none',
}}
key={`condition-${index}`}
>
<ConditionsFormRowFields
oldCondition={c}
index={index}
onRuleChange={onRuleChange}
conditionRow={conditionRow}
criteria={criteria}
conditionRulesData={conditionRulesData}
handleSetErrors={handleSetErrors}
setRemoveAllClicked={setRemoveAllClicked}
nestedConditionRow={nestedConditionRow}
nestedConditionCriteria={Object.keys(nc)[0]}
nestedConditionIndex={nestedConditionIndex}
ruleIndex={index}
updateRules={updateRules}
/>
<IconButton
title="Remove"
className={classes.removeRuleButton}
disabled={index === 0}
onClick={() =>
handleRemoveNestedConditionRule(
criterias.allOf,
nestedConditionIndex,
index,
)
}
>
<RemoveIcon />
</IconButton>
</div>
))}
{Object.keys(nc)[0] === criterias.anyOf &&
nc.anyOf?.map((c, index) => {
if ((c as PermissionCondition).rule === undefined) {
showMultilevelNestedConditionWarning = true;
}
return (
<div
style={{
display:
(c as PermissionCondition).rule !== undefined
? 'flex'
: 'none',
}}
key={`condition-${index}`}
nc.anyOf?.map((c, index) => (
<div
style={{
display:
(c as PermissionCondition).rule !== undefined
? 'flex'
: 'none',
}}
key={`condition-${index}`}
>
<ConditionsFormRowFields
oldCondition={c}
index={index}
onRuleChange={onRuleChange}
conditionRow={conditionRow}
criteria={criteria}
conditionRulesData={conditionRulesData}
handleSetErrors={handleSetErrors}
setRemoveAllClicked={setRemoveAllClicked}
nestedConditionRow={nestedConditionRow}
nestedConditionCriteria={Object.keys(nc)[0]}
nestedConditionIndex={nestedConditionIndex}
ruleIndex={index}
updateRules={updateRules}
/>
<IconButton
title="Remove"
className={classes.removeRuleButton}
disabled={index === 0}
onClick={() =>
handleRemoveNestedConditionRule(
criterias.anyOf,
nestedConditionIndex,
index,
)
}
>
<ConditionsFormRowFields
oldCondition={c}
index={index}
onRuleChange={onRuleChange}
conditionRow={conditionRow}
criteria={criteria}
conditionRulesData={conditionRulesData}
handleSetErrors={handleSetErrors}
setRemoveAllClicked={setRemoveAllClicked}
nestedConditionRow={nestedConditionRow}
nestedConditionCriteria={Object.keys(nc)[0]}
nestedConditionIndex={nestedConditionIndex}
ruleIndex={index}
updateRules={updateRules}
/>
<IconButton
title="Remove"
className={classes.removeRuleButton}
disabled={index === 0}
onClick={() =>
handleRemoveNestedConditionRule(
criterias.anyOf,
nestedConditionIndex,
index,
)
}
>
<RemoveIcon />
</IconButton>
</div>
);
})}
<RemoveIcon />
</IconButton>
</div>
))}
{Object.keys(nc)[0] === criterias.not && (
<ConditionsFormRowFields
oldCondition={
Expand Down Expand Up @@ -870,13 +858,6 @@ export const ConditionsFormRow = ({
updateRules={updateRules}
/>
)}
{showMultilevelNestedConditionWarning && (
<div style={{ width: '90%', marginTop: '2rem' }}>
This condition contains multiple nested levels that the UI
does not yet support. Please use the CLI to view
additional levels of nested conditions.
</div>
)}
{Object.keys(nc)[0] !== criterias.not && (
<Button
className={classes.addRuleButton}
Expand Down

0 comments on commit b13cae2

Please sign in to comment.