refactor: user agent parsing
This commit is contained in:
parent
791b275cdf
commit
839e6aae47
8 changed files with 21 additions and 63 deletions
|
|
@ -68,10 +68,12 @@ app.use('/front-camera-photo', serveStatic(frontCameraBasedir, { index: false })
|
||||||
|
|
||||||
app.get('/api/register', (req, res, next) => {
|
app.get('/api/register', (req, res, next) => {
|
||||||
const otp = req.query.otp
|
const otp = req.query.otp
|
||||||
|
const ua = req.headers['user-agent']
|
||||||
|
const ip = req.ip
|
||||||
|
|
||||||
if (!otp) return next()
|
if (!otp) return next()
|
||||||
|
|
||||||
return login.register(req)
|
return login.register(otp, ua, ip)
|
||||||
.then(r => {
|
.then(r => {
|
||||||
if (r.expired) return res.status(401).send('OTP expired, generate new registration link')
|
if (r.expired) return res.status(401).send('OTP expired, generate new registration link')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,8 @@ const typeDefs = gql`
|
||||||
token: String!
|
token: String!
|
||||||
name: String!
|
name: String!
|
||||||
created: Date!
|
created: Date!
|
||||||
|
user_agent: String!
|
||||||
|
ip_address: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Transaction {
|
type Transaction {
|
||||||
|
|
@ -224,7 +226,7 @@ const typeDefs = gql`
|
||||||
transactionsCsv(from: Date, until: Date, limit: Int, offset: Int): String
|
transactionsCsv(from: Date, until: Date, limit: Int, offset: Int): String
|
||||||
accounts: JSONObject
|
accounts: JSONObject
|
||||||
config: JSONObject
|
config: JSONObject
|
||||||
userTokens(browser: String!, os: String!): [UserToken]
|
userTokens: [UserToken]
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MachineAction {
|
enum MachineAction {
|
||||||
|
|
@ -283,7 +285,7 @@ const resolvers = {
|
||||||
transactions.batch(from, until, limit, offset).then(parseAsync),
|
transactions.batch(from, until, limit, offset).then(parseAsync),
|
||||||
config: () => settingsLoader.loadLatestConfigOrNone(),
|
config: () => settingsLoader.loadLatestConfigOrNone(),
|
||||||
accounts: () => settingsLoader.loadAccounts(),
|
accounts: () => settingsLoader.loadAccounts(),
|
||||||
userTokens: (...[, { browser, os }]) => tokenManager.getTokenList(browser, os)
|
userTokens: () => tokenManager.getTokenList()
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
machineAction: (...[, { deviceId, action, cassette1, cassette2, newName }]) => machineAction({ deviceId, action, cassette1, cassette2, newName }),
|
machineAction: (...[, { deviceId, action, cassette1, cassette2, newName }]) => machineAction({ deviceId, action, cassette1, cassette2, newName }),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
|
|
||||||
const browserOS = require('../../new-lamassu-admin/src/utils/browser-os')
|
|
||||||
const db = require('../db')
|
const db = require('../db')
|
||||||
|
|
||||||
function generateOTP (name) {
|
function generateOTP (name) {
|
||||||
|
|
@ -22,18 +21,15 @@ function validateOTP (otp) {
|
||||||
.catch(() => ({ success: false, expired: false }))
|
.catch(() => ({ success: false, expired: false }))
|
||||||
}
|
}
|
||||||
|
|
||||||
function register (req) {
|
function register (otp, ua, ip) {
|
||||||
const otp = req.query.otp
|
|
||||||
|
|
||||||
return validateOTP(otp)
|
return validateOTP(otp)
|
||||||
.then(r => {
|
.then(r => {
|
||||||
if (!r.success) return r
|
if (!r.success) return r
|
||||||
|
|
||||||
const deviceInfo = browserOS.getInformation(req.headers['user-agent'])
|
|
||||||
const token = crypto.randomBytes(32).toString('hex')
|
const token = crypto.randomBytes(32).toString('hex')
|
||||||
const sql = 'insert into user_tokens (token, name, browser_version, os_version, ip_address) values ($1, $2, $3, $4, $5)'
|
const sql = 'insert into user_tokens (token, name, user_agent, ip_address) values ($1, $2, $3, $4)'
|
||||||
|
|
||||||
return db.none(sql, [token, r.name, deviceInfo.browser, deviceInfo.OS, browserOS.getRequestIP(req)])
|
return db.none(sql, [token, r.name, ua, ip])
|
||||||
.then(() => ({ success: true, token: token }))
|
.then(() => ({ success: true, token: token }))
|
||||||
})
|
})
|
||||||
.catch(() => ({ success: false, expired: false }))
|
.catch(() => ({ success: false, expired: false }))
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
const db = require('./db')
|
const db = require('./db')
|
||||||
|
|
||||||
function getTokenList (browser, os) {
|
function getTokenList () {
|
||||||
const sql = `select * from user_tokens where browser_version=$1 and os_version=$2`
|
const sql = `select * from user_tokens`
|
||||||
return db.any(sql, [browser, os])
|
return db.any(sql)
|
||||||
}
|
}
|
||||||
|
|
||||||
function revokeToken (token) {
|
function revokeToken (token) {
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@ const db = require('./db')
|
||||||
|
|
||||||
exports.up = function (next) {
|
exports.up = function (next) {
|
||||||
var sql = [
|
var sql = [
|
||||||
'ALTER TABLE user_tokens ADD COLUMN browser_version text',
|
'ALTER TABLE user_tokens ADD COLUMN user_agent text',
|
||||||
'ALTER TABLE user_tokens ADD COLUMN os_version text',
|
|
||||||
'ALTER TABLE user_tokens ADD COLUMN ip_address inet',
|
'ALTER TABLE user_tokens ADD COLUMN ip_address inet',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,22 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import Title from 'src/components/Title'
|
|
||||||
import { IconButton } from 'src/components/buttons'
|
import { IconButton } from 'src/components/buttons'
|
||||||
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
import DataTable from 'src/components/tables/DataTable'
|
import DataTable from 'src/components/tables/DataTable'
|
||||||
import { ReactComponent as DeleteIcon } from 'src/styling/icons/action/delete/enabled.svg'
|
import { ReactComponent as DeleteIcon } from 'src/styling/icons/action/delete/enabled.svg'
|
||||||
import * as browserOS from 'src/utils/browser-os'
|
|
||||||
|
|
||||||
import { mainStyles } from './TokenManagement.styles'
|
|
||||||
|
|
||||||
const useStyles = makeStyles(mainStyles)
|
|
||||||
|
|
||||||
const GET_USER_TOKENS = gql`
|
const GET_USER_TOKENS = gql`
|
||||||
query userTokens($browser: String!, $os: String!) {
|
query userTokens {
|
||||||
userTokens(browser: $browser, os: $os) {
|
userTokens {
|
||||||
token
|
token
|
||||||
name
|
name
|
||||||
created
|
created
|
||||||
|
user_agent
|
||||||
|
ip_address
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
@ -34,16 +30,7 @@ const REVOKE_USER_TOKEN = gql`
|
||||||
`
|
`
|
||||||
|
|
||||||
const Tokens = () => {
|
const Tokens = () => {
|
||||||
const classes = useStyles()
|
const { data: tknResponse } = useQuery(GET_USER_TOKENS)
|
||||||
|
|
||||||
const userAgent = browserOS.getInformation(navigator.userAgent)
|
|
||||||
|
|
||||||
const { data: tknResponse } = useQuery(GET_USER_TOKENS, {
|
|
||||||
variables: {
|
|
||||||
browser: `${userAgent.browser}`,
|
|
||||||
os: `${userAgent.OS}`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const [revokeToken] = useMutation(REVOKE_USER_TOKEN, {
|
const [revokeToken] = useMutation(REVOKE_USER_TOKEN, {
|
||||||
refetchQueries: () => ['userTokens']
|
refetchQueries: () => ['userTokens']
|
||||||
|
|
@ -96,11 +83,7 @@ const Tokens = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={classes.titleWrapper}>
|
<TitleSection title="Token Management" />
|
||||||
<div className={classes.titleAndButtonsContainer}>
|
|
||||||
<Title>Token Management</Title>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<DataTable
|
<DataTable
|
||||||
elements={elements}
|
elements={elements}
|
||||||
data={R.path(['userTokens'])(tknResponse)}
|
data={R.path(['userTokens'])(tknResponse)}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
import baseStyles from 'src/pages/Logs.styles'
|
|
||||||
|
|
||||||
const { titleWrapper, titleAndButtonsContainer } = baseStyles
|
|
||||||
|
|
||||||
const mainStyles = {
|
|
||||||
titleWrapper,
|
|
||||||
titleAndButtonsContainer
|
|
||||||
}
|
|
||||||
|
|
||||||
export { mainStyles }
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
const parser = require('ua-parser-js')
|
|
||||||
|
|
||||||
function getRequestIP(req) {
|
|
||||||
return req.ip
|
|
||||||
}
|
|
||||||
|
|
||||||
function getInformation(uaString) {
|
|
||||||
const userAgent = parser(uaString)
|
|
||||||
const browser = `${userAgent.browser.name} ${userAgent.browser.version}`
|
|
||||||
const OS = `${userAgent.os.name} ${userAgent.os.version}`
|
|
||||||
return { browser: browser, OS: OS }
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { getRequestIP, getInformation }
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue