4949use Throwable ;
5050
5151class Backend extends ABackend implements IPasswordConfirmationBackend, IGetDisplayNameBackend, IApacheBackend, ICustomLogout, ICountUsersBackend {
52+ private const SESSION_USER_DATA = 'user_oidc.oidcUserData ' ;
53+
54+ /** @var list<class-string<IBearerTokenValidator>> */
5255 private $ tokenValidators = [
5356 SelfEncodedValidator::class,
5457 UserInfoValidator::class,
@@ -184,14 +187,23 @@ public function getLogoutUrl(): string {
184187 * Inspired by user_saml
185188 */
186189 public function getUserData (): array {
187- $ userData = $ this ->session ->get ('user_oidc.oidcUserData ' );
188- $ providerId = (int )$ this ->session ->get (LoginController::PROVIDERID );
189- $ userData = $ this ->formatUserData ($ providerId , $ userData );
190-
191- // make sure that a valid UID is given
192- if (empty ($ userData ['formatted ' ]['uid ' ])) {
193- $ this ->logger ->error ('No valid uid given, please check your attribute mapping. Got uid: {uid} ' , ['app ' => 'user_oidc ' , 'uid ' => $ userData ['formatted ' ]['uid ' ]]);
194- throw new \InvalidArgumentException ('No valid uid given, please check your attribute mapping. Got uid: ' . $ userData ['formatted ' ]['uid ' ]);
190+ $ userData = $ this ->session ->get (self ::SESSION_USER_DATA ) ?? [];
191+ $ rawProviderId = $ this ->session ->get (LoginController::PROVIDERID );
192+ if ($ rawProviderId === null ) {
193+ throw new \InvalidArgumentException ('No OIDC provider ID in session ' );
194+ }
195+
196+ $ providerId = (int )$ rawProviderId ;
197+
198+ $ userData = $ this ->formatUserData ($ providerId , is_array ($ userData ) ? $ userData : []);
199+
200+ if (!$ this ->isAcceptableUserId ($ userData ['formatted ' ]['uid ' ] ?? null )) {
201+ $ uid = is_scalar ($ userData ['formatted ' ]['uid ' ] ?? null ) ? (string )$ userData ['formatted ' ]['uid ' ] : '' ;
202+ $ this ->logger ->error ('No valid uid given, please check your attribute mapping. Got uid: {uid} ' , [
203+ 'app ' => 'user_oidc ' ,
204+ 'uid ' => $ uid ,
205+ ]);
206+ throw new \InvalidArgumentException ('No valid uid given, please check your attribute mapping. Got uid: ' . $ uid );
195207 }
196208
197209 return $ userData ;
@@ -394,6 +406,15 @@ public function getCurrentUserId(): string {
394406 return '' ;
395407 }
396408
409+ /**
410+ * Returns true only if $userId is a non-empty, non-whitespace-only string.
411+ * Used as a lightweight sanity check on user IDs returned by token validators
412+ * before any database lookup or provisioning takes place.
413+ */
414+ private function isAcceptableUserId (mixed $ userId ): bool {
415+ return is_string ($ userId ) && $ userId !== '' && trim ($ userId ) !== '' ;
416+ }
417+
397418 /**
398419 * Set the user in IUserSession after bearer token validation.
399420 * Without this, DI-injected $userId is null in OCS controllers
@@ -420,55 +441,65 @@ private function setSessionUser(string $userId): void {
420441 }
421442
422443 /**
423- * Inspired by lib/private/User/Session.php::prepareUserLogin()
424444 *
425- * @param string $userId
426- * @return bool
427- * @throws NotFoundException
445+ * Performs first-login initialisation (home folder setup, skeleton copy, events)
446+ * if the user has never logged in before, then updates the last-login timestamp.
447+ * Inspired by lib/private/User/Session.php::prepareUserLogin().
428448 */
429449 private function checkFirstLogin (string $ userId ): bool {
430450 $ user = $ this ->userManager ->get ($ userId );
431-
432451 if ($ user === null ) {
433452 return false ;
434453 }
435454
436455 $ firstLogin = $ user ->getLastLogin () === 0 ;
437456 if ($ firstLogin ) {
438- /** @psalm-suppress UndefinedVariable Replace with ServerVersion once we depends on NC 31 */
439- $ OC_Version [0 ] >= 34 ? Server::get (ISetupManager::class)->setupForUser ($ user ) : \OC_Util::setupFS ($ userId );
440- // trigger creation of user home and /files folder
441- $ userFolder = Server::get (IRootFolder::class)->getUserFolder ($ userId );
457+ /** @psalm-suppress UndefinedVariable Replace with ServerVersion once we depend on NC 31 */
458+ if ($ OC_Version [0 ] >= 34 ) {
459+ Server::get (ISetupManager::class)->setupForUser ($ user );
460+ } else {
461+ \OC_Util::setupFS ($ userId );
462+ }
463+
442464 try {
465+ // trigger creation of user home and /files folder
466+ $ userFolder = Server::get (IRootFolder::class)->getUserFolder ($ userId );
443467 // copy skeleton
444468 \OC_Util::copySkeleton ($ userId , $ userFolder );
445- } catch (NotPermittedException $ ex ) {
446- // read only uses
469+ } catch (NotFoundException | NotPermittedException $ ex ) {
470+ $ this -> logger -> warning ( ' Could not set up user folder on first login ' , [ ' exception ' => $ ex ]);
447471 }
448472
449- // trigger any other initialization
450473 $ this ->eventDispatcher ->dispatch (IUser::class . '::firstLogin ' , new GenericEvent ($ user ));
451474 $ this ->eventDispatcher ->dispatchTyped (new UserFirstTimeLoggedInEvent ($ user ));
452475 }
476+
453477 $ user ->updateLastLoginTimestamp ();
454478 return $ firstLogin ;
455479 }
456480
457481 /**
458482 * Triggers user provisioning based on the provided strategy
459- *
460- * @param string $provisioningStrategyClass
461- * @param Provider $provider
462- * @param string $tokenUserId
463- * @param string $headerToken
464- * @param IUser|null $existingUser
465- * @return IUser|null
466483 */
467484 private function provisionUser (
468- string $ provisioningStrategyClass , Provider $ provider , string $ tokenUserId , string $ headerToken ,
485+ string $ provisioningStrategyClass ,
486+ Provider $ provider ,
487+ string $ tokenUserId ,
488+ string $ headerToken ,
469489 ?IUser $ existingUser ,
470490 ): ?IUser {
471- $ provisioningStrategy = Server::get ($ provisioningStrategyClass );
472- return $ provisioningStrategy ->provisionUser ($ provider , $ tokenUserId , $ headerToken , $ existingUser );
491+ try {
492+ $ provisioningStrategy = Server::get ($ provisioningStrategyClass );
493+ return $ provisioningStrategy ->provisionUser ($ provider , $ tokenUserId , $ headerToken , $ existingUser );
494+ } catch (Throwable $ e ) {
495+ $ this ->logger ->error ('Failed to provision user via strategy ' , [
496+ 'strategy ' => $ provisioningStrategyClass ,
497+ 'providerId ' => $ provider ->getId (),
498+ 'providerIdentifier ' => $ provider ->getIdentifier (),
499+ 'userId ' => $ tokenUserId ,
500+ 'exception ' => $ e ,
501+ ]);
502+ return null ;
503+ }
473504 }
474505}
0 commit comments