3
3
declare (strict_types=1 );
4
4
5
5
/**
6
+ * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
6
7
* @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
7
8
*
8
9
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
31
32
use OC \Security \Bruteforce \Throttler ;
32
33
use OCP \AppFramework \Controller ;
33
34
use OCP \AppFramework \Http ;
35
+ use OCP \AppFramework \Http \Attribute \BruteForceProtection ;
34
36
use OCP \AppFramework \Http \Response ;
35
37
use OCP \AppFramework \Http \TooManyRequestsResponse ;
36
38
use OCP \AppFramework \Middleware ;
37
39
use OCP \AppFramework \OCS \OCSException ;
38
40
use OCP \AppFramework \OCSController ;
39
41
use OCP \IRequest ;
40
42
use OCP \Security \Bruteforce \MaxDelayReached ;
43
+ use Psr \Log \LoggerInterface ;
44
+ use ReflectionMethod ;
41
45
42
46
/**
43
47
* Class BruteForceMiddleware performs the bruteforce protection for controllers
47
51
* @package OC\AppFramework\Middleware\Security
48
52
*/
49
53
class BruteForceMiddleware extends Middleware {
50
- private ControllerMethodReflector $ reflector ;
51
- private Throttler $ throttler ;
52
- private IRequest $ request ;
53
-
54
- public function __construct (ControllerMethodReflector $ controllerMethodReflector ,
55
- Throttler $ throttler ,
56
- IRequest $ request ) {
57
- $ this ->reflector = $ controllerMethodReflector ;
58
- $ this ->throttler = $ throttler ;
59
- $ this ->request = $ request ;
54
+ public function __construct (
55
+ protected ControllerMethodReflector $ reflector ,
56
+ protected Throttler $ throttler ,
57
+ protected IRequest $ request ,
58
+ protected LoggerInterface $ logger ,
59
+ ) {
60
60
}
61
61
62
62
/**
@@ -68,18 +68,55 @@ public function beforeController($controller, $methodName) {
68
68
if ($ this ->reflector ->hasAnnotation ('BruteForceProtection ' )) {
69
69
$ action = $ this ->reflector ->getAnnotationParameter ('BruteForceProtection ' , 'action ' );
70
70
$ this ->throttler ->sleepDelayOrThrowOnMax ($ this ->request ->getRemoteAddress (), $ action );
71
+ } else {
72
+ $ reflectionMethod = new ReflectionMethod ($ controller , $ methodName );
73
+ $ attributes = $ reflectionMethod ->getAttributes (BruteForceProtection::class);
74
+
75
+ if (!empty ($ attributes )) {
76
+ $ remoteAddress = $ this ->request ->getRemoteAddress ();
77
+
78
+ foreach ($ attributes as $ attribute ) {
79
+ /** @var BruteForceProtection $protection */
80
+ $ protection = $ attribute ->newInstance ();
81
+ $ action = $ protection ->getAction ();
82
+ $ this ->throttler ->sleepDelayOrThrowOnMax ($ remoteAddress , $ action );
83
+ }
84
+ }
71
85
}
72
86
}
73
87
74
88
/**
75
89
* {@inheritDoc}
76
90
*/
77
91
public function afterController ($ controller , $ methodName , Response $ response ) {
78
- if ($ this ->reflector ->hasAnnotation ('BruteForceProtection ' ) && $ response ->isThrottled ()) {
79
- $ action = $ this ->reflector ->getAnnotationParameter ('BruteForceProtection ' , 'action ' );
80
- $ ip = $ this ->request ->getRemoteAddress ();
81
- $ this ->throttler ->sleepDelay ($ ip , $ action );
82
- $ this ->throttler ->registerAttempt ($ action , $ ip , $ response ->getThrottleMetadata ());
92
+ if ($ response ->isThrottled ()) {
93
+ if ($ this ->reflector ->hasAnnotation ('BruteForceProtection ' )) {
94
+ $ action = $ this ->reflector ->getAnnotationParameter ('BruteForceProtection ' , 'action ' );
95
+ $ ip = $ this ->request ->getRemoteAddress ();
96
+ $ this ->throttler ->sleepDelay ($ ip , $ action );
97
+ $ this ->throttler ->registerAttempt ($ action , $ ip , $ response ->getThrottleMetadata ());
98
+ } else {
99
+ $ reflectionMethod = new ReflectionMethod ($ controller , $ methodName );
100
+ $ attributes = $ reflectionMethod ->getAttributes (BruteForceProtection::class);
101
+
102
+ if (!empty ($ attributes )) {
103
+ $ ip = $ this ->request ->getRemoteAddress ();
104
+ $ metaData = $ response ->getThrottleMetadata ();
105
+
106
+ foreach ($ attributes as $ attribute ) {
107
+ /** @var BruteForceProtection $protection */
108
+ $ protection = $ attribute ->newInstance ();
109
+ $ action = $ protection ->getAction ();
110
+
111
+ if (!isset ($ metaData ['action ' ]) || $ metaData ['action ' ] === $ action ) {
112
+ $ this ->throttler ->sleepDelay ($ ip , $ action );
113
+ $ this ->throttler ->registerAttempt ($ action , $ ip , $ metaData );
114
+ }
115
+ }
116
+ } else {
117
+ $ this ->logger ->debug ('Response for ' . get_class ($ controller ) . ':: ' . $ methodName . ' got bruteforce throttled but has no annotation nor attribute defined. ' );
118
+ }
119
+ }
83
120
}
84
121
85
122
return parent ::afterController ($ controller , $ methodName , $ response );
0 commit comments