summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/config/default.js19
-rw-r--r--lib/config/environment.js6
-rw-r--r--lib/config/index.js49
-rw-r--r--lib/config/utils.js33
-rw-r--r--lib/csp.js2
-rw-r--r--lib/logger.js36
-rw-r--r--lib/migrations/20150702001020-update-to-0_3_1.js2
-rw-r--r--lib/migrations/20160112220142-note-add-lastchange.js2
-rw-r--r--lib/migrations/20160420180355-note-add-alias.js2
-rw-r--r--lib/migrations/20160515114000-user-add-tokens.js2
-rw-r--r--lib/migrations/20160607060246-support-revision.js2
-rw-r--r--lib/migrations/20160703062241-support-authorship.js2
-rw-r--r--lib/migrations/20161009040430-support-delete-note.js2
-rw-r--r--lib/migrations/20161201050312-support-email-signin.js4
-rw-r--r--lib/models/index.js1
-rw-r--r--lib/models/revision.js3
-rw-r--r--lib/models/user.js2
-rwxr-xr-xlib/ot/editor-socketio-server.js10
-rw-r--r--lib/realtime.js5
-rw-r--r--lib/response.js81
-rw-r--r--lib/web/auth/index.js1
-rw-r--r--lib/web/auth/oauth2/index.js8
-rw-r--r--lib/web/auth/openid/index.js61
-rw-r--r--lib/web/imageRouter/filesystem.js3
-rw-r--r--lib/web/statusRouter.js4
25 files changed, 215 insertions, 127 deletions
diff --git a/lib/config/default.js b/lib/config/default.js
index 6096bce4..71375b98 100644
--- a/lib/config/default.js
+++ b/lib/config/default.js
@@ -1,10 +1,13 @@
'use strict'
+const os = require('os')
+
module.exports = {
domain: '',
urlPath: '',
host: '0.0.0.0',
port: 3000,
+ loglevel: 'info',
urlAddPort: false,
allowOrigin: ['localhost'],
useSSL: false,
@@ -38,15 +41,10 @@ module.exports = {
sslCAPath: '',
dhParamPath: '',
// other path
- tmpPath: './tmp',
+ viewPath: './public/views',
+ tmpPath: os.tmpdir(),
defaultNotePath: './public/default.md',
docsPath: './public/docs',
- indexPath: './public/views/index.ejs',
- codimdPath: './public/views/codimd.ejs',
- errorPath: './public/views/error.ejs',
- prettyPath: './public/views/pretty.ejs',
- slidePath: './public/views/slide.ejs',
- constantsPath: './public/js/lib/common/constant.ejs',
uploadsPath: './public/uploads',
// session
sessionName: 'connect.sid',
@@ -83,6 +81,7 @@ module.exports = {
},
// authentication
oauth2: {
+ providerName: undefined,
authorizationURL: undefined,
tokenURL: undefined,
clientID: undefined,
@@ -104,7 +103,8 @@ module.exports = {
baseURL: undefined,
clientID: undefined,
clientSecret: undefined,
- scope: undefined
+ scope: undefined,
+ version: 'v4'
},
mattermost: {
baseURL: undefined,
@@ -149,5 +149,6 @@ module.exports = {
email: true,
allowEmailRegister: true,
allowGravatar: true,
- allowPDFExport: true
+ allowPDFExport: true,
+ openID: true
}
diff --git a/lib/config/environment.js b/lib/config/environment.js
index 6c4ce92f..4220e54d 100644
--- a/lib/config/environment.js
+++ b/lib/config/environment.js
@@ -3,11 +3,13 @@
const {toBooleanConfig, toArrayConfig, toIntegerConfig} = require('./utils')
module.exports = {
+ sourceURL: process.env.CMD_SOURCE_URL,
domain: process.env.CMD_DOMAIN,
urlPath: process.env.CMD_URL_PATH,
host: process.env.CMD_HOST,
port: toIntegerConfig(process.env.CMD_PORT),
path: process.env.CMD_PATH,
+ loglevel: process.env.CMD_LOGLEVEL,
urlAddPort: toBooleanConfig(process.env.CMD_URL_ADDPORT),
useSSL: toBooleanConfig(process.env.CMD_USESSL),
hsts: {
@@ -75,6 +77,7 @@ module.exports = {
clientSecret: process.env.CMD_MATTERMOST_CLIENTSECRET
},
oauth2: {
+ providerName: process.env.CMD_OAUTH2_PROVIDERNAME,
baseURL: process.env.CMD_OAUTH2_BASEURL,
userProfileURL: process.env.CMD_OAUTH2_USER_PROFILE_URL,
userProfileUsernameAttr: process.env.CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR,
@@ -123,5 +126,6 @@ module.exports = {
email: toBooleanConfig(process.env.CMD_EMAIL),
allowEmailRegister: toBooleanConfig(process.env.CMD_ALLOW_EMAIL_REGISTER),
allowGravatar: toBooleanConfig(process.env.CMD_ALLOW_GRAVATAR),
- allowPDFExport: toBooleanConfig(process.env.CMD_ALLOW_PDF_EXPORT)
+ allowPDFExport: toBooleanConfig(process.env.CMD_ALLOW_PDF_EXPORT),
+ openID: toBooleanConfig(process.env.CMD_OPENID)
}
diff --git a/lib/config/index.js b/lib/config/index.js
index f96684ea..c1005b0b 100644
--- a/lib/config/index.js
+++ b/lib/config/index.js
@@ -8,22 +8,30 @@ const {merge} = require('lodash')
const deepFreeze = require('deep-freeze')
const {Environment, Permission} = require('./enum')
const logger = require('../logger')
+const {getGitCommit, getGitHubURL} = require('./utils')
-const appRootPath = path.join(__dirname, '../../')
+const appRootPath = path.resolve(__dirname, '../../')
const env = process.env.NODE_ENV || Environment.development
const debugConfig = {
debug: (env === Environment.development)
}
// Get version string from package.json
-const {version} = require(path.join(appRootPath, 'package.json'))
+const {version, repository} = require(path.join(appRootPath, 'package.json'))
+
+const commitID = getGitCommit(appRootPath)
+const sourceURL = getGitHubURL(repository.url, commitID || version)
+const fullversion = commitID ? `${version}-${commitID}` : version
const packageConfig = {
version: version,
- minimumCompatibleVersion: '0.5.0'
+ minimumCompatibleVersion: '0.5.0',
+ fullversion: fullversion,
+ sourceURL: sourceURL
}
-const configFilePath = path.join(appRootPath, 'config.json')
+const configFilePath = path.resolve(appRootPath, process.env.CMD_CONFIG_FILE ||
+'config.json')
const fileConfig = fs.existsSync(configFilePath) ? require(configFilePath)[env] : undefined
let config = require('./default')
@@ -37,6 +45,12 @@ merge(config, require('./hackmdEnvironment'))
merge(config, require('./environment'))
merge(config, require('./dockerSecret'))
+if (['debug', 'verbose', 'info', 'warn', 'error'].includes(config.loglevel)) {
+ logger.level = config.loglevel
+} else {
+ logger.error('Selected loglevel %s doesn\'t exist, using default level \'debug\'. Available options: debug, verbose, info, warn, error', config.loglevel)
+}
+
// load LDAP CA
if (config.ldap.tlsca) {
let ca = config.ldap.tlsca.split(',')
@@ -95,6 +109,7 @@ config.isGoogleEnable = config.google.clientID && config.google.clientSecret
config.isDropboxEnable = config.dropbox.clientID && config.dropbox.clientSecret
config.isTwitterEnable = config.twitter.consumerKey && config.twitter.consumerSecret
config.isEmailEnable = config.email
+config.isOpenIDEnable = config.openID
config.isGitHubEnable = config.github.clientID && config.github.clientSecret
config.isGitLabEnable = config.gitlab.clientID && config.gitlab.clientSecret
config.isMattermostEnable = config.mattermost.clientID && config.mattermost.clientSecret
@@ -104,10 +119,12 @@ config.isOAuth2Enable = config.oauth2.clientID && config.oauth2.clientSecret
config.isPDFExportEnable = config.allowPDFExport
// Check gitlab api version
-if (config.gitlab.version !== 'v4' && config.gitlab.version !== 'v3') {
+if (config.gitlab && config.gitlab.version !== 'v4' && config.gitlab.version !== 'v3') {
logger.warn('config.js contains wrong version (' + config.gitlab.version + ') for gitlab api; it should be \'v3\' or \'v4\'. Defaulting to v4')
config.gitlab.version = 'v4'
}
+// If gitlab scope is api, enable snippets Export/import
+config.isGitlabSnippetsEnable = (!config.gitlab.scope || config.gitlab.scope === 'api')
// Only update i18n files in development setups
config.updateI18nFiles = (env === Environment.development)
@@ -173,20 +190,14 @@ config.sslCAPath.forEach(function (capath, i, array) {
array[i] = path.resolve(appRootPath, capath)
})
-config.sslCertPath = path.join(appRootPath, config.sslCertPath)
-config.sslKeyPath = path.join(appRootPath, config.sslKeyPath)
-config.dhParamPath = path.join(appRootPath, config.dhParamPath)
-
-config.tmpPath = path.join(appRootPath, config.tmpPath)
-config.defaultNotePath = path.join(appRootPath, config.defaultNotePath)
-config.docsPath = path.join(appRootPath, config.docsPath)
-config.indexPath = path.join(appRootPath, config.indexPath)
-config.codimdPath = path.join(appRootPath, config.codimdPath)
-config.errorPath = path.join(appRootPath, config.errorPath)
-config.prettyPath = path.join(appRootPath, config.prettyPath)
-config.slidePath = path.join(appRootPath, config.slidePath)
-config.constantsPath = path.join(appRootPath, config.constantsPath)
-config.uploadsPath = path.join(appRootPath, config.uploadsPath)
+config.sslCertPath = path.resolve(appRootPath, config.sslCertPath)
+config.sslKeyPath = path.resolve(appRootPath, config.sslKeyPath)
+config.dhParamPath = path.resolve(appRootPath, config.dhParamPath)
+config.viewPath = path.resolve(appRootPath, config.viewPath)
+config.tmpPath = path.resolve(appRootPath, config.tmpPath)
+config.defaultNotePath = path.resolve(appRootPath, config.defaultNotePath)
+config.docsPath = path.resolve(appRootPath, config.docsPath)
+config.uploadsPath = path.resolve(appRootPath, config.uploadsPath)
// make config readonly
config = deepFreeze(config)
diff --git a/lib/config/utils.js b/lib/config/utils.js
index b2406cf1..9646f8c0 100644
--- a/lib/config/utils.js
+++ b/lib/config/utils.js
@@ -1,5 +1,8 @@
'use strict'
+const fs = require('fs')
+const path = require('path')
+
exports.toBooleanConfig = function toBooleanConfig (configValue) {
if (configValue && typeof configValue === 'string') {
return (configValue === 'true')
@@ -20,3 +23,33 @@ exports.toIntegerConfig = function toIntegerConfig (configValue) {
}
return configValue
}
+
+exports.getGitCommit = function getGitCommit (repodir) {
+ if (!fs.existsSync(repodir + '/.git/HEAD')) {
+ return undefined
+ }
+ let reference = fs.readFileSync(repodir + '/.git/HEAD', 'utf8')
+ if (reference.startsWith('ref: ')) {
+ reference = reference.substr(5).replace('\n', '')
+ reference = fs.readFileSync(path.resolve(repodir + '/.git', reference), 'utf8')
+ }
+ reference = reference.replace('\n', '')
+ return reference
+}
+
+exports.getGitHubURL = function getGitHubURL (repo, reference) {
+ // if it's not a github reference, we handle handle that anyway
+ if (!repo.startsWith('https://github.com') && !repo.startsWith('git@github.com')) {
+ return repo
+ }
+ if (repo.startsWith('git@github.com') || repo.startsWith('ssh://git@github.com')) {
+ repo = repo.replace(/^(ssh:\/\/)?git@github.com:/, 'https://github.com/')
+ }
+
+ if (repo.endsWith('.git')) {
+ repo = repo.replace(/\.git$/, '/')
+ } else if (!repo.endsWith('/')) {
+ repo = repo + '/'
+ }
+ return repo + 'tree/' + reference
+}
diff --git a/lib/csp.js b/lib/csp.js
index 0987df85..96be533a 100644
--- a/lib/csp.js
+++ b/lib/csp.js
@@ -9,7 +9,7 @@ var defaultDirectives = {
// ^ TODO: Remove unsafe-eval - webpack script-loader issues https://github.com/hackmdio/codimd/issues/594
imgSrc: ['*'],
styleSrc: ['\'self\'', '\'unsafe-inline\'', 'https://assets-cdn.github.com'], // unsafe-inline is required for some libs, plus used in views
- fontSrc: ['\'self\'', 'https://public.slidesharecdn.com'],
+ fontSrc: ['\'self\'', 'data:', 'https://public.slidesharecdn.com'],
objectSrc: ['*'], // Chrome PDF viewer treats PDFs as objects :/
mediaSrc: ['*'],
childSrc: ['*'],
diff --git a/lib/logger.js b/lib/logger.js
index f8b3895c..5ef1860a 100644
--- a/lib/logger.js
+++ b/lib/logger.js
@@ -1,23 +1,27 @@
'use strict'
-const winston = require('winston')
+const {createLogger, format, transports} = require('winston')
-class Logger extends winston.Logger {
- // Implement stream.writable.write interface
- write (chunk) {
- this.info(chunk)
- }
-}
-
-module.exports = new Logger({
+const logger = createLogger({
+ level: 'debug',
+ format: format.combine(
+ format.uncolorize(),
+ format.timestamp(),
+ format.align(),
+ format.splat(),
+ format.printf(info => `${info.timestamp} ${info.level}: ${info.message}`)
+ ),
transports: [
- new winston.transports.Console({
- level: 'debug',
- handleExceptions: true,
- json: false,
- colorize: false,
- timestamp: true
+ new transports.Console({
+ handleExceptions: true
})
],
- emitErrs: true,
exitOnError: false
})
+
+logger.stream = {
+ write: function (message, encoding) {
+ logger.info(message)
+ }
+}
+
+module.exports = logger
diff --git a/lib/migrations/20150702001020-update-to-0_3_1.js b/lib/migrations/20150702001020-update-to-0_3_1.js
index e661a343..e1a88661 100644
--- a/lib/migrations/20150702001020-update-to-0_3_1.js
+++ b/lib/migrations/20150702001020-update-to-0_3_1.js
@@ -21,7 +21,7 @@ module.exports = {
defaultValue: 0
})
}).catch(function (error) {
- if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'shortid'") {
+ if (error.message === 'SQLITE_ERROR: duplicate column name: shortid' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'shortid'" || error.message === 'column "shortid" of relation "Notes" already exists') {
console.log('Migration has already run… ignoring.')
} else {
throw error
diff --git a/lib/migrations/20160112220142-note-add-lastchange.js b/lib/migrations/20160112220142-note-add-lastchange.js
index d0030d6b..87e3ff19 100644
--- a/lib/migrations/20160112220142-note-add-lastchange.js
+++ b/lib/migrations/20160112220142-note-add-lastchange.js
@@ -8,7 +8,7 @@ module.exports = {
type: Sequelize.DATE
})
}).catch(function (error) {
- if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'lastchangeuserId'") {
+ if (error.message === 'SQLITE_ERROR: duplicate column name: lastchangeuserId' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'lastchangeuserId'" || error.message === 'column "lastchangeuserId" of relation "Notes" already exists') {
console.log('Migration has already run… ignoring.')
} else {
throw error
diff --git a/lib/migrations/20160420180355-note-add-alias.js b/lib/migrations/20160420180355-note-add-alias.js
index 4bad29ca..45d53e69 100644
--- a/lib/migrations/20160420180355-note-add-alias.js
+++ b/lib/migrations/20160420180355-note-add-alias.js
@@ -8,7 +8,7 @@ module.exports = {
indicesType: 'UNIQUE'
})
}).catch(function (error) {
- if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'alias'") {
+ if (error.message === 'SQLITE_ERROR: duplicate column name: alias' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'alias'" || error.message === 'column "alias" of relation "Notes" already exists') {
console.log('Migration has already run… ignoring.')
} else {
throw error
diff --git a/lib/migrations/20160515114000-user-add-tokens.js b/lib/migrations/20160515114000-user-add-tokens.js
index 4245f1ad..435ae9cb 100644
--- a/lib/migrations/20160515114000-user-add-tokens.js
+++ b/lib/migrations/20160515114000-user-add-tokens.js
@@ -4,7 +4,7 @@ module.exports = {
return queryInterface.addColumn('Users', 'accessToken', Sequelize.STRING).then(function () {
return queryInterface.addColumn('Users', 'refreshToken', Sequelize.STRING)
}).catch(function (error) {
- if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'accessToken'") {
+ if (error.message === 'SQLITE_ERROR: duplicate column name: accessToken' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'accessToken'" || error.message === 'column "accessToken" of relation "Users" already exists') {
console.log('Migration has already run… ignoring.')
} else {
throw error
diff --git a/lib/migrations/20160607060246-support-revision.js b/lib/migrations/20160607060246-support-revision.js
index 10f288b0..547f89b8 100644
--- a/lib/migrations/20160607060246-support-revision.js
+++ b/lib/migrations/20160607060246-support-revision.js
@@ -16,7 +16,7 @@ module.exports = {
updatedAt: Sequelize.DATE
})
}).catch(function (error) {
- if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'savedAt'") {
+ if (error.message === 'SQLITE_ERROR: duplicate column name: savedAt' | error.message === "ER_DUP_FIELDNAME: Duplicate column name 'savedAt'" || error.message === 'column "savedAt" of relation "Notes" already exists') {
console.log('Migration has already run… ignoring.')
} else {
throw error
diff --git a/lib/migrations/20160703062241-support-authorship.js b/lib/migrations/20160703062241-support-authorship.js
index b3ced8c4..f452b1a7 100644
--- a/lib/migrations/20160703062241-support-authorship.js
+++ b/lib/migrations/20160703062241-support-authorship.js
@@ -17,7 +17,7 @@ module.exports = {
updatedAt: Sequelize.DATE
})
}).catch(function (error) {
- if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'authorship'") {
+ if (error.message === 'SQLITE_ERROR: duplicate column name: authorship' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'authorship'" || error.message === 'column "authorship" of relation "Notes" already exists') {
console.log('Migration has already run… ignoring.')
} else {
throw error
diff --git a/lib/migrations/20161009040430-support-delete-note.js b/lib/migrations/20161009040430-support-delete-note.js
index 4df7a81c..56a336ac 100644
--- a/lib/migrations/20161009040430-support-delete-note.js
+++ b/lib/migrations/20161009040430-support-delete-note.js
@@ -2,7 +2,7 @@
module.exports = {
up: function (queryInterface, Sequelize) {
return queryInterface.addColumn('Notes', 'deletedAt', Sequelize.DATE).catch(function (error) {
- if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'deletedAt'") {
+ if (error.message === 'SQLITE_ERROR: duplicate column name: deletedAt' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'deletedAt'" || error.message === 'column "deletedAt" of relation "Notes" already exists') {
console.log('Migration has already run… ignoring.')
} else {
throw error
diff --git a/lib/migrations/20161201050312-support-email-signin.js b/lib/migrations/20161201050312-support-email-signin.js
index 4653e67a..26bc09ea 100644
--- a/lib/migrations/20161201050312-support-email-signin.js
+++ b/lib/migrations/20161201050312-support-email-signin.js
@@ -3,14 +3,14 @@ module.exports = {
up: function (queryInterface, Sequelize) {
return queryInterface.addColumn('Users', 'email', Sequelize.TEXT).then(function () {
return queryInterface.addColumn('Users', 'password', Sequelize.TEXT).catch(function (error) {
- if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'password'") {
+ if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'password'" || error.message === 'column "password" of relation "Users" already exists') {
console.log('Migration has already run… ignoring.')
} else {
throw error
}
})
}).catch(function (error) {
- if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'email'") {
+ if (error.message === 'SQLITE_ERROR: duplicate column name: email' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'email'" || error.message === 'column "email" of relation "Users" already exists') {
console.log('Migration has already run… ignoring.')
} else {
throw error
diff --git a/lib/models/index.js b/lib/models/index.js
index 0a44ca87..ef70475e 100644
--- a/lib/models/index.js
+++ b/lib/models/index.js
@@ -25,6 +25,7 @@ if (config.dbURL) {
// https://github.com/sequelize/sequelize/issues/6485
function stripNullByte (value) {
value = '' + value
+ // eslint-disable-next-line no-control-regex
return value ? value.replace(/\u0000/g, '') : value
}
sequelize.stripNullByte = stripNullByte
diff --git a/lib/models/revision.js b/lib/models/revision.js
index 8bc95cb1..4ee080da 100644
--- a/lib/models/revision.js
+++ b/lib/models/revision.js
@@ -5,6 +5,7 @@ var async = require('async')
var moment = require('moment')
var childProcess = require('child_process')
var shortId = require('shortid')
+var path = require('path')
// core
var config = require('../config')
@@ -14,7 +15,7 @@ var dmpWorker = createDmpWorker()
var dmpCallbackCache = {}
function createDmpWorker () {
- var worker = childProcess.fork('./lib/workers/dmpWorker.js', {
+ var worker = childProcess.fork(path.resolve(__dirname, '../workers/dmpWorker.js'), {
stdio: 'ignore'
})
if (config.debug) logger.info('dmp worker process started')
diff --git a/lib/models/user.js b/lib/models/user.js
index 1bd8c745..2ebf6d06 100644
--- a/lib/models/user.js
+++ b/lib/models/user.js
@@ -50,7 +50,7 @@ module.exports = function (sequelize, DataTypes) {
}, {
instanceMethods: {
verifyPassword: function (attempt) {
- if (scrypt.verifyKdfSync(new Buffer(this.password, 'hex'), attempt)) {
+ if (scrypt.verifyKdfSync(Buffer.from(this.password, 'hex'), attempt)) {
return this
} else {
return false
diff --git a/lib/ot/editor-socketio-server.js b/lib/ot/editor-socketio-server.js
index 7b204539..5014ac8d 100755
--- a/lib/ot/editor-socketio-server.js
+++ b/lib/ot/editor-socketio-server.js
@@ -44,7 +44,7 @@ EditorSocketIOServer.prototype.addClient = function (socket) {
socket.origin = 'operation';
self.mayWrite(socket, function (mayWrite) {
if (!mayWrite) {
- console.log("User doesn't have the right to edit.");
+ logger.info("User doesn't have the right to edit.");
return;
}
try {
@@ -71,14 +71,14 @@ EditorSocketIOServer.prototype.addClient = function (socket) {
socket.origin = 'selection';
self.mayWrite(socket, function (mayWrite) {
if (!mayWrite) {
- console.log("User doesn't have the right to edit.");
+ logger.info("User doesn't have the right to edit.");
return;
}
self.updateSelection(socket, obj && Selection.fromJSON(obj));
});
});
socket.on('disconnect', function () {
- //console.log("Disconnect");
+ logger.debug("Disconnect");
socket.leave(self.docId);
self.onDisconnect(socket);
/*
@@ -106,7 +106,7 @@ EditorSocketIOServer.prototype.onOperation = function (socket, revision, operati
var clientId = socket.id;
var wrappedPrime = this.receiveOperation(revision, wrapped);
if(!wrappedPrime) return;
- //console.log("new operation: " + JSON.stringify(wrapped));
+ logger.debug("new operation: " + JSON.stringify(wrapped));
this.getClient(clientId).selection = wrappedPrime.meta;
revision = this.operations.length;
socket.emit('ack', revision);
@@ -161,4 +161,4 @@ EditorSocketIOServer.prototype.onDisconnect = function (socket) {
socket.broadcast.to(this.docId).emit('client_left', clientId);
};
-module.exports = EditorSocketIOServer; \ No newline at end of file
+module.exports = EditorSocketIOServer;
diff --git a/lib/realtime.js b/lib/realtime.js
index f6c62d4e..d04ffdc2 100644
--- a/lib/realtime.js
+++ b/lib/realtime.js
@@ -242,6 +242,7 @@ function getStatus (callback) {
}
})
models.User.count().then(function (regcount) {
+ // eslint-disable-next-line standard/no-callback-literal
return callback ? callback({
onlineNotes: Object.keys(notes).length,
onlineUsers: Object.keys(users).length,
@@ -283,7 +284,7 @@ function extractNoteIdFromSocket (socket) {
if (!referer) {
return false
}
- var hostUrl = url.parse(referer)
+ var hostUrl = url.URL.parse(referer)
var noteId = config.urlPath ? hostUrl.pathname.slice(config.urlPath.length + 1, hostUrl.pathname.length).split('/')[1] : hostUrl.pathname.split('/')[1]
return noteId
} else {
@@ -887,7 +888,7 @@ function connection (socket) {
// check version
socket.on('version', function () {
socket.emit('version', {
- version: config.version,
+ version: config.fullversion,
minimumCompatibleVersion: config.minimumCompatibleVersion
})
})
diff --git a/lib/response.js b/lib/response.js
index 37211998..671aa120 100644
--- a/lib/response.js
+++ b/lib/response.js
@@ -32,6 +32,9 @@ var response = {
errorBadRequest: function (res) {
responseError(res, '400', 'Bad Request', 'something not right.')
},
+ errorTooLong: function (res) {
+ responseError(res, '413', 'Payload Too Large', 'Shorten your note!')
+ },
errorInternalError: function (res) {
responseError(res, '500', 'Internal Error', 'wtf.')
},
@@ -51,13 +54,11 @@ var response = {
}
function responseError (res, code, detail, msg) {
- res.status(code).render(config.errorPath, {
- url: config.serverURL,
+ res.status(code).render('error.ejs', {
title: code + ' ' + detail + ' ' + msg,
code: code,
detail: detail,
- msg: msg,
- useCDN: config.useCDN
+ msg: msg
})
}
@@ -66,25 +67,6 @@ function showIndex (req, res, next) {
var deleteToken = ''
var data = {
- url: config.serverURL,
- useCDN: config.useCDN,
- allowAnonymous: config.allowAnonymous,
- allowAnonymousEdits: config.allowAnonymousEdits,
- facebook: config.isFacebookEnable,
- twitter: config.isTwitterEnable,
- github: config.isGitHubEnable,
- gitlab: config.isGitLabEnable,
- mattermost: config.isMattermostEnable,
- dropbox: config.isDropboxEnable,
- google: config.isGoogleEnable,
- ldap: config.isLDAPEnable,
- ldapProviderName: config.ldap.providerName,
- saml: config.isSAMLEnable,
- oauth2: config.isOAuth2Enable,
- oauth2ProviderName: config.oauth2.providerName,
- email: config.isEmailEnable,
- allowEmailRegister: config.allowEmailRegister,
- allowPDFExport: config.allowPDFExport,
signin: authStatus,
infoMessage: req.flash('info'),
errorMessage: req.flash('error'),
@@ -101,11 +83,11 @@ function showIndex (req, res, next) {
}).then(function (user) {
if (user) {
data.deleteToken = user.deleteToken
- res.render(config.indexPath, data)
+ res.render('index.ejs', data)
}
})
} else {
- res.render(config.indexPath, data)
+ res.render('index.ejs', data)
}
}
@@ -119,33 +101,19 @@ function responseCodiMD (res, note) {
'Cache-Control': 'private', // only cache by client
'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
})
- res.render(config.codimdPath, {
- url: config.serverURL,
- title: title,
- useCDN: config.useCDN,
- allowAnonymous: config.allowAnonymous,
- allowAnonymousEdits: config.allowAnonymousEdits,
- facebook: config.isFacebookEnable,
- twitter: config.isTwitterEnable,
- github: config.isGitHubEnable,
- gitlab: config.isGitLabEnable,
- mattermost: config.isMattermostEnable,
- dropbox: config.isDropboxEnable,
- google: config.isGoogleEnable,
- ldap: config.isLDAPEnable,
- ldapProviderName: config.ldap.providerName,
- oauth2ProviderName: config.oauth2.providerName,
- saml: config.isSAMLEnable,
- oauth2: config.isOAuth2Enable,
- email: config.isEmailEnable,
- allowEmailRegister: config.allowEmailRegister,
- allowPDFExport: config.allowPDFExport
+ res.render('codimd.ejs', {
+ title: title
})
}
function newNote (req, res, next) {
var owner = null
- var body = req.body ? req.body : ''
+ var body = ''
+ if (req.body && req.body.length > config.documentMaxLength) {
+ return response.errorTooLong(res)
+ } else if (req.body) {
+ body = req.body
+ }
body = body.replace(/[\r]/g, '')
if (req.isAuthenticated()) {
owner = req.user.id
@@ -244,16 +212,13 @@ function showPublishNote (req, res, next) {
var updatetime = note.lastchangeAt
var title = models.Note.decodeTitle(note.title)
title = models.Note.generateWebTitle(meta.title || title)
- var origin = config.serverURL
var data = {
title: title,
description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
viewcount: note.viewcount,
createtime: createtime,
updatetime: updatetime,
- url: origin,
body: body,
- useCDN: config.useCDN,
owner: note.owner ? note.owner.id : null,
ownerprofile: note.owner ? models.User.getProfile(note.owner) : null,
lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
@@ -275,7 +240,7 @@ function renderPublish (data, res) {
res.set({
'Cache-Control': 'private' // only cache by client
})
- res.render(config.prettyPath, data)
+ res.render('pretty.ejs', data)
}
function actionPublish (req, res, note) {
@@ -341,6 +306,10 @@ function actionPDF (req, res, note) {
var path = config.tmpPath + '/' + Date.now() + '.pdf'
content = content.replace(/\]\(\//g, '](' + url + '/')
markdownpdf().from.string(content).to(path, function () {
+ if (!fs.existsSync(path)) {
+ logger.error('PDF seems to not be generated as expected. File doesn\'t exist: ' + path)
+ return response.errorInternalError(res)
+ }
var stream = fs.createReadStream(path)
var filename = title
// Be careful of special characters
@@ -454,6 +423,9 @@ function publishNoteActions (req, res, next) {
findNote(req, res, function (note) {
var action = req.params.action
switch (action) {
+ case 'download':
+ actionDownload(req, res, note)
+ break
case 'edit':
res.redirect(config.serverURL + '/' + (note.alias ? note.alias : models.Note.encodeNoteId(note.id)))
break
@@ -577,7 +549,7 @@ function gitlabActionProjects (req, res, note) {
ret.accesstoken = user.accessToken
ret.profileid = user.profileid
request(
- config.gitlab.baseURL + '/api/' + config.gitlab.version + '/projects?access_token=' + user.accessToken,
+ config.gitlab.baseURL + '/api/' + config.gitlab.version + '/projects?membership=yes&per_page=100&access_token=' + user.accessToken,
function (error, httpResponse, body) {
if (!error && httpResponse.statusCode === 200) {
ret.projects = JSON.parse(body)
@@ -620,18 +592,15 @@ function showPublishSlide (req, res, next) {
var updatetime = note.lastchangeAt
var title = models.Note.decodeTitle(note.title)
title = models.Note.generateWebTitle(meta.title || title)
- var origin = config.serverURL
var data = {
title: title,
description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
viewcount: note.viewcount,
createtime: createtime,
updatetime: updatetime,
- url: origin,
body: markdown,
theme: meta.slideOptions && utils.isRevealTheme(meta.slideOptions.theme),
meta: JSON.stringify(extracted.meta),
- useCDN: config.useCDN,
owner: note.owner ? note.owner.id : null,
ownerprofile: note.owner ? models.User.getProfile(note.owner) : null,
lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
@@ -653,7 +622,7 @@ function renderPublishSlide (data, res) {
res.set({
'Cache-Control': 'private' // only cache by client
})
- res.render(config.slidePath, data)
+ res.render('slide.ejs', data)
}
module.exports = response
diff --git a/lib/web/auth/index.js b/lib/web/auth/index.js
index 61e7c3f9..86ab4b28 100644
--- a/lib/web/auth/index.js
+++ b/lib/web/auth/index.js
@@ -45,6 +45,7 @@ if (config.isLDAPEnable) authRouter.use(require('./ldap'))
if (config.isSAMLEnable) authRouter.use(require('./saml'))
if (config.isOAuth2Enable) authRouter.use(require('./oauth2'))
if (config.isEmailEnable) authRouter.use(require('./email'))
+if (config.isOpenIDEnable) authRouter.use(require('./openid'))
// logout
authRouter.get('/logout', function (req, res) {
diff --git a/lib/web/auth/oauth2/index.js b/lib/web/auth/oauth2/index.js
index f2a3132d..b9160f6e 100644
--- a/lib/web/auth/oauth2/index.js
+++ b/lib/web/auth/oauth2/index.js
@@ -2,13 +2,13 @@
const Router = require('express').Router
const passport = require('passport')
-const OAuth2Strategy = require('passport-oauth2').Strategy
+const { Strategy, InternalOAuthError } = require('passport-oauth2')
const config = require('../../../config')
const {setReturnToFromReferer, passportGeneralCallback} = require('../utils')
let oauth2Auth = module.exports = Router()
-class OAuth2CustomStrategy extends OAuth2Strategy {
+class OAuth2CustomStrategy extends Strategy {
constructor (options, verify) {
options.customHeaders = options.customHeaders || {}
super(options, verify)
@@ -22,7 +22,7 @@ class OAuth2CustomStrategy extends OAuth2Strategy {
var json
if (err) {
- return done(new passport.InternalOAuthError('Failed to fetch user profile', err))
+ return done(new InternalOAuthError('Failed to fetch user profile', err))
}
try {
@@ -67,7 +67,7 @@ OAuth2CustomStrategy.prototype.userProfile = function (accessToken, done) {
var json
if (err) {
- return done(new passport.InternalOAuthError('Failed to fetch user profile', err))
+ return done(new InternalOAuthError('Failed to fetch user profile', err))
}
try {
diff --git a/lib/web/auth/openid/index.js b/lib/web/auth/openid/index.js
new file mode 100644
index 00000000..96f61807
--- /dev/null
+++ b/lib/web/auth/openid/index.js
@@ -0,0 +1,61 @@
+'use strict'
+
+const Router = require('express').Router
+const passport = require('passport')
+const OpenIDStrategy = require('@passport-next/passport-openid').Strategy
+const config = require('../../../config')
+const models = require('../../../models')
+const logger = require('../../../logger')
+const {urlencodedParser} = require('../../utils')
+const {setReturnToFromReferer} = require('../utils')
+
+let openIDAuth = module.exports = Router()
+
+passport.use(new OpenIDStrategy({
+ returnURL: config.serverURL + '/auth/openid/callback',
+ realm: config.serverURL,
+ profile: true
+}, function (openid, profile, done) {
+ var stringifiedProfile = JSON.stringify(profile)
+ models.User.findOrCreate({
+ where: {
+ profileid: openid
+ },
+ defaults: {
+ profile: stringifiedProfile
+ }
+ }).spread(function (user, created) {
+ if (user) {
+ var needSave = false
+ if (user.profile !== stringifiedProfile) {
+ user.profile = stringifiedProfile
+ needSave = true
+ }
+ if (needSave) {
+ user.save().then(function () {
+ if (config.debug) { logger.info('user login: ' + user.id) }
+ return done(null, user)
+ })
+ } else {
+ if (config.debug) { logger.info('user login: ' + user.id) }
+ return done(null, user)
+ }
+ }
+ }).catch(function (err) {
+ logger.error('auth callback failed: ' + err)
+ return done(err, null)
+ })
+}))
+
+openIDAuth.post('/auth/openid', urlencodedParser, function (req, res, next) {
+ setReturnToFromReferer(req)
+ passport.authenticate('openid')(req, res, next)
+})
+
+// openID auth callback
+openIDAuth.get('/auth/openid/callback',
+ passport.authenticate('openid', {
+ successReturnToOrRedirect: config.serverurl + '/',
+ failureRedirect: config.serverurl + '/'
+ })
+)
diff --git a/lib/web/imageRouter/filesystem.js b/lib/web/imageRouter/filesystem.js
index 4bf82b31..a2f8700d 100644
--- a/lib/web/imageRouter/filesystem.js
+++ b/lib/web/imageRouter/filesystem.js
@@ -1,5 +1,6 @@
'use strict'
const url = require('url')
+const path = require('path')
const config = require('../../config')
const logger = require('../../logger')
@@ -15,5 +16,5 @@ exports.uploadImage = function (imagePath, callback) {
return
}
- callback(null, url.resolve(config.serverURL + '/', imagePath.match(/public\/(.+)$/)[1]))
+ callback(null, url.URL.resolve(config.serverURL + '/uploads/', path.basename(imagePath)))
}
diff --git a/lib/web/statusRouter.js b/lib/web/statusRouter.js
index 7ecf3839..2b9cb65f 100644
--- a/lib/web/statusRouter.js
+++ b/lib/web/statusRouter.js
@@ -96,7 +96,7 @@ statusRouter.get('/config', function (req, res) {
domain: config.domain,
urlpath: config.urlPath,
debug: config.debug,
- version: config.version,
+ version: config.fullversion,
DROPBOX_APP_KEY: config.dropbox.appKey,
allowedUploadMimeTypes: config.allowedUploadMimeTypes
}
@@ -105,5 +105,5 @@ statusRouter.get('/config', function (req, res) {
'X-Robots-Tag': 'noindex, nofollow', // prevent crawling
'Content-Type': 'application/javascript'
})
- res.render(config.constantsPath, data)
+ res.render('../js/lib/common/constant.ejs', data)
})