{
const Login = ({ location }) => {
const [loading, setLoading] = useState(false)
+ const [authSources, setAuthSources] = useState([
+ { id: 'internal', name: 'Internal' },
+ ])
+ const [authSource, setAuthSource] = useState('internal')
const translate = useTranslate()
const notify = useNotify()
const login = useLogin()
const dispatch = useDispatch()
+ useEffect(() => {
+ fetch(baseUrl('/auth/sources'))
+ .then((response) => (response.ok ? response.json() : []))
+ .then((sources) => {
+ if (sources.length) {
+ setAuthSources(sources)
+ setAuthSource(sources[0].id)
+ }
+ })
+ .catch(() => {})
+ }, [])
+
const handleSubmit = useCallback(
(auth) => {
setLoading(true)
dispatch(clearQueue())
- login(auth, location.state ? location.state.nextPathname : '/').catch(
- (error) => {
- setLoading(false)
- notify(
- typeof error === 'string'
- ? error
- : typeof error === 'undefined' || !error.message
- ? 'ra.auth.sign_in_error'
- : error.message,
- 'warning',
- )
- },
- )
+ login(
+ { ...auth, authSource },
+ location.state ? location.state.nextPathname : '/',
+ ).catch((error) => {
+ setLoading(false)
+ notify(
+ typeof error === 'string'
+ ? error
+ : typeof error === 'undefined' || !error.message
+ ? 'ra.auth.sign_in_error'
+ : error.message,
+ 'warning',
+ )
+ })
},
- [dispatch, login, notify, setLoading, location],
+ [authSource, dispatch, login, notify, setLoading, location],
)
const validateLogin = useCallback(
diff --git a/ui/src/ldap/index.jsx b/ui/src/ldap/index.jsx
new file mode 100644
index 000000000..be7d8e236
--- /dev/null
+++ b/ui/src/ldap/index.jsx
@@ -0,0 +1,102 @@
+import React, { useEffect, useState } from 'react'
+import {
+ Button,
+ Card,
+ CardContent,
+ TextField,
+ Typography,
+} from '@material-ui/core'
+import SettingsEthernetIcon from '@material-ui/icons/SettingsEthernet'
+import { useNotify } from 'react-admin'
+import httpClient from '../dataProvider/httpClient'
+import { REST_URL } from '../consts'
+
+const defaultConfig = { sources: [] }
+
+export const LdapList = () => {
+ const notify = useNotify()
+ const [text, setText] = useState(JSON.stringify(defaultConfig, null, 2))
+ const [loading, setLoading] = useState(false)
+
+ useEffect(() => {
+ httpClient(`${REST_URL}/ldap`)
+ .then(({ json }) =>
+ setText(JSON.stringify({ sources: json.sources || [] }, null, 2)),
+ )
+ .catch(() => notify('Could not load LDAP configuration', 'warning'))
+ }, [notify])
+
+ const save = () => {
+ setLoading(true)
+ httpClient(`${REST_URL}/ldap`, { method: 'PUT', body: text })
+ .then(({ json }) => {
+ setText(JSON.stringify({ sources: json.sources || [] }, null, 2))
+ notify('LDAP configuration saved')
+ })
+ .catch((e) =>
+ notify(`Could not save LDAP configuration: ${e.message}`, 'warning'),
+ )
+ .finally(() => setLoading(false))
+ }
+
+ const test = () => {
+ const cfg = JSON.parse(text)
+ const source = cfg.sources?.[0]
+ if (!source) {
+ notify('Add at least one LDAP source to test', 'warning')
+ return
+ }
+ setLoading(true)
+ httpClient(`${REST_URL}/ldap/test`, {
+ method: 'POST',
+ body: JSON.stringify(source),
+ })
+ .then(({ json }) =>
+ notify(
+ `LDAP test found ${json.cache?.users?.length || 0} users and ${json.cache?.groups?.length || 0} groups`,
+ ),
+ )
+ .catch((e) => notify(`LDAP test failed: ${e.message}`, 'warning'))
+ .finally(() => setLoading(false))
+ }
+
+ return (
+
+
+
+ LDAP Authentication
+
+
+ Configure LDAP sources as JSON. Sources are evaluated after internal
+ auth for external clients; the login page shows enabled sources as
+ tabs.
+
+ setText(e.target.value)}
+ />
+
+
+
+
+ )
+}