Usando el FOSUserBundle para habilitar la administración de usuarios en Symfony2 e incluirle Roles

agosto 28, 2012 at 15:14 30 comentarios

El FOSUserBundle es un proyecto para Symfony (tanto para Symfony1 como para Symfony2) que nos permite el uso de un subsitema de Administración de usuarios.

Este bundle permite cosas como Registrar nuevos usuarios, enviar un mensaje de confirmación a los usuarios recién creados, editar mi perfil de usuario, encriptación de contraseñas y agrega un campo en la base de datos para poder guardar roles (incluye más características no comentadas en este artículo)

Instalación

Suponemos que symfony2 está en la ruta /var/www/symfony.

Para instalar y usar el FOSUserBundle llevamos a cabo los siguientes pasos:

  • Agregamos al archivo /var/www/deps la siguiente línea al final:
    [FOSUserBundle]
        git=git://github.com/FriendsOfSymfony/FOSUserBundle.git
        target=bundles/FOS/UserBundle
        version=1.2.0
    
  • Ahora corremos el script de vendors para instalarlo:
    php bin/vendors install
  • Editamos el archivo /var/www/symfony/app/autoload.php indicando allí que cargue el Bundle automáticamente:
    <?php
    // app/autoload.php
    
    registerNamespaces(array(
        // ...
        'FOS' => __DIR__.'/../vendor/bundles',
    ));
    
  • Ahora habilitamos el bundle en el archivo /var/www/symfony/app/AppKernel.php:
    <php
    // app/AppKernel.php
    
    public function registerBundles()
    {
        $bundles = array(
            // ...
            new FOS\UserBundle\FOSUserBundle(),
        );
    }
    

Uso

Suponemos que el bundle creado por nosotros se encuentra en la ruta /var/www/symfony/src/Mi/PropioBundle.

Para poder usar el FOSUserBundle realizamos los pasos siguientes:

  • Creamos la clase Usuario en la ruta /var/www/symfony/src/Mi/PropioBundle/Entity/Usuario.php con el contenido:
    <?php
     
    namespace Mi\PropioBundle\Entity;
     
    use Doctrine\ORM\Mapping as ORM;
    use FOS\UserBundle\Entity\User as BaseUser;
     
    /**
     * @ORM\Entity
     * @ORM\Table(name="Usuario")
     */
    class Usuario extends BaseUser
    {
    
        /**
         * @ORM\Column(type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        protected $id;
    
        /**
         * Agrega un rol al usuario.
         * @throws Exception
         * @param Rol $rol 
         */
        public function addRole( $rol )
        {
    	if($rol == 1) {
    	  array_push($this->roles, 'ROLE_ADMIN');
    	}
    	else if($rol == 2) {
    	  array_push($this->roles, 'ROLE_USER');
    	}
        }
    
    }
    

    Nota: agrego la función addRole para que dependiendo de un número entero que llega a través del parámetro $rol agregue al usuario que se está creando ya sea el rol Administrador o el rol usuario simple.

  • Configuramos la seguridad modificando el archivo /var/www/symfony/app/config/security.yml que es la configuración de seguridad del Symfony para que quede como el texto siguiente:
    security:
        encoders:
            "FOS\UserBundle\Model\UserInterface": sha512
    
        role_hierarchy:
            ROLE_ADMIN:             ROLE_USER
            ROLE_SUPER_ADMIN:       ROLE_ADMIN
    
        providers:
            fos_userbundle:
                id: fos_user.user_manager
    
        firewalls:
            main:
                pattern: ^/
                form_login:
                    provider: fos_userbundle
                    csrf_provider: form.csrf_provider
                logout: true
                anonymous: true
                security: true
    
    
        access_control:
            - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
            - { path: ^/register$, role: ROLE_ADMIN }
            - { path: ^/resetting$, role: ROLE_ADMIN }
            - { path: /admin*, roles: ROLE_ADMIN }
    
  • Ahora indicamos al Symfony que use nuestra clase Usuario para la autenticación, para ello agregamos al archivo /var/www/symfony/app/config/config.yml el siguiente texto:
    fos_user:
        db_driver: orm
        firewall_name: main
        user_class: Mi\PropioBundle\Entity\Usuario
        registration:
          form:
               type: mi_user_registration
    
  • En el archivo /var/www/syfmony/app/config/routing.yml agregamos las rutas que usa el FOSUserBundle:
    fos_user_security:
        resource: "@FOSUserBundle/Resources/config/routing/security.xml"
    
    fos_user_profile:
        resource: "@FOSUserBundle/Resources/config/routing/profile.xml"
        prefix: /profile
    
    fos_user_register:
        resource: "@FOSUserBundle/Resources/config/routing/registration.xml"
        prefix: /register
    
    fos_user_resetting:
        resource: "@FOSUserBundle/Resources/config/routing/resetting.xml"
        prefix: /resetting
    
    fos_user_change_password:
        resource: "@FOSUserBundle/Resources/config/routing/change_password.xml"
        prefix: /profile
    
  • Y a nuestro archivo de rutas (/var/www/symfony/src/Mi/PropioBundle/Resources/config/routing.yml) agregamos lo siguiente:
    login_check:
        pattern: /login_check
    
    logout:
        pattern: /logout
    

    Nota: podemos checkear nuestras rutas con el comando:

    php app/console router:debug
  • Actualizamos nuestra base de datos para que refleje los cambios hechos a la entidad Usuario:
    $ php app/console doctrine:schema:update --force

