diff options
37 files changed, 447 insertions, 234 deletions
@@ -68,7 +68,7 @@ Just to more confusion: We are still friends with HackMD :heart: ### Prerequisite -- Node.js 6.x or up (test up to 7.5.0) +- Node.js 6.x or up (test up to 7.5.0) and <10.x - Database (PostgreSQL, MySQL, MariaDB, SQLite, MSSQL) use charset `utf8` - npm (and its dependencies, especially [uWebSockets](https://github.com/uWebSockets/uWebSockets#nodejs-developers), [node-gyp](https://github.com/nodejs/node-gyp#installation)) - For **building** CodiMD we recommend to use a machine with at least **2GB** RAM @@ -174,6 +174,7 @@ There are some config settings you need to change in the files below. | --------- | ------ | ----------- | | `NODE_ENV` | `production` or `development` | set current environment (will apply corresponding settings in the `config.json`) | | `DEBUG` | `true` or `false` | set debug mode; show more logs | +| `CMD_CONFIG_FILE` | `/path/to/config.json` | optional override for the path to CodiMD's config file | | `CMD_DOMAIN` | `codimd.org` | domain name | | `CMD_URL_PATH` | `codimd` | sub URL path, like `www.example.com/<URL_PATH>` | | `CMD_HOST` | `localhost` | host to listen on | @@ -228,6 +229,15 @@ There are some config settings you need to change in the files below. | `CMD_SAML_ATTRIBUTE_ID` | `sAMAccountName` | attribute map for `id` (optional, default: NameID of SAML response) | | `CMD_SAML_ATTRIBUTE_USERNAME` | `mailNickname` | attribute map for `username` (optional, default: NameID of SAML response) | | `CMD_SAML_ATTRIBUTE_EMAIL` | `mail` | attribute map for `email` (optional, default: NameID of SAML response if `CMD_SAML_IDENTIFIERFORMAT` is default) | +| `CMD_OAUTH2_USER_PROFILE_URL` | `https://example.com` | where retrieve information about a user after succesful login. Needs to output JSON. (no default value) Refer to the [Mattermost](docs/guides/auth/mattermost-self-hosted.md) or [Nextcloud](docs/guides/auth/nextcloud.md) examples for more details on all of the `CMD_OAUTH2...` options. | +| `CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR` | `name` | where to find the username in the JSON from the user profile URL. (no default value)| +| `CMD_OAUTH2_USER_PROFILE_DISPLAY_NAME_ATTR` | `display-name` | where to find the display-name in the JSON from the user profile URL. (no default value) | +| `CMD_OAUTH2_USER_PROFILE_EMAIL_ATTR` | `email` | where to find the email address in the JSON from the user profile URL. (no default value) | +| `CMD_OAUTH2_TOKEN_URL` | `https://example.com` | sometimes called token endpoint, please refer to the documentation of your OAuth2 provider (no default value) | +| `CMD_OAUTH2_AUTHORIZATION_URL` | `https://example.com` | authorization URL of your provider, please refer to the documentation of your OAuth2 provider (no default value) | +| `CMD_OAUTH2_CLIENT_ID` | `afae02fckafd...` | you will get this from your OAuth2 provider when you register CodiMD as OAuth2-client, (no default value) | +| `CMD_OAUTH2_CLIENT_SECRET` | `afae02fckafd...` | you will get this from your OAuth2 provider when you register CodiMD as OAuth2-client, (no default value) | +| `CMD_OAUTH2_PROVIDERNAME` | `My institution` | Optional name to be displayed at login form indicating the oAuth2 provider | | `CMD_IMGUR_CLIENTID` | no example | Imgur API client id | | `CMD_EMAIL` | `true` or `false` | set to allow email signin | | `CMD_ALLOW_PDF_EXPORT` | `true` or `false` | Enable or disable PDF exports | @@ -277,19 +287,15 @@ There are some config settings you need to change in the files below. | `defaultPermission` | `freely`, `editable`, `limited`, `locked`, `protected` or `private` | set notes default permission (only applied on signed users) | | `dbURL` | `mysql://localhost:3306/database` | set the db URL; if set, then db config (below) won't be applied | | `db` | `{ "dialect": "sqlite", "storage": "./db.codimd.sqlite" }` | set the db configs, [see more here](http://sequelize.readthedocs.org/en/latest/api/sequelize/) | -| `sslKeyPath` | `./cert/client.key` | SSL key path (only need when you set `useSSL`) | -| `sslCertPath` | `./cert/codimd_io.crt` | SSL cert path (only need when you set `useSSL`) | -| `sslCAPath` | `['./cert/COMODORSAAddTrustCA.crt']` | SSL ca chain (only need when you set `useSSL`) | -| `dhParamPath` | `./cert/dhparam.pem` | SSL dhparam path (only need when you set `useSSL`) | -| `tmpPath` | `./tmp/` | temp directory path | -| `defaultNotePath` | `./public/default.md` | default note file path | -| `docsPath` | `./public/docs` | docs directory path | -| `indexPath` | `./public/views/index.ejs` | index template file path | -| `hackmdPath` | `./public/views/hackmd.ejs` | hackmd template file path | -| `errorPath` | `./public/views/error.ejs` | error template file path | -| `prettyPath` | `./public/views/pretty.ejs` | pretty template file path | -| `slidePath` | `./public/views/slide.hbs` | slide template file path | -| `uploadsPath` | `./public/uploads` | uploads directory - needs to be persistent when you use imageUploadType `filesystem` | +| `sslKeyPath` | `./cert/client.key` | SSL key path<sup>1</sup> (only need when you set `useSSL`) | +| `sslCertPath` | `./cert/codimd_io.crt` | SSL cert path<sup>1</sup> (only need when you set `useSSL`) | +| `sslCAPath` | `['./cert/COMODORSAAddTrustCA.crt']` | SSL ca chain<sup>1</sup> (only need when you set `useSSL`) | +| `dhParamPath` | `./cert/dhparam.pem` | SSL dhparam path<sup>1</sup> (only need when you set `useSSL`) | +| `tmpPath` | `./tmp/` | temp directory path<sup>1</sup> | +| `defaultNotePath` | `./public/default.md` | default note file path<sup>1</sup> | +| `docsPath` | `./public/docs` | docs directory path<sup>1</sup> | +| `viewPath` | `./public/views` | template directory path<sup>1</sup> | +| `uploadsPath` | `./public/uploads` | uploads directory<sup>1</sup> - needs to be persistent when you use imageUploadType `filesystem` | | `sessionName` | `connect.sid` | cookie session name | | `sessionSecret` | `secret` | cookie session secret | | `sessionLife` | `14 * 24 * 60 * 60 * 1000` | cookie session life | @@ -298,6 +304,7 @@ There are some config settings you need to change in the files below. | `heartbeatTimeout` | `10000` | socket.io heartbeat timeout | | `documentMaxLength` | `100000` | note max length | | `email` | `true` or `false` | set to allow email signin | +| `oauth2` | `{baseURL: ..., userProfileURL: ..., userProfileUsernameAttr: ..., userProfileDisplayNameAttr: ..., userProfileEmailAttr: ..., tokenURL: ..., authorizationURL: ..., clientID: ..., clientSecret: ...}` | An object detailing your OAuth2 provider. Refer to the [Mattermost](docs/guides/auth/mattermost-self-hosted.md) or [Nextcloud](docs/guides/auth/nextcloud.md) examples for more details!| | `allowEmailRegister` | `true` or `false` | set to allow email register (only applied when email is set, default is `true`. Note `bin/manage_users` might help you if registration is `false`.) | | `allowGravatar` | `true` or `false` | set to `false` to disable gravatar as profile picture source on your instance | | `imageUploadType` | `imgur`, `s3`, `minio`, `azure` or `filesystem`(default) | Where to upload images. For S3, see our Image Upload Guides for [S3](docs/guides/s3-image-upload.md) or [Minio](docs/guides/minio-image-upload.md)| @@ -305,6 +312,8 @@ There are some config settings you need to change in the files below. | `s3` | `{ "accessKeyId": "YOUR_S3_ACCESS_KEY_ID", "secretAccessKey": "YOUR_S3_ACCESS_KEY", "region": "YOUR_S3_REGION" }` | When `imageuploadtype` be set to `s3`, you would also need to setup this key, check our [S3 Image Upload Guide](docs/guides/s3-image-upload.md) | | `s3bucket` | `YOUR_S3_BUCKET_NAME` | bucket name when `imageUploadType` is set to `s3` or `minio` | +<sup>1</sup>: relative paths are based on CodiMD's base directory + ## Third-party integration API key settings | service | settings location | description | @@ -126,6 +126,9 @@ app.use(i18n.init) // routes without sessions // static files app.use('/', express.static(path.join(__dirname, '/public'), { maxAge: config.staticCacheTime })) +app.use('/docs', express.static(path.resolve(__dirname, config.docsPath), { maxAge: config.staticCacheTime })) +app.use('/uploads', express.static(path.resolve(__dirname, config.uploadsPath), { maxAge: config.staticCacheTime })) +app.use('/default.md', express.static(path.resolve(__dirname, config.defaultNotePath), { maxAge: config.staticCacheTime })) // session app.use(session({ @@ -167,7 +170,7 @@ app.use(require('./lib/web/middleware/codiMDVersion')) // routes need sessions // template files -app.set('views', path.join(__dirname, '/public/views')) +app.set('views', config.viewPath) // set render engine app.engine('ejs', ejs.renderFile) // set view engine diff --git a/docs/dev/webpack.md b/docs/dev/webpack.md new file mode 100644 index 00000000..7b391f3a --- /dev/null +++ b/docs/dev/webpack.md @@ -0,0 +1,26 @@ +# Webpack Docs +## `webpack.common.js` +This file contains all common definition for chunks and plugins, that are needed by the whole app. + +**TODO:** Document which entry points are used for what. + +## `webpack.htmlexport.js` +Separate config for the "save as html" feature. +Packs all CSS from `public/js/htmlExport.js` to `build/html.min.css`. +This file is then downloaded by client-side JS and used to create the HTML. +See `exportToHTML()` in `public/js/extra.js`. + + +## `webpack.dev.js` +The development config uses both common configs, enables development mode and enables "cheap" source maps (lines only). +If you need more detailed source maps while developing, you might want to use the `source-maps` option. +See https://webpack.js.org/configuration/devtool/ for details. + +## `webpack.prod.js` +The production config uses both common configs and enables production mode. +This automatically enables various optimizations (e.g. UglifyJS). See https://webpack.js.org/concepts/mode/ for details. + +For the global app config, the name of the emitted chunks is changed to include the content hash. +See https://webpack.js.org/guides/caching/ on why this is a good idea. + +For the HTML export config, CSS minification is enabled. diff --git a/docs/guides/auth/nextcloud.md b/docs/guides/auth/nextcloud.md new file mode 100644 index 00000000..108772dd --- /dev/null +++ b/docs/guides/auth/nextcloud.md @@ -0,0 +1,52 @@ +Authentication guide - Nextcloud (self-hosted) +=== + +*This has been constructed using the [Nextcloud OAuth2 Documentation](https://docs.nextcloud.com/server/14/admin_manual/configuration_server/oauth2.html?highlight=oauth2) combined with [this issue comment on the nextcloud bugtracker](https://github.com/nextcloud/server/issues/5694#issuecomment-314761326).* + +This guide uses the generic OAuth2 module for compatibility with Nextcloud 13 and above (this guide has been tested successfully with Nextcloud 14). + +1. Sign-in with an administrator account to your Nextcloud server + +2. Navigate to the OAuth integration settings: Profile Icon (top right) --> Settings + Then choose Security Settings from the *Administration* part of the list - Don't confuse this with Personal Security Settings, where you would change your personal password! + At the top there's OAuth 2.0-Clients. + ![Where to find OAuth2 in Nextcloud](../images/auth/nextcloud-oauth2-1-settings.png) + +3. Add your CodiMD instance by giving it a *name* (perhaps CodiMD, but could be anything) and a *Redirection-URI*. The Redirection-URI will be `\<your-codimd-url\>/auth/oauth2/callback`. Click <kbd>Add</kbd>. + ![Adding a client to Nextcloud](../images/auth/nextcloud-oauth2-2-client-add.png) + + +4. You'll now see a line containing a *client identifier* and a *Secret*. + ![Successfully added OAuth2-client](../images/auth/nextcloud-oauth2-3-clientid-secret.png) + +5. That's it for Nextcloud, the rest is configured in your CodiMD `config.json` or via the `CMD_` environment variables! + +6. Add the Client ID and Client Secret to your `config.json` file or pass them as environment variables. Make sure you also replace `<your-nextcloud-domain>` with the right domain name. + * `config.json`: + ```javascript + { + "production": { + "oauth2": { + "clientID": "ii4p1u3jz7dXXXXXXXXXXXXXXX", + "clientSecret": "mqzzx6fydbXXXXXXXXXXXXXXXX", + "authorizationURL": "https://<your-nextcloud-domain>/apps/oauth2/authorize", + "tokenURL": "https://<your-nextcloud-domain>/apps/oauth2/api/v1/token", + "userProfileURL": "https://<your-nextcloud-domain>/ocs/v2.php/cloud/user?format=json", + "userProfileUsernameAttr": "ocs.data.id", + "userProfileDisplayNameAttr": "ocs.data.display-name", + "userProfileEmailAttr": "ocs.data.email" + } + } + } + ``` + * environment variables: + ```sh + CMD_OAUTH2_CLIENT_ID=ii4p1u3jz7dXXXXXXXXXXXXXXX + CMD_OAUTH2_CLIENT_SECRET=mqzzx6fydbXXXXXXXXXXXXXXXX + CMD_OAUTH2_AUTHORIZATION_URL=https://<your-nextcloud-domain>/apps/oauth2/authorize + CMD_OAUTH2_TOKEN_URL=https://<your-nextcloud-domain>/apps/oauth2/api/v1/token + CMD_OAUTH2_USER_PROFILE_URL=https://<your-nextcloud-domain>/ocs/v2.php/cloud/user?format=json + CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR=ocs.data.id + CMD_OAUTH2_USER_PROFILE_DISPLAY_NAME_ATTR=ocs.data.display-name + CMD_OAUTH2_USER_PROFILE_EMAIL_ATTR=ocs.data.email + ``` diff --git a/docs/guides/images/auth/nextcloud-oauth2-1-settings.png b/docs/guides/images/auth/nextcloud-oauth2-1-settings.png Binary files differnew file mode 100644 index 00000000..82652a54 --- /dev/null +++ b/docs/guides/images/auth/nextcloud-oauth2-1-settings.png diff --git a/docs/guides/images/auth/nextcloud-oauth2-2-client-add.png b/docs/guides/images/auth/nextcloud-oauth2-2-client-add.png Binary files differnew file mode 100644 index 00000000..7909fa62 --- /dev/null +++ b/docs/guides/images/auth/nextcloud-oauth2-2-client-add.png diff --git a/docs/guides/images/auth/nextcloud-oauth2-3-clientid-secret.png b/docs/guides/images/auth/nextcloud-oauth2-3-clientid-secret.png Binary files differnew file mode 100644 index 00000000..b05513d1 --- /dev/null +++ b/docs/guides/images/auth/nextcloud-oauth2-3-clientid-secret.png diff --git a/lib/config/default.js b/lib/config/default.js index c34279bd..c3ada982 100644 --- a/lib/config/default.js +++ b/lib/config/default.js @@ -38,15 +38,10 @@ module.exports = { sslCAPath: '', dhParamPath: '', // other path + viewPath: './public/views', tmpPath: './tmp', 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 +78,7 @@ module.exports = { }, // authentication oauth2: { + providerName: undefined, authorizationURL: undefined, tokenURL: undefined, clientID: undefined, @@ -150,5 +146,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..6737637c 100644 --- a/lib/config/environment.js +++ b/lib/config/environment.js @@ -75,6 +75,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 +124,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 26f0ae96..f8b68e30 100644 --- a/lib/config/index.js +++ b/lib/config/index.js @@ -9,7 +9,7 @@ const deepFreeze = require('deep-freeze') const {Environment, Permission} = require('./enum') const logger = require('../logger') -const appRootPath = path.join(__dirname, '../../') +const appRootPath = path.resolve(__dirname, '../../') const env = process.env.NODE_ENV || Environment.development const debugConfig = { debug: (env === Environment.development) @@ -23,7 +23,8 @@ const packageConfig = { minimumCompatibleVersion: '0.5.0' } -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') @@ -95,6 +96,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 @@ -173,20 +175,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/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/response.js b/lib/response.js index 4df036b7..4f572e47 100644 --- a/lib/response.js +++ b/lib/response.js @@ -54,7 +54,7 @@ var response = { } function responseError (res, code, detail, msg) { - res.status(code).render(config.errorPath, { + res.status(code).render('error.ejs', { url: config.serverURL, title: code + ' ' + detail + ' ' + msg, code: code, @@ -88,6 +88,7 @@ function showIndex (req, res, next) { email: config.isEmailEnable, allowEmailRegister: config.allowEmailRegister, allowPDFExport: config.allowPDFExport, + openID: config.isOpenIDEnable, signin: authStatus, infoMessage: req.flash('info'), errorMessage: req.flash('error'), @@ -104,11 +105,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) } } @@ -122,7 +123,7 @@ function responseCodiMD (res, note) { 'Cache-Control': 'private', // only cache by client 'X-Robots-Tag': 'noindex, nofollow' // prevent crawling }) - res.render(config.codimdPath, { + res.render('codimd.ejs', { url: config.serverURL, title: title, useCDN: config.useCDN, @@ -142,7 +143,8 @@ function responseCodiMD (res, note) { oauth2: config.isOAuth2Enable, email: config.isEmailEnable, allowEmailRegister: config.allowEmailRegister, - allowPDFExport: config.allowPDFExport + allowPDFExport: config.allowPDFExport, + openID: config.isOpenIDEnable }) } @@ -283,7 +285,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) { @@ -589,7 +591,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) @@ -665,7 +667,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/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..8c432b0c 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.resolve(config.serverURL + '/uploads/', path.basename(imagePath))) } diff --git a/lib/web/statusRouter.js b/lib/web/statusRouter.js index 7ecf3839..fb2609ea 100644 --- a/lib/web/statusRouter.js +++ b/lib/web/statusRouter.js @@ -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) }) diff --git a/package.json b/package.json index 6a48e903..a887ada1 100644 --- a/package.json +++ b/package.json @@ -8,13 +8,14 @@ "test": "npm run-script standard && npm run-script jsonlint", "jsonlint": "find . -not -path './node_modules/*' -type f -name '*.json' -o -type f -name '*.json.example' | while read json; do echo $json ; jq . $json; done", "standard": "node ./node_modules/standard/bin/cmd.js", - "dev": "webpack --config webpack.config.js --progress --colors --watch", - "build": "webpack --config webpack.production.js --progress --colors --bail", + "dev": "webpack --config webpack.dev.js --progress --colors --watch", + "build": "webpack --config webpack.prod.js --progress --colors --bail", "postinstall": "bin/heroku", "start": "node app.js", "doctoc": "doctoc --title='# Table of Contents' README.md" }, "dependencies": { + "@passport-next/passport-openid": "^1.0.0", "Idle.Js": "git+https://github.com/shawnmclean/Idle.js", "archiver": "^2.1.1", "async": "^2.1.4", @@ -41,7 +42,7 @@ "express-session": "^1.14.2", "file-saver": "^1.3.3", "flowchart.js": "^1.6.4", - "font-awesome": "^4.7.0", + "fork-awesome": "^1.1.3", "formidable": "^1.0.17", "gist-embed": "~2.6.0", "graceful-fs": "^4.1.11", @@ -88,7 +89,6 @@ "morgan": "^1.7.0", "mysql": "^2.12.0", "node-uuid": "^1.4.7", - "octicons": "~4.4.0", "passport": "^0.4.0", "passport-dropbox-oauth2": "^1.1.0", "passport-facebook": "^2.1.1", @@ -142,11 +142,15 @@ "Markdown", "Notes" ], - "homepage": "https://hackmd.io", + "homepage": "https://codimd.org", "maintainers": [ { "name": "Max Wu", "email": "jackymaxj@gmail.com" + }, + { + "name": "Christoph (Sheogorath) Kern", + "email": "codimd@sheogorath.shivering-isles.com" } ], "repository": { @@ -183,6 +187,7 @@ "url-loader": "^1.0.1", "webpack": "^4.14.0", "webpack-cli": "^3.1.0", + "webpack-merge": "^4.1.4", "webpack-parallel-uglify-plugin": "^1.1.0" }, "standard": { diff --git a/public/css/github-extract.css b/public/css/github-extract.css index 7f7058a0..f7b15445 100644 --- a/public/css/github-extract.css +++ b/public/css/github-extract.css @@ -119,23 +119,24 @@ color: #ddd; } -.markdown-body h1 .octicon-link, -.markdown-body h2 .octicon-link, -.markdown-body h3 .octicon-link, -.markdown-body h4 .octicon-link, -.markdown-body h5 .octicon-link, -.markdown-body h6 .octicon-link { +.markdown-body h1 .fa-link, +.markdown-body h2 .fa-link, +.markdown-body h3 .fa-link, +.markdown-body h4 .fa-link, +.markdown-body h5 .fa-link, +.markdown-body h6 .fa-link { color: #000; vertical-align: middle; visibility: hidden; + font-size: 16px; } -.night .markdown-body h1 .octicon-link, -.night .markdown-body h2 .octicon-link, -.night .markdown-body h3 .octicon-link, -.night .markdown-body h4 .octicon-link, -.night .markdown-body h5 .octicon-link, -.night .markdown-body h6 .octicon-link { +.night .markdown-body h1 .fa-link, +.night .markdown-body h2 .fa-link, +.night .markdown-body h3 .fa-link, +.night .markdown-body h4 .fa-link, +.night .markdown-body h5 .fa-link, +.night .markdown-body h6 .fa-link { color: #fff; } @@ -148,12 +149,12 @@ text-decoration: none; } -.markdown-body h1:hover .anchor .octicon-link, -.markdown-body h2:hover .anchor .octicon-link, -.markdown-body h3:hover .anchor .octicon-link, -.markdown-body h4:hover .anchor .octicon-link, -.markdown-body h5:hover .anchor .octicon-link, -.markdown-body h6:hover .anchor .octicon-link { +.markdown-body h1:hover .anchor .fa-link, +.markdown-body h2:hover .anchor .fa-link, +.markdown-body h3:hover .anchor .fa-link, +.markdown-body h4:hover .anchor .fa-link, +.markdown-body h5:hover .anchor .fa-link, +.markdown-body h6:hover .anchor .fa-link { visibility: visible; } diff --git a/public/js/extra.js b/public/js/extra.js index d6bbb0c6..dd264047 100644 --- a/public/js/extra.js +++ b/public/js/extra.js @@ -570,7 +570,9 @@ export function postProcess (code) { $(value).html(html) }) // link should open in new window or tab - result.find('a:not([href^="#"]):not([target])').attr('target', '_blank') + // also add noopener to prevent clickjacking + // See details: https://mathiasbynens.github.io/rel-noopener/ + result.find('a:not([href^="#"]):not([target])').attr('target', '_blank').attr('rel', 'noopener') // update continue line numbers const linenumberdivs = result.find('.gutter.linenumber').toArray() for (let i = 0; i < linenumberdivs.length; i++) { @@ -832,7 +834,7 @@ const anchorForId = id => { const anchor = document.createElement('a') anchor.className = 'anchor hidden-xs' anchor.href = `#${id}` - anchor.innerHTML = '<span class="octicon octicon-link"></span>' + anchor.innerHTML = '<i class="fa fa-link"></i>' anchor.title = id return anchor } diff --git a/public/js/index.js b/public/js/index.js index 1330deac..3dd25fa6 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -1479,7 +1479,7 @@ $('#snippetExportModalConfirm').click(function () { file_name: $('#snippetExportModalFileName').val(), code: editor.getValue(), visibility_level: $('#snippetExportModalVisibility').val(), - visibility: $('#snippetExportModalVisibility').val() === 0 ? 'private' : ($('#snippetExportModalVisibility').val() === 10 ? 'internal' : '') + visibility: $('#snippetExportModalVisibility').val() === '0' ? 'private' : ($('#snippetExportModalVisibility').val() === '10' ? 'internal' : 'private') } if (!data.title || !data.file_name || !data.code || !data.visibility_level || !$('#snippetExportModalProjects').val()) return diff --git a/public/views/codimd/body.ejs b/public/views/codimd/body.ejs index d4f27a93..dc111909 100644 --- a/public/views/codimd/body.ejs +++ b/public/views/codimd/body.ejs @@ -113,7 +113,7 @@ </div> <div class="modal-body" style="color:black;"> <h5></h5> - <a target="_blank" style="word-break: break-all;"></a> + <a target="_blank" rel="noopener" style="word-break: break-all;"></a> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal"><%= __('OK') %></button> diff --git a/public/views/codimd/head.ejs b/public/views/codimd/head.ejs index 1f446253..356ded6d 100644 --- a/public/views/codimd/head.ejs +++ b/public/views/codimd/head.ejs @@ -9,10 +9,9 @@ <link rel="apple-touch-icon" href="<%- url %>/apple-touch-icon.png"> <% if(useCDN) { %> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" /> -<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" /> +<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fork-awesome/1.1.3/css/fork-awesome.min.css" integrity="sha256-ZhApazu+kejqTYhMF+1DzNKjIzP7KXu6AzyXcC1gMus=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-social/4.9.0/bootstrap-social.min.css" integrity="sha256-02JtFTurpwBjQJ6q13iJe82/NF0RbZlJroDegK5g87Y=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css" integrity="sha256-3iu9jgsy9TpTwXKb7bNQzqWekRX7pPK+2OLj3R922fo=" crossorigin="anonymous" /> -<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/octicons.min.css" integrity="sha256-4eCms8tUm+iL29tUvmRhNRnz/7lNI4oTtQzqa/uM2dI=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/emojify.js/1.1.0/css/basic/emojify.min.css" integrity="sha256-UOrvMOsSDSrW6szVLe8ZDZezBxh5IoIfgTwdNDgTjiU=" crossorigin="anonymous" /> <%- include ../build/index-header %> <%- include ../shared/polyfill %> diff --git a/public/views/codimd/header.ejs b/public/views/codimd/header.ejs index 8fc050b7..1b5e4222 100644 --- a/public/views/codimd/header.ejs +++ b/public/views/codimd/header.ejs @@ -22,15 +22,15 @@ <i class="fa fa-caret-down"></i> </a> <ul class="dropdown-menu list" role="menu" aria-labelledby="menu"> - <li role="presentation"><a role="menuitem" class="ui-new" tabindex="-1" href="<%- url %>/new" target="_blank"><i class="fa fa-plus fa-fw"></i> <%= __('New') %></a> + <li role="presentation"><a role="menuitem" class="ui-new" tabindex="-1" href="<%- url %>/new" target="_blank" rel="noopener"><i class="fa fa-plus fa-fw"></i> <%= __('New') %></a> </li> - <li role="presentation"><a role="menuitem" class="ui-publish" tabindex="-1" href="#" target="_blank"><i class="fa fa-share-square-o fa-fw"></i> <%= __('Publish') %></a> + <li role="presentation"><a role="menuitem" class="ui-publish" tabindex="-1" href="#" target="_blank" rel="noopener"><i class="fa fa-share-square-o fa-fw"></i> <%= __('Publish') %></a> </li> <li class="divider"></li> <li class="dropdown-header"><%= __('Extra') %></li> <li role="presentation"><a role="menuitem" class="ui-extra-revision" tabindex="-1" data-toggle="modal" data-target="#revisionModal"><i class="fa fa-history fa-fw"></i> <%= __('Revision') %></a> </li> - <li role="presentation"><a role="menuitem" class="ui-extra-slide" tabindex="-1" href="#" target="_blank"><i class="fa fa-tv fa-fw"></i> <%= __('Slide Mode') %></a> + <li role="presentation"><a role="menuitem" class="ui-extra-slide" tabindex="-1" href="#" target="_blank" rel="noopener"><i class="fa fa-tv fa-fw"></i> <%= __('Slide Mode') %></a> </li> <% if((typeof github !== 'undefined' && github) || (typeof dropbox !== 'undefined' && dropbox) || (typeof gitlab !== 'undefined' && gitlab && (!gitlab.scope || gitlab.scope === 'api'))) { %> <li class="divider"></li> @@ -38,7 +38,7 @@ <li role="presentation"><a role="menuitem" class="ui-save-dropbox" tabindex="-1" href="#" target="_self"><i class="fa fa-dropbox fa-fw"></i> Dropbox</a> </li> <% if(typeof github !== 'undefined' && github) { %> - <li role="presentation"><a role="menuitem" class="ui-save-gist" tabindex="-1" href="#" target="_blank"><i class="fa fa-github fa-fw"></i> Gist</a> + <li role="presentation"><a role="menuitem" class="ui-save-gist" tabindex="-1" href="#" target="_blank" rel="noopener"><i class="fa fa-github fa-fw"></i> Gist</a> </li> <% } %> <% if(typeof gitlab !== 'undefined' && gitlab && (!gitlab.scope || gitlab.scope === 'api')) { %> @@ -115,12 +115,12 @@ </ul> <ul class="nav navbar-nav navbar-right" style="padding:0;"> <li> - <a href="<%- url %>/new" target="_blank" class="ui-new"> + <a href="<%- url %>/new" target="_blank" rel="noopener" class="ui-new"> <i class="fa fa-plus"></i> <%= __('New') %> </a> </li> <li> - <a href="#" target="_blank" class="ui-publish"> + <a href="#" target="_blank" rel="noopener" class="ui-publish"> <i class="fa fa-share-square-o"></i> <%= __('Publish') %> </a> </li> @@ -132,7 +132,7 @@ <li class="dropdown-header"><%= __('Extra') %></li> <li role="presentation"><a role="menuitem" class="ui-extra-revision" tabindex="-1" data-toggle="modal" data-target="#revisionModal"><i class="fa fa-history fa-fw"></i> <%= __('Revision') %></a> </li> - <li role="presentation"><a role="menuitem" class="ui-extra-slide" tabindex="-1" href="#" target="_blank"><i class="fa fa-tv fa-fw"></i> <%= __('Slide Mode') %></a> + <li role="presentation"><a role="menuitem" class="ui-extra-slide" tabindex="-1" href="#" target="_blank" rel="noopener"><i class="fa fa-tv fa-fw"></i> <%= __('Slide Mode') %></a> </li> <% if((typeof github !== 'undefined' && github) || (typeof dropbox !== 'undefined' && dropbox) || (typeof gitlab !== 'undefined' && gitlab && (!gitlab.scope || gitlab.scope === 'api'))) { %> <li class="divider"></li> @@ -140,7 +140,7 @@ <li role="presentation"><a role="menuitem" class="ui-save-dropbox" tabindex="-1" href="#" target="_self"><i class="fa fa-dropbox fa-fw"></i> Dropbox</a> </li> <% if(typeof github !== 'undefined' && github) { %> - <li role="presentation"><a role="menuitem" class="ui-save-gist" tabindex="-1" href="#" target="_blank"><i class="fa fa-github fa-fw"></i> Gist</a> + <li role="presentation"><a role="menuitem" class="ui-save-gist" tabindex="-1" href="#" target="_blank" rel="noopener"><i class="fa fa-github fa-fw"></i> Gist</a> </li> <% } %> <% if(typeof gitlab !== 'undefined' && gitlab && (!gitlab.scope || gitlab.scope === 'api')) { %> diff --git a/public/views/html.hbs b/public/views/html.hbs index a87bf81a..490d31a4 100644 --- a/public/views/html.hbs +++ b/public/views/html.hbs @@ -16,9 +16,8 @@ <link rel="apple-touch-icon" href="{{{url}}}/apple-touch-icon.png"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" /> - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" /> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fork-awesome/1.1.3/css/fork-awesome.min.css" integrity="sha256-ZhApazu+kejqTYhMF+1DzNKjIzP7KXu6AzyXcC1gMus=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css" integrity="sha256-3iu9jgsy9TpTwXKb7bNQzqWekRX7pPK+2OLj3R922fo=" crossorigin="anonymous" /> - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/octicons.min.css" integrity="sha256-4eCms8tUm+iL29tUvmRhNRnz/7lNI4oTtQzqa/uM2dI=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/themes/prism.min.css" integrity="sha256-vtR0hSWRc3Tb26iuN2oZHt3KRUomwTufNIf5/4oeCyg=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github-gist.min.css" integrity="sha256-tAflq+ymku3Khs+I/WcAneIlafYgDiOQ9stIHH985Wo=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/emojify.js/1.1.0/css/basic/emojify.min.css" integrity="sha256-UOrvMOsSDSrW6szVLe8ZDZezBxh5IoIfgTwdNDgTjiU=" crossorigin="anonymous" /> diff --git a/public/views/index/body.ejs b/public/views/index/body.ejs index 0f2813b9..18bffddb 100644 --- a/public/views/index/body.ejs +++ b/public/views/index/body.ejs @@ -150,10 +150,10 @@ <option value="id">Bahasa Indonesia</option> </select> <p> - Powered by <a href="https://codimd.org">CodiMD</a> | <a href="<%- url %>/s/release-notes" target="_blank"><%= __('Releases') %></a><% if(privacyStatement) { %> | <a href="<%- url %>/s/privacy" target="_blank"><%= __('Privacy') %></a><% } %><% if(termsOfUse) { %> | <a href="<%- url %>/s/terms-of-use" target="_blank"><%= __('Terms of Use') %></a><% } %> + Powered by <a href="https://codimd.org">CodiMD</a> | <a href="<%- url %>/s/release-notes" target="_blank" rel="noopener"><%= __('Releases') %></a><% if(privacyStatement) { %> | <a href="<%- url %>/s/privacy" target="_blank" rel="noopener"><%= __('Privacy') %></a><% } %><% if(termsOfUse) { %> | <a href="<%- url %>/s/terms-of-use" target="_blank" rel="noopener"><%= __('Terms of Use') %></a><% } %> </p> <h6 class="social-foot"> - <%- __('Follow us on %s and %s.', '<a href="https://github.com/hackmdio/CodiMD" target="_blank"><i class="fa fa-github"></i> GitHub</a>, <a href="https://riot.im/app/#/room/#codimd:matrix.org" target="_blank"><i class="fa fa-comments"></i> Riot</a>', '<a href="https://translate.codimd.org" target="_blank"><i class="fa fa-globe"></i> POEditor</a>') %> + <%- __('Follow us on %s and %s.', '<a href="https://github.com/hackmdio/CodiMD" target="_blank" rel="noopener"><i class="fa fa-github"></i> GitHub</a>, <a href="https://riot.im/app/#/room/#codimd:matrix.org" target="_blank" rel="noopener"><i class="fa fa-comments"></i> Riot</a>', '<a href="https://translate.codimd.org" target="_blank" rel="noopener"><i class="fa fa-globe"></i> POEditor</a>') %> </h6> </div> </div> diff --git a/public/views/index/head.ejs b/public/views/index/head.ejs index f826d9ee..fc10dfa0 100644 --- a/public/views/index/head.ejs +++ b/public/views/index/head.ejs @@ -11,7 +11,7 @@ <link rel="apple-touch-icon" href="<%- url %>/apple-touch-icon.png"> <% if(useCDN) { %> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" /> -<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" /> +<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fork-awesome/1.1.3/css/fork-awesome.min.css" integrity="sha256-ZhApazu+kejqTYhMF+1DzNKjIzP7KXu6AzyXcC1gMus=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-social/4.9.0/bootstrap-social.min.css" integrity="sha256-02JtFTurpwBjQJ6q13iJe82/NF0RbZlJroDegK5g87Y=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/3.5.4/select2.min.css" integrity="sha256-ijlUKKj3hJCiiT2HWo1kqkI79NTEYpzOsw5Rs3k42dI=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/3.5.4/select2-bootstrap.min.css" integrity="sha256-NAWFcNIZdH+TS1xpWujF/EB/Y8gwBbEOCoaK/eqaer8=" crossorigin="anonymous" /> diff --git a/public/views/pretty.ejs b/public/views/pretty.ejs index 4c2fc0ab..a087be7f 100644 --- a/public/views/pretty.ejs +++ b/public/views/pretty.ejs @@ -19,9 +19,8 @@ <link rel="apple-touch-icon" href="<%- url %>/apple-touch-icon.png"> <% if(useCDN) { %> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" /> - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" /> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fork-awesome/1.1.3/css/fork-awesome.min.css" integrity="sha256-ZhApazu+kejqTYhMF+1DzNKjIzP7KXu6AzyXcC1gMus=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css" integrity="sha256-3iu9jgsy9TpTwXKb7bNQzqWekRX7pPK+2OLj3R922fo=" crossorigin="anonymous" /> - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/octicons.min.css" integrity="sha256-4eCms8tUm+iL29tUvmRhNRnz/7lNI4oTtQzqa/uM2dI=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/emojify.js/1.1.0/css/basic/emojify.min.css" integrity="sha256-UOrvMOsSDSrW6szVLe8ZDZezBxh5IoIfgTwdNDgTjiU=" crossorigin="anonymous" /> <%- include build/pretty-header %> <%- include shared/polyfill %> diff --git a/public/views/shared/refresh-modal.ejs b/public/views/shared/refresh-modal.ejs index 5be41b2a..64580545 100644 --- a/public/views/shared/refresh-modal.ejs +++ b/public/views/shared/refresh-modal.ejs @@ -14,7 +14,7 @@ </div> <div class="new-version" style="display:none;"> <h5><%= __('New version available!') %></h5> - <a href="<%- url %>/s/release-notes" target="_blank"><%= __('See releases notes here') %></a> + <a href="<%- url %>/s/release-notes" target="_blank" rel="noopener"><%= __('See releases notes here') %></a> <br> <strong><%= __('Refresh to enjoy new features.') %></strong> </div> diff --git a/public/views/shared/signin-modal.ejs b/public/views/shared/signin-modal.ejs index f0cffad9..b413511c 100644 --- a/public/views/shared/signin-modal.ejs +++ b/public/views/shared/signin-modal.ejs @@ -78,7 +78,26 @@ </div> </form> <% } %> - <% if((facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || oauth2) && email) { %> + <% if((facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || oauth2) && openID) { %> + <hr> + <% }%> + <% if(openID) { %> + <h4>OpenID</h4> + <form data-toggle="validator" role="form" class="form-horizontal" method="post" enctype="application/x-www-form-urlencoded"> + <div class="form-group"> + <div class="col-sm-12"> + <input type="text" class="form-control" name="openid_identifier" placeholder="OpenID" required> + <span class="help-block control-label with-errors" style="display: inline;"></span> + </div> + </div> + <div class="form-group"> + <div class="col-sm-12"> + <button type="submit" class="btn btn-primary" formaction="<%- url %>/auth/openid">Sign in</button> + </div> + </div> + </form> + <% } %> + <% if((facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || oauth2 || openID) && email) { %> <hr> <% }%> <% if(email) { %> diff --git a/public/views/slide.ejs b/public/views/slide.ejs index c11d2984..42fb5199 100644 --- a/public/views/slide.ejs +++ b/public/views/slide.ejs @@ -16,9 +16,8 @@ <link rel="apple-touch-icon" href="<%- url %>/apple-touch-icon.png"> <% if(useCDN) { %> - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" /> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fork-awesome/1.1.3/css/fork-awesome.min.css" integrity="sha256-ZhApazu+kejqTYhMF+1DzNKjIzP7KXu6AzyXcC1gMus=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css" integrity="sha256-3iu9jgsy9TpTwXKb7bNQzqWekRX7pPK+2OLj3R922fo=" crossorigin="anonymous" /> - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/octicons.min.css" integrity="sha256-4eCms8tUm+iL29tUvmRhNRnz/7lNI4oTtQzqa/uM2dI=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.6.0/css/reveal.min.css" integrity="sha256-ol2N5Xr80jdDqxK0lte3orKGb9Ja3sOnpAUV7TTADmg=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/emojify.js/1.1.0/css/basic/emojify.min.css" integrity="sha256-UOrvMOsSDSrW6szVLe8ZDZezBxh5IoIfgTwdNDgTjiU=" crossorigin="anonymous" /> <%- include build/slide-header %> diff --git a/webpackBaseConfig.js b/webpack.common.js index 19d021d9..efd2b3e2 100644 --- a/webpackBaseConfig.js +++ b/webpack.common.js @@ -10,6 +10,7 @@ var gracefulFs = require('graceful-fs') gracefulFs.gracefulify(fs) module.exports = { + name: 'app', plugins: [ new webpack.ProvidePlugin({ Visibility: 'visibilityjs', @@ -184,7 +185,7 @@ module.exports = { ], 'cover-styles-pack': [ path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'), - path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'), + path.join(__dirname, 'node_modules/fork-awesome/css/fork-awesome.min.css'), path.join(__dirname, 'public/css/bootstrap-social.css'), path.join(__dirname, 'node_modules/select2/select2.css'), path.join(__dirname, 'node_modules/select2/select2-bootstrap.css') @@ -236,10 +237,9 @@ module.exports = { ], 'index-styles-pack': [ path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'), - path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'), + path.join(__dirname, 'node_modules/fork-awesome/css/fork-awesome.min.css'), path.join(__dirname, 'public/css/bootstrap-social.css'), - path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'), - path.join(__dirname, 'node_modules/octicons/build/octicons.css') + path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css') ], 'index-pack': [ 'babel-polyfill', @@ -287,9 +287,8 @@ module.exports = { ], 'pretty-styles-pack': [ path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'), - path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'), - path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'), - path.join(__dirname, 'node_modules/octicons/build/octicons.css') + path.join(__dirname, 'node_modules/fork-awesome/css/fork-awesome.min.css'), + path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css') ], 'pretty-pack': [ 'babel-polyfill', @@ -324,9 +323,8 @@ module.exports = { path.join(__dirname, 'public/css/markdown.css') ], 'slide-styles-pack': [ - path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'), - path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'), - path.join(__dirname, 'node_modules/octicons/build/octicons.css') + path.join(__dirname, 'node_modules/fork-awesome/css/fork-awesome.min.css'), + path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css') ], 'slide-pack': [ 'babel-polyfill', diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 3c7c727e..00000000 --- a/webpack.config.js +++ /dev/null @@ -1,41 +0,0 @@ -var baseConfig = require('./webpackBaseConfig') -const MiniCssExtractPlugin = require('mini-css-extract-plugin') -var path = require('path') - -module.exports = [Object.assign({}, baseConfig, { - plugins: baseConfig.plugins.concat([ - new MiniCssExtractPlugin({ - filename: '[name].css', - chunkFilename: '[id].css' - }) - - ]), - devtool: 'source-map' -}), { - devtool: 'source-map', - entry: { - htmlExport: path.join(__dirname, 'public/js/htmlExport.js') - }, - module: { - rules: [{ - test: /\.css$/, - use: ['style-loader', 'css-loader'] - }, { - test: /\.scss$/, - use: ['style-loader', 'sass-loader'] - }, { - test: /\.less$/, - use: ['style-loader', 'less-loader'] - }] - }, - output: { - path: path.join(__dirname, 'public/build'), - publicPath: '/build/', - filename: '[name].js' - }, - plugins: [ - new MiniCssExtractPlugin({ - filename: 'html.min.css' - }) - ] -}] diff --git a/webpack.dev.js b/webpack.dev.js new file mode 100644 index 00000000..b1ed3612 --- /dev/null +++ b/webpack.dev.js @@ -0,0 +1,14 @@ +const common = require('./webpack.common.js') +const htmlexport = require('./webpack.htmlexport') +const merge = require('webpack-merge') + +module.exports = [ + // merge common config + merge(common, { + mode: 'development', + devtool: 'cheap-module-eval-source-map' + }), + merge(htmlexport, { + mode: 'development', + devtool: 'cheap-module-eval-source-map' + })] diff --git a/webpack.htmlexport.js b/webpack.htmlexport.js new file mode 100644 index 00000000..dd6f4c01 --- /dev/null +++ b/webpack.htmlexport.js @@ -0,0 +1,25 @@ +const MiniCssExtractPlugin = require('mini-css-extract-plugin') +const path = require('path') + +module.exports = { + name: 'save-as-html', + entry: { + htmlExport: path.join(__dirname, 'public/js/htmlExport.js') + }, + module: { + rules: [{ + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, 'css-loader'] + }] + }, + output: { + path: path.join(__dirname, 'public/build'), + publicPath: '/build/', + filename: '[name].js' + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: 'html.min.css' + }) + ] +} diff --git a/webpack.prod.js b/webpack.prod.js new file mode 100644 index 00000000..188d9881 --- /dev/null +++ b/webpack.prod.js @@ -0,0 +1,23 @@ +const common = require('./webpack.common.js') +const htmlexport = require('./webpack.htmlexport') +const merge = require('webpack-merge') +const path = require('path') +const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin') + +module.exports = [ + merge(common, { + mode: 'production', + output: { + path: path.join(__dirname, 'public/build'), + publicPath: '/build/', + filename: '[name].[contenthash].js' + } + }), + merge(htmlexport, { + mode: 'production', + optimization: { + minimizer: [ + new OptimizeCSSAssetsPlugin({}) + ] + } + })] diff --git a/webpack.production.js b/webpack.production.js deleted file mode 100644 index 67387583..00000000 --- a/webpack.production.js +++ /dev/null @@ -1,75 +0,0 @@ -var baseConfig = require('./webpackBaseConfig') -var webpack = require('webpack') -var path = require('path') -const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin') -const UglifyJsPlugin = require('uglifyjs-webpack-plugin') -const MiniCssExtractPlugin = require('mini-css-extract-plugin') - -module.exports = [Object.assign({}, baseConfig, { - plugins: baseConfig.plugins.concat([ - new webpack.DefinePlugin({ - 'process.env': { - 'NODE_ENV': JSON.stringify('production') - } - }) - ]), - - optimization: { - minimizer: [ - new UglifyJsPlugin({ - parallel: true, - cache: true - }) - ], - splitChunks: { - chunks: 'async', - minChunks: Infinity - } - }, - - output: { - path: path.join(__dirname, 'public/build'), - publicPath: '/build/', - filename: '[id].[name].[hash].js' - // baseUrl: '<%- url %>' - } -}), { - // This Chunk is used in the 'save as html' feature. - // It is embedded in the html file and contains CSS for styling. - - entry: { - htmlExport: path.join(__dirname, 'public/js/htmlExport.js') - }, - - output: { - path: path.join(__dirname, 'public/build'), - publicPath: '/build/', - filename: '[name].js' - }, - plugins: [ - new webpack.DefinePlugin({ - 'process.env': { - 'NODE_ENV': JSON.stringify('production') - } - }), - new MiniCssExtractPlugin({ - filename: 'html.min.css' - }) - ], - - optimization: { - minimizer: [ - new OptimizeCSSAssetsPlugin({}) - ] - }, - - module: { - rules: [{ - test: /\.css$/, - use: [ - MiniCssExtractPlugin.loader, - 'css-loader' - ] - }] - } -}] @@ -2,6 +2,19 @@ # yarn lockfile v1 +"@passport-next/passport-openid@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@passport-next/passport-openid/-/passport-openid-1.0.0.tgz#d3b5e067a9aa1388ed172ab7cc02c39b8634283d" + integrity sha512-W9uj4Ui/ZK/iBUNzSNxPWDQ8wCD1tUddGEVSGm0FN0B7ewo3yBQLGMoW3i3UqcwEzxdyGbAj06ohAhNQIXC4VA== + dependencies: + "@passport-next/passport-strategy" "1.x.x" + openid "2.x.x" + +"@passport-next/passport-strategy@1.x.x": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@passport-next/passport-strategy/-/passport-strategy-1.1.0.tgz#4c0df069e2ec9262791b9ef1e23320c1d73bdb74" + integrity sha512-2KhFjtPueJG6xVj2HnqXt9BlANOfYCVLyu+pXYjPGBDT8yk+vQwc/6tsceIj+mayKcoxMau2JimggXRPHgoc8w== + "@types/body-parser@*": version "1.17.0" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c" @@ -350,7 +363,7 @@ ajv@^4.7.0, ajv@^4.9.1: co "^4.6.0" json-stable-stringify "^1.0.1" -ajv@^5.1.0: +ajv@^5.1.0, ajv@^5.3.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= @@ -738,6 +751,11 @@ aws4@^1.2.1, aws4@^1.6.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" integrity sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w== +aws4@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== + azure-storage@^2.7.0: version "2.10.0" resolved "https://registry.yarnpkg.com/azure-storage/-/azure-storage-2.10.0.tgz#020ac343262c5552ef86516cbb7679241e95e4de" @@ -2225,6 +2243,13 @@ combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" +combined-stream@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" + integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w== + dependencies: + delayed-stream "~1.0.0" + commander@2.15.x, commander@~2.15.0: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" @@ -3818,6 +3843,11 @@ extend@~1.2.1: resolved "https://registry.yarnpkg.com/extend/-/extend-1.2.1.tgz#a0f5fd6cfc83a5fe49ef698d60ec8a624dd4576c" integrity sha1-oPX9bPyDpf5J72mNYOyKYk3UV2w= +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + extendr@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/extendr/-/extendr-2.1.0.tgz#301aa0bbea565f4d2dc8f570f2a22611a8527b56" @@ -4120,11 +4150,6 @@ flush-write-stream@^1.0.0: inherits "^2.0.1" readable-stream "^2.0.4" -font-awesome@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" - integrity sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM= - for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -4149,6 +4174,11 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +fork-awesome@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fork-awesome/-/fork-awesome-1.1.3.tgz#820403b860a8f8bb90c3e0d8509c81b42ae2dcf8" + integrity sha512-pjeV0OWPbl/EiZJ64nsxB0ATGyG+e0/mnOI/BFlwT+171P2/2eI96KaRHy8ySZTdXG/lCCdffs7dODGm0hDBXg== + form-data@1.0.0-rc3: version "1.0.0-rc3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-1.0.0-rc3.tgz#d35bc62e7fbc2937ae78f948aaa0d38d90607577" @@ -4167,7 +4197,7 @@ form-data@~2.1.1: combined-stream "^1.0.5" mime-types "^2.1.12" -form-data@~2.3.1: +form-data@~2.3.1, form-data@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" integrity sha1-SXBJi+YEwgwAXU9cI67NIda0kJk= @@ -4733,6 +4763,14 @@ har-validator@~5.0.3: ajv "^5.1.0" har-schema "^2.0.0" +har-validator@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.0.tgz#44657f5688a22cfd4b72486e81b3a3fb11742c29" + integrity sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA== + dependencies: + ajv "^5.3.0" + har-schema "^2.0.0" + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -6830,6 +6868,11 @@ mime-db@~1.33.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== +mime-db@~1.36.0: + version "1.36.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397" + integrity sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw== + mime-types@^2.1.12, mime-types@^2.1.14, mime-types@^2.1.3, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.7: version "2.1.18" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" @@ -6837,6 +6880,13 @@ mime-types@^2.1.12, mime-types@^2.1.14, mime-types@^2.1.3, mime-types@~2.1.17, m dependencies: mime-db "~1.33.0" +mime-types@~2.1.19: + version "2.1.20" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19" + integrity sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A== + dependencies: + mime-db "~1.36.0" + mime@1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" @@ -7338,6 +7388,11 @@ oauth-sign@~0.8.1, oauth-sign@~0.8.2: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" integrity sha1-Rqarfwrq2N6unsBWV4C31O/rnUM= +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + oauth@0.9.x: version "0.9.15" resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" @@ -7478,6 +7533,13 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +openid@2.x.x: + version "2.0.6" + resolved "https://registry.yarnpkg.com/openid/-/openid-2.0.6.tgz#707375e59ab9f73025899727679b20328171c9aa" + integrity sha1-cHN15Zq59zAliZcnZ5sgMoFxyao= + dependencies: + request "^2.61.0" + optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -8605,7 +8667,7 @@ qs@6.5.1: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" integrity sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A== -qs@6.5.2, qs@~6.5.1: +qs@6.5.2, qs@~6.5.1, qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== @@ -9112,6 +9174,32 @@ request@2.x, request@^2.40.0, request@^2.79.0, request@^2.81.0, request@^2.86.0: tunnel-agent "^0.6.0" uuid "^3.1.0" +request@^2.61.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -10420,7 +10508,7 @@ toposort-class@^1.0.1: resolved "https://registry.yarnpkg.com/toposort-class/-/toposort-class-1.0.1.tgz#7ffd1f78c8be28c3ba45cd4e1a3f5ee193bd9988" integrity sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg= -tough-cookie@^2.3.2: +tough-cookie@^2.3.2, tough-cookie@~2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== @@ -10879,7 +10967,7 @@ uuid@3.1.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" integrity sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g== -uuid@^3.0.0, uuid@^3.1.0: +uuid@^3.0.0, uuid@^3.1.0, uuid@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== @@ -11080,6 +11168,13 @@ webpack-cli@^3.1.0: v8-compile-cache "^2.0.0" yargs "^12.0.1" +webpack-merge@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.4.tgz#0fde38eabf2d5fd85251c24a5a8c48f8a3f4eb7b" + integrity sha512-TmSe1HZKeOPey3oy1Ov2iS3guIZjWvMT2BBJDzzT5jScHTjVC3mpjJofgueEzaEd6ibhxRDD6MIblDr8tzh8iQ== + dependencies: + lodash "^4.17.5" + webpack-parallel-uglify-plugin@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/webpack-parallel-uglify-plugin/-/webpack-parallel-uglify-plugin-1.1.0.tgz#252a6c796bf79a8047b00de2cf08c23aa9861441" |