You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

314 lines
6.7 KiB
JavaScript

const pg = require('../pg')
const joinjs = require('join-js').default;
const debug = require('debug')('sos:db:user')
const mappings = require('../mappings')
const dbUtil = require('../util')
const uuid = require('uuid')
const bcrypt = require('bcrypt')
const session = require('./session')
const user = module.exports = {}
const saltRounds = parseInt(process.env.PW_SALTROUNDS, 10) || 10
user.findById = async (user_uuid) => {
const query = {
text: 'select * from "user" where user_uuid = $1',
values: [
user_uuid
]
}
debug(query);
const {rows} = await pg.query(query)
return joinjs.map(rows, mappings, 'userMap', 'user_')[0];
}
user.findByEmail = async (email) => {
const query = {
text: 'select * from sos.v_user where user_email = $1',
values: [
email
]
}
debug(query);
const {rows} = await pg.query(query)
return joinjs.map(rows, mappings, 'userMap', 'user_')[0]
}
user.register = async (email, password) => {
const hash = await bcrypt.hash(password, saltRounds)
const query = {
text: 'select * from sos.register_user($1, $2)',
values: [
email,
hash
]
}
debug(query);
const {rows} = await pg.query(query)
return joinjs.map(rows, mappings, 'userMap', 'user_')[0];
}
user.import = async (email, hash) => {
const query = {
text: 'select * from sos.register_user($1, $2)',
values: [
email,
hash
]
}
debug(query);
const {rows} = await pg.query(query)
return joinjs.map(rows, mappings, 'userMap', 'user_')[0];
}
user.updateRegistrationDate = async (uuid, timestamp, emailConfirmed) => {
await dbUtil.executeQuery({
query: {
text: `
update sos."user"
set (
user_time_registered,
user_time_password_changed
) = (
$2,
$2
)
where user_uuid = $1
`,
values: [uuid, timestamp]
},
returnType: 'user',
tablePrefix: 'user_',
single: true
})
if (!emailConfirmed) return;
await dbUtil.executeQuery({
query: {
text: `
update sos."user"
set
user_time_email_confirmed
=
$2
where user_uuid = $1
`,
values: [uuid, timestamp]
},
returnType: 'user',
tablePrefix: 'user_',
single: true
})
}
user.login = async (email, password) => {
const _user = await user.findByEmail(email)
if(!_user){
// Avoid early exit timing difference
await bcrypt.hash(password, saltRounds)
5 years ago
return null
}
const passwordCorrect = await bcrypt.compare(password, _user.password_hash)
if(!passwordCorrect)
5 years ago
return null
return _user
}
user.changePassword = async (user_uuid, oldPassword, newPassword) => {
const _user = await user.findById(user_uuid)
if(!_user){
// Avoid early exit timing difference
await bcrypt.hash(oldPassword, saltRounds)
return null
}
const passwordCorrect = await bcrypt.compare(oldPassword, _user.password_hash)
if(!passwordCorrect)
return null
return await user.overwritePassword(user_uuid, newPassword)
}
user.overwritePassword = async (user_uuid, new_password) => {
const newHash = await bcrypt.hash(new_password, saltRounds)
const query = {
text: 'select * from sos.change_password($1, $2)',
values: [
user_uuid,
newHash
]
}
debug(query);
const {rows} = await pg.query(query)
return joinjs.map(rows, mappings, 'userMap', 'user_')[0];
}
user.getOpenEmailLinks = (user_uuid) =>
dbUtil.executeFunction({
name: 'get_open_email_links_for_user',
params: [
user_uuid
],
returnType: 'emailLink',
tablePrefix: 'email_link_',
single: false
})
user.createLoginLink = async (user_uuid) => {
const linkCode = uuid.v4()
const hash = await bcrypt.hash(linkCode, saltRounds)
const link_record = await dbUtil.executeFunction({
name: 'create_login_link',
params: [
user_uuid,
'2 hours',
hash,
'email_confirm'
],
returnType: 'emailLink',
tablePrefix: 'email_link_',
single: true
})
return `${process.env.EXTERNAL_URL}/api/email/confirm/${link_record.uuid}?key=${linkCode}`
}
user.verifyLoginLink = async (link_uuid, key) => {
const link_record = await dbUtil.executeQuery({
query: {
text: 'select * from sos.email_link where email_link_uuid = $1 and email_link_time_used is null',
values: [link_uuid]
},
returnType: 'emailLink',
tablePrefix: 'email_link_',
single: true
})
if(!link_record){
// Avoid early exit timing difference
await bcrypt.hash(key, saltRounds)
return null
}
const valid = await bcrypt.compare(key, link_record.login_hash)
if(!valid) return null
if(link_record.type !== 'email_confirm') return null
return link_record
}
user.createPasswordReset = async user_uuid => {
const linkCode = uuid.v4()
const hash = await bcrypt.hash(linkCode, saltRounds)
const link_record = await dbUtil.executeFunction({
name: 'create_login_link',
params: [
user_uuid,
'2 hours',
hash,
'password_reset'
],
returnType: 'emailLink',
tablePrefix: 'email_link_',
single: true
})
return `${process.env.EXTERNAL_URL}/account/reset-password?id=${link_record.uuid}&key=${linkCode}`
}
user.verifyPasswordReset = async (link_uuid, key) => {
const link_record = await dbUtil.executeQuery({
query: {
text: 'select * from sos.email_link where email_link_uuid = $1 and email_link_time_used is null',
values: [link_uuid]
},
returnType: 'emailLink',
tablePrefix: 'email_link_',
single: true
})
if(!link_record){
// Avoid early exit timing difference
await bcrypt.hash(key, saltRounds)
return null
}
const valid = await bcrypt.compare(key, link_record.login_hash)
if(!valid) return null
if(link_record.type !== 'password_reset') return null
return user.findById(link_record.user_uuid)
}
user.markLinkUsed = link_uuid =>
dbUtil.executeFunction({
name: 'set_link_used',
params: [link_uuid],
returnType: 'emailLink',
tablePrefix: 'email_link_',
single: true
})
user.markEmailVerified = user_uuid =>
dbUtil.executeFunction({
name: 'set_user_email_verified',
params: [user_uuid],
returnType: 'user',
single: true
})
user.findAll = () =>
dbUtil.executeQuery({
query: 'select * from sos.v_user',
returnType: 'user'
})
user.makeAdmin = user_uuid =>
dbUtil.executeFunction({
name: 'set_user_admin',
params: [
user_uuid,
true
],
returnType: 'user',
single: true
})
user.removeAdmin = user_uuid =>
dbUtil.executeFunction({
name: 'set_user_admin',
params: [
user_uuid,
false
],
returnType: 'user',
single: true
})