Reemplazando los formularios predeterminados

Ahora vamos a agregar nuestro propio formulario de login, la manera recomendada es reemplazar el archivo /var/www/symfony/vendor/bundles/FOS/UserBundle/Resources/views/Security/login.html.twig por nuestro propio formulario pero en mi caso quiero que la mayoría de archivos existan dentro de mi propio bundle por lo cual lo hago de la siguiente manera:

  • Agregar al archivo /var/www/symfony/src/Mi/PropioBundle/MiPropioBundle.php el texto:
    <?php
    
    namespace Mi\PropioBundle;
    
    use Symfony\Component\HttpKernel\Bundle\Bundle;
    
    class MiPropioBundle extends Bundle
    {
        public function getParent()
        {
    	return 'FOSUserBundle';
        }
    }
    
  • Seguidamente agregamos un controlador para manejar la seguridad, en este caso el archivo /var/www/src/Mi/PropioBundle/Controller/SecurityController.php con el contenido:
    <?php
     
    namespace Mi\PropioBundle\Controller;
     
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Symfony\Component\Security\Core\SecurityContext;
    use Symfony\Component\HttpFoundation\Response;
     
    class SecurityController extends Controller
    {
        public function loginAction()
        {
            $request = $this->getRequest();
            $session = $request->getSession();
    
            if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
                $error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
            } else {
                $error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
            }
    
            return $this->render('MiPropioBundle:Security:login.html.twig', array(
                // last username entered by the user
                'last_username' => $session->get(SecurityContext::LAST_USERNAME),
                'error'         => $error,
            ));
        }
    
        public function getTokenAction()
        {
            return new Response($this->container->get('form.csrf_provider')
                ->generateCsrfToken('authenticate'));
        }
    
    }
    
  • Y agregamos el formulario de login en la ruta /var/www/symfony/src/Mi/PropioBundle/Resources/views/Security/login.html.twig con un contenido similar al siguiente:

    {% extends "FOSUserBundle::layout.html.twig" %}
    {% block fos_user_content %}
    <div id="page-wrapper">
    <div id="main">
    {% if error %}
    <div id="login-error">{{ error.message|trans }}</div>
    {% endif %}
    <form action="{{ path('login_check') }}" method="post">
    <input type="hidden" name="_csrf_token" value="{% render('MiPropioBundle:Security:getToken') %}" />
    <div class="form-item">
    <label for="username">Usuario</label>
    <input id="username" type="text" name="_username" value="{{ last_username }}" />
    </div>
    <div class="form-item">
    <label for="password">Contraseña</label>
    <input id="password" type="password" name="_password" />
    </div>
    <input type="submit" name="login" value="Iniciar sesión" />
    </form>
    </div>
    </div>
    {% endblock fos_user_content %}
  • Ahora para manejar los roles vamos a modificar el constructor del formulario para registro de usuarios para agregar una lista de selección con los roles disponibles (ROLE_ADMIN, ROLE_USER), para ello agregamos el archivo /var/www/symfony/src/Mi/PropioBundle/Form/RegistrationFormType.php con el siguiente contenido:
    <?php
    
    namespace Mi\PropioBundle\Form;
    
    use Symfony\Component\Form\FormBuilder;
    use FOS\UserBundle\Form\Type\RegistrationFormType as BaseType;
    
    class RegistrationFormType extends BaseType
    {
        public function buildForm(FormBuilder $builder, array $options)     {
            parent::buildForm($builder, $options);
    
            $builder->add('roles', 'choice', array('label' => 'Rol', 'required' => true, 'choices' => array( 1 => 'ROLE_ADMIN', 2 => 'ROLE_USER'), 'multiple' => true));
    
        }
    
        public function getName() {
            return 'mi_user_registration';
        }
    }
    
  • Por último agregamos al archivo /var/www/symfony/src/Mi/PropioBundle/Resources/config/services.yml el texto:
    services:
        mi_propio.registration.form.type:
            class: Mi\PropioBundle\Form\RegistrationFormType
            arguments: [%fos_user.model.user.class%]
            tags:
                - { name: form.type, alias: mi_user_registration }
    

