<?php
namespace App\Security;
use App\Entity\Company;
use App\Entity\Log;
use App\Entity\Role;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Client\OAuth2ClientInterface;
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
class KeycloakAuthenticator extends SocialAuthenticator
{
private ClientRegistry $clientRegistry;
private EntityManagerInterface $em;
private RouterInterface $router;
public function __construct(ClientRegistry $clientRegistry, EntityManagerInterface $em, RouterInterface $router)
{
$this->clientRegistry = $clientRegistry;
$this->em = $em;
$this->router = $router;
}
public function start(Request $request, AuthenticationException $authException = null)
{
return new RedirectResponse(
'/login', // might be the site, where users choose their oauth provider
Response::HTTP_TEMPORARY_REDIRECT
);
}
public function supports(Request $request)
{
return 'openid_check_auth' === $request->attributes->get('_route');
}
public function getCredentials(Request $request)
{
return $this->fetchAccessToken($this->getKeycloakClient());
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$keycloakUser = $this->getKeycloakClient()->fetchUserFromToken($credentials);
$existingUser = $this
->em
->getRepository(User::class)
->findOneBy(['keycloakId' => $keycloakUser->getId()]);
if ($existingUser) {
return $existingUser;
}
// if user exist but never connected with keycloak
$email = $keycloakUser->getEmail();
/** @var User $userInDatabase */
$userInDatabase = $this->em->getRepository(User::class)
->findOneBy(['email' => $email]);
if($userInDatabase) {
$userInDatabase->setKeycloakId($keycloakUser->getId());
$this->em->persist($userInDatabase);
$this->em->flush();
return $userInDatabase;
}
$keycloakUserArray = $keycloakUser->toArray();
//user not exist in database =======> CREATE
$user = new User();
$user->setKeycloakId($keycloakUser->getId());
$user->setEmail($keycloakUser->getEmail());
//Roles
if(isset($keycloakUserArray['roles'])){
if(in_array('kinalgo-user',$keycloakUserArray['roles'])){
$user_role = $this->em->getRepository(Role::class)->findOneBy(['name' => User::ROLE_USER_ACTIF]);
}
else if(in_array('kinalgo-rh',$keycloakUserArray['roles'])){
$user_role = $this->em->getRepository(Role::class)->findOneBy(['name' => 'ROLE_RH']);
}
else if(in_array('kinalgo-admin',$keycloakUserArray['roles'])){
$user_role = $this->em->getRepository(Role::class)->findOneBy(['name' => 'ROLE_ADMIN']);
}
else{
$user_role = $this->em->getRepository(Role::class)->findOneBy(['name' => User::ROLE_USER_ACTIF]);
}
}
else{
$user_role = $this->em->getRepository(Role::class)->findOneBy(['name' => User::ROLE_USER_ACTIF]);
}
$user->setRoles($user_role);
//company
if(isset($keycloakUserArray['groups'])){
$companyName = str_replace('/',"",$keycloakUserArray['groups'][0]);
$company = $this->em->getRepository(Company::class)->findOneBy(['slug' => trim($companyName)]);
}
if(!isset($company) || !$company) return false;
if($company){
$user->setCompany($company);
}
$user->setFirstname($keycloakUserArray['given_name']);
$user->setLastname($keycloakUserArray['family_name']);
$this->em->persist($user);
$this->em->flush();
return $user;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$message = strtr($exception->getMessageKey(), $exception->getMessageData());
return new Response($message, Response::HTTP_FORBIDDEN);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey)
{
/* @var $user User*/
$user = $token->getUser();
if($user->getLastLoginAt() !== null){
$this->setLastLoginAt($user);
$targetUrl = $this->router->generate('user_dashboard');
}
else{
$this->setLastLoginAt($user);
$targetUrl = $this->router->generate('profil');
}
return new RedirectResponse($targetUrl);
}
/**
* @return OAuth2ClientInterface
*/
private function getKeycloakClient()
{
return $this->clientRegistry->getClient('keycloak');
}
private function setLastLoginAt($user){
//TODO à voir si on garde le champ lastLoginAt ou si on utilise celui des logs
$date = new \DateTime("now", new \DateTimeZone("Europe/Paris"));
$user->setLastLoginAt($date);
$this->em->persist($user);
//Persistence en BD pour les stats - date sur createdAt
$log = new Log();
$log->setUser( $user );
$log->setEvent('login');
$log->setSources(['ip'=>$_SERVER['REMOTE_ADDR'],'date'=>$date->format('Y-m-d H:i')]);
$this->em->persist($log);
$this->em->flush();
}
}