refactor: full refactor of user management
This commit is contained in:
parent
bbc37c0202
commit
9d028897bd
10 changed files with 328 additions and 225 deletions
|
|
@ -29,7 +29,7 @@ function authenticateUser(username, password) {
|
||||||
|
|
||||||
const getUserData = context => {
|
const getUserData = context => {
|
||||||
const lidCookie = context.req.cookies && context.req.cookies.lid
|
const lidCookie = context.req.cookies && context.req.cookies.lid
|
||||||
if (!lidCookie) throw new AuthenticationError()
|
if (!lidCookie) return
|
||||||
|
|
||||||
const user = context.req.session.user
|
const user = context.req.session.user
|
||||||
return user
|
return user
|
||||||
|
|
@ -145,26 +145,78 @@ const setup2FA = (username, password, rememberMe, secret, codeConfirmation, cont
|
||||||
context.req.session.user = finalUser
|
context.req.session.user = finalUser
|
||||||
if (rememberMe) context.req.session.cookie.maxAge = REMEMBER_ME_AGE
|
if (rememberMe) context.req.session.cookie.maxAge = REMEMBER_ME_AGE
|
||||||
|
|
||||||
return true
|
return users.save2FASecret(user.id, secret)
|
||||||
})
|
})
|
||||||
|
.then(() => true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const createResetPasswordToken = userID => {
|
const changeUserRole = (code, id, newRole, context) => {
|
||||||
return users.getUserById(userID)
|
const action = (id, newRole) => users.changeUserRole(id, newRole)
|
||||||
.then(user => {
|
|
||||||
if (!user) throw new authErrors.InvalidCredentialsError()
|
if (!code) {
|
||||||
return users.createAuthToken(user.id, 'reset_password')
|
return action(id, newRole)
|
||||||
})
|
}
|
||||||
.catch(err => console.error(err))
|
|
||||||
|
return confirm2FA(code, context)
|
||||||
|
.then(() => action(id, newRole))
|
||||||
}
|
}
|
||||||
|
|
||||||
const createReset2FAToken = userID => {
|
const enableUser = (code, id, context) => {
|
||||||
return users.getUserById(userID)
|
const action = id => users.enableUser(id)
|
||||||
.then(user => {
|
|
||||||
if (!user) throw new authErrors.InvalidCredentialsError()
|
if (!code) {
|
||||||
return users.createAuthToken(user.id, 'reset_twofa')
|
return action(id)
|
||||||
})
|
}
|
||||||
.catch(err => console.error(err))
|
|
||||||
|
return confirm2FA(code, context)
|
||||||
|
.then(() => action(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
const disableUser = (code, id, context) => {
|
||||||
|
const action = id => users.disableUser(id)
|
||||||
|
|
||||||
|
if (!code) {
|
||||||
|
return action(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return confirm2FA(code, context)
|
||||||
|
.then(() => action(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
const createResetPasswordToken = (code, userID, context) => {
|
||||||
|
const action = userID => {
|
||||||
|
return users.getUserById(userID)
|
||||||
|
.then(user => {
|
||||||
|
if (!user) throw new authErrors.InvalidCredentialsError()
|
||||||
|
return users.createAuthToken(user.id, 'reset_password')
|
||||||
|
})
|
||||||
|
.catch(err => console.error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!code) {
|
||||||
|
return action(userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return confirm2FA(code, context)
|
||||||
|
.then(() => action(userID))
|
||||||
|
}
|
||||||
|
|
||||||
|
const createReset2FAToken = (code, userID, context) => {
|
||||||
|
const action = userID => {
|
||||||
|
return users.getUserById(userID)
|
||||||
|
.then(user => {
|
||||||
|
if (!user) throw new authErrors.InvalidCredentialsError()
|
||||||
|
return users.createAuthToken(user.id, 'reset_twofa')
|
||||||
|
})
|
||||||
|
.catch(err => console.error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!code) {
|
||||||
|
return action(userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return confirm2FA(code, context)
|
||||||
|
.then(() => action(userID))
|
||||||
}
|
}
|
||||||
|
|
||||||
const createRegisterToken = (username, role) => {
|
const createRegisterToken = (username, role) => {
|
||||||
|
|
@ -220,6 +272,9 @@ module.exports = {
|
||||||
login,
|
login,
|
||||||
input2FA,
|
input2FA,
|
||||||
setup2FA,
|
setup2FA,
|
||||||
|
changeUserRole,
|
||||||
|
enableUser,
|
||||||
|
disableUser,
|
||||||
createResetPasswordToken,
|
createResetPasswordToken,
|
||||||
createReset2FAToken,
|
createReset2FAToken,
|
||||||
createRegisterToken,
|
createRegisterToken,
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,16 @@ const resolver = {
|
||||||
validateReset2FALink: (...[, { token }]) => authentication.validateReset2FALink(token)
|
validateReset2FALink: (...[, { token }]) => authentication.validateReset2FALink(token)
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
enableUser: (...[, { id }]) => users.enableUser(id),
|
enableUser: (...[, { confirmationCode, id }, context]) => authentication.enableUser(confirmationCode, id, context),
|
||||||
disableUser: (...[, { id }]) => users.disableUser(id),
|
disableUser: (...[, { confirmationCode, id }, context]) => authentication.disableUser(confirmationCode, id, context),
|
||||||
deleteSession: (...[, { sid }, context]) => authentication.deleteSession(sid, context),
|
deleteSession: (...[, { sid }, context]) => authentication.deleteSession(sid, context),
|
||||||
deleteUserSessions: (...[, { username }]) => sessionManager.deleteSessionsByUsername(username),
|
deleteUserSessions: (...[, { username }]) => sessionManager.deleteSessionsByUsername(username),
|
||||||
changeUserRole: (...[, { id, newRole }]) => users.changeUserRole(id, newRole),
|
changeUserRole: (...[, { confirmationCode, id, newRole }, context]) => authentication.changeUserRole(confirmationCode, id, newRole, context),
|
||||||
login: (...[, { username, password }]) => authentication.login(username, password),
|
login: (...[, { username, password }]) => authentication.login(username, password),
|
||||||
input2FA: (...[, { username, password, rememberMe, code }, context]) => authentication.input2FA(username, password, rememberMe, code, context),
|
input2FA: (...[, { username, password, rememberMe, code }, context]) => authentication.input2FA(username, password, rememberMe, code, context),
|
||||||
setup2FA: (...[, { username, password, rememberMe, secret, codeConfirmation }, context]) => authentication.setup2FA(username, password, rememberMe, secret, codeConfirmation, context),
|
setup2FA: (...[, { username, password, rememberMe, secret, codeConfirmation }, context]) => authentication.setup2FA(username, password, rememberMe, secret, codeConfirmation, context),
|
||||||
createResetPasswordToken: (...[, { userID }]) => authentication.createResetPasswordToken(userID),
|
createResetPasswordToken: (...[, { confirmationCode, userID }, context]) => authentication.createResetPasswordToken(confirmationCode, userID, context),
|
||||||
createReset2FAToken: (...[, { userID }]) => authentication.createReset2FAToken(userID),
|
createReset2FAToken: (...[, { confirmationCode, userID }, context]) => authentication.createReset2FAToken(confirmationCode, userID, context),
|
||||||
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),
|
||||||
|
|
|
||||||
|
|
@ -57,17 +57,17 @@ const typeDef = `
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
enableUser(id: ID!): User @auth(requires: [SUPERUSER])
|
enableUser(confirmationCode: String, id: ID!): User @auth(requires: [SUPERUSER])
|
||||||
disableUser(id: ID!): User @auth(requires: [SUPERUSER])
|
disableUser(confirmationCode: String, id: ID!): User @auth(requires: [SUPERUSER])
|
||||||
deleteSession(sid: String!): UserSession @auth(requires: [SUPERUSER])
|
deleteSession(sid: String!): UserSession @auth(requires: [SUPERUSER])
|
||||||
deleteUserSessions(username: String!): [UserSession] @auth(requires: [SUPERUSER])
|
deleteUserSessions(username: String!): [UserSession] @auth(requires: [SUPERUSER])
|
||||||
changeUserRole(id: ID!, newRole: String!): User @auth(requires: [SUPERUSER])
|
changeUserRole(confirmationCode: String, id: ID!, newRole: String!): User @auth(requires: [SUPERUSER])
|
||||||
toggleUserEnable(id: ID!): User @auth(requires: [SUPERUSER])
|
toggleUserEnable(id: ID!): User @auth(requires: [SUPERUSER])
|
||||||
login(username: String!, password: String!): String
|
login(username: String!, password: String!): String
|
||||||
input2FA(username: String!, password: String!, code: String!, rememberMe: Boolean!): Boolean
|
input2FA(username: String!, password: String!, code: String!, rememberMe: Boolean!): Boolean
|
||||||
setup2FA(username: String!, password: String!, rememberMe: Boolean!, secret: String!, codeConfirmation: String!): Boolean
|
setup2FA(username: String!, password: String!, rememberMe: Boolean!, secret: String!, codeConfirmation: String!): Boolean
|
||||||
createResetPasswordToken(userID: ID!): ResetToken @auth(requires: [SUPERUSER])
|
createResetPasswordToken(confirmationCode: String, userID: ID!): ResetToken @auth(requires: [SUPERUSER])
|
||||||
createReset2FAToken(userID: ID!): ResetToken @auth(requires: [SUPERUSER])
|
createReset2FAToken(confirmationCode: String, userID: ID!): ResetToken @auth(requires: [SUPERUSER])
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { CodeInput } from 'src/components/inputs/base'
|
||||||
import { H2, P } from 'src/components/typography'
|
import { H2, P } from 'src/components/typography'
|
||||||
|
|
||||||
import styles from './Login.styles'
|
import styles from './Login.styles'
|
||||||
|
import { STATES } from './states'
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
|
|
@ -47,7 +48,12 @@ const Input2FAState = ({ state, dispatch }) => {
|
||||||
const [invalidToken, setInvalidToken] = useState(false)
|
const [invalidToken, setInvalidToken] = useState(false)
|
||||||
|
|
||||||
const handle2FAChange = value => {
|
const handle2FAChange = value => {
|
||||||
dispatch({ type: 'twoFAField', payload: value })
|
dispatch({
|
||||||
|
type: STATES.INPUT_2FA,
|
||||||
|
payload: {
|
||||||
|
twoFAField: value
|
||||||
|
}
|
||||||
|
})
|
||||||
setInvalidToken(false)
|
setInvalidToken(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
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'
|
||||||
|
|
@ -14,7 +14,6 @@ import styles from './UserManagement.styles'
|
||||||
import ChangeRoleModal from './modals/ChangeRoleModal'
|
import ChangeRoleModal from './modals/ChangeRoleModal'
|
||||||
import CreateUserModal from './modals/CreateUserModal'
|
import CreateUserModal from './modals/CreateUserModal'
|
||||||
import EnableUserModal from './modals/EnableUserModal'
|
import EnableUserModal from './modals/EnableUserModal'
|
||||||
import Input2FAModal from './modals/Input2FAModal'
|
|
||||||
import Reset2FAModal from './modals/Reset2FAModal'
|
import Reset2FAModal from './modals/Reset2FAModal'
|
||||||
import ResetPasswordModal from './modals/ResetPasswordModal'
|
import ResetPasswordModal from './modals/ResetPasswordModal'
|
||||||
|
|
||||||
|
|
@ -34,98 +33,21 @@ const GET_USERS = gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const CHANGE_USER_ROLE = gql`
|
|
||||||
mutation changeUserRole($id: ID!, $newRole: String!) {
|
|
||||||
changeUserRole(id: $id, newRole: $newRole) {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const ENABLE_USER = gql`
|
|
||||||
mutation enableUser($id: ID!) {
|
|
||||||
enableUser(id: $id) {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const DISABLE_USER = gql`
|
|
||||||
mutation disableUser($id: ID!) {
|
|
||||||
disableUser(id: $id) {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const CREATE_RESET_PASSWORD_TOKEN = gql`
|
|
||||||
mutation createResetPasswordToken($userID: ID!) {
|
|
||||||
createResetPasswordToken(userID: $userID) {
|
|
||||||
token
|
|
||||||
user_id
|
|
||||||
expire
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const CREATE_RESET_2FA_TOKEN = gql`
|
|
||||||
mutation createReset2FAToken($userID: ID!) {
|
|
||||||
createReset2FAToken(userID: $userID) {
|
|
||||||
token
|
|
||||||
user_id
|
|
||||||
expire
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const Users = () => {
|
const Users = () => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
const { userData } = useContext(AppContext)
|
const { userData } = useContext(AppContext)
|
||||||
|
|
||||||
const { data: userResponse } = useQuery(GET_USERS)
|
const { data: userResponse } = useQuery(GET_USERS)
|
||||||
|
|
||||||
const [changeUserRole] = useMutation(CHANGE_USER_ROLE, {
|
|
||||||
refetchQueries: () => ['users']
|
|
||||||
})
|
|
||||||
|
|
||||||
const [enableUser] = useMutation(ENABLE_USER, {
|
|
||||||
refetchQueries: () => ['users']
|
|
||||||
})
|
|
||||||
|
|
||||||
const [disableUser] = useMutation(DISABLE_USER, {
|
|
||||||
refetchQueries: () => ['users']
|
|
||||||
})
|
|
||||||
|
|
||||||
const [createResetPasswordToken] = useMutation(CREATE_RESET_PASSWORD_TOKEN, {
|
|
||||||
onCompleted: ({ createResetPasswordToken: token }) => {
|
|
||||||
setResetPasswordUrl(
|
|
||||||
`https://localhost:3001/resetpassword?t=${token.token}`
|
|
||||||
)
|
|
||||||
toggleResetPasswordModal()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const [createReset2FAToken] = useMutation(CREATE_RESET_2FA_TOKEN, {
|
|
||||||
onCompleted: ({ createReset2FAToken: token }) => {
|
|
||||||
setReset2FAUrl(`https://localhost:3001/reset2fa?t=${token.token}`)
|
|
||||||
toggleReset2FAModal()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const [userInfo, setUserInfo] = useState(null)
|
|
||||||
|
|
||||||
const [showCreateUserModal, setShowCreateUserModal] = useState(false)
|
const [showCreateUserModal, setShowCreateUserModal] = useState(false)
|
||||||
const toggleCreateUserModal = () =>
|
const toggleCreateUserModal = () =>
|
||||||
setShowCreateUserModal(!showCreateUserModal)
|
setShowCreateUserModal(!showCreateUserModal)
|
||||||
|
|
||||||
const [showResetPasswordModal, setShowResetPasswordModal] = useState(false)
|
const [showResetPasswordModal, setShowResetPasswordModal] = useState(false)
|
||||||
const [resetPasswordUrl, setResetPasswordUrl] = useState('')
|
|
||||||
const toggleResetPasswordModal = () =>
|
const toggleResetPasswordModal = () =>
|
||||||
setShowResetPasswordModal(!showResetPasswordModal)
|
setShowResetPasswordModal(!showResetPasswordModal)
|
||||||
|
|
||||||
const [showReset2FAModal, setShowReset2FAModal] = useState(false)
|
const [showReset2FAModal, setShowReset2FAModal] = useState(false)
|
||||||
const [reset2FAUrl, setReset2FAUrl] = useState('')
|
|
||||||
const toggleReset2FAModal = () => setShowReset2FAModal(!showReset2FAModal)
|
const toggleReset2FAModal = () => setShowReset2FAModal(!showReset2FAModal)
|
||||||
|
|
||||||
const [showRoleModal, setShowRoleModal] = useState(false)
|
const [showRoleModal, setShowRoleModal] = useState(false)
|
||||||
|
|
@ -135,11 +57,7 @@ const Users = () => {
|
||||||
const toggleEnableUserModal = () =>
|
const toggleEnableUserModal = () =>
|
||||||
setShowEnableUserModal(!showEnableUserModal)
|
setShowEnableUserModal(!showEnableUserModal)
|
||||||
|
|
||||||
const [showInputConfirmModal, setShowInputConfirmModal] = useState(false)
|
const [userInfo, setUserInfo] = useState(null)
|
||||||
const toggleInputConfirmModal = () =>
|
|
||||||
setShowInputConfirmModal(!showInputConfirmModal)
|
|
||||||
|
|
||||||
const [action, setAction] = useState(null)
|
|
||||||
|
|
||||||
const elements = [
|
const elements = [
|
||||||
{
|
{
|
||||||
|
|
@ -212,22 +130,7 @@ const Users = () => {
|
||||||
className={classes.actionChip}
|
className={classes.actionChip}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setUserInfo(u)
|
setUserInfo(u)
|
||||||
if (u.role === 'superuser') {
|
toggleResetPasswordModal()
|
||||||
setAction(() =>
|
|
||||||
createResetPasswordToken.bind(null, {
|
|
||||||
variables: {
|
|
||||||
userID: u.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
toggleInputConfirmModal()
|
|
||||||
} else {
|
|
||||||
createResetPasswordToken({
|
|
||||||
variables: {
|
|
||||||
userID: u.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Chip
|
<Chip
|
||||||
|
|
@ -236,22 +139,7 @@ const Users = () => {
|
||||||
className={classes.actionChip}
|
className={classes.actionChip}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setUserInfo(u)
|
setUserInfo(u)
|
||||||
if (u.role === 'superuser') {
|
toggleReset2FAModal()
|
||||||
setAction(() => () =>
|
|
||||||
createReset2FAToken({
|
|
||||||
variables: {
|
|
||||||
userID: u.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
toggleInputConfirmModal()
|
|
||||||
} else {
|
|
||||||
createReset2FAToken({
|
|
||||||
variables: {
|
|
||||||
userID: u.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|
@ -298,35 +186,26 @@ const Users = () => {
|
||||||
<ResetPasswordModal
|
<ResetPasswordModal
|
||||||
showModal={showResetPasswordModal}
|
showModal={showResetPasswordModal}
|
||||||
toggleModal={toggleResetPasswordModal}
|
toggleModal={toggleResetPasswordModal}
|
||||||
resetPasswordURL={resetPasswordUrl}
|
|
||||||
user={userInfo}
|
user={userInfo}
|
||||||
|
requiresConfirmation={userInfo?.role === 'superuser'}
|
||||||
/>
|
/>
|
||||||
<Reset2FAModal
|
<Reset2FAModal
|
||||||
showModal={showReset2FAModal}
|
showModal={showReset2FAModal}
|
||||||
toggleModal={toggleReset2FAModal}
|
toggleModal={toggleReset2FAModal}
|
||||||
reset2FAURL={reset2FAUrl}
|
|
||||||
user={userInfo}
|
user={userInfo}
|
||||||
|
requiresConfirmation={userInfo?.role === 'superuser'}
|
||||||
/>
|
/>
|
||||||
<ChangeRoleModal
|
<ChangeRoleModal
|
||||||
showModal={showRoleModal}
|
showModal={showRoleModal}
|
||||||
toggleModal={toggleRoleModal}
|
toggleModal={toggleRoleModal}
|
||||||
user={userInfo}
|
user={userInfo}
|
||||||
confirm={changeUserRole}
|
requiresConfirmation={userInfo?.role === 'superuser'}
|
||||||
inputConfirmToggle={toggleInputConfirmModal}
|
|
||||||
setAction={setAction}
|
|
||||||
/>
|
/>
|
||||||
<EnableUserModal
|
<EnableUserModal
|
||||||
showModal={showEnableUserModal}
|
showModal={showEnableUserModal}
|
||||||
toggleModal={toggleEnableUserModal}
|
toggleModal={toggleEnableUserModal}
|
||||||
user={userInfo}
|
user={userInfo}
|
||||||
confirm={userInfo?.enabled ? disableUser : enableUser}
|
requiresConfirmation={userInfo?.role === 'superuser'}
|
||||||
inputConfirmToggle={toggleInputConfirmModal}
|
|
||||||
setAction={setAction}
|
|
||||||
/>
|
|
||||||
<Input2FAModal
|
|
||||||
showModal={showInputConfirmModal}
|
|
||||||
toggleModal={toggleInputConfirmModal}
|
|
||||||
action={action}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
import { useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import React from 'react'
|
import gql from 'graphql-tag'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import Modal from 'src/components/Modal'
|
import Modal from 'src/components/Modal'
|
||||||
import { Button } from 'src/components/buttons'
|
import { Button } from 'src/components/buttons'
|
||||||
|
|
@ -7,24 +9,65 @@ import { Info2, P } from 'src/components/typography'
|
||||||
|
|
||||||
import styles from '../UserManagement.styles'
|
import styles from '../UserManagement.styles'
|
||||||
|
|
||||||
|
import Input2FAModal from './Input2FAModal'
|
||||||
|
|
||||||
|
const CHANGE_USER_ROLE = gql`
|
||||||
|
mutation changeUserRole(
|
||||||
|
$confirmationCode: String
|
||||||
|
$id: ID!
|
||||||
|
$newRole: String!
|
||||||
|
) {
|
||||||
|
changeUserRole(
|
||||||
|
confirmationCode: $confirmationCode
|
||||||
|
id: $id
|
||||||
|
newRole: $newRole
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const ChangeRoleModal = ({
|
const ChangeRoleModal = ({
|
||||||
showModal,
|
showModal,
|
||||||
toggleModal,
|
toggleModal,
|
||||||
user,
|
user,
|
||||||
confirm,
|
requiresConfirmation
|
||||||
inputConfirmToggle,
|
|
||||||
setAction
|
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const [changeUserRole] = useMutation(CHANGE_USER_ROLE, {
|
||||||
|
refetchQueries: () => ['users']
|
||||||
|
})
|
||||||
|
|
||||||
|
const [confirmation, setConfirmation] = useState(null)
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
changeUserRole({
|
||||||
|
variables: {
|
||||||
|
confirmationCode: confirmation,
|
||||||
|
id: user.id,
|
||||||
|
newRole: user.role === 'superuser' ? 'user' : 'superuser'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
handleClose()
|
||||||
|
}
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
|
setConfirmation(null)
|
||||||
toggleModal()
|
toggleModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
showModal && (
|
(showModal && requiresConfirmation && !confirmation && (
|
||||||
|
<Input2FAModal
|
||||||
|
showModal={showModal}
|
||||||
|
handleClose={handleClose}
|
||||||
|
setConfirmation={setConfirmation}
|
||||||
|
/>
|
||||||
|
)) ||
|
||||||
|
(showModal && (
|
||||||
<Modal
|
<Modal
|
||||||
closeOnBackdropClick={true}
|
closeOnBackdropClick={true}
|
||||||
width={450}
|
width={450}
|
||||||
|
|
@ -40,25 +83,12 @@ const ChangeRoleModal = ({
|
||||||
</P>
|
</P>
|
||||||
<P className={classes.info}>Do you wish to proceed?</P>
|
<P className={classes.info}>Do you wish to proceed?</P>
|
||||||
<div className={classes.footer}>
|
<div className={classes.footer}>
|
||||||
<Button
|
<Button className={classes.submit} onClick={() => submit()}>
|
||||||
className={classes.submit}
|
|
||||||
onClick={() => {
|
|
||||||
setAction(() =>
|
|
||||||
confirm.bind(null, {
|
|
||||||
variables: {
|
|
||||||
id: user.id,
|
|
||||||
newRole: user.role === 'superuser' ? 'user' : 'superuser'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
inputConfirmToggle()
|
|
||||||
handleClose()
|
|
||||||
}}>
|
|
||||||
Confirm
|
Confirm
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
import { useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import React from 'react'
|
import gql from 'graphql-tag'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import Modal from 'src/components/Modal'
|
import Modal from 'src/components/Modal'
|
||||||
import { Button } from 'src/components/buttons'
|
import { Button } from 'src/components/buttons'
|
||||||
|
|
@ -7,24 +9,75 @@ import { Info2, P } from 'src/components/typography'
|
||||||
|
|
||||||
import styles from '../UserManagement.styles'
|
import styles from '../UserManagement.styles'
|
||||||
|
|
||||||
|
import Input2FAModal from './Input2FAModal'
|
||||||
|
|
||||||
|
const ENABLE_USER = gql`
|
||||||
|
mutation enableUser($confirmationCode: String, $id: ID!) {
|
||||||
|
enableUser(confirmationCode: $confirmationCode, id: $id) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const DISABLE_USER = gql`
|
||||||
|
mutation disableUser($confirmationCode: String, $id: ID!) {
|
||||||
|
disableUser(confirmationCode: $confirmationCode, id: $id) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const EnableUserModal = ({
|
const EnableUserModal = ({
|
||||||
showModal,
|
showModal,
|
||||||
toggleModal,
|
toggleModal,
|
||||||
user,
|
user,
|
||||||
confirm,
|
requiresConfirmation
|
||||||
inputConfirmToggle,
|
|
||||||
setAction
|
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const [enableUser] = useMutation(ENABLE_USER, {
|
||||||
|
refetchQueries: () => ['users']
|
||||||
|
})
|
||||||
|
|
||||||
|
const [disableUser] = useMutation(DISABLE_USER, {
|
||||||
|
refetchQueries: () => ['users']
|
||||||
|
})
|
||||||
|
|
||||||
|
const [confirmation, setConfirmation] = useState(null)
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
user?.enabled
|
||||||
|
? disableUser({
|
||||||
|
variables: {
|
||||||
|
confirmationCode: confirmation,
|
||||||
|
id: user.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: enableUser({
|
||||||
|
variables: {
|
||||||
|
confirmationCode: confirmation,
|
||||||
|
id: user.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
handleClose()
|
||||||
|
}
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
|
setConfirmation(null)
|
||||||
toggleModal()
|
toggleModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
showModal && (
|
(showModal && requiresConfirmation && !confirmation && (
|
||||||
|
<Input2FAModal
|
||||||
|
showModal={showModal}
|
||||||
|
handleClose={handleClose}
|
||||||
|
setConfirmation={setConfirmation}
|
||||||
|
/>
|
||||||
|
)) ||
|
||||||
|
(showModal && (
|
||||||
<Modal
|
<Modal
|
||||||
closeOnBackdropClick={true}
|
closeOnBackdropClick={true}
|
||||||
width={450}
|
width={450}
|
||||||
|
|
@ -58,32 +111,12 @@ const EnableUserModal = ({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<div className={classes.footer}>
|
<div className={classes.footer}>
|
||||||
<Button
|
<Button className={classes.submit} onClick={() => submit()}>
|
||||||
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
|
Confirm
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ const CONFIRM_2FA = gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const Input2FAModal = ({ showModal, toggleModal, action, vars }) => {
|
const Input2FAModal = ({ showModal, handleClose, setConfirmation }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
const [twoFACode, setTwoFACode] = useState('')
|
const [twoFACode, setTwoFACode] = useState('')
|
||||||
|
|
@ -29,21 +29,15 @@ const Input2FAModal = ({ showModal, toggleModal, action, vars }) => {
|
||||||
setInvalidCode(false)
|
setInvalidCode(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClose = () => {
|
const onContinue = () => {
|
||||||
|
setConfirmation(twoFACode)
|
||||||
setTwoFACode('')
|
setTwoFACode('')
|
||||||
setInvalidCode(false)
|
setInvalidCode(false)
|
||||||
toggleModal()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const [confirm2FA, { error: queryError }] = useLazyQuery(CONFIRM_2FA, {
|
const [confirm2FA, { error: queryError }] = useLazyQuery(CONFIRM_2FA, {
|
||||||
onCompleted: ({ confirm2FA: success }) => {
|
onCompleted: ({ confirm2FA: success }) =>
|
||||||
if (!success) {
|
!success ? setInvalidCode(true) : onContinue()
|
||||||
setInvalidCode(true)
|
|
||||||
} else {
|
|
||||||
action()
|
|
||||||
handleClose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const getErrorMsg = () => {
|
const getErrorMsg = () => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
import { useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import React from 'react'
|
import gql from 'graphql-tag'
|
||||||
|
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'
|
||||||
|
|
@ -7,17 +9,65 @@ import CopyToClipboard from 'src/pages/Transactions/CopyToClipboard'
|
||||||
|
|
||||||
import styles from '../UserManagement.styles'
|
import styles from '../UserManagement.styles'
|
||||||
|
|
||||||
|
import Input2FAModal from './Input2FAModal'
|
||||||
|
|
||||||
|
const CREATE_RESET_2FA_TOKEN = gql`
|
||||||
|
mutation createReset2FAToken($confirmationCode: String, $userID: ID!) {
|
||||||
|
createReset2FAToken(confirmationCode: $confirmationCode, userID: $userID) {
|
||||||
|
token
|
||||||
|
user_id
|
||||||
|
expire
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const Reset2FAModal = ({ showModal, toggleModal, reset2FAURL, user }) => {
|
const Reset2FAModal = ({
|
||||||
|
showModal,
|
||||||
|
toggleModal,
|
||||||
|
user,
|
||||||
|
requiresConfirmation
|
||||||
|
}) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
const [reset2FAUrl, setReset2FAUrl] = useState('')
|
||||||
|
|
||||||
|
const [createReset2FAToken, { loading }] = useMutation(
|
||||||
|
CREATE_RESET_2FA_TOKEN,
|
||||||
|
{
|
||||||
|
onCompleted: ({ createReset2FAToken: token }) => {
|
||||||
|
setReset2FAUrl(`https://localhost:3001/reset2fa?t=${token.token}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const [confirmation, setConfirmation] = useState(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
showModal &&
|
||||||
|
(confirmation || !requiresConfirmation) &&
|
||||||
|
createReset2FAToken({
|
||||||
|
variables: {
|
||||||
|
confirmationCode: confirmation,
|
||||||
|
userID: user?.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [confirmation, createReset2FAToken, requiresConfirmation, showModal, user])
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
|
setConfirmation(null)
|
||||||
toggleModal()
|
toggleModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
showModal && (
|
(showModal && requiresConfirmation && !confirmation && (
|
||||||
|
<Input2FAModal
|
||||||
|
showModal={showModal}
|
||||||
|
handleClose={handleClose}
|
||||||
|
setConfirmation={setConfirmation}
|
||||||
|
/>
|
||||||
|
)) ||
|
||||||
|
(showModal && (confirmation || !requiresConfirmation) && !loading && (
|
||||||
<Modal
|
<Modal
|
||||||
closeOnBackdropClick={true}
|
closeOnBackdropClick={true}
|
||||||
width={500}
|
width={500}
|
||||||
|
|
@ -38,13 +88,13 @@ const Reset2FAModal = ({ showModal, toggleModal, reset2FAURL, user }) => {
|
||||||
className={classes.link}
|
className={classes.link}
|
||||||
buttonClassname={classes.copyToClipboard}
|
buttonClassname={classes.copyToClipboard}
|
||||||
wrapperClassname={classes.linkWrapper}>
|
wrapperClassname={classes.linkWrapper}>
|
||||||
{reset2FAURL}
|
{reset2FAUrl}
|
||||||
</CopyToClipboard>
|
</CopyToClipboard>
|
||||||
</strong>
|
</strong>
|
||||||
</Mono>
|
</Mono>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
import { useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import React from 'react'
|
import gql from 'graphql-tag'
|
||||||
|
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'
|
||||||
|
|
@ -7,22 +9,76 @@ import CopyToClipboard from 'src/pages/Transactions/CopyToClipboard'
|
||||||
|
|
||||||
import styles from '../UserManagement.styles'
|
import styles from '../UserManagement.styles'
|
||||||
|
|
||||||
|
import Input2FAModal from './Input2FAModal'
|
||||||
|
|
||||||
|
const CREATE_RESET_PASSWORD_TOKEN = gql`
|
||||||
|
mutation createResetPasswordToken($confirmationCode: String, $userID: ID!) {
|
||||||
|
createResetPasswordToken(
|
||||||
|
confirmationCode: $confirmationCode
|
||||||
|
userID: $userID
|
||||||
|
) {
|
||||||
|
token
|
||||||
|
user_id
|
||||||
|
expire
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const ResetPasswordModal = ({
|
const ResetPasswordModal = ({
|
||||||
showModal,
|
showModal,
|
||||||
toggleModal,
|
toggleModal,
|
||||||
resetPasswordURL,
|
user,
|
||||||
user
|
requiresConfirmation
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
const [resetPasswordUrl, setResetPasswordUrl] = useState('')
|
||||||
|
|
||||||
|
const [createResetPasswordToken, { loading }] = useMutation(
|
||||||
|
CREATE_RESET_PASSWORD_TOKEN,
|
||||||
|
{
|
||||||
|
onCompleted: ({ createResetPasswordToken: token }) => {
|
||||||
|
setResetPasswordUrl(
|
||||||
|
`https://localhost:3001/resetpassword?t=${token.token}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const [confirmation, setConfirmation] = useState(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
showModal &&
|
||||||
|
(confirmation || !requiresConfirmation) &&
|
||||||
|
createResetPasswordToken({
|
||||||
|
variables: {
|
||||||
|
confirmationCode: confirmation,
|
||||||
|
userID: user?.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [
|
||||||
|
confirmation,
|
||||||
|
createResetPasswordToken,
|
||||||
|
showModal,
|
||||||
|
user,
|
||||||
|
requiresConfirmation
|
||||||
|
])
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
|
setConfirmation(null)
|
||||||
toggleModal()
|
toggleModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
showModal && (
|
(showModal && requiresConfirmation && !confirmation && (
|
||||||
|
<Input2FAModal
|
||||||
|
showModal={showModal}
|
||||||
|
handleClose={handleClose}
|
||||||
|
setConfirmation={setConfirmation}
|
||||||
|
/>
|
||||||
|
)) ||
|
||||||
|
(showModal && (confirmation || !requiresConfirmation) && !loading && (
|
||||||
<Modal
|
<Modal
|
||||||
closeOnBackdropClick={true}
|
closeOnBackdropClick={true}
|
||||||
width={500}
|
width={500}
|
||||||
|
|
@ -42,13 +98,13 @@ const ResetPasswordModal = ({
|
||||||
className={classes.link}
|
className={classes.link}
|
||||||
buttonClassname={classes.copyToClipboard}
|
buttonClassname={classes.copyToClipboard}
|
||||||
wrapperClassname={classes.linkWrapper}>
|
wrapperClassname={classes.linkWrapper}>
|
||||||
{resetPasswordURL}
|
{resetPasswordUrl}
|
||||||
</CopyToClipboard>
|
</CopyToClipboard>
|
||||||
</strong>
|
</strong>
|
||||||
</Mono>
|
</Mono>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue