Skip to content

Commit 10f77c6

Browse files
committed
Added input validation for policy attributes
1 parent 116be88 commit 10f77c6

File tree

2 files changed

+28
-7
lines changed

2 files changed

+28
-7
lines changed

client/src/locale/en.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,7 @@ const en = {
11131113
denyPlaceholder: "Type your descriptive message",
11141114
submitNew: "Save and activate",
11151115
submitExisting: "Update",
1116+
attributeValueErrors: "The following value(s) are invalid for {{name}}: {{values}}",
11161117
breakdown: {
11171118
if: "If",
11181119
when: "When",

client/src/policies/PolicyForm.jsx

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,20 @@ import InputField from "../components/InputField.jsx";
66
import {useAppStore} from "../stores/AppStore.js";
77
import {useShallow} from "zustand/react/shallow";
88
import {deletePolicy, newPolicy, uniquePolicyName, updatePolicy} from "../api/index.js";
9-
import {isEmpty} from "../utils/Utils.js";
9+
import {isEmpty, splitListSemantically} from "../utils/Utils.js";
1010
import ErrorIndicator from "../components/ErrorIndicator.jsx";
1111
import SelectField from "../components/SelectField.jsx";
1212
import {defaultAttributes, flatMapByValues, policyDesscription} from "../utils/Policy.js";
1313
import TrashIcon from "@surfnet/sds/icons/functional-icons/bin.svg";
1414
import ConfirmationDialog from "../components/ConfirmationDialog.jsx";
1515

16+
1617
export const PolicyForm = ({policy, setPolicy, isExistingPolicy, originalName, refreshPolicies}) => {
1718

1819
const [initial, setInitial] = useState(true);
1920
const [duplicatePolicyName, setDuplicatePolicyName] = useState(false);
2021
const [confirmation, setConfirmation] = useState({});
22+
const [attributeValueErrors, setAttributeValueErrors] = useState({});
2123

2224
const required = ["name", "denyAdvice", "denyAdviceNl"];
2325

@@ -31,7 +33,8 @@ export const PolicyForm = ({policy, setPolicy, isExistingPolicy, originalName, r
3133
}
3234

3335
const isValid = () => {
34-
return required.every(attr => !isEmpty(policy.data[attr])) && !duplicatePolicyName;
36+
const allAttributesValuesValid = Object.values(attributeValueErrors).every(values => isEmpty(values));
37+
return required.every(attr => !isEmpty(policy.data[attr])) && !duplicatePolicyName && allAttributesValuesValid;
3538
}
3639

3740
const doDeletePolicy = (confirmationRequired, policy) => {
@@ -93,14 +96,23 @@ export const PolicyForm = ({policy, setPolicy, isExistingPolicy, originalName, r
9396

9497
const attributeDeleted = index => {
9598
const newAttributes = policy.data.attributes.filter((item, i) => i !== index);
96-
internalUpdatePolicy({attributes: defaultAttributes(newAttributes)})
99+
internalUpdatePolicy({attributes: defaultAttributes(newAttributes)});
100+
const deletedAttribute = policy.data.attributes[index];
101+
delete attributeValueErrors[deletedAttribute.name];
102+
setAttributeValueErrors({...attributeValueErrors});
97103
}
98104

99-
const attributeValueChanged = (value, index) => {
105+
const attributeValueChanged = (values, index) => {
100106
const newAttributes = [...policy.data.attributes];
101107
const attribute = newAttributes[index];
102-
attribute.value = value;
103-
internalUpdatePolicy({attributes: newAttributes})
108+
attribute.value = values;
109+
internalUpdatePolicy({attributes: newAttributes});
110+
const validationRegex = allowedAttributes.find(attr => attr.value === attribute.name).validationRegex;
111+
const regex = new RegExp(validationRegex);
112+
const invalidValues = values
113+
.map(value => value.value)
114+
.filter(value => !regex.test(value));
115+
setAttributeValueErrors({...attributeValueErrors, [attribute.name]: invalidValues});
104116
}
105117

106118
const denyRuleToggle = val => {
@@ -163,7 +175,6 @@ export const PolicyForm = ({policy, setPolicy, isExistingPolicy, originalName, r
163175
{duplicatePolicyName &&
164176
<ErrorIndicator adjustMargin={true}
165177
msg={I18n.t("appAccess.duplicateName", {name: policy.data.name})}/>}
166-
167178
<div className="row">
168179
<div className="row-item">
169180
<span className="label standalone">{I18n.t("appAccess.allowDeny")}
@@ -210,6 +221,15 @@ export const PolicyForm = ({policy, setPolicy, isExistingPolicy, originalName, r
210221
placeholder={I18n.t("appAccess.permittedValuesPlaceholder")}
211222
onChange={values => attributeValueChanged(values, index)}
212223
/>
224+
{!isEmpty(attributeValueErrors[attribute.name]) &&
225+
<ErrorIndicator adjustMargin={false}
226+
msg={I18n.t("appAccess.attributeValueErrors",
227+
{
228+
name: allowedAttributes.find(attr => attr.value === attribute.name).label,
229+
values: splitListSemantically(
230+
attributeValueErrors[attribute.name].map(val => `'${val}'`),
231+
I18n.t("forms.and"))
232+
})}/>}
213233
</div>)}
214234
{policy.data.attributes.every(attribute => attribute.name && !isEmpty(attribute.value)) &&
215235
<div className="add-attribute-container">

0 commit comments

Comments
 (0)