feat(middleware): Migrate BruteForceProtection annotation to PHP Attribute and allow multiple#36928
Conversation
…ibute and allow multiple Signed-off-by: Joas Schilling <coding@schilljs.com>
Signed-off-by: Joas Schilling <coding@schilljs.com>
c26dc84 to
2b49861
Compare
|
Documentation update at nextcloud/documentation#9742 |
| if ($this->reflector->hasAnnotation('BruteForceProtection')) { | ||
| $action = $this->reflector->getAnnotationParameter('BruteForceProtection', 'action'); | ||
| $this->throttler->sleepDelayOrThrowOnMax($this->request->getRemoteAddress(), $action); | ||
| } else { |
There was a problem hiding this comment.
Would it make sense to add hasAttribute / getAttributes to ControllerMethodReflector ?
There was a problem hiding this comment.
THought about that too. I'm still up for it as a follow up, to combine the logic our for UseSession and this one.
Just thought it's easier to first get this in and then do more rework.
There was a problem hiding this comment.
I’m always afraid the follow-up never comes in these cases :-)
We should try to make the new way as easy to use as the previous one if we want the code to get migrated at some point.
There was a problem hiding this comment.
I’m always afraid the follow-up never comes in these cases
I guess you have a point, at the same time there is the RateLimit annotation waiting for me as a task this and next month, so will come.
| if ($response->isThrottled()) { | ||
| if ($this->reflector->hasAnnotation('BruteForceProtection')) { | ||
| $action = $this->reflector->getAnnotationParameter('BruteForceProtection', 'action'); | ||
| $ip = $this->request->getRemoteAddress(); | ||
| $this->throttler->sleepDelay($ip, $action); | ||
| $this->throttler->registerAttempt($action, $ip, $response->getThrottleMetadata()); | ||
| } else { | ||
| $reflectionMethod = new ReflectionMethod($controller, $methodName); | ||
| $attributes = $reflectionMethod->getAttributes(BruteForceProtection::class); | ||
|
|
||
| if (!empty($attributes)) { | ||
| $ip = $this->request->getRemoteAddress(); | ||
| $metaData = $response->getThrottleMetadata(); | ||
|
|
||
| foreach ($attributes as $attribute) { | ||
| /** @var BruteForceProtection $protection */ | ||
| $protection = $attribute->newInstance(); | ||
| $action = $protection->getAction(); | ||
|
|
||
| if (!isset($metaData['action']) || $metaData['action'] === $action) { | ||
| $this->throttler->sleepDelay($ip, $action); | ||
| $this->throttler->registerAttempt($action, $ip, $metaData); | ||
| } | ||
| } | ||
| } else { | ||
| $this->logger->debug('Response for ' . get_class($controller) . '::' . $methodName . ' got bruteforce throttled but has no annotation nor attribute defined.'); | ||
| } | ||
| } |
There was a problem hiding this comment.
Is a good piece of code to refactor and split the responsibility at separated method to make the reading more easy.
Maybe anything like:
->parseBruteForceAnnotation
->parseBruteForceAttributes
Previously (and still supported)
New syntax
Multiple attributes now supported
By specifying an
'action'in thethrottle($metadata)only the dedicated attribute will trigger it's BFP, if none is specified all will throttle.This is useful e.g. in controllers that need to protect different data, e.g. Talk has the tokens and the signaling secret in the same method and needs to do a lot of manual work instead of just calling
throttleon the response:https://github.com/nextcloud/spreed/blob/29a442cf86d49b426ecd47776c86ab6f6de5da58/lib/Controller/SignalingController.php#L154-L179
🚧 ToDo
Checklist