Ya podemos ir a la dirección: http://ip_del_servidor/symfony/web/app_dev.php/register y registrar usuarios

Listo listo.

setas
Enjoy!

Entry filed under: Debian, frameworks, linux, php, programación, symfony. Tags: , , .

Sincronizar dos computadores con Unison Actualizando a Symfony 2.2

30 comentarios Add your own

  • 1. Santii Barchetta  |  septiembre 25, 2012 en 06:11

    hongos al final!!! excelente tuto 🙂

    Responder
  • 2. Amed E. Dávila Fuertes  |  octubre 1, 2012 en 16:09

    Muy buen tuto

    Responder
  • 3. puentesdiaz  |  octubre 5, 2012 en 13:06

    hola buen tuto, te consulto
    estoy usasdo SF2.1.2 y FOSUserBundle dev-master (imagino :p )

    si a mi clase usuario le agrego alguna propiedad me salta que la tabla no lo guarda, lo que logico

    como creo mis propiedades de usuario , como DNI, PAIS, SEXO, etc

    y en que tabla se debe guardar, no veo en la doc que lo explique

    Responder
    • 4. constrict0r  |  octubre 5, 2012 en 16:06

      Este tutorial lo escribí para Symfony 2.0, si agregas un campo a tu clase Usuario debería reflejarse en la tabla de tu base de datos cuando actualices su estructura con el comando: php app/console doctrine:schema:update –force

      Por ejemplo puede ser que agregues el campo miCampo de la siguiente manera:

      /**
      * @ORM\Column(name=”miCampo”, type=”string”, length=250)
      */
      protected $miCampo;

      public function setCampo($miCampo) {
      $this->miCampo = $miCampo;
      }

      public function getMiCampo() {
      return $this->miCampo;
      }

      Sin embargo el simple hecho de agregarlo no lo mostrará en el formulario de registro de usuario, para ello debes sobreescribir el constructor del formulario que se llama RegistrationFormType.php y decirle al form builder que vas a incluir dicho campo, luego en la plantilla de registro debes habilitarlo.

      En el caso de datos como el país y el sexo, para esto usualmente se usa una clase para cada uno que tiene un identificador y un nombre (por ej: 1 => masculino, 2 => femenino), entonces en vez de agregar el campo directamente en la clase Usuario lo que haces es que agregas una relación, por ejemplo:

      /**
      * @ORM\ManyToOne(targetEntity=”Pais”)
      * @ORM\JoinColumn(name=”pais_id”, referencedColumnName=”id”, onDelete=”CASCADE”)
      */
      private $pais;

      Y por supuesto agregar el get y el set para el país.

      Responder
  • 5. kporras07  |  octubre 21, 2012 en 00:11

    Excelente tutorial!
    Sólo una duda, lo sigo con Oracle y después de loguear un usuario me da el siguiente error:
    ORA-01843: not a valid month
    500 Internal Server Error – OCI8Exception

    Alguna idea???

    Responder
    • 6. constrict0r  |  octubre 21, 2012 en 09:12

      Podría ser que por el formato de fecha que estás utilizando el día y el mes están invertidos y por eso un mes mayor a 12 no sería válido.

      Responder
      • 7. kporras07  |  octubre 21, 2012 en 20:21

        Si, de hecho según investigué el problema es ese 😦
        Mi gran problema es que no tengo ni idea como entrarle; ya que el error me lo da en el path login_check.
        Acepto sugerencias

  • 8. Alejandro Carvajal  |  noviembre 17, 2012 en 21:56

    Segui este tutorial pero me encuentro con un problema, cuando entro al register… se me va para el login jejeje, como puedo solucionar eso?.

    Responder
    • 9. Alejandro Carvajal  |  noviembre 17, 2012 en 22:00

      Ya lo solucione, no habia caido en cuenta que en el security, se le habia dado adminrole al register. muy bien tutorial

      Responder
      • 10. Rena  |  enero 25, 2013 en 12:51

        Yo tengo ese problema y soy muy nuevo en esto, como sería la solución? muchas gracias

  • 11. jolfp  |  noviembre 19, 2012 en 06:40

    Hola, el problema que tengo yo es que no sé como agregarle roles al FOSUser…Estoy empezando con symfony… Y nada, atascado estoy con eso.

    Responder
    • 12. constrict0r  |  noviembre 19, 2012 en 09:24

      Umm… básicamente instalas el bundle, creas una entidad Usuario que incluya una función llamada addRole y ahí es dónde agregas los roles al usuario. Luego configuras el security.yml para permitir el acceso según el rol.

      Responder
      • 13. jolfp  |  noviembre 20, 2012 en 04:51

        El Bundle lo tengo instalado y funcionando (gracias a seguir tu tutorial :D). He podio asignar roles por la consola. Pero el problema que me encuentro es que los roles se guardan en la base de datos con formato de Array. Y ahí es donde me bloqueo.

        He estado haciendo alguna prueba con la función AddRole, pero creo que iba totalmente desencaminado.

      • 14. constrict0r  |  noviembre 20, 2012 en 11:13

        Sí, los roles se guardan como un array dentro de la talba Usuario, por ejemplo el rol admin se almacena como la siguiente hilera: a:1:{i:0;s:10:”ROLE_ADMIN”;}.

        Es por eso que en la función addRole se lleva a cabo la instrucción: array_push($this->roles, ‘ROLE_ADMIN’); que lo que hace es agregar la hilera “ROLE_ADMIN” al array roles de la entidad Usuario y como el array roles está mapeado al campo roles de la tabla Usuario, esto permite que se almacene correctamente.

  • 15. jucles  |  noviembre 26, 2012 en 12:46

    Lo segui al pie de la letra y cuadno pongo
    php app/console doctrine:schema:update –force
    me muestra una excepcion de doctrine indicando que no se reconoce el tipo int.

    Responder
    • 16. constrict0r  |  noviembre 26, 2012 en 14:42

      ¿Qué motor de base de datos se está usando?

      Responder
      • 17. jucles  |  noviembre 27, 2012 en 03:04

        Estoy utilizando Doctrine contra mysql

      • 18. jucles  |  noviembre 27, 2012 en 03:09

        Concretamente este error:
        [Doctrine\DBAL\DBALException]

        Unknown column type “int” requested. Any Doctrine type that you use has to be registered with \Doctrine\DBAL\Types\Type::addType(). You can get a list of all….

  • 19. Eli Wade  |  enero 10, 2013 en 07:05

    Como el archivo symfony es ejecutable, los usuarios de Unix puede reemplazar todas las apariciones de ‘php symfony’ por ‘./symfony’ de ahora en adelante.

    Responder
  • 20. Fernando Del Fresno Garcia  |  febrero 11, 2013 en 05:30

    Hola, tengo una duda. Soy algo nuevo con symfony 2 pero no entiendo para que sirve el codigo siguiente si no lo asignamos a ningun controlador. Excelente tuto. Gracias

    login_check:
    pattern: /login_check

    logout:
    pattern: /logout

    Responder
    • 21. constrict0r  |  abril 13, 2013 en 11:27

      Eso está asignado en las rutas que define el FriendsOfSymfony User Bundle.

      Puedes revisar el archivo /ruta_symfony/vendor/friendsofsymfony/user-bundle/FOS/UserBundle/Resources/config/routing/security.xml

      Puedes ver todas las rutas con el comando: php app/console router:debug

      Responder
  • 22. Ingrid  |  febrero 27, 2013 en 10:06

    Buen dia, estoy usando Symfony 2.1.8, quiero instalar el FOSUserBundle, pero no puedo empezar, porque el archivo deps no lo tengo, aqui como empiezo? Desde ya, gracias!

    Responder
  • 24. dariongg  |  marzo 27, 2013 en 08:13

    Hola,

    Este método addRole funciona para agregar un solo rol.

    y el formulario tiene la opcion de seleccionar mas de uno.

    tengo estos roles definidos en addRole y form

    1 => ‘ROLE_ADMIN’,
    2 => ‘ROLE_USER’,
    3 => ‘ROLE_SUPERVISOR’,
    4 => ‘ROLE_AREA’,
    5 => ‘ROLE_CCHH’),

    si selecciono rol 3 y 5 guarda solo 5.
    si selecciono rol 3 y 4 guarda solo 4.

    como lo hago para que pueda agregar mas de un rol a un usuario.

    Responder
    • 25. constrict0r  |  abril 13, 2013 en 11:19

      public function addRole( $rol )
      {
      if($rol == 1) {
      array_push($this->roles, ‘ROLE_ADMIN’);
      }
      else if($rol == 2) {
      array_push($this->roles, ‘ROLE_USER’);
      }
      }

      Responder
  • 26. dariongg  |  marzo 27, 2013 en 08:23

    Tengo una duda el formulario se puede seleccionar mas de un rol, pero solo me guarda el ultimo de una lista. No le hecho ninguna modificación al código.

    tengo estos roles

    1 => ‘ROLE_ADMIN’,
    2 => ‘ROLE_USER’,
    3 => ‘ROLE_SUPERVISOR’,
    4 => ‘ROLE_AREA’,
    5 => ‘ROLE_CCHH’

    Si selecciono 3 y 4 me guarda el 4.

    Si selecciono 1, 2 y 5 me guarda el 5.

    como lo puedo hacer para registrar mas de un rol.

    Responder
    • 27. dariongg  |  marzo 27, 2013 en 08:40

      EL problema es que cada vez que llama a addRole()

      ejecuta esto $this->roles = array();

      y borra el contenido de $this->roles.

      Tienes una idea de como solucionar esto.

      Responder
      • 28. constrict0r  |  abril 13, 2013 en 11:19

        Sí, en Entity/Usuario.php hay que poner la función así:

        public function addRole( $rol )
        {
        if($rol == 1) {
        array_push($this->roles, ‘ROLE_ADMIN’);
        }
        else if($rol == 2) {
        array_push($this->roles, ‘ROLE_USER’);
        }
        }

    • 29. constrict0r  |  abril 13, 2013 en 11:19

      public function addRole( $rol )
      {
      if($rol == 1) {
      array_push($this->roles, ‘ROLE_ADMIN’);
      }
      else if($rol == 2) {
      array_push($this->roles, ‘ROLE_USER’);
      }
      }

      Responder
  • 30. Diego Martinez  |  diciembre 15, 2014 en 11:43

    en la parte de la plantilla personalizada del login:

    Para Symfony 2.6 te hace falta la parte de renderizar el controllador, porque sino toma es una ruta, sería:

    Responder

Dejáte un comentario

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Trackback this post  |  Subscribe to the comments via RSS Feed


Digo yo

Que dura está la vida

Dice Jhon Lennon

La vida es lo que pasa mientras haces planes

Digo yo

Dónde haya un computín esté dónde esté algo comenzará en cero

Acerca de Mi:


Hell-Out world!!

Dice Einstein

Dios no juega a los dados

Dice Niels Bohr

Einstein no le diga a Dios lo que tiene que hacer

Dice el Che:

Seamos realistas y hagamos lo imposible.

Dice Goethe:

Lo que no comprendemos no lo poseemos.

Digo yo:

Hay que tratar de travesear todo

Dice Oscar Wilde:

Cualquier hombre puede llegar a ser feliz con una mujer, con tal de que no la ame.

Digo yo:

De fijo dijo eso porque era homosexual.

Artículos

agosto 2012
L M X J V S D
« Jun   Abr »
 12345
6789101112
13141516171819
20212223242526
2728293031  

A %d blogueros les gusta esto: