From 5cda55086acfc1000f0a0062045db50ad415db59 Mon Sep 17 00:00:00 2001 From: Christoph Witzany Date: Sun, 29 Oct 2017 11:16:40 +0100 Subject: [PATCH] Add mattermost authentication --- README.md | 22 ++++++++----- app.json | 12 +++++++ config.json.example | 5 +++ lib/config/default.js | 5 +++ lib/config/dockerSecret.js | 4 +++ lib/config/environment.js | 5 +++ lib/config/index.js | 1 + lib/models/user.js | 9 +++++ lib/response.js | 1 + lib/web/auth/index.js | 1 + lib/web/auth/mattermost/index.js | 49 ++++++++++++++++++++++++++++ package.json | 2 ++ public/views/index/body.ejs | 4 +-- public/views/shared/signin-modal.ejs | 9 +++-- 14 files changed, 116 insertions(+), 13 deletions(-) create mode 100644 lib/web/auth/mattermost/index.js diff --git a/README.md b/README.md index 718f686..894b690 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ HackMD [![version][github-version-badge]][github-release-page] -HackMD lets you create realtime collaborative markdown notes on all platforms. -Inspired by Hackpad, with more focus on speed and flexibility. +HackMD lets you create realtime collaborative markdown notes on all platforms. +Inspired by Hackpad, with more focus on speed and flexibility. Still in the early stage, feel free to fork or contribute to HackMD. Thanks for using! :smile: @@ -97,13 +97,13 @@ If you are upgrading HackMD from an older version, follow these steps: * [migration-to-0.5.0](https://github.com/hackmdio/migration-to-0.5.0) -We don't use LZString to compress socket.io data and DB data after version 0.5.0. +We don't use LZString to compress socket.io data and DB data after version 0.5.0. Please run the migration tool if you're upgrading from the old version. * [migration-to-0.4.0](https://github.com/hackmdio/migration-to-0.4.0) -We've dropped MongoDB after version 0.4.0. -So here is the migration tool for you to transfer the old DB data to the new DB. +We've dropped MongoDB after version 0.4.0. +So here is the migration tool for you to transfer the old DB data to the new DB. This tool is also used for official service. # Configuration @@ -141,6 +141,9 @@ There are some configs you need to change in the files below | HMD_GITLAB_BASEURL | no example | GitLab authentication endpoint, set to use other endpoint than GitLab.com (optional) | | HMD_GITLAB_CLIENTID | no example | GitLab API client id | | HMD_GITLAB_CLIENTSECRET | no example | GitLab API client secret | +| HMD_MATTERMOST_BASEURL | no example | Mattermost authentication endpoint | +| HMD_MATTERMOST_CLIENTID | no example | Mattermost API client id | +| HMD_MATTERMOST_CLIENTSECRET | no example | Mattermost API client secret | | HMD_DROPBOX_CLIENTID | no example | Dropbox API client id | | HMD_DROPBOX_CLIENTSECRET | no example | Dropbox API client secret | | HMD_GOOGLE_CLIENTID | no example | Google API client id | @@ -216,7 +219,7 @@ There are some configs you need to change in the files below | service | settings location | description | | ------- | --------- | ----------- | -| facebook, twitter, github, gitlab, dropbox, google, ldap | environment variables or `config.json` | for signin | +| facebook, twitter, github, gitlab, mattermost, dropbox, google, ldap | environment variables or `config.json` | for signin | | imgur | environment variables or `config.json` | for image upload | | google drive(`google/apiKey`, `google/clientID`), dropbox(`dropbox/appKey`) | `config.json` | for export and import | @@ -228,6 +231,7 @@ There are some configs you need to change in the files below | twitter | `/auth/twitter/callback` | | github | `/auth/github/callback` | | gitlab | `/auth/gitlab/callback` | +| mattermost | `/auth/mattermost/callback` | | dropbox | `/auth/dropbox/callback` | | google | `/auth/google/callback` | @@ -249,9 +253,9 @@ hackmd/ ## Operational Transformation -From 0.3.2, we started supporting operational transformation. -It makes concurrent editing safe and will not break up other users' operations. -Additionally, now can show other clients' selections. +From 0.3.2, we started supporting operational transformation. +It makes concurrent editing safe and will not break up other users' operations. +Additionally, now can show other clients' selections. See more at [http://operational-transformation.github.io/](http://operational-transformation.github.io/) diff --git a/app.json b/app.json index d1804c5..b2116eb 100644 --- a/app.json +++ b/app.json @@ -100,6 +100,18 @@ "description": "GitLab API client scope (optional)", "required": false }, + "HMD_MATTERMOST_BASEURL": { + "description": "Mattermost authentication endpoint", + "required": false + }, + "HMD_MATTERMOST_CLIENTID": { + "description": "Mattermost API client id", + "required": false + }, + "HMD_MATTERMOST_CLIENTSECRET": { + "description": "Mattermost API client secret", + "required": false + }, "HMD_DROPBOX_CLIENTID": { "description": "Dropbox API client id", "required": false diff --git a/config.json.example b/config.json.example index e2d774c..6dd20b7 100644 --- a/config.json.example +++ b/config.json.example @@ -48,6 +48,11 @@ "clientSecret": "change this", "scope": "use 'read_user' scope for auth user only or remove this property if you need gitlab snippet import/export support (will result to be default scope 'api')" }, + "mattermost": { + "baseURL": "change this", + "clientID": "change this", + "clientSecret": "change this" + }, "dropbox": { "clientID": "change this", "clientSecret": "change this", diff --git a/lib/config/default.js b/lib/config/default.js index e7e2e4b..273bad0 100644 --- a/lib/config/default.js +++ b/lib/config/default.js @@ -74,6 +74,11 @@ module.exports = { clientSecret: undefined, scope: undefined }, + mattermost: { + baseURL: undefined, + clientID: undefined, + clientSecret: undefined + }, dropbox: { clientID: undefined, clientSecret: undefined diff --git a/lib/config/dockerSecret.js b/lib/config/dockerSecret.js index eea2faf..ac54fd1 100644 --- a/lib/config/dockerSecret.js +++ b/lib/config/dockerSecret.js @@ -38,6 +38,10 @@ if (fs.existsSync(basePath)) { clientID: getSecret('gitlab_clientID'), clientSecret: getSecret('gitlab_clientSecret') }, + mattermost: { + clientID: getSecret('mattermost_clientID'), + clientSecret: getSecret('mattermost_clientSecret') + }, dropbox: { clientID: getSecret('dropbox_clientID'), clientSecret: getSecret('dropbox_clientSecret') diff --git a/lib/config/environment.js b/lib/config/environment.js index 6f33d14..0c272f0 100644 --- a/lib/config/environment.js +++ b/lib/config/environment.js @@ -49,6 +49,11 @@ module.exports = { clientSecret: process.env.HMD_GITLAB_CLIENTSECRET, scope: process.env.HMD_GITLAB_SCOPE }, + mattermost: { + baseURL: process.env.HMD_MATTERMOST_BASEURL, + clientID: process.env.HMD_MATTERMOST_CLIENTID, + clientSecret: process.env.HMD_MATTERMOST_CLIENTSECRET + }, dropbox: { clientID: process.env.HMD_DROPBOX_CLIENTID, clientSecret: process.env.HMD_DROPBOX_CLIENTSECRET diff --git a/lib/config/index.js b/lib/config/index.js index dfad28e..addd8ba 100644 --- a/lib/config/index.js +++ b/lib/config/index.js @@ -90,6 +90,7 @@ config.isTwitterEnable = config.twitter.consumerKey && config.twitter.consumerSe config.isEmailEnable = config.email config.isGitHubEnable = config.github.clientID && config.github.clientSecret config.isGitLabEnable = config.gitlab.clientID && config.gitlab.clientSecret +config.isMattermostEnable = config.mattermost.clientID && config.mattermost.clientSecret config.isLDAPEnable = config.ldap.url config.isPDFExportEnable = config.allowpdfexport diff --git a/lib/models/user.js b/lib/models/user.js index e59b86c..27566de 100644 --- a/lib/models/user.js +++ b/lib/models/user.js @@ -111,6 +111,15 @@ module.exports = function (sequelize, DataTypes) { photo = letterAvatars(profile.username) } break + case 'mattermost': + photo = profile.avatarUrl + if (photo) { + if (bigger) photo = photo.replace(/(\?s=)\d*$/i, '$1400') + else photo = photo.replace(/(\?s=)\d*$/i, '$196') + } else { + photo = letterAvatars(profile.username) + } + break case 'dropbox': // no image api provided, use gravatar photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0].value) diff --git a/lib/response.js b/lib/response.js index 9e39ffb..0c6a95e 100755 --- a/lib/response.js +++ b/lib/response.js @@ -64,6 +64,7 @@ function showIndex (req, res, next) { twitter: config.isTwitterEnable, github: config.isGitHubEnable, gitlab: config.isGitLabEnable, + mattermost: config.isMattermostEnable, dropbox: config.isDropboxEnable, google: config.isGoogleEnable, ldap: config.isLDAPEnable, diff --git a/lib/web/auth/index.js b/lib/web/auth/index.js index b5ca843..4b61810 100644 --- a/lib/web/auth/index.js +++ b/lib/web/auth/index.js @@ -33,6 +33,7 @@ if (config.isFacebookEnable) authRouter.use(require('./facebook')) if (config.isTwitterEnable) authRouter.use(require('./twitter')) if (config.isGitHubEnable) authRouter.use(require('./github')) if (config.isGitLabEnable) authRouter.use(require('./gitlab')) +if (config.isMattermostEnable) authRouter.use(require('./mattermost')) if (config.isDropboxEnable) authRouter.use(require('./dropbox')) if (config.isGoogleEnable) authRouter.use(require('./google')) if (config.isLDAPEnable) authRouter.use(require('./ldap')) diff --git a/lib/web/auth/mattermost/index.js b/lib/web/auth/mattermost/index.js new file mode 100644 index 0000000..9ccf3de --- /dev/null +++ b/lib/web/auth/mattermost/index.js @@ -0,0 +1,49 @@ +'use strict' + +const Router = require('express').Router +const passport = require('passport') +const Mattermost = require('mattermost') +const OAuthStrategy = require('passport-oauth2').Strategy +const config = require('../../../config') +const {setReturnToFromReferer, passportGeneralCallback} = require('../utils') + +const mattermost = new Mattermost.Client() + +let mattermostAuth = module.exports = Router() + +let mattermostStrategy = new OAuthStrategy({ + authorizationURL: config.mattermost.baseURL + '/oauth/authorize', + tokenURL: config.mattermost.baseURL + '/oauth/access_token', + clientID: config.mattermost.clientID, + clientSecret: config.mattermost.clientSecret, + callbackURL: config.serverurl + '/auth/mattermost/callback' +}, passportGeneralCallback) + +mattermostStrategy.userProfile = (accessToken, done) => { + mattermost.setUrl(config.mattermost.baseURL) + mattermost.token = accessToken + mattermost.useHeaderToken() + mattermost.getMe( + (data) => { + done(null, data) + }, + (err) => { + done(err) + } + ) +} + +passport.use(mattermostStrategy) + +mattermostAuth.get('/auth/mattermost', function (req, res, next) { + setReturnToFromReferer(req) + passport.authenticate('oauth2')(req, res, next) +}) + +// mattermost auth callback +mattermostAuth.get('/auth/mattermost/callback', + passport.authenticate('oauth2', { + successReturnToOrRedirect: config.serverurl + '/', + failureRedirect: config.serverurl + '/' + }) +) diff --git a/package.json b/package.json index a78bb2b..b4942c5 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "markdown-pdf": "^7.0.0", "mathjax": "~2.7.0", "mermaid": "~7.1.0", + "mattermost": "^3.4.0", "meta-marked": "^0.4.2", "method-override": "^2.3.7", "moment": "^2.17.1", @@ -91,6 +92,7 @@ "passport-google-oauth20": "^1.0.0", "passport-ldapauth": "^0.6.0", "passport-local": "^1.0.0", + "passport-oauth2": "^1.4.0", "passport-twitter": "^1.0.4", "passport.socketio": "^3.7.0", "pdfobject": "^2.0.201604172", diff --git a/public/views/index/body.ejs b/public/views/index/body.ejs index b9c5c42..230eb11 100644 --- a/public/views/index/body.ejs +++ b/public/views/index/body.ejs @@ -15,7 +15,7 @@ <% if(allowAnonymous) { %> <%= __('New guest note') %> <% } %> - <% if(facebook || twitter || github || gitlab || dropbox || google || ldap || email) { %> + <% if(facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || email) { %> <% } %> @@ -48,7 +48,7 @@ <% if (errorMessage && errorMessage.length > 0) { %>
<%= errorMessage %>
<% } %> - <% if(facebook || twitter || github || gitlab || dropbox || google || ldap || email) { %> + <% if(facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap || email) { %>
diff --git a/public/views/shared/signin-modal.ejs b/public/views/shared/signin-modal.ejs index a8af62e..89b542e 100644 --- a/public/views/shared/signin-modal.ejs +++ b/public/views/shared/signin-modal.ejs @@ -28,6 +28,11 @@ <%= __('Sign in via %s', 'GitLab') %> <% } %> + <% if(mattermost) { %> + + <%= __('Sign in via %s', 'Mattermost') %> + + <% } %> <% if(dropbox) { %> <%= __('Sign in via %s', 'Dropbox') %> @@ -38,7 +43,7 @@ <%= __('Sign in via %s', 'Google') %> <% } %> - <% if((facebook || twitter || github || gitlab || dropbox || google) && ldap) { %> + <% if((facebook || twitter || github || gitlab || mattermost || dropbox || google) && ldap) { %>
<% }%> <% if(ldap) { %> @@ -63,7 +68,7 @@ <% } %> - <% if((facebook || twitter || github || gitlab || dropbox || google || ldap) && email) { %> + <% if((facebook || twitter || github || gitlab || mattermost || dropbox || google || ldap) && email) { %>
<% }%> <% if(email) { %>