aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/twofactor_backupcodes/js/settingsview.js5
-rw-r--r--apps/twofactor_backupcodes/lib/Controller/SettingsController.php10
-rw-r--r--apps/twofactor_backupcodes/tests/Unit/Controller/SettingsControllerTest.php5
-rw-r--r--apps/workflowengine/js/admin.js10
-rw-r--r--apps/workflowengine/lib/Controller/FlowOperations.php6
-rw-r--r--core/Controller/LoginController.php37
-rw-r--r--core/Controller/OCJSController.php10
-rw-r--r--core/css/jquery.ocdialog.css1
-rw-r--r--core/js/js.js72
-rw-r--r--core/js/public/appconfig.js4
-rw-r--r--core/routes.php1
-rw-r--r--core/templates/layout.user.php8
-rw-r--r--lib/composer/composer/autoload_classmap.php1
-rw-r--r--lib/composer/composer/autoload_static.php1
-rw-r--r--lib/private/AppFramework/DependencyInjection/DIContainer.php1
-rw-r--r--lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php37
-rw-r--r--lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php14
-rw-r--r--lib/private/Template/JSConfigHelper.php20
-rw-r--r--lib/private/TemplateLayout.php1
-rw-r--r--settings/Controller/AuthSettingsController.php8
-rw-r--r--settings/Controller/ChangePasswordController.php1
-rw-r--r--settings/Controller/GroupsController.php2
-rw-r--r--settings/Controller/MailSettingsController.php6
-rw-r--r--settings/Controller/UsersController.php4
-rw-r--r--settings/ajax/setquota.php7
-rw-r--r--settings/ajax/togglegroups.php7
-rw-r--r--settings/ajax/togglesubadmins.php7
-rw-r--r--settings/js/admin.js47
-rw-r--r--settings/js/authtoken_view.js5
-rw-r--r--settings/js/personal.js2
-rw-r--r--settings/js/users/groups.js15
-rw-r--r--settings/js/users/users.js252
-rw-r--r--settings/templates/personal.php3
-rw-r--r--tests/Settings/Controller/AuthSettingsControllerTest.php4
-rw-r--r--tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php5
35 files changed, 513 insertions, 106 deletions
diff --git a/apps/twofactor_backupcodes/js/settingsview.js b/apps/twofactor_backupcodes/js/settingsview.js
index 224f5f4797f..76396023120 100644
--- a/apps/twofactor_backupcodes/js/settingsview.js
+++ b/apps/twofactor_backupcodes/js/settingsview.js
@@ -89,6 +89,11 @@
}.bind(this));
},
_onGenerateBackupCodes: function () {
+ if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this._onGenerateBackupCodes, this));
+ return;
+ }
+
// Hide old codes
this._enabled = false;
this.render();
diff --git a/apps/twofactor_backupcodes/lib/Controller/SettingsController.php b/apps/twofactor_backupcodes/lib/Controller/SettingsController.php
index fed7634643d..9b0b0fc57ba 100644
--- a/apps/twofactor_backupcodes/lib/Controller/SettingsController.php
+++ b/apps/twofactor_backupcodes/lib/Controller/SettingsController.php
@@ -59,15 +59,17 @@ class SettingsController extends Controller {
/**
* @NoAdminRequired
+ * @PasswordConfirmationRequired
+ *
* @return JSONResponse
*/
public function createCodes() {
$user = $this->userSession->getUser();
$codes = $this->storage->createCodes($user);
- return [
- 'codes' => $codes,
- 'state' => $this->storage->getBackupCodesState($user),
- ];
+ return new JSONResponse([
+ 'codes' => $codes,
+ 'state' => $this->storage->getBackupCodesState($user),
+ ]);
}
}
diff --git a/apps/twofactor_backupcodes/tests/Unit/Controller/SettingsControllerTest.php b/apps/twofactor_backupcodes/tests/Unit/Controller/SettingsControllerTest.php
index c7ac33467b3..8427c10e839 100644
--- a/apps/twofactor_backupcodes/tests/Unit/Controller/SettingsControllerTest.php
+++ b/apps/twofactor_backupcodes/tests/Unit/Controller/SettingsControllerTest.php
@@ -24,6 +24,7 @@ namespace OCA\TwoFactorBackupCodes\Tests\Unit\Controller;
use OCA\TwoFactorBackupCodes\Controller\SettingsController;
use OCA\TwoFactorBackupCodes\Service\BackupCodeStorage;
+use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
use OCP\IUser;
use OCP\IUserSession;
@@ -89,7 +90,9 @@ class SettingsControllerTest extends TestCase {
'codes' => $codes,
'state' => 'state',
];
- $this->assertEquals($expected, $this->controller->createCodes());
+ $response = $this->controller->createCodes();
+ $this->assertInstanceOf(JSONResponse::class, $response);
+ $this->assertEquals($expected, $response->getData());
}
}
diff --git a/apps/workflowengine/js/admin.js b/apps/workflowengine/js/admin.js
index 0357d741ab1..7f9a709ec14 100644
--- a/apps/workflowengine/js/admin.js
+++ b/apps/workflowengine/js/admin.js
@@ -163,6 +163,11 @@
}
},
delete: function() {
+ if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this.delete, this));
+ return;
+ }
+
this.model.destroy();
this.remove();
},
@@ -173,6 +178,11 @@
this.render();
},
save: function() {
+ if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this.save, this));
+ return;
+ }
+
var success = function(model, response, options) {
this.saving = false;
this.originalModel = JSON.parse(JSON.stringify(this.model));
diff --git a/apps/workflowengine/lib/Controller/FlowOperations.php b/apps/workflowengine/lib/Controller/FlowOperations.php
index 94b8b9ddc79..753aa2c26a7 100644
--- a/apps/workflowengine/lib/Controller/FlowOperations.php
+++ b/apps/workflowengine/lib/Controller/FlowOperations.php
@@ -58,6 +58,8 @@ class FlowOperations extends Controller {
}
/**
+ * @PasswordConfirmationRequired
+ *
* @param string $class
* @param string $name
* @param array[] $checks
@@ -75,6 +77,8 @@ class FlowOperations extends Controller {
}
/**
+ * @PasswordConfirmationRequired
+ *
* @param int $id
* @param string $name
* @param array[] $checks
@@ -92,6 +96,8 @@ class FlowOperations extends Controller {
}
/**
+ * @PasswordConfirmationRequired
+ *
* @param int $id
* @return JSONResponse
*/
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/jquery.ocdialog.css b/core/css/jquery.ocdialog.css
index 0e46ff20152..d1a42694589 100644
--- a/core/css/jquery.ocdialog.css
+++ b/core/css/jquery.ocdialog.css
@@ -22,7 +22,6 @@
.oc-dialog-buttonrow {
display: block;
background: transparent;
- position: absolute;
right: 0;
bottom: 0;
padding: 10px;
diff --git a/core/js/js.js b/core/js/js.js
index 54b103a7b7d..64c7dda31e2 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -1512,8 +1512,80 @@ function initCore() {
$(this).text(OC.Util.relativeModifiedDate(parseInt($(this).attr('data-timestamp'), 10)));
});
}, 30 * 1000);
+
+ OC.PasswordConfirmation.init();
}
+OC.PasswordConfirmation = {
+ callback: null,
+
+ init: function() {
+ $('.password-confirm-required').on('click', _.bind(this.requirePasswordConfirmation, this));
+ },
+
+ requiresPasswordConfirmation: function() {
+ var timeSinceLogin = moment.now() - nc_lastLogin * 1000;
+ return timeSinceLogin > 10 * 1000; // 30 minutes
+ return timeSinceLogin > 30 * 60 * 1000; // 30 minutes
+ },
+
+ /**
+ * @param {function} callback
+ */
+ requirePasswordConfirmation: function(callback) {
+ var self = this;
+
+ if (this.requiresPasswordConfirmation()) {
+ OC.dialogs.prompt(
+ t(
+ 'core',
+ 'This action requires you to confirm your password'
+ ),
+ t('core','Authentication required'),
+ function (result, password) {
+ if (result && password !== '') {
+ self._confirmPassword(password);
+ }
+ },
+ true,
+ t('core','Password'),
+ true
+ ).then(function() {
+ var $dialog = $('.oc-dialog:visible');
+ $dialog.find('.ui-icon').remove();
+
+ var $buttons = $dialog.find('button');
+ $buttons.eq(0).text(t('core', 'Cancel'));
+ $buttons.eq(1).text(t('core', 'Confirm'));
+ });
+ }
+
+ this.callback = callback;
+ },
+
+ _confirmPassword: function(password) {
+ var self = this;
+
+ $.ajax({
+ url: OC.generateUrl('/login/confirm'),
+ data: {
+ password: password
+ },
+ type: 'POST',
+ success: function(response) {
+ nc_lastLogin = response.lastLogin;
+
+ if (_.isFunction(self.callback)) {
+ self.callback();
+ }
+ },
+ error: function() {
+ OC.Notification.showTemporary(t('core', 'Failed to authenticate, try again'));
+ }
+ });
+ }
+};
+
$(document).ready(initCore);
/**
diff --git a/core/js/public/appconfig.js b/core/js/public/appconfig.js
index de04f334ca8..d84ddaab404 100644
--- a/core/js/public/appconfig.js
+++ b/core/js/public/appconfig.js
@@ -33,6 +33,10 @@ OCP.AppConfig = {
* @internal
*/
_call: function(method, endpoint, options) {
+ if ((method === 'post' || method === 'delete') && OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this._call, this, method, endpoint, options));
+ return;
+ }
$.ajax({
type: method.toUpperCase(),
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..1d0ac5fa146 100644
--- a/core/templates/layout.user.php
+++ b/core/templates/layout.user.php
@@ -146,6 +146,14 @@
</div>
</div></nav>
+ <div id="sudo-login-background" class="hidden"></div>
+ <div id="sudo-login-form" class="hidden">
+ <?php p($l->t('This action requires you to confirm your password:')); ?><br>
+ <input type="password" class="question" autocomplete="off" name="question" value=" <?php /* Hack against firefox ignoring autocomplete="off" */ ?>"
+ 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/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 69e8428fdea..dafa46bc996 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -265,6 +265,7 @@ return array(
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\AppNotEnabledException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php',
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\CrossSiteRequestForgeryException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/CrossSiteRequestForgeryException.php',
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\NotAdminException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/NotAdminException.php',
+ 'OC\\AppFramework\\Middleware\\Security\\Exceptions\\NotConfirmedException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php',
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\NotLoggedInException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/NotLoggedInException.php',
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\SecurityException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/SecurityException.php',
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\StrictCookieMissingException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/StrictCookieMissingException.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index c960a35d951..5b8356785bc 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -295,6 +295,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\AppNotEnabledException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php',
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\CrossSiteRequestForgeryException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/CrossSiteRequestForgeryException.php',
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\NotAdminException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/NotAdminException.php',
+ 'OC\\AppFramework\\Middleware\\Security\\Exceptions\\NotConfirmedException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php',
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\NotLoggedInException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/NotLoggedInException.php',
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\SecurityException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/SecurityException.php',
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\StrictCookieMissingException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/StrictCookieMissingException.php',
diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php
index e1516c47ed6..48c9b6f4589 100644
--- a/lib/private/AppFramework/DependencyInjection/DIContainer.php
+++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php
@@ -383,6 +383,7 @@ class DIContainer extends SimpleContainer implements IAppContainer {
$app->getServer()->getNavigationManager(),
$app->getServer()->getURLGenerator(),
$app->getServer()->getLogger(),
+ $app->getServer()->getSession(),
$c['AppName'],
$app->isLoggedIn(),
$app->isAdminUser(),
diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php
new file mode 100644
index 00000000000..1ecd463b004
--- /dev/null
+++ b/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\AppFramework\Middleware\Security\Exceptions;
+
+use OCP\AppFramework\Http;
+
+/**
+ * Class NotConfirmedException is thrown when a resource has been requested by a
+ * user that has not confirmed their password in the last 30 minutes.
+ *
+ * @package OC\AppFramework\Middleware\Security\Exceptions
+ */
+class NotConfirmedException extends SecurityException {
+ public function __construct() {
+ parent::__construct('Password confirmation is required', Http::STATUS_FORBIDDEN);
+ }
+}
diff --git a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
index 183e55740ea..d5f7a7660a7 100644
--- a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
@@ -32,6 +32,7 @@ namespace OC\AppFramework\Middleware\Security;
use OC\AppFramework\Middleware\Security\Exceptions\AppNotEnabledException;
use OC\AppFramework\Middleware\Security\Exceptions\CrossSiteRequestForgeryException;
use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException;
+use OC\AppFramework\Middleware\Security\Exceptions\NotConfirmedException;
use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException;
use OC\AppFramework\Middleware\Security\Exceptions\StrictCookieMissingException;
use OC\AppFramework\Utility\ControllerMethodReflector;
@@ -47,6 +48,7 @@ use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\OCSController;
use OCP\INavigationManager;
+use OCP\ISession;
use OCP\IURLGenerator;
use OCP\IRequest;
use OCP\ILogger;
@@ -73,6 +75,8 @@ class SecurityMiddleware extends Middleware {
private $urlGenerator;
/** @var ILogger */
private $logger;
+ /** @var ISession */
+ private $session;
/** @var bool */
private $isLoggedIn;
/** @var bool */
@@ -90,6 +94,7 @@ class SecurityMiddleware extends Middleware {
* @param INavigationManager $navigationManager
* @param IURLGenerator $urlGenerator
* @param ILogger $logger
+ * @param ISession $session
* @param string $appName
* @param bool $isLoggedIn
* @param bool $isAdminUser
@@ -102,6 +107,7 @@ class SecurityMiddleware extends Middleware {
INavigationManager $navigationManager,
IURLGenerator $urlGenerator,
ILogger $logger,
+ ISession $session,
$appName,
$isLoggedIn,
$isAdminUser,
@@ -114,6 +120,7 @@ class SecurityMiddleware extends Middleware {
$this->appName = $appName;
$this->urlGenerator = $urlGenerator;
$this->logger = $logger;
+ $this->session = $session;
$this->isLoggedIn = $isLoggedIn;
$this->isAdminUser = $isAdminUser;
$this->contentSecurityPolicyManager = $contentSecurityPolicyManager;
@@ -150,6 +157,13 @@ class SecurityMiddleware extends Middleware {
}
}
+ if ($this->reflector->hasAnnotation('PasswordConfirmationRequired')) {
+ $lastConfirm = (int) $this->session->get('last-password-confirm');
+ if ($lastConfirm < (time() - (30 * 60 + 15))) { // allow 15 seconds delay
+ throw new NotConfirmedException();
+ }
+ }
+
// Check for strict cookie requirement
if($this->reflector->hasAnnotation('StrictCookieRequired') || !$this->reflector->hasAnnotation('NoCSRFRequired')) {
if(!$this->request->passesStrictCookieCheck()) {
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(),
diff --git a/settings/Controller/AuthSettingsController.php b/settings/Controller/AuthSettingsController.php
index 2a4b30dbd76..57192e119a9 100644
--- a/settings/Controller/AuthSettingsController.php
+++ b/settings/Controller/AuthSettingsController.php
@@ -111,7 +111,9 @@ class AuthSettingsController extends Controller {
/**
* @NoAdminRequired
* @NoSubadminRequired
+ * @PasswordConfirmationRequired
*
+ * @param string $name
* @return JSONResponse
*/
public function create($name) {
@@ -138,11 +140,11 @@ class AuthSettingsController extends Controller {
$tokenData = $deviceToken->jsonSerialize();
$tokenData['canDelete'] = true;
- return [
+ return new JSONResponse([
'token' => $token,
'loginName' => $loginName,
- 'deviceToken' => $tokenData
- ];
+ 'deviceToken' => $tokenData,
+ ]);
}
private function getServiceNotAvailableResponse() {
diff --git a/settings/Controller/ChangePasswordController.php b/settings/Controller/ChangePasswordController.php
index e43d0d8f343..832cdbefdbe 100644
--- a/settings/Controller/ChangePasswordController.php
+++ b/settings/Controller/ChangePasswordController.php
@@ -131,6 +131,7 @@ class ChangePasswordController extends Controller {
/**
* @NoAdminRequired
+ * @PasswordConfirmationRequired
*
* @param string $username
* @param string $password
diff --git a/settings/Controller/GroupsController.php b/settings/Controller/GroupsController.php
index feed45b118e..8985a76ec95 100644
--- a/settings/Controller/GroupsController.php
+++ b/settings/Controller/GroupsController.php
@@ -95,6 +95,7 @@ class GroupsController extends Controller {
}
/**
+ * @PasswordConfirmationRequired
* @param string $id
* @return DataResponse
*/
@@ -128,6 +129,7 @@ class GroupsController extends Controller {
}
/**
+ * @PasswordConfirmationRequired
* @param string $id
* @return DataResponse
*/
diff --git a/settings/Controller/MailSettingsController.php b/settings/Controller/MailSettingsController.php
index 84423aa8e29..108ac3f393a 100644
--- a/settings/Controller/MailSettingsController.php
+++ b/settings/Controller/MailSettingsController.php
@@ -73,6 +73,9 @@ class MailSettingsController extends Controller {
/**
* Sets the email settings
+ *
+ * @PasswordConfirmationRequired
+ *
* @param string $mail_domain
* @param string $mail_from_address
* @param string $mail_smtpmode
@@ -116,6 +119,9 @@ class MailSettingsController extends Controller {
/**
* Store the credentials used for SMTP in the config
+ *
+ * @PasswordConfirmationRequired
+ *
* @param string $mail_smtpname
* @param string $mail_smtppassword
* @return array
diff --git a/settings/Controller/UsersController.php b/settings/Controller/UsersController.php
index fde29de3598..89831a66aba 100644
--- a/settings/Controller/UsersController.php
+++ b/settings/Controller/UsersController.php
@@ -301,6 +301,7 @@ class UsersController extends Controller {
/**
* @NoAdminRequired
+ * @PasswordConfirmationRequired
*
* @param string $username
* @param string $password
@@ -433,6 +434,7 @@ class UsersController extends Controller {
/**
* @NoAdminRequired
+ * @PasswordConfirmationRequired
*
* @param string $id
* @return DataResponse
@@ -495,6 +497,7 @@ class UsersController extends Controller {
*
* @NoAdminRequired
* @NoSubadminRequired
+ * @PasswordConfirmationRequired
*
* @param string $id
* @param string $mailAddress
@@ -615,6 +618,7 @@ class UsersController extends Controller {
*
* @NoAdminRequired
* @NoSubadminRequired
+ * @PasswordConfirmationRequired
*
* @param string $username
* @param string $displayName
diff --git a/settings/ajax/setquota.php b/settings/ajax/setquota.php
index eee1de407b9..0906102ec20 100644
--- a/settings/ajax/setquota.php
+++ b/settings/ajax/setquota.php
@@ -32,6 +32,13 @@
OC_JSON::checkSubAdminUser();
OCP\JSON::callCheck();
+$lastConfirm = (int) \OC::$server->getSession()->get('last-password-confirm');
+if ($lastConfirm < (time() - 30 * 60 + 15)) { // allow 15 seconds delay
+ $l = \OC::$server->getL10N('core');
+ OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required'))));
+ exit();
+}
+
$username = isset($_POST["username"]) ? (string)$_POST["username"] : '';
$isUserAccessible = false;
diff --git a/settings/ajax/togglegroups.php b/settings/ajax/togglegroups.php
index ff79861b811..b9958bef0c9 100644
--- a/settings/ajax/togglegroups.php
+++ b/settings/ajax/togglegroups.php
@@ -28,6 +28,13 @@
OC_JSON::checkSubAdminUser();
OCP\JSON::callCheck();
+$lastConfirm = (int) \OC::$server->getSession()->get('last-password-confirm');
+if ($lastConfirm < (time() - 30 * 60 + 15)) { // allow 15 seconds delay
+ $l = \OC::$server->getL10N('core');
+ OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required'))));
+ exit();
+}
+
$success = true;
$username = (string)$_POST['username'];
$group = (string)$_POST['group'];
diff --git a/settings/ajax/togglesubadmins.php b/settings/ajax/togglesubadmins.php
index 390e5c09ef3..5658a382410 100644
--- a/settings/ajax/togglesubadmins.php
+++ b/settings/ajax/togglesubadmins.php
@@ -24,6 +24,13 @@
OC_JSON::checkAdminUser();
OCP\JSON::callCheck();
+$lastConfirm = (int) \OC::$server->getSession()->get('last-password-confirm');
+if ($lastConfirm < (time() - 30 * 60 + 15)) { // allow 15 seconds delay
+ $l = \OC::$server->getL10N('core');
+ OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required'))));
+ exit();
+}
+
$username = (string)$_POST['username'];
$group = (string)$_POST['group'];
diff --git a/settings/js/admin.js b/settings/js/admin.js
index 7c2b507280f..094b12b2bab 100644
--- a/settings/js/admin.js
+++ b/settings/js/admin.js
@@ -172,21 +172,48 @@ $(document).ready(function(){
}
});
- $('#mail_general_settings_form').change(function(){
+ var changeEmailSettings = function() {
+ if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(changeEmailSettings);
+ return;
+ }
+
OC.msg.startSaving('#mail_settings_msg');
- var post = $( "#mail_general_settings_form" ).serialize();
- $.post(OC.generateUrl('/settings/admin/mailsettings'), post, function(data){
- OC.msg.finishedSaving('#mail_settings_msg', data);
+ $.ajax({
+ url: OC.generateUrl('/settings/admin/mailsettings'),
+ type: 'POST',
+ data: $('#mail_general_settings_form').serialize(),
+ success: function(data){
+ OC.msg.finishedSaving('#mail_settings_msg', data);
+ },
+ error: function(data){
+ OC.msg.finishedError('#mail_settings_msg', data.responseJSON.message);
+ }
});
- });
+ };
+
+ var toggleEmailCredentials = function() {
+ if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(toggleEmailCredentials);
+ return;
+ }
- $('#mail_credentials_settings_submit').click(function(){
OC.msg.startSaving('#mail_settings_msg');
- var post = $( "#mail_credentials_settings" ).serialize();
- $.post(OC.generateUrl('/settings/admin/mailsettings/credentials'), post, function(data){
- OC.msg.finishedSaving('#mail_settings_msg', data);
+ $.ajax({
+ url: OC.generateUrl('/settings/admin/mailsettings/credentials'),
+ type: 'POST',
+ data: $('#mail_credentials_settings').serialize(),
+ success: function(data){
+ OC.msg.finishedSaving('#mail_settings_msg', data);
+ },
+ error: function(data){
+ OC.msg.finishedError('#mail_settings_msg', data.responseJSON.message);
+ }
});
- });
+ };
+
+ $('#mail_general_settings_form').change(changeEmailSettings);
+ $('#mail_credentials_settings_submit').click(toggleEmailCredentials);
$('#sendtestemail').click(function(event){
event.preventDefault();
diff --git a/settings/js/authtoken_view.js b/settings/js/authtoken_view.js
index d8537c415ce..24593a04f53 100644
--- a/settings/js/authtoken_view.js
+++ b/settings/js/authtoken_view.js
@@ -299,6 +299,11 @@
},
_addAppPassword: function () {
+ if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this._addAppPassword, this));
+ return;
+ }
+
var _this = this;
this._toggleAddingToken(true);
diff --git a/settings/js/personal.js b/settings/js/personal.js
index aef67f719c0..c2cb437bd13 100644
--- a/settings/js/personal.js
+++ b/settings/js/personal.js
@@ -65,7 +65,7 @@ function changeEmailAddress () {
// for failure the first parameter is the result object
OC.msg.finishedSaving('#lostpassword .msg', result);
}).fail(function(result){
- OC.msg.finishedSaving('#lostpassword .msg', result.responseJSON);
+ OC.msg.finishedError('#lostpassword .msg', result.responseJSON.message);
});
}
diff --git a/settings/js/users/groups.js b/settings/js/users/groups.js
index 8f4d95432a8..cfe01c17530 100644
--- a/settings/js/users/groups.js
+++ b/settings/js/users/groups.js
@@ -128,6 +128,11 @@ GroupList = {
},
createGroup: function (groupname) {
+ if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this.createGroup, this, groupname));
+ return;
+ }
+
$.post(
OC.generateUrl('/settings/users/groups'),
{
@@ -278,10 +283,16 @@ GroupList = {
GroupList.show);
//when to mark user for delete
- $userGroupList.on('click', '.delete', function () {
+ var deleteAction = function () {
+ if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(deleteAction, this));
+ return;
+ }
+
// Call function for handling delete/undo
GroupDeleteHandler.mark(GroupList.getElementGID(this));
- });
+ };
+ $userGroupList.on('click', '.delete', deleteAction);
//delete a marked user when leaving the page
$(window).on('beforeunload', function () {
diff --git a/settings/js/users/users.js b/settings/js/users/users.js
index 3a357c0e9c4..7e70d98f091 100644
--- a/settings/js/users/users.js
+++ b/settings/js/users/users.js
@@ -353,6 +353,14 @@ var UserList = {
$userListBody.on('click', '.delete', function () {
// Call function for handling delete/undo
var uid = UserList.getUID(this);
+
+ if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(function() {
+ UserDeleteHandler.mark(uid);
+ });
+ return;
+ }
+
UserDeleteHandler.mark(uid);
});
@@ -405,6 +413,11 @@ var UserList = {
},
applyGroupSelect: function (element, user, checked) {
+ if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this.applySubadminSelect, this, element, user, checked));
+ return;
+ }
+
var $element = $(element);
var checkHandler = null;
@@ -467,6 +480,11 @@ var UserList = {
},
applySubadminSelect: function (element, user, checked) {
+ if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this.applySubadminSelect, this, element, user, checked));
+ return;
+ }
+
var $element = $(element);
var checkHandler = function (group) {
if (group === 'admin') {
@@ -478,7 +496,10 @@ var UserList = {
username: user,
group: group
},
- function () {
+ function (response) {
+ if (response.data.message) {
+ OC.Notification.show(response.data.message);
+ }
}
);
};
@@ -518,7 +539,7 @@ var UserList = {
OC.Notification.showTemporary(t('core', 'Invalid quota value "{val}"', {val: quota}));
return;
}
- UserList._updateQuota(uid, quota, function(returnedQuota){
+ UserList._updateQuota(uid, quota, function(returnedQuota) {
if (quota !== returnedQuota) {
$select.find(':selected').text(returnedQuota);
}
@@ -532,12 +553,21 @@ var UserList = {
* @param {Function} ready callback after save
*/
_updateQuota: function(uid, quota, ready) {
+ if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this._updateQuota, this, uid, quota, ready));
+ return;
+ }
+
$.post(
OC.filePath('settings', 'ajax', 'setquota.php'),
{username: uid, quota: quota},
function (result) {
- if (ready) {
- ready(result.data.quota);
+ if (result.status === 'error') {
+ OC.Notification.showTemporary(result.data.message);
+ } else {
+ if (ready) {
+ ready(result.data.quota);
+ }
}
}
);
@@ -635,6 +665,28 @@ $(document).ready(function () {
// TODO: move other init calls inside of initialize
UserList.initialize($('#userlist'));
+ var _submitPasswordChange = function(uid, password, recoveryPasswordVal, blurFunction) {
+ if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(function() {
+ _submitPasswordChange(uid, password, recoveryPasswordVal, blurFunction);
+ });
+ return;
+ }
+
+ $.post(
+ OC.generateUrl('/settings/users/changepassword'),
+ {username: uid, password: password, recoveryPassword: recoveryPasswordVal},
+ function (result) {
+ blurFunction();
+ if (result.status === 'success') {
+ OC.Notification.showTemporary(t('admin', 'Password successfully changed'));
+ } else {
+ OC.Notification.showTemporary(t('admin', result.data.message));
+ }
+ }
+ ).fail(blurFunction);
+ };
+
$userListBody.on('click', '.password', function (event) {
event.stopPropagation();
@@ -643,6 +695,12 @@ $(document).ready(function () {
var uid = UserList.getUID($td);
var $input = $('<input type="password">');
var isRestoreDisabled = UserList.getRestoreDisabled($td) === true;
+ var blurFunction = function () {
+ $(this).replaceWith($('<span>●●●●●●●</span>'));
+ $td.find('img').show();
+ // remove highlight class from users without recovery ability
+ $tr.removeClass('row-warning');
+ };
if(isRestoreDisabled) {
$tr.addClass('row-warning');
// add tipsy if the password change could cause data loss - no recovery enabled
@@ -657,34 +715,51 @@ $(document).ready(function () {
if (event.keyCode === 13) {
if ($(this).val().length > 0) {
var recoveryPasswordVal = $('input:password[id="recoveryPassword"]').val();
- $.post(
- OC.generateUrl('/settings/users/changepassword'),
- {username: uid, password: $(this).val(), recoveryPassword: recoveryPasswordVal},
- function (result) {
- if (result.status === 'success') {
- OC.Notification.showTemporary(t('admin', 'Password successfully changed'));
- } else {
- OC.Notification.showTemporary(t('admin', result.data.message));
- }
- }
- );
- $input.blur();
+ $input.off('blur');
+ _submitPasswordChange(uid, $(this).val(), recoveryPasswordVal, blurFunction);
} else {
$input.blur();
}
}
})
- .blur(function () {
- $(this).replaceWith($('<span>●●●●●●●</span>'));
- $td.find('img').show();
- // remove highlight class from users without recovery ability
- $tr.removeClass('row-warning');
- });
+ .blur(blurFunction);
});
$('input:password[id="recoveryPassword"]').keyup(function() {
OC.Notification.hide();
});
+ var _submitDisplayNameChange = function($tr, uid, displayName, blurFunction) {
+ var $div = $tr.find('div.avatardiv');
+ if ($div.length) {
+ $div.imageplaceholder(uid, displayName);
+ }
+
+ if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(function() {
+ _submitDisplayNameChange($tr, uid, displayName, blurFunction);
+ });
+ return;
+ }
+
+ $.ajax({
+ type: 'POST',
+ url: OC.generateUrl('/settings/users/{id}/displayName', {id: uid}),
+ data: {
+ username: uid,
+ displayName: displayName
+ }
+ }).success(function (result) {
+ if (result && result.status==='success' && $div.length){
+ $div.avatar(result.data.username, 32);
+ }
+ $tr.data('displayname', displayName);
+ blurFunction();
+ }).fail(function (result) {
+ OC.Notification.showTemporary(result.responseJSON.message);
+ $tr.find('.displayName input').blur(blurFunction);
+ });
+ };
+
$userListBody.on('click', '.displayName', function (event) {
event.stopPropagation();
var $td = $(this).closest('td');
@@ -692,6 +767,11 @@ $(document).ready(function () {
var uid = UserList.getUID($td);
var displayName = escapeHTML(UserList.getDisplayName($td));
var $input = $('<input type="text" value="' + displayName + '">');
+ var blurFunction = function() {
+ var displayName = $tr.data('displayname');
+ $input.replaceWith('<span>' + escapeHTML(displayName) + '</span>');
+ $td.find('img').show();
+ };
$td.find('img').hide();
$td.children('span').replaceWith($input);
$input
@@ -699,34 +779,53 @@ $(document).ready(function () {
.keypress(function (event) {
if (event.keyCode === 13) {
if ($(this).val().length > 0) {
- var $div = $tr.find('div.avatardiv');
- if ($div.length) {
- $div.imageplaceholder(uid, displayName);
- }
- $.post(
- OC.generateUrl('/settings/users/{id}/displayName', {id: uid}),
- {username: uid, displayName: $(this).val()},
- function (result) {
- if (result && result.status==='success' && $div.length){
- $div.avatar(result.data.username, 32);
- }
- }
- );
- var displayName = $input.val();
- $tr.data('displayname', displayName);
- $input.blur();
+ $input.off('blur');
+ _submitDisplayNameChange($tr, uid, $(this).val(), blurFunction);
} else {
$input.blur();
}
}
})
- .blur(function () {
- var displayName = $tr.data('displayname');
- $input.replaceWith('<span>' + escapeHTML(displayName) + '</span>');
- $td.find('img').show();
- });
+ .blur(blurFunction);
});
+ var _submitEmailChange = function($tr, $td, $input, uid, mailAddress, blurFunction) {
+ if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(function() {
+ _submitEmailChange($tr, $td, $input, uid, mailAddress, blurFunction);
+ });
+ return;
+ }
+
+ $.ajax({
+ type: 'PUT',
+ url: OC.generateUrl('/settings/users/{id}/mailAddress', {id: uid}),
+ data: {
+ mailAddress: mailAddress
+ }
+ }).success(function () {
+ // set data attribute to new value
+ // will in blur() be used to show the text instead of the input field
+ $tr.data('mailAddress', mailAddress);
+ $td.find('.loading-small').css('display', '');
+ $input.removeAttr('disabled')
+ .triggerHandler('blur'); // needed instead of $input.blur() for Firefox
+ blurFunction();
+ }).fail(function (result) {
+ if (!_.isUndefined(result.responseJSON.data)) {
+ OC.Notification.showTemporary(result.responseJSON.data.message);
+ } else if (!_.isUndefined(result.responseJSON.message)) {
+ OC.Notification.showTemporary(result.responseJSON.message);
+ } else {
+ OC.Notification.showTemporary(t('settings', 'Could not change the users email'));
+ }
+ $td.find('.loading-small').css('display', '');
+ $input.removeAttr('disabled')
+ .css('padding-right', '6px');
+ $input.blur(blurFunction);
+ });
+ };
+
$userListBody.on('click', '.mailAddress', function (event) {
event.stopPropagation();
var $td = $(this).closest('td');
@@ -734,6 +833,15 @@ $(document).ready(function () {
var uid = UserList.getUID($td);
var mailAddress = escapeHTML(UserList.getMailAddress($td));
var $input = $('<input type="text">').val(mailAddress);
+ var blurFunction = function() {
+ if($td.find('.loading-small').css('display') === 'inline-block') {
+ // in Chrome the blur event is fired too early by the browser - even if the request is still running
+ return;
+ }
+ var $span = $('<span>').text($tr.data('mailAddress'));
+ $input.replaceWith($span);
+ $td.find('img').show();
+ };
$td.children('span').replaceWith($input);
$td.find('img').hide();
$input
@@ -742,40 +850,14 @@ $(document).ready(function () {
if (event.keyCode === 13) {
// enter key
- var mailAddress = $input.val();
$td.find('.loading-small').css('display', 'inline-block');
$input.css('padding-right', '26px');
$input.attr('disabled', 'disabled');
- $.ajax({
- type: 'PUT',
- url: OC.generateUrl('/settings/users/{id}/mailAddress', {id: uid}),
- data: {
- mailAddress: $(this).val()
- }
- }).success(function () {
- // set data attribute to new value
- // will in blur() be used to show the text instead of the input field
- $tr.data('mailAddress', mailAddress);
- $td.find('.loading-small').css('display', '');
- $input.removeAttr('disabled')
- .triggerHandler('blur'); // needed instead of $input.blur() for Firefox
- }).fail(function (result) {
- OC.Notification.showTemporary(result.responseJSON.data.message);
- $td.find('.loading-small').css('display', '');
- $input.removeAttr('disabled')
- .css('padding-right', '6px');
- });
+ $input.off('blur');
+ _submitEmailChange($tr, $td, $input, uid, $(this).val(), blurFunction);
}
})
- .blur(function () {
- if($td.find('.loading-small').css('display') === 'inline-block') {
- // in Chrome the blur event is fired too early by the browser - even if the request is still running
- return;
- }
- var $span = $('<span>').text($tr.data('mailAddress'));
- $input.replaceWith($span);
- $td.find('img').show();
- });
+ .blur(blurFunction);
});
$('#newuser .groupsListContainer').on('click', function (event) {
@@ -796,8 +878,15 @@ $(document).ready(function () {
});
UserList._updateGroupListLabel($('#newuser .groups'), []);
- $('#newuser').submit(function (event) {
+ var _submitNewUserForm = function (event) {
event.preventDefault();
+ if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(function() {
+ _submitNewUserForm(event);
+ });
+ return;
+ }
+
var username = $('#newusername').val();
var password = $('#newuserpassword').val();
var email = $('#newemail').val();
@@ -866,7 +955,8 @@ $(document).ready(function () {
$('#newuser').get(0).reset();
});
});
- });
+ };
+ $('#newuser').submit(_submitNewUserForm);
if ($('#CheckboxStorageLocation').is(':checked')) {
$("#userlist .storageLocation").show();
@@ -874,11 +964,17 @@ $(document).ready(function () {
// Option to display/hide the "Storage location" column
$('#CheckboxStorageLocation').click(function() {
if ($('#CheckboxStorageLocation').is(':checked')) {
- $("#userlist .storageLocation").show();
- OCP.AppConfig.setValue('core', 'umgmt_show_storage_location', 'true');
+ OCP.AppConfig.setValue('core', 'umgmt_show_storage_location', 'true', {
+ success: function () {
+ $("#userlist .storageLocation").show();
+ }
+ });
} else {
- $("#userlist .storageLocation").hide();
- OCP.AppConfig.setValue('core', 'umgmt_show_storage_location', 'false');
+ OCP.AppConfig.setValue('core', 'umgmt_show_storage_location', 'false', {
+ success: function () {
+ $("#userlist .storageLocation").hide();
+ }
+ });
}
});
diff --git a/settings/templates/personal.php b/settings/templates/personal.php
index 69d3660477d..8f34d7b87b8 100644
--- a/settings/templates/personal.php
+++ b/settings/templates/personal.php
@@ -65,7 +65,7 @@ if($_['displayNameChangeSupported']) {
<h2>
<label for="displayName"><?php echo $l->t('Full name');?></label>
</h2>
- <input type="text" id="displayName" name="displayName"
+ <input type="text" id="displayName" name="displayName" class="password-confirm-required"
value="<?php p($_['displayName'])?>"
autocomplete="on" autocapitalize="off" autocorrect="off" />
<span class="msg"></span>
@@ -91,6 +91,7 @@ if($_['displayNameChangeSupported']) {
</h2>
<input type="email" name="email" id="email" value="<?php p($_['email']); ?>"
placeholder="<?php p($l->t('Your email address'));?>"
+ class="password-confirm-required"
autocomplete="on" autocapitalize="off" autocorrect="off" />
<span class="msg"></span><br />
<em><?php p($l->t('For password recovery and notifications'));?></em>
diff --git a/tests/Settings/Controller/AuthSettingsControllerTest.php b/tests/Settings/Controller/AuthSettingsControllerTest.php
index bb87958cf0f..7f4277acd73 100644
--- a/tests/Settings/Controller/AuthSettingsControllerTest.php
+++ b/tests/Settings/Controller/AuthSettingsControllerTest.php
@@ -153,7 +153,9 @@ class AuthSettingsControllerTest extends TestCase {
'deviceToken' => ['dummy' => 'dummy', 'canDelete' => true],
'loginName' => 'User13',
];
- $this->assertEquals($expected, $this->controller->create($name));
+ $response = $this->controller->create($name);
+ $this->assertInstanceOf(JSONResponse::class, $response);
+ $this->assertEquals($expected, $response->getData());
}
public function testCreateSessionNotAvailable() {
diff --git a/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php b/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php
index 1fdcf485c28..480bff5f59f 100644
--- a/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php
+++ b/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php
@@ -49,6 +49,7 @@ use OCP\IConfig;
use OCP\ILogger;
use OCP\INavigationManager;
use OCP\IRequest;
+use OCP\ISession;
use OCP\IURLGenerator;
use OCP\Security\ISecureRandom;
@@ -63,6 +64,8 @@ class SecurityMiddlewareTest extends \Test\TestCase {
private $secException;
/** @var SecurityException */
private $secAjaxException;
+ /** @var ISession|\PHPUnit_Framework_MockObject_MockObject */
+ private $session;
/** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */
private $request;
/** @var ControllerMethodReflector */
@@ -88,6 +91,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
$this->logger = $this->createMock(ILogger::class);
$this->navigationManager = $this->createMock(INavigationManager::class);
$this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->session = $this->createMock(ISession::class);
$this->request = $this->createMock(IRequest::class);
$this->contentSecurityPolicyManager = $this->createMock(ContentSecurityPolicyManager::class);
$this->csrfTokenManager = $this->createMock(CsrfTokenManager::class);
@@ -109,6 +113,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
$this->navigationManager,
$this->urlGenerator,
$this->logger,
+ $this->session,
'files',
$isLoggedIn,
$isAdminUser,