mirror of
https://github.com/BobbyWibowo/lolisafe.git
synced 2025-02-23 21:59:05 +00:00
refactor: generateUniqueToken -> getUniqueToken
this now matches lifecycle with similar functions in upload and album controllers also added a new util function .mask() for basic string masking
This commit is contained in:
parent
b7dcf30578
commit
4591b8bb42
@ -6,7 +6,6 @@ const perms = require('./permissionController')
|
|||||||
const tokens = require('./tokenController')
|
const tokens = require('./tokenController')
|
||||||
const utils = require('./utilsController')
|
const utils = require('./utilsController')
|
||||||
const ClientError = require('./utils/ClientError')
|
const ClientError = require('./utils/ClientError')
|
||||||
const ServerError = require('./utils/ServerError')
|
|
||||||
const config = require('./../config')
|
const config = require('./../config')
|
||||||
|
|
||||||
// Don't forget to update min/max length of text inputs in auth.njk
|
// Don't forget to update min/max length of text inputs in auth.njk
|
||||||
@ -96,10 +95,7 @@ self.register = async (req, res) => {
|
|||||||
|
|
||||||
const hash = await bcrypt.hash(password, saltRounds)
|
const hash = await bcrypt.hash(password, saltRounds)
|
||||||
|
|
||||||
const token = await tokens.generateUniqueToken()
|
const token = await tokens.getUniqueToken(res)
|
||||||
if (!token) {
|
|
||||||
throw new ServerError('Failed to allocate a unique token. Try again?')
|
|
||||||
}
|
|
||||||
|
|
||||||
await utils.db.table('users')
|
await utils.db.table('users')
|
||||||
.insert({
|
.insert({
|
||||||
@ -110,8 +106,8 @@ self.register = async (req, res) => {
|
|||||||
permission: perms.permissions.user,
|
permission: perms.permissions.user,
|
||||||
registration: Math.floor(Date.now() / 1000)
|
registration: Math.floor(Date.now() / 1000)
|
||||||
})
|
})
|
||||||
|
|
||||||
utils.invalidateStatsCache('users')
|
utils.invalidateStatsCache('users')
|
||||||
tokens.onHold.delete(token)
|
|
||||||
|
|
||||||
return res.json({ success: true, token })
|
return res.json({ success: true, token })
|
||||||
}
|
}
|
||||||
@ -195,10 +191,7 @@ self.createUser = async (req, res) => {
|
|||||||
|
|
||||||
const hash = await bcrypt.hash(password, saltRounds)
|
const hash = await bcrypt.hash(password, saltRounds)
|
||||||
|
|
||||||
const token = await tokens.generateUniqueToken()
|
const token = await tokens.getUniqueToken(res)
|
||||||
if (!token) {
|
|
||||||
throw new ServerError('Failed to allocate a unique token. Try again?')
|
|
||||||
}
|
|
||||||
|
|
||||||
await utils.db.table('users')
|
await utils.db.table('users')
|
||||||
.insert({
|
.insert({
|
||||||
@ -209,8 +202,8 @@ self.createUser = async (req, res) => {
|
|||||||
permission,
|
permission,
|
||||||
registration: Math.floor(Date.now() / 1000)
|
registration: Math.floor(Date.now() / 1000)
|
||||||
})
|
})
|
||||||
|
|
||||||
utils.invalidateStatsCache('users')
|
utils.invalidateStatsCache('users')
|
||||||
tokens.onHold.delete(token)
|
|
||||||
|
|
||||||
return res.json({ success: true, username, password, group })
|
return res.json({ success: true, username, password, group })
|
||||||
}
|
}
|
||||||
|
@ -3,17 +3,23 @@ const perms = require('./permissionController')
|
|||||||
const utils = require('./utilsController')
|
const utils = require('./utilsController')
|
||||||
const ClientError = require('./utils/ClientError')
|
const ClientError = require('./utils/ClientError')
|
||||||
const ServerError = require('./utils/ServerError')
|
const ServerError = require('./utils/ServerError')
|
||||||
|
const logger = require('./../logger')
|
||||||
|
|
||||||
const self = {
|
const self = {
|
||||||
tokenLength: 64,
|
tokenLength: 64,
|
||||||
tokenMaxTries: 3,
|
tokenMaxTries: 3,
|
||||||
onHold: new Set()
|
|
||||||
|
onHold: new Set() // temporarily held random tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
self.generateUniqueToken = async () => {
|
self.getUniqueToken = async res => {
|
||||||
for (let i = 0; i < self.tokenMaxTries; i++) {
|
for (let i = 0; i < self.tokenMaxTries; i++) {
|
||||||
const token = randomstring.generate(self.tokenLength)
|
const token = randomstring.generate(self.tokenLength)
|
||||||
if (self.onHold.has(token)) continue
|
|
||||||
|
if (self.onHold.has(token)) {
|
||||||
|
logger.debug(`Token ${utils.mask(token)} is currently held by another request (${i + 1}/${utils.idMaxTries}).`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Put token on-hold (wait for it to be inserted to DB)
|
// Put token on-hold (wait for it to be inserted to DB)
|
||||||
self.onHold.add(token)
|
self.onHold.add(token)
|
||||||
@ -24,13 +30,36 @@ self.generateUniqueToken = async () => {
|
|||||||
.first()
|
.first()
|
||||||
if (user) {
|
if (user) {
|
||||||
self.onHold.delete(token)
|
self.onHold.delete(token)
|
||||||
|
logger.debug(`User with token ${utils.mask(token)} already exists (${i + 1}/${utils.idMaxTries}).`)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unhold token once the Response has been sent
|
||||||
|
if (res) {
|
||||||
|
// Keep in an array for future-proofing
|
||||||
|
// if a single Request needs to generate multiple tokens
|
||||||
|
if (!res.locals.tokens) {
|
||||||
|
res.locals.tokens = []
|
||||||
|
res.once('finish', () => { self.unholdTokens(res) })
|
||||||
|
}
|
||||||
|
res.locals.tokens.push(token)
|
||||||
|
}
|
||||||
|
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
throw new ServerError('Failed to allocate a unique token. Try again?')
|
||||||
|
}
|
||||||
|
|
||||||
|
self.unholdTokens = res => {
|
||||||
|
if (!res.locals.tokens) return
|
||||||
|
|
||||||
|
for (const token of res.locals.tokens) {
|
||||||
|
self.onHold.delete(token)
|
||||||
|
logger.debug(`Unheld token ${utils.mask(token)}.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete res.locals.tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
self.verify = async (req, res) => {
|
self.verify = async (req, res) => {
|
||||||
@ -84,10 +113,7 @@ self.list = async (req, res) => {
|
|||||||
self.change = async (req, res) => {
|
self.change = async (req, res) => {
|
||||||
const user = await utils.authorize(req, 'token')
|
const user = await utils.authorize(req, 'token')
|
||||||
|
|
||||||
const newToken = await self.generateUniqueToken()
|
const newToken = await self.getUniqueToken(res)
|
||||||
if (!newToken) {
|
|
||||||
throw new ServerError('Failed to allocate a unique token. Try again?')
|
|
||||||
}
|
|
||||||
|
|
||||||
await utils.db.table('users')
|
await utils.db.table('users')
|
||||||
.where('token', user.token)
|
.where('token', user.token)
|
||||||
@ -95,7 +121,6 @@ self.change = async (req, res) => {
|
|||||||
token: newToken,
|
token: newToken,
|
||||||
timestamp: Math.floor(Date.now() / 1000)
|
timestamp: Math.floor(Date.now() / 1000)
|
||||||
})
|
})
|
||||||
self.onHold.delete(newToken)
|
|
||||||
|
|
||||||
return res.json({ success: true, token: newToken })
|
return res.json({ success: true, token: newToken })
|
||||||
}
|
}
|
||||||
|
@ -362,7 +362,7 @@ self.escape = string => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.stripIndents = string => {
|
self.stripIndents = string => {
|
||||||
if (!string) return
|
if (!string) return string
|
||||||
const result = string.replace(/^[^\S\n]+/gm, '')
|
const result = string.replace(/^[^\S\n]+/gm, '')
|
||||||
const match = result.match(/^[^\S\n]*(?=\S)/gm)
|
const match = result.match(/^[^\S\n]*(?=\S)/gm)
|
||||||
const indent = match && Math.min(...match.map(el => el.length))
|
const indent = match && Math.min(...match.map(el => el.length))
|
||||||
@ -373,6 +373,19 @@ self.stripIndents = string => {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.mask = string => {
|
||||||
|
if (!string) return string
|
||||||
|
const max = Math.min(Math.floor(string.length / 2), 8)
|
||||||
|
const fragment = Math.floor(max / 2)
|
||||||
|
if (string.length <= fragment) {
|
||||||
|
return '*'.repeat(string.length)
|
||||||
|
} else {
|
||||||
|
return string.substring(0, fragment) +
|
||||||
|
'*'.repeat(Math.min(string.length - (fragment * 2), 4)) +
|
||||||
|
string.substring(string.length - fragment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.assertRequestType = (req, type) => {
|
self.assertRequestType = (req, type) => {
|
||||||
if (!req.is(type)) {
|
if (!req.is(type)) {
|
||||||
throw new ClientError(`Request Content-Type must be ${type}.`)
|
throw new ClientError(`Request Content-Type must be ${type}.`)
|
||||||
|
Loading…
Reference in New Issue
Block a user