fix: url resolver and minor fixes
This commit is contained in:
parent
2062413c75
commit
75a2ecd3c2
15 changed files with 274 additions and 290 deletions
|
|
@ -220,17 +220,14 @@ const resetPassword = (token, userID, newPassword, context) => {
|
||||||
.then(() => true)
|
.then(() => true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const reset2FA = (token, userID, code, secret, context) => {
|
const reset2FA = (token, userID, code, context) => {
|
||||||
const isCodeValid = otplib.authenticator.verify({ token: code, secret })
|
|
||||||
if (!isCodeValid) throw new authErrors.InvalidTwoFactorError()
|
|
||||||
|
|
||||||
return users.getUserById(userID)
|
return users.getUserById(userID)
|
||||||
.then(user => {
|
.then(user => {
|
||||||
|
const isCodeValid = otplib.authenticator.verify({ token: code, secret: user.temp_twofa_code })
|
||||||
|
if (!isCodeValid) throw new authErrors.InvalidTwoFactorError()
|
||||||
|
|
||||||
destroySessionIfSameUser(context, user)
|
destroySessionIfSameUser(context, user)
|
||||||
if (user.temp_twofa_code !== secret) {
|
return users.reset2FASecret(token, user.id, user.temp_twofa_code)
|
||||||
throw new authErrors.InvalidTwoFactorError()
|
|
||||||
}
|
|
||||||
return users.reset2FASecret(token, user.id, secret)
|
|
||||||
})
|
})
|
||||||
.then(() => true)
|
.then(() => true)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ const resolver = {
|
||||||
createRegisterToken: (...[, { username, role }]) => authentication.createRegisterToken(username, role),
|
createRegisterToken: (...[, { username, role }]) => authentication.createRegisterToken(username, role),
|
||||||
register: (...[, { token, username, password, role }]) => authentication.register(token, username, password, role),
|
register: (...[, { token, username, password, role }]) => authentication.register(token, username, password, role),
|
||||||
resetPassword: (...[, { token, userID, newPassword }, context]) => authentication.resetPassword(token, userID, newPassword, context),
|
resetPassword: (...[, { token, userID, newPassword }, context]) => authentication.resetPassword(token, userID, newPassword, context),
|
||||||
reset2FA: (...[, { token, userID, code, secret }, context]) => authentication.reset2FA(token, userID, code, secret, context)
|
reset2FA: (...[, { token, userID, code }, context]) => authentication.reset2FA(token, userID, code, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ const typeDef = `
|
||||||
createRegisterToken(username: String!, role: String!): RegistrationToken @auth(requires: [SUPERUSER])
|
createRegisterToken(username: String!, role: String!): RegistrationToken @auth(requires: [SUPERUSER])
|
||||||
register(token: String!, username: String!, password: String!, role: String!): Boolean
|
register(token: String!, username: String!, password: String!, role: String!): Boolean
|
||||||
resetPassword(token: String!, userID: ID!, newPassword: String!): Boolean
|
resetPassword(token: String!, userID: ID!, newPassword: String!): Boolean
|
||||||
reset2FA(token: String!, userID: ID!, secret: String!, code: String!): Boolean
|
reset2FA(token: String!, userID: ID!, code: String!): Boolean
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useLazyQuery } from '@apollo/react-hooks'
|
import { useQuery } from '@apollo/react-hooks'
|
||||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||||
import Grid from '@material-ui/core/Grid'
|
import Grid from '@material-ui/core/Grid'
|
||||||
import Slide from '@material-ui/core/Slide'
|
import Slide from '@material-ui/core/Slide'
|
||||||
|
|
@ -11,7 +11,7 @@ import {
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import { create } from 'jss'
|
import { create } from 'jss'
|
||||||
import extendJss from 'jss-plugin-extend'
|
import extendJss from 'jss-plugin-extend'
|
||||||
import React, { useContext, useEffect, useState } from 'react'
|
import React, { useContext, useState } from 'react'
|
||||||
import {
|
import {
|
||||||
useLocation,
|
useLocation,
|
||||||
useHistory,
|
useHistory,
|
||||||
|
|
@ -91,17 +91,13 @@ const Main = () => {
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
const { wizardTested, userData, setUserData } = useContext(AppContext)
|
const { wizardTested, userData, setUserData } = useContext(AppContext)
|
||||||
|
|
||||||
const [getUserData, { loading }] = useLazyQuery(GET_USER_DATA, {
|
const { loading } = useQuery(GET_USER_DATA, {
|
||||||
onCompleted: userResponse => {
|
onCompleted: userResponse => {
|
||||||
if (!userData && userResponse?.userData)
|
if (!userData && userResponse?.userData)
|
||||||
setUserData(userResponse.userData)
|
setUserData(userResponse.userData)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getUserData()
|
|
||||||
}, [getUserData])
|
|
||||||
|
|
||||||
const route = location.pathname
|
const route = location.pathname
|
||||||
|
|
||||||
const sidebar = hasSidebar(route)
|
const sidebar = hasSidebar(route)
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,19 @@ const Input2FAState = ({ state, dispatch }) => {
|
||||||
|
|
||||||
const [invalidToken, setInvalidToken] = useState(false)
|
const [invalidToken, setInvalidToken] = useState(false)
|
||||||
|
|
||||||
|
const [getUserData, { error: queryError }] = useLazyQuery(GET_USER_DATA, {
|
||||||
|
onCompleted: ({ userData }) => {
|
||||||
|
setUserData(userData)
|
||||||
|
history.push('/')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const [input2FA, { error: mutationError }] = useMutation(INPUT_2FA, {
|
||||||
|
onCompleted: ({ input2FA: success }) => {
|
||||||
|
success ? getUserData() : setInvalidToken(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const handle2FAChange = value => {
|
const handle2FAChange = value => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: STATES.INPUT_2FA,
|
type: STATES.INPUT_2FA,
|
||||||
|
|
@ -73,19 +86,6 @@ const Input2FAState = ({ state, dispatch }) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const [input2FA, { error: mutationError }] = useMutation(INPUT_2FA, {
|
|
||||||
onCompleted: ({ input2FA: success }) => {
|
|
||||||
success ? getUserData() : setInvalidToken(true)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const [getUserData, { error: queryError }] = useLazyQuery(GET_USER_DATA, {
|
|
||||||
onCompleted: ({ userData }) => {
|
|
||||||
setUserData(userData)
|
|
||||||
history.push('/')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const getErrorMsg = () => {
|
const getErrorMsg = () => {
|
||||||
if (queryError) return 'Internal server error'
|
if (queryError) return 'Internal server error'
|
||||||
if (state.twoFAField.length !== 6 && invalidToken)
|
if (state.twoFAField.length !== 6 && invalidToken)
|
||||||
|
|
|
||||||
|
|
@ -69,12 +69,12 @@ const Register = () => {
|
||||||
const initialState = {
|
const initialState = {
|
||||||
username: null,
|
username: null,
|
||||||
role: null,
|
role: null,
|
||||||
wasSuccessful: false
|
result: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const reducer = (state, action) => {
|
const reducer = (state, action) => {
|
||||||
const { type, payload } = action
|
const { type, payload } = action
|
||||||
return { ...state, [type]: payload }
|
return { ...state, ...payload, result: type }
|
||||||
}
|
}
|
||||||
|
|
||||||
const [state, dispatch] = useReducer(reducer, initialState)
|
const [state, dispatch] = useReducer(reducer, initialState)
|
||||||
|
|
@ -83,15 +83,23 @@ const Register = () => {
|
||||||
variables: { token: token },
|
variables: { token: token },
|
||||||
onCompleted: ({ validateRegisterLink: info }) => {
|
onCompleted: ({ validateRegisterLink: info }) => {
|
||||||
if (!info) {
|
if (!info) {
|
||||||
dispatch({ type: 'wasSuccessful', payload: false })
|
dispatch({
|
||||||
|
type: 'failure'
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
dispatch({ type: 'wasSuccessful', payload: true })
|
dispatch({
|
||||||
dispatch({ type: 'username', payload: info.username })
|
type: 'success',
|
||||||
dispatch({ type: 'role', payload: info.role })
|
payload: {
|
||||||
|
username: info.username,
|
||||||
|
role: info.role
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: () => {
|
||||||
dispatch({ type: 'wasSuccessful', payload: false })
|
dispatch({
|
||||||
|
type: 'failure'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -127,7 +135,7 @@ const Register = () => {
|
||||||
<Logo className={classes.icon} />
|
<Logo className={classes.icon} />
|
||||||
<H2 className={classes.title}>Lamassu Admin</H2>
|
<H2 className={classes.title}>Lamassu Admin</H2>
|
||||||
</div>
|
</div>
|
||||||
{!loading && state.wasSuccessful && (
|
{!loading && state.result === 'success' && (
|
||||||
<Formik
|
<Formik
|
||||||
validationSchema={validationSchema}
|
validationSchema={validationSchema}
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
|
|
@ -176,7 +184,7 @@ const Register = () => {
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
)}
|
)}
|
||||||
{!loading && !state.wasSuccessful && (
|
{!loading && state.result === 'failure' && (
|
||||||
<>
|
<>
|
||||||
<Label3>Link has expired</Label3>
|
<Label3>Link has expired</Label3>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { makeStyles, Grid } from '@material-ui/core'
|
||||||
import Paper from '@material-ui/core/Paper'
|
import Paper from '@material-ui/core/Paper'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import QRCode from 'qrcode.react'
|
import QRCode from 'qrcode.react'
|
||||||
import React, { useState } from 'react'
|
import React, { useReducer, useState } from 'react'
|
||||||
import { useLocation, useHistory } from 'react-router-dom'
|
import { useLocation, useHistory } from 'react-router-dom'
|
||||||
|
|
||||||
import { ActionButton, Button } from 'src/components/buttons'
|
import { ActionButton, Button } from 'src/components/buttons'
|
||||||
|
|
@ -28,13 +28,8 @@ const VALIDATE_RESET_2FA_LINK = gql`
|
||||||
`
|
`
|
||||||
|
|
||||||
const RESET_2FA = gql`
|
const RESET_2FA = gql`
|
||||||
mutation reset2FA(
|
mutation reset2FA($token: String!, $userID: ID!, $code: String!) {
|
||||||
$token: String!
|
reset2FA(token: $token, userID: $userID, code: $code)
|
||||||
$userID: ID!
|
|
||||||
$secret: String!
|
|
||||||
$code: String!
|
|
||||||
) {
|
|
||||||
reset2FA(token: $token, userID: $userID, secret: $secret, code: $code)
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
@ -42,37 +37,52 @@ const Reset2FA = () => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
const token = QueryParams().get('t')
|
const token = QueryParams().get('t')
|
||||||
const [userID, setUserID] = useState(null)
|
|
||||||
const [isLoading, setLoading] = useState(true)
|
|
||||||
const [wasSuccessful, setSuccess] = useState(false)
|
|
||||||
const [secret, setSecret] = useState(null)
|
|
||||||
const [otpauth, setOtpauth] = useState(null)
|
|
||||||
|
|
||||||
const [isShowing, setShowing] = useState(false)
|
const [isShowing, setShowing] = useState(false)
|
||||||
const [invalidToken, setInvalidToken] = useState(false)
|
const [invalidToken, setInvalidToken] = useState(false)
|
||||||
const [twoFAConfirmation, setTwoFAConfirmation] = useState('')
|
const [twoFAConfirmation, setTwoFAConfirmation] = useState('')
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
userID: null,
|
||||||
|
secret: null,
|
||||||
|
otpauth: null,
|
||||||
|
result: null
|
||||||
|
}
|
||||||
|
|
||||||
|
const reducer = (state, action) => {
|
||||||
|
const { type, payload } = action
|
||||||
|
return { ...state, ...payload, result: type }
|
||||||
|
}
|
||||||
|
|
||||||
|
const [state, dispatch] = useReducer(reducer, initialState)
|
||||||
|
|
||||||
const handle2FAChange = value => {
|
const handle2FAChange = value => {
|
||||||
setTwoFAConfirmation(value)
|
setTwoFAConfirmation(value)
|
||||||
setInvalidToken(false)
|
setInvalidToken(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { error: queryError } = useQuery(VALIDATE_RESET_2FA_LINK, {
|
const { error: queryError, loading } = useQuery(VALIDATE_RESET_2FA_LINK, {
|
||||||
variables: { token: token },
|
variables: { token: token },
|
||||||
onCompleted: ({ validateReset2FALink: info }) => {
|
onCompleted: ({ validateReset2FALink: info }) => {
|
||||||
setLoading(false)
|
|
||||||
if (!info) {
|
if (!info) {
|
||||||
setSuccess(false)
|
dispatch({
|
||||||
|
type: 'failure'
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
setUserID(info.user_id)
|
dispatch({
|
||||||
setSecret(info.secret)
|
type: 'success',
|
||||||
setOtpauth(info.otpauth)
|
payload: {
|
||||||
setSuccess(true)
|
userID: info.user_id,
|
||||||
|
secret: info.secret,
|
||||||
|
otpauth: info.otpauth
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: () => {
|
||||||
setLoading(false)
|
dispatch({
|
||||||
setSuccess(false)
|
type: 'failure'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -107,7 +117,7 @@ const Reset2FA = () => {
|
||||||
<Logo className={classes.icon} />
|
<Logo className={classes.icon} />
|
||||||
<H2 className={classes.title}>Lamassu Admin</H2>
|
<H2 className={classes.title}>Lamassu Admin</H2>
|
||||||
</div>
|
</div>
|
||||||
{!isLoading && wasSuccessful && (
|
{!loading && state.result === 'success' && (
|
||||||
<>
|
<>
|
||||||
<div className={classes.infoWrapper}>
|
<div className={classes.infoWrapper}>
|
||||||
<Label2 className={classes.info2}>
|
<Label2 className={classes.info2}>
|
||||||
|
|
@ -117,7 +127,11 @@ const Reset2FA = () => {
|
||||||
</Label2>
|
</Label2>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.qrCodeWrapper}>
|
<div className={classes.qrCodeWrapper}>
|
||||||
<QRCode size={240} fgColor={primaryColor} value={otpauth} />
|
<QRCode
|
||||||
|
size={240}
|
||||||
|
fgColor={primaryColor}
|
||||||
|
value={state.otpauth}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.secretWrapper}>
|
<div className={classes.secretWrapper}>
|
||||||
<Label2 className={classes.secretLabel}>
|
<Label2 className={classes.secretLabel}>
|
||||||
|
|
@ -127,7 +141,7 @@ const Reset2FA = () => {
|
||||||
className={
|
className={
|
||||||
isShowing ? classes.secret : classes.hiddenSecret
|
isShowing ? classes.secret : classes.hiddenSecret
|
||||||
}>
|
}>
|
||||||
{secret}
|
{state.secret}
|
||||||
</Label2>
|
</Label2>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
@ -160,8 +174,7 @@ const Reset2FA = () => {
|
||||||
reset2FA({
|
reset2FA({
|
||||||
variables: {
|
variables: {
|
||||||
token: token,
|
token: token,
|
||||||
userID: userID,
|
userID: state.userID,
|
||||||
secret: secret,
|
|
||||||
code: twoFAConfirmation
|
code: twoFAConfirmation
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -172,7 +185,7 @@ const Reset2FA = () => {
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{!isLoading && !wasSuccessful && (
|
{!loading && state.result === 'failure' && (
|
||||||
<>
|
<>
|
||||||
<Label3>Link has expired</Label3>
|
<Label3>Link has expired</Label3>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { useQuery } from '@apollo/react-hooks'
|
||||||
import { makeStyles, Box, Chip } from '@material-ui/core'
|
import { makeStyles, Box, Chip } from '@material-ui/core'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState, useContext } from 'react'
|
import React, { useReducer, useState, useContext } from 'react'
|
||||||
|
|
||||||
import AppContext from 'src/AppContext'
|
import AppContext from 'src/AppContext'
|
||||||
import { Link } from 'src/components/buttons'
|
import { Link } from 'src/components/buttons'
|
||||||
|
|
@ -39,23 +39,27 @@ const Users = () => {
|
||||||
|
|
||||||
const { data: userResponse } = useQuery(GET_USERS)
|
const { data: userResponse } = useQuery(GET_USERS)
|
||||||
|
|
||||||
const [showCreateUserModal, setShowCreateUserModal] = useState(false)
|
const initialState = {
|
||||||
const toggleCreateUserModal = () =>
|
showCreateUserModal: false,
|
||||||
setShowCreateUserModal(!showCreateUserModal)
|
showResetPasswordModal: false,
|
||||||
|
showReset2FAModal: false,
|
||||||
|
showRoleModal: false,
|
||||||
|
showEnableUserModal: false
|
||||||
|
}
|
||||||
|
|
||||||
const [showResetPasswordModal, setShowResetPasswordModal] = useState(false)
|
const reducer = (_, action) => {
|
||||||
const toggleResetPasswordModal = () =>
|
const { type, payload } = action
|
||||||
setShowResetPasswordModal(!showResetPasswordModal)
|
switch (type) {
|
||||||
|
case 'close':
|
||||||
|
return initialState
|
||||||
|
case 'open':
|
||||||
|
return { ...initialState, [payload]: true }
|
||||||
|
default:
|
||||||
|
return initialState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const [showReset2FAModal, setShowReset2FAModal] = useState(false)
|
const [state, dispatch] = useReducer(reducer, initialState)
|
||||||
const toggleReset2FAModal = () => setShowReset2FAModal(!showReset2FAModal)
|
|
||||||
|
|
||||||
const [showRoleModal, setShowRoleModal] = useState(false)
|
|
||||||
const toggleRoleModal = () => setShowRoleModal(!showRoleModal)
|
|
||||||
|
|
||||||
const [showEnableUserModal, setShowEnableUserModal] = useState(false)
|
|
||||||
const toggleEnableUserModal = () =>
|
|
||||||
setShowEnableUserModal(!showEnableUserModal)
|
|
||||||
|
|
||||||
const [userInfo, setUserInfo] = useState(null)
|
const [userInfo, setUserInfo] = useState(null)
|
||||||
|
|
||||||
|
|
@ -103,7 +107,10 @@ const Users = () => {
|
||||||
checked={u.role === 'superuser'}
|
checked={u.role === 'superuser'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setUserInfo(u)
|
setUserInfo(u)
|
||||||
toggleRoleModal()
|
dispatch({
|
||||||
|
type: 'open',
|
||||||
|
payload: 'showRoleModal'
|
||||||
|
})
|
||||||
}}
|
}}
|
||||||
value={u.role === 'superuser'}
|
value={u.role === 'superuser'}
|
||||||
/>
|
/>
|
||||||
|
|
@ -130,7 +137,10 @@ const Users = () => {
|
||||||
className={classes.actionChip}
|
className={classes.actionChip}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setUserInfo(u)
|
setUserInfo(u)
|
||||||
toggleResetPasswordModal()
|
dispatch({
|
||||||
|
type: 'open',
|
||||||
|
payload: 'showResetPasswordModal'
|
||||||
|
})
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Chip
|
<Chip
|
||||||
|
|
@ -139,7 +149,10 @@ const Users = () => {
|
||||||
className={classes.actionChip}
|
className={classes.actionChip}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setUserInfo(u)
|
setUserInfo(u)
|
||||||
toggleReset2FAModal()
|
dispatch({
|
||||||
|
type: 'open',
|
||||||
|
payload: 'showReset2FAModal'
|
||||||
|
})
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|
@ -157,7 +170,10 @@ const Users = () => {
|
||||||
checked={u.enabled}
|
checked={u.enabled}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setUserInfo(u)
|
setUserInfo(u)
|
||||||
toggleEnableUserModal()
|
dispatch({
|
||||||
|
type: 'open',
|
||||||
|
payload: 'showEnableUserModal'
|
||||||
|
})
|
||||||
}}
|
}}
|
||||||
value={u.enabled}
|
value={u.enabled}
|
||||||
/>
|
/>
|
||||||
|
|
@ -174,36 +190,40 @@ const Users = () => {
|
||||||
className={classes.tableWidth}
|
className={classes.tableWidth}
|
||||||
display="flex"
|
display="flex"
|
||||||
justifyContent="flex-end">
|
justifyContent="flex-end">
|
||||||
<Link color="primary" onClick={toggleCreateUserModal}>
|
<Link
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
dispatch({
|
||||||
|
type: 'open',
|
||||||
|
payload: 'showCreateUserModal'
|
||||||
|
})
|
||||||
|
}}>
|
||||||
Add new user
|
Add new user
|
||||||
</Link>
|
</Link>
|
||||||
</Box>
|
</Box>
|
||||||
<DataTable elements={elements} data={R.path(['users'])(userResponse)} />
|
<DataTable elements={elements} data={R.path(['users'])(userResponse)} />
|
||||||
<CreateUserModal
|
<CreateUserModal state={state} dispatch={dispatch} />
|
||||||
showModal={showCreateUserModal}
|
|
||||||
toggleModal={toggleCreateUserModal}
|
|
||||||
/>
|
|
||||||
<ResetPasswordModal
|
<ResetPasswordModal
|
||||||
showModal={showResetPasswordModal}
|
state={state}
|
||||||
toggleModal={toggleResetPasswordModal}
|
dispatch={dispatch}
|
||||||
user={userInfo}
|
user={userInfo}
|
||||||
requiresConfirmation={userInfo?.role === 'superuser'}
|
requiresConfirmation={userInfo?.role === 'superuser'}
|
||||||
/>
|
/>
|
||||||
<Reset2FAModal
|
<Reset2FAModal
|
||||||
showModal={showReset2FAModal}
|
state={state}
|
||||||
toggleModal={toggleReset2FAModal}
|
dispatch={dispatch}
|
||||||
user={userInfo}
|
user={userInfo}
|
||||||
requiresConfirmation={userInfo?.role === 'superuser'}
|
requiresConfirmation={userInfo?.role === 'superuser'}
|
||||||
/>
|
/>
|
||||||
<ChangeRoleModal
|
<ChangeRoleModal
|
||||||
showModal={showRoleModal}
|
state={state}
|
||||||
toggleModal={toggleRoleModal}
|
dispatch={dispatch}
|
||||||
user={userInfo}
|
user={userInfo}
|
||||||
requiresConfirmation={userInfo?.role === 'superuser'}
|
requiresConfirmation={userInfo?.role === 'superuser'}
|
||||||
/>
|
/>
|
||||||
<EnableUserModal
|
<EnableUserModal
|
||||||
showModal={showEnableUserModal}
|
state={state}
|
||||||
toggleModal={toggleEnableUserModal}
|
dispatch={dispatch}
|
||||||
user={userInfo}
|
user={userInfo}
|
||||||
requiresConfirmation={userInfo?.role === 'superuser'}
|
requiresConfirmation={userInfo?.role === 'superuser'}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,7 @@ const CHANGE_USER_ROLE = gql`
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const ChangeRoleModal = ({
|
const ChangeRoleModal = ({ state, dispatch, user, requiresConfirmation }) => {
|
||||||
showModal,
|
|
||||||
toggleModal,
|
|
||||||
user,
|
|
||||||
requiresConfirmation
|
|
||||||
}) => {
|
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
const [changeUserRole] = useMutation(CHANGE_USER_ROLE, {
|
const [changeUserRole] = useMutation(CHANGE_USER_ROLE, {
|
||||||
|
|
@ -56,18 +51,21 @@ const ChangeRoleModal = ({
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setConfirmation(null)
|
setConfirmation(null)
|
||||||
toggleModal()
|
dispatch({
|
||||||
|
type: 'close',
|
||||||
|
payload: 'showRoleModal'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(showModal && requiresConfirmation && !confirmation && (
|
(state.showRoleModal && requiresConfirmation && !confirmation && (
|
||||||
<Input2FAModal
|
<Input2FAModal
|
||||||
showModal={showModal}
|
showModal={state.showRoleModal}
|
||||||
handleClose={handleClose}
|
handleClose={handleClose}
|
||||||
setConfirmation={setConfirmation}
|
setConfirmation={setConfirmation}
|
||||||
/>
|
/>
|
||||||
)) ||
|
)) ||
|
||||||
(showModal && (
|
(state.showRoleModal && (
|
||||||
<Modal
|
<Modal
|
||||||
closeOnBackdropClick={true}
|
closeOnBackdropClick={true}
|
||||||
width={450}
|
width={450}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import { Button } from 'src/components/buttons'
|
||||||
import { TextInput, RadioGroup } from 'src/components/inputs/formik'
|
import { TextInput, RadioGroup } from 'src/components/inputs/formik'
|
||||||
import { H1, H3, Info2, P, Mono } from 'src/components/typography'
|
import { H1, H3, Info2, P, Mono } from 'src/components/typography'
|
||||||
import CopyToClipboard from 'src/pages/Transactions/CopyToClipboard'
|
import CopyToClipboard from 'src/pages/Transactions/CopyToClipboard'
|
||||||
import { URI } from 'src/utils/apollo'
|
import { urlResolver } from 'src/utils/urlResolver'
|
||||||
|
|
||||||
import styles from '../UserManagement.styles'
|
import styles from '../UserManagement.styles'
|
||||||
|
|
||||||
|
|
@ -39,7 +39,7 @@ const initialValues = {
|
||||||
role: ''
|
role: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const CreateUserModal = ({ showModal, toggleModal }) => {
|
const CreateUserModal = ({ state, dispatch }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
const [usernameField, setUsernameField] = useState('')
|
const [usernameField, setUsernameField] = useState('')
|
||||||
|
|
@ -58,12 +58,15 @@ const CreateUserModal = ({ showModal, toggleModal }) => {
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setCreateUserURL(null)
|
setCreateUserURL(null)
|
||||||
toggleModal()
|
dispatch({
|
||||||
|
type: 'close',
|
||||||
|
payload: 'showCreateUserModal'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const [createUser, { error }] = useMutation(CREATE_USER, {
|
const [createUser, { error }] = useMutation(CREATE_USER, {
|
||||||
onCompleted: ({ createRegisterToken: token }) => {
|
onCompleted: ({ createRegisterToken: token }) => {
|
||||||
setCreateUserURL(`${URI}/register?t=${token.token}`)
|
setCreateUserURL(urlResolver(`/register?t=${token.token}`))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -81,7 +84,7 @@ const CreateUserModal = ({ showModal, toggleModal }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{showModal && !createUserURL && (
|
{state.showCreateUserModal && !createUserURL && (
|
||||||
<Modal
|
<Modal
|
||||||
closeOnBackdropClick={true}
|
closeOnBackdropClick={true}
|
||||||
width={600}
|
width={600}
|
||||||
|
|
@ -137,7 +140,7 @@ const CreateUserModal = ({ showModal, toggleModal }) => {
|
||||||
</Formik>
|
</Formik>
|
||||||
</Modal>
|
</Modal>
|
||||||
)}
|
)}
|
||||||
{showModal && createUserURL && (
|
{state.showCreateUserModal && createUserURL && (
|
||||||
<Modal
|
<Modal
|
||||||
closeOnBackdropClick={true}
|
closeOnBackdropClick={true}
|
||||||
width={500}
|
width={500}
|
||||||
|
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
import Modal from 'src/components/Modal'
|
|
||||||
import { Button } from 'src/components/buttons'
|
|
||||||
import { Info2, P } from 'src/components/typography'
|
|
||||||
|
|
||||||
import styles from '../UserManagement.styles'
|
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
|
||||||
|
|
||||||
const DeleteUserModal = ({
|
|
||||||
showModal,
|
|
||||||
toggleModal,
|
|
||||||
user,
|
|
||||||
confirm,
|
|
||||||
inputConfirmToggle,
|
|
||||||
setAction
|
|
||||||
}) => {
|
|
||||||
const classes = useStyles()
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
toggleModal()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
showModal && (
|
|
||||||
<Modal
|
|
||||||
closeOnBackdropClick={true}
|
|
||||||
width={600}
|
|
||||||
height={275}
|
|
||||||
handleClose={handleClose}
|
|
||||||
open={true}>
|
|
||||||
<Info2 className={classes.modalTitle}>Delete {user.username}?</Info2>
|
|
||||||
<P className={classes.info}>
|
|
||||||
You are about to delete {user.username}. This will remove existent
|
|
||||||
sessions and revoke this user's permissions to access the system.
|
|
||||||
</P>
|
|
||||||
<P className={classes.info}>
|
|
||||||
This is a <b>PERMANENT</b> operation. Do you wish to proceed?
|
|
||||||
</P>
|
|
||||||
<div className={classes.footer}>
|
|
||||||
<Button
|
|
||||||
className={classes.submit}
|
|
||||||
onClick={() => {
|
|
||||||
if (user.role === 'superuser') {
|
|
||||||
setAction(() =>
|
|
||||||
confirm.bind(null, {
|
|
||||||
variables: {
|
|
||||||
id: user.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
inputConfirmToggle()
|
|
||||||
} else {
|
|
||||||
confirm({
|
|
||||||
variables: {
|
|
||||||
id: user.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
handleClose()
|
|
||||||
}}>
|
|
||||||
Confirm
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DeleteUserModal
|
|
||||||
|
|
@ -29,12 +29,7 @@ const DISABLE_USER = gql`
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const EnableUserModal = ({
|
const EnableUserModal = ({ state, dispatch, user, requiresConfirmation }) => {
|
||||||
showModal,
|
|
||||||
toggleModal,
|
|
||||||
user,
|
|
||||||
requiresConfirmation
|
|
||||||
}) => {
|
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
const [enableUser] = useMutation(ENABLE_USER, {
|
const [enableUser] = useMutation(ENABLE_USER, {
|
||||||
|
|
@ -47,37 +42,46 @@ const EnableUserModal = ({
|
||||||
|
|
||||||
const [confirmation, setConfirmation] = useState(null)
|
const [confirmation, setConfirmation] = useState(null)
|
||||||
|
|
||||||
|
const disable = () => {
|
||||||
|
disableUser({
|
||||||
|
variables: {
|
||||||
|
confirmationCode: confirmation,
|
||||||
|
id: user.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const enable = () => {
|
||||||
|
enableUser({
|
||||||
|
variables: {
|
||||||
|
confirmationCode: confirmation,
|
||||||
|
id: user.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
user?.enabled
|
user?.enabled ? disable() : enable()
|
||||||
? disableUser({
|
|
||||||
variables: {
|
|
||||||
confirmationCode: confirmation,
|
|
||||||
id: user.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
: enableUser({
|
|
||||||
variables: {
|
|
||||||
confirmationCode: confirmation,
|
|
||||||
id: user.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
handleClose()
|
handleClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setConfirmation(null)
|
setConfirmation(null)
|
||||||
toggleModal()
|
dispatch({
|
||||||
|
type: 'close',
|
||||||
|
payload: 'showEnableUserModal'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(showModal && requiresConfirmation && !confirmation && (
|
(state.showEnableUserModal && requiresConfirmation && !confirmation && (
|
||||||
<Input2FAModal
|
<Input2FAModal
|
||||||
showModal={showModal}
|
showModal={state.showEnableUserModal}
|
||||||
handleClose={handleClose}
|
handleClose={handleClose}
|
||||||
setConfirmation={setConfirmation}
|
setConfirmation={setConfirmation}
|
||||||
/>
|
/>
|
||||||
)) ||
|
)) ||
|
||||||
(showModal && (
|
(state.showEnableUserModal && (
|
||||||
<Modal
|
<Modal
|
||||||
closeOnBackdropClick={true}
|
closeOnBackdropClick={true}
|
||||||
width={450}
|
width={450}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import React, { useEffect, useState } from 'react'
|
||||||
import Modal from 'src/components/Modal'
|
import Modal from 'src/components/Modal'
|
||||||
import { Info2, P, Mono } from 'src/components/typography'
|
import { Info2, P, Mono } from 'src/components/typography'
|
||||||
import CopyToClipboard from 'src/pages/Transactions/CopyToClipboard'
|
import CopyToClipboard from 'src/pages/Transactions/CopyToClipboard'
|
||||||
import { URI } from 'src/utils/apollo'
|
import { urlResolver } from 'src/utils/urlResolver'
|
||||||
|
|
||||||
import styles from '../UserManagement.styles'
|
import styles from '../UserManagement.styles'
|
||||||
|
|
||||||
|
|
@ -24,12 +24,7 @@ const CREATE_RESET_2FA_TOKEN = gql`
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const Reset2FAModal = ({
|
const Reset2FAModal = ({ state, dispatch, user, requiresConfirmation }) => {
|
||||||
showModal,
|
|
||||||
toggleModal,
|
|
||||||
user,
|
|
||||||
requiresConfirmation
|
|
||||||
}) => {
|
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const [reset2FAUrl, setReset2FAUrl] = useState('')
|
const [reset2FAUrl, setReset2FAUrl] = useState('')
|
||||||
|
|
||||||
|
|
@ -37,7 +32,7 @@ const Reset2FAModal = ({
|
||||||
CREATE_RESET_2FA_TOKEN,
|
CREATE_RESET_2FA_TOKEN,
|
||||||
{
|
{
|
||||||
onCompleted: ({ createReset2FAToken: token }) => {
|
onCompleted: ({ createReset2FAToken: token }) => {
|
||||||
setReset2FAUrl(`${URI}/reset2fa?t=${token.token}`)
|
setReset2FAUrl(urlResolver(`/reset2fa?t=${token.token}`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -45,7 +40,7 @@ const Reset2FAModal = ({
|
||||||
const [confirmation, setConfirmation] = useState(null)
|
const [confirmation, setConfirmation] = useState(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
showModal &&
|
state.showReset2FAModal &&
|
||||||
(confirmation || !requiresConfirmation) &&
|
(confirmation || !requiresConfirmation) &&
|
||||||
createReset2FAToken({
|
createReset2FAToken({
|
||||||
variables: {
|
variables: {
|
||||||
|
|
@ -53,49 +48,60 @@ const Reset2FAModal = ({
|
||||||
userID: user?.id
|
userID: user?.id
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [confirmation, createReset2FAToken, requiresConfirmation, showModal, user])
|
}, [
|
||||||
|
confirmation,
|
||||||
|
createReset2FAToken,
|
||||||
|
requiresConfirmation,
|
||||||
|
state.showReset2FAModal,
|
||||||
|
user?.id
|
||||||
|
])
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setConfirmation(null)
|
setConfirmation(null)
|
||||||
toggleModal()
|
dispatch({
|
||||||
|
type: 'close',
|
||||||
|
payload: 'showReset2FAModal'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(showModal && requiresConfirmation && !confirmation && (
|
(state.showReset2FAModal && requiresConfirmation && !confirmation && (
|
||||||
<Input2FAModal
|
<Input2FAModal
|
||||||
showModal={showModal}
|
showModal={state.showReset2FAModal}
|
||||||
handleClose={handleClose}
|
handleClose={handleClose}
|
||||||
setConfirmation={setConfirmation}
|
setConfirmation={setConfirmation}
|
||||||
/>
|
/>
|
||||||
)) ||
|
)) ||
|
||||||
(showModal && (confirmation || !requiresConfirmation) && !loading && (
|
(state.showReset2FAModal &&
|
||||||
<Modal
|
(confirmation || !requiresConfirmation) &&
|
||||||
closeOnBackdropClick={true}
|
!loading && (
|
||||||
width={500}
|
<Modal
|
||||||
height={200}
|
closeOnBackdropClick={true}
|
||||||
handleClose={handleClose}
|
width={500}
|
||||||
open={true}>
|
height={200}
|
||||||
<Info2 className={classes.modalTitle}>
|
handleClose={handleClose}
|
||||||
Reset 2FA for {user.username}
|
open={true}>
|
||||||
</Info2>
|
<Info2 className={classes.modalTitle}>
|
||||||
<P className={classes.info}>
|
Reset 2FA for {user.username}
|
||||||
Safely share this link with {user.username} for a two-factor
|
</Info2>
|
||||||
authentication reset.
|
<P className={classes.info}>
|
||||||
</P>
|
Safely share this link with {user.username} for a two-factor
|
||||||
<div className={classes.addressWrapper}>
|
authentication reset.
|
||||||
<Mono className={classes.address}>
|
</P>
|
||||||
<strong>
|
<div className={classes.addressWrapper}>
|
||||||
<CopyToClipboard
|
<Mono className={classes.address}>
|
||||||
className={classes.link}
|
<strong>
|
||||||
buttonClassname={classes.copyToClipboard}
|
<CopyToClipboard
|
||||||
wrapperClassname={classes.linkWrapper}>
|
className={classes.link}
|
||||||
{reset2FAUrl}
|
buttonClassname={classes.copyToClipboard}
|
||||||
</CopyToClipboard>
|
wrapperClassname={classes.linkWrapper}>
|
||||||
</strong>
|
{reset2FAUrl}
|
||||||
</Mono>
|
</CopyToClipboard>
|
||||||
</div>
|
</strong>
|
||||||
</Modal>
|
</Mono>
|
||||||
))
|
</div>
|
||||||
|
</Modal>
|
||||||
|
))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import React, { useEffect, useState } from 'react'
|
||||||
import Modal from 'src/components/Modal'
|
import Modal from 'src/components/Modal'
|
||||||
import { Info2, P, Mono } from 'src/components/typography'
|
import { Info2, P, Mono } from 'src/components/typography'
|
||||||
import CopyToClipboard from 'src/pages/Transactions/CopyToClipboard'
|
import CopyToClipboard from 'src/pages/Transactions/CopyToClipboard'
|
||||||
import { URI } from 'src/utils/apollo'
|
import { urlResolver } from 'src/utils/urlResolver'
|
||||||
|
|
||||||
import styles from '../UserManagement.styles'
|
import styles from '../UserManagement.styles'
|
||||||
|
|
||||||
|
|
@ -28,8 +28,8 @@ const CREATE_RESET_PASSWORD_TOKEN = gql`
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const ResetPasswordModal = ({
|
const ResetPasswordModal = ({
|
||||||
showModal,
|
state,
|
||||||
toggleModal,
|
dispatch,
|
||||||
user,
|
user,
|
||||||
requiresConfirmation
|
requiresConfirmation
|
||||||
}) => {
|
}) => {
|
||||||
|
|
@ -40,7 +40,7 @@ const ResetPasswordModal = ({
|
||||||
CREATE_RESET_PASSWORD_TOKEN,
|
CREATE_RESET_PASSWORD_TOKEN,
|
||||||
{
|
{
|
||||||
onCompleted: ({ createResetPasswordToken: token }) => {
|
onCompleted: ({ createResetPasswordToken: token }) => {
|
||||||
setResetPasswordUrl(`${URI}/resetpassword?t=${token.token}`)
|
setResetPasswordUrl(urlResolver(`/resetpassword?t=${token.token}`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -48,7 +48,7 @@ const ResetPasswordModal = ({
|
||||||
const [confirmation, setConfirmation] = useState(null)
|
const [confirmation, setConfirmation] = useState(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
showModal &&
|
state.showResetPasswordModal &&
|
||||||
(confirmation || !requiresConfirmation) &&
|
(confirmation || !requiresConfirmation) &&
|
||||||
createResetPasswordToken({
|
createResetPasswordToken({
|
||||||
variables: {
|
variables: {
|
||||||
|
|
@ -59,51 +59,56 @@ const ResetPasswordModal = ({
|
||||||
}, [
|
}, [
|
||||||
confirmation,
|
confirmation,
|
||||||
createResetPasswordToken,
|
createResetPasswordToken,
|
||||||
showModal,
|
requiresConfirmation,
|
||||||
user,
|
state.showResetPasswordModal,
|
||||||
requiresConfirmation
|
user?.id
|
||||||
])
|
])
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setConfirmation(null)
|
setConfirmation(null)
|
||||||
toggleModal()
|
dispatch({
|
||||||
|
type: 'close',
|
||||||
|
payload: 'showResetPasswordModal'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(showModal && requiresConfirmation && !confirmation && (
|
(state.showResetPasswordModal && requiresConfirmation && !confirmation && (
|
||||||
<Input2FAModal
|
<Input2FAModal
|
||||||
showModal={showModal}
|
showModal={state.showResetPasswordModal}
|
||||||
handleClose={handleClose}
|
handleClose={handleClose}
|
||||||
setConfirmation={setConfirmation}
|
setConfirmation={setConfirmation}
|
||||||
/>
|
/>
|
||||||
)) ||
|
)) ||
|
||||||
(showModal && (confirmation || !requiresConfirmation) && !loading && (
|
(state.showResetPasswordModal &&
|
||||||
<Modal
|
(confirmation || !requiresConfirmation) &&
|
||||||
closeOnBackdropClick={true}
|
!loading && (
|
||||||
width={500}
|
<Modal
|
||||||
height={180}
|
closeOnBackdropClick={true}
|
||||||
handleClose={handleClose}
|
width={500}
|
||||||
open={true}>
|
height={180}
|
||||||
<Info2 className={classes.modalTitle}>
|
handleClose={handleClose}
|
||||||
Reset password for {user.username}
|
open={true}>
|
||||||
</Info2>
|
<Info2 className={classes.modalTitle}>
|
||||||
<P className={classes.info}>
|
Reset password for {user.username}
|
||||||
Safely share this link with {user.username} for a password reset.
|
</Info2>
|
||||||
</P>
|
<P className={classes.info}>
|
||||||
<div className={classes.addressWrapper}>
|
Safely share this link with {user.username} for a password reset.
|
||||||
<Mono className={classes.address}>
|
</P>
|
||||||
<strong>
|
<div className={classes.addressWrapper}>
|
||||||
<CopyToClipboard
|
<Mono className={classes.address}>
|
||||||
className={classes.link}
|
<strong>
|
||||||
buttonClassname={classes.copyToClipboard}
|
<CopyToClipboard
|
||||||
wrapperClassname={classes.linkWrapper}>
|
className={classes.link}
|
||||||
{resetPasswordUrl}
|
buttonClassname={classes.copyToClipboard}
|
||||||
</CopyToClipboard>
|
wrapperClassname={classes.linkWrapper}>
|
||||||
</strong>
|
{resetPasswordUrl}
|
||||||
</Mono>
|
</CopyToClipboard>
|
||||||
</div>
|
</strong>
|
||||||
</Modal>
|
</Mono>
|
||||||
))
|
</div>
|
||||||
|
</Modal>
|
||||||
|
))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
6
new-lamassu-admin/src/utils/urlResolver.js
Normal file
6
new-lamassu-admin/src/utils/urlResolver.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
const url =
|
||||||
|
process.env.NODE_ENV === 'development' ? 'https://localhost:3001' : ''
|
||||||
|
|
||||||
|
const urlResolver = content => `${url}${content}`
|
||||||
|
|
||||||
|
export { urlResolver }
|
||||||
Loading…
Add table
Add a link
Reference in a new issue