33declare (strict_types=1 );
44
55/**
6+ * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
67 * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
78 *
89 * @author Christoph Wurst <christoph@winzerhof-wurst.at>
3132use OC \Security \Bruteforce \Throttler ;
3233use OCP \AppFramework \Controller ;
3334use OCP \AppFramework \Http ;
35+ use OCP \AppFramework \Http \Attribute \BruteForceProtection ;
3436use OCP \AppFramework \Http \Response ;
3537use OCP \AppFramework \Http \TooManyRequestsResponse ;
3638use OCP \AppFramework \Middleware ;
3739use OCP \AppFramework \OCS \OCSException ;
3840use OCP \AppFramework \OCSController ;
3941use OCP \IRequest ;
4042use OCP \Security \Bruteforce \MaxDelayReached ;
43+ use Psr \Log \LoggerInterface ;
44+ use ReflectionMethod ;
4145
4246/**
4347 * Class BruteForceMiddleware performs the bruteforce protection for controllers
4751 * @package OC\AppFramework\Middleware\Security
4852 */
4953class 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+ ) {
6060 }
6161
6262 /**
@@ -68,18 +68,55 @@ public function beforeController($controller, $methodName) {
6868 if ($ this ->reflector ->hasAnnotation ('BruteForceProtection ' )) {
6969 $ action = $ this ->reflector ->getAnnotationParameter ('BruteForceProtection ' , 'action ' );
7070 $ 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+ }
7185 }
7286 }
7387
7488 /**
7589 * {@inheritDoc}
7690 */
7791 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+ }
83120 }
84121
85122 return parent ::afterController ($ controller , $ methodName , $ response );
0 commit comments