feat: modular auth + queue tasks

This commit is contained in:
NGPixel
2017-07-29 17:33:08 -04:00
parent 9c112ab535
commit f32429325c
25 changed files with 822 additions and 770 deletions

View File

@@ -24,232 +24,51 @@ module.exports = function (passport) {
})
})
// Local Account
if (wiki.config.auth.local && wiki.config.auth.local.enabled) {
const LocalStrategy = require('passport-local').Strategy
passport.use('local',
new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
}, (uEmail, uPassword, done) => {
wiki.db.User.findOne({ email: uEmail, provider: 'local' }).then((user) => {
if (user) {
return user.validatePassword(uPassword).then(() => {
return done(null, user) || true
}).catch((err) => {
return done(err, null)
})
} else {
return done(new Error('INVALID_LOGIN'), null)
}
}).catch((err) => {
done(err, null)
})
}
))
}
// Google ID
if (wiki.config.auth.google && wiki.config.auth.google.enabled) {
const GoogleStrategy = require('passport-google-oauth20').Strategy
passport.use('google',
new GoogleStrategy({
clientID: wiki.config.auth.google.clientId,
clientSecret: wiki.config.auth.google.clientSecret,
callbackURL: wiki.config.host + '/login/google/callback'
}, (accessToken, refreshToken, profile, cb) => {
wiki.db.User.processProfile(profile).then((user) => {
return cb(null, user) || true
}).catch((err) => {
return cb(err, null) || true
})
}
))
}
// Microsoft Accounts
if (wiki.config.auth.microsoft && wiki.config.auth.microsoft.enabled) {
const WindowsLiveStrategy = require('passport-windowslive').Strategy
passport.use('windowslive',
new WindowsLiveStrategy({
clientID: wiki.config.auth.microsoft.clientId,
clientSecret: wiki.config.auth.microsoft.clientSecret,
callbackURL: wiki.config.host + '/login/ms/callback'
}, function (accessToken, refreshToken, profile, cb) {
wiki.db.User.processProfile(profile).then((user) => {
return cb(null, user) || true
}).catch((err) => {
return cb(err, null) || true
})
}
))
}
// Facebook
if (wiki.config.auth.facebook && wiki.config.auth.facebook.enabled) {
const FacebookStrategy = require('passport-facebook').Strategy
passport.use('facebook',
new FacebookStrategy({
clientID: wiki.config.auth.facebook.clientId,
clientSecret: wiki.config.auth.facebook.clientSecret,
callbackURL: wiki.config.host + '/login/facebook/callback',
profileFields: ['id', 'displayName', 'email']
}, function (accessToken, refreshToken, profile, cb) {
wiki.db.User.processProfile(profile).then((user) => {
return cb(null, user) || true
}).catch((err) => {
return cb(err, null) || true
})
}
))
}
// GitHub
if (wiki.config.auth.github && wiki.config.auth.github.enabled) {
const GitHubStrategy = require('passport-github2').Strategy
passport.use('github',
new GitHubStrategy({
clientID: wiki.config.auth.github.clientId,
clientSecret: wiki.config.auth.github.clientSecret,
callbackURL: wiki.config.host + '/login/github/callback',
scope: ['user:email']
}, (accessToken, refreshToken, profile, cb) => {
wiki.db.User.processProfile(profile).then((user) => {
return cb(null, user) || true
}).catch((err) => {
return cb(err, null) || true
})
}
))
}
// Slack
if (wiki.config.auth.slack && wiki.config.auth.slack.enabled) {
const SlackStrategy = require('passport-slack').Strategy
passport.use('slack',
new SlackStrategy({
clientID: wiki.config.auth.slack.clientId,
clientSecret: wiki.config.auth.slack.clientSecret,
callbackURL: wiki.config.host + '/login/slack/callback'
}, (accessToken, refreshToken, profile, cb) => {
wiki.db.User.processProfile(profile).then((user) => {
return cb(null, user) || true
}).catch((err) => {
return cb(err, null) || true
})
}
))
}
// LDAP
if (wiki.config.auth.ldap && wiki.config.auth.ldap.enabled) {
const LdapStrategy = require('passport-ldapauth').Strategy
passport.use('ldapauth',
new LdapStrategy({
server: {
url: wiki.config.auth.ldap.url,
bindDn: wiki.config.auth.ldap.bindDn,
bindCredentials: wiki.config.auth.ldap.bindCredentials,
searchBase: wiki.config.auth.ldap.searchBase,
searchFilter: wiki.config.auth.ldap.searchFilter,
searchAttributes: ['displayName', 'name', 'cn', 'mail'],
tlsOptions: (wiki.config.auth.ldap.tlsEnabled) ? {
ca: [
fs.readFileSync(wiki.config.auth.ldap.tlsCertPath)
]
} : {}
},
usernameField: 'email',
passReqToCallback: false
}, (profile, cb) => {
profile.provider = 'ldap'
profile.id = profile.dn
wiki.db.User.processProfile(profile).then((user) => {
return cb(null, user) || true
}).catch((err) => {
return cb(err, null) || true
})
}
))
}
// AZURE AD
if (wiki.config.auth.azure && wiki.config.auth.azure.enabled) {
const AzureAdOAuth2Strategy = require('passport-azure-ad-oauth2').Strategy
const jwt = require('jsonwebtoken')
passport.use('azure_ad_oauth2',
new AzureAdOAuth2Strategy({
clientID: wiki.config.auth.azure.clientId,
clientSecret: wiki.config.auth.azure.clientSecret,
callbackURL: wiki.config.host + '/login/azure/callback',
resource: wiki.config.auth.azure.resource,
tenant: wiki.config.auth.azure.tenant
}, (accessToken, refreshToken, params, profile, cb) => {
let waadProfile = jwt.decode(params.id_token)
waadProfile.id = waadProfile.oid
waadProfile.provider = 'azure'
wiki.db.User.processProfile(waadProfile).then((user) => {
return cb(null, user) || true
}).catch((err) => {
return cb(err, null) || true
})
}
))
}
// Create users for first-time
wiki.db.onReady.then(() => {
return wiki.db.User.findOne({ provider: 'local', email: 'guest' }).then((c) => {
if (c < 1) {
// Create guest account
return wiki.db.User.findOne({ provider: 'local', email: 'guest@example.com' }).then((c) => {
if (c < 1) {
// Create guest account
return wiki.db.User.create({
provider: 'local',
email: 'guest@example.com',
name: 'Guest',
password: '',
role: 'guest'
}).then(() => {
wiki.logger.info('[AUTH] Guest account created successfully!')
return true
}).catch((err) => {
wiki.logger.error('[AUTH] An error occured while creating guest account:')
wiki.logger.error(err)
return err
})
}
}).then(() => {
if (process.env.WIKI_JS_HEROKU) {
return wiki.db.User.findOne({ provider: 'local', email: process.env.WIKI_ADMIN_EMAIL }).then((c) => {
if (c < 1) {
// Create root admin account (HEROKU ONLY)
return wiki.db.User.create({
provider: 'local',
email: process.env.WIKI_ADMIN_EMAIL,
name: 'Administrator',
password: '$2a$04$MAHRw785Xe/Jd5kcKzr3D.VRZDeomFZu2lius4gGpZZ9cJw7B7Mna', // admin123 (default)
role: 'admin'
}).then(() => {
wiki.logger.info('[AUTH] Root admin account created successfully!')
return true
}).catch((err) => {
wiki.logger.error('[AUTH] An error occured while creating root admin account:')
wiki.logger.error(err)
return err
})
} else { return true }
})
} else { return true }
})
return wiki.db.User.create({
provider: 'local',
email: 'guest@example.com',
name: 'Guest',
password: '',
role: 'guest'
}).then(() => {
wiki.logger.info('[AUTH] Guest account created successfully!')
return true
}).catch((err) => {
wiki.logger.error('[AUTH] An error occured while creating guest account:')
wiki.logger.error(err)
return err
})
}
})
// .then(() => {
// if (process.env.WIKI_JS_HEROKU) {
// return wiki.db.User.findOne({ provider: 'local', email: process.env.WIKI_ADMIN_EMAIL }).then((c) => {
// if (c < 1) {
// // Create root admin account (HEROKU ONLY)
// return wiki.db.User.create({
// provider: 'local',
// email: process.env.WIKI_ADMIN_EMAIL,
// name: 'Administrator',
// password: '$2a$04$MAHRw785Xe/Jd5kcKzr3D.VRZDeomFZu2lius4gGpZZ9cJw7B7Mna', // admin123 (default)
// role: 'admin'
// }).then(() => {
// wiki.logger.info('[AUTH] Root admin account created successfully!')
// return true
// }).catch((err) => {
// wiki.logger.error('[AUTH] An error occured while creating root admin account:')
// wiki.logger.error(err)
// return err
// })
// } else { return true }
// })
// } else { return true }
// })
}

View File

@@ -8,62 +8,95 @@ const _ = require('lodash')
const path = require('path')
const cfgHelper = require('../helpers/config')
/**
* Load Application Configuration
*
* @param {Object} confPaths Path to the configuration files
* @return {Object} Application Configuration
*/
module.exports = (confPaths) => {
confPaths = _.defaults(confPaths, {
config: path.join(wiki.ROOTPATH, 'config.yml'),
data: path.join(wiki.SERVERPATH, 'app/data.yml'),
dataRegex: path.join(wiki.SERVERPATH, 'app/regex.js')
})
module.exports = {
SUBSETS: ['auth', 'features', 'git', 'logging', 'site', 'theme', 'uploads'],
let appconfig = {}
let appdata = {}
/**
* Load root config from disk
*
* @param {any} confPaths
* @returns
*/
init() {
let confPaths = {
config: path.join(wiki.ROOTPATH, 'config.yml'),
data: path.join(wiki.SERVERPATH, 'app/data.yml'),
dataRegex: path.join(wiki.SERVERPATH, 'app/regex.js')
}
try {
appconfig = yaml.safeLoad(
cfgHelper.parseConfigValue(
fs.readFileSync(confPaths.config, 'utf8')
let appconfig = {}
let appdata = {}
try {
appconfig = yaml.safeLoad(
cfgHelper.parseConfigValue(
fs.readFileSync(confPaths.config, 'utf8')
)
)
)
appdata = yaml.safeLoad(fs.readFileSync(confPaths.data, 'utf8'))
appdata.regex = require(confPaths.dataRegex)
} catch (ex) {
console.error(ex)
process.exit(1)
}
appdata = yaml.safeLoad(fs.readFileSync(confPaths.data, 'utf8'))
appdata.regex = require(confPaths.dataRegex)
} catch (ex) {
console.error(ex)
process.exit(1)
}
// Merge with defaults
// Merge with defaults
appconfig = _.defaultsDeep(appconfig, appdata.defaults.config)
appconfig = _.defaultsDeep(appconfig, appdata.defaults.config)
// Check port
// Check port
if (appconfig.port < 1) {
appconfig.port = process.env.PORT || 80
}
if (appconfig.port < 1) {
appconfig.port = process.env.PORT || 80
}
// Convert booleans
appconfig.public = (appconfig.public === true || _.toLower(appconfig.public) === 'true')
// List authentication strategies
wiki.config = appconfig
wiki.data = appdata
appconfig.authStrategies = {
list: _.filter(appconfig.auth, ['enabled', true]),
socialEnabled: (_.chain(appconfig.auth).omit(['local', 'ldap']).filter(['enabled', true]).value().length > 0)
}
if (appconfig.authStrategies.list.length < 1) {
console.error(new Error('You must enable at least 1 authentication strategy!'))
process.exit(1)
}
// List authentication strategies
return {
config: appconfig,
data: appdata
// appconfig.authStrategies = {
// list: _.filter(appconfig.auth, ['enabled', true]),
// socialEnabled: (_.chain(appconfig.auth).omit('local').filter(['enabled', true]).value().length > 0)
// }
// if (appconfig.authStrategies.list.length < 1) {
// console.error(new Error('You must enable at least 1 authentication strategy!'))
// process.exit(1)
// }
},
/**
* Load config from DB
*
* @param {Array} subsets Array of subsets to load
* @returns Promise
*/
loadFromDb(subsets) {
if (!_.isArray(subsets) || subsets.length === 0) {
subsets = this.SUBSETS
}
return wiki.db.Setting.findAll({
attributes: ['key', 'config'],
where: {
key: {
$in: subsets
}
}
}).then(results => {
if (_.isArray(results) && results.length > 0) {
results.forEach(result => {
wiki.config[result.key] = result.config
})
return true
} else {
return Promise.reject(new Error('Invalid DB Configuration result set'))
}
})
}
}

View File

@@ -5,6 +5,7 @@
const fs = require('fs')
const path = require('path')
const _ = require('lodash')
const Promise = require('bluebird')
/**
* PostgreSQL DB module
@@ -33,7 +34,8 @@ module.exports = {
max: 10,
min: 0,
idle: 10000
}
},
logging: false
})
// Attempt to connect and authenticate to DB
@@ -63,10 +65,10 @@ module.exports = {
// Sync DB
self.onReady = self.inst.sync({
self.onReady = (wiki.IS_MASTER) ? self.inst.sync({
force: false,
logging: false
})
}) : Promise.resolve()
return self
}

View File

@@ -27,7 +27,7 @@ module.exports = {
this._uploadsThumbsPath = path.resolve(wiki.ROOTPATH, wiki.config.paths.data, 'thumbs')
this.createBaseDirectories()
this.initMulter()
// this.initMulter()
return this
},
@@ -37,8 +37,10 @@ module.exports = {
*/
initMulter () {
let maxFileSizes = {
img: wiki.config.uploads.maxImageFileSize * 1024 * 1024,
file: wiki.config.uploads.maxOtherFileSize * 1024 * 1024
// img: wiki.config.uploads.maxImageFileSize * 1024 * 1024,
// file: wiki.config.uploads.maxOtherFileSize * 1024 * 1024
img: 3 * 1024 * 1024,
file: 10 * 1024 * 1024
}
// -> IMAGES

View File

@@ -51,7 +51,7 @@ module.exports = {
// -> Initialize repository
self.onReady = self._initRepo()
self.onReady = (wiki.IS_MASTER) ? self._initRepo() : Promise.resolve()
// Define signature

View File

@@ -4,75 +4,77 @@
const cluster = require('cluster')
module.exports = () => {
let winston = require('winston')
module.exports = {
init() {
let winston = require('winston')
// Console
// Console
let logger = new (winston.Logger)({
level: (wiki.IS_DEBUG) ? 'debug' : 'info',
transports: [
new (winston.transports.Console)({
level: (wiki.IS_DEBUG) ? 'debug' : 'info',
prettyPrint: true,
colorize: true,
silent: false,
timestamp: true
})
]
})
logger.filters.push((level, msg) => {
let processName = (cluster.isMaster) ? 'MASTER' : `WORKER-${cluster.worker.id}`
return '[' + processName + '] ' + msg
})
// External services
if (wiki.config.externalLogging.bugsnag) {
const bugsnagTransport = require('./winston-transports/bugsnag')
logger.add(bugsnagTransport, {
level: 'warn',
key: wiki.config.externalLogging.bugsnag
let logger = new (winston.Logger)({
level: (wiki.IS_DEBUG) ? 'debug' : 'info',
transports: [
new (winston.transports.Console)({
level: (wiki.IS_DEBUG) ? 'debug' : 'info',
prettyPrint: true,
colorize: true,
silent: false,
timestamp: true
})
]
})
}
if (wiki.config.externalLogging.loggly) {
require('winston-loggly-bulk')
logger.add(winston.transports.Loggly, {
token: wiki.config.externalLogging.loggly.token,
subdomain: wiki.config.externalLogging.loggly.subdomain,
tags: ['wiki-js'],
level: 'warn',
json: true
logger.filters.push((level, msg) => {
let processName = (cluster.isMaster) ? 'MASTER' : `WORKER-${cluster.worker.id}`
return '[' + processName + '] ' + msg
})
}
if (wiki.config.externalLogging.papertrail) {
require('winston-papertrail').Papertrail // eslint-disable-line no-unused-expressions
logger.add(winston.transports.Papertrail, {
host: wiki.config.externalLogging.papertrail.host,
port: wiki.config.externalLogging.papertrail.port,
level: 'warn',
program: 'wiki.js'
})
}
// External services
if (wiki.config.externalLogging.rollbar) {
const rollbarTransport = require('./winston-transports/rollbar')
logger.add(rollbarTransport, {
level: 'warn',
key: wiki.config.externalLogging.rollbar
})
}
// if (wiki.config.externalLogging.bugsnag) {
// const bugsnagTransport = require('./winston-transports/bugsnag')
// logger.add(bugsnagTransport, {
// level: 'warn',
// key: wiki.config.externalLogging.bugsnag
// })
// }
if (wiki.config.externalLogging.sentry) {
const sentryTransport = require('./winston-transports/sentry')
logger.add(sentryTransport, {
level: 'warn',
key: wiki.config.externalLogging.sentry
})
}
// if (wiki.config.externalLogging.loggly) {
// require('winston-loggly-bulk')
// logger.add(winston.transports.Loggly, {
// token: wiki.config.externalLogging.loggly.token,
// subdomain: wiki.config.externalLogging.loggly.subdomain,
// tags: ['wiki-js'],
// level: 'warn',
// json: true
// })
// }
return logger
// if (wiki.config.externalLogging.papertrail) {
// require('winston-papertrail').Papertrail // eslint-disable-line no-unused-expressions
// logger.add(winston.transports.Papertrail, {
// host: wiki.config.externalLogging.papertrail.host,
// port: wiki.config.externalLogging.papertrail.port,
// level: 'warn',
// program: 'wiki.js'
// })
// }
// if (wiki.config.externalLogging.rollbar) {
// const rollbarTransport = require('./winston-transports/rollbar')
// logger.add(rollbarTransport, {
// level: 'warn',
// key: wiki.config.externalLogging.rollbar
// })
// }
// if (wiki.config.externalLogging.sentry) {
// const sentryTransport = require('./winston-transports/sentry')
// logger.add(sentryTransport, {
// level: 'warn',
// key: wiki.config.externalLogging.sentry
// })
// }
return logger
}
}

View File

@@ -23,7 +23,8 @@ const mdRemove = require('remove-markdown')
var mkdown = md({
html: true,
breaks: wiki.config.features.linebreaks,
// breaks: wiki.config.features.linebreaks,
breaks: true,
linkify: true,
typography: true,
highlight(str, lang) {
@@ -57,7 +58,8 @@ var mkdown = md({
})
.use(mdAttrs)
if (wiki.config.features.mathjax) {
// if (wiki.config.features.mathjax) {
if (true) {
mkdown.use(mdMathjax)
}

View File

@@ -1,6 +1,6 @@
'use strict'
/* global db, git, lang, upl */
/* global wiki */
const path = require('path')
const Promise = require('bluebird')
@@ -32,8 +32,8 @@ module.exports = {
init () {
let self = this
self._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads')
self._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs')
self._uploadsPath = path.resolve(wiki.ROOTPATH, wiki.config.paths.repo, 'uploads')
self._uploadsThumbsPath = path.resolve(wiki.ROOTPATH, wiki.config.paths.data, 'thumbs')
return self
},
@@ -59,16 +59,16 @@ module.exports = {
self._watcher.on('add', (p) => {
let pInfo = self.parseUploadsRelPath(p)
return self.processFile(pInfo.folder, pInfo.filename).then((mData) => {
return db.UplFile.findByIdAndUpdate(mData._id, mData, { upsert: true })
return wiki.db.UplFile.findByIdAndUpdate(mData._id, mData, { upsert: true })
}).then(() => {
return git.commitUploads(lang.t('git:uploaded', { path: p }))
return wiki.git.commitUploads(wiki.lang.t('git:uploaded', { path: p }))
})
})
// -> Remove upload file
self._watcher.on('unlink', (p) => {
return git.commitUploads(lang.t('git:deleted', { path: p }))
return wiki.git.commitUploads(wiki.lang.t('git:deleted', { path: p }))
})
},
@@ -91,8 +91,8 @@ module.exports = {
// Add folders to DB
return db.UplFolder.remove({}).then(() => {
return db.UplFolder.insertMany(_.map(folderNames, (f) => {
return wiki.db.UplFolder.remove({}).then(() => {
return wiki.db.UplFolder.insertMany(_.map(folderNames, (f) => {
return {
_id: 'f:' + f,
name: f
@@ -107,7 +107,7 @@ module.exports = {
let fldPath = path.join(self._uploadsPath, fldName)
return fs.readdirAsync(fldPath).then((fList) => {
return Promise.map(fList, (f) => {
return upl.processFile(fldName, f).then((mData) => {
return wiki.upl.processFile(fldName, f).then((mData) => {
if (mData) {
allFiles.push(mData)
}
@@ -118,9 +118,9 @@ module.exports = {
}, {concurrency: 1}).finally(() => {
// Add files to DB
return db.UplFile.remove({}).then(() => {
return wiki.db.UplFile.remove({}).then(() => {
if (_.isArray(allFiles) && allFiles.length > 0) {
return db.UplFile.insertMany(allFiles)
return wiki.db.UplFile.insertMany(allFiles)
} else {
return true
}
@@ -131,7 +131,7 @@ module.exports = {
}).then(() => {
// Watch for new changes
return upl.watch()
return wiki.upl.watch()
})
},