Initial commit

This commit is contained in:
Luka Romih
2022-12-07 04:43:36 +01:00
commit 257f3c354f
496 changed files with 55373 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
const passport = require('passport')
const LocalStrategy = require('passport-local').Strategy
const RememberMeStrategy = require('passport-remember-me-extended').Strategy
const bcrypt = require('bcrypt')
const db = require('../models/db')
const User = require('../models/user')
const {
rememberMeCookieSettings,
REMEMBER_ME_DURATION_SQL
} = require('../config/settings')
passport.serializeUser((user, done) => {
done(null, user.id)
})
passport.deserializeUser(async (id, done) => {
try {
// TODO Consider storing this data in cache.
// TODO In that case, make sure to invalidate it at every (relevant) data change.
const user = await User.fetchDeserializedDataById(id)
// TODO Consider what to do if no user was found?
done(null, user)
} catch (error) {
done(error)
}
})
passport.use(
new LocalStrategy(
{ usernameField: 'usernameOrEmail' },
async (usernameOrEmail, password, done) => {
try {
const { rows } = await db.query(
'SELECT id, status, bcrypt_hash FROM "user" WHERE username = $1 OR email = $1',
[usernameOrEmail]
)
const user = rows[0]
if (!user) {
return done(null, false, {
message: 'Nepravilno uporabniško ime, elektronski naslov ali geslo.'
})
}
if (user.status !== 'active') {
return done(null, false, {
message:
'Uporabniški račun še ni aktiviran. Kliknite aktivacijsko povezavo, katero smo vam poslali po elektronski pošti.'
})
}
const isCorrectPassword = await bcrypt.compare(
password,
user.bcrypt_hash
)
if (!isCorrectPassword) {
return done(null, false, {
message: 'Nepravilno uporabniško ime, elektronski naslov ali geslo.'
})
}
delete user.bcrypt_hash
done(null, user)
} catch (error) {
done(error)
}
}
)
)
passport.use(
new RememberMeStrategy(
{ cookie: rememberMeCookieSettings, signed: true },
async (token, done) => {
try {
const text = `
DELETE
FROM user_token_remember_me t
WHERE
t.token = $1
AND AGE(NOW(), t.time_created) < $2::INTERVAL
RETURNING (SELECT u.id FROM "user" u where u.id = t.user_id)
`
const values = [token, REMEMBER_ME_DURATION_SQL]
let {
rows: [user]
} = await db.query(text, values)
if (!user) return done(null, false)
user = await User.fetchDeserializedDataById(user.id)
done(null, user)
} catch (error) {
done(error)
}
},
async (user, done) => {
try {
const token = await User.generateRememberMeToken()
await User.saveRememberMeToken(user, token)
done(null, token)
} catch (error) {
done(error)
}
}
)
)
module.exports = passport
+6
View File
@@ -0,0 +1,6 @@
const { getInstanceSetting } = require('../models/helpers')
exports.enhanceLocals = async (req, res, next) => {
res.locals.portalCode = await getInstanceSetting('portal_code')
next()
}
+54
View File
@@ -0,0 +1,54 @@
const session = require('express-session')
const RedisStore = require('connect-redis')(session)
const { secret, cookiesSecure } = require('../config/keys')
const redisClient = require('../models/cache')
const SESSION_DURATION = 1000 * 60 * 60 * 2 // 2 hours.
const SESSION_ID_COOKIE_NAME = 'sid'
// const RETRY_PERIOD = 3000 // 3 seconds.
const options = {
cookie: { maxAge: SESSION_DURATION, sameSite: 'lax', secure: cookiesSecure },
secret,
name: SESSION_ID_COOKIE_NAME,
rolling: true,
resave: false,
saveUninitialized: false,
store: new RedisStore({ client: redisClient })
}
module.exports = session(options)
// Aditional checking logic below.
// Export it instead of the middleware above if it proves necessary.
// const sessionMiddleware = session(options)
// function verifiedSession(req, res, next) {
// let tries = 3
// let timeoutId
// function lookupSession(err) {
// clearTimeout(timeoutId)
// if (err) return next(err)
// if (req.session !== undefined) return next()
// tries -= 1
// if (tries < 0) {
// return next(Error('Session store unresponsive'))
// }
// sessionMiddleware(req, res, lookupSession)
// if (req.session === undefined) {
// timeoutId = setTimeout(lookupSession, RETRY_PERIOD)
// }
// }
// lookupSession()
// }
// module.exports = verifiedSession
+23
View File
@@ -0,0 +1,23 @@
const settings = {}
const { getInstanceSetting } = require('../models/helpers')
settings.prepareRequiredSettings = async (req, res, next) => {
const isExtractionEnabled =
(await getInstanceSetting('is_extraction_enabled')) === 'T'
const isDictionariesEnabled =
(await getInstanceSetting('is_dictionaries_enabled')) === 'T'
const isConsultancyEnabled =
(await getInstanceSetting('is_consultancy_enabled')) === 'T'
// req.extractionEnabled = isExtractionEnabled
// req.dictionariesEnabled = isDictionariesEnabled
// req.consultancyEnabled = isConsultancyEnabled
res.locals.extractionEnabled = isExtractionEnabled
res.locals.dictionariesEnabled = isDictionariesEnabled
res.locals.consultancyEnabled = isConsultancyEnabled
next()
}
module.exports = settings
+95
View File
@@ -0,0 +1,95 @@
const user = {}
user.enhance = (req, res, next) => {
// Make req.user available to view engine.
res.locals.user = req.user
// Extend the req.user object with 3 rolechecking methods.
if (!req.user) return next()
req.user.hasRole = hasRole
req.user.hasDictionaryRole = hasDictionaryRole
req.user.hasAnyDictionaryRole = hasAnyDictionaryRole
req.user.isEditorOfConsultancyEntry = isEditorOfConsultancyEntry
next()
}
user.isDictionaryAdmin = (req, res, next) => {
const { dictionaryId } = req.params
const isAdmin = req.user.hasDictionaryRole(dictionaryId, 'administration')
if (isAdmin) return next()
if (req.isAjax) return res.status(400).end()
res.redirect(req.baseUrl)
}
user.isDictionaryEditor = (req, res, next) => {
const { dictionaryId } = req.params
const isEditor = req.user.hasAnyDictionaryRole(dictionaryId)
if (isEditor) return next()
if (req.isAjax) return res.status(400).end()
res.redirect(req.baseUrl)
}
/**
* Checks if the user has a specific role.
*
* @param {'portal admin'|'dictionaries admin'|'consultancy admin'|'consultant'|'editor'} roleName
* @returns {boolean}
*/
function hasRole(roleName) {
return this.userRoles.some(role => role.roleName === roleName)
}
/**
* Checks if the user has a specific dictionary role.
*
* @param {number} dictionaryId
* @param {'administration'|'terminologyReview'|'languageReview'|'editing'} roleName
* @returns {boolean}
*/
function hasDictionaryRole(dictionaryId, roleName) {
return this.userRoles.some(role => {
const validDictionaryRoles = [
'administration',
'editing',
'terminologyReview',
'languageReview'
]
return (
role.dictionaryId === +dictionaryId &&
role.roleName === 'editor' &&
validDictionaryRoles.includes(roleName) &&
role[roleName]
)
})
}
/**
* Checks if the user has any dictionary role for the specified dictionary.
*
* @param {number} dictionaryId
* @returns {boolean}
*/
function hasAnyDictionaryRole(dictionaryId) {
return this.userRoles.some(role => {
return role.dictionaryId === +dictionaryId && role.roleName === 'editor'
})
}
/**
* Checks if the user is an editor of the speficied consultancy entry.
*
* @param {number} consultancyEntryId
* @returns {boolean}
*/
function isEditorOfConsultancyEntry(consultancyEntryId) {
return this.assignedConsultancyEntries.some(entry => {
return entry.id === +consultancyEntryId
})
}
module.exports = user