From eac0ce8353529f1d40d4c3e55259bbfb57bb5bbc Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 7 Aug 2017 12:24:04 +0200 Subject: [PATCH] Initial spike of Overleaf based logins --- .../OverleafAuthenticationController.coffee | 33 +++++++++++++++++++ .../OverleafAuthenticationManager.coffee | 0 .../coffee/Features/User/UserCreator.coffee | 20 ++++------- .../app/coffee/infrastructure/Server.coffee | 26 ++++++++++++++- services/web/app/coffee/models/User.coffee | 3 ++ services/web/app/coffee/router.coffee | 4 +++ services/web/npm-shrinkwrap.json | 21 ++++++++++-- services/web/package.json | 1 + 8 files changed, 90 insertions(+), 18 deletions(-) create mode 100644 services/web/app/coffee/Features/Authentication/OverleafAuthenticationController.coffee create mode 100644 services/web/app/coffee/Features/Authentication/OverleafAuthenticationManager.coffee diff --git a/services/web/app/coffee/Features/Authentication/OverleafAuthenticationController.coffee b/services/web/app/coffee/Features/Authentication/OverleafAuthenticationController.coffee new file mode 100644 index 0000000000..8cc11c65c0 --- /dev/null +++ b/services/web/app/coffee/Features/Authentication/OverleafAuthenticationController.coffee @@ -0,0 +1,33 @@ +logger = require("logger-sharelatex") +settings = require "settings-sharelatex" +{User} = require "../../models/User" +UserCreator = require "../User/UserCreator" +AuthenticationController = require "./AuthenticationController" + +passport = require "passport" + +module.exports = OverleafAuthenticationController = + passportLogin: passport.authenticate("oauth2") + + passportCallback: passport.authenticate("oauth2") + + afterPassportLogin: (req, res, next) -> + logger.log {user: req.user}, "successful log in!" + AuthenticationController.afterLoginSessionSetup req, req.user, (err) -> + return next(err) if err? + res.redirect("/") + + doPassportLogin: (accessToken, refreshToken, profile, cb) -> + logger.log {accessToken, refreshToken, profile}, "authing user via overleaf oauth" + OverleafAuthenticationController._findOrCreateUser profile, (err, user) -> + return cb(err) if err? + user.overleaf.accessToken = accessToken + user.save (err) -> + return cb(err) if err? + return cb(null, user) + + _findOrCreateUser: (profile, cb) -> + User.findOne { "overleaf.id": profile.id }, (err, user) -> + return cb(err) if err? + return cb(null, user) if user? + UserCreator.createNewUser { overleaf: { id: profile.id }, email: profile.email }, cb diff --git a/services/web/app/coffee/Features/Authentication/OverleafAuthenticationManager.coffee b/services/web/app/coffee/Features/Authentication/OverleafAuthenticationManager.coffee new file mode 100644 index 0000000000..e69de29bb2 diff --git a/services/web/app/coffee/Features/User/UserCreator.coffee b/services/web/app/coffee/Features/User/UserCreator.coffee index d4ce82cafb..d08b953559 100644 --- a/services/web/app/coffee/Features/User/UserCreator.coffee +++ b/services/web/app/coffee/Features/User/UserCreator.coffee @@ -17,23 +17,15 @@ module.exports = UserCreator = createNewUser: (opts, callback)-> logger.log opts:opts, "creating new user" user = new User() - user.email = opts.email - user.holdingAccount = opts.holdingAccount - user.ace.syntaxValidation = true username = opts.email.match(/^[^@]*/) - if opts.first_name? and opts.first_name.length != 0 - user.first_name = opts.first_name - else if username? - user.first_name = username[0] - else - user.first_name = "" - - if opts.last_name? - user.last_name = opts.last_name - else - user.last_name = "" + if !opts.first_name? or opts.first_name == "" + opts.first_name = username[0] + for key, value of opts + user[key] = value + + user.ace.syntaxValidation = true user.featureSwitches?.pdfng = true user.save (err)-> diff --git a/services/web/app/coffee/infrastructure/Server.coffee b/services/web/app/coffee/infrastructure/Server.coffee index 48f7fd3e65..f159523379 100644 --- a/services/web/app/coffee/infrastructure/Server.coffee +++ b/services/web/app/coffee/infrastructure/Server.coffee @@ -25,6 +25,7 @@ sessionStore = new RedisStore(client:sessionsRedisClient) passport = require('passport') LocalStrategy = require('passport-local').Strategy +OAuth2Strategy = require('passport-oauth2').Strategy Mongoose = require("./Mongoose") @@ -38,7 +39,7 @@ Modules = require "./Modules" ErrorController = require "../Features/Errors/ErrorController" UserSessionsManager = require "../Features/User/UserSessionsManager" AuthenticationController = require "../Features/Authentication/AuthenticationController" - +OverleafAuthenticationController = require "../Features/Authentication/OverleafAuthenticationController" metrics.event_loop?.monitor(logger) @@ -105,6 +106,29 @@ passport.use(new LocalStrategy( passport.serializeUser(AuthenticationController.serializeUser) passport.deserializeUser(AuthenticationController.deserializeUser) +overleafOAuth2Strategy = new OAuth2Strategy( + { + authorizationURL: 'http://localhost:5000/oauth/authorize', + tokenURL: 'http://localhost:5000/oauth/token', + clientID: "0479498de20727971b5f40f86dc558264fe7a5021ae74c3e0e03f7dccfeaf0ab", + clientSecret: "ecb446d53bb9a1555fecd74b5e3faabefe1345ca6a9228da0c1fbdac2338c502", + callbackURL: "http://www.sharelatex.dev:3000/overleaf/callback" + }, + OverleafAuthenticationController.doPassportLogin +) +overleafOAuth2Strategy.userProfile = (accessToken, cb) -> + require("request").get { + url: "http://localhost:5000/api/v1/sharelatex/profile" + json: true + headers: + Authorization: "Bearer #{accessToken}" + }, (err, response, body) -> + console.log {err, response, body} + return cb(err) if err? + cb(null, body) +passport.use(overleafOAuth2Strategy) + + Modules.hooks.fire 'passportSetup', passport, (err) -> if err? logger.err {err}, "error setting up passport in modules" diff --git a/services/web/app/coffee/models/User.coffee b/services/web/app/coffee/models/User.coffee index fce4b18bc0..b5de12919c 100644 --- a/services/web/app/coffee/models/User.coffee +++ b/services/web/app/coffee/models/User.coffee @@ -59,6 +59,9 @@ UserSchema = new Schema zotero: Boolean } betaProgram: { type:Boolean, default: false} + overleaf: + id: { type: Number } + accessToken: { type: String } conn = mongoose.createConnection(Settings.mongo.url, server: poolSize: 10) diff --git a/services/web/app/coffee/router.coffee b/services/web/app/coffee/router.coffee index 2ebde53748..e3a90d3431 100644 --- a/services/web/app/coffee/router.coffee +++ b/services/web/app/coffee/router.coffee @@ -12,6 +12,7 @@ UploadsRouter = require './Features/Uploads/UploadsRouter' metrics = require('metrics-sharelatex') ReferalController = require('./Features/Referal/ReferalController') AuthenticationController = require('./Features/Authentication/AuthenticationController') +OverleafAuthenticationController = require('./Features/Authentication/OverleafAuthenticationController') TagsController = require("./Features/Tags/TagsController") NotificationsController = require("./Features/Notifications/NotificationsController") CollaboratorsRouter = require('./Features/Collaborators/CollaboratorsRouter') @@ -58,6 +59,9 @@ module.exports = class Router AuthenticationController.addEndpointToLoginWhitelist '/login' webRouter.post '/login', AuthenticationController.passportLogin + + webRouter.get '/overleaf/login', OverleafAuthenticationController.passportLogin + webRouter.get '/overleaf/callback', OverleafAuthenticationController.passportCallback, OverleafAuthenticationController.afterPassportLogin webRouter.get '/logout', UserController.logout webRouter.get '/restricted', AuthorizationMiddlewear.restricted diff --git a/services/web/npm-shrinkwrap.json b/services/web/npm-shrinkwrap.json index 6f6d9e475e..042a93f8af 100644 --- a/services/web/npm-shrinkwrap.json +++ b/services/web/npm-shrinkwrap.json @@ -1509,7 +1509,7 @@ }, "minimatch": { "version": "3.0.4", - "from": "minimatch@^3.0.4", + "from": "minimatch@^3.0.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "dev": true }, @@ -1678,7 +1678,7 @@ }, "iconv-lite": { "version": "0.2.11", - "from": "iconv-lite@>=0.2.11 <0.3.0", + "from": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz" }, "ieee754": { @@ -1698,7 +1698,7 @@ }, "inherits": { "version": "2.0.3", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "inherits@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "ini": { @@ -2847,6 +2847,11 @@ "from": "number-is-nan@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" }, + "oauth": { + "version": "0.9.15", + "from": "oauth@>=0.9.0 <0.10.0", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz" + }, "oauth-sign": { "version": "0.8.2", "from": "oauth-sign@>=0.8.1 <0.9.0", @@ -3070,6 +3075,11 @@ "from": "passport-local@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz" }, + "passport-oauth2": { + "version": "1.4.0", + "from": "passport-oauth2@latest", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.4.0.tgz" + }, "passport-saml": { "version": "0.15.0", "from": "passport-saml@>=0.15.0 <0.16.0", @@ -4292,6 +4302,11 @@ "from": "uid-safe@2.1.4", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.4.tgz" }, + "uid2": { + "version": "0.0.3", + "from": "uid2@>=0.0.0 <0.1.0", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz" + }, "underscore": { "version": "1.6.0", "from": "underscore@1.6.0", diff --git a/services/web/package.json b/services/web/package.json index 14c7f00070..2c3dab21d8 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -49,6 +49,7 @@ "passport": "^0.3.2", "passport-ldapauth": "^0.6.0", "passport-local": "^1.0.0", + "passport-oauth2": "^1.4.0", "passport-saml": "^0.15.0", "pug": "^2.0.0-beta6", "redis": "0.10.1",