Partage
  • Partager sur Facebook
  • Partager sur Twitter

Symfony - Utilisateur sans mot de passe (annuaire)

    26 juillet 2017 à 12:22:43

    Bonjour,

    J'utilise le Framework Symfony dans sa dernière version. Je suis en train de suivre le tutoriel (et encore merci à l'auteur au passage, le tutoriel est vraiment bien foutu). Néanmoins, j'ai un point de blocage et si l'un d'entre vous pouvait éclairer ma lanterne, j'en serai reconnaissant !

    Le contexte :

    Je suis en train de gérer la partie sécurité et utilisateur du site. Comme beaucoup de sites (pour faire simple) certains de mes menus apparaissent et sont accessibles que pour des utilisateurs "connectés". Faisant partie d'une grande entreprise, je ne gère pas la partie utilisateur. En effet, ma page de connexion est définit dans une directive d'apache pour permettre à l'utilisateur de se connecter via son compte présent dans nos annuaires LDAP internes, totalement indépendants de mon application. La manière dont est structurée et organisée la partie gestion utilisateur présenté dans le tutoriel de Symfony est très sympa, mais comment gérer mon cas ?

    En gros, je me suis contente de définir une entité User:

    <?php
    
    namespace projet\UserBundle\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    use Symfony\Component\Security\Core\User\UserInterface;
    
    /**
     * @ORM\Table(name="user")
     * @ORM\Entity(repositoryClass="projet\UserBundle\Repository\UserRepository")
     */
    class User implements UserInterface
    {
      /**
       * @ORM\Column(name="id", type="integer")
       * @ORM\Id
       * @ORM\GeneratedValue(strategy="AUTO")
       */
      private $id;
    
      /**
       * @ORM\Column(name="user_name", type="string", length=100)
       */
      private $userName;
    
      /**
       * @ORM\Column(name="user_first_name", type="string", length=100)
       */
      private $userFirstName;
    
      /**
       * @ORM\Column(name="intern_id", type="string", length=8, unique=true)
       */
      private $intern_id;
    
      /**
       * @ORM\Column(name="mail", type="string", length=100)
       */
      private $mail;
    
      /**
       * @ORM\Column(name="roles", type="array")
       */
      private $roles = array();
    
      public function eraseCredentials()
      {
      }
    
      public function getRoles(){}
    
      public function getPassword(){}
    
      public function getSalt(){}
    
      public function getUsername(){}
    
    
        /**
         * Get id
         *
         * @return integer
         */
        public function getId()
        {
            return $this->id;
        }
    
        /**
         * Set userName
         *
         * @param string $userName
         *
         * @return User
         */
        public function setUserName($userName)
        {
            $this->userName = $userName;
    
            return $this;
        }
    
        /**
         * Set userFirstName
         *
         * @param string $userFirstName
         *
         * @return User
         */
        public function setUserFirstName($userFirstName)
        {
            $this->userFirstName = $userFirstName;
    
            return $this;
        }
    
        /**
         * Get userFirstName
         *
         * @return string
         */
        public function getUserFirstName()
        {
            return $this->userFirstName;
        }
    
        /**
         * Set intern_id
         *
         * @param string $intern_id
         *
         * @return User
         */
        public function setInternId($intern_id)
        {
            $this->intern_id= $intern_id;
    
            return $this;
        }
    
        /**
         * Get intern_id
         *
         * @return string
         */
        public function getInternId()
        {
            return $this->intern_id;
        }
    
        /**
         * Set roles
         *
         * @param array $roles
         *
         * @return User
         */
        public function setRoles($roles)
        {
            $this->roles = $roles;
    
            return $this;
        }
    
        /**
         * Set mail
         *
         * @param string $mail
         *
         * @return User
         */
        public function setMail($mail)
        {
            $this->mail = $mail;
    
            return $this;
        }
    
        /**
         * Get mail
         *
         * @return string
         */
        public function getMail()
        {
            return $this->mail;
        }
    }
    

    Un utilisateur se connecte avec "intern_id" et son mot de passe. Je ne souhaite pas stocker ces mots de passes dans ma base de données. Une fois l'authentification effectuée, sa connexion est stockée en session avec comme paramètre de session ce fameux "intern_id" (me permettant éventuellement de faire le lien avec la table dans ma base).

    Avez-vous des pistes concernant l'approche que je devrai avoir ? Vous l'aurez remarquer, je débute. J'ai plein d'idées quant à la manière de résoudre ce problème, mais je souhaite le faire de la bonne manière. Même un lien vers une doc me conviendrait !

    Merci d'avance à ceux qui auront pris le temps de me lire.

    Cordialement

    EDIT :

    Le fichier security.yml, du coup, très similaire à celui du tutoriel.

    # To get started with security, check out the documentation:
    # http://symfony.com/doc/current/security.html
    security:
        encoders:
            Symfony\Component\Security\Core\User\User: plaintext
            projet\UserBundle\Entity\\User: plaintext
    
        role_hierarchy:
            ROLE_ADMIN:       ROLE_USER
            ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
    
        providers:
            main:
                entity:
                    class: projet\UserBundle\Entity\User
                    property: intern_id
        
        provider: main
    
        firewalls:
            dev:
                pattern: ^/(_(profiler|wdt)|css|images|js)/
                security: false
            main:
                pattern:   ^/
                anonymous: true
                provider: main
                form_login:
                    login_path: login
                    check_path: login_check
                    default_target_path: home
                logout:
                    path:       logout
                    target:     login
    
        access_control:
            #- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }






    -
    Edité par ImCouscous 26 juillet 2017 à 12:36:33

    • Partager sur Facebook
    • Partager sur Twitter
      26 juillet 2017 à 12:55:25

      Bonjour,

      Si tu as un rôle pour l'utilisateur connecté alors c'est du classique pour autoriser ou pas un menu, non ?

      Au passage utilises-tu KnpMenuBundle ? https://symfony.com/doc/current/bundles/KnpMenuBundle/index.html

      A+

      • Partager sur Facebook
      • Partager sur Twitter
        26 juillet 2017 à 13:28:31

        monkey3d a écrit:

        Bonjour,

        Si tu as un rôle pour l'utilisateur connecté alors c'est du classique pour autoriser ou pas un menu, non ?

        Au passage utilises-tu KnpMenuBundle ? https://symfony.com/doc/current/bundles/KnpMenuBundle/index.html

        A+

        Bonjour,

        Déjà, merci de ta réponse. En fait ma problématique n'est pas liée aux menus (ce n'était qu'un exemple) mais à savoir comment je gère l'authentification via une base LDAP tout en conservant mes utilisateurs dans ma base mysql, sans y stocker le mot de passe.

        Donc que veux-tu dire par "c'est du classique ?". Je me suis sans doute mal exprimé, j'ai suivi le tutoriel de OC concernant la gestion d'utilisateur et de connexion mais je souhaiterai qu'elle se déroule via nos annuaires LDAP plutôt que via MySQL. Une fois connecté, je veux savoir comment on "gère" la session de ce dit utilisateur pour que je me retourne vis-à-vis du tutoriel. J'ai trouvé ce lien pendant ma pause : http://blog.henriet.eu/authentification-ldap-avec-symfony-2.1-et-fosuserbundle.html

        Est-ce une approche possible ? Désolé, mais je débute vraiment et j'essaie d'en comprendre le fonctionnement..

        • Partager sur Facebook
        • Partager sur Twitter
          26 juillet 2017 à 16:03:24

          J'avais compris que tu faisais le lien avec l'annuaire LDAP et ton appli par son inter_id qui figure dans ton entité User.

          Et dans ce cas, tu as le rôle de l'utilisateur à partir de ton entité User et donc tu peux gérer 'classiquement' les menus, pages et fonction accessibles dans ton appli en fonction du rôle.

          Alors certes on ne sait pas par quel process le inter_id arrive ou part de ton appli en synchro avec le LDAP mais ce n'était pas la question.

          Je pense que ton problème est mal formulé : ce que tu veux c'est avoir l'annuaire LDAP comme système d'authentification ?

          As tu regardé ce doc : https://symfony.com/doc/current/security/ldap.html

          Car ton lien est plutôt périmé : symfony 2.1 ....

          A+

          -
          Edité par monkey3d 26 juillet 2017 à 16:03:52

          • Partager sur Facebook
          • Partager sur Twitter
            26 juillet 2017 à 17:08:39

            Salut, 

            Une maniere simple d'integrer un ldap comme user provider est d'utiliser guard. Je te laisse lire la documentation associée.

            Voici un example de la classe associée. Elle utilise (a l'époque du projet, je ne sais pas s'il existe toujours), ldap.forumsys.com, un ldap libre d'acces pour tester que ca marche.

            Enjoy : https://gist.github.com/Stoakes/41b7d30a2dfd42059c9566b186cc1af4

            -
            Edité par _stoakes 2 août 2017 à 11:03:13

            • Partager sur Facebook
            • Partager sur Twitter
              27 juillet 2017 à 15:09:31

              J'ai réussi à me connecter via notre annuaire LDAP et je pense pouvoir mieux poser ma question.

              En gros :

              - J'ai défini un fichier Entity/User pour ma bdd MySql que j'ai complété "à la main". Il n'y a qu'une dizaine d'utilisateur pour le site.

              - J'ai un bouton "login". Quand on clique sur Login, ce dernier est intercepté par FOSuser et utilise FR3D-Bundle pour la partie authentification. Ce dernier va me permettre de me connecter à ma base LDAP, vérifier les "credentials" rentrer par l’utilisateur et s'assurer que la combinaison login/password fonctionne. Si cela fonctionne : la base de donnée et plus précisément la table "user" est enrichie automatiquement grâce aux informations remonté depuis mon annuaire.

              Cela fonctionne, c'est déjà un bon point.

              Ma problématique maintenant :

              Je veux qu'en réalité, quand un utilisateur tente de se connecter, la vérification se fasse au niveau de l'annuaire (comme c'est le cas actuellement) mais que si le mot de passe / nom utilisateur correspondent à une entrée dans mon annuaire, Symfony aille vérifier dans ma base si l'utilisateur existe déjà. Si oui, on permet la connexion. Si non, on refuse la connexion (alors qu'actuellement la connexion est autorisée et l'utilisateur va être ajouté à la base..). Petite précision : aucun mot de passe n'est présent dans ma base, c'est un volonté de séparée mon application de la partie authentification.

              Je sais pas si je suis un peu plus clair..

              Voici les différents codes :

              <?php
              
              namespace projet\UserBundle\Entity;
              
              use FOS\UserBundle\Model\User as BaseUser;
              use FR3D\LdapBundle\Model\LdapUserInterface as LdapUserInterface;
              use Doctrine\ORM\Mapping as ORM;
              
              /**
               * @ORM\Table(name="user")
               * @ORM\Entity(repositoryClass="projet\UserBundle\Repository\UserRepository")
               */
              class User extends BaseUser implements LdapUserInterface
              {
                /**
                 * @ORM\Column(name="id", type="integer")
                 * @ORM\Id
                 * @ORM\GeneratedValue(strategy="AUTO")
                 */
                protected $id;
              
                /**
                 * @ORM\Column(type="string")
                 */
                protected $dn;
              
                /**
                 * @ORM\Column(type="string")
                 */
                protected $name;
              
                /**
                 * @ORM\Column(type="string")
                 */
                protected $language;
              
                public function __construct()
                {
                   parent::__construct();
                   if (empty($this->roles)) {
                     $this->roles[] = 'ROLE_USER';
                   }
                }
                public function setDn($dn) {
                    $this->dn = $dn;
                }
                public function getDn() {
                    return $this->dn;
                }
                public function setName($name) {
                    $this->name = $name;
                }
                public function setLanguage($language) {
                    $this->language = $language;
                }
              
                  /**
                   * Get name
                   *
                   * @return string
                   */
                  public function getName()
                  {
                      return $this->name;
                  }
              
                  /**
                   * Get language
                   *
                   * @return string
                   */
                  public function getLanguage()
                  {
                      return $this->language;
                  }
              }
              
              # app/config/security.yml
              security:
                  encoders:
                      projet\UserBundle\Entity\User: plaintext
              
                  role_hierarchy:
                      ROLE_ADMIN:       ROLE_USER
                      ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
              
                  providers:
                      chain_provider:
                          chain:
                              providers: [fos_userbundle, fr3d_ldapbundle]
                      fr3d_ldapbundle:
                          id: fr3d_ldap.security.user.provider
                      fos_userbundle:
                          id: fos_user.user_provider.username
              
                  firewalls:
                      dev:
                          pattern: ^/(_(profiler|wdt)|css|images|js)/
                          security: false
                      main:
                          pattern:   ^/
                          fr3d_ldap: ~
                          anonymous: true
                          form_login:
                              provider: fos_userbundle
                              login_path: login
                              check_path: login_check
                              csrf_token_generator: security.csrf.token_manager
                              always_use_default_target_path: true
                              default_target_path: /home
                          logout:
                              path:       logout
                              target:     login
              
                  access_control:
                      #- { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
              framework:
                  csrf_protection: ~
                  ...
              
              # Doctrine Configuration
              doctrine:
                  dbal:
                      driver: pdo_mysql
                      host: '%database_host%'
                      port: '%database_port%'
                      dbname: '%database_name%'
                      user: '%database_user%'
                      password: '%database_password%'
                      charset: UTF8
                      # if using pdo_sqlite as your database driver:
                      #   1. add the path in parameters.yml
                      #     e.g. database_path: "%kernel.project_dir%/var/data/data.sqlite"
                      #   2. Uncomment database_path in parameters.yml.dist
                      #   3. Uncomment next line:
                      #path: '%database_path%'
              
                  orm:
                      auto_generate_proxy_classes: '%kernel.debug%'
                      naming_strategy: doctrine.orm.naming_strategy.underscore
                      auto_mapping: true
              
              fos_user:
                  db_driver: orm
                  firewall_name: main 
                  user_class: projet\UserBundle\Entity\User 
                  from_email:
                      address: you@example.com
                      sender_name: You
              
              fr3d_ldap:
                  driver:
                     host:     ipip
                  user:
                      baseDn: ou=people,o=domaindomain
                      filter: (&(ObjectClass=Person))
                      attributes:
                         - { ldap_attr: samaccountname,  user_method: setUsername } # champ login
                         - { ldap_attr: sn, user_method: setName }
                         - { ldap_attr: preferredlanguage, user_method: setLanguage }
                         - { ldap_attr: mail, user_method: setEmail } # setter dans BaseUser
              
              
              #app/config/routing.yml
              
              login:
                  path: /{_locale}/login
                  defaults:
                      _controller: ProjetUserBundle:Security:login
              
              login_check:
                  path: /{_locale}/login_check
              
              logout:
                  path: /{_locale}/logout
              <?php
              
              namespace projet\UserBundle\Controller;
              
              use Symfony\Bundle\FrameworkBundle\Controller\Controller;
              use Symfony\Component\HttpFoundation\Request;
              
              class SecurityController extends Controller
              {
                public function loginAction(Request $request)
                {
                  if ($this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
                    return $this->redirectToRoute('home');
                  }
              
                  $authenticationUtils = $this->get('security.authentication_utils');
              
                  return $this->render('ProjetUserBundle:Security:login.html.twig', array(
                    'last_username' => $authenticationUtils->getLastUsername(),
                    'error'         => $authenticationUtils->getLastAuthenticationError(),
                  ));
                }
              }

              Je sais, c'est un sacré casse tête dans lequel je me suis imposé.. mais si vous comprenez ma démarche, même si elle vous paraît tordu, c'est imposé par le contexte de l'entreprise..




              • Partager sur Facebook
              • Partager sur Twitter
                27 juillet 2017 à 16:42:20

                Salut ImCousous,
                Il te faut un authentication provider perso. Il te faudra un peut adapter mais, tout est là : https://symfony.com/doc/current/security/custom_authentication_provider.html

                -
                Edité par m_12_u 27 juillet 2017 à 16:46:30

                • Partager sur Facebook
                • Partager sur Twitter
                  27 juillet 2017 à 17:28:41

                  Je check ça ce soir ou demain, merci pour votre aide. Dans le cas où cela fonctionne je posterai mon code histoire d'aider les suivants. Je vous tiens au courant !
                  • Partager sur Facebook
                  • Partager sur Twitter
                    31 juillet 2017 à 15:05:14

                    Bonjour,

                    Avec mes faibles connaissances du Framework, j'ai du mal a m'en sortir..

                    J'ai tout repris à 0, ayant pour but de m'authentifier avec mon annuaire LDAP, je ferai la correspondance avec ma bdd plus tard. Mais même là je bloque..

                    J'ai définis le service LDAP :

                       Symfony\Component\Ldap\Ldap:
                            arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
                            
                        Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
                            arguments:
                                -   host: monserveurldap
                                    port: 389
                                    encryption: tls
                                    options:
                                        protocol_version: 3
                                        referrals: false

                    puis j'ai configuré le par feu et les providers (security.yml) :

                    security:
                        encoders:
                            Symfony\Component\Security\Core\User\User: plaintext
                    
                        role_hierarchy:
                            ROLE_ADMIN:       ROLE_USER
                            ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
                    
                        firewalls:
                            dev:
                                pattern: ^/(_(profiler|wdt)|css|images|js)/
                                security: false
                            main:
                                pattern:  ^/
                                anonymous: true
                                provider:  main
                                form_login_ldap:
                                    login_path: login
                                    check_path: login_check
                                    service: Symfony\Component\Ldap\Ldap
                                    dn_string: 'uid={username},ou=people,o=monentreprise'
                                logout:
                                    path:   login
                    
                        providers:
                            in_memory:
                                memory: ~
                            main:
                                ldap:
                                    service: Symfony\Component\Ldap\Ldap
                                    base_dn: ou=people,o=monentreprise
                                    search_dn: "uid=reader,ou=admin,o=monentreprise"
                                    search_password: password
                                    default_roles: ROLE_USER
                                    uid_key: uid

                    et le routing.yml principal :

                    login:
                        path: /{_locale}/login
                        defaults:
                            _controller: ProjetCoreBundle:Security:login
                    
                    login_check:
                        path: /{_locale}/login_check
                    
                    logout:
                        path: /{_locale}/logout

                    Je pense avoir un problème de route. Quand je tape sur projet/fr/monsite cela marche sans soucis. Quand je tape sur projet/fr/login, j'me tape une jolie 404 avec cette erreur :

                    request.ERROR: Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\NotFoundHttpException: "No route found for "GET /"" at C:\wamp64\www\projet\var\cache\dev\classes.php line 3652 {"exception":"[object] (Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException(code: 0): No route found for \"GET /\" at C:\\wamp64\\www\\projet\\var\\cache\\dev\\classes.php:3652, Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException(code: 0):  at C:\\wamp64\\www\\projet\\var\\cache\\dev\\appDevDebugProjectContainerUrlMatcher.php:193)"} []

                    En effet, l'url /projet/fr/login me redirige vers /projet.. je ne sais pas pourquoi. Je sais que le problème vient de là, qu'il me sort une 404 car il manque le préfix _locale, mais pourquoi il me fait une redirection ?

                    J'ai évidemment vidé le cache et supprimé les tokens de sessions..

                    Surement une erreur toute bête au niveau de la configuration du service, j'ai en effet suivi bêtement la documentation officielle. Quelqu'un pour éclairer ma lanterne ..? J'y arriverai !

                    Merci.. Désolé de vous solliciter mais j'ai jusqu'à vendredi pour mettre en place cette authentification :(

                    Edit : Mon SecurityController.php :

                    <?php
                    
                    namespace Projet\CoreBundle\Controller;
                    
                    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
                    use Symfony\Component\HttpFoundation\Request;
                    use Symfony\Component\HttpFoundation\Response;
                    
                    class SecurityController extends Controller
                    {
                        public function loginAction()
                        {
                    		$content = $this->renderView('ProjetCoreBundle:Security:login.html.twig');
                        
                        	return new Response($content); 
                        }
                    }




                    -
                    Edité par ImCouscous 31 juillet 2017 à 15:14:38

                    • Partager sur Facebook
                    • Partager sur Twitter
                      1 août 2017 à 15:34:00

                      Je me permets de rafraichir le sujet, n'ayant toujours pas de solution.. :(
                      • Partager sur Facebook
                      • Partager sur Twitter
                        2 août 2017 à 11:02:49

                        Salut,

                        Ton path de login est /{locale}/login.  C'est normal que /projet/{locale}/login ne fonctionne pas non ? (J'ai peut être loupé qqc).


                        En mode développement, la debug toolbar te dit quel pare-feu  a intercepté ta requête. Regardes de ce coté ça t'aidera peut être.

                        Je peut te fournir mon code et ma config pour se connecter avec un ldap, mais comme dit dans mon premier message, j'utilise Guard, pas le ldap component.

                        • Partager sur Facebook
                        • Partager sur Twitter
                          2 août 2017 à 13:00:30

                          Non toutes les pages fonctionnent correctement avec cette page.. et le pare-feu indiqué par la toolbar est bien "main". En fait / correspond à /projet/ pour mon site. L'erreur 404 est generée car il lui faut un /locale (fr ou en). Mais le soucis vient pas de la, c'est de la redirection qu'il fait automatiquement quand je tape /projet/fr/login vers /projet/. Je ne sais pas pourquoi il fait ça de lui même..

                          merci en tout cas de prendre du temps pour moi 

                          • Partager sur Facebook
                          • Partager sur Twitter

                          Symfony - Utilisateur sans mot de passe (annuaire)

                          × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                          × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                          • Editeur
                          • Markdown