diff --git a/client/components/setup.vue b/client/components/setup.vue index 1833055c..42ba0f91 100644 --- a/client/components/setup.vue +++ b/client/components/setup.vue @@ -220,10 +220,11 @@ ) v-flex(xs6) v-text-field( + ref='adminPasswordConfirm', v-model='conf.adminPasswordConfirm', label='Confirm Password', hint='Verify your password again.', - v-validate='{ required: true, confirmed: `$adminPassword` }', + v-validate='{ required: true, min: 8 }', data-vv-name='adminPasswordConfirm', data-vv-as='Confirm Password', data-vv-scope='admin', @@ -308,10 +309,6 @@ export default { wikiVersion: { type: String, required: true - }, - langs: { - type: Array, - required: true } }, data() { @@ -394,7 +391,7 @@ export default { async proceedToUpgrade () { if (this.state < 5) { const validationSuccess = await this.$validator.validateAll('admin') - if (!validationSuccess) { + if (!validationSuccess || this.conf.adminPassword !== this.conf.adminPasswordConfirm) { this.state = 4 return } diff --git a/config.sample.yml b/config.sample.yml index 30a7b4fc..3c3ef217 100644 --- a/config.sample.yml +++ b/config.sample.yml @@ -23,12 +23,13 @@ paths: # --------------------------------------------------------------------- # Supported Database Engines: # - postgres = PostgreSQL 9.5 or later -# - mysql = MySQL 5.7.8 or later +# - mysql = MySQL 8.0 / MariaDB 10.2.7 or later +# - mssql = MS SQL Server 2012 or later # - sqlite = SQLite 3.9 or later db: type: postgres - # PostgreSQL and MySQL only: + # PostgreSQL / MySQL / MariaDB / MS SQL Server only: host: localhost port: 5432 user: wikijs diff --git a/package.json b/package.json index 772c9cc6..63be3669 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "restart": "node wiki restart", "dev": "node wiki dev", "build": "webpack --profile --config dev/webpack/webpack.prod.js", - "build:locales": "node dev/tasks/localization", "watch": "webpack --config dev/webpack/webpack.dev.js", "test": "eslint --format codeframe --ext .js,.vue . && pug-lint server/views && jest" }, @@ -43,9 +42,9 @@ "axios": "0.18.0", "bcryptjs-then": "1.0.1", "bluebird": "3.5.1", - "body-parser": "1.18.2", + "body-parser": "1.18.3", "bugsnag": "2.3.1", - "bull": "3.4.1", + "bull": "3.4.2", "cheerio": "1.0.0-rc.2", "child-process-promise": "2.2.1", "chokidar": "2.0.3", @@ -61,15 +60,15 @@ "express-brute": "1.0.1", "express-brute-redis": "0.0.1", "express-session": "1.15.6", - "file-type": "7.7.1", + "file-type": "8.0.0", "filesize.js": "1.0.2", - "follow-redirects": "1.4.1", + "follow-redirects": "1.5.0", "fs-extra": "6.0.1", "getos": "3.1.0", "graphql": "0.13.2", "graphql-list-fields": "2.0.2", "graphql-tools": "3.0.1", - "i18next": "11.3.1", + "i18next": "11.3.2", "i18next-express-middleware": "1.1.1", "i18next-localstorage-cache": "1.1.1", "i18next-node-fs-backend": "1.0.0", @@ -78,6 +77,7 @@ "js-yaml": "3.11.0", "jsonwebtoken": "8.2.1", "klaw": "2.1.1", + "knex": "0.14.6", "lodash": "4.17.10", "markdown-it": "8.4.1", "markdown-it-abbr": "1.0.4", @@ -96,12 +96,14 @@ "mathjax-node": "2.1.0", "mime-types": "2.1.18", "moment": "2.22.1", - "moment-timezone": "0.5.16", - "mongodb": "3.0.7", + "moment-timezone": "0.5.17", + "mongodb": "3.1.0-beta4", + "mssql": "4.1.0", "multer": "1.3.0", "mysql2": "1.5.3", "node-2fa": "1.1.2", "oauth2orize": "1.11.0", + "objection": "1.1.8", "ora": "2.1.0", "passport": "0.4.0", "passport-auth0": "0.6.1", @@ -117,20 +119,18 @@ "passport-slack": "0.0.7", "passport-twitch": "1.0.3", "passport-windowslive": "1.0.2", - "pg": "6.4.2", + "pg": "7.4.3", "pg-hstore": "2.3.2", - "pg-promise": "7.5.3", - "pm2": "2.10.3", + "pm2": "2.10.4", "pug": "2.0.3", "qr-image": "3.2.0", - "raven": "2.6.1", + "raven": "2.6.2", "read-chunk": "2.1.0", "remove-markdown": "0.2.2", - "request": "2.85.0", + "request": "2.86.0", "request-promise": "4.2.2", "scim-query-filter-parser": "1.1.0", "semver": "5.5.0", - "sequelize": "4.37.7", "serve-favicon": "2.5.0", "sqlite3": "4.0.0", "uuid": "3.2.1", @@ -145,7 +145,7 @@ "apollo-client-preset": "1.0.8", "apollo-fetch": "0.7.0", "apollo-link-batch-http": "1.2.2", - "autoprefixer": "8.4.1", + "autoprefixer": "8.5.0", "babel-cli": "6.26.0", "babel-core": "6.26.3", "babel-eslint": "8.2.3", @@ -169,7 +169,7 @@ "eslint": "4.19.1", "eslint-config-requarks": "1.0.7", "eslint-config-standard": "11.0.0", - "eslint-plugin-import": "2.11.0", + "eslint-plugin-import": "2.12.0", "eslint-plugin-node": "6.0.1", "eslint-plugin-promise": "3.7.0", "eslint-plugin-standard": "3.1.0", @@ -183,8 +183,8 @@ "html-webpack-pug-plugin": "0.3.0", "i18next-xhr-backend": "1.5.1", "ignore-loader": "0.1.2", - "jest": "22.4.3", - "jest-junit": "3.7.0", + "jest": "22.4.4", + "jest-junit": "4.0.0", "js-cookie": "2.2.0", "lodash-webpack-plugin": "0.11.5", "mini-css-extract-plugin": "0.4.0", @@ -196,7 +196,7 @@ "postcss-flexibility": "2.0.0", "postcss-import": "11.1.0", "postcss-loader": "2.1.5", - "postcss-selector-parser": "4.0.0", + "postcss-selector-parser": "5.0.0-rc.3", "pug-lint": "2.5.0", "pug-loader": "2.4.0", "pug-plain-loader": "1.0.0", @@ -218,23 +218,23 @@ "vue-clipboards": "1.2.4", "vue-codemirror": "4.0.5", "vue-hot-reload-api": "2.3.0", - "vue-loader": "15.0.10", + "vue-loader": "15.1.0", "vue-material-design-icons": "1.4.0", - "vue-moment": "3.2.0", + "vue-moment": "4.0.0-0", "vue-router": "3.0.1", "vue-simple-breakpoints": "1.0.3", "vue-template-compiler": "2.5.16", - "vuetify": "1.0.17", + "vuetify": "1.0.18", "vuex": "3.0.1", - "vuex-persistedstate": "2.5.2", - "webpack": "4.8.2", - "webpack-bundle-analyzer": "2.11.2", + "vuex-persistedstate": "2.5.4", + "webpack": "4.8.3", + "webpack-bundle-analyzer": "2.12.0", "webpack-cli": "2.1.3", "webpack-dev-middleware": "3.1.3", - "webpack-hot-middleware": "2.22.1", + "webpack-hot-middleware": "2.22.2", "webpack-merge": "4.1.2", "whatwg-fetch": "2.0.4", - "write-file-webpack-plugin": "4.2.0" + "write-file-webpack-plugin": "4.3.2" }, "browserslist": [ "> 1%", diff --git a/server/core/auth.js b/server/core/auth.js index 73d065ed..deb1f5c7 100644 --- a/server/core/auth.js +++ b/server/core/auth.js @@ -18,7 +18,7 @@ module.exports = { }) passport.deserializeUser(function (id, done) { - WIKI.db.User.findById(id).then((user) => { + WIKI.db.users.query().findById(id).then((user) => { if (user) { done(null, user) } else { @@ -58,57 +58,6 @@ module.exports = { WIKI.logger.info(`Authentication Provider ${strategy.title}: [ OK ]`) }) - // Create Guest account for first-time - - WIKI.db.User.findOne({ - where: { - provider: 'local', - email: 'guest@example.com' - } - }).then((c) => { - if (c < 1) { - 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 this } } diff --git a/server/core/config.js b/server/core/config.js index 377dd3b1..52d9586e 100644 --- a/server/core/config.js +++ b/server/core/config.js @@ -59,17 +59,10 @@ module.exports = { subsets = WIKI.data.configNamespaces } - let results = await WIKI.db.Setting.findAll({ - attributes: ['key', 'config'], - where: { - key: { - $in: subsets - } - } - }) + let results = await WIKI.db.settings.query().select(['key', 'value']).whereIn('key', subsets) if (_.isArray(results) && results.length === subsets.length) { results.forEach(result => { - WIKI.config[result.key] = result.config + WIKI.config[result.key] = result.value }) return true } else { @@ -88,14 +81,18 @@ module.exports = { subsets = WIKI.data.configNamespaces } + let trx = await WIKI.db.Objection.transaction.start(WIKI.db.knex) + try { for (let set of subsets) { - await WIKI.db.Setting.upsert({ - key: set, - config: _.get(WIKI.config, set, {}) - }) + console.info(set) + await WIKI.db.settings.query(trx).patch({ + value: _.get(WIKI.config, set, {}) + }).where('key', set) } + await trx.commit() } catch (err) { + await trx.rollback(err) WIKI.logger.error(`Failed to save configuration to DB: ${err.message}`) return false } diff --git a/server/core/db.js b/server/core/db.js index 9200285b..05657a75 100644 --- a/server/core/db.js +++ b/server/core/db.js @@ -1,55 +1,18 @@ const _ = require('lodash') -const fs = require('fs') +const autoload = require('auto-load') const path = require('path') const Promise = require('bluebird') -const Sequelize = require('sequelize') +const Knex = require('knex') +const Objection = require('objection') /* global WIKI */ -const operatorsAliases = { - $eq: Sequelize.Op.eq, - $ne: Sequelize.Op.ne, - $gte: Sequelize.Op.gte, - $gt: Sequelize.Op.gt, - $lte: Sequelize.Op.lte, - $lt: Sequelize.Op.lt, - $not: Sequelize.Op.not, - $in: Sequelize.Op.in, - $notIn: Sequelize.Op.notIn, - $is: Sequelize.Op.is, - $like: Sequelize.Op.like, - $notLike: Sequelize.Op.notLike, - $iLike: Sequelize.Op.iLike, - $notILike: Sequelize.Op.notILike, - $regexp: Sequelize.Op.regexp, - $notRegexp: Sequelize.Op.notRegexp, - $iRegexp: Sequelize.Op.iRegexp, - $notIRegexp: Sequelize.Op.notIRegexp, - $between: Sequelize.Op.between, - $notBetween: Sequelize.Op.notBetween, - $overlap: Sequelize.Op.overlap, - $contains: Sequelize.Op.contains, - $contained: Sequelize.Op.contained, - $adjacent: Sequelize.Op.adjacent, - $strictLeft: Sequelize.Op.strictLeft, - $strictRight: Sequelize.Op.strictRight, - $noExtendRight: Sequelize.Op.noExtendRight, - $noExtendLeft: Sequelize.Op.noExtendLeft, - $and: Sequelize.Op.and, - $or: Sequelize.Op.or, - $any: Sequelize.Op.any, - $all: Sequelize.Op.all, - $values: Sequelize.Op.values, - $col: Sequelize.Op.col -} - /** * PostgreSQL DB module */ module.exports = { - Sequelize, - Op: Sequelize.Op, - + Objection, + knex: null, /** * Initialize DB * @@ -57,65 +20,63 @@ module.exports = { */ init() { let self = this - let dbModelsPath = path.join(WIKI.SERVERPATH, 'models') - // Define Sequelize instance - - this.inst = new this.Sequelize(WIKI.config.db.db, WIKI.config.db.user, WIKI.config.db.pass, { + let dbClient = null + const dbConfig = (!_.isEmpty(process.env.WIKI_DB_CONNSTR)) ? process.env.WIKI_DB_CONNSTR : { host: WIKI.config.db.host, + user: WIKI.config.db.user, + password: WIKI.config.db.pass, + database: WIKI.config.db.db, port: WIKI.config.db.port, - dialect: WIKI.config.db.type, - storage: WIKI.config.db.storage, - pool: { - max: 10, - min: 0, - idle: 10000 - }, - logging: log => { WIKI.logger.log('debug', log) }, - operatorsAliases + filename: WIKI.config.db.storage + } + + switch (WIKI.config.db.type) { + case 'postgres': + dbClient = 'pg' + break + case 'mysql': + dbClient = 'mysql2' + break + case 'mssql': + dbClient = 'mssql' + break + case 'sqlite': + dbClient = 'sqlite3' + break + default: + WIKI.logger.error('Invalid DB Type') + process.exit(1) + } + + this.knex = Knex({ + client: dbClient, + useNullAsDefault: true, + connection: dbConfig, + debug: WIKI.IS_DEBUG }) - // Attempt to connect and authenticate to DB - - this.inst.authenticate().then(() => { - WIKI.logger.info(`Database (${WIKI.config.db.type}) connection: [ OK ]`) - }).catch(err => { - WIKI.logger.error(`Failed to connect to ${WIKI.config.db.type} instance.`) - WIKI.logger.error(err) - process.exit(1) - }) + Objection.Model.knex(this.knex) // Load DB Models - fs - .readdirSync(dbModelsPath) - .filter(file => { - return (file.indexOf('.') !== 0 && file.indexOf('_') !== 0) - }) - .forEach(file => { - let modelName = _.upperFirst(_.camelCase(_.split(file, '.')[0])) - self[modelName] = self.inst.import(path.join(dbModelsPath, file)) - }) - - // Associate DB Models - - require(path.join(dbModelsPath, '_relations.js'))(self) + const models = autoload(path.join(WIKI.SERVERPATH, 'db/models')) // Set init tasks let initTasks = { - // -> Sync DB Schemas - syncSchemas() { - return self.inst.sync({ - force: false, - logging: log => { WIKI.logger.log('debug', log) } + // -> Migrate DB Schemas + async syncSchemas() { + return self.knex.migrate.latest({ + directory: path.join(WIKI.SERVERPATH, 'db/migrations'), + tableName: 'migrations' }) }, // -> Set Connection App Name - setAppName() { + async setAppName() { switch (WIKI.config.db.type) { case 'postgres': - return self.inst.query(`set application_name = 'WIKI.js'`, { raw: true }) + return self.knex.raw(`set application_name = 'Wiki.js'`) } } } @@ -131,6 +92,9 @@ module.exports = { this.onReady = Promise.each(initTasksQueue, t => t()).return(true) - return this + return { + ...this, + ...models + } } } diff --git a/server/core/localization.js b/server/core/localization.js index dd735b68..8d2310b6 100644 --- a/server/core/localization.js +++ b/server/core/localization.js @@ -52,11 +52,7 @@ module.exports = { } }, async loadLocale(locale, opts = { silent: false }) { - const res = await WIKI.db.Locale.findOne({ - where: { - code: locale - } - }) + const res = await WIKI.db.locales.query().findOne('code', locale) if (res) { if (_.isPlainObject(res.strings)) { _.forOwn(res.strings, (data, ns) => { diff --git a/server/db/migrations/2.0.0.js b/server/db/migrations/2.0.0.js new file mode 100644 index 00000000..bebb6ede --- /dev/null +++ b/server/db/migrations/2.0.0.js @@ -0,0 +1,75 @@ +exports.up = knex => { + return knex.schema + // ------------------------------------- + // GROUPS + // ------------------------------------- + .createTable('groups', table => { + table.increments('id').primary() + + table.string('name').notNullable() + table.string('createdAt').notNullable() + table.string('updatedAt').notNullable() + }) + // ------------------------------------- + // LOCALES + // ------------------------------------- + .createTable('locales', table => { + table.increments('id').primary() + + table.string('code', 2).notNullable().unique() + table.json('strings') + table.boolean('isRTL').notNullable().defaultTo(false) + table.string('name').notNullable() + table.string('nativeName').notNullable() + table.string('createdAt').notNullable() + table.string('updatedAt').notNullable() + }) + // ------------------------------------- + // SETTINGS + // ------------------------------------- + .createTable('settings', table => { + table.increments('id').primary() + + table.string('key').notNullable().unique() + table.json('value') + table.string('createdAt').notNullable() + table.string('updatedAt').notNullable() + }) + // ------------------------------------- + // USERS + // ------------------------------------- + .createTable('users', table => { + table.increments('id').primary() + + table.string('email').notNullable() + table.string('name').notNullable() + table.string('provider').notNullable().defaultTo('local') + table.string('providerId') + table.string('password') + table.boolean('tfaIsActive').notNullable().defaultTo(false) + table.string('tfaSecret') + table.enum('role', ['admin', 'guest', 'user']).notNullable().defaultTo('guest') + table.string('createdAt').notNullable() + table.string('updatedAt').notNullable() + + table.unique(['provider', 'email']) + }) + // ------------------------------------- + // USER GROUPS + // ------------------------------------- + .createTable('userGroups', table => { + table.increments('id').primary() + + table.integer('userId').unsigned().references('id').inTable('users') + table.integer('groupId').unsigned().references('id').inTable('groups') + }) +} + +exports.down = knex => { + return knex.schema + .dropTableIfExists('userGroups') + .dropTableIfExists('groups') + .dropTableIfExists('locales') + .dropTableIfExists('settings') + .dropTableIfExists('users') +} diff --git a/server/db/models/groups.js b/server/db/models/groups.js new file mode 100644 index 00000000..f3d50018 --- /dev/null +++ b/server/db/models/groups.js @@ -0,0 +1,48 @@ +const Model = require('objection').Model + +/** + * Settings model + */ +module.exports = class Group extends Model { + static get tableName() { return 'groups' } + + static get jsonSchema () { + return { + type: 'object', + required: ['name'], + + properties: { + id: {type: 'integer'}, + name: {type: 'string'}, + createdAt: {type: 'string'}, + updatedAt: {type: 'string'} + } + } + } + + static get relationMappings() { + const User = require('./users') + return { + users: { + relation: Model.ManyToManyRelation, + modelClass: User, + join: { + from: 'groups.id', + through: { + from: 'userGroups.groupId', + to: 'userGroups.userId' + }, + to: 'users.id' + } + } + } + } + + $beforeUpdate() { + this.updatedAt = new Date().toISOString() + } + $beforeInsert() { + this.createdAt = new Date().toISOString() + this.updatedAt = new Date().toISOString() + } +} diff --git a/server/db/models/locales.js b/server/db/models/locales.js new file mode 100644 index 00000000..607dd098 --- /dev/null +++ b/server/db/models/locales.js @@ -0,0 +1,34 @@ +const Model = require('objection').Model + +/** + * Locales model + */ +module.exports = class User extends Model { + static get tableName() { return 'locales' } + + static get jsonSchema () { + return { + type: 'object', + required: ['code', 'name'], + + properties: { + id: {type: 'integer'}, + code: {type: 'string'}, + strings: {type: 'object'}, + isRTL: {type: 'boolean', default: false}, + name: {type: 'string'}, + nativeName: {type: 'string'}, + createdAt: {type: 'string'}, + updatedAt: {type: 'string'} + } + } + } + + $beforeUpdate() { + this.updatedAt = new Date().toISOString() + } + $beforeInsert() { + this.createdAt = new Date().toISOString() + this.updatedAt = new Date().toISOString() + } +} diff --git a/server/db/models/settings.js b/server/db/models/settings.js new file mode 100644 index 00000000..bedfe807 --- /dev/null +++ b/server/db/models/settings.js @@ -0,0 +1,31 @@ +const Model = require('objection').Model + +/** + * Settings model + */ +module.exports = class User extends Model { + static get tableName() { return 'settings' } + + static get jsonSchema () { + return { + type: 'object', + required: ['key', 'value'], + + properties: { + id: {type: 'integer'}, + key: {type: 'string'}, + value: {type: 'object'}, + createdAt: {type: 'string'}, + updatedAt: {type: 'string'} + } + } + } + + $beforeUpdate() { + this.updatedAt = new Date().toISOString() + } + $beforeInsert() { + this.createdAt = new Date().toISOString() + this.updatedAt = new Date().toISOString() + } +} diff --git a/server/db/models/users.js b/server/db/models/users.js new file mode 100644 index 00000000..863086e8 --- /dev/null +++ b/server/db/models/users.js @@ -0,0 +1,235 @@ +/* global WIKI */ + +const bcrypt = require('bcryptjs-then') +const _ = require('lodash') +const tfa = require('node-2fa') +const securityHelper = require('../../helpers/security') +const Model = require('objection').Model + +const bcryptRegexp = /^\$2[ayb]\$[0-9]{2}\$[A-Za-z0-9./]{53}$/ + +/** + * Users model + */ +module.exports = class User extends Model { + static get tableName() { return 'users' } + + static get jsonSchema () { + return { + type: 'object', + required: ['email', 'name', 'provider'], + + properties: { + id: {type: 'integer'}, + email: {type: 'string', format: 'email'}, + name: {type: 'string', minLength: 1, maxLength: 255}, + provider: {type: 'string', minLength: 1, maxLength: 255}, + providerId: {type: 'number'}, + password: {type: 'string'}, + role: {type: 'string', enum: ['admin', 'guest', 'user']}, + tfaIsActive: {type: 'boolean', default: false}, + tfaSecret: {type: 'string'}, + createdAt: {type: 'string'}, + updatedAt: {type: 'string'} + } + } + } + + static get relationMappings() { + const Group = require('./groups') + return { + groups: { + relation: Model.ManyToManyRelation, + modelClass: Group, + join: { + from: 'users.id', + through: { + from: 'userGroups.userId', + to: 'userGroups.groupId' + }, + to: 'groups.id' + } + } + } + } + + async $beforeUpdate(opt, context) { + await super.$beforeUpdate(opt, context) + + this.updatedAt = new Date().toISOString() + + if (!(opt.patch && this.password === undefined)) { + await this.generateHash() + } + } + async $beforeInsert(context) { + await super.$beforeInsert(context) + + this.createdAt = new Date().toISOString() + this.updatedAt = new Date().toISOString() + + await this.generateHash() + } + + async generateHash() { + if (this.password) { + if (bcryptRegexp.test(this.password)) { return } + this.password = await bcrypt.hash(this.password, 12) + } + } + + async verifyPassword(pwd) { + if (await bcrypt.compare(this.password, pwd) === true) { + return true + } else { + throw new WIKI.Error.AuthLoginFailed() + } + } + + async enableTFA() { + let tfaInfo = tfa.generateSecret({ + name: WIKI.config.site.title + }) + return this.$query.patch({ + tfaIsActive: true, + tfaSecret: tfaInfo.secret + }) + } + + async disableTFA() { + return this.$query.patch({ + tfaIsActive: false, + tfaSecret: '' + }) + } + + async verifyTFA(code) { + let result = tfa.verifyToken(this.tfaSecret, code) + return (result && _.has(result, 'delta') && result.delta === 0) + } + + static async processProfile(profile) { + let primaryEmail = '' + if (_.isArray(profile.emails)) { + let e = _.find(profile.emails, ['primary', true]) + primaryEmail = (e) ? e.value : _.first(profile.emails).value + } else if (_.isString(profile.email) && profile.email.length > 5) { + primaryEmail = profile.email + } else if (_.isString(profile.mail) && profile.mail.length > 5) { + primaryEmail = profile.mail + } else if (profile.user && profile.user.email && profile.user.email.length > 5) { + primaryEmail = profile.user.email + } else { + return Promise.reject(new Error(WIKI.lang.t('auth:errors.invaliduseremail'))) + } + + profile.provider = _.lowerCase(profile.provider) + primaryEmail = _.toLower(primaryEmail) + + let user = await WIKI.db.users.query().findOne({ + email: primaryEmail, + provider: profile.provider + }) + if (user) { + user.$query().patchAdnFetch({ + email: primaryEmail, + provider: profile.provider, + providerId: profile.id, + name: profile.displayName || _.split(primaryEmail, '@')[0] + }) + } else { + user = await WIKI.db.users.query().insertAndFetch({ + email: primaryEmail, + provider: profile.provider, + providerId: profile.id, + name: profile.displayName || _.split(primaryEmail, '@')[0] + }) + } + + // Handle unregistered accounts + // if (!user && profile.provider !== 'local' && (WIKI.config.auth.defaultReadAccess || profile.provider === 'ldap' || profile.provider === 'azure')) { + // let nUsr = { + // email: primaryEmail, + // provider: profile.provider, + // providerId: profile.id, + // password: '', + // name: profile.displayName || profile.name || profile.cn, + // rights: [{ + // role: 'read', + // path: '/', + // exact: false, + // deny: false + // }] + // } + // return WIKI.db.users.query().insert(nUsr) + // } + + return user + } + + static async login (opts, context) { + if (_.has(WIKI.config.auth.strategies, opts.provider)) { + _.set(context.req, 'body.email', opts.username) + _.set(context.req, 'body.password', opts.password) + + // Authenticate + return new Promise((resolve, reject) => { + WIKI.auth.passport.authenticate(opts.provider, async (err, user, info) => { + if (err) { return reject(err) } + if (!user) { return reject(new WIKI.Error.AuthLoginFailed()) } + + // Is 2FA required? + if (user.tfaIsActive) { + try { + let loginToken = await securityHelper.generateToken(32) + await WIKI.redis.set(`tfa:${loginToken}`, user.id, 'EX', 600) + return resolve({ + tfaRequired: true, + tfaLoginToken: loginToken + }) + } catch (err) { + WIKI.logger.warn(err) + return reject(new WIKI.Error.AuthGenericError()) + } + } else { + // No 2FA, log in user + return context.req.logIn(user, err => { + if (err) { return reject(err) } + resolve({ + tfaRequired: false + }) + }) + } + })(context.req, context.res, () => {}) + }) + } else { + throw new WIKI.Error.AuthProviderInvalid() + } + } + + static async loginTFA(opts, context) { + if (opts.securityCode.length === 6 && opts.loginToken.length === 64) { + let result = await WIKI.redis.get(`tfa:${opts.loginToken}`) + if (result) { + let userId = _.toSafeInteger(result) + if (userId && userId > 0) { + let user = await WIKI.db.users.query().findById(userId) + if (user && user.verifyTFA(opts.securityCode)) { + return Promise.fromCallback(clb => { + context.req.logIn(user, clb) + }).return({ + succeeded: true, + message: 'Login Successful' + }).catch(err => { + WIKI.logger.warn(err) + throw new WIKI.Error.AuthGenericError() + }) + } else { + throw new WIKI.Error.AuthTFAFailed() + } + } + } + } + throw new WIKI.Error.AuthTFAInvalid() + } +} diff --git a/server/db/seeds/settings.js b/server/db/seeds/settings.js new file mode 100644 index 00000000..d8c30727 --- /dev/null +++ b/server/db/seeds/settings.js @@ -0,0 +1,11 @@ +exports.seed = (knex, Promise) => { + return knex('settings') + .insert([ + { key: 'auth', value: {} }, + { key: 'features', value: {} }, + { key: 'logging', value: {} }, + { key: 'site', value: {} }, + { key: 'theme', value: {} }, + { key: 'uploads', value: {} } + ]) +} diff --git a/server/graph/resolvers/authentication.js b/server/graph/resolvers/authentication.js index 018f8e0d..f639e281 100644 --- a/server/graph/resolvers/authentication.js +++ b/server/graph/resolvers/authentication.js @@ -32,7 +32,7 @@ module.exports = { AuthenticationMutation: { async login(obj, args, context) { try { - let authResult = await WIKI.db.User.login(args, context) + let authResult = await WIKI.db.users.login(args, context) return { ...authResult, responseResult: graphHelper.generateSuccess('Login success') @@ -43,7 +43,7 @@ module.exports = { }, async loginTFA(obj, args, context) { try { - let authResult = await WIKI.db.User.loginTFA(args, context) + let authResult = await WIKI.db.users.loginTFA(args, context) return { ...authResult, responseResult: graphHelper.generateSuccess('TFA success') diff --git a/server/graph/resolvers/group.js b/server/graph/resolvers/group.js index 027693c0..17e05ec9 100644 --- a/server/graph/resolvers/group.js +++ b/server/graph/resolvers/group.js @@ -13,43 +13,32 @@ module.exports = { }, GroupQuery: { async list(obj, args, context, info) { - return WIKI.db.Group.findAll({ - attributes: { - include: [[WIKI.db.inst.fn('COUNT', WIKI.db.inst.col('users.id')), 'userCount']] - }, - include: [{ - model: WIKI.db.User, - attributes: [], - through: { - attributes: [] - } - }], - raw: true, - // TODO: Figure out how to exclude these extra fields... - group: ['group.id', 'users->userGroups.createdAt', 'users->userGroups.updatedAt', 'users->userGroups.version', 'users->userGroups.userId', 'users->userGroups.groupId'] - }) + return WIKI.db.groups.query().select( + 'groups.*', + WIKI.db.groups.relatedQuery('users').count().as('userCount') + ) }, async single(obj, args, context, info) { - return WIKI.db.Group.findById(args.id) + return WIKI.db.groups.query().findById(args.id) } }, GroupMutation: { async assignUser(obj, args) { - const grp = await WIKI.db.Group.findById(args.groupId) + const grp = await WIKI.db.groups.query().findById(args.groupId) if (!grp) { throw new gql.GraphQLError('Invalid Group ID') } - const usr = await WIKI.db.User.findById(args.userId) + const usr = await WIKI.db.users.query().findById(args.userId) if (!usr) { throw new gql.GraphQLError('Invalid User ID') } - await grp.addUser(usr) + await grp.$relatedQuery('users').relate(usr.id) return { responseResult: graphHelper.generateSuccess('User has been assigned to group.') } }, async create(obj, args) { - const group = await WIKI.db.Group.create({ + const group = await WIKI.db.groups.query().insertAndFetch({ name: args.name }) return { @@ -58,36 +47,27 @@ module.exports = { } }, async delete(obj, args) { - await WIKI.db.Group.destroy({ - where: { - id: args.id - }, - limit: 1 - }) + await WIKI.db.groups.query().deleteById(args.id) return { responseResult: graphHelper.generateSuccess('Group has been deleted.') } }, async unassignUser(obj, args) { - const grp = await WIKI.db.Group.findById(args.groupId) + const grp = await WIKI.db.groups.query().findById(args.groupId) if (!grp) { throw new gql.GraphQLError('Invalid Group ID') } - const usr = await WIKI.db.User.findById(args.userId) + const usr = await WIKI.db.users.query().findById(args.userId) if (!usr) { throw new gql.GraphQLError('Invalid User ID') } - await grp.removeUser(usr) + await grp.$relatedQuery('users').unrelate().where('userId', usr.id) return { responseResult: graphHelper.generateSuccess('User has been unassigned from group.') } }, async update(obj, args) { - await WIKI.db.Group.update({ - name: args.name - }, { - where: { id: args.id } - }) + await WIKI.db.groups.query().patch({ name: args.name }).where('id', args.id) return { responseResult: graphHelper.generateSuccess('Group has been updated.') } @@ -95,7 +75,7 @@ module.exports = { }, Group: { users(grp) { - return grp.getUsers() + return grp.$relatedQuery('users') } } } diff --git a/server/graph/resolvers/localization.js b/server/graph/resolvers/localization.js index 2204bedf..d0ed3310 100644 --- a/server/graph/resolvers/localization.js +++ b/server/graph/resolvers/localization.js @@ -13,12 +13,7 @@ module.exports = { LocalizationQuery: { async locales(obj, args, context, info) { let remoteLocales = await WIKI.redis.get('locales') - let localLocales = await WIKI.db.Locale.findAll({ - attributes: { - exclude: ['strings'] - }, - raw: true - }) + let localLocales = await WIKI.db.locales.query().select('id', 'code', 'isRTL', 'name', 'nativeName', 'createdAt', 'updatedAt') remoteLocales = (remoteLocales) ? JSON.parse(remoteLocales) : localLocales return _.map(remoteLocales, rl => { let isInstalled = _.some(localLocales, ['code', rl.code]) diff --git a/server/graph/resolvers/system.js b/server/graph/resolvers/system.js index 90c385bf..5f6f46d4 100644 --- a/server/graph/resolvers/system.js +++ b/server/graph/resolvers/system.js @@ -10,7 +10,8 @@ const path = require('path') const dbTypes = { mysql: 'MySQL / MariaDB', postgres: 'PostgreSQL', - sqlite: 'SQLite' + sqlite: 'SQLite', + mssql: 'MS SQL Server' } module.exports = { @@ -28,12 +29,14 @@ module.exports = { osLabel = `${os.type()} - ${osInfo.dist} (${osInfo.codename || os.platform()}) ${osInfo.release || os.release()} ${os.arch()}` } + console.info(WIKI.db.knex.client) + return { configFile: path.join(process.cwd(), 'config.yml'), currentVersion: WIKI.version, dbType: _.get(dbTypes, WIKI.config.db.type, 'Unknown DB'), - dbVersion: WIKI.db.inst.options.databaseVersion, - dbHost: WIKI.db.inst.options.host, + dbVersion: _.get(WIKI.db, 'knex.client.version', 'Unknown version'), + dbHost: WIKI.config.db.host, latestVersion: WIKI.version, // TODO latestVersionReleaseDate: new Date(), // TODO operatingSystem: osLabel, diff --git a/server/graph/resolvers/user.js b/server/graph/resolvers/user.js index b7cdbd32..9765a4c3 100644 --- a/server/graph/resolvers/user.js +++ b/server/graph/resolvers/user.js @@ -10,52 +10,35 @@ module.exports = { }, UserQuery: { async list(obj, args, context, info) { - return WIKI.db.User.findAll({ - attributes: { - exclude: ['password', 'tfaSecret'] - }, - raw: true - }) + return WIKI.db.users.query() + .select('id', 'email', 'name', 'provider', 'role', 'createdAt', 'updatedAt') }, async search(obj, args, context, info) { - return WIKI.db.User.findAll({ - where: { - $or: [ - { email: { $like: `%${args.query}%` } }, - { name: { $like: `%${args.query}%` } } - ] - }, - limit: 10, - attributes: ['id', 'email', 'name', 'provider', 'role', 'createdAt', 'updatedAt'], - raw: true - }) + return WIKI.db.users.query() + .where('email', 'like', `%${args.query}%`) + .orWhere('name', 'like', `%${args.query}%`) + .limit(10) + .select('id', 'email', 'name', 'provider', 'role', 'createdAt', 'updatedAt') }, async single(obj, args, context, info) { - return WIKI.db.User.findById(args.id) + return WIKI.db.users.query().findById(args.id) } }, UserMutation: { create(obj, args) { - return WIKI.db.User.create(args) + return WIKI.db.users.query().insertAndFetch(args) }, delete(obj, args) { - return WIKI.db.User.destroy({ - where: { - id: args.id - }, - limit: 1 - }) + return WIKI.db.users.query().deleteById(args.id) }, update(obj, args) { - return WIKI.db.User.update({ + return WIKI.db.users.query().patch({ email: args.email, name: args.name, provider: args.provider, providerId: args.providerId, role: args.role - }, { - where: { id: args.id } - }) + }).where('id', args.id) }, resetPassword(obj, args) { return false diff --git a/server/jobs/fetch-graph-locale.js b/server/jobs/fetch-graph-locale.js index 897575ad..fe09e088 100644 --- a/server/jobs/fetch-graph-locale.js +++ b/server/jobs/fetch-graph-locale.js @@ -38,7 +38,8 @@ module.exports = async (job) => { const locales = await WIKI.redis.get('locales') if (locales) { const currentLocale = _.find(JSON.parse(locales), ['code', job.data.locale]) || {} - await WIKI.db.Locale.upsert({ + await WIKI.db.locales.query().delete().where('code', job.data.locale) + await WIKI.db.locales.query().insert({ code: job.data.locale, strings: lcObj, isRTL: currentLocale.isRTL, diff --git a/server/jobs/sync-graph-locales.js b/server/jobs/sync-graph-locales.js index e8116e50..4417ad38 100644 --- a/server/jobs/sync-graph-locales.js +++ b/server/jobs/sync-graph-locales.js @@ -60,13 +60,13 @@ module.exports = async (job) => { _.set(lcObj, row.key.replace(':', '.'), row.value) }) - await WIKI.db.Locale.upsert({ + await WIKI.db.locales.query().update({ code: WIKI.config.site.lang, strings: lcObj, isRTL: currentLocale.isRTL, name: currentLocale.name, nativeName: currentLocale.nativeName - }) + }).where('code', WIKI.config.site.lang) } WIKI.logger.info('Syncing locales with Graph endpoint: [ COMPLETED ]') diff --git a/server/models/user.js b/server/models/user.js deleted file mode 100644 index 3b2013d7..00000000 --- a/server/models/user.js +++ /dev/null @@ -1,210 +0,0 @@ -/* global WIKI */ - -const Promise = require('bluebird') -const bcrypt = require('bcryptjs-then') -const _ = require('lodash') -const tfa = require('node-2fa') -const securityHelper = require('../helpers/security') - -/** - * Users schema - */ -module.exports = (sequelize, DataTypes) => { - let userSchema = sequelize.define('user', { - email: { - type: DataTypes.STRING, - allowNull: false, - validate: { - isEmail: true - } - }, - provider: { - type: DataTypes.STRING, - allowNull: false - }, - providerId: { - type: DataTypes.STRING, - allowNull: true - }, - password: { - type: DataTypes.STRING, - allowNull: true - }, - name: { - type: DataTypes.STRING, - allowNull: true - }, - role: { - type: DataTypes.ENUM('admin', 'user', 'guest'), - allowNull: false - }, - tfaIsActive: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: false - }, - tfaSecret: { - type: DataTypes.STRING, - allowNull: true - } - }, { - timestamps: true, - version: true, - indexes: [ - { - unique: true, - fields: ['provider', 'email'] - } - ] - }) - - userSchema.prototype.validatePassword = async function (rawPwd) { - if (await bcrypt.compare(rawPwd, this.password) === true) { - return true - } else { - throw new WIKI.Error.AuthLoginFailed() - } - } - - userSchema.prototype.enableTFA = async function () { - let tfaInfo = tfa.generateSecret({ - name: WIKI.config.site.title - }) - this.tfaIsActive = true - this.tfaSecret = tfaInfo.secret - return this.save() - } - - userSchema.prototype.disableTFA = async function () { - this.tfaIsActive = false - this.tfaSecret = '' - return this.save() - } - - userSchema.prototype.verifyTFA = function (code) { - let result = tfa.verifyToken(this.tfaSecret, code) - return (result && _.has(result, 'delta') && result.delta === 0) - } - - userSchema.login = async (opts, context) => { - if (_.has(WIKI.config.auth.strategies, opts.provider)) { - _.set(context.req, 'body.email', opts.username) - _.set(context.req, 'body.password', opts.password) - - // Authenticate - return new Promise((resolve, reject) => { - WIKI.auth.passport.authenticate(opts.provider, async (err, user, info) => { - if (err) { return reject(err) } - if (!user) { return reject(new WIKI.Error.AuthLoginFailed()) } - - // Is 2FA required? - if (user.tfaIsActive) { - try { - let loginToken = await securityHelper.generateToken(32) - await WIKI.redis.set(`tfa:${loginToken}`, user.id, 'EX', 600) - return resolve({ - tfaRequired: true, - tfaLoginToken: loginToken - }) - } catch (err) { - WIKI.logger.warn(err) - return reject(new WIKI.Error.AuthGenericError()) - } - } else { - // No 2FA, log in user - return context.req.logIn(user, err => { - if (err) { return reject(err) } - resolve({ - tfaRequired: false - }) - }) - } - })(context.req, context.res, () => {}) - }) - } else { - throw new WIKI.Error.AuthProviderInvalid() - } - } - - userSchema.loginTFA = async (opts, context) => { - if (opts.securityCode.length === 6 && opts.loginToken.length === 64) { - let result = await WIKI.redis.get(`tfa:${opts.loginToken}`) - if (result) { - let userId = _.toSafeInteger(result) - if (userId && userId > 0) { - let user = await WIKI.db.User.findById(userId) - if (user && user.verifyTFA(opts.securityCode)) { - return Promise.fromCallback(clb => { - context.req.logIn(user, clb) - }).return({ - succeeded: true, - message: 'Login Successful' - }).catch(err => { - WIKI.logger.warn(err) - throw new WIKI.Error.AuthGenericError() - }) - } else { - throw new WIKI.Error.AuthTFAFailed() - } - } - } - } - throw new WIKI.Error.AuthTFAInvalid() - } - - userSchema.processProfile = (profile) => { - let primaryEmail = '' - if (_.isArray(profile.emails)) { - let e = _.find(profile.emails, ['primary', true]) - primaryEmail = (e) ? e.value : _.first(profile.emails).value - } else if (_.isString(profile.email) && profile.email.length > 5) { - primaryEmail = profile.email - } else if (_.isString(profile.mail) && profile.mail.length > 5) { - primaryEmail = profile.mail - } else if (profile.user && profile.user.email && profile.user.email.length > 5) { - primaryEmail = profile.user.email - } else { - return Promise.reject(new Error(WIKI.lang.t('auth:errors.invaliduseremail'))) - } - - profile.provider = _.lowerCase(profile.provider) - primaryEmail = _.toLower(primaryEmail) - - return WIKI.db.User.findOneAndUpdate({ - email: primaryEmail, - provider: profile.provider - }, { - email: primaryEmail, - provider: profile.provider, - providerId: profile.id, - name: profile.displayName || _.split(primaryEmail, '@')[0] - }, { - new: true - }).then((user) => { - // Handle unregistered accounts - if (!user && profile.provider !== 'local' && (WIKI.config.auth.defaultReadAccess || profile.provider === 'ldap' || profile.provider === 'azure')) { - let nUsr = { - email: primaryEmail, - provider: profile.provider, - providerId: profile.id, - password: '', - name: profile.displayName || profile.name || profile.cn, - rights: [{ - role: 'read', - path: '/', - exact: false, - deny: false - }] - } - return WIKI.db.User.create(nUsr) - } - return user || Promise.reject(new Error(WIKI.lang.t('auth:errors:notyetauthorized'))) - }) - } - - userSchema.hashPassword = (rawPwd) => { - return bcrypt.hash(rawPwd) - } - - return userSchema -} diff --git a/server/models/_relations.js b/server/models_old/_relations.js similarity index 100% rename from server/models/_relations.js rename to server/models_old/_relations.js diff --git a/server/models/comment.js b/server/models_old/comment.js similarity index 100% rename from server/models/comment.js rename to server/models_old/comment.js diff --git a/server/models/document.js b/server/models_old/document.js similarity index 100% rename from server/models/document.js rename to server/models_old/document.js diff --git a/server/models/file.js b/server/models_old/file.js similarity index 100% rename from server/models/file.js rename to server/models_old/file.js diff --git a/server/models/folder.js b/server/models_old/folder.js similarity index 100% rename from server/models/folder.js rename to server/models_old/folder.js diff --git a/server/models/group.js b/server/models_old/group.js similarity index 100% rename from server/models/group.js rename to server/models_old/group.js diff --git a/server/models/locale.js b/server/models_old/locale.js similarity index 100% rename from server/models/locale.js rename to server/models_old/locale.js diff --git a/server/models/right.js b/server/models_old/right.js similarity index 100% rename from server/models/right.js rename to server/models_old/right.js diff --git a/server/models/setting.js b/server/models_old/setting.js similarity index 100% rename from server/models/setting.js rename to server/models_old/setting.js diff --git a/server/models/tag.js b/server/models_old/tag.js similarity index 100% rename from server/models/tag.js rename to server/models_old/tag.js diff --git a/server/modules/authentication/auth0.js b/server/modules/authentication/auth0.js index 453c0dab..58ec34ff 100644 --- a/server/modules/authentication/auth0.js +++ b/server/modules/authentication/auth0.js @@ -19,7 +19,7 @@ module.exports = { clientSecret: conf.clientSecret, callbackURL: conf.callbackURL }, function (accessToken, refreshToken, profile, cb) { - WIKI.db.User.processProfile(profile).then((user) => { + WIKI.db.users.processProfile(profile).then((user) => { return cb(null, user) || true }).catch((err) => { return cb(err, null) || true diff --git a/server/modules/authentication/azure.js b/server/modules/authentication/azure.js index ad191a3c..6dae3b62 100644 --- a/server/modules/authentication/azure.js +++ b/server/modules/authentication/azure.js @@ -24,7 +24,7 @@ module.exports = { let waadProfile = jwt.decode(params.id_token) waadProfile.id = waadProfile.oid waadProfile.provider = 'azure' - WIKI.db.User.processProfile(waadProfile).then((user) => { + WIKI.db.users.processProfile(waadProfile).then((user) => { return cb(null, user) || true }).catch((err) => { return cb(err, null) || true diff --git a/server/modules/authentication/discord.js b/server/modules/authentication/discord.js index 2e22b1b7..1a5e2f5f 100644 --- a/server/modules/authentication/discord.js +++ b/server/modules/authentication/discord.js @@ -19,7 +19,7 @@ module.exports = { callbackURL: conf.callbackURL, scope: 'identify email' }, function (accessToken, refreshToken, profile, cb) { - WIKI.db.User.processProfile(profile).then((user) => { + WIKI.db.users.processProfile(profile).then((user) => { return cb(null, user) || true }).catch((err) => { return cb(err, null) || true diff --git a/server/modules/authentication/dropbox.js b/server/modules/authentication/dropbox.js index b20050a3..750a82e0 100644 --- a/server/modules/authentication/dropbox.js +++ b/server/modules/authentication/dropbox.js @@ -19,7 +19,7 @@ module.exports = { clientSecret: conf.clientSecret, callbackURL: conf.callbackURL }, (accessToken, refreshToken, profile, cb) => { - WIKI.db.User.processProfile(profile).then((user) => { + WIKI.db.users.processProfile(profile).then((user) => { return cb(null, user) || true }).catch((err) => { return cb(err, null) || true diff --git a/server/modules/authentication/facebook.js b/server/modules/authentication/facebook.js index 374e7dd8..54aa7628 100644 --- a/server/modules/authentication/facebook.js +++ b/server/modules/authentication/facebook.js @@ -19,7 +19,7 @@ module.exports = { callbackURL: conf.callbackURL, profileFields: ['id', 'displayName', 'email'] }, function (accessToken, refreshToken, profile, cb) { - WIKI.db.User.processProfile(profile).then((user) => { + WIKI.db.users.processProfile(profile).then((user) => { return cb(null, user) || true }).catch((err) => { return cb(err, null) || true diff --git a/server/modules/authentication/github.js b/server/modules/authentication/github.js index aaf92e7c..da618805 100644 --- a/server/modules/authentication/github.js +++ b/server/modules/authentication/github.js @@ -19,7 +19,7 @@ module.exports = { callbackURL: conf.callbackURL, scope: ['user:email'] }, (accessToken, refreshToken, profile, cb) => { - WIKI.db.User.processProfile(profile).then((user) => { + WIKI.db.users.processProfile(profile).then((user) => { return cb(null, user) || true }).catch((err) => { return cb(err, null) || true diff --git a/server/modules/authentication/google.js b/server/modules/authentication/google.js index 0298058f..2553995b 100644 --- a/server/modules/authentication/google.js +++ b/server/modules/authentication/google.js @@ -18,7 +18,7 @@ module.exports = { clientSecret: conf.clientSecret, callbackURL: conf.callbackURL }, (accessToken, refreshToken, profile, cb) => { - WIKI.db.User.processProfile(profile).then((user) => { + WIKI.db.users.processProfile(profile).then((user) => { return cb(null, user) || true }).catch((err) => { return cb(err, null) || true diff --git a/server/modules/authentication/ldap.js b/server/modules/authentication/ldap.js index 1991b019..c924baad 100644 --- a/server/modules/authentication/ldap.js +++ b/server/modules/authentication/ldap.js @@ -33,7 +33,7 @@ module.exports = { }, (profile, cb) => { profile.provider = 'ldap' profile.id = profile.dn - WIKI.db.User.processProfile(profile).then((user) => { + WIKI.db.users.processProfile(profile).then((user) => { return cb(null, user) || true }).catch((err) => { return cb(err, null) || true diff --git a/server/modules/authentication/local.js b/server/modules/authentication/local.js index db96f8d8..2b67ea2d 100644 --- a/server/modules/authentication/local.js +++ b/server/modules/authentication/local.js @@ -17,14 +17,12 @@ module.exports = { usernameField: 'email', passwordField: 'password' }, (uEmail, uPassword, done) => { - WIKI.db.User.findOne({ - where: { - email: uEmail, - provider: 'local' - } + WIKI.db.users.query().findOne({ + email: uEmail, + provider: 'local' }).then((user) => { if (user) { - return user.validatePassword(uPassword).then(() => { + return user.verifyPassword(uPassword).then(() => { return done(null, user) || true }).catch((err) => { return done(err, null) diff --git a/server/modules/authentication/microsoft.js b/server/modules/authentication/microsoft.js index 989373c3..cfe23760 100644 --- a/server/modules/authentication/microsoft.js +++ b/server/modules/authentication/microsoft.js @@ -18,7 +18,7 @@ module.exports = { clientSecret: conf.clientSecret, callbackURL: conf.callbackURL }, function (accessToken, refreshToken, profile, cb) { - WIKI.db.User.processProfile(profile).then((user) => { + WIKI.db.users.processProfile(profile).then((user) => { return cb(null, user) || true }).catch((err) => { return cb(err, null) || true diff --git a/server/modules/authentication/oauth2.js b/server/modules/authentication/oauth2.js index 6f930efe..e8ad97ff 100644 --- a/server/modules/authentication/oauth2.js +++ b/server/modules/authentication/oauth2.js @@ -20,7 +20,7 @@ module.exports = { clientSecret: conf.clientSecret, callbackURL: conf.callbackURL }, (accessToken, refreshToken, profile, cb) => { - WIKI.db.User.processProfile(profile).then((user) => { + WIKI.db.users.processProfile(profile).then((user) => { return cb(null, user) || true }).catch((err) => { return cb(err, null) || true diff --git a/server/modules/authentication/slack.js b/server/modules/authentication/slack.js index aa0a7e61..76291352 100644 --- a/server/modules/authentication/slack.js +++ b/server/modules/authentication/slack.js @@ -18,7 +18,7 @@ module.exports = { clientSecret: conf.clientSecret, callbackURL: conf.callbackURL }, (accessToken, refreshToken, profile, cb) => { - WIKI.db.User.processProfile(profile).then((user) => { + WIKI.db.users.processProfile(profile).then((user) => { return cb(null, user) || true }).catch((err) => { return cb(err, null) || true diff --git a/server/modules/authentication/twitch.js b/server/modules/authentication/twitch.js index 10266ac6..952b318b 100644 --- a/server/modules/authentication/twitch.js +++ b/server/modules/authentication/twitch.js @@ -19,7 +19,7 @@ module.exports = { callbackURL: conf.callbackURL, scope: 'user_read' }, function (accessToken, refreshToken, profile, cb) { - WIKI.db.User.processProfile(profile).then((user) => { + WIKI.db.users.processProfile(profile).then((user) => { return cb(null, user) || true }).catch((err) => { return cb(err, null) || true diff --git a/server/setup.js b/server/setup.js index c06df4e8..07ea151a 100644 --- a/server/setup.js +++ b/server/setup.js @@ -5,7 +5,7 @@ const path = require('path') module.exports = () => { WIKI.config.site = { path: '', - title: 'WIKI.js' + title: 'Wiki.js' } WIKI.system = require('./core/system') @@ -298,22 +298,46 @@ module.exports = () => { // Save config to DB WIKI.logger.info('Persisting config to DB...') - await WIKI.configSvc.saveToDb() + await WIKI.db.settings.query().insert([ + { key: 'auth', value: WIKI.config.auth }, + { key: 'features', value: WIKI.config.features }, + { key: 'logging', value: WIKI.config.logging }, + { key: 'site', value: WIKI.config.site }, + { key: 'theme', value: WIKI.config.theme }, + { key: 'uploads', value: WIKI.config.uploads } + ]) // Create root administrator WIKI.logger.info('Creating root administrator...') - await WIKI.db.User.upsert({ + await WIKI.db.users.query().insert({ email: req.body.adminEmail, provider: 'local', - password: await WIKI.db.User.hashPassword(req.body.adminPassword), + password: req.body.adminPassword, name: 'Administrator', role: 'admin', tfaIsActive: false }) + // Create Guest account + WIKI.logger.info('Creating root administrator...') + const guestUsr = await WIKI.db.users.query().findOne({ + provider: 'local', + email: 'guest@example.com' + }) + if (!guestUsr) { + await WIKI.db.users.query().insert({ + provider: 'local', + email: 'guest@example.com', + name: 'Guest', + password: '', + role: 'guest', + tfaIsActive: false + }) + } + // Create default locale WIKI.logger.info('Installing default locale...') - await WIKI.db.Locale.upsert({ + await WIKI.db.locales.query().insert({ code: 'en', strings: require('./locales/default.json'), isRTL: false, @@ -330,7 +354,7 @@ module.exports = () => { WIKI.logger.info('Stopping Setup...') WIKI.server.destroy(() => { - WIKI.logger.info('Setup stopped. Starting WIKI.js...') + WIKI.logger.info('Setup stopped. Starting Wiki.js...') _.delay(() => { WIKI.kernel.bootMaster() }, 1000) diff --git a/yarn.lock b/yarn.lock index 8264e31c..631cee00 100644 Binary files a/yarn.lock and b/yarn.lock differ