summaryrefslogtreecommitdiffstats
path: root/lib/private/AppFramework
diff options
context:
space:
mode:
authorLukas Reschke <lukas@statuscode.ch>2017-04-12 22:14:11 +0200
committerLukas Reschke <lukas@statuscode.ch>2017-04-13 12:00:17 +0200
commita1ae5275f9b2aa4d6446aca5b38e6f324f632832 (patch)
treef3f54f0805ff809428d1a1331a27bfc5a40706bf /lib/private/AppFramework
parenta05471eb43bd10ab33008993b919375a2a1e8b41 (diff)
downloadnextcloud-server-a1ae5275f9b2aa4d6446aca5b38e6f324f632832.tar.gz
nextcloud-server-a1ae5275f9b2aa4d6446aca5b38e6f324f632832.zip
Move to dedicated MiddleWare
Signed-off-by: Lukas Reschke <lukas@statuscode.ch>
Diffstat (limited to 'lib/private/AppFramework')
-rw-r--r--lib/private/AppFramework/DependencyInjection/DIContainer.php19
-rw-r--r--lib/private/AppFramework/Middleware/Security/RateLimitingMiddleware.php134
-rw-r--r--lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php45
3 files changed, 160 insertions, 38 deletions
diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php
index 588f52e452c..d279140afbb 100644
--- a/lib/private/AppFramework/DependencyInjection/DIContainer.php
+++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php
@@ -40,6 +40,7 @@ use OC\AppFramework\Http\Output;
use OC\AppFramework\Middleware\MiddlewareDispatcher;
use OC\AppFramework\Middleware\Security\CORSMiddleware;
use OC\AppFramework\Middleware\OCSMiddleware;
+use OC\AppFramework\Middleware\Security\RateLimitingMiddleware;
use OC\AppFramework\Middleware\Security\SecurityMiddleware;
use OC\AppFramework\Middleware\SessionMiddleware;
use OC\AppFramework\Utility\SimpleContainer;
@@ -219,17 +220,28 @@ class DIContainer extends SimpleContainer implements IAppContainer {
$server->getLogger(),
$server->getSession(),
$c['AppName'],
- $server->getUserSession(),
+ $app->isLoggedIn(),
$app->isAdminUser(),
$server->getContentSecurityPolicyManager(),
$server->getCsrfTokenManager(),
$server->getContentSecurityPolicyNonceManager(),
- $server->getBruteForceThrottler(),
- $c->query(OC\Security\RateLimiting\Limiter::class)
+ $server->getBruteForceThrottler()
);
});
+ $this->registerService('RateLimitingMiddleware', function($c) use ($app) {
+ /** @var \OC\Server $server */
+ $server = $app->getServer();
+
+ return new RateLimitingMiddleware(
+ $server->getRequest(),
+ $server->getUserSession(),
+ $c['ControllerMethodReflector'],
+ $c->query(OC\Security\RateLimiting\Limiter::class)
+ );
+ });
+
$this->registerService('CORSMiddleware', function($c) {
return new CORSMiddleware(
$c['Request'],
@@ -270,6 +282,7 @@ class DIContainer extends SimpleContainer implements IAppContainer {
$dispatcher->registerMiddleware($c['OCSMiddleware']);
$dispatcher->registerMiddleware($c['SecurityMiddleware']);
$dispatcher->registerMiddleWare($c['TwoFactorMiddleware']);
+ $dispatcher->registerMiddleware($c['RateLimitingMiddleware']);
foreach($middleWares as $middleWare) {
$dispatcher->registerMiddleware($c[$middleWare]);
diff --git a/lib/private/AppFramework/Middleware/Security/RateLimitingMiddleware.php b/lib/private/AppFramework/Middleware/Security/RateLimitingMiddleware.php
new file mode 100644
index 00000000000..e2ad7955dd0
--- /dev/null
+++ b/lib/private/AppFramework/Middleware/Security/RateLimitingMiddleware.php
@@ -0,0 +1,134 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @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;
+
+use OC\AppFramework\Utility\ControllerMethodReflector;
+use OC\Security\RateLimiting\Exception\RateLimitExceededException;
+use OC\Security\RateLimiting\Limiter;
+use OCP\AppFramework\Http\JSONResponse;
+use OCP\AppFramework\Http\Response;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Middleware;
+use OCP\IRequest;
+use OCP\IUserSession;
+
+/**
+ * Class RateLimitingMiddleware is the middleware responsible for implementing the
+ * ratelimiting in Nextcloud.
+ *
+ * It parses annotations such as:
+ *
+ * @UserRateThrottle(limit=5, period=100)
+ * @AnonRateThrottle(limit=1, period=100)
+ *
+ * Those annotations above would mean that logged-in users can access the page 5
+ * times within 100 seconds, and anonymous users 1 time within 100 seconds. If
+ * only an AnonRateThrottle is specified that one will also be applied to logged-in
+ * users.
+ *
+ * @package OC\AppFramework\Middleware\Security
+ */
+class RateLimitingMiddleware extends Middleware {
+ /** @var IRequest $request */
+ private $request;
+ /** @var IUserSession */
+ private $userSession;
+ /** @var ControllerMethodReflector */
+ private $reflector;
+ /** @var Limiter */
+ private $limiter;
+
+ /**
+ * @param IRequest $request
+ * @param IUserSession $userSession
+ * @param ControllerMethodReflector $reflector
+ * @param Limiter $limiter
+ */
+ public function __construct(IRequest $request,
+ IUserSession $userSession,
+ ControllerMethodReflector $reflector,
+ Limiter $limiter) {
+ $this->request = $request;
+ $this->userSession = $userSession;
+ $this->reflector = $reflector;
+ $this->limiter = $limiter;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws RateLimitExceededException
+ */
+ public function beforeController($controller, $methodName) {
+ parent::beforeController($controller, $methodName);
+
+ $anonLimit = $this->reflector->getAnnotationParameter('AnonRateThrottle', 'limit');
+ $anonPeriod = $this->reflector->getAnnotationParameter('AnonRateThrottle', 'period');
+ $userLimit = $this->reflector->getAnnotationParameter('UserRateThrottle', 'limit');
+ $userPeriod = $this->reflector->getAnnotationParameter('UserRateThrottle', 'period');
+ $rateLimitIdentifier = get_class($controller) . '::' . $methodName;
+ if($userLimit !== '' && $userPeriod !== '' && $this->userSession->isLoggedIn()) {
+ $this->limiter->registerUserRequest(
+ $rateLimitIdentifier,
+ $userLimit,
+ $userPeriod,
+ $this->userSession->getUser()
+ );
+ } elseif ($anonLimit !== '' && $anonPeriod !== '') {
+ $this->limiter->registerAnonRequest(
+ $rateLimitIdentifier,
+ $anonLimit,
+ $anonPeriod,
+ $this->request->getRemoteAddress()
+ );
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function afterException($controller, $methodName, \Exception $exception) {
+ if($exception instanceof RateLimitExceededException) {
+ if (stripos($this->request->getHeader('Accept'),'html') === false) {
+ $response = new JSONResponse(
+ [
+ 'message' => $exception->getMessage(),
+ ],
+ $exception->getCode()
+ );
+ } else {
+ $response = new TemplateResponse(
+ 'core',
+ '403',
+ [
+ 'file' => $exception->getMessage()
+ ],
+ 'guest'
+ );
+ $response->setStatus($exception->getCode());
+ }
+
+ return $response;
+ }
+
+ throw $exception;
+ }
+}
diff --git a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
index d8bbe84c86e..d4d0598c94f 100644
--- a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
@@ -26,6 +26,7 @@
*
*/
+
namespace OC\AppFramework\Middleware\Security;
use OC\AppFramework\Middleware\Security\Exceptions\AppNotEnabledException;
@@ -39,7 +40,6 @@ use OC\Security\Bruteforce\Throttler;
use OC\Security\CSP\ContentSecurityPolicyManager;
use OC\Security\CSP\ContentSecurityPolicyNonceManager;
use OC\Security\CSRF\CsrfTokenManager;
-use OC\Security\RateLimiting\Limiter;
use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\AppFramework\Http\EmptyContentSecurityPolicy;
use OCP\AppFramework\Http\RedirectResponse;
@@ -54,7 +54,6 @@ use OCP\IURLGenerator;
use OCP\IRequest;
use OCP\ILogger;
use OCP\AppFramework\Controller;
-use OCP\IUserSession;
use OCP\Util;
use OC\AppFramework\Middleware\Security\Exceptions\SecurityException;
@@ -79,8 +78,8 @@ class SecurityMiddleware extends Middleware {
private $logger;
/** @var ISession */
private $session;
- /** @var IUserSession */
- private $userSession;
+ /** @var bool */
+ private $isLoggedIn;
/** @var bool */
private $isAdminUser;
/** @var ContentSecurityPolicyManager */
@@ -91,8 +90,6 @@ class SecurityMiddleware extends Middleware {
private $cspNonceManager;
/** @var Throttler */
private $throttler;
- /** @var Limiter */
- private $limiter;
/**
* @param IRequest $request
@@ -102,13 +99,12 @@ class SecurityMiddleware extends Middleware {
* @param ILogger $logger
* @param ISession $session
* @param string $appName
- * @param IUserSession $userSession
+ * @param bool $isLoggedIn
* @param bool $isAdminUser
* @param ContentSecurityPolicyManager $contentSecurityPolicyManager
* @param CSRFTokenManager $csrfTokenManager
* @param ContentSecurityPolicyNonceManager $cspNonceManager
* @param Throttler $throttler
- * @param Limiter $limiter
*/
public function __construct(IRequest $request,
ControllerMethodReflector $reflector,
@@ -117,13 +113,12 @@ class SecurityMiddleware extends Middleware {
ILogger $logger,
ISession $session,
$appName,
- IUserSession $userSession,
+ $isLoggedIn,
$isAdminUser,
ContentSecurityPolicyManager $contentSecurityPolicyManager,
CsrfTokenManager $csrfTokenManager,
ContentSecurityPolicyNonceManager $cspNonceManager,
- Throttler $throttler,
- Limiter $limiter) {
+ Throttler $throttler) {
$this->navigationManager = $navigationManager;
$this->request = $request;
$this->reflector = $reflector;
@@ -131,15 +126,15 @@ class SecurityMiddleware extends Middleware {
$this->urlGenerator = $urlGenerator;
$this->logger = $logger;
$this->session = $session;
- $this->userSession = $userSession;
+ $this->isLoggedIn = $isLoggedIn;
$this->isAdminUser = $isAdminUser;
$this->contentSecurityPolicyManager = $contentSecurityPolicyManager;
$this->csrfTokenManager = $csrfTokenManager;
$this->cspNonceManager = $cspNonceManager;
$this->throttler = $throttler;
- $this->limiter = $limiter;
}
+
/**
* This runs all the security checks before a method call. The
* security checks are determined by inspecting the controller method
@@ -157,7 +152,7 @@ class SecurityMiddleware extends Middleware {
// security checks
$isPublicPage = $this->reflector->hasAnnotation('PublicPage');
if(!$isPublicPage) {
- if(!$this->userSession->isLoggedIn()) {
+ if(!$this->isLoggedIn) {
throw new NotLoggedInException();
}
@@ -196,27 +191,6 @@ class SecurityMiddleware extends Middleware {
}
}
- $anonLimit = $this->reflector->getAnnotationParameter('AnonRateThrottle', 'limit');
- $anonPeriod = $this->reflector->getAnnotationParameter('AnonRateThrottle', 'period');
- $userLimit = $this->reflector->getAnnotationParameter('UserRateThrottle', 'limit');
- $userPeriod = $this->reflector->getAnnotationParameter('UserRateThrottle', 'period');
- $rateLimitIdentifier = get_class($controller) . '::' . $methodName;
- if($userLimit !== '' && $userPeriod !== '' && $this->userSession->isLoggedIn()) {
- $this->limiter->registerUserRequest(
- $rateLimitIdentifier,
- $userLimit,
- $userPeriod,
- $this->userSession->getUser()
- );
- } elseif ($anonLimit !== '' && $anonPeriod !== '') {
- $this->limiter->registerAnonRequest(
- $rateLimitIdentifier,
- $anonLimit,
- $anonPeriod,
- $this->request->getRemoteAddress()
- );
- }
-
if($this->reflector->hasAnnotation('BruteForceProtection')) {
$action = $this->reflector->getAnnotationParameter('BruteForceProtection', 'action');
$this->throttler->sleepDelay($this->request->getRemoteAddress(), $action);
@@ -232,6 +206,7 @@ class SecurityMiddleware extends Middleware {
if(\OC_App::getAppPath($this->appName) !== false && !\OC_App::isEnabled($this->appName)) {
throw new AppNotEnabledException();
}
+
}
/**