src/Security/UserLoader.php line 24

Open in your IDE?
  1. <?php
  2. // src/Security/UserLoader.php
  3. namespace App\Security;
  4. use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
  5. use Symfony\Component\Security\Core\Exception\UserNotFoundException;
  6. use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
  7. use Symfony\Component\Security\Core\User\UserInterface;
  8. use Symfony\Component\Security\Core\User\UserProviderInterface;
  9. use Doctrine\ORM\EntityManagerInterface;
  10. use App\AppUtils\AppGoogleClient;
  11. use Symfony\Component\HttpFoundation\RequestStack;
  12. use App\Entity\Customer;
  13. use App\Entity\CustomerOtherDomain//domini alias o sottodomini google Workspace
  14. class UserLoader implements UserProviderInterfacePasswordUpgraderInterface
  15. {
  16.     // proprietà aggiuntive
  17.     private $customerID="";
  18.     private $em;
  19.     private $session;
  20.     //costruttore per iniettare strumenti utili negli altri metodi
  21.     public function __construct(EntityManagerInterface $emRequestStack $requestStack){
  22.         $this->session $requestStack->getSession(); //recupera la sessione e ne salva il riferimento nell'autenticatore
  23.         $this->em $em//recupera l'entity manager doctrine
  24.     }
  25.         /**
  26.      * The loadUserByIdentifier() method was introduced in Symfony 5.3.
  27.      * In previous versions it was called loadUserByUsername()
  28.      *
  29.      * Symfony calls this method if you use features like switch_user
  30.      * or remember_me. If you're not using these features, you do not
  31.      * need to implement this method.
  32.      *
  33.      * @throws UserNotFoundException if the user is not found
  34.      */
  35.     public function loadUserByIdentifier(string $identifier): UserInterface
  36.     {
  37.         //recupera la sessione per poter salvare i dati utili
  38.         if (filter_var($identifierFILTER_VALIDATE_EMAIL)) {
  39.             $email $identifier;
  40.             $emailesplosa=explode('@',$email); // esplode l'indirizzo mail in due stringhe prima e dopo la chiocciola @
  41.             $username=$emailesplosa[0]; //estrae lo username dalla mail
  42.             $dominio $emailesplosa[1]; //estrae il dominio dalla mail
  43.         } else {
  44.             throw new \Exception("Identitificativo {$identifier} non riconosciuto come email valida");
  45.         }
  46.         //### PORZIONE PER SUPERADMIN SCAN2GO, azionata soltanto se in sessione c'è l'apposito trigger
  47.         if ( isset($_ENV['SUPERADMINS']) && is_string($_ENV['SUPERADMINS']) && $_ENV['SUPERADMINS'] != "" && $this->session->get('SuperAdminAuth') ){
  48.             $superadminsArray json_decode($_ENV['SUPERADMINS']); //dopo aver controllato che esista, prelevo l'array dei superadmin dall'environment
  49.             if ($superadminsArray != false && is_array($superadminsArray)){
  50.                 //in questo caso debbo verificare che l'utente sia superadmin
  51.                 if ( in_array($email,$superadminsArray) ){
  52.                     //in questo caso ho verificato che è un superadmin, quindi creo e restituisco l'oggetto utente
  53.                     //creo l'utente
  54.                     $utente = new User;
  55.                     $utente->setEmail($email);
  56.                     $ruoliSA=['ROLE_SUPERADMIN'];
  57.                     $utente->setRoles($ruoliSA);
  58.                     //lo salvo in sessione e poi lo ritorno
  59.                     $this->session->set('email',$email);
  60.                     $this->session->set('roles',$ruoliSA);
  61.                     //salvo altri dati utili in sessione
  62.                     //nome utente (troncato a 30 caratteri) e nome utente completo
  63.                     $un explode('@'$email)[0]; //separo il nome utente dall'indirizzo email
  64.                     if(strlen($un)>30){ //nel caso in cui l'utente sia più lungo di 30 caratteri, taglio in modo da non infastidire il layout interfaccia
  65.                         $unc $un;
  66.                         $un substr($un,0,30).'...'//sovrascrivo il nomeutente con i primi 30 caratteri dell'originale e aggiungo tre puntini di sospensione
  67.                     }
  68.                     else {$unc null;}
  69.                     $this->session->set('nomeutente',$un); //salvo in sessione il nomeutente
  70.                     $this->session->set('nomeutentecompleto',$unc); //salvo in sessione il nomeutentecompleto
  71.                     //Chiudo il service e l'AGC inutile in linea di massima, tanto per precauzione
  72.                     $service null;
  73.                     $AGC null;
  74.                     return $utente;
  75.                 } else {} //prosegue i controlli normalmente
  76.             } else {} //prosegue i controlli normalmente
  77.         } else {
  78.             $this->session->remove('SuperAdminAuth'); //per sicurezza rimuovo dalla sessione il valore trigger
  79.         //prosegue i controlli normalmente
  80.         //### FINE PORZIONE PER SUPERADMIN SCAN2GO
  81.         //PORZIONE DI CODICE PER DOMINI ALIAS / SOTTODOMINI ETC.
  82.         $customerOtherDomain $this->session->get('customerOtherDomain'); //leggo dalla sessione l'eventuale presenza del parametro che segnala il fatto che l'utente usa un dominio alternativo
  83.         $customerOtherDomain = (int) $customerOtherDomain//converto in intero
  84.         if ( $customerOtherDomain != null && $customerOtherDomain ){
  85.             $tempRecord=$this->em->getRepository(Customer::class)->find($customerOtherDomain);
  86.             if ($tempRecord != null){
  87.                 //tutto ok, sovrascrivo $dominio con il gDomain corrispondente al tempRecord, di modo da evitare problemi
  88.                 $dominio $tempRecord->getGDomain();
  89.             }
  90.             else { //in teoria non ci troviamo MAI in questa situazione, dato che questi controlli sono già stati passati al passaggio precedente in PfSenseTokenAuthenticator
  91.                 throw new UserNotFoundException("Customer Other Domain: {$dominio} non riconosciuto! Cid:{$customerOtherDomain}");
  92.             }
  93.         } else { //nel caso in cui non ci troviamo in corrispondenza di alias / sottodomini alternativi etc. eseguo il codice normale
  94.             //dentro questo else c'era la porzione di codice eseguita in precedenza
  95.             $tempRecord=$this->em->getRepository(Customer::class)->findOneBy([ //ricerca il record customer corrispondente al dominio passato
  96.                 'gDomain' => $dominio
  97.             ]);
  98.         }
  99.         //FINE PORZIONE DI CODICE PER DOMINI ALIAS / SOTTODOMINI ETC.
  100.         $codMec $tempRecord->getCodMec();
  101.         $customerID $tempRecord->getGCustomerId();
  102.         //Non occorre che faccia check particolari, in quanto l'app authenticator dello step precedente li ha già fatti
  103.         try {
  104.             $scopesArray=json_decode($_ENV['GAPI_SCOPES'],true); //permessi da richiedere sulle GAPI
  105.             $AGC = new AppGoogleClient($this->em,$codMec,$customerID,false,$scopesArray); // istanzia il client em doctrine, meccanografico,google customer id, batch mode, array di scopes gapi
  106.         } catch (\Exception $e){
  107.             $errore $e->getMessage();
  108.             $this->session->set('googleAuthUrl',$errore);
  109.             $this->session->set('customer',$tempRecord);
  110.             throw new UserNotFoundException($errore);
  111.         }
  112.         try {
  113.             $service = new \Google_Service_Directory($AGC->getClient()); // recupera il service della Directory
  114.             //$this->session->set('scopesDebug',$_ENV['GAPI_SCOPES']);
  115.         } catch (\Exception $e){
  116.             $errore $e->getMessage();
  117.             //$this->session->set('scopesDebug',$_ENV['GAPI_SCOPES']);
  118.             throw new UserNotFoundException($errore);
  119.         }
  120.         $utenti $service->users//recupera l'oggetto che lavora sugli utenti della directory
  121.         $membri $service->members//recupera l'oggetto che lavora sulle memberships a qualsiasi gruppo della gSuite in questione
  122.         $ruoliutente=array(); //array vuoto in cui fare il push dei ruoli
  123.         $contatoreOccorrenze=0;
  124.         //### Prelevo i gruppi gSuite sui quali fare le assegnazioni dei ruoli, direttamente dal database
  125.         $gruppoGsuiteOperatori=$tempRecord->getGruppoGsuiteOperatori();
  126.         $gruppoGsuiteAmministratori=$tempRecord->getGruppoGsuiteAmministratori();
  127.         $gruppoGsuiteIncaricati=$tempRecord->getGruppoGsuiteIncaricati();
  128.         $mappaturaGSuiteApp=array(
  129.             $gruppoGsuiteAmministratori => 'ROLE_ADMIN',
  130.             $gruppoGsuiteOperatori => 'ROLE_OPERATORE',
  131.         );
  132.         //### Per retrocompatibilità, procedo all'inserimento del controllo del ruolo incaricato, solamente quando esiste nei gruppi definiti all'interno del customer
  133.         if ($gruppoGsuiteIncaricati != null){
  134.             $mappaturaGSuiteApp[$gruppoGsuiteIncaricati] = 'ROLE_INCARICATO';
  135.         } else {
  136.             //### Implementazione gruppi di incaricati multipli (abbinamento per plesso)
  137.             //prelevo la configurazione extra del customer
  138.             $configCustomer $tempRecord->getConfig();
  139.             if ($configCustomer != null && isset($configCustomer['gruppiIncaricatiEPlessi']) && $configCustomer['gruppiIncaricatiEPlessi'] != null && is_array($configCustomer['gruppiIncaricatiEPlessi'])){
  140.                 //$configCustomer['gruppiIncaricatiEPlessi'] è un array in cui ciascun elemento è un abbinamento plesso -> gruppoIncaricati
  141.                 $abbinamentiPlessoIncaricati $configCustomer['gruppiIncaricatiEPlessi'];
  142.                 //ciclo gli abbinamenti
  143.                 $gruppiAbbinatiPrecedenti = [];
  144.                 $plessiAbbinati = [];
  145.                 foreach ($abbinamentiPlessoIncaricati as $plesso=>$gruppoInc){
  146.                     //caso in cui lo stesso gruppo è abbinato a più plessi, risparmio una chiamata api e procedo già ad aggiungere il plesso tra quelli abbinati al delegato corrente
  147.                     if ( in_array($gruppoInc,$gruppiAbbinatiPrecedenti) ){
  148.                         array_push($plessiAbbinati,$plesso); //inserisco nei plessi abbinati quello iterato
  149.                         $effettuaApiCall false//disabilito l'if successivo
  150.                     } else {
  151.                         $effettuaApiCall true//abilito l'if successivo
  152.                     }
  153.                     //nel caso in cui sia effettivamente membro del gruppo
  154.                     if ($effettuaApiCall && $membri->hasMember($gruppoInc.'@'.$dominio,$email)->getIsMember()){
  155.                         array_push($plessiAbbinati,$plesso); //inserisco nei plessi abbinati quello iterato
  156.                         array_push($gruppiAbbinatiPrecedenti,$gruppoInc); //inserisco il gruppo corrente in quelli già abbinati al delegato corrente
  157.                     } else {}
  158.                 }
  159.                 if (count($gruppiAbbinatiPrecedenti)>0){
  160.                     //se ho effettuato almeno un abbinamento attribuisco il ruolo di incaricato
  161.                     array_push($ruoliutente,'ROLE_INCARICATO');
  162.                     $this->session->set('plessiAbbinati',$plessiAbbinati); //salvo in sessione l'abbinamento plesso
  163.                 } else {}
  164.             } else {} //nel caso in cui non sia implementata nel customer, non faccio più niente
  165.         }
  166.         //Check esistenza in GSuite , verifica prima se l'utente è un admin di gsuite nel caso gli da ROLE_ADMIN
  167.         $utente=$utenti->get($email);
  168.         if( $utente!=null ){
  169.             //PORZIONE COMMENTATA e sostituita di riga sottostante PER EVITARE CHE IL GSADMIN RICEVA RUOLO ADMIN
  170.             /*if($utente->getIsAdmin()){ //nel caso in cui l'utente selezionato sia admin gsuite
  171.                 array_push($ruoliutente,'ROLE_ADMIN'); //gli do il ruolo di admin gsuite
  172.             }
  173.             else { 
  174.                 array_push($ruoliutente,'ROLE_USER'); //ruolo utente viene attribuito a tutti
  175.             }*/
  176.             array_push($ruoliutente,'ROLE_USER'); //ruolo utente viene attribuito a tutti
  177.         }
  178.         else {
  179.             //Se l'utente è null
  180.             throw new \Exception("Nessun Utente Rintracciato con email = {$identifier}");
  181.         }
  182.         $primoCheck=true//il flag del primo check mi serve per bypassare gli altri check nel caso in cui l'utente venga riconosciuto come amministratore (eredita automaticamente tutti i ruoli inferiori nella gerarchia)
  183.         foreach($mappaturaGSuiteApp as $gruppo_gsuite=>$ruolo_attribuito){
  184.             if ($membri->hasMember($gruppo_gsuite.'@'.$dominio,$email)->getIsMember()){
  185.                 array_push($ruoliutente,$ruolo_attribuito);
  186.                 $contatoreOccorrenze++;
  187.             }
  188.             if(($contatoreOccorrenze>0) && $primoCheck){ //qualora l'utente sia stato riconosciuto come app_admin, interrompo perché eredita già il ruolo app_docente in automatico, per via della gerarchia
  189.                 break;
  190.             }
  191.             $primoCheck=false//impone a false il valore del primocheck, in vista dei prossimi giri di controllo
  192.         }
  193.         //se c'è stata almeno un'occorrenza nell'appartenenza ai gruppi, procedo alla creazione dell'utente
  194.         //if($contatoreOccorrenze>0){
  195.         //creo l'utente
  196.         $utente = new User;
  197.         $utente->setEmail($email);
  198.         $utente->setRoles($ruoliutente);
  199.         //lo salvo in sessione e poi lo ritorno
  200.         $this->session->set('email',$email);
  201.         $this->session->set('roles',$ruoliutente);
  202.         $this->session->set('dominio',$dominio);
  203.         $codMec=$tempRecord->getCodMec();
  204.         $this->session->set('codMec',$codMec);
  205.         $sqlIDC=$tempRecord->getId();
  206.         $this->session->set('sqlIDC',$sqlIDC);
  207.         $customerID=$tempRecord->getGCustomerId();
  208.         $this->session->set('gCID',$customerID);
  209.         //salvo altri dati utili in sessione
  210.         //nome utente (troncato a 30 caratteri) e nome utente completo
  211.         $un explode('@'$email)[0]; //separo il nome utente dall'indirizzo email
  212.         if(strlen($un)>30){ //nel caso in cui l'utente sia più lungo di 30 caratteri, taglio in modo da non infastidire il layout interfaccia
  213.             $unc $un;
  214.             $un substr($un,0,30).'...'//sovrascrivo il nomeutente con i primi 30 caratteri dell'originale e aggiungo tre puntini di sospensione
  215.         }
  216.         else {$unc null;}
  217.         $this->session->set('nomeutente',$un); //salvo in sessione il nomeutente
  218.         $this->session->set('nomeutentecompleto',$unc); //salvo in sessione il nomeutentecompleto
  219.         //Chiudo il service e l'AGC inutile in linea di massima, tanto per precauzione
  220.         $service null;
  221.         $AGC null;
  222.         return $utente;
  223.     }
  224.     public function loadUserByUsername($username): UserInterface
  225.     {
  226.         throw new \Exception('TODO: fill in loadUserByUsername() inside '.__FILE__);
  227.     }
  228.     /**
  229.      * Refreshes the user after being reloaded from the session.
  230.      *
  231.      * When a user is logged in, at the beginning of each request, the
  232.      * User object is loaded from the session and then this method is
  233.      * called. Your job is to make sure the user's data is still fresh by,
  234.      * for example, re-querying for fresh User data.
  235.      *
  236.      * If your firewall is "stateless: true" (for a pure API), this
  237.      * method is not called.
  238.      *
  239.      * @return UserInterface
  240.      */
  241.     public function refreshUser(UserInterface $user)
  242.     {
  243.         if (!$user instanceof User) {
  244.             throw new UnsupportedUserException(sprintf('Invalid user class "%s".'get_class($user)));
  245.         }
  246.         if( ($this->session->get('userRefresh') !== null) && ($this->session->get('userRefresh') == true) ){
  247.             //INSERIRE IL REFRESH DELL'UTENTE, idealmente un richiamo al loadUserByIdentifier
  248.         }
  249.         else {
  250.             return $user;
  251.         }
  252.         // Return a User object after making sure its data is "fresh".
  253.         // Or throw a UserNotFoundException if the user no longer exists.
  254.         throw new \Exception('TODO: fill in refreshUser() inside '.__FILE__);
  255.     }
  256.     /**
  257.      * Tells Symfony to use this provider for this User class.
  258.      */
  259.     public function supportsClass(string $class): bool
  260.     {
  261.         return User::class === $class || is_subclass_of($classUser::class);
  262.     }
  263.     /**
  264.      * Upgrades the hashed password of a user, typically for using a better hash algorithm.
  265.      */
  266.     public function upgradePassword(UserInterface $userstring $newHashedPassword): void
  267.     {
  268.         // TODO: when hashed passwords are in use, this method should:
  269.         // 1. persist the new password in the user storage
  270.         // 2. update the $user object with $user->setPassword($newHashedPassword);
  271.     }
  272. }