Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x |
import bcrypt from 'bcrypt'
import { db } from '../../db'
import { ModelTypes } from '../../cache/dbs/index.generated'
import { lockUserAndThrow } from './userLockService'
import { getId, timeout, random } from 'topkat-utils'
import { getPluginConfig } from '../../plugins/pluginSystem'
const tooMuchPasswordMessages = 'tooMuchPasswordAttempts'
export async function comparePasswordAddAttemptAndLockIfNecessary(
ctx: Ctx,
password: string,
hash: string,
userOrId: ModelTypes['user'] | string,
conf: {
loginRetrialCountResetTimeMinutes?: number
maxPasswordRetry?: number
throwIfError?: boolean
} = {}
): Promise<boolean> {
const loginRetrialCountResetTimeMs = (conf.loginRetrialCountResetTimeMinutes || 30) * 60 * 1000
await timeout(random(1, 80)) // anti timer attack
const user = typeof userOrId === 'string' ? await db.user.getById(ctx, userOrId, { triggerErrorIfNotSet: true }) : userOrId
const isValid = await bcrypt.compare(password, hash)
if (isValid) {
await db.user.update(ctx.GM, getId(user), {
lastPasswordCompareTime: null,
passwordRetrialNb: 0,
...(user.lockedReason?.includes(tooMuchPasswordMessages) ? {
isLocked: false,
lockUntil: null,
} : {}),
})
return true
} else {
if ((new Date(user.lastPasswordCompareTime)).getTime() < Date.now() - loginRetrialCountResetTimeMs) {
// THIS IS THE FIRST ATTEMPT IN A LONG TIME so we reset password attempts
await db.user.update(ctx.GM, getId(user), {
lastPasswordCompareTime: new Date(),
passwordRetrialNb: 1,
})
} else {
// MULTIPLE ATTEMPTS, increment attemps number and lock user is needed
if (user.passwordRetrialNb >= (conf.maxPasswordRetry || 4)) {
// TOO MUCH ATTEMPTS
await lockUserAndThrow(ctx, getId(user), tooMuchPasswordMessages)
} else {
// ACCEPTABLE NB OF ATTEMPTS
await db.user.update(ctx.GM, getId(user), {
lastPasswordCompareTime: new Date(),
$inc: { passwordRetrialNb: 1 }
})
}
}
if (typeof conf.throwIfError === 'undefined' || conf.throwIfError === true) throw ctx.error.wrongPassword({ passwordRetrialNb: user.passwordRetrialNb })
return false
}
}
export async function encryptPassword(password: string): Promise<string> {
const { saltRoundsForPasswordEncryption } = getPluginConfig('GDmanagedLogin')
const salt = bcrypt.genSaltSync(saltRoundsForPasswordEncryption || 11)
return bcrypt.hashSync(password, salt)
} |