feat: user menu + jwt certs + UI fixes

This commit is contained in:
Nicolas Giard
2018-12-02 21:42:43 -05:00
parent 74aa09f39c
commit f856da074e
25 changed files with 327 additions and 194 deletions

View File

@@ -1,35 +1,7 @@
/* global WIKI */
const Promise = require('bluebird')
const express = require('express')
const router = express.Router()
const ExpressBrute = require('express-brute')
const ExpressBruteRedisStore = require('express-brute-redis')
const jwt = require('jsonwebtoken')
const moment = require('moment')
const _ = require('lodash')
/**
* Setup Express-Brute
*/
const EBstore = new ExpressBruteRedisStore({
client: WIKI.redis
})
const bruteforce = new ExpressBrute(EBstore, {
freeRetries: 5,
minWait: 60 * 1000,
maxWait: 5 * 60 * 1000,
refreshTimeoutOnRequest: false,
failCallback (req, res, next, nextValidRequestDate) {
req.flash('alert', {
class: 'error',
title: WIKI.lang.t('auth:errors.toomanyattempts'),
message: WIKI.lang.t('auth:errors.toomanyattemptsmsg', { time: moment(nextValidRequestDate).fromNow() }),
iconClass: 'fa-times'
})
res.redirect('/login')
}
})
/**
* Login form
@@ -38,72 +10,6 @@ router.get('/login', function (req, res, next) {
res.render('login')
})
router.post('/login', bruteforce.prevent, function (req, res, next) {
new Promise((resolve, reject) => {
// [1] LOCAL AUTHENTICATION
WIKI.auth.passport.authenticate('local', { session: false }, function (err, user, info) {
if (err) { return reject(err) }
if (!user) { return reject(new Error('INVALID_LOGIN')) }
resolve(user)
})(req, res, next)
}).catch({ message: 'INVALID_LOGIN' }, err => {
if (_.has(WIKI.config.auth.strategy, 'ldap')) {
// [2] LDAP AUTHENTICATION
return new Promise((resolve, reject) => {
WIKI.auth.passport.authenticate('ldapauth', { session: false }, function (err, user, info) {
if (err) { return reject(err) }
if (info && info.message) { return reject(new Error(info.message)) }
if (!user) { return reject(new Error('INVALID_LOGIN')) }
resolve(user)
})(req, res, next)
})
} else {
throw err
}
}).then((user) => {
// LOGIN SUCCESS
return req.logIn(user, { session: false }, function (err) {
if (err) { return next(err) }
req.brute.reset(function () {
return res.redirect('/')
})
})
}).catch(err => {
// LOGIN FAIL
if (err.message === 'INVALID_LOGIN') {
req.flash('alert', {
title: WIKI.lang.t('auth:errors.invalidlogin'),
message: WIKI.lang.t('auth:errors.invalidloginmsg')
})
return res.redirect('/login')
} else {
req.flash('alert', {
title: WIKI.lang.t('auth:errors.loginerror'),
message: err.message
})
return res.redirect('/login')
}
})
})
/**
* Social Login
*/
router.get('/login/ms', WIKI.auth.passport.authenticate('windowslive', { scope: ['wl.signin', 'wl.basic', 'wl.emails'] }))
router.get('/login/google', WIKI.auth.passport.authenticate('google', { scope: ['profile', 'email'] }))
router.get('/login/facebook', WIKI.auth.passport.authenticate('facebook', { scope: ['public_profile', 'email'] }))
router.get('/login/github', WIKI.auth.passport.authenticate('github', { scope: ['user:email'] }))
router.get('/login/slack', WIKI.auth.passport.authenticate('slack', { scope: ['identity.basic', 'identity.email'] }))
router.get('/login/azure', WIKI.auth.passport.authenticate('azure_ad_oauth2'))
router.get('/login/ms/callback', WIKI.auth.passport.authenticate('windowslive', { failureRedirect: '/login', successRedirect: '/' }))
router.get('/login/google/callback', WIKI.auth.passport.authenticate('google', { failureRedirect: '/login', successRedirect: '/' }))
router.get('/login/facebook/callback', WIKI.auth.passport.authenticate('facebook', { failureRedirect: '/login', successRedirect: '/' }))
router.get('/login/github/callback', WIKI.auth.passport.authenticate('github', { failureRedirect: '/login', successRedirect: '/' }))
router.get('/login/slack/callback', WIKI.auth.passport.authenticate('slack', { failureRedirect: '/login', successRedirect: '/' }))
router.get('/login/azure/callback', WIKI.auth.passport.authenticate('azure_ad_oauth2', { failureRedirect: '/login', successRedirect: '/' }))
/**
* Logout
*/
@@ -112,4 +18,14 @@ router.get('/logout', function (req, res) {
res.redirect('/')
})
/**
* JWT Public Endpoints
*/
router.get('/.well-known/jwk.json', function (req, res, next) {
res.json(WIKI.config.certs.jwk)
})
router.get('/.well-known/jwk.pem', function (req, res, next) {
res.send(WIKI.config.certs.public)
})
module.exports = router

View File

@@ -45,7 +45,7 @@ module.exports = {
// Load JWT
passport.use('jwt', new passportJWT.Strategy({
jwtFromRequest: securityHelper.extractJWT,
secretOrKey: WIKI.config.sessionSecret,
secretOrKey: WIKI.config.certs.public,
audience: 'urn:wiki.js', // TODO: use value from admin
issuer: 'urn:wiki.js'
}, (jwtPayload, cb) => {

View File

@@ -253,7 +253,11 @@ module.exports = class User extends Model {
localeCode: user.localeCode,
defaultEditor: user.defaultEditor,
permissions: ['manage:system']
}, WIKI.config.sessionSecret, {
}, {
key: WIKI.config.certs.private,
passphrase: WIKI.config.sessionSecret
}, {
algorithm: 'RS256',
expiresIn: '30m',
audience: 'urn:wiki.js', // TODO: use value from admin
issuer: 'urn:wiki.js'

View File

@@ -25,6 +25,7 @@ module.exports = () => {
const _ = require('lodash')
const cfgHelper = require('./helpers/config')
const crypto = Promise.promisifyAll(require('crypto'))
const pem2jwk = require('pem-jwk').pem2jwk
// ----------------------------------------
// Define Express App
@@ -90,6 +91,7 @@ module.exports = () => {
}
// Create directory structure
WIKI.logger.info('Creating data directories...')
const dataPath = path.join(process.cwd(), 'data')
await fs.ensureDir(dataPath)
await fs.ensureDir(path.join(dataPath, 'cache'))
@@ -110,6 +112,26 @@ module.exports = () => {
_.set(WIKI.config, 'theming.darkMode', false)
_.set(WIKI.config, 'title', 'Wiki.js')
// Generate certificates
WIKI.logger.info('Generating certificates...')
const certs = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'pkcs1',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs1',
format: 'pem',
cipher: 'aes-256-cbc',
passphrase: WIKI.config.sessionSecret
}
})
_.set(WIKI.config, 'certs.jwk', pem2jwk(certs.publicKey))
_.set(WIKI.config, 'certs.public', certs.publicKey)
_.set(WIKI.config, 'certs.private', certs.privateKey)
// Save config to DB
WIKI.logger.info('Persisting config to DB...')
await WIKI.configSvc.saveToDb([
@@ -120,7 +142,8 @@ module.exports = () => {
'sessionSecret',
'telemetry',
'theming',
'title'
'title',
'certs'
])
// Create default locale