From 40974dd501fc677ba04c9606a7833cc222ec832b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Tue, 6 Apr 2021 00:39:52 +0100 Subject: [PATCH] fix: naming and redundancy issues --- lib/new-admin/admin-server.js | 34 +----- .../graphql/errors/authentication.js | 36 +++++++ .../graphql/modules/authentication.js | 102 ++++++++---------- .../graphql/resolvers/users.resolver.js | 6 +- lib/new-admin/middlewares/session.js | 40 +++++++ lib/new-admin/services/login.js | 5 +- lib/session-manager.js | 14 +-- lib/users.js | 25 +++-- migrations/1605181184453-users.js | 4 +- .../src/pages/Authentication/Input2FAState.js | 5 +- .../src/pages/Authentication/LoginState.js | 15 +-- .../src/pages/Authentication/Reset2FA.js | 5 +- new-lamassu-admin/src/routing/routes.js | 15 ++- new-lamassu-admin/src/routing/utils.js | 8 +- new-lamassu-admin/src/utils/apollo.js | 23 +++- 15 files changed, 194 insertions(+), 143 deletions(-) create mode 100644 lib/new-admin/graphql/errors/authentication.js create mode 100644 lib/new-admin/middlewares/session.js diff --git a/lib/new-admin/admin-server.js b/lib/new-admin/admin-server.js index 844d81c1..25c66e56 100644 --- a/lib/new-admin/admin-server.js +++ b/lib/new-admin/admin-server.js @@ -11,19 +11,15 @@ const cookieParser = require('cookie-parser') const bodyParser = require('body-parser') const { ApolloServer, AuthenticationError } = require('apollo-server-express') const _ = require('lodash/fp') -const session = require('express-session') -const pgSession = require('connect-pg-simple')(session) -const hkdf = require('futoin-hkdf') const pify = require('pify') const login = require('./services/login') const register = require('./routes/authentication') const options = require('../options') -const db = require('../db') const users = require('../users') -const mnemonicHelpers = require('../mnemonic-helpers') +const session = require('./middlewares/session') const authRouter = require('./routes/auth') const { AuthDirective } = require('./graphql/directives') const { typeDefs, resolvers } = require('./graphql/schema') @@ -46,33 +42,7 @@ app.use(cookieParser()) app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: true })) // support encoded bodies app.use(express.static(path.resolve(__dirname, '..', '..', 'public'))) - -const getSecret = () => { - const mnemonic = fs.readFileSync(options.mnemonicPath, 'utf8') - return hkdf( - mnemonicHelpers.toEntropyBuffer(mnemonic), - 16, - { salt: 'lamassu-server-salt', info: 'operator-id' } - ).toString('hex') -} - -app.use('*', session({ - store: new pgSession({ - pgPromise: db, - tableName: 'user_sessions' - }), - name: 'lid', - secret: getSecret(), - resave: false, - saveUninitialized: false, - cookie: { - httpOnly: true, - secure: true, - domain: hostname, - sameSite: true, - maxAge: 60 * 10 * 1000 // 10 minutes - } -})) +app.use(session) const apolloServer = new ApolloServer({ typeDefs, diff --git a/lib/new-admin/graphql/errors/authentication.js b/lib/new-admin/graphql/errors/authentication.js new file mode 100644 index 00000000..7ddaede5 --- /dev/null +++ b/lib/new-admin/graphql/errors/authentication.js @@ -0,0 +1,36 @@ +const { ApolloError } = require('apollo-server-express') + +class InvalidCredentialsError extends ApolloError { + constructor(message) { + super(message, 'INVALID_CREDENTIALS') + Object.defineProperty(this, 'name', { value: 'InvalidCredentialsError' }) + } +} + +class UserAlreadyExistsError extends ApolloError { + constructor(message) { + super(message, 'USER_ALREADY_EXISTS') + Object.defineProperty(this, 'name', { value: 'UserAlreadyExistsError' }) + } +} + +class InvalidTwoFactorError extends ApolloError { + constructor(message) { + super(message, 'INVALID_TWO_FACTOR_CODE') + Object.defineProperty(this, 'name', { value: 'InvalidTwoFactorError' }) + } +} + +class InvalidUrlError extends ApolloError { + constructor(message) { + super(message, 'INVALID_URL_TOKEN') + Object.defineProperty(this, 'name', { value: 'InvalidUrlError' }) + } +} + +module.exports = { + InvalidCredentialsError, + UserAlreadyExistsError, + InvalidTwoFactorError, + InvalidUrlError +} diff --git a/lib/new-admin/graphql/modules/authentication.js b/lib/new-admin/graphql/modules/authentication.js index 71dbb8c1..9b9e4703 100644 --- a/lib/new-admin/graphql/modules/authentication.js +++ b/lib/new-admin/graphql/modules/authentication.js @@ -5,27 +5,28 @@ const loginHelper = require('../../services/login') const T = require('../../../time') const users = require('../../../users') const sessionManager = require('../../../session-manager') +const authErrors = require('../errors/authentication') const REMEMBER_ME_AGE = 90 * T.day -function authenticateUser (username, password) { - return loginHelper.checkUser(username).then(hashedPassword => { - if (!hashedPassword) return null - return Promise.all([bcrypt.compare(password, hashedPassword), hashedPassword]) - }).then(([isMatch, hashedPassword]) => { - if (!isMatch) return null - return loginHelper.validateUser(username, hashedPassword) - }).then(user => { - if (!user) return null - return user - }).catch(e => { - console.error(e) - }) +function authenticateUser(username, password) { + return loginHelper.checkUser(username) + .then(hashedPassword => { + if (!hashedPassword) throw new authErrors.InvalidCredentialsError() + return Promise.all([bcrypt.compare(password, hashedPassword), hashedPassword]) + }) + .then(([isMatch, hashedPassword]) => { + if (!isMatch) throw new authErrors.InvalidCredentialsError() + return loginHelper.validateUser(username, hashedPassword) + }) + .catch(e => { + console.error(e) + }) } const getUserData = context => { const lidCookie = context.req.cookies && context.req.cookies.lid - if (!lidCookie) return null + if (!lidCookie) throw new authErrors.InvalidCredentialsError() const user = context.req.session.user return user @@ -33,7 +34,7 @@ const getUserData = context => { const get2FASecret = (username, password) => { return authenticateUser(username, password).then(user => { - if (!user) return null + if (!user) throw new authErrors.InvalidCredentialsError() const secret = otplib.authenticator.generateSecret() const otpauth = otplib.authenticator.keyuri(username, 'Lamassu Industries', secret) @@ -41,46 +42,45 @@ const get2FASecret = (username, password) => { }) } -const confirm2FA = (codeArg, context) => { - const code = codeArg +const confirm2FA = (token, context) => { const requestingUser = context.req.session.user - if (!requestingUser) return false + if (!requestingUser) throw new authErrors.InvalidCredentialsError() return users.get2FASecret(requestingUser.id).then(user => { const secret = user.twofa_code - const isCodeValid = otplib.authenticator.verify({ token: code, secret: secret }) + const isCodeValid = otplib.authenticator.verify({ token, secret }) - if (!isCodeValid) return false + if (!isCodeValid) throw new authErrors.InvalidTwoFactorError() return true }) } const validateRegisterLink = token => { - if (!token) return null + if (!token) throw new authErrors.InvalidUrlError() return users.validateUserRegistrationToken(token) .then(r => { - if (!r.success) return null + if (!r.success) throw new authErrors.InvalidUrlError() return { username: r.username, role: r.role } }) .catch(err => console.error(err)) } const validateResetPasswordLink = token => { - if (!token) return null + if (!token) throw new authErrors.InvalidUrlError() return users.validatePasswordResetToken(token) .then(r => { - if (!r.success) return null + if (!r.success) throw new authErrors.InvalidUrlError() return { id: r.userID } }) .catch(err => console.error(err)) } const validateReset2FALink = token => { - if (!token) return null + if (!token) throw new authErrors.InvalidUrlError() return users.validate2FAResetToken(token) .then(r => { - if (!r.success) return null + if (!r.success) throw new authErrors.InvalidUrlError() return users.findById(r.userID) }) .then(user => { @@ -95,12 +95,12 @@ const deleteSession = (sessionID, context) => { if (sessionID === context.req.session.id) { context.req.session.destroy() } - return sessionManager.deleteSession(sessionID) + return sessionManager.deleteSessionById(sessionID) } const login = (username, password) => { return authenticateUser(username, password).then(user => { - if (!user) return 'FAILED' + if (!user) throw new authErrors.InvalidCredentialsError() return users.get2FASecret(user.id).then(user => { const twoFASecret = user.twofa_code return twoFASecret ? 'INPUT2FA' : 'SETUP2FA' @@ -110,12 +110,12 @@ const login = (username, password) => { const input2FA = (username, password, rememberMe, code, context) => { return authenticateUser(username, password).then(user => { - if (!user) return false + if (!user) throw new authErrors.InvalidCredentialsError() return users.get2FASecret(user.id).then(user => { const secret = user.twofa_code const isCodeValid = otplib.authenticator.verify({ token: code, secret: secret }) - if (!isCodeValid) return false + if (!isCodeValid) throw new authErrors.InvalidTwoFactorError() const finalUser = { id: user.id, username: user.username, role: user.role } context.req.session.user = finalUser @@ -128,48 +128,39 @@ const input2FA = (username, password, rememberMe, code, context) => { const setup2FA = (username, password, secret, codeConfirmation) => { return authenticateUser(username, password).then(user => { - if (!user || !secret) return false + if (!user || !secret) throw new authErrors.InvalidCredentialsError() const isCodeValid = otplib.authenticator.verify({ token: codeConfirmation, secret: secret }) - if (!isCodeValid) return false + if (!isCodeValid) throw new authErrors.InvalidTwoFactorError() - users.save2FASecret(user.id, secret) - return true + return users.save2FASecret(user.id, secret).then(() => true) }) } const createResetPasswordToken = userID => { return users.findById(userID) .then(user => { - if (!user) return null + if (!user) throw new authErrors.InvalidCredentialsError() return users.createResetPasswordToken(user.id) }) - .then(token => { - return token - }) .catch(err => console.error(err)) } const createReset2FAToken = userID => { return users.findById(userID) .then(user => { - if (!user) return null + if (!user) throw new authErrors.InvalidCredentialsError() return users.createReset2FAToken(user.id) }) - .then(token => { - return token - }) .catch(err => console.error(err)) } const createRegisterToken = (username, role) => { return users.getByName(username) .then(user => { - if (user) return null + if (user) throw new authErrors.UserAlreadyExistsError() - return users.createUserRegistrationToken(username, role).then(token => { - return token - }) + return users.createUserRegistrationToken(username, role) }) .catch(err => console.error(err)) } @@ -177,29 +168,26 @@ const createRegisterToken = (username, role) => { const register = (username, password, role) => { return users.getByName(username) .then(user => { - if (user) return false - - users.createUser(username, password, role) - return true + if (user) throw new authErrors.UserAlreadyExistsError() + return users.createUser(username, password, role).then(() => true) }) .catch(err => console.error(err)) } const resetPassword = (userID, newPassword, context) => { return users.findById(userID).then(user => { - if (!user) return false + if (!user) throw new authErrors.InvalidCredentialsError() if (context.req.session.user && user.id === context.req.session.user.id) context.req.session.destroy() return users.updatePassword(user.id, newPassword) - }).then(() => { return true }).catch(err => console.error(err)) + }).then(() => true).catch(err => console.error(err)) } -const reset2FA = (userID, code, secret, context) => { +const reset2FA = (userID, token, secret, context) => { return users.findById(userID).then(user => { - const isCodeValid = otplib.authenticator.verify({ token: code, secret: secret }) - if (!isCodeValid) return false - + const isCodeValid = otplib.authenticator.verify({ token, secret }) + if (!isCodeValid) throw new authErrors.InvalidTwoFactorError() if (context.req.session.user && user.id === context.req.session.user.id) context.req.session.destroy() - return users.save2FASecret(user.id, secret).then(() => { return true }) + return users.save2FASecret(user.id, secret).then(() => true) }).catch(err => console.error(err)) } diff --git a/lib/new-admin/graphql/resolvers/users.resolver.js b/lib/new-admin/graphql/resolvers/users.resolver.js index 846cab57..c35d5eda 100644 --- a/lib/new-admin/graphql/resolvers/users.resolver.js +++ b/lib/new-admin/graphql/resolvers/users.resolver.js @@ -5,8 +5,8 @@ const sessionManager = require('../../../session-manager') const resolver = { Query: { users: () => users.getUsers(), - sessions: () => sessionManager.getSessionList(), - userSessions: (...[, { username }]) => sessionManager.getUserSessions(username), + sessions: () => sessionManager.getSessions(), + userSessions: (...[, { username }]) => sessionManager.getSessionsByUsername(username), userData: (root, args, context, info) => authentication.getUserData(context), get2FASecret: (...[, { username, password }]) => authentication.get2FASecret(username, password), confirm2FA: (root, args, context, info) => authentication.confirm2FA(args.code, context), @@ -17,7 +17,7 @@ const resolver = { Mutation: { deleteUser: (...[, { id }]) => users.deleteUser(id), deleteSession: (root, args, context, info) => authentication.deleteSession(args.sid, context), - deleteUserSessions: (...[, { username }]) => sessionManager.deleteUserSessions(username), + deleteUserSessions: (...[, { username }]) => sessionManager.deleteSessionsByUsername(username), changeUserRole: (...[, { id, newRole }]) => users.changeUserRole(id, newRole), toggleUserEnable: (...[, { id }]) => users.toggleUserEnable(id), login: (...[, { username, password }]) => authentication.login(username, password), diff --git a/lib/new-admin/middlewares/session.js b/lib/new-admin/middlewares/session.js new file mode 100644 index 00000000..bb75e20a --- /dev/null +++ b/lib/new-admin/middlewares/session.js @@ -0,0 +1,40 @@ +const fs = require('fs') +const express = require('express') +const router = express.Router() +const hkdf = require('futoin-hkdf') +const session = require('express-session') +const pgSession = require('connect-pg-simple')(session) +const mnemonicHelpers = require('../../mnemonic-helpers') +const db = require('../../db') +const options = require('../../options') + +const getSecret = () => { + const mnemonic = fs.readFileSync(options.mnemonicPath, 'utf8') + return hkdf( + mnemonicHelpers.toEntropyBuffer(mnemonic), + 16, + { salt: 'lamassu-server-salt', info: 'operator-id' } + ).toString('hex') +} + +const hostname = options.hostname + +router.use('*', session({ + store: new pgSession({ + pgPromise: db, + tableName: 'user_sessions' + }), + name: 'lid', + secret: getSecret(), + resave: false, + saveUninitialized: false, + cookie: { + httpOnly: true, + secure: true, + domain: hostname, + sameSite: true, + maxAge: 60 * 10 * 1000 // 10 minutes + } +})) + +module.exports = router diff --git a/lib/new-admin/services/login.js b/lib/new-admin/services/login.js index 41ac8561..9e763cab 100644 --- a/lib/new-admin/services/login.js +++ b/lib/new-admin/services/login.js @@ -10,7 +10,10 @@ function validateUser (username, password) { const sqlUpdateLastAccessed = 'UPDATE users SET last_accessed = now() WHERE username=$1' return db.oneOrNone(sql, [username, password]) - .then(user => { db.none(sqlUpdateLastAccessed, [user.username]); return user }) + .then(user => { + return db.none(sqlUpdateLastAccessed, [user.username]) + .then(() => user) + }) .catch(() => false) } diff --git a/lib/session-manager.js b/lib/session-manager.js index 1938239d..3785c576 100644 --- a/lib/session-manager.js +++ b/lib/session-manager.js @@ -1,11 +1,11 @@ const db = require('./db') -function getSessionList () { +function getSessions () { const sql = `SELECT * FROM user_sessions ORDER BY sess -> 'user' ->> 'username'` return db.any(sql) } -function getLastSessionByUser () { +function getLastSessionPerUser () { const sql = `SELECT b.username, a.user_agent, a.ip_address, a.last_used, b.role FROM ( SELECT sess -> 'user' ->> 'username' AS username, sess ->> 'ua' AS user_agent, @@ -19,24 +19,24 @@ function getLastSessionByUser () { return db.any(sql) } -function getUserSessions (username) { +function getSessionsByUsername (username) { const sql = `SELECT * FROM user_sessions WHERE sess -> 'user' ->> 'username'=$1` return db.any(sql, [username]) } -function getSession (sessionID) { +function getSessionById (sessionID) { const sql = `SELECT * FROM user_sessions WHERE sid=$1` return db.any(sql, [sessionID]) } -function deleteUserSessions (username) { +function deleteSessionsByUsername (username) { const sql = `DELETE FROM user_sessions WHERE sess -> 'user' ->> 'username'=$1` return db.none(sql, [username]) } -function deleteSession (sessionID) { +function deleteSessionById (sessionID) { const sql = `DELETE FROM user_sessions WHERE sid=$1` return db.none(sql, [sessionID]) } -module.exports = { getSessionList, getLastSessionByUser, getUserSessions, getSession, deleteUserSessions, deleteSession } +module.exports = { getSessions, getLastSessionPerUser, getSessionsByUsername, getSessionById, deleteSessionsByUsername, deleteSessionById } diff --git a/lib/users.js b/lib/users.js index 0b052e0e..df785317 100644 --- a/lib/users.js +++ b/lib/users.js @@ -61,11 +61,12 @@ function verifyAndUpdateUser (id, ua, ip) { function createUser (username, password, role) { const sql = `INSERT INTO users (id, username, password, role) VALUES ($1, $2, $3, $4)` - bcrypt.hash(password, 12).then(function (hash) { + return bcrypt.hash(password, 12).then(function (hash) { return db.none(sql, [uuid.v4(), username, hash, role]) }) } +// TO DELETE function deleteUser (id) { const sql = `DELETE FROM users WHERE id=$1` const sql2 = `DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1` @@ -84,9 +85,14 @@ function get2FASecret (id) { } function save2FASecret (id, secret) { - const sql = 'UPDATE users SET twofa_code=$1 WHERE id=$2' - const sql2 = `DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1` - return db.none(sql, [secret, id]).then(() => db.none(sql2, [id])) + return db.tx(t => { + const q1 = t.none('UPDATE users SET twofa_code=$1 WHERE id=$2', [secret, id]) + const q2 = t.none(`DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1`, [id]) + return t.batch([q1, q2]) + // const sql = 'UPDATE users SET twofa_code=$1 WHERE id=$2' + // const sql2 = `DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1` + // return db.none(sql, [secret, id]).then(() => db.none(sql2, [id])) + }) } function validate2FAResetToken (token) { @@ -107,9 +113,14 @@ function createReset2FAToken (userID) { function updatePassword (id, password) { bcrypt.hash(password, 12).then(function (hash) { - const sql = `UPDATE users SET password=$1 WHERE id=$2` - const sql2 = `DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1` - return db.none(sql, [hash, id]).then(() => db.none(sql2, [id])) + return db.tx(t => { + const q1 = t.none(`UPDATE users SET password=$1 WHERE id=$2`, [hash, id]) + const q2 = t.none(`DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1`, [id]) + return t.batch([q1, q2]) + }) + // const sql = `UPDATE users SET password=$1 WHERE id=$2` + // const sql2 = `DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1` + // return db.none(sql, [hash, id]).then(() => db.none(sql2, [id])) }) } diff --git a/migrations/1605181184453-users.js b/migrations/1605181184453-users.js index 45c24e70..088ded55 100644 --- a/migrations/1605181184453-users.js +++ b/migrations/1605181184453-users.js @@ -5,9 +5,9 @@ exports.up = function (next) { `CREATE TYPE role AS ENUM('user', 'superuser')`, `CREATE TABLE users ( id UUID PRIMARY KEY, - username VARCHAR(50) UNIQUE, + username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100), - role role DEFAULT 'user', + role role NOT NULL DEFAULT 'user', enabled BOOLEAN DEFAULT true, twofa_code VARCHAR(100), created TIMESTAMPTZ NOT NULL DEFAULT now(), diff --git a/new-lamassu-admin/src/pages/Authentication/Input2FAState.js b/new-lamassu-admin/src/pages/Authentication/Input2FAState.js index 63c4ad27..1e653afb 100644 --- a/new-lamassu-admin/src/pages/Authentication/Input2FAState.js +++ b/new-lamassu-admin/src/pages/Authentication/Input2FAState.js @@ -71,10 +71,11 @@ const Input2FAState = ({ }) const getErrorMsg = () => { - if (mutationError || queryError) return 'Internal server error' + if (queryError) return 'Internal server error' if (twoFAField.length !== 6 && invalidToken) return 'The code should have 6 characters!' - if (invalidToken) return 'Code is invalid. Please try again.' + if (mutationError || invalidToken) + return 'Code is invalid. Please try again.' return null } diff --git a/new-lamassu-admin/src/pages/Authentication/LoginState.js b/new-lamassu-admin/src/pages/Authentication/LoginState.js index 885cb53d..f9304060 100644 --- a/new-lamassu-admin/src/pages/Authentication/LoginState.js +++ b/new-lamassu-admin/src/pages/Authentication/LoginState.js @@ -2,7 +2,7 @@ import { useMutation } from '@apollo/react-hooks' import { makeStyles } from '@material-ui/core/styles' import { Field, Form, Formik } from 'formik' import gql from 'graphql-tag' -import React, { useState } from 'react' +import React from 'react' import * as Yup from 'yup' import { Button } from 'src/components/buttons' @@ -46,19 +46,15 @@ const LoginState = ({ onCompleted: ({ login }) => { if (login === 'INPUT2FA') handleLoginState(STATES.INPUT_2FA) if (login === 'SETUP2FA') handleLoginState(STATES.SETUP_2FA) - if (login === 'FAILED') setInvalidLogin(true) } }) - const [invalidLogin, setInvalidLogin] = useState(false) - const getErrorMsg = (formikErrors, formikTouched) => { if (!formikErrors || !formikTouched) return null - if (mutationError) return 'Internal server error' + if (mutationError) return 'Invalid login/password combination' if (formikErrors.client && formikTouched.client) return formikErrors.client if (formikErrors.password && formikTouched.password) return formikErrors.password - if (invalidLogin) return 'Invalid login/password combination' return null } @@ -67,7 +63,6 @@ const LoginState = ({ validationSchema={validationSchema} initialValues={initialValues} onSubmit={values => { - setInvalidLogin(false) onClientChange(values.client) onPasswordChange(values.password) onRememberMeChange(values.rememberMe) @@ -89,9 +84,6 @@ const LoginState = ({ autoFocus className={classes.input} error={getErrorMsg(errors, touched)} - onKeyUp={() => { - if (invalidLogin) setInvalidLogin(false) - }} /> { - if (invalidLogin) setInvalidLogin(false) - }} />
{ }) const getErrorMsg = () => { - if (mutationError || queryError) return 'Internal server error' + if (queryError) return 'Internal server error' if (twoFAConfirmation.length !== 6 && invalidToken) return 'The code should have 6 characters!' - if (invalidToken) return 'Code is invalid. Please try again.' + if (mutationError || invalidToken) + return 'Code is invalid. Please try again.' return null } diff --git a/new-lamassu-admin/src/routing/routes.js b/new-lamassu-admin/src/routing/routes.js index f1b92ed3..6b8ae778 100644 --- a/new-lamassu-admin/src/routing/routes.js +++ b/new-lamassu-admin/src/routing/routes.js @@ -5,7 +5,6 @@ import * as R from 'ramda' import React, { useContext } from 'react' import { matchPath, - Route, Redirect, Switch, useHistory, @@ -398,11 +397,11 @@ const Routes = () => { - - {/* */} + + {/* */} - - + + {getFilteredRoutes().map(({ route, component: Page, key }) => ( { /> ))} - - + + - + ) } diff --git a/new-lamassu-admin/src/routing/utils.js b/new-lamassu-admin/src/routing/utils.js index 075f2a9c..37a2595f 100644 --- a/new-lamassu-admin/src/routing/utils.js +++ b/new-lamassu-admin/src/routing/utils.js @@ -1,8 +1,6 @@ -export const isLoggedIn = userData => { - return userData -} +export const isLoggedIn = userData => !!userData export const ROLES = { - USER: { key: 'user', value: '0' }, - SUPERUSER: { key: 'superuser', value: '1' } + USER: { key: 'user' }, + SUPERUSER: { key: 'superuser' } } diff --git a/new-lamassu-admin/src/utils/apollo.js b/new-lamassu-admin/src/utils/apollo.js index 0a2b58f3..f3f3f2ea 100644 --- a/new-lamassu-admin/src/utils/apollo.js +++ b/new-lamassu-admin/src/utils/apollo.js @@ -4,6 +4,7 @@ import { ApolloClient } from 'apollo-client' import { ApolloLink } from 'apollo-link' import { onError } from 'apollo-link-error' import { HttpLink } from 'apollo-link-http' +import * as R from 'ramda' import React, { useContext } from 'react' import { useHistory, useLocation } from 'react-router-dom' @@ -18,10 +19,7 @@ const getClient = (history, location, setUserData) => onError(({ graphQLErrors, networkError }) => { if (graphQLErrors) graphQLErrors.forEach(({ message, locations, path, extensions }) => { - if (extensions?.code === 'UNAUTHENTICATED') { - setUserData(null) - if (location.pathname !== '/login') history.push('/login') - } + handle(extensions?.code, history, location, setUserData) console.log( `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` ) @@ -49,6 +47,23 @@ const getClient = (history, location, setUserData) => } }) +const handle = (type, ...args) => { + const handler = { + UNAUTHENTICATED: ({ history, location, setUserData }) => { + setUserData(null) + if (location.pathname !== '/login') history.push('/login') + }, + INVALID_CREDENTIALS: () => {}, + INVALID_TWO_FACTOR_CODE: () => {}, + INVALID_URL_TOKEN: () => {}, + USER_ALREADY_EXISTS: () => {} + } + + if (!R.has(type, handler)) throw new Error('Unknown error code.') + + return handler[type](...args) +} + const Provider = ({ children }) => { const history = useHistory() const location = useLocation()