feat: auto userdata fetch

fix: gql directives and overall minor fixes
This commit is contained in:
Sérgio Salgado 2021-04-07 16:11:06 +01:00 committed by Josh Harvey
parent 3f6c0e6037
commit 9fa97725ec
22 changed files with 94 additions and 127 deletions

View file

@ -11,13 +11,11 @@ const cookieParser = require('cookie-parser')
const bodyParser = require('body-parser') const bodyParser = require('body-parser')
const { ApolloServer, AuthenticationError } = require('apollo-server-express') const { ApolloServer, AuthenticationError } = require('apollo-server-express')
const _ = require('lodash/fp') const _ = require('lodash/fp')
const pify = require('pify')
const options = require('../options') const options = require('../options')
const users = require('../users') const users = require('../users')
const session = require('./middlewares/session') const session = require('./middlewares/session')
const authRouter = require('./routes/auth')
const { AuthDirective } = require('./graphql/directives') const { AuthDirective } = require('./graphql/directives')
const { typeDefs, resolvers } = require('./graphql/schema') const { typeDefs, resolvers } = require('./graphql/schema')
@ -86,7 +84,6 @@ app.use(cors({ credentials: true, origin: devMode && 'https://localhost:3001' })
app.use('/id-card-photo', serveStatic(idPhotoCardBasedir, { index: false })) app.use('/id-card-photo', serveStatic(idPhotoCardBasedir, { index: false }))
app.use('/front-camera-photo', serveStatic(frontCameraBasedir, { index: false })) app.use('/front-camera-photo', serveStatic(frontCameraBasedir, { index: false }))
app.use(authRouter)
// Everything not on graphql or api/register is redirected to the front-end // Everything not on graphql or api/register is redirected to the front-end
app.get('*', (req, res) => res.sendFile(path.resolve(__dirname, '..', '..', 'public', 'index.html'))) app.get('*', (req, res) => res.sendFile(path.resolve(__dirname, '..', '..', 'public', 'index.html')))

View file

@ -1,5 +1,6 @@
const otplib = require('otplib') const otplib = require('otplib')
const bcrypt = require('bcrypt') const bcrypt = require('bcrypt')
const { AuthenticationError } = require('apollo-server-express')
const loginHelper = require('../../services/login') const loginHelper = require('../../services/login')
const T = require('../../../time') const T = require('../../../time')
@ -27,7 +28,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 authErrors.InvalidCredentialsError() if (!lidCookie) throw new AuthenticationError()
const user = context.req.session.user const user = context.req.session.user
return user return user

View file

@ -9,7 +9,7 @@ const typeDef = gql`
} }
type Query { type Query {
bills: [Bill] bills: [Bill] @auth
} }
` `

View file

@ -8,12 +8,12 @@ const typeDef = gql`
} }
type Query { type Query {
blacklist: [Blacklist] blacklist: [Blacklist] @auth
} }
type Mutation { type Mutation {
deleteBlacklistRow(cryptoCode: String!, address: String!): Blacklist deleteBlacklistRow(cryptoCode: String!, address: String!): Blacklist @auth
insertBlacklistRow(cryptoCode: String!, address: String!): Blacklist insertBlacklistRow(cryptoCode: String!, address: String!): Blacklist @auth
} }
` `

View file

@ -20,9 +20,9 @@ const typeDef = gql`
} }
type Query { type Query {
countries: [Country] countries: [Country] @auth
languages: [Language] languages: [Language] @auth
accountsConfig: [AccountConfig] accountsConfig: [AccountConfig] @auth
} }
` `

View file

@ -12,8 +12,8 @@ const typeDef = gql`
} }
type Query { type Query {
currencies: [Currency] currencies: [Currency] @auth
cryptoCurrencies: [CryptoCurrency] cryptoCurrencies: [CryptoCurrency] @auth
} }
` `

View file

@ -54,12 +54,12 @@ const typeDef = gql`
} }
type Query { type Query {
customers: [Customer] customers: [Customer] @auth
customer(customerId: ID!): Customer customer(customerId: ID!): Customer @auth
} }
type Mutation { type Mutation {
setCustomer(customerId: ID!, customerInput: CustomerInput): Customer setCustomer(customerId: ID!, customerInput: CustomerInput): Customer @auth
} }
` `

View file

@ -16,7 +16,7 @@ const typeDef = gql`
} }
type Query { type Query {
funding: [CoinFunds] funding: [CoinFunds] @auth
} }
` `

View file

