diff options
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, |