From ef00f9d2033f6de11e71bf3a909399cae6f73a9f Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Wed, 27 May 2020 13:35:48 +0200 Subject: [PATCH] Process password change controller through Slim --- .../controller/admin/PasswordController.php | 100 ++++++++++ .../admin/ShaarliAdminController.php | 59 ++++++ .../visitor/ShaarliVisitorController.php | 8 + .../OpenShaarliPasswordException.php | 18 ++ .../exceptions/ShaarliFrontException.php | 4 +- .../front/exceptions/WrongTokenException.php | 18 ++ application/render/PageBuilder.php | 26 ++- application/security/SessionManager.php | 4 + index.php | 54 +---- .../admin/FrontAdminControllerMockHelper.php | 3 +- .../controller/admin/LogoutControllerTest.php | 2 - .../admin/PasswordControllerTest.php | 186 ++++++++++++++++++ .../admin/SessionFilterControllerTest.php | 18 -- .../controller/admin/ToolsControllerTest.php | 4 - .../visitor/DailyControllerTest.php | 14 -- .../controller/visitor/FeedControllerTest.php | 6 - .../visitor/FrontControllerMockHelper.php | 8 +- .../visitor/LoginControllerTest.php | 10 - .../visitor/OpenSearchControllerTest.php | 2 - .../visitor/PictureWallControllerTest.php | 4 - .../visitor/ShaarliPublicControllerTest.php | 18 -- .../visitor/TagCloudControllerTest.php | 12 -- .../controller/visitor/TagControllerTest.php | 26 --- tpl/default/page.header.html | 28 +++ 24 files changed, 450 insertions(+), 182 deletions(-) create mode 100644 application/front/controller/admin/PasswordController.php create mode 100644 application/front/exceptions/OpenShaarliPasswordException.php create mode 100644 application/front/exceptions/WrongTokenException.php create mode 100644 tests/front/controller/admin/PasswordControllerTest.php diff --git a/application/front/controller/admin/PasswordController.php b/application/front/controller/admin/PasswordController.php new file mode 100644 index 00000000..6e8f0bcb --- /dev/null +++ b/application/front/controller/admin/PasswordController.php @@ -0,0 +1,100 @@ +assignView( + 'pagetitle', + t('Change password') .' - '. $this->container->conf->get('general.title', 'Shaarli') + ); + } + + /** + * GET /password - Displays the change password template + */ + public function index(Request $request, Response $response): Response + { + return $response->write($this->render('changepassword')); + } + + /** + * POST /password - Change admin password - existing and new passwords need to be provided. + */ + public function change(Request $request, Response $response): Response + { + $this->checkToken($request); + + if ($this->container->conf->get('security.open_shaarli', false)) { + throw new OpenShaarliPasswordException(); + } + + $oldPassword = $request->getParam('oldpassword'); + $newPassword = $request->getParam('setpassword'); + + if (empty($newPassword) || empty($oldPassword)) { + $this->saveErrorMessage(t('You must provide the current and new password to change it.')); + + return $response + ->withStatus(400) + ->write($this->render('changepassword')) + ; + } + + // Make sure old password is correct. + $oldHash = sha1( + $oldPassword . + $this->container->conf->get('credentials.login') . + $this->container->conf->get('credentials.salt') + ); + + if ($oldHash !== $this->container->conf->get('credentials.hash')) { + $this->saveErrorMessage(t('The old password is not correct.')); + + return $response + ->withStatus(400) + ->write($this->render('changepassword')) + ; + } + + // Save new password + // Salt renders rainbow-tables attacks useless. + $this->container->conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand())); + $this->container->conf->set( + 'credentials.hash', + sha1( + $newPassword + . $this->container->conf->get('credentials.login') + . $this->container->conf->get('credentials.salt') + ) + ); + + try { + $this->container->conf->write($this->container->loginManager->isLoggedIn()); + } catch (Throwable $e) { + throw new ShaarliFrontException($e->getMessage(), 500, $e); + } + + $this->saveSuccessMessage(t('Your password has been changed')); + + return $response->write($this->render('changepassword')); + } +} diff --git a/application/front/controller/admin/ShaarliAdminController.php b/application/front/controller/admin/ShaarliAdminController.php index ea703f62..3385006c 100644 --- a/application/front/controller/admin/ShaarliAdminController.php +++ b/application/front/controller/admin/ShaarliAdminController.php @@ -7,7 +7,19 @@ namespace Shaarli\Front\Controller\Admin; use Shaarli\Container\ShaarliContainer; use Shaarli\Front\Controller\Visitor\ShaarliVisitorController; use Shaarli\Front\Exception\UnauthorizedException; +use Shaarli\Front\Exception\WrongTokenException; +use Shaarli\Security\SessionManager; +use Slim\Http\Request; +/** + * Class ShaarliAdminController + * + * All admin controllers (for logged in users) MUST extend this abstract class. + * It makes sure that the user is properly logged in, and otherwise throw an exception + * which will redirect to the login page. + * + * @package Shaarli\Front\Controller\Admin + */ abstract class ShaarliAdminController extends ShaarliVisitorController { public function __construct(ShaarliContainer $container) @@ -18,4 +30,51 @@ abstract class ShaarliAdminController extends ShaarliVisitorController throw new UnauthorizedException(); } } + + /** + * Any persistent action to the config or data store must check the XSRF token validity. + */ + protected function checkToken(Request $request): void + { + if (!$this->container->sessionManager->checkToken($request->getParam('token'))) { + throw new WrongTokenException(); + } + } + + /** + * Save a SUCCESS message in user session, which will be displayed on any template page. + */ + protected function saveSuccessMessage(string $message): void + { + $this->saveMessage(SessionManager::KEY_SUCCESS_MESSAGES, $message); + } + + /** + * Save a WARNING message in user session, which will be displayed on any template page. + */ + protected function saveWarningMessage(string $message): void + { + $this->saveMessage(SessionManager::KEY_WARNING_MESSAGES, $message); + } + + /** + * Save an ERROR message in user session, which will be displayed on any template page. + */ + protected function saveErrorMessage(string $message): void + { + $this->saveMessage(SessionManager::KEY_ERROR_MESSAGES, $message); + } + + /** + * Use the sessionManager to save the provided message using the proper type. + * + * @param string $type successed/warnings/errors + */ + protected function saveMessage(string $type, string $message): void + { + $messages = $this->container->sessionManager->getSessionParameter($type) ?? []; + $messages[] = $message; + + $this->container->sessionManager->setSessionParameter($type, $messages); + } } diff --git a/application/front/controller/visitor/ShaarliVisitorController.php b/application/front/controller/visitor/ShaarliVisitorController.php index 655b3baa..f12915c1 100644 --- a/application/front/controller/visitor/ShaarliVisitorController.php +++ b/application/front/controller/visitor/ShaarliVisitorController.php @@ -9,6 +9,14 @@ use Shaarli\Container\ShaarliContainer; use Slim\Http\Request; use Slim\Http\Response; +/** + * Class ShaarliVisitorController + * + * All controllers accessible by visitors (non logged in users) should extend this abstract class. + * Contains a few helper function for template rendering, plugins, etc. + * + * @package Shaarli\Front\Controller\Visitor + */ abstract class ShaarliVisitorController { /** @var ShaarliContainer */ diff --git a/application/front/exceptions/OpenShaarliPasswordException.php b/application/front/exceptions/OpenShaarliPasswordException.php new file mode 100644 index 00000000..a6f0b3ae --- /dev/null +++ b/application/front/exceptions/OpenShaarliPasswordException.php @@ -0,0 +1,18 @@ +tpl->assign('thumbnails_width', $this->conf->get('thumbnails.width')); $this->tpl->assign('thumbnails_height', $this->conf->get('thumbnails.height')); - if (!empty($_SESSION['warnings'])) { - $this->tpl->assign('global_warnings', $_SESSION['warnings']); - unset($_SESSION['warnings']); - } - $this->tpl->assign('formatter', $this->conf->get('formatter', 'default')); // To be removed with a proper theme configuration. $this->tpl->assign('conf', $this->conf); } + protected function finalize(): void + { + // TODO: use the SessionManager + $messageKeys = [ + SessionManager::KEY_SUCCESS_MESSAGES, + SessionManager::KEY_WARNING_MESSAGES, + SessionManager::KEY_ERROR_MESSAGES + ]; + foreach ($messageKeys as $messageKey) { + if (!empty($_SESSION[$messageKey])) { + $this->tpl->assign('global_' . $messageKey, $_SESSION[$messageKey]); + unset($_SESSION[$messageKey]); + } + } + } + /** * The following assign() method is basically the same as RainTPL (except lazy loading) * @@ -196,6 +208,8 @@ class PageBuilder $this->initialize(); } + $this->finalize(); + $this->tpl->draw($page); } @@ -213,6 +227,8 @@ class PageBuilder $this->initialize(); } + $this->finalize(); + return $this->tpl->draw($page, true); } diff --git a/application/security/SessionManager.php b/application/security/SessionManager.php index 8b77d362..0ac17d9a 100644 --- a/application/security/SessionManager.php +++ b/application/security/SessionManager.php @@ -12,6 +12,10 @@ class SessionManager public const KEY_VISIBILITY = 'visibility'; public const KEY_UNTAGGED_ONLY = 'untaggedonly'; + public const KEY_SUCCESS_MESSAGES = 'successes'; + public const KEY_WARNING_MESSAGES = 'warnings'; + public const KEY_ERROR_MESSAGES = 'errors'; + /** @var int Session expiration timeout, in seconds */ public static $SHORT_TIMEOUT = 3600; // 1 hour diff --git a/index.php b/index.php index f4c8b391..ae56b800 100644 --- a/index.php +++ b/index.php @@ -507,56 +507,8 @@ function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionM // -------- User wants to change his/her password. if ($targetPage == Router::$PAGE_CHANGEPASSWORD) { - if ($conf->get('security.open_shaarli')) { - die(t('You are not supposed to change a password on an Open Shaarli.')); - } - - if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) { - if (!$sessionManager->checkToken($_POST['token'])) { - die(t('Wrong token.')); // Go away! - } - - // Make sure old password is correct. - $oldhash = sha1( - $_POST['oldpassword'].$conf->get('credentials.login').$conf->get('credentials.salt') - ); - if ($oldhash != $conf->get('credentials.hash')) { - echo ''; - exit; - } - // Save new password - // Salt renders rainbow-tables attacks useless. - $conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand())); - $conf->set( - 'credentials.hash', - sha1( - $_POST['setpassword'] - . $conf->get('credentials.login') - . $conf->get('credentials.salt') - ) - ); - try { - $conf->write($loginManager->isLoggedIn()); - } catch (Exception $e) { - error_log( - 'ERROR while writing config file after changing password.' . PHP_EOL . - $e->getMessage() - ); - - // TODO: do not handle exceptions/errors in JS. - echo ''; - exit; - } - echo ''; - exit; - } else { - // show the change password form. - $PAGE->assign('pagetitle', t('Change password') .' - '. $conf->get('general.title', 'Shaarli')); - $PAGE->renderPage('changepassword'); - exit; - } + header('Location: ./password'); + exit; } // -------- User wants to change configuration @@ -1504,6 +1456,8 @@ $app->group('', function () { /* -- LOGGED IN -- */ $this->get('/logout', '\Shaarli\Front\Controller\Admin\LogoutController:index')->setName('logout'); $this->get('/tools', '\Shaarli\Front\Controller\Admin\ToolsController:index')->setName('tools'); + $this->get('/password', '\Shaarli\Front\Controller\Admin\PasswordController:index')->setName('password'); + $this->post('/password', '\Shaarli\Front\Controller\Admin\PasswordController:change')->setName('changePassword'); $this ->get('/links-per-page', '\Shaarli\Front\Controller\Admin\SessionFilterController:linksPerPage') diff --git a/tests/front/controller/admin/FrontAdminControllerMockHelper.php b/tests/front/controller/admin/FrontAdminControllerMockHelper.php index 94581c09..bd40c0c7 100644 --- a/tests/front/controller/admin/FrontAdminControllerMockHelper.php +++ b/tests/front/controller/admin/FrontAdminControllerMockHelper.php @@ -6,7 +6,6 @@ namespace Shaarli\Front\Controller\Admin; use Shaarli\Container\ShaarliTestContainer; use Shaarli\Front\Controller\Visitor\FrontControllerMockHelper; -use Shaarli\Security\LoginManager; /** * Trait FrontControllerMockHelper @@ -28,7 +27,7 @@ trait FrontAdminControllerMockHelper { $this->parentCreateContainer(); - $this->container->loginManager = $this->createMock(LoginManager::class); $this->container->loginManager->method('isLoggedIn')->willReturn(true); + $this->container->sessionManager->method('checkToken')->willReturn(true); } } diff --git a/tests/front/controller/admin/LogoutControllerTest.php b/tests/front/controller/admin/LogoutControllerTest.php index ba681b16..78a0fe73 100644 --- a/tests/front/controller/admin/LogoutControllerTest.php +++ b/tests/front/controller/admin/LogoutControllerTest.php @@ -35,8 +35,6 @@ class LogoutControllerTest extends TestCase public function testValidControllerInvoke(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); diff --git a/tests/front/controller/admin/PasswordControllerTest.php b/tests/front/controller/admin/PasswordControllerTest.php new file mode 100644 index 00000000..7262243e --- /dev/null +++ b/tests/front/controller/admin/PasswordControllerTest.php @@ -0,0 +1,186 @@ +createContainer(); + $this->assignTemplateVars($this->assignedVariables); + + $this->controller = new PasswordController($this->container); + } + + /** + * Test displaying the change password page. + */ + public function testGetPage(): void + { + $request = $this->createMock(Request::class); + $response = new Response(); + + $result = $this->controller->index($request, $response); + + static::assertSame(200, $result->getStatusCode()); + static::assertSame('changepassword', (string) $result->getBody()); + static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']); + } + + /** + * Change the password with valid parameters + */ + public function testPostNewPasswordDefault(): void + { + $request = $this->createMock(Request::class); + $request->method('getParam')->willReturnCallback(function (string $key): string { + if ('oldpassword' === $key) { + return 'old'; + } + if ('setpassword' === $key) { + return 'new'; + } + + return $key; + }); + $response = new Response(); + + $this->container->conf = $this->createMock(ConfigManager::class); + $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) { + if ('credentials.hash' === $key) { + return sha1('old' . 'credentials.login' . 'credentials.salt'); + } + + return strpos($key, 'credentials') !== false ? $key : $default; + }); + $this->container->conf->expects(static::once())->method('write')->with(true); + + $this->container->conf + ->method('set') + ->willReturnCallback(function (string $key, string $value) { + if ('credentials.hash' === $key) { + static::assertSame(sha1('new' . 'credentials.login' . 'credentials.salt'), $value); + } + }) + ; + + $result = $this->controller->change($request, $response); + + static::assertSame(200, $result->getStatusCode()); + static::assertSame('changepassword', (string) $result->getBody()); + static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']); + } + + /** + * Change the password with a wrong existing password + */ + public function testPostNewPasswordWrongOldPassword(): void + { + $request = $this->createMock(Request::class); + $request->method('getParam')->willReturnCallback(function (string $key): string { + if ('oldpassword' === $key) { + return 'wrong'; + } + if ('setpassword' === $key) { + return 'new'; + } + + return $key; + }); + $response = new Response(); + + $this->container->conf = $this->createMock(ConfigManager::class); + $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) { + if ('credentials.hash' === $key) { + return sha1('old' . 'credentials.login' . 'credentials.salt'); + } + + return strpos($key, 'credentials') !== false ? $key : $default; + }); + + $this->container->conf->expects(static::never())->method('set'); + $this->container->conf->expects(static::never())->method('write'); + + $this->container->sessionManager + ->expects(static::once()) + ->method('setSessionParameter') + ->with(SessionManager::KEY_ERROR_MESSAGES, ['The old password is not correct.']) + ; + + $result = $this->controller->change($request, $response); + + static::assertSame(400, $result->getStatusCode()); + static::assertSame('changepassword', (string) $result->getBody()); + static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']); + } + + /** + * Change the password with a wrong existing password + */ + public function testPostNewPasswordWrongToken(): void + { + $this->container->sessionManager = $this->createMock(SessionManager::class); + $this->container->sessionManager->method('checkToken')->willReturn(false); + + $this->container->conf->expects(static::never())->method('set'); + $this->container->conf->expects(static::never())->method('write'); + + $request = $this->createMock(Request::class); + $response = new Response(); + + $this->expectException(WrongTokenException::class); + + $this->controller->change($request, $response); + } + + /** + * Change the password with an empty new password + */ + public function testPostNewEmptyPassword(): void + { + $this->container->sessionManager + ->expects(static::once()) + ->method('setSessionParameter') + ->with(SessionManager::KEY_ERROR_MESSAGES, ['You must provide the current and new password to change it.']) + ; + + $this->container->conf->expects(static::never())->method('set'); + $this->container->conf->expects(static::never())->method('write'); + + $request = $this->createMock(Request::class); + $request->method('getParam')->willReturnCallback(function (string $key): string { + if ('oldpassword' === $key) { + return 'old'; + } + if ('setpassword' === $key) { + return ''; + } + + return $key; + }); + $response = new Response(); + + $result = $this->controller->change($request, $response); + + static::assertSame(400, $result->getStatusCode()); + static::assertSame('changepassword', (string) $result->getBody()); + static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']); + } +} diff --git a/tests/front/controller/admin/SessionFilterControllerTest.php b/tests/front/controller/admin/SessionFilterControllerTest.php index f50f2fc2..096963cf 100644 --- a/tests/front/controller/admin/SessionFilterControllerTest.php +++ b/tests/front/controller/admin/SessionFilterControllerTest.php @@ -30,8 +30,6 @@ class SessionFilterControllerTest extends TestCase */ public function testLinksPerPage(): void { - $this->createValidContainerMockSet(); - $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/subfolder/controller/?searchtag=abc']; $request = $this->createMock(Request::class); @@ -62,8 +60,6 @@ class SessionFilterControllerTest extends TestCase */ public function testLinksPerPageNotValid(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $request->method('getUri')->willReturnCallback(function (): Uri { $uri = $this->createMock(Uri::class); @@ -92,8 +88,6 @@ class SessionFilterControllerTest extends TestCase */ public function testVisibility(): void { - $this->createValidContainerMockSet(); - $arg = ['visibility' => 'private']; $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/subfolder/controller/?searchtag=abc']; @@ -126,8 +120,6 @@ class SessionFilterControllerTest extends TestCase */ public function testVisibilityToggleOff(): void { - $this->createValidContainerMockSet(); - $arg = ['visibility' => 'private']; $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/subfolder/controller/?searchtag=abc']; @@ -169,8 +161,6 @@ class SessionFilterControllerTest extends TestCase */ public function testVisibilitySwitch(): void { - $this->createValidContainerMockSet(); - $arg = ['visibility' => 'private']; $this->container->loginManager->method('isLoggedIn')->willReturn(true); @@ -206,8 +196,6 @@ class SessionFilterControllerTest extends TestCase */ public function testVisibilityInvalidValue(): void { - $this->createValidContainerMockSet(); - $arg = ['visibility' => 'test']; $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/subfolder/controller/?searchtag=abc']; @@ -244,8 +232,6 @@ class SessionFilterControllerTest extends TestCase */ public function testVisibilityLoggedOut(): void { - $this->createValidContainerMockSet(); - $arg = ['visibility' => 'test']; $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/subfolder/controller/?searchtag=abc']; @@ -283,8 +269,6 @@ class SessionFilterControllerTest extends TestCase */ public function testUntaggedOnly(): void { - $this->createValidContainerMockSet(); - $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/subfolder/controller/?searchtag=abc']; $request = $this->createMock(Request::class); @@ -314,8 +298,6 @@ class SessionFilterControllerTest extends TestCase */ public function testUntaggedOnlyToggleOff(): void { - $this->createValidContainerMockSet(); - $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/subfolder/controller/?searchtag=abc']; $request = $this->createMock(Request::class); diff --git a/tests/front/controller/admin/ToolsControllerTest.php b/tests/front/controller/admin/ToolsControllerTest.php index 47c5746e..fc756f0f 100644 --- a/tests/front/controller/admin/ToolsControllerTest.php +++ b/tests/front/controller/admin/ToolsControllerTest.php @@ -24,8 +24,6 @@ class ToolsControllerTestControllerTest extends TestCase public function testDefaultInvokeWithHttps(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); @@ -49,8 +47,6 @@ class ToolsControllerTestControllerTest extends TestCase public function testDefaultInvokeWithoutHttps(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); diff --git a/tests/front/controller/visitor/DailyControllerTest.php b/tests/front/controller/visitor/DailyControllerTest.php index 6ff769fc..872420fd 100644 --- a/tests/front/controller/visitor/DailyControllerTest.php +++ b/tests/front/controller/visitor/DailyControllerTest.php @@ -27,8 +27,6 @@ class DailyControllerTest extends TestCase public function testValidIndexControllerInvokeDefault(): void { - $this->createValidContainerMockSet(); - $currentDay = new \DateTimeImmutable('2020-05-13'); $request = $this->createMock(Request::class); @@ -169,8 +167,6 @@ class DailyControllerTest extends TestCase */ public function testValidIndexControllerInvokeNoFutureOrPast(): void { - $this->createValidContainerMockSet(); - $currentDay = new \DateTimeImmutable('2020-05-13'); $request = $this->createMock(Request::class); @@ -243,8 +239,6 @@ class DailyControllerTest extends TestCase */ public function testValidIndexControllerInvokeHeightAdjustment(): void { - $this->createValidContainerMockSet(); - $currentDay = new \DateTimeImmutable('2020-05-13'); $request = $this->createMock(Request::class); @@ -314,8 +308,6 @@ class DailyControllerTest extends TestCase */ public function testValidIndexControllerInvokeNoBookmark(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); @@ -363,8 +355,6 @@ class DailyControllerTest extends TestCase */ public function testValidRssControllerInvokeDefault(): void { - $this->createValidContainerMockSet(); - $dates = [ new \DateTimeImmutable('2020-05-17'), new \DateTimeImmutable('2020-05-15'), @@ -439,8 +429,6 @@ class DailyControllerTest extends TestCase */ public function testValidRssControllerInvokeTriggerCache(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); @@ -465,8 +453,6 @@ class DailyControllerTest extends TestCase */ public function testValidRssControllerInvokeNoBookmark(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); diff --git a/tests/front/controller/visitor/FeedControllerTest.php b/tests/front/controller/visitor/FeedControllerTest.php index fd4679ea..fb417e2a 100644 --- a/tests/front/controller/visitor/FeedControllerTest.php +++ b/tests/front/controller/visitor/FeedControllerTest.php @@ -30,8 +30,6 @@ class FeedControllerTest extends TestCase */ public function testDefaultRssController(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); @@ -71,8 +69,6 @@ class FeedControllerTest extends TestCase */ public function testDefaultAtomController(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); @@ -112,8 +108,6 @@ class FeedControllerTest extends TestCase */ public function testAtomControllerWithParameters(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $request->method('getParams')->willReturn(['parameter' => 'value']); $response = new Response(); diff --git a/tests/front/controller/visitor/FrontControllerMockHelper.php b/tests/front/controller/visitor/FrontControllerMockHelper.php index bc3266b5..d16b6949 100644 --- a/tests/front/controller/visitor/FrontControllerMockHelper.php +++ b/tests/front/controller/visitor/FrontControllerMockHelper.php @@ -31,18 +31,12 @@ trait FrontControllerMockHelper protected $container; /** - * Mock the container instance + * Mock the container instance and initialize container's services used by tests */ protected function createContainer(): void { $this->container = $this->createMock(ShaarliTestContainer::class); - } - /** - * Initialize container's services used by tests - */ - protected function createValidContainerMockSet(): void - { $this->container->loginManager = $this->createMock(LoginManager::class); // Config diff --git a/tests/front/controller/visitor/LoginControllerTest.php b/tests/front/controller/visitor/LoginControllerTest.php index 9d223316..faa8ac71 100644 --- a/tests/front/controller/visitor/LoginControllerTest.php +++ b/tests/front/controller/visitor/LoginControllerTest.php @@ -26,8 +26,6 @@ class LoginControllerTest extends TestCase public function testValidControllerInvoke(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $request->expects(static::once())->method('getServerParam')->willReturn('> referer'); $response = new Response(); @@ -57,8 +55,6 @@ class LoginControllerTest extends TestCase public function testValidControllerInvokeWithUserName(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $request->expects(static::once())->method('getServerParam')->willReturn('> referer'); $request->expects(static::exactly(2))->method('getParam')->willReturn('myUser>'); @@ -90,8 +86,6 @@ class LoginControllerTest extends TestCase public function testLoginControllerWhileLoggedIn(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); @@ -106,8 +100,6 @@ class LoginControllerTest extends TestCase public function testLoginControllerOpenShaarli(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); @@ -129,8 +121,6 @@ class LoginControllerTest extends TestCase public function testLoginControllerWhileBanned(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); diff --git a/tests/front/controller/visitor/OpenSearchControllerTest.php b/tests/front/controller/visitor/OpenSearchControllerTest.php index 52475318..5f9f5b12 100644 --- a/tests/front/controller/visitor/OpenSearchControllerTest.php +++ b/tests/front/controller/visitor/OpenSearchControllerTest.php @@ -24,8 +24,6 @@ class OpenSearchControllerTest extends TestCase public function testOpenSearchController(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); diff --git a/tests/front/controller/visitor/PictureWallControllerTest.php b/tests/front/controller/visitor/PictureWallControllerTest.php index 7ac842cb..3dc3f292 100644 --- a/tests/front/controller/visitor/PictureWallControllerTest.php +++ b/tests/front/controller/visitor/PictureWallControllerTest.php @@ -28,8 +28,6 @@ class PictureWallControllerTest extends TestCase public function testValidControllerInvokeDefault(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $request->expects(static::once())->method('getQueryParams')->willReturn([]); $response = new Response(); @@ -106,8 +104,6 @@ class PictureWallControllerTest extends TestCase { $this->expectException(ThumbnailsDisabledException::class); - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); diff --git a/tests/front/controller/visitor/ShaarliPublicControllerTest.php b/tests/front/controller/visitor/ShaarliPublicControllerTest.php index e2e88da3..1f7d57ad 100644 --- a/tests/front/controller/visitor/ShaarliPublicControllerTest.php +++ b/tests/front/controller/visitor/ShaarliPublicControllerTest.php @@ -67,8 +67,6 @@ class ShaarliControllerTest extends TestCase public function testAssignView(): void { - $this->createValidContainerMockSet(); - $this->assignTemplateVars($this->assignedValues); $self = $this->controller->assignView('variableName', 'variableValue'); @@ -79,8 +77,6 @@ class ShaarliControllerTest extends TestCase public function testRender(): void { - $this->createValidContainerMockSet(); - $this->assignTemplateVars($this->assignedValues); $this->container->bookmarkService @@ -120,8 +116,6 @@ class ShaarliControllerTest extends TestCase */ public function testRedirectFromRefererDefault(): void { - $this->createValidContainerMockSet(); - $this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2'; $response = new Response(); @@ -137,8 +131,6 @@ class ShaarliControllerTest extends TestCase */ public function testRedirectFromRefererWithUnmatchedLoopTerm(): void { - $this->createValidContainerMockSet(); - $this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2'; $response = new Response(); @@ -154,8 +146,6 @@ class ShaarliControllerTest extends TestCase */ public function testRedirectFromRefererWithMatchingLoopTermInPath(): void { - $this->createValidContainerMockSet(); - $this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2'; $response = new Response(); @@ -171,8 +161,6 @@ class ShaarliControllerTest extends TestCase */ public function testRedirectFromRefererWithMatchingLoopTermInQueryParam(): void { - $this->createValidContainerMockSet(); - $this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2'; $response = new Response(); @@ -189,8 +177,6 @@ class ShaarliControllerTest extends TestCase */ public function testRedirectFromRefererWithMatchingLoopTermInQueryValue(): void { - $this->createValidContainerMockSet(); - $this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2'; $response = new Response(); @@ -207,8 +193,6 @@ class ShaarliControllerTest extends TestCase */ public function testRedirectFromRefererWithLoopTermInDomain(): void { - $this->createValidContainerMockSet(); - $this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2'; $response = new Response(); @@ -225,8 +209,6 @@ class ShaarliControllerTest extends TestCase */ public function testRedirectFromRefererWithMatchingClearedParam(): void { - $this->createValidContainerMockSet(); - $this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2'; $response = new Response(); diff --git a/tests/front/controller/visitor/TagCloudControllerTest.php b/tests/front/controller/visitor/TagCloudControllerTest.php index e636d496..9a6a4bc0 100644 --- a/tests/front/controller/visitor/TagCloudControllerTest.php +++ b/tests/front/controller/visitor/TagCloudControllerTest.php @@ -28,8 +28,6 @@ class TagCloudControllerTest extends TestCase */ public function testValidCloudControllerInvokeDefault(): void { - $this->createValidContainerMockSet(); - $allTags = [ 'ghi' => 1, 'abc' => 3, @@ -94,8 +92,6 @@ class TagCloudControllerTest extends TestCase */ public function testValidCloudControllerInvokeWithParameters(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $request ->method('getQueryParam') @@ -161,8 +157,6 @@ class TagCloudControllerTest extends TestCase */ public function testEmptyCloud(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); @@ -209,8 +203,6 @@ class TagCloudControllerTest extends TestCase */ public function testValidListControllerInvokeDefault(): void { - $this->createValidContainerMockSet(); - $allTags = [ 'def' => 12, 'abc' => 3, @@ -271,8 +263,6 @@ class TagCloudControllerTest extends TestCase */ public function testValidListControllerInvokeWithParameters(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $request ->method('getQueryParam') @@ -336,8 +326,6 @@ class TagCloudControllerTest extends TestCase */ public function testEmptyList(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); diff --git a/tests/front/controller/visitor/TagControllerTest.php b/tests/front/controller/visitor/TagControllerTest.php index 9a2b1f71..1242a2e9 100644 --- a/tests/front/controller/visitor/TagControllerTest.php +++ b/tests/front/controller/visitor/TagControllerTest.php @@ -23,8 +23,6 @@ class TagControllerTest extends TestCase public function testAddTagWithReferer(): void { - $this->createValidContainerMockSet(); - $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/']; $request = $this->createMock(Request::class); @@ -41,8 +39,6 @@ class TagControllerTest extends TestCase public function testAddTagWithRefererAndExistingSearch(): void { - $this->createValidContainerMockSet(); - $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def']; $request = $this->createMock(Request::class); @@ -59,8 +55,6 @@ class TagControllerTest extends TestCase public function testAddTagWithoutRefererAndExistingSearch(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); @@ -75,8 +69,6 @@ class TagControllerTest extends TestCase public function testAddTagRemoveLegacyQueryParam(): void { - $this->createValidContainerMockSet(); - $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def&addtag=abc']; $request = $this->createMock(Request::class); @@ -93,8 +85,6 @@ class TagControllerTest extends TestCase public function testAddTagResetPagination(): void { - $this->createValidContainerMockSet(); - $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def&page=12']; $request = $this->createMock(Request::class); @@ -111,8 +101,6 @@ class TagControllerTest extends TestCase public function testAddTagWithRefererAndEmptySearch(): void { - $this->createValidContainerMockSet(); - $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=']; $request = $this->createMock(Request::class); @@ -129,8 +117,6 @@ class TagControllerTest extends TestCase public function testAddTagWithoutNewTagWithReferer(): void { - $this->createValidContainerMockSet(); - $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def']; $request = $this->createMock(Request::class); @@ -145,8 +131,6 @@ class TagControllerTest extends TestCase public function testAddTagWithoutNewTagWithoutReferer(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); @@ -159,8 +143,6 @@ class TagControllerTest extends TestCase public function testRemoveTagWithoutMatchingTag(): void { - $this->createValidContainerMockSet(); - $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def']; $request = $this->createMock(Request::class); @@ -177,8 +159,6 @@ class TagControllerTest extends TestCase public function testRemoveTagWithoutTagsearch(): void { - $this->createValidContainerMockSet(); - $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/']; $request = $this->createMock(Request::class); @@ -195,8 +175,6 @@ class TagControllerTest extends TestCase public function testRemoveTagWithoutReferer(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); @@ -211,8 +189,6 @@ class TagControllerTest extends TestCase public function testRemoveTagWithoutTag(): void { - $this->createValidContainerMockSet(); - $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtag=abc']; $request = $this->createMock(Request::class); @@ -227,8 +203,6 @@ class TagControllerTest extends TestCase public function testRemoveTagWithoutTagWithoutReferer(): void { - $this->createValidContainerMockSet(); - $request = $this->createMock(Request::class); $response = new Response(); diff --git a/tpl/default/page.header.html b/tpl/default/page.header.html index ca7dc1bc..4afcca73 100644 --- a/tpl/default/page.header.html +++ b/tpl/default/page.header.html @@ -184,6 +184,20 @@ {/if} +{if="!empty($global_errors) && $is_logged_in"} +
+
+
+ {loop="$global_errors"} +

{$value}

+ {/loop} +
+
+ +
+
+{/if} + {if="!empty($global_warnings) && $is_logged_in"}
@@ -198,4 +212,18 @@
{/if} +{if="!empty($global_successes) && $is_logged_in"} +
+
+
+ {loop="$global_successes"} +

{$value}

+ {/loop} +
+
+ +
+
+{/if} +
-- 2.41.0