@ -16,10 +16,10 @@ const typeDef = gql`
} }
type Query { type Query {
machineLogs(deviceId: ID!, from: Date, until: Date, limit: Int, offset: Int): [MachineLog] machineLogs(deviceId: ID!, from: Date, until: Date, limit: Int, offset: Int): [MachineLog] @auth
machineLogsCsv(deviceId: ID!, from: Date, until: Date, limit: Int, offset: Int): String machineLogsCsv(deviceId: ID!, from: Date, until: Date, limit: Int, offset: Int): String @auth
serverLogs(from: Date, until: Date, limit: Int, offset: Int): [ServerLog] serverLogs(from: Date, until: Date, limit: Int, offset: Int): [ServerLog] @auth
serverLogsCsv(from: Date, until: Date, limit: Int, offset: Int): String serverLogsCsv(from: Date, until: Date, limit: Int, offset: Int): String @auth
} }
` `

View file

@ -43,12 +43,12 @@ const typeDef = gql`
} }
type Query { type Query {
machines: [Machine] machines: [Machine] @auth
machine(deviceId: ID!): Machine machine(deviceId: ID!): Machine @auth
} }
type Mutation { type Mutation {
machineAction(deviceId:ID!, action: MachineAction!, cashbox: Int, cassette1: Int, cassette2: Int, newName: String): Machine machineAction(deviceId:ID!, action: MachineAction!, cashbox: Int, cassette1: Int, cassette2: Int, newName: String): Machine @auth
} }
` `

View file

@ -12,14 +12,14 @@ const typeDef = gql`
} }
type Query { type Query {
notifications: [Notification] notifications: [Notification] @auth
alerts: [Notification] alerts: [Notification] @auth
hasUnreadNotifications: Boolean hasUnreadNotifications: Boolean @auth
} }
type Mutation { type Mutation {
toggleClearNotification(id: ID!, read: Boolean!): Notification toggleClearNotification(id: ID!, read: Boolean!): Notification @auth
clearAllNotifications: Notification clearAllNotifications: Notification @auth
} }
` `

View file

@ -2,7 +2,7 @@ const { gql } = require('apollo-server-express')
const typeDef = gql` const typeDef = gql`
type Mutation { type Mutation {
createPairingTotem(name: String!): String createPairingTotem(name: String!): String @auth
} }
` `

View file

@ -8,12 +8,12 @@ const typeDef = gql`
} }
type Query { type Query {
promoCodes: [PromoCode] promoCodes: [PromoCode] @auth
} }
type Mutation { type Mutation {
createPromoCode(code: String!, discount: Int!): PromoCode createPromoCode(code: String!, discount: Int!): PromoCode @auth
deletePromoCode(codeId: ID!): PromoCode deletePromoCode(codeId: ID!): PromoCode @auth
} }
` `

View file

@ -8,8 +8,8 @@ const typeDef = gql`
} }
type Query { type Query {
cryptoRates: JSONObject cryptoRates: JSONObject @auth
fiatRates: [Rate] fiatRates: [Rate] @auth
} }
` `

View file

@ -2,16 +2,16 @@ const { gql } = require('apollo-server-express')
const typeDef = gql` const typeDef = gql`
type Query { type Query {
accounts: JSONObject accounts: JSONObject @auth
config: JSONObject config: JSONObject @auth
} }
type Mutation { type Mutation {
saveAccounts(accounts: JSONObject): JSONObject saveAccounts(accounts: JSONObject): JSONObject @auth
# resetAccounts(schemaVersion: Int): JSONObject # resetAccounts(schemaVersion: Int): JSONObject @auth
saveConfig(config: JSONObject): JSONObject saveConfig(config: JSONObject): JSONObject @auth
# resetConfig(schemaVersion: Int): JSONObject # resetConfig(schemaVersion: Int): JSONObject @auth
# migrateConfigAndAccounts: JSONObject # migrateConfigAndAccounts: JSONObject @auth
} }
` `

View file

@ -8,7 +8,7 @@ const typeDef = gql`
} }
type Query { type Query {
uptime: [ProcessStatus] uptime: [ProcessStatus] @auth
} }
` `

View file

