Passed
Push — master ( 60be72...4c6eb9 )
by Morris
12:37
created

BruteForceMiddleware   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 64
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 22
c 0
b 0
f 0
dl 0
loc 64
rs 10
wmc 9

4 Methods

Rating   Name   Duplication   Size   Complexity  
A afterController() 0 9 3
A afterException() 0 10 3
A beforeController() 0 6 2
A __construct() 0 6 1
1
<?php
2
3
declare(strict_types=1);
4
/**
5
 * @copyright Copyright (c) 2017 Lukas Reschke <[email protected]>
6
 *
7
 * @author Christoph Wurst <[email protected]>
8
 * @author Lukas Reschke <[email protected]>
9
 *
10
 * @license GNU AGPL version 3 or any later version
11
 *
12
 * This program is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License as
14
 * published by the Free Software Foundation, either version 3 of the
15
 * License, or (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License
23
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 */
26
27
namespace OC\AppFramework\Middleware\Security;
28
29
use OC\AppFramework\Utility\ControllerMethodReflector;
30
use OC\Security\Bruteforce\Throttler;
31
use OCP\AppFramework\Controller;
32
use OCP\AppFramework\Http;
33
use OCP\AppFramework\Http\Response;
34
use OCP\AppFramework\Http\TooManyRequestsResponse;
35
use OCP\AppFramework\Middleware;
36
use OCP\AppFramework\OCS\OCSException;
37
use OCP\AppFramework\OCSController;
38
use OCP\IRequest;
39
use OCP\Security\Bruteforce\MaxDelayReached;
40
41
/**
42
 * Class BruteForceMiddleware performs the bruteforce protection for controllers
43
 * that are annotated with @BruteForceProtection(action=$action) whereas $action
44
 * is the action that should be logged within the database.
45
 *
46
 * @package OC\AppFramework\Middleware\Security
47
 */
48
class BruteForceMiddleware extends Middleware {
49
	/** @var ControllerMethodReflector */
50
	private $reflector;
51
	/** @var Throttler */
52
	private $throttler;
53
	/** @var IRequest */
54
	private $request;
55
56
	/**
57
	 * @param ControllerMethodReflector $controllerMethodReflector
58
	 * @param Throttler $throttler
59
	 * @param IRequest $request
60
	 */
61
	public function __construct(ControllerMethodReflector $controllerMethodReflector,
62
								Throttler $throttler,
63
								IRequest $request) {
64
		$this->reflector = $controllerMethodReflector;
65
		$this->throttler = $throttler;
66
		$this->request = $request;
67
	}
68
69
	/**
70
	 * {@inheritDoc}
71
	 */
72
	public function beforeController($controller, $methodName) {
73
		parent::beforeController($controller, $methodName);
74
75
		if ($this->reflector->hasAnnotation('BruteForceProtection')) {
76
			$action = $this->reflector->getAnnotationParameter('BruteForceProtection', 'action');
77
			$this->throttler->sleepDelayOrThrowOnMax($this->request->getRemoteAddress(), $action);
78
		}
79
	}
80
81
	/**
82
	 * {@inheritDoc}
83
	 */
84
	public function afterController($controller, $methodName, Response $response) {
85
		if ($this->reflector->hasAnnotation('BruteForceProtection') && $response->isThrottled()) {
86
			$action = $this->reflector->getAnnotationParameter('BruteForceProtection', 'action');
87
			$ip = $this->request->getRemoteAddress();
88
			$this->throttler->sleepDelay($ip, $action);
89
			$this->throttler->registerAttempt($action, $ip, $response->getThrottleMetadata());
90
		}
91
92
		return parent::afterController($controller, $methodName, $response);
93
	}
94
95
	/**
96
	 * @param Controller $controller
97
	 * @param string $methodName
98
	 * @param \Exception $exception
99
	 * @throws \Exception
100
	 * @return Response
101
	 */
102
	public function afterException($controller, $methodName, \Exception $exception): Response {
103
		if ($exception instanceof MaxDelayReached) {
104
			if ($controller instanceof OCSController) {
105
				throw new OCSException($exception->getMessage(), Http::STATUS_TOO_MANY_REQUESTS);
106
			}
107
108
			return new TooManyRequestsResponse();
109
		}
110
111
		throw $exception;
112
	}
113
}
114