Completed
Push — master ( 189e1f...3cdfe6 )
by
unknown
26:26
created
private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php 1 patch
Indentation   +92 added lines, -92 removed lines patch added patch discarded remove patch
@@ -28,96 +28,96 @@
 block discarded – undo
28 28
 use ReflectionMethod;
29 29
 
30 30
 class PasswordConfirmationMiddleware extends Middleware {
31
-	private array $excludedUserBackEnds = ['user_saml' => true, 'user_globalsiteselector' => true];
32
-
33
-	public function __construct(
34
-		private ControllerMethodReflector $reflector,
35
-		private ISession $session,
36
-		private IUserSession $userSession,
37
-		private ITimeFactory $timeFactory,
38
-		private IProvider $tokenProvider,
39
-		private readonly LoggerInterface $logger,
40
-		private readonly IRequest $request,
41
-		private readonly Manager $userManager,
42
-	) {
43
-	}
44
-
45
-	/**
46
-	 * @throws NotConfirmedException
47
-	 */
48
-	public function beforeController(Controller $controller, string $methodName) {
49
-		$reflectionMethod = new ReflectionMethod($controller, $methodName);
50
-
51
-		if (!$this->needsPasswordConfirmation($reflectionMethod)) {
52
-			return;
53
-		}
54
-
55
-		$user = $this->userSession->getUser();
56
-		$backendClassName = '';
57
-		if ($user !== null) {
58
-			$backend = $user->getBackend();
59
-			if ($backend instanceof IPasswordConfirmationBackend) {
60
-				if (!$backend->canConfirmPassword($user->getUID())) {
61
-					return;
62
-				}
63
-			}
64
-
65
-			$backendClassName = $user->getBackendClassName();
66
-		}
67
-
68
-		try {
69
-			$sessionId = $this->session->getId();
70
-			$token = $this->tokenProvider->getToken($sessionId);
71
-		} catch (SessionNotAvailableException|InvalidTokenException|WipeTokenException|ExpiredTokenException) {
72
-			// States we do not deal with here.
73
-			return;
74
-		}
75
-
76
-		$scope = $token->getScopeAsArray();
77
-		if (isset($scope[IToken::SCOPE_SKIP_PASSWORD_VALIDATION]) && $scope[IToken::SCOPE_SKIP_PASSWORD_VALIDATION] === true) {
78
-			// Users logging in from SSO backends cannot confirm their password by design
79
-			return;
80
-		}
81
-
82
-		if ($this->isPasswordConfirmationStrict($reflectionMethod)) {
83
-			$authHeader = $this->request->getHeader('Authorization');
84
-			if (!str_starts_with(strtolower($authHeader), 'basic ')) {
85
-				throw new NotConfirmedException('Required authorization header missing');
86
-			}
87
-			[, $password] = explode(':', base64_decode(substr($authHeader, 6)), 2);
88
-			$loginName = $this->session->get('loginname');
89
-			$loginResult = $this->userManager->checkPassword($loginName, $password);
90
-			if ($loginResult === false) {
91
-				throw new NotConfirmedException();
92
-			}
93
-
94
-			$this->session->set('last-password-confirm', $this->timeFactory->getTime());
95
-		} else {
96
-			$lastConfirm = (int)$this->session->get('last-password-confirm');
97
-			// TODO: confirm excludedUserBackEnds can go away and remove it
98
-			if (!isset($this->excludedUserBackEnds[$backendClassName]) && $lastConfirm < ($this->timeFactory->getTime() - (30 * 60 + 15))) { // allow 15 seconds delay
99
-				throw new NotConfirmedException();
100
-			}
101
-		}
102
-	}
103
-
104
-	private function needsPasswordConfirmation(ReflectionMethod $reflectionMethod): bool {
105
-		$attributes = $reflectionMethod->getAttributes(PasswordConfirmationRequired::class);
106
-		if (!empty($attributes)) {
107
-			return true;
108
-		}
109
-
110
-		if ($this->reflector->hasAnnotation('PasswordConfirmationRequired')) {
111
-			$this->logger->debug($reflectionMethod->getDeclaringClass()->getName() . '::' . $reflectionMethod->getName() . ' uses the @' . 'PasswordConfirmationRequired' . ' annotation and should use the #[PasswordConfirmationRequired] attribute instead');
112
-			return true;
113
-		}
114
-
115
-		return false;
116
-	}
117
-
118
-	private function isPasswordConfirmationStrict(ReflectionMethod $reflectionMethod): bool {
119
-		/** @var ReflectionAttribute<PasswordConfirmationRequired>[] $attributes */
120
-		$attributes = $reflectionMethod->getAttributes(PasswordConfirmationRequired::class);
121
-		return !empty($attributes) && ($attributes[0]->newInstance()->getStrict());
122
-	}
31
+    private array $excludedUserBackEnds = ['user_saml' => true, 'user_globalsiteselector' => true];
32
+
33
+    public function __construct(
34
+        private ControllerMethodReflector $reflector,
35
+        private ISession $session,
36
+        private IUserSession $userSession,
37
+        private ITimeFactory $timeFactory,
38
+        private IProvider $tokenProvider,
39
+        private readonly LoggerInterface $logger,
40
+        private readonly IRequest $request,
41
+        private readonly Manager $userManager,
42
+    ) {
43
+    }
44
+
45
+    /**
46
+     * @throws NotConfirmedException
47
+     */
48
+    public function beforeController(Controller $controller, string $methodName) {
49
+        $reflectionMethod = new ReflectionMethod($controller, $methodName);
50
+
51
+        if (!$this->needsPasswordConfirmation($reflectionMethod)) {
52
+            return;
53
+        }
54
+
55
+        $user = $this->userSession->getUser();
56
+        $backendClassName = '';
57
+        if ($user !== null) {
58
+            $backend = $user->getBackend();
59
+            if ($backend instanceof IPasswordConfirmationBackend) {
60
+                if (!$backend->canConfirmPassword($user->getUID())) {
61
+                    return;
62
+                }
63
+            }
64
+
65
+            $backendClassName = $user->getBackendClassName();
66
+        }
67
+
68
+        try {
69
+            $sessionId = $this->session->getId();
70
+            $token = $this->tokenProvider->getToken($sessionId);
71
+        } catch (SessionNotAvailableException|InvalidTokenException|WipeTokenException|ExpiredTokenException) {
72
+            // States we do not deal with here.
73
+            return;
74
+        }
75
+
76
+        $scope = $token->getScopeAsArray();
77
+        if (isset($scope[IToken::SCOPE_SKIP_PASSWORD_VALIDATION]) && $scope[IToken::SCOPE_SKIP_PASSWORD_VALIDATION] === true) {
78
+            // Users logging in from SSO backends cannot confirm their password by design
79
+            return;
80
+        }
81
+
82
+        if ($this->isPasswordConfirmationStrict($reflectionMethod)) {
83
+            $authHeader = $this->request->getHeader('Authorization');
84
+            if (!str_starts_with(strtolower($authHeader), 'basic ')) {
85
+                throw new NotConfirmedException('Required authorization header missing');
86
+            }
87
+            [, $password] = explode(':', base64_decode(substr($authHeader, 6)), 2);
88
+            $loginName = $this->session->get('loginname');
89
+            $loginResult = $this->userManager->checkPassword($loginName, $password);
90
+            if ($loginResult === false) {
91
+                throw new NotConfirmedException();
92
+            }
93
+
94
+            $this->session->set('last-password-confirm', $this->timeFactory->getTime());
95
+        } else {
96
+            $lastConfirm = (int)$this->session->get('last-password-confirm');
97
+            // TODO: confirm excludedUserBackEnds can go away and remove it
98
+            if (!isset($this->excludedUserBackEnds[$backendClassName]) && $lastConfirm < ($this->timeFactory->getTime() - (30 * 60 + 15))) { // allow 15 seconds delay
99
+                throw new NotConfirmedException();
100
+            }
101
+        }
102
+    }
103
+
104
+    private function needsPasswordConfirmation(ReflectionMethod $reflectionMethod): bool {
105
+        $attributes = $reflectionMethod->getAttributes(PasswordConfirmationRequired::class);
106
+        if (!empty($attributes)) {
107
+            return true;
108
+        }
109
+
110
+        if ($this->reflector->hasAnnotation('PasswordConfirmationRequired')) {
111
+            $this->logger->debug($reflectionMethod->getDeclaringClass()->getName() . '::' . $reflectionMethod->getName() . ' uses the @' . 'PasswordConfirmationRequired' . ' annotation and should use the #[PasswordConfirmationRequired] attribute instead');
112
+            return true;
113
+        }
114
+
115
+        return false;
116
+    }
117
+
118
+    private function isPasswordConfirmationStrict(ReflectionMethod $reflectionMethod): bool {
119
+        /** @var ReflectionAttribute<PasswordConfirmationRequired>[] $attributes */
120
+        $attributes = $reflectionMethod->getAttributes(PasswordConfirmationRequired::class);
121
+        return !empty($attributes) && ($attributes[0]->newInstance()->getStrict());
122
+    }
123 123
 }
Please login to merge, or discard this patch.