Skip to content

Commit 38fba6b

Browse files
syntrydymoabu
andauthored
fix(admin-ui): Use typescript generated client for ACR and Authn pages (#2553)
* fix(admin-ui): Use typescript generated client for ACR and Authn pages #2552 * fix(admin-ui): Use typescript generated client for ACR and Authn pages #2552 * fix(admin-ui): Apply code review #2552 * fix(admin-ui): Apply code review #2552 * fix(admin-ui): Apply code review #2552 --------- Co-authored-by: Mohammad Abudayyeh <47318409+moabu@users.noreply.github.com>
1 parent 7962770 commit 38fba6b

File tree

23 files changed

+862
-776
lines changed

23 files changed

+862
-776
lines changed

admin-ui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@
164164
"holderjs": "^2.9.9",
165165
"i18next": "^23.10.1",
166166
"jans_config_api": "file:jans_config_api",
167+
"jotai": "^2.16.1",
167168
"jszip": "^3.10.1",
168169
"jwt-decode": "^4.0.0",
169170
"lodash": "^4.17.21",

admin-ui/plugins/auth-server/components/AuthN/AuthNDetailPage.js renamed to admin-ui/plugins/auth-server/components/AuthN/AuthNDetailPage.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,27 @@
1-
import React from 'react'
1+
import React, { type ReactElement } from 'react'
22
import { Container, Row, Col } from 'Components'
33
import GluuFormDetailRow from 'Routes/Apps/Gluu/GluuFormDetailRow'
44
import { AUTHN } from 'Utils/ApiResources'
5-
65
import customColors from '@/customColors'
76

8-
function AuthNDetailPage({ row }) {
7+
interface AuthNDetailRow {
8+
acrName?: string
9+
level?: number
10+
passwordAttribute?: string
11+
hashAlgorithm?: string
12+
primaryKey?: string
13+
samlACR?: string
14+
description?: string
15+
}
16+
17+
interface AuthNDetailPageProps {
18+
row: AuthNDetailRow
19+
}
20+
21+
function AuthNDetailPage({ row }: AuthNDetailPageProps): ReactElement {
922
return (
1023
<React.Fragment>
11-
<Container style={{ backgroundColor: customColors.smokeWhite }}>
24+
<Container style={{ backgroundColor: customColors.whiteSmoke }}>
1225
<Row>
1326
<Col sm={6}>
1427
<GluuFormDetailRow
@@ -47,7 +60,6 @@ function AuthNDetailPage({ row }) {
4760
</Col>
4861
</Row>
4962
<Row>
50-
{' '}
5163
<Col sm={6}>
5264
<GluuFormDetailRow
5365
label="fields.primary_key"

admin-ui/plugins/auth-server/components/AuthN/AuthNEditPage.js

Lines changed: 0 additions & 104 deletions
This file was deleted.
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
import React, { useState, useCallback, useRef, useEffect, type ReactElement } from 'react'
2+
import { CardBody, Card } from 'Components'
3+
import { useAtomValue } from 'jotai'
4+
import AuthNForm from './AuthNForm'
5+
import GluuLoader from 'Routes/Apps/Gluu/GluuLoader'
6+
import { useTranslation } from 'react-i18next'
7+
import applicationStyle from 'Routes/Apps/Gluu/styles/applicationstyle'
8+
import { useAppNavigation, ROUTES } from '@/helpers/navigation'
9+
import {
10+
usePutAcrs,
11+
usePutConfigDatabaseLdap,
12+
usePutConfigScripts,
13+
type AuthenticationMethod,
14+
type GluuLdapConfiguration,
15+
type CustomScript,
16+
} from 'JansConfigApi'
17+
import { updateToast } from 'Redux/features/toastSlice'
18+
import { useDispatch } from 'react-redux'
19+
import { currentAuthNItemAtom, type AuthNItem, type ConfigurationProperty } from './atoms'
20+
21+
const isDefaultAuthNMethod = (value: boolean | string): boolean =>
22+
value === 'true' || value === true
23+
24+
const transformConfigurationProperties = (
25+
properties: ConfigurationProperty[] | undefined,
26+
): Array<{ value1: string; value2: string; hide: boolean }> | undefined => {
27+
if (!properties || properties.length === 0) {
28+
return undefined
29+
}
30+
return properties
31+
.filter((e): e is ConfigurationProperty => e != null)
32+
.filter((e) => Object.keys(e).length !== 0)
33+
.map((e) => ({
34+
value1: e.key || e.value1 || '',
35+
value2: e.value || e.value2 || '',
36+
hide: false,
37+
}))
38+
}
39+
40+
interface AuthNFormValues {
41+
acr: string
42+
level: number
43+
defaultAuthNMethod: boolean | string
44+
samlACR: string
45+
description: string
46+
primaryKey: string
47+
passwordAttribute: string
48+
hashAlgorithm: string
49+
bindDN: string
50+
maxConnections: string | number
51+
remotePrimaryKey: string
52+
localPrimaryKey: string
53+
servers: string | string[]
54+
baseDNs: string | string[]
55+
bindPassword: string
56+
useSSL: boolean
57+
enabled: boolean
58+
configId: string
59+
baseDn: string | undefined
60+
inum: string | undefined
61+
configurationProperties?: ConfigurationProperty[]
62+
}
63+
64+
function AuthNEditPage(): ReactElement {
65+
const dispatch = useDispatch()
66+
const { navigateToRoute } = useAppNavigation()
67+
const { t } = useTranslation()
68+
const atomItem = useAtomValue(currentAuthNItemAtom)
69+
const [isSubmitting, setIsSubmitting] = useState(false)
70+
const navigationTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
71+
72+
useEffect(() => {
73+
return () => {
74+
if (navigationTimeoutRef.current) {
75+
clearTimeout(navigationTimeoutRef.current)
76+
}
77+
}
78+
}, [])
79+
80+
const handleSuccess = useCallback(() => {
81+
setIsSubmitting(false)
82+
dispatch(updateToast(true, 'success'))
83+
navigationTimeoutRef.current = setTimeout(() => {
84+
navigateToRoute(ROUTES.AUTH_SERVER_AUTHN)
85+
}, 2000)
86+
}, [dispatch, navigateToRoute])
87+
88+
const handleError = useCallback(
89+
(error: Error) => {
90+
const errorMessage = error?.message || t('messages.error_in_saving')
91+
dispatch(updateToast(true, 'error', errorMessage))
92+
setIsSubmitting(false)
93+
},
94+
[dispatch, t],
95+
)
96+
97+
const putAcrsMutation = usePutAcrs()
98+
99+
const putLdapMutation = usePutConfigDatabaseLdap({
100+
mutation: {
101+
onError: handleError,
102+
},
103+
})
104+
105+
const putScriptMutation = usePutConfigScripts({
106+
mutation: {
107+
onError: handleError,
108+
},
109+
})
110+
111+
const isLoading =
112+
isSubmitting ||
113+
putAcrsMutation.isPending ||
114+
putLdapMutation.isPending ||
115+
putScriptMutation.isPending
116+
117+
async function handleSubmit(data: AuthNFormValues): Promise<void> {
118+
if (!atomItem?.name) {
119+
dispatch(updateToast(true, 'error', t('messages.error_in_saving')))
120+
return
121+
}
122+
123+
setIsSubmitting(true)
124+
125+
try {
126+
if (atomItem.name === 'simple_password_auth') {
127+
if (isDefaultAuthNMethod(data.defaultAuthNMethod)) {
128+
try {
129+
const acrData: AuthenticationMethod = { defaultAcr: 'simple_password_auth' }
130+
await putAcrsMutation.mutateAsync({ data: acrData })
131+
} catch (error) {
132+
handleError(error as Error)
133+
return
134+
}
135+
}
136+
handleSuccess()
137+
} else if (atomItem.name === 'default_ldap_password') {
138+
const ldapPayload: GluuLdapConfiguration = {
139+
configId: atomItem.configId || '',
140+
bindDN: data.bindDN,
141+
bindPassword: data.bindPassword,
142+
servers: Array.isArray(data.servers) ? data.servers : [data.servers],
143+
baseDNs: Array.isArray(data.baseDNs) ? data.baseDNs : [data.baseDNs],
144+
primaryKey: data.primaryKey,
145+
localPrimaryKey: data.localPrimaryKey,
146+
maxConnections:
147+
typeof data.maxConnections === 'string'
148+
? parseInt(data.maxConnections, 10)
149+
: data.maxConnections,
150+
useSSL: data.useSSL,
151+
enabled: data.enabled,
152+
level: data.level,
153+
}
154+
155+
await putLdapMutation.mutateAsync({ data: ldapPayload })
156+
157+
if (isDefaultAuthNMethod(data.defaultAuthNMethod)) {
158+
try {
159+
const acrData: AuthenticationMethod = { defaultAcr: data.configId }
160+
await putAcrsMutation.mutateAsync({ data: acrData })
161+
} catch (acrError) {
162+
dispatch(
163+
updateToast(
164+
true,
165+
'warning',
166+
t(
167+
'messages.ldap_saved_acr_failed',
168+
'LDAP config saved, but failed to set default ACR',
169+
),
170+
),
171+
)
172+
setIsSubmitting(false)
173+
return
174+
}
175+
}
176+
handleSuccess()
177+
} else {
178+
const scriptPayload: CustomScript = {
179+
inum: data.inum,
180+
dn: data.baseDn,
181+
name: atomItem.acrName || '',
182+
description: data.description,
183+
level: data.level,
184+
scriptType: 'person_authentication',
185+
configurationProperties: transformConfigurationProperties(data.configurationProperties),
186+
}
187+
188+
await putScriptMutation.mutateAsync({ data: scriptPayload })
189+
190+
if (isDefaultAuthNMethod(data.defaultAuthNMethod)) {
191+
try {
192+
const acrData: AuthenticationMethod = { defaultAcr: atomItem.acrName || '' }
193+
await putAcrsMutation.mutateAsync({ data: acrData })
194+
} catch (acrError) {
195+
dispatch(
196+
updateToast(
197+
true,
198+
'warning',
199+
t(
200+
'messages.script_saved_acr_failed',
201+
'Script config saved, but failed to set default ACR',
202+
),
203+
),
204+
)
205+
setIsSubmitting(false)
206+
return
207+
}
208+
}
209+
handleSuccess()
210+
}
211+
} catch (error) {
212+
if (error instanceof Error && !('response' in error)) {
213+
console.error('Unexpected error during form submission:', error)
214+
dispatch(updateToast(true, 'error', error.message || t('messages.error_in_saving')))
215+
}
216+
setIsSubmitting(false)
217+
}
218+
}
219+
220+
if (!atomItem) {
221+
return (
222+
<GluuLoader blocking={true}>
223+
<Card className="mb-3" style={applicationStyle.mainCard}>
224+
<CardBody>{t('messages.no_item_selected', 'No item selected')}</CardBody>
225+
</Card>
226+
</GluuLoader>
227+
)
228+
}
229+
230+
return (
231+
<GluuLoader blocking={isLoading}>
232+
<Card className="mb-3" style={applicationStyle.mainCard}>
233+
<CardBody>
234+
<AuthNForm handleSubmit={handleSubmit} item={atomItem} />
235+
</CardBody>
236+
</Card>
237+
</GluuLoader>
238+
)
239+
}
240+
241+
export default AuthNEditPage

0 commit comments

Comments
 (0)