diff options
-rw-r--r-- | core/Controller/LoginController.php | 37 | ||||
-rw-r--r-- | core/Controller/OCJSController.php | 10 | ||||
-rw-r--r-- | core/css/styles.css | 31 | ||||
-rw-r--r-- | core/js/js.js | 64 | ||||
-rw-r--r-- | core/routes.php | 1 | ||||
-rw-r--r-- | core/templates/layout.user.php | 7 | ||||
-rw-r--r-- | lib/private/Template/JSConfigHelper.php | 20 | ||||
-rw-r--r-- | lib/private/TemplateLayout.php | 1 |
8 files changed, 167 insertions, 4 deletions
diff --git a/core/Controller/LoginController.php b/core/Controller/LoginController.php index 71478470ffe..b1542de5d3c 100644 --- a/core/Controller/LoginController.php +++ b/core/Controller/LoginController.php @@ -1,5 +1,6 @@ <?php /** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Christoph Wurst <christoph@owncloud.com> @@ -31,6 +32,8 @@ use OC\User\Session; use OC_App; use OC_Util; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; use OCP\Authentication\TwoFactorAuth\IProvider; @@ -242,6 +245,8 @@ class LoginController extends Controller { // User has successfully logged in, now remove the password reset link, when it is available $this->config->deleteUserValue($loginResult->getUID(), 'core', 'lostpassword'); + $this->session->set('last-password-confirm', $loginResult->getLastLogin()); + if ($this->twoFactorManager->isTwoFactorAuthenticated($loginResult)) { $this->twoFactorManager->prepareTwoFactorLogin($loginResult, $remember_login); @@ -273,4 +278,36 @@ class LoginController extends Controller { return $this->generateRedirect($redirect_url); } + /** + * @NoAdminRequired + * @UseSession + * + * @license GNU AGPL version 3 or any later version + * + * @param string $password + * @return DataResponse + */ + public function confirmPassword($password) { + $currentDelay = $this->throttler->getDelay($this->request->getRemoteAddress()); + $this->throttler->sleepDelay($this->request->getRemoteAddress()); + + $user = $this->userSession->getUser(); + if (!$user instanceof IUser) { + return new DataResponse([], Http::STATUS_UNAUTHORIZED); + } + + $loginResult = $this->userManager->checkPassword($user->getUID(), $password); + if ($loginResult === false) { + $this->throttler->registerAttempt('sudo', $this->request->getRemoteAddress(), ['user' => $user->getUID()]); + if ($currentDelay === 0) { + $this->throttler->sleepDelay($this->request->getRemoteAddress()); + } + + return new DataResponse([], Http::STATUS_FORBIDDEN); + } + + $confirmTimestamp = time(); + $this->session->set('last-password-confirm', $confirmTimestamp); + return new DataResponse(['lastLogin' => $confirmTimestamp], Http::STATUS_OK); + } } diff --git a/core/Controller/OCJSController.php b/core/Controller/OCJSController.php index b1c2208377e..c2292a6733e 100644 --- a/core/Controller/OCJSController.php +++ b/core/Controller/OCJSController.php @@ -32,6 +32,7 @@ use OCP\IConfig; use OCP\IGroupManager; use OCP\IL10N; use OCP\IRequest; +use OCP\ISession; use OCP\IURLGenerator; use OCP\IUserSession; @@ -48,7 +49,8 @@ class OCJSController extends Controller { * @param IL10N $l * @param \OC_Defaults $defaults * @param IAppManager $appManager - * @param IUserSession $session + * @param ISession $session + * @param IUserSession $userSession * @param IConfig $config * @param IGroupManager $groupManager * @param IniGetWrapper $iniWrapper @@ -59,7 +61,8 @@ class OCJSController extends Controller { IL10N $l, \OC_Defaults $defaults, IAppManager $appManager, - IUserSession $session, + ISession $session, + IUserSession $userSession, IConfig $config, IGroupManager $groupManager, IniGetWrapper $iniWrapper, @@ -70,7 +73,8 @@ class OCJSController extends Controller { $l, $defaults, $appManager, - $session->getUser(), + $session, + $userSession->getUser(), $config, $groupManager, $iniWrapper, diff --git a/core/css/styles.css b/core/css/styles.css index f0c4c4f33ff..7b2be87f610 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -983,3 +983,34 @@ fieldset.warning legend + p, fieldset.update legend + p { opacity: 0; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; } + +#sudo-login-background { + position: absolute; + width: 100%; + height: 100%; + background-color: #1d2d44; + opacity:0.4; + z-index: 999; +} + +#sudo-login-form { + position: absolute; + top: 45%; + left: 40%; + border: 1px solid #e9322d; + z-index: 1000; + background: #e4b9c0; + border-radius: 10px; + opacity:1; + padding: 25px; +} + +#sudo-login-form .question { + width: 250px; +} + +#sudo-login-form .confirm { + position: absolute; + top: 25px; + right: 25px; +} diff --git a/core/js/js.js b/core/js/js.js index 54b103a7b7d..bc99e1c77da 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -1512,8 +1512,72 @@ function initCore() { $(this).text(OC.Util.relativeModifiedDate(parseInt($(this).attr('data-timestamp'), 10))); }); }, 30 * 1000); + + OC.PasswordConfirmation.init(); } +OC.PasswordConfirmation = { + $form: null, + $background: null, + $input: null, + $submit: null, + + init: function() { + this.$form = $('#sudo-login-form'); + this.$background = $('#sudo-login-background'); + this.$input = this.$form.find('.question'); + this.$submit = this.$form.find('.confirm'); + + this.$background.on('click', _.bind(this._fadeOut, this)); + $('.password-confirm-required').on('click', _.bind(this.requirePasswordConfirmation, this)); + this.$submit.on('click', _.bind(this._submitPasswordConfirmation, this)); + }, + + requirePasswordConfirmation: function() { + var timeSinceLogin = moment.now() - nc_lastLogin * 1000; + if (timeSinceLogin > 30 * 60 * 1000) { // 30 minutes + this.$form.removeClass('hidden'); + this.$background.removeClass('hidden'); + + // Hack against firefox ignoring autocomplete="off" + if (this.$input.val() === ' ') { + this.$input.val(''); + } + } + }, + + _submitPasswordConfirmation: function() { + var self = this; + + self.$submit.removeClass('icon-confirm').addClass('icon-loading-small'); + + $.ajax({ + url: OC.generateUrl('/login/confirm'), + data: { + password: this.$input.val() + }, + type: 'POST', + success: function(response) { + nc_lastLogin = response.lastLogin; + self.$submit.addClass('icon-confirm').removeClass('icon-loading-small'); + + self.$form.addClass('hidden'); + self.$background.addClass('hidden'); + }, + error: function() { + OC.Notification.showTemporary(t('core', 'Failed to authenticate, try again')); + self.$submit.addClass('icon-confirm').removeClass('icon-loading-small'); + } + }); + }, + + _fadeOut: function() { + this.$form.addClass('hidden'); + this.$background.addClass('hidden'); + this.$input.value = ''; + } +}; + $(document).ready(initCore); /** diff --git a/core/routes.php b/core/routes.php index 2ddd77c1445..e5636ff6c00 100644 --- a/core/routes.php +++ b/core/routes.php @@ -46,6 +46,7 @@ $application->registerRoutes($this, [ ['name' => 'avatar#getTmpAvatar', 'url' => '/avatar/tmp', 'verb' => 'GET'], ['name' => 'avatar#postAvatar', 'url' => '/avatar/', 'verb' => 'POST'], ['name' => 'login#tryLogin', 'url' => '/login', 'verb' => 'POST'], + ['name' => 'login#confirmPassword', 'url' => '/login/confirm', 'verb' => 'POST'], ['name' => 'login#showLoginForm', 'url' => '/login', 'verb' => 'GET'], ['name' => 'login#logout', 'url' => '/logout', 'verb' => 'GET'], ['name' => 'TwoFactorChallenge#selectChallenge', 'url' => '/login/selectchallenge', 'verb' => 'GET'], diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php index bc8edf085d0..1b4f0bc0030 100644 --- a/core/templates/layout.user.php +++ b/core/templates/layout.user.php @@ -146,6 +146,13 @@ </div> </div></nav> + <div id="sudo-login-background" class="hidden"></div> + <div id="sudo-login-form" class="hidden"> + <input type="password" class="question" autocomplete="off" name="question" value=" " + placeholder="<?php p($l->t('Confirm your password')); ?>" /> + <input class="confirm icon-confirm" title="<?php p($l->t('Confirm')); ?>" value="" type="submit"> + </div> + <div id="content-wrapper"> <div id="content" class="app-<?php p($_['appid']) ?>" role="main"> <?php print_unescaped($_['content']); ?> diff --git a/lib/private/Template/JSConfigHelper.php b/lib/private/Template/JSConfigHelper.php index a7f8c251cee..eceaed0c380 100644 --- a/lib/private/Template/JSConfigHelper.php +++ b/lib/private/Template/JSConfigHelper.php @@ -27,6 +27,7 @@ use OCP\App\IAppManager; use OCP\IConfig; use OCP\IGroupManager; use OCP\IL10N; +use OCP\ISession; use OCP\IURLGenerator; use OCP\IUser; @@ -41,7 +42,10 @@ class JSConfigHelper { /** @var IAppManager */ private $appManager; - /** @var IUser */ + /** @var ISession */ + private $session; + + /** @var IUser|null */ private $currentUser; /** @var IConfig */ @@ -60,6 +64,7 @@ class JSConfigHelper { * @param IL10N $l * @param \OC_Defaults $defaults * @param IAppManager $appManager + * @param ISession $session * @param IUser|null $currentUser * @param IConfig $config * @param IGroupManager $groupManager @@ -69,6 +74,7 @@ class JSConfigHelper { public function __construct(IL10N $l, \OC_Defaults $defaults, IAppManager $appManager, + ISession $session, $currentUser, IConfig $config, IGroupManager $groupManager, @@ -77,6 +83,7 @@ class JSConfigHelper { $this->l = $l; $this->defaults = $defaults; $this->appManager = $appManager; + $this->session = $session; $this->currentUser = $currentUser; $this->config = $config; $this->groupManager = $groupManager; @@ -119,6 +126,16 @@ class JSConfigHelper { $dataLocation = false; } + if ($this->currentUser instanceof IUser) { + $lastConfirmTimestamp = $this->currentUser->getLastLogin(); + $sessionTime = $this->session->get('last-password-confirm'); + if (is_int($sessionTime)) { + $lastConfirmTimestamp = $sessionTime; + } + } else { + $lastConfirmTimestamp = 0; + } + $array = [ "oc_debug" => $this->config->getSystemValue('debug', false) ? 'true' : 'false', "oc_isadmin" => $this->groupManager->isAdmin($uid) ? 'true' : 'false', @@ -126,6 +143,7 @@ class JSConfigHelper { "oc_webroot" => "\"".\OC::$WEBROOT."\"", "oc_appswebroots" => str_replace('\\/', '/', json_encode($apps_paths)), // Ugly unescape slashes waiting for better solution "datepickerFormatDate" => json_encode($this->l->l('jsdate', null)), + 'nc_lastLogin' => $lastConfirmTimestamp, "dayNames" => json_encode([ (string)$this->l->t('Sunday'), (string)$this->l->t('Monday'), diff --git a/lib/private/TemplateLayout.php b/lib/private/TemplateLayout.php index 7878737bdef..8919f14216e 100644 --- a/lib/private/TemplateLayout.php +++ b/lib/private/TemplateLayout.php @@ -148,6 +148,7 @@ class TemplateLayout extends \OC_Template { \OC::$server->getL10N('core'), \OC::$server->getThemingDefaults(), \OC::$server->getAppManager(), + \OC::$server->getSession(), \OC::$server->getUserSession()->getUser(), \OC::$server->getConfig(), \OC::$server->getGroupManager(), |