Przeglądaj źródła

Add sudo-mode 'confirm password' prompt

Shane Kilkelly 9 lat temu
rodzic
commit
029c96c7cc

+ 58 - 0
services/web/app/coffee/Features/SudoMode/SudoModeController.coffee

@@ -0,0 +1,58 @@
+UserLocator = require "../User/UserLocator"
+Settings = require "settings-sharelatex"
+logger = require 'logger-sharelatex'
+SudoModeHandler = require './SudoModeHandler'
+AuthenticationController = require '../Authentication/AuthenticationController'
+AuthenticationManager = require '../Authentication/AuthenticationManager'
+ObjectId = require('../../infrastructure/Mongoose').mongo.ObjectId
+UserGetter = require '../User/UserGetter'
+
+
+module.exports = SudoModeController =
+
+	sudoModePrompt: (req, res, next) ->
+		userId = AuthenticationController.getLoggedInUserId(req)
+		logger.log {userId}, "[SudoMode] rendering sudo mode password page"
+		SudoModeHandler.isSudoModeActive userId, (err, isActive) ->
+			if err?
+				logger.err {err, userId}, "[SudoMode] error checking if sudo mode is active"
+				return next(err)
+			if isActive
+				logger.log {userId}, "[SudoMode] sudo mode already active, redirecting"
+				return res.redirect('/project')
+			res.render 'sudo_mode/sudo_mode_prompt', title: 'confirm_your_password'
+
+	submitPassword: (req, res, next) ->
+		userId = AuthenticationController.getLoggedInUserId(req)
+		redir = AuthenticationController._getRedirectFromSession(req) || "/project"
+		password = req.body.password
+		if !password
+			logger.log {userId}, "[SudoMode] no password supplied, failed authentication"
+			return next(new Error('no password supplied'))
+		logger.log {userId, redir}, "[SudoMode] checking user password"
+		UserGetter.getUser ObjectId(userId), {email: 1}, (err, userRecord) ->
+			if err?
+				logger.err {err, userId}, "[SudoMode] error getting user"
+				return next(err)
+			AuthenticationManager.authenticate email: userRecord.email, password, (err, user) ->
+				if err?
+					logger.err {err, userId}, "[SudoMode] error authenticating user"
+					return next(err)
+				if user?
+					logger.log {userId}, "[SudoMode] authenticated user, activating sudo mode"
+					SudoModeHandler.activateSudoMode userId, (err) ->
+						if err?
+							logger.err {err, userId}, "[SudoMode] error activating sudo mode"
+							return next(err)
+						return res.json {
+							redir: redir
+						}
+				else
+					logger.log {userId}, "[SudoMode] authentication failed for user"
+					return res.json {
+						message: {
+							text: req.i18n.translate("invalid_password"),
+							type: 'error'
+						}
+					}
+

+ 27 - 0
services/web/app/coffee/Features/SudoMode/SudoModeHandler.coffee

@@ -0,0 +1,27 @@
+RedisWrapper = require('../../infrastructure/RedisWrapper')
+rclient = RedisWrapper.client('sudomode')
+logger = require('logger-sharelatex')
+
+
+TIMEOUT_IN_SECONDS = 60 * 10
+
+
+module.exports = SudoModeHandler =
+
+	_buildKey: (userId) ->
+		"SudoMode:{#{userId}}"
+
+	activateSudoMode: (userId, callback=(err)->) ->
+		if !userId?
+			return callback(new Error('[SudoMode] user must not be supplied'))
+		duration = TIMEOUT_IN_SECONDS
+		logger.log {userId, duration}, "[SudoMode] activating sudo mode for user"
+		rclient.set SudoModeHandler._buildKey(userId), '1', 'EX', duration, callback
+
+	isSudoModeActive: (userId, callback=(err, isActive)->) ->
+		if !userId?
+			return callback(new Error('[SudoMode] user must not be supplied'))
+		rclient.get SudoModeHandler._buildKey(userId), (err, result) ->
+			if err?
+				return callback(err)
+			callback(null, result == '1')

+ 4 - 0
services/web/app/coffee/router.coffee

@@ -38,6 +38,7 @@ ContactRouter = require("./Features/Contacts/ContactRouter")
 ReferencesController = require('./Features/References/ReferencesController')
 AuthorizationMiddlewear = require('./Features/Authorization/AuthorizationMiddlewear')
 BetaProgramController = require('./Features/BetaProgram/BetaProgramController')
+SudoModeController = require('./Features/SudoMode/SudoModeController')
 AnalyticsRouter = require('./Features/Analytics/AnalyticsRouter')
 AnnouncementsController = require("./Features/Announcements/AnnouncementsController")
 
@@ -238,6 +239,9 @@ module.exports = class Router
 		webRouter.get "/beta/participate",  AuthenticationController.requireLogin(), BetaProgramController.optInPage
 		webRouter.post "/beta/opt-in", AuthenticationController.requireLogin(), BetaProgramController.optIn
 		webRouter.post "/beta/opt-out", AuthenticationController.requireLogin(), BetaProgramController.optOut
+		webRouter.get "/confirm-password", AuthenticationController.requireLogin(), SudoModeController.sudoModePrompt
+		webRouter.post "/confirm-password/submit", AuthenticationController.requireLogin(), SudoModeController.submitPassword
+
 
 		#Admin Stuff
 		webRouter.get  '/admin', AuthorizationMiddlewear.ensureUserIsSiteAdmin, AdminController.index

+ 39 - 0
services/web/app/views/sudo_mode/sudo_mode_prompt.pug

@@ -0,0 +1,39 @@
+extends ../layout
+
+block content
+	.content.content-alt
+		.container
+			.row
+				.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
+					.card
+						.page-header.text-centered
+							h1 Confirm your password to continue
+						div
+							.container-fluid
+								.row
+									.col-md-8.col-md-offset-2
+										form(async-form="confirmPassword", name="confirmPassword",
+												action='/confirm-password/submit', method="POST", ng-cloak)
+											input(name='_csrf', type='hidden', value=csrfToken)
+											form-messages(for="confirmPassword")
+											.form-group
+												label
+													| #{translate('password')}
+												input.form-control(
+													type='password',
+													name='password',
+													required,
+													placeholder='********',
+													ng-model="password"
+												)
+												span.small.text-primary(
+													ng-show="confirmPassword.password.$invalid && confirmPassword.password.$dirty"
+												)
+													| #{translate("required")}
+											.actions
+												button.btn-primary.btn(
+													style="width: 100%",
+													type='submit',
+													ng-disabled="confirmPassword.inflight"
+												)
+													span #{translate("confirm")}