1- <?php namespace Auth ;
1+ <?php
2+ namespace Auth ;
23/**
34 * Copyright 2016 OpenStack Foundation
45 * Licensed under the Apache License, Version 2.0 (the "License");
@@ -97,21 +98,19 @@ final class AuthService extends AbstractService implements IAuthService
9798 * @param IAuthUserService $auth_user_service
9899 * @param ISecurityContextService $security_context_service
99100 * @param ITransactionService $tx_service
100- * @params ISecurityContextService $security_context_service
101101 */
102102 public function __construct
103103 (
104- IUserRepository $ user_repository ,
105- IOAuth2OTPRepository $ otp_repository ,
106- IPrincipalService $ principal_service ,
107- IUserService $ user_service ,
108- IUserActionService $ user_action_service ,
109- ICacheService $ cache_service ,
110- IAuthUserService $ auth_user_service ,
104+ IUserRepository $ user_repository ,
105+ IOAuth2OTPRepository $ otp_repository ,
106+ IPrincipalService $ principal_service ,
107+ IUserService $ user_service ,
108+ IUserActionService $ user_action_service ,
109+ ICacheService $ cache_service ,
110+ IAuthUserService $ auth_user_service ,
111111 ISecurityContextService $ security_context_service ,
112- ITransactionService $ tx_service
113- )
114- {
112+ ITransactionService $ tx_service
113+ ) {
115114 parent ::__construct ($ tx_service );
116115 $ this ->user_repository = $ user_repository ;
117116 $ this ->principal_service = $ principal_service ;
@@ -131,6 +130,17 @@ public function isUserLogged()
131130 return Auth::check ();
132131 }
133132
133+ /**
134+ * @return User|null
135+ */
136+ public function getCurrentUser (): ?User
137+ {
138+ $ user = Auth::user ();
139+ if ($ user instanceof User) {
140+ return $ user ;
141+ }
142+ }
143+
134144 /**
135145 * Finds the OTP by value/connection/username, logs the redeem attempt (TX-A),
136146 * then validates lifecycle / value / scope / audience (TX-B).
@@ -140,13 +150,12 @@ public function isUserLogged()
140150 * @throws InvalidOTPException
141151 */
142152 private function findAndValidateOTP (
143- string $ otp_value ,
144- string $ user_name ,
145- string $ otp_conn ,
153+ string $ otp_value ,
154+ string $ user_name ,
155+ string $ otp_conn ,
146156 ?string $ otp_required_scopes ,
147157 ?Client $ client
148- ): OAuth2OTP
149- {
158+ ): OAuth2OTP {
150159 // TX-A: find + log attempt (committed before any validation can throw)
151160 $ otp = $ this ->tx_service ->transaction (function () use ($ otp_value , $ otp_conn , $ user_name , $ client ) {
152161
@@ -189,8 +198,10 @@ private function findAndValidateOTP(
189198 throw new InvalidOTPException ("Single-use code requested scopes escalates former scopes. " );
190199 }
191200
192- if (($ otp ->hasClient () && is_null ($ client )) ||
193- ($ otp ->hasClient () && !is_null ($ client ) && $ client ->getClientId () != $ otp ->getClient ()->getClientId ())) {
201+ if (
202+ ($ otp ->hasClient () && is_null ($ client )) ||
203+ ($ otp ->hasClient () && !is_null ($ client ) && $ client ->getClientId () != $ otp ->getClient ()->getClientId ())
204+ ) {
194205 throw new AuthenticationException ("Single-use code audience mismatch. " );
195206 }
196207
@@ -319,8 +330,7 @@ public function verifyOTPChallenge(
319330 OAuth2OTP $ otpClaim ,
320331 User $ sessionUser ,
321332 ?Client $ client = null
322- ): OAuth2OTP
323- {
333+ ): OAuth2OTP {
324334 Log::debug (sprintf (
325335 "AuthService::verifyOTPChallenge otp %s session user %s " ,
326336 $ otpClaim ->getValue (),
@@ -384,11 +394,10 @@ public function login(string $username, string $password, bool $remember_me): bo
384394 {
385395 Log::debug ("AuthService::login " );
386396
387- $ this ->last_login_error = "" ;
388397 if (!Auth::attempt (['username ' => $ username , 'password ' => $ password ], $ remember_me )) {
389398 throw new AuthenticationException
390399 (
391- "We are sorry, your username or password does not match an existing record. "
400+ "username or password does not match an existing record. "
392401 );
393402 }
394403 Log::debug ("AuthService::login: clearing principal " );
@@ -397,7 +406,7 @@ public function login(string $username, string $password, bool $remember_me): bo
397406 if (is_null ($ current_user ) || !$ current_user ->canLogin ())
398407 throw new AuthenticationException
399408 (
400- "We are sorry, your username or password does not match an existing record. "
409+ "username or password does not match an existing record. "
401410 );
402411 $ this ->principal_service ->register
403412 (
@@ -409,11 +418,51 @@ public function login(string $username, string $password, bool $remember_me): bo
409418 }
410419
411420 /**
412- * @return User|null
421+ * @param string $username
422+ * @param string $password
423+ * @return User
424+ * @throws AuthenticationException
413425 */
414- public function getCurrentUser ( ): ? User
426+ public function validateCredentials ( string $ username , string $ password ): User
415427 {
416- return Auth::user ();
428+ Log::debug ("AuthService::validateCredentials " );
429+
430+ // retrieveByCredentials swallows AuthenticationLockedUserLoginAttempt and returns null,
431+ // so pre-check lock state here to surface a distinct message for locked accounts.
432+ $ existing = $ this ->user_repository ->getByEmailOrName ($ username );
433+ if (!is_null ($ existing ) && !$ existing ->isActive ()) {
434+ throw new AuthenticationException (
435+ sprintf ("User %s is locked. " , $ username )
436+ );
437+ }
438+
439+ // Known cost: retrieveByCredentials() calls user_repository->getByEmailOrName() internally
440+ // (CustomAuthProvider line ~122), duplicating the query above. Eliminating it would require
441+ // either changing the provider API to accept a pre-fetched User, or moving
442+ // LockUserCounterMeasure checkpoint logic out of the provider — both out of scope here.
443+ $ user = Auth::getProvider ()->retrieveByCredentials ([
444+ 'username ' => $ username ,
445+ 'password ' => $ password ,
446+ ]);
447+
448+ if (is_null ($ user ) || !$ user instanceof User || !$ user ->canLogin ()) {
449+ throw new AuthenticationException (
450+ "username or password does not match an existing record. "
451+ );
452+ }
453+
454+ return $ user ;
455+ }
456+
457+ /**
458+ * @param User $user
459+ * @param bool $remember
460+ * @return void
461+ */
462+ public function loginUser (User $ user , bool $ remember ): void
463+ {
464+ Log::debug ("AuthService::loginUser " );
465+ Auth::login ($ user , $ remember );
417466 }
418467
419468 /**
@@ -618,7 +667,8 @@ public function registerRPLogin(string $client_id): void
618667 $ rps = $ zlib ->uncompress ($ rps );
619668 $ rps .= '| ' ;
620669 }
621- if (is_null ($ rps )) $ rps = "" ;
670+ if (is_null ($ rps ))
671+ $ rps = "" ;
622672
623673 if (!str_contains ($ rps , $ client_id ))
624674 $ rps .= $ client_id ;
@@ -720,12 +770,15 @@ public function postLoginUserActions(int $user_id): void
720770 Log::debug (sprintf ("AuthService::postLoginUserActions user %s " , $ user_id ));
721771 $ this ->tx_service ->transaction (function () use ($ user_id ) {
722772 $ user = $ this ->user_repository ->getById ($ user_id );
723- if (!$ user instanceof User) return ;
773+ if (!$ user instanceof User)
774+ return ;
724775
725776 if (!$ user ->isActive ()) {
726777 Log::warning (sprintf ("AuthService::postLoginUserActions user %s is not active. " , $ user_id ));
727- throw new AuthenticationLockedUserLoginAttempt ($ user ->getEmail (),
728- sprintf ("User %s is locked. " , $ user ->getEmail ()));
778+ throw new AuthenticationLockedUserLoginAttempt (
779+ $ user ->getEmail (),
780+ sprintf ("User %s is locked. " , $ user ->getEmail ())
781+ );
729782 }
730783
731784 //update user fields
@@ -736,4 +789,4 @@ public function postLoginUserActions(int $user_id): void
736789
737790 });
738791 }
739- }
792+ }
0 commit comments