diff --git a/lib/new-admin/graphql/modules/authentication.js b/lib/new-admin/graphql/modules/authentication.js
index 16aec232..5520b328 100644
--- a/lib/new-admin/graphql/modules/authentication.js
+++ b/lib/new-admin/graphql/modules/authentication.js
@@ -220,17 +220,14 @@ const resetPassword = (token, userID, newPassword, context) => {
.then(() => true)
}
-const reset2FA = (token, userID, code, secret, context) => {
- const isCodeValid = otplib.authenticator.verify({ token: code, secret })
- if (!isCodeValid) throw new authErrors.InvalidTwoFactorError()
-
+const reset2FA = (token, userID, code, context) => {
return users.getUserById(userID)
.then(user => {
+ const isCodeValid = otplib.authenticator.verify({ token: code, secret: user.temp_twofa_code })
+ if (!isCodeValid) throw new authErrors.InvalidTwoFactorError()
+
destroySessionIfSameUser(context, user)
- if (user.temp_twofa_code !== secret) {
- throw new authErrors.InvalidTwoFactorError()
- }
- return users.reset2FASecret(token, user.id, secret)
+ return users.reset2FASecret(token, user.id, user.temp_twofa_code)
})
.then(() => true)
}
diff --git a/lib/new-admin/graphql/resolvers/users.resolver.js b/lib/new-admin/graphql/resolvers/users.resolver.js
index d26ca689..149a8252 100644
--- a/lib/new-admin/graphql/resolvers/users.resolver.js
+++ b/lib/new-admin/graphql/resolvers/users.resolver.js
@@ -28,7 +28,7 @@ const resolver = {
createRegisterToken: (...[, { username, role }]) => authentication.createRegisterToken(username, role),
register: (...[, { token, username, password, role }]) => authentication.register(token, username, password, role),
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)
}
}
diff --git a/lib/new-admin/graphql/types/users.type.js b/lib/new-admin/graphql/types/users.type.js
index a010ac61..9362d747 100644
--- a/lib/new-admin/graphql/types/users.type.js
+++ b/lib/new-admin/graphql/types/users.type.js
@@ -71,7 +71,7 @@ const typeDef = `
createRegisterToken(username: String!, role: String!): RegistrationToken @auth(requires: [SUPERUSER])
register(token: String!, username: String!, password: String!, role: 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
}
`
diff --git a/new-lamassu-admin/src/lamassu/App.js b/new-lamassu-admin/src/lamassu/App.js
index a2b74e3f..48373584 100644
--- a/new-lamassu-admin/src/lamassu/App.js
+++ b/new-lamassu-admin/src/lamassu/App.js
@@ -1,4 +1,4 @@
-import { useLazyQuery } from '@apollo/react-hooks'
+import { useQuery } from '@apollo/react-hooks'
import CssBaseline from '@material-ui/core/CssBaseline'
import Grid from '@material-ui/core/Grid'
import Slide from '@material-ui/core/Slide'
@@ -11,7 +11,7 @@ import {
import gql from 'graphql-tag'
import { create } from 'jss'
import extendJss from 'jss-plugin-extend'
-import React, { useContext, useEffect, useState } from 'react'
+import React, { useContext, useState } from 'react'
import {
useLocation,
useHistory,
@@ -91,17 +91,13 @@ const Main = () => {
const history = useHistory()
const { wizardTested, userData, setUserData } = useContext(AppContext)
- const [getUserData, { loading }] = useLazyQuery(GET_USER_DATA, {
+ const { loading } = useQuery(GET_USER_DATA, {
onCompleted: userResponse => {
if (!userData && userResponse?.userData)
setUserData(userResponse.userData)
}
})
- useEffect(() => {
- getUserData()
- }, [getUserData])
-
const route = location.pathname
const sidebar = hasSidebar(route)
diff --git a/new-lamassu-admin/src/pages/Authentication/Input2FAState.js b/new-lamassu-admin/src/pages/Authentication/Input2FAState.js
index 70fe8691..2cb6cce1 100644
--- a/new-lamassu-admin/src/pages/Authentication/Input2FAState.js
+++ b/new-lamassu-admin/src/pages/Authentication/Input2FAState.js
@@ -47,6 +47,19 @@ const Input2FAState = ({ state, dispatch }) => {
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 => {
dispatch({
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 = () => {
if (queryError) return 'Internal server error'
if (state.twoFAField.length !== 6 && invalidToken)
diff --git a/new-lamassu-admin/src/pages/Authentication/Register.js b/new-lamassu-admin/src/pages/Authentication/Register.js
index f529d93c..1572429f 100644
--- a/new-lamassu-admin/src/pages/Authentication/Register.js
+++ b/new-lamassu-admin/src/pages/Authentication/Register.js
@@ -69,12 +69,12 @@ const Register = () => {
const initialState = {
username: null,
role: null,
- wasSuccessful: false
+ result: ''
}
const reducer = (state, action) => {
const { type, payload } = action
- return { ...state, [type]: payload }
+ return { ...state, ...payload, result: type }
}
const [state, dispatch] = useReducer(reducer, initialState)
@@ -83,15 +83,23 @@ const Register = () => {
variables: { token: token },
onCompleted: ({ validateRegisterLink: info }) => {
if (!info) {
- dispatch({ type: 'wasSuccessful', payload: false })
+ dispatch({
+ type: 'failure'
+ })
} else {
- dispatch({ type: 'wasSuccessful', payload: true })
- dispatch({ type: 'username', payload: info.username })
- dispatch({ type: 'role', payload: info.role })
+ dispatch({
+ type: 'success',
+ payload: {
+ username: info.username,
+ role: info.role
+ }
+ })
}
},
onError: () => {
- dispatch({ type: 'wasSuccessful', payload: false })
+ dispatch({
+ type: 'failure'
+ })
}
})
@@ -127,7 +135,7 @@ const Register = () => {
Lamassu Admin
- {!loading && state.wasSuccessful && (
+ {!loading && state.result === 'success' && (
{
)}
)}
- {!loading && !state.wasSuccessful && (
+ {!loading && state.result === 'failure' && (
<>
Link has expired
>
diff --git a/new-lamassu-admin/src/pages/Authentication/Reset2FA.js b/new-lamassu-admin/src/pages/Authentication/Reset2FA.js
index f54b8b6d..b58ffc8d 100644
--- a/new-lamassu-admin/src/pages/Authentication/Reset2FA.js
+++ b/new-lamassu-admin/src/pages/Authentication/Reset2FA.js
@@ -3,7 +3,7 @@ import { makeStyles, Grid } from '@material-ui/core'
import Paper from '@material-ui/core/Paper'
import gql from 'graphql-tag'
import QRCode from 'qrcode.react'
-import React, { useState } from 'react'
+import React, { useReducer, useState } from 'react'
import { useLocation, useHistory } from 'react-router-dom'
import { ActionButton, Button } from 'src/components/buttons'
@@ -28,13 +28,8 @@ const VALIDATE_RESET_2FA_LINK = gql`
`
const RESET_2FA = gql`
- mutation reset2FA(
- $token: String!
- $userID: ID!
- $secret: String!
- $code: String!
- ) {
- reset2FA(token: $token, userID: $userID, secret: $secret, code: $code)
+ mutation reset2FA($token: String!, $userID: ID!, $code: String!) {
+ reset2FA(token: $token, userID: $userID, code: $code)
}
`
@@ -42,37 +37,52 @@ const Reset2FA = () => {
const classes = useStyles()
const history = useHistory()
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 [invalidToken, setInvalidToken] = useState(false)
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 => {
setTwoFAConfirmation(value)
setInvalidToken(false)
}
- const { error: queryError } = useQuery(VALIDATE_RESET_2FA_LINK, {
+ const { error: queryError, loading } = useQuery(VALIDATE_RESET_2FA_LINK, {
variables: { token: token },
onCompleted: ({ validateReset2FALink: info }) => {
- setLoading(false)
if (!info) {
- setSuccess(false)
+ dispatch({
+ type: 'failure'
+ })
} else {
- setUserID(info.user_id)
- setSecret(info.secret)
- setOtpauth(info.otpauth)
- setSuccess(true)
+ dispatch({
+ type: 'success',
+ payload: {
+ userID: info.user_id,
+ secret: info.secret,
+ otpauth: info.otpauth
+ }
+ })
}
},
onError: () => {
- setLoading(false)
- setSuccess(false)
+ dispatch({
+ type: 'failure'
+ })
}
})
@@ -107,7 +117,7 @@ const Reset2FA = () => {
Lamassu Admin
- {!isLoading && wasSuccessful && (
+ {!loading && state.result === 'success' && (
<>
@@ -117,7 +127,11 @@ const Reset2FA = () => {
-
+
@@ -127,7 +141,7 @@ const Reset2FA = () => {
className={
isShowing ? classes.secret : classes.hiddenSecret
}>
- {secret}
+ {state.secret}
{
reset2FA({
variables: {
token: token,
- userID: userID,
- secret: secret,
+ userID: state.userID,
code: twoFAConfirmation
}
})
@@ -172,7 +185,7 @@ const Reset2FA = () => {
>
)}
- {!isLoading && !wasSuccessful && (
+ {!loading && state.result === 'failure' && (
<>
Link has expired
>
diff --git a/new-lamassu-admin/src/pages/UserManagement/UserManagement.js b/new-lamassu-admin/src/pages/UserManagement/UserManagement.js
index 12688a90..5d7ec85c 100644
--- a/new-lamassu-admin/src/pages/UserManagement/UserManagement.js
+++ b/new-lamassu-admin/src/pages/UserManagement/UserManagement.js
@@ -2,7 +2,7 @@ import { useQuery } from '@apollo/react-hooks'
import { makeStyles, Box, Chip } from '@material-ui/core'
import gql from 'graphql-tag'
import * as R from 'ramda'
-import React, { useState, useContext } from 'react'
+import React, { useReducer, useState, useContext } from 'react'
import AppContext from 'src/AppContext'
import { Link } from 'src/components/buttons'
@@ -39,23 +39,27 @@ const Users = () => {
const { data: userResponse } = useQuery(GET_USERS)
- const [showCreateUserModal, setShowCreateUserModal] = useState(false)
- const toggleCreateUserModal = () =>
- setShowCreateUserModal(!showCreateUserModal)
+ const initialState = {
+ showCreateUserModal: false,
+ showResetPasswordModal: false,
+ showReset2FAModal: false,
+ showRoleModal: false,
+ showEnableUserModal: false
+ }
- const [showResetPasswordModal, setShowResetPasswordModal] = useState(false)
- const toggleResetPasswordModal = () =>
- setShowResetPasswordModal(!showResetPasswordModal)
+ const reducer = (_, action) => {
+ const { type, payload } = action
+ switch (type) {
+ case 'close':
+ return initialState
+ case 'open':
+ return { ...initialState, [payload]: true }
+ default:
+ return initialState
+ }
+ }
- const [showReset2FAModal, setShowReset2FAModal] = useState(false)
- const toggleReset2FAModal = () => setShowReset2FAModal(!showReset2FAModal)
-
- const [showRoleModal, setShowRoleModal] = useState(false)
- const toggleRoleModal = () => setShowRoleModal(!showRoleModal)
-
- const [showEnableUserModal, setShowEnableUserModal] = useState(false)
- const toggleEnableUserModal = () =>
- setShowEnableUserModal(!showEnableUserModal)
+ const [state, dispatch] = useReducer(reducer, initialState)
const [userInfo, setUserInfo] = useState(null)
@@ -103,7 +107,10 @@ const Users = () => {
checked={u.role === 'superuser'}
onClick={() => {
setUserInfo(u)
- toggleRoleModal()
+ dispatch({
+ type: 'open',
+ payload: 'showRoleModal'
+ })
}}
value={u.role === 'superuser'}
/>
@@ -130,7 +137,10 @@ const Users = () => {
className={classes.actionChip}
onClick={() => {
setUserInfo(u)
- toggleResetPasswordModal()
+ dispatch({
+ type: 'open',
+ payload: 'showResetPasswordModal'
+ })
}}
/>
{
className={classes.actionChip}
onClick={() => {
setUserInfo(u)
- toggleReset2FAModal()
+ dispatch({
+ type: 'open',
+ payload: 'showReset2FAModal'
+ })
}}
/>
>
@@ -157,7 +170,10 @@ const Users = () => {
checked={u.enabled}
onClick={() => {
setUserInfo(u)
- toggleEnableUserModal()
+ dispatch({
+ type: 'open',
+ payload: 'showEnableUserModal'
+ })
}}
value={u.enabled}
/>
@@ -174,36 +190,40 @@ const Users = () => {
className={classes.tableWidth}
display="flex"
justifyContent="flex-end">
-
+ {
+ dispatch({
+ type: 'open',
+ payload: 'showCreateUserModal'
+ })
+ }}>
Add new user
-
+
diff --git a/new-lamassu-admin/src/pages/UserManagement/modals/ChangeRoleModal.js b/new-lamassu-admin/src/pages/UserManagement/modals/ChangeRoleModal.js
index 265f02f6..3704a6bb 100644
--- a/new-lamassu-admin/src/pages/UserManagement/modals/ChangeRoleModal.js
+++ b/new-lamassu-admin/src/pages/UserManagement/modals/ChangeRoleModal.js
@@ -29,12 +29,7 @@ const CHANGE_USER_ROLE = gql`
const useStyles = makeStyles(styles)
-const ChangeRoleModal = ({
- showModal,
- toggleModal,
- user,
- requiresConfirmation
-}) => {
+const ChangeRoleModal = ({ state, dispatch, user, requiresConfirmation }) => {
const classes = useStyles()
const [changeUserRole] = useMutation(CHANGE_USER_ROLE, {
@@ -56,18 +51,21 @@ const ChangeRoleModal = ({
const handleClose = () => {
setConfirmation(null)
- toggleModal()
+ dispatch({
+ type: 'close',
+ payload: 'showRoleModal'
+ })
}
return (
- (showModal && requiresConfirmation && !confirmation && (
+ (state.showRoleModal && requiresConfirmation && !confirmation && (
)) ||
- (showModal && (
+ (state.showRoleModal && (
{
+const CreateUserModal = ({ state, dispatch }) => {
const classes = useStyles()
const [usernameField, setUsernameField] = useState('')
@@ -58,12 +58,15 @@ const CreateUserModal = ({ showModal, toggleModal }) => {
const handleClose = () => {
setCreateUserURL(null)
- toggleModal()
+ dispatch({
+ type: 'close',
+ payload: 'showCreateUserModal'
+ })
}
const [createUser, { error }] = useMutation(CREATE_USER, {
onCompleted: ({ createRegisterToken: token }) => {
- setCreateUserURL(`${URI}/register?t=${token.token}`)
+ setCreateUserURL(urlResolver(`/register?t=${token.token}`))
}
})
@@ -81,7 +84,7 @@ const CreateUserModal = ({ showModal, toggleModal }) => {
return (
<>
- {showModal && !createUserURL && (
+ {state.showCreateUserModal && !createUserURL && (
{
)}
- {showModal && createUserURL && (
+ {state.showCreateUserModal && createUserURL && (
{
- const classes = useStyles()
-
- const handleClose = () => {
- toggleModal()
- }
-
- return (
- showModal && (
-
- Delete {user.username}?
-
- You are about to delete {user.username}. This will remove existent
- sessions and revoke this user's permissions to access the system.
-
-
- This is a PERMANENT operation. Do you wish to proceed?
-
-
-
-
-
- )
- )
-}
-
-export default DeleteUserModal
diff --git a/new-lamassu-admin/src/pages/UserManagement/modals/EnableUserModal.js b/new-lamassu-admin/src/pages/UserManagement/modals/EnableUserModal.js
index 8bf06d37..5757f6ea 100644
--- a/new-lamassu-admin/src/pages/UserManagement/modals/EnableUserModal.js
+++ b/new-lamassu-admin/src/pages/UserManagement/modals/EnableUserModal.js
@@ -29,12 +29,7 @@ const DISABLE_USER = gql`
const useStyles = makeStyles(styles)
-const EnableUserModal = ({
- showModal,
- toggleModal,
- user,
- requiresConfirmation
-}) => {
+const EnableUserModal = ({ state, dispatch, user, requiresConfirmation }) => {
const classes = useStyles()
const [enableUser] = useMutation(ENABLE_USER, {
@@ -47,37 +42,46 @@ const EnableUserModal = ({
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 = () => {
- user?.enabled
- ? disableUser({
- variables: {
- confirmationCode: confirmation,
- id: user.id
- }
- })
- : enableUser({
- variables: {
- confirmationCode: confirmation,
- id: user.id
- }
- })
+ user?.enabled ? disable() : enable()
handleClose()
}
const handleClose = () => {
setConfirmation(null)
- toggleModal()
+ dispatch({
+ type: 'close',
+ payload: 'showEnableUserModal'
+ })
}
return (
- (showModal && requiresConfirmation && !confirmation && (
+ (state.showEnableUserModal && requiresConfirmation && !confirmation && (
)) ||
- (showModal && (
+ (state.showEnableUserModal && (
{
+const Reset2FAModal = ({ state, dispatch, user, requiresConfirmation }) => {
const classes = useStyles()
const [reset2FAUrl, setReset2FAUrl] = useState('')
@@ -37,7 +32,7 @@ const Reset2FAModal = ({
CREATE_RESET_2FA_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)
useEffect(() => {
- showModal &&
+ state.showReset2FAModal &&
(confirmation || !requiresConfirmation) &&
createReset2FAToken({
variables: {
@@ -53,49 +48,60 @@ const Reset2FAModal = ({
userID: user?.id
}
})
- }, [confirmation, createReset2FAToken, requiresConfirmation, showModal, user])
+ }, [
+ confirmation,
+ createReset2FAToken,
+ requiresConfirmation,
+ state.showReset2FAModal,
+ user?.id
+ ])
const handleClose = () => {
setConfirmation(null)
- toggleModal()
+ dispatch({
+ type: 'close',
+ payload: 'showReset2FAModal'
+ })
}
return (
- (showModal && requiresConfirmation && !confirmation && (
+ (state.showReset2FAModal && requiresConfirmation && !confirmation && (
)) ||
- (showModal && (confirmation || !requiresConfirmation) && !loading && (
-
-
- Reset 2FA for {user.username}
-
-
- Safely share this link with {user.username} for a two-factor
- authentication reset.
-
-
-
-
-
- {reset2FAUrl}
-
-
-
-
-
- ))
+ (state.showReset2FAModal &&
+ (confirmation || !requiresConfirmation) &&
+ !loading && (
+
+
+ Reset 2FA for {user.username}
+
+
+ Safely share this link with {user.username} for a two-factor
+ authentication reset.
+
+
+
+
+
+ {reset2FAUrl}
+
+
+
+
+
+ ))
)
}
diff --git a/new-lamassu-admin/src/pages/UserManagement/modals/ResetPasswordModal.js b/new-lamassu-admin/src/pages/UserManagement/modals/ResetPasswordModal.js
index d7590bce..c0875e5b 100644
--- a/new-lamassu-admin/src/pages/UserManagement/modals/ResetPasswordModal.js
+++ b/new-lamassu-admin/src/pages/UserManagement/modals/ResetPasswordModal.js
@@ -6,7 +6,7 @@ import React, { useEffect, useState } from 'react'
import Modal from 'src/components/Modal'
import { Info2, P, Mono } from 'src/components/typography'
import CopyToClipboard from 'src/pages/Transactions/CopyToClipboard'
-import { URI } from 'src/utils/apollo'
+import { urlResolver } from 'src/utils/urlResolver'
import styles from '../UserManagement.styles'
@@ -28,8 +28,8 @@ const CREATE_RESET_PASSWORD_TOKEN = gql`
const useStyles = makeStyles(styles)
const ResetPasswordModal = ({
- showModal,
- toggleModal,
+ state,
+ dispatch,
user,
requiresConfirmation
}) => {
@@ -40,7 +40,7 @@ const ResetPasswordModal = ({
CREATE_RESET_PASSWORD_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)
useEffect(() => {
- showModal &&
+ state.showResetPasswordModal &&
(confirmation || !requiresConfirmation) &&
createResetPasswordToken({
variables: {
@@ -59,51 +59,56 @@ const ResetPasswordModal = ({
}, [
confirmation,
createResetPasswordToken,
- showModal,
- user,
- requiresConfirmation
+ requiresConfirmation,
+ state.showResetPasswordModal,
+ user?.id
])
const handleClose = () => {
setConfirmation(null)
- toggleModal()
+ dispatch({
+ type: 'close',
+ payload: 'showResetPasswordModal'
+ })
}
return (
- (showModal && requiresConfirmation && !confirmation && (
+ (state.showResetPasswordModal && requiresConfirmation && !confirmation && (
)) ||
- (showModal && (confirmation || !requiresConfirmation) && !loading && (
-
-
- Reset password for {user.username}
-
-
- Safely share this link with {user.username} for a password reset.
-
-
-
-
-
- {resetPasswordUrl}
-
-
-
-
-
- ))
+ (state.showResetPasswordModal &&
+ (confirmation || !requiresConfirmation) &&
+ !loading && (
+
+
+ Reset password for {user.username}
+
+
+ Safely share this link with {user.username} for a password reset.
+
+
+
+
+
+ {resetPasswordUrl}
+
+
+
+
+
+ ))
)
}
diff --git a/new-lamassu-admin/src/utils/urlResolver.js b/new-lamassu-admin/src/utils/urlResolver.js
new file mode 100644
index 00000000..5e0785bb
--- /dev/null
+++ b/new-lamassu-admin/src/utils/urlResolver.js
@@ -0,0 +1,6 @@
+const url =
+ process.env.NODE_ENV === 'development' ? 'https://localhost:3001' : ''
+
+const urlResolver = content => `${url}${content}`
+
+export { urlResolver }