Use Middleware to intercept login page

Signed-off-by: Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com>
This commit is contained in:
Benjamin Gaussorgues 2023-08-22 09:56:42 +02:00
Родитель d13795bdd6
Коммит 96de09e1d0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 5DAC1CAFAA6DB883
4 изменённых файлов: 112 добавлений и 29 удалений

Просмотреть файл

@ -25,14 +25,12 @@ declare(strict_types=1);
namespace OCA\LimitLoginToIp\AppInfo;
use OCA\LimitLoginToIp\IsRequestAllowed;
use OCA\LimitLoginToIp\LoginHookListener;
use OCA\LimitLoginToIp\Middleware\CanSeeLoginPageMiddleware;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\User\Events\BeforeUserLoggedInEvent;
/**
@ -41,11 +39,12 @@ use OCP\User\Events\BeforeUserLoggedInEvent;
class Application extends App implements IBootstrap {
public const APP_ID = 'limit_login_to_ip';
public function __construct() {
parent::__construct(self::APP_ID);
public function __construct(array $urlParams = []) {
parent::__construct(self::APP_ID, $urlParams);
}
public function register(IRegistrationContext $context): void {
$context->registerMiddleware(CanSeeLoginPageMiddleware::class, true);
$context->registerEventListener(
BeforeUserLoggedInEvent::class,
LoginHookListener::class,
@ -54,23 +53,5 @@ class Application extends App implements IBootstrap {
}
public function boot(IBootContext $context): void {
$server = $context->getServerContainer();
/** @var IURLGenerator */
$urlGenerator = $server->get(IURLGenerator::class);
/** @var IRequest */
$request = $server->get(IRequest::class);
// Special behaviour for login page
// Block page before displaying form
$currentPath = parse_url($request->getRequestUri(), PHP_URL_PATH);
$loginPath = $urlGenerator->linkToRoute('core.login.showLoginForm');
/** @var IsRequestAllowed */
$isRequestAllowed = $server->get(IsRequestAllowed::class);
if ($currentPath === $loginPath && !$isRequestAllowed()) {
http_response_code(403);
$forbiddenLoginPath = $urlGenerator->linkToRouteAbsolute('limit_login_to_ip.LoginDenied.showErrorPage');
header('Location: '. $forbiddenLoginPath);
exit;
}
}
}

Просмотреть файл

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2023 Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.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 OCA\LimitLoginToIp\Exception;
use Exception;
final class ForbiddenLoginException extends Exception {
public function __construct() {
parent::__construct('Login denied', 403);
}
}

Просмотреть файл

@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2023 Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.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 OCA\LimitLoginToIp\Middleware;
use Exception;
use OC\Core\Controller\LoginController;
use OCA\LimitLoginToIp\Exception\ForbiddenLoginException;
use OCA\LimitLoginToIp\IsRequestAllowed;
use OCP\AppFramework\Middleware;
use OCP\IURLGenerator;
/**
* Checks whether the "sharing check" is enabled
*
* @package OCA\LimitLoginToIp\Middleware
* @psalm-api
*/
class CanSeeLoginPageMiddleware extends Middleware {
private const LOGIN_METHOD_NAMES = [
'tryLogin',
'confirmPassword',
'showLoginForm',
];
public function __construct(
private IsRequestAllowed $isRequestAllowed,
private IURLGenerator $urlGenerator,
) {
}
public function beforeController($controller, $methodName): void {
// Only filter login actions
/** @psalm-suppress all */
if (!$controller instanceof LoginController || !in_array($methodName, self::LOGIN_METHOD_NAMES, true)) {
return;
}
if (!($this->isRequestAllowed)()) {
throw new ForbiddenLoginException();
}
}
public function afterException($controller, $methodName, Exception $exception): void {
if (!$exception instanceof ForbiddenLoginException) {
throw $exception;
}
$forbiddenLoginPath = $this->urlGenerator->linkToRouteAbsolute('limit_login_to_ip.LoginDenied.showErrorPage');
header('Location: '. $forbiddenLoginPath);
exit;
}
}

Просмотреть файл

@ -1,10 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="5.14.1@b9d355e0829c397b9b3b47d0c0ed042a8a70284d">
<file src="lib/AppInfo/Application.php">
<DeprecatedInterface>
<code><![CDATA[$context->getServerContainer()]]></code>
</DeprecatedInterface>
</file>
<files psalm-version="5.15.0@5c774aca4746caf3d239d9c8cadb9f882ca29352">
<file src="lib/IsRequestAllowed.php">
<PossiblyUnusedMethod>
<code>__construct</code>