Skip to content

Commit

Permalink
fix: handle v42 login
Browse files Browse the repository at this point in the history
  • Loading branch information
tomzemp committed Apr 25, 2024
1 parent 9477b8f commit 976c5d7
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 47 deletions.
25 changes: 20 additions & 5 deletions adapter/i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2024-03-14T15:31:40.141Z\n"
"PO-Revision-Date: 2024-03-14T15:31:40.141Z\n"
"POT-Creation-Date: 2024-04-24T13:58:13.591Z\n"
"PO-Revision-Date: 2024-04-24T13:58:13.591Z\n"

msgid "Save your data"
msgstr "Save your data"
Expand Down Expand Up @@ -60,9 +60,27 @@ msgstr "The following information may be requested by technical support."
msgid "Copy technical details to clipboard"
msgstr "Copy technical details to clipboard"

msgid "Signing in..."
msgstr "Signing in..."

msgid "Sign in"
msgstr "Sign in"

msgid "Going to app..."
msgstr "Going to app..."

msgid "Go to app"
msgstr "Go to app"

msgid "Please sign in"
msgstr "Please sign in"

msgid "Specify server"
msgstr "Specify server"

msgid "Could not log in"
msgstr "Could not log in"

msgid "Server"
msgstr "Server"

Expand All @@ -71,6 +89,3 @@ msgstr "Username"

msgid "Password"
msgstr "Password"

msgid "Sign in"
msgstr "Sign in"
157 changes: 119 additions & 38 deletions adapter/src/components/LoginModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,57 +6,133 @@ import {
ModalActions,
Button,
InputField,
NoticeBox,
} from '@dhis2/ui'
import PropTypes from 'prop-types'
import React, { useState } from 'react'
import i18n from '../locales/index.js'
import { post } from '../utils/api.js'
import { get, post, postJSON } from '../utils/api.js'
import { styles } from './styles/LoginModal.style.js'

// Check if base URL is set statically as an env var (typical in production)
const staticUrl = process.env.REACT_APP_DHIS2_BASE_URL

export const LoginModal = ({ appName, baseUrl }) => {
const getUseNewLoginAPI = async (server) => {
try {
// if loginConfig is available, the instance can use new endpoints
await get(`${server}/api/loginConfig`)
return true
} catch (e) {
// if loginConfig is not available, the instance must use old endpoints
return false
}
}

const loginWithNewEndpoints = async ({
server,
username,
password,
setError,
setIsLoggingIn,
}) => {
try {
await postJSON(
`${server}/api/auth/login`,
JSON.stringify({
username: encodeURIComponent(username),
password: encodeURIComponent(password),
})
)
window.location.reload()
} catch (e) {
setError(e)
setIsLoggingIn(false)
}
}

const loginWithOldEndpoints = async ({ server, username, password }) => {
try {
await post(
`${server}/dhis-web-commons-security/login.action`,
`j_username=${encodeURIComponent(
username
)}&j_password=${encodeURIComponent(password)}`
)
} catch (e) {
console.error(e)
} finally {
window.location.reload()
}
}

export const LoginModal = ({ appName, baseUrl, loginApp = false }) => {
const [server, setServer] = useState(baseUrl || '')
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [isDirty, setIsDirty] = useState(false)
const [error, setError] = useState(null)
const [isLoggingIn, setIsLoggingIn] = useState(false)

const isValid = (val) => val && val.length >= 2
const getSignInButtonText = ({ loginApp, isLoggingIn }) => {
if (!loginApp) {
return isLoggingIn ? i18n.t('Signing in...') : i18n.t('Sign in')
}
return isLoggingIn ? i18n.t('Going to app...') : i18n.t('Go to app')
}

const onSubmit = async (e) => {
e.preventDefault()
setIsDirty(true)
if (isValid(server) && isValid(username) && isValid(password)) {
if (
isValid(server) &&
((isValid(username) && isValid(password)) || loginApp)
) {
setIsLoggingIn(true)
if (!staticUrl) {
// keep the localStorage value here -- it's still used in some
// obscure cases, like in the cypress network shim
window.localStorage.DHIS2_BASE_URL = server
await setBaseUrlByAppName({ appName, baseUrl: server })
if (loginApp) {
window.location.reload()
}
}
try {
await post(
`${server}/dhis-web-commons-security/login.action`,
`j_username=${encodeURIComponent(
username
)}&j_password=${encodeURIComponent(password)}`
)
} catch (e) {
console.log(
'TODO: This will always error and cancel the request until we get a real login endpoint!'
)
}

// TODO: Hacky solution... this shouldn't require a reload
window.location.reload()
const useNewLoginAPI = await getUseNewLoginAPI(server)

if (useNewLoginAPI) {
loginWithNewEndpoints({
server,
username,
password,
setError,
setIsLoggingIn,
})
} else {
loginWithOldEndpoints({ server, username, password })
}
}
}

return (
<Modal open small dataTest="dhis2-adapter-loginmodal">
<style jsx>{styles}</style>
<form onSubmit={onSubmit}>
<ModalTitle>{i18n.t('Please sign in')}</ModalTitle>
<ModalTitle>
{!loginApp
? i18n.t('Please sign in')
: i18n.t('Specify server')}
</ModalTitle>

<ModalContent>
{error && (
<div className="errorNotification">
<NoticeBox error title={i18n.t('Could not log in')}>
{error?.message || error?.details?.message}
</NoticeBox>
</div>
)}
{!staticUrl && (
<InputField
dataTest="dhis2-adapter-loginserver"
Expand All @@ -68,35 +144,39 @@ export const LoginModal = ({ appName, baseUrl }) => {
onChange={(input) => setServer(input.value)}
/>
)}
{!loginApp && (
<>
<InputField
dataTest="dhis2-adapter-loginname"
error={isDirty && !isValid(username)}
label={i18n.t('Username')}
name="j_username"
type="text"
value={username}
onChange={(input) => setUsername(input.value)}
/>

<InputField
dataTest="dhis2-adapter-loginname"
error={isDirty && !isValid(username)}
label={i18n.t('Username')}
name="j_username"
type="text"
value={username}
onChange={(input) => setUsername(input.value)}
/>

<InputField
dataTest="dhis2-adapter-loginpassword"
error={isDirty && !isValid(password)}
label={i18n.t('Password')}
name="j_password"
type="password"
value={password}
onChange={(input) => setPassword(input.value)}
/>
<InputField
dataTest="dhis2-adapter-loginpassword"
error={isDirty && !isValid(password)}
label={i18n.t('Password')}
name="j_password"
type="password"
value={password}
onChange={(input) => setPassword(input.value)}
/>
</>
)}
</ModalContent>

<ModalActions>
<Button
primary
dataTest="dhis2-adapter-loginsubmit"
type="submit"
disabled={isLoggingIn}
>
{i18n.t('Sign in')}
{getSignInButtonText({ loginApp, isLoggingIn })}
</Button>
</ModalActions>
</form>
Expand All @@ -106,4 +186,5 @@ export const LoginModal = ({ appName, baseUrl }) => {
LoginModal.propTypes = {
appName: PropTypes.string,
baseUrl: PropTypes.string,
loginApp: PropTypes.bool,
}
10 changes: 6 additions & 4 deletions adapter/src/components/ServerVersionProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,12 @@ export const ServerVersionProvider = ({

// This needs to come before 'loading' case to show modal at correct times
if (systemInfoState.error || baseUrlState.error) {
return !loginApp ? (
<LoginModal appName={appName} baseUrl={baseUrl} />
) : (
<p>Specify DHIS2_BASE_URL environment variable</p>
return (
<LoginModal
appName={appName}
baseUrl={baseUrl}
loginApp={loginApp}
/>
)
}

Expand Down
7 changes: 7 additions & 0 deletions adapter/src/components/styles/LoginModal.style.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import css from 'styled-jsx/css'

export const styles = css`
.errorNotification {
margin-block: 8px;
}
`
9 changes: 9 additions & 0 deletions adapter/src/utils/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,12 @@ export const post = (url, body) =>
'Content-Type': 'application/x-www-form-urlencoded',
},
})

export const postJSON = (url, body) =>
request(url, {
method: 'POST',
body,
headers: {
'Content-Type': 'application/json',
},
})

0 comments on commit 976c5d7

Please sign in to comment.