Skip to content

Commit c041857

Browse files
fix(admin-ui): enable Apply button on field changes in FIDO static co… (#2586)
* fix(admin-ui): enable Apply button on field changes in FIDO static configuration form (#2585) * feat(admin-ui): Resolve comments in the pr * feat(admin-ui): Resolve issues in the User form about the apply button * feat(admin-ui): Resolve comments on the pr * feat(admin-ui): Resolve comments on the pr * SAML Web SSO and Identity Brokering fixed * typo fixed * Code rabbit suggestions * Code rabbit suggestions --------- Co-authored-by: Mohammad Abudayyeh <47318409+moabu@users.noreply.github.com>
1 parent 7ccde1b commit c041857

File tree

13 files changed

+300
-139
lines changed

13 files changed

+300
-139
lines changed

admin-ui/app/i18n.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ const i18nConfig: InitOptions = {
3030
react: {
3131
useSuspense: false,
3232
},
33+
interpolation: {
34+
escapeValue: false, // React already escapes HTML for XSS protection, so we don't need i18next to escape
35+
// This prevents double-escaping which causes &#39; to appear instead of '
36+
},
3337
}
3438

3539
i18n.use(initReactI18next).init(i18nConfig)

admin-ui/app/locales/en/translation.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -915,7 +915,9 @@
915915
"attribute_update_failed": "Error updating attribute",
916916
"attribute_delete_failed": "Error deleting attribute",
917917
"attribute_load_failed": "Error loading attribute",
918-
"attribute_not_found": "Attribute not found"
918+
"cannot_contain_spaces": "{{field}} cannot contain spaces",
919+
"cannot_be_empty": "{{field}} cannot be empty",
920+
"must_be_valid_url": "{{field}} must be a valid URL"
919921
},
920922
"tooltips": {
921923
"add_attribute": "Add attribute",

admin-ui/app/locales/es/translation.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,9 @@
911911
"attribute_update_failed": "Error al actualizar el atributo",
912912
"attribute_delete_failed": "Error al eliminar el atributo",
913913
"attribute_load_failed": "Error al cargar el atributo",
914-
"attribute_not_found": "Atributo no encontrado"
914+
"cannot_contain_spaces": "{{field}} no puede contener espacios",
915+
"cannot_be_empty": "{{field}} no puede estar vacío",
916+
"must_be_valid_url": "{{field}} debe ser una URL válida"
915917
},
916918
"tooltips": {
917919
"add_attribute": "Añadir atributo",

admin-ui/app/locales/fr/translation.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,7 +851,9 @@
851851
"attribute_update_failed": "Erreur lors de la mise à jour de l'attribut",
852852
"attribute_delete_failed": "Erreur lors de la suppression de l'attribut",
853853
"attribute_load_failed": "Erreur lors du chargement de l'attribut",
854-
"attribute_not_found": "Attribut introuvable"
854+
"cannot_contain_spaces": "{{field}} ne peut pas contenir d'espaces",
855+
"cannot_be_empty": "{{field}} ne peut pas être vide",
856+
"must_be_valid_url": "{{field}} doit être une URL valide"
855857
},
856858
"tooltips": {
857859
"add_attribute": "Ajouter un attribut",

admin-ui/app/locales/pt/translation.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,9 @@
846846
"attribute_update_failed": "Erro ao atualizar atributo",
847847
"attribute_delete_failed": "Erro ao excluir atributo",
848848
"attribute_load_failed": "Erro ao carregar atributo",
849-
"attribute_not_found": "Atributo não encontrado"
849+
"cannot_contain_spaces": "{{field}} não pode conter espaços",
850+
"cannot_be_empty": "{{field}} não pode estar vazio",
851+
"must_be_valid_url": "{{field}} deve ser uma URL válida"
850852
},
851853
"tooltips": {
852854
"add_attribute": "Adicionar atributo",

admin-ui/app/routes/Apps/Gluu/GluuInputRow.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ function GluuInputRow({
5757
formik.handleChange(event)
5858
}
5959
}}
60+
onBlur={formik.handleBlur}
6061
onFocus={onFocus}
6162
onKeyDown={(evt) => evt.key === 'e' && type === 'number' && evt.preventDefault()}
6263
disabled={disabled}

admin-ui/plugins/fido/components/DynamicConfiguration.tsx

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useCallback, useMemo } from 'react'
1+
import React, { useState, useCallback, useEffect, useMemo } from 'react'
22
import { useFormik } from 'formik'
33
import { Row, Col, Form, FormGroup } from 'Components'
44
import GluuInputRow from 'Routes/Apps/Gluu/GluuInputRow'
@@ -23,17 +23,30 @@ const DynamicConfiguration: React.FC<DynamicConfigurationProps> = ({
2323
setModal((prev) => !prev)
2424
}, [])
2525

26+
const initialValues = useMemo<DynamicConfigFormValues>(
27+
() =>
28+
transformToFormValues(fidoConfiguration, fidoConstants.DYNAMIC) as DynamicConfigFormValues,
29+
[fidoConfiguration],
30+
)
31+
2632
const formik = useFormik<DynamicConfigFormValues>({
27-
initialValues: transformToFormValues(
28-
fidoConfiguration,
29-
fidoConstants.DYNAMIC,
30-
) as DynamicConfigFormValues,
33+
initialValues,
3134
onSubmit: readOnly ? () => undefined : toggle,
3235
validationSchema: validationSchema[fidoConstants.VALIDATION_SCHEMAS.DYNAMIC_CONFIG],
33-
enableReinitialize: true,
3436
validateOnMount: true,
3537
})
3638

39+
useEffect(() => {
40+
if (fidoConfiguration) {
41+
formik.resetForm({
42+
values: transformToFormValues(
43+
fidoConfiguration,
44+
fidoConstants.DYNAMIC,
45+
) as DynamicConfigFormValues,
46+
})
47+
}
48+
}, [fidoConfiguration])
49+
3750
const submitForm = useCallback(
3851
(userMessage: string) => {
3952
if (readOnly) {
@@ -46,12 +59,8 @@ const DynamicConfiguration: React.FC<DynamicConfigurationProps> = ({
4659
)
4760

4861
const handleCancel = useCallback(() => {
49-
const initialValues = transformToFormValues(
50-
fidoConfiguration,
51-
fidoConstants.DYNAMIC,
52-
) as DynamicConfigFormValues
53-
formik.resetForm({ values: initialValues })
54-
}, [formik, fidoConfiguration])
62+
formik.resetForm()
63+
}, [formik])
5564

5665
const handleFormSubmit = useCallback(
5766
(e: React.FormEvent<HTMLFormElement>) => {

admin-ui/plugins/fido/components/StaticConfiguration.tsx

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useCallback, useMemo } from 'react'
1+
import React, { useState, useCallback, useEffect, useMemo } from 'react'
22
import { useFormik } from 'formik'
33
import { useTranslation } from 'react-i18next'
44

@@ -34,8 +34,6 @@ const StaticConfiguration: React.FC<StaticConfigurationProps> = ({
3434
isSubmitting,
3535
readOnly,
3636
}) => {
37-
const staticConfiguration = fidoConfiguration?.fido2Configuration
38-
3937
const { t } = useTranslation()
4038

4139
const [modal, setModal] = useState(false)
@@ -44,17 +42,29 @@ const StaticConfiguration: React.FC<StaticConfigurationProps> = ({
4442
setModal((prev) => !prev)
4543
}, [])
4644

45+
const initialValues: StaticConfigFormValues = transformToFormValues(
46+
fidoConfiguration?.fido2Configuration,
47+
fidoConstants.STATIC,
48+
) as StaticConfigFormValues
49+
4750
const formik = useFormik<StaticConfigFormValues>({
48-
initialValues: transformToFormValues(
49-
staticConfiguration,
50-
fidoConstants.STATIC,
51-
) as StaticConfigFormValues,
51+
initialValues,
5252
onSubmit: readOnly ? () => undefined : toggle,
5353
validationSchema: validationSchema[fidoConstants.VALIDATION_SCHEMAS.STATIC_CONFIG],
54-
enableReinitialize: true,
5554
validateOnMount: true,
5655
})
5756

57+
useEffect(() => {
58+
if (fidoConfiguration?.fido2Configuration) {
59+
formik.resetForm({
60+
values: transformToFormValues(
61+
fidoConfiguration.fido2Configuration,
62+
fidoConstants.STATIC,
63+
) as StaticConfigFormValues,
64+
})
65+
}
66+
}, [fidoConfiguration])
67+
5868
const submitForm = useCallback(
5969
(userMessage: string) => {
6070
if (readOnly) {
@@ -67,12 +77,8 @@ const StaticConfiguration: React.FC<StaticConfigurationProps> = ({
6777
)
6878

6979
const handleCancel = useCallback(() => {
70-
const initialValues = transformToFormValues(
71-
staticConfiguration,
72-
fidoConstants.STATIC,
73-
) as StaticConfigFormValues
74-
formik.resetForm({ values: initialValues })
75-
}, [formik, staticConfiguration])
80+
formik.resetForm()
81+
}, [formik])
7682

7783
const requestedPartiesOptions = useMemo(() => {
7884
return (formik.values.requestedParties || []).map((item) => ({
@@ -364,7 +370,7 @@ const StaticConfiguration: React.FC<StaticConfigurationProps> = ({
364370
showError={!!(formik.errors.attestationMode && formik.touched.attestationMode)}
365371
errorMessage={formik.errors.attestationMode}
366372
handleChange={(_e) => {
367-
formik.setFieldTouched(fidoConstants.FORM_FIELDS.ATTESTATION_MODE, true)
373+
formik.setFieldTouched(fidoConstants.FORM_FIELDS.ATTESTATION_MODE, true, false)
368374
}}
369375
/>
370376
</Col>

admin-ui/plugins/saml/components/WebsiteSsoIdentityProviderForm.tsx

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ const WebsiteSsoIdentityProviderForm = ({
216216
}, [formik, initialValues])
217217

218218
const handleFormSubmit = useCallback(
219-
(e: React.FormEvent<HTMLFormElement>) => {
219+
async (e: React.FormEvent<HTMLFormElement>) => {
220220
e.preventDefault()
221221
if (
222222
!formik.values.metaDataFile &&
@@ -226,6 +226,17 @@ const WebsiteSsoIdentityProviderForm = ({
226226
formik.setFieldTouched('metaDataFile', true)
227227
return
228228
}
229+
230+
const errors = await formik.validateForm()
231+
if (Object.keys(errors).length > 0) {
232+
const touched: Record<string, boolean> = {}
233+
Object.keys(errors).forEach((key) => {
234+
touched[key] = true
235+
})
236+
formik.setTouched(touched, false)
237+
return
238+
}
239+
229240
formik.handleSubmit(e)
230241
},
231242
[formik, configs?.idpMetaDataFN],
@@ -260,9 +271,14 @@ const WebsiteSsoIdentityProviderForm = ({
260271
const error = formik.errors[fieldName]
261272
const touched = formik.touched[fieldName]
262273
const value = formik.values[fieldName]
263-
return Boolean(error && (touched || (value !== undefined && value !== null && value !== '')))
274+
return Boolean(
275+
error &&
276+
(touched ||
277+
formik.submitCount > 0 ||
278+
(value !== undefined && value !== null && String(value).length > 0)),
279+
)
264280
},
265-
[formik.errors, formik.touched, formik.values],
281+
[formik.errors, formik.touched, formik.values, formik.submitCount],
266282
)
267283

268284
useEffect(() => {
@@ -394,7 +410,10 @@ const WebsiteSsoIdentityProviderForm = ({
394410
required={!formik.values.metaDataFileImportedFlag}
395411
lsize={4}
396412
rsize={8}
397-
showError={shouldShowError('idpEntityId')}
413+
showError={Boolean(
414+
formik.errors.idpEntityId &&
415+
(formik.touched.idpEntityId || formik.submitCount > 0),
416+
)}
398417
errorMessage={formik.errors.idpEntityId}
399418
disabled={viewOnly}
400419
doc_category={DOC_SECTION}
@@ -411,7 +430,10 @@ const WebsiteSsoIdentityProviderForm = ({
411430
lsize={4}
412431
rsize={8}
413432
required={!formik.values.metaDataFileImportedFlag}
414-
showError={shouldShowError('nameIDPolicyFormat')}
433+
showError={Boolean(
434+
formik.errors.nameIDPolicyFormat &&
435+
(formik.touched.nameIDPolicyFormat || formik.submitCount > 0),
436+
)}
415437
errorMessage={formik.errors.nameIDPolicyFormat}
416438
disabled={viewOnly}
417439
doc_category={DOC_SECTION}
@@ -427,7 +449,10 @@ const WebsiteSsoIdentityProviderForm = ({
427449
formik={formik}
428450
lsize={4}
429451
rsize={8}
430-
showError={shouldShowError('singleSignOnServiceUrl')}
452+
showError={Boolean(
453+
formik.errors.singleSignOnServiceUrl &&
454+
(formik.touched.singleSignOnServiceUrl || formik.submitCount > 0),
455+
)}
431456
errorMessage={formik.errors.singleSignOnServiceUrl}
432457
disabled={viewOnly}
433458
doc_category={DOC_SECTION}
@@ -441,7 +466,10 @@ const WebsiteSsoIdentityProviderForm = ({
441466
formik={formik}
442467
lsize={4}
443468
rsize={8}
444-
showError={shouldShowError('singleLogoutServiceUrl')}
469+
showError={Boolean(
470+
formik.errors.singleLogoutServiceUrl &&
471+
(formik.touched.singleLogoutServiceUrl || formik.submitCount > 0),
472+
)}
445473
errorMessage={formik.errors.singleLogoutServiceUrl}
446474
disabled={viewOnly}
447475
doc_category={DOC_SECTION}
@@ -456,7 +484,10 @@ const WebsiteSsoIdentityProviderForm = ({
456484
lsize={4}
457485
rsize={8}
458486
type="textarea"
459-
showError={shouldShowError('signingCertificate')}
487+
showError={Boolean(
488+
formik.errors.signingCertificate &&
489+
(formik.touched.signingCertificate || formik.submitCount > 0),
490+
)}
460491
errorMessage={formik.errors.signingCertificate}
461492
disabled={viewOnly}
462493
rows={10}
@@ -472,7 +503,10 @@ const WebsiteSsoIdentityProviderForm = ({
472503
lsize={4}
473504
rsize={8}
474505
type="textarea"
475-
showError={shouldShowError('encryptionPublicKey')}
506+
showError={Boolean(
507+
formik.errors.encryptionPublicKey &&
508+
(formik.touched.encryptionPublicKey || formik.submitCount > 0),
509+
)}
476510
errorMessage={formik.errors.encryptionPublicKey}
477511
disabled={viewOnly}
478512
rows={10}
@@ -487,7 +521,10 @@ const WebsiteSsoIdentityProviderForm = ({
487521
formik={formik}
488522
lsize={4}
489523
rsize={8}
490-
showError={shouldShowError('principalAttribute')}
524+
showError={Boolean(
525+
formik.errors.principalAttribute &&
526+
(formik.touched.principalAttribute || formik.submitCount > 0),
527+
)}
491528
errorMessage={formik.errors.principalAttribute}
492529
disabled={viewOnly}
493530
doc_category={DOC_SECTION}
@@ -501,7 +538,10 @@ const WebsiteSsoIdentityProviderForm = ({
501538
formik={formik}
502539
lsize={4}
503540
rsize={8}
504-
showError={shouldShowError('principalType')}
541+
showError={Boolean(
542+
formik.errors.principalType &&
543+
(formik.touched.principalType || formik.submitCount > 0),
544+
)}
505545
errorMessage={formik.errors.principalType}
506546
disabled={viewOnly}
507547
doc_category={DOC_SECTION}

0 commit comments

Comments
 (0)