<?php
namespace App\Security\User;
use BigIdea\IdentityBundle\Entity\TeamMate;
use BigIdea\IdentityBundle\Repository\TeamMateRepository;
use BigIdea\IdentityBundle\Security\Factory\SsoUserFactoryInterface;
use Drenso\OidcBundle\Model\OidcUserData;
use Drenso\OidcBundle\Security\UserProvider\OidcUserProviderInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* TeamMate provider that re-links legacy/fixture users by email on first SSO login.
*/
final class EmailLinkingTeamMateProvider implements OidcUserProviderInterface
{
public function __construct(
private SsoUserFactoryInterface $factory,
private TeamMateRepository $repository,
) {
}
public function refreshUser(UserInterface $user): UserInterface
{
if (!$user instanceof TeamMate) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($user)));
}
return $this->loadOidcUser($user->getUserIdentifier());
}
public function supportsClass(string $class): bool
{
return $class === TeamMate::class;
}
public function loadUserByIdentifier(string $identifier): UserInterface
{
$user = $this->repository->findByEmail($identifier);
$this->assertUserFound($user, $identifier);
if (!$user->isAllowedPasswordAuthentication()) {
throw new AccessDeniedException();
}
return $user;
}
public function loadUserByUsername(string $username): UserInterface
{
return $this->loadUserByIdentifier($username);
}
public function ensureUserExists(string $userIdentifier, OidcUserData $userData): void
{
$user = $this->repository->findBySsoIdentifier($userIdentifier);
if (null === $user) {
$email = $userData->getEmail();
if (is_string($email) && '' !== $email) {
$user = $this->repository->findByEmail($email);
}
if (null === $user) {
$user = $this->factory->create($userIdentifier, $userData);
} else {
$this->relinkSsoIdentifier($user, $userIdentifier);
$this->factory->update($user, $userData);
}
} else {
$this->factory->update($user, $userData);
}
$this->repository->save($user);
}
public function loadOidcUser(string $userIdentifier): UserInterface
{
$user = $this->repository->findBySsoIdentifier($userIdentifier);
$this->assertUserFound($user, $userIdentifier);
return $user;
}
private function relinkSsoIdentifier(TeamMate $user, string $ssoIdentifier): void
{
$property = new \ReflectionProperty(TeamMate::class, 'ssoIdentifier');
$property->setValue($user, $ssoIdentifier);
}
private function assertUserFound(?UserInterface $user, int|string $identifier): void
{
if (null === $user) {
$ex = new UserNotFoundException();
$ex->setUserIdentifier((string) $identifier);
throw $ex;
}
}
}