@ -46,7 +46,7 @@ const typeDef = gql`
} }
type Query { type Query {
transactions(from: Date, until: Date, limit: Int, offset: Int, deviceId: ID): [Transaction] transactions(from: Date, until: Date, limit: Int, offset: Int, deviceId: ID): [Transaction] @auth
transactionsCsv(from: Date, until: Date, limit: Int, offset: Int): String transactionsCsv(from: Date, until: Date, limit: Int, offset: Int): String
} }
` `

View file

@ -2,7 +2,7 @@ const { gql } = require('apollo-server-express')
const typeDef = gql` const typeDef = gql`
type Query { type Query {
serverVersion: String! serverVersion: String! @auth
} }
` `

View file

@ -1,17 +0,0 @@
const express = require('express')
const router = express.Router()
const getUserData = function (req, res, next) {
const lidCookie = req.cookies && req.cookies.lid
if (!lidCookie) {
res.sendStatus(403)
return
}
const user = req.session.user
return res.status(200).json({ message: 'Success', user: user })
}
router.get('/user-data', getUserData)
module.exports = router

View file

@ -162,13 +162,19 @@ function changeUserRole (id, newRole) {
} }
function enableUser (id) { function enableUser (id) {
const sql = `UPDATE users SET enabled=true WHERE id=$1` return db.tx(t => {
return db.none(sql, [id]) const q1 = t.none(`UPDATE users SET enabled=true WHERE id=$1`, [id])
const q2 = t.none(`DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1`, [id])
return t.batch([q1, q2])
})
} }
function disableUser (id) { function disableUser (id) {
const sql = `UPDATE users SET enabled=false WHERE id=$1` return db.tx(t => {
return db.none(sql, [id]) const q1 = t.none(`UPDATE users SET enabled=false WHERE id=$1`, [id])
const q2 = t.none(`DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1`, [id])
return t.batch([q1, q2])
})
} }
module.exports = { module.exports = {

View file

@ -1,3 +1,4 @@
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 { import {
@ -6,10 +7,11 @@ import {
MuiThemeProvider, MuiThemeProvider,
makeStyles makeStyles
} from '@material-ui/core/styles' } from '@material-ui/core/styles'
import { axios } from '@use-hooks/axios' 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 * as R from 'ramda'
import React, { useContext, useState, useEffect } from 'react'
import { import {
useLocation, useLocation,
useHistory, useHistory,
@ -73,7 +75,28 @@ const Main = () => {
const classes = useStyles() const classes = useStyles()
const location = useLocation() const location = useLocation()
const history = useHistory() const history = useHistory()
const { wizardTested, userData } = useContext(AppContext) const { wizardTested, userData, setUserData } = useContext(AppContext)
const GET_USER_DATA = gql`
query userData {
userData {
id
username
role
enabled
last_accessed
last_accessed_from
last_accessed_address
}
}
`
const { data: userResponse, loading } = useQuery(GET_USER_DATA)
useEffect(() => {
if (!R.equals(userData, userResponse?.userData) && !loading)
setUserData(userResponse?.userData)
}, [loading, setUserData, userData, userResponse])
const route = location.pathname const route = location.pathname
@ -109,9 +132,7 @@ const Main = () => {
onClick={onClick} onClick={onClick}
/> />
)} )}
<div className={contentClassName}> <div className={contentClassName}>{!loading && <Routes />}</div>
<Routes />
</div>
</Grid> </Grid>
</main> </main>
</div> </div>
@ -121,31 +142,10 @@ const Main = () => {
const App = () => { const App = () => {
const [wizardTested, setWizardTested] = useState(false) const [wizardTested, setWizardTested] = useState(false)
const [userData, setUserData] = useState(null) const [userData, setUserData] = useState(null)
const [loading, setLoading] = useState(true)
const url =
process.env.NODE_ENV === 'development' ? 'https://localhost:8070' : ''
useEffect(() => {
axios({
method: 'GET',
url: `${url}/user-data`,
withCredentials: true
})
.then(res => {
setLoading(false)
if (res.status === 200) setUserData(res.data.user)
})
.catch(err => {
setLoading(false)
if (err.status === 403) setUserData(null)
})
}, [url])
return ( return (
<AppContext.Provider <AppContext.Provider
value={{ wizardTested, setWizardTested, userData, setUserData }}> value={{ wizardTested, setWizardTested, userData, setUserData }}>
{!loading && (
<Router> <Router>
<ApolloProvider> <ApolloProvider>
<StylesProvider jss={jss}> <StylesProvider jss={jss}>
@ -156,7 +156,6 @@ const App = () => {
</StylesProvider> </StylesProvider>
</ApolloProvider> </ApolloProvider>
</Router> </Router>
)}
</AppContext.Provider> </AppContext.Provider>
) )
} }

View file

@ -4,7 +4,6 @@ import { ApolloClient } from 'apollo-client'
import { ApolloLink } from 'apollo-link' import { ApolloLink } from 'apollo-link'
import { onError } from 'apollo-link-error' import { onError } from 'apollo-link-error'
import { HttpLink } from 'apollo-link-http' import { HttpLink } from 'apollo-link-http'
import * as R from 'ramda'
import React, { useContext } from 'react' import React, { useContext } from 'react'
import { useHistory, useLocation } from 'react-router-dom' import { useHistory, useLocation } from 'react-router-dom'
@ -19,12 +18,10 @@ const getClient = (history, location, setUserData) =>
onError(({ graphQLErrors, networkError }) => { onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path, extensions }) => { graphQLErrors.forEach(({ message, locations, path, extensions }) => {
handle( if (extensions?.code === 'UNAUTHENTICATED') {
{ message, locations, path, extensions }, setUserData(null)
history, if (location.pathname !== '/login') history.push('/login')
location, }
setUserData
)
console.log( console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
) )
@ -52,22 +49,6 @@ const getClient = (history, location, setUserData) =>
} }
}) })
const handle = (apolloError, ...args) => {
const handler = {
UNAUTHENTICATED: (...args) => {
const history = args[0]
const location = args[1]
const setUserData = args[2]
setUserData(null)
if (location.pathname !== '/login') history.push('/login')
}
}
if (!R.has(apolloError.extensions?.code, handler)) return apolloError
return handler[apolloError.extensions?.code](...args)
}
const Provider = ({ children }) => { const Provider = ({ children }) => {
const history = useHistory() const history = useHistory()
const location = useLocation() const location = useLocation()