security symfony roles symfony-2.1 symfony-security

security - ¿Cuándo se actualizan los roles de los usuarios y cómo forzarlos?



symfony symfony-2.1 (8)

En primer lugar, no estoy usando FOSUserBundle y no puedo porque estoy portando un sistema heredado que tiene su propia capa de Modelo (no hay Doctrine / Mongo / lo que sea aquí) y otro comportamiento muy personalizado.

Estoy tratando de conectar mi sistema de rol heredado con el de Symfony para poder usar la seguridad nativa de Symfony en los controladores y las vistas.

Mi primer intento fue cargar y devolver todas las funciones del usuario en el método getRoles() desde Symfony/Component/Security/Core/User/UserInterface . Al principio, parecía que funcionaba. Pero después de profundizar, me di cuenta de que estas funciones solo se actualizan cuando el usuario inicia sesión. Esto significa que si otorgo o revoco las funciones de un usuario, tendrá que cerrar sesión y volver a iniciarla para que los cambios surtan efecto. Sin embargo, si revoco los roles de seguridad de un usuario, quiero que se aplique inmediatamente, por lo que ese comportamiento no es aceptable para mí.

Lo que quiero que haga Symfony es recargar los roles de un usuario en cada solicitud para asegurarme de que estén actualizados. He implementado un proveedor de usuario personalizado y se está refreshUser(UserInterface $user) su refreshUser(UserInterface $user) en cada solicitud, pero de alguna manera no se están actualizando los roles.

El código para cargar / actualizar el usuario en mi UserProvider es algo así:

public function loadUserByUsername($username) { $user = UserModel::loadByUsername($username); // Loads a fresh user object including roles! if (!$user) { throw new UsernameNotFoundException("User not found"); } return $user; }

( refreshUser parece similar)

¿Hay alguna manera de hacer que Symfony actualice los roles de usuario en cada solicitud?



Así que después de un par de días tratando de encontrar una solución viable y contribuyendo a la lista de correo de usuarios de Symfony2, finalmente la encontré. Lo siguiente se ha derivado de la discusión en https://groups.google.com/d/topic/symfony2/NDBb4JN3mNc/discussion

Resulta que hay una interfaz Symfony/Component/Security/Core/User/EquatableInterface que no está destinada a comparar la identidad del objeto sino precisamente a

Probar si dos objetos son iguales en el contexto de seguridad y autenticación.

Implemente esa interfaz en su clase de usuario (la que ya implementa UserInterface ). Implemente el único método requerido isEqualTo(UserInterface $user) para que devuelva falso si los roles del usuario actual difieren de los del usuario pasado.

Nota: El objeto Usuario se serializa en la sesión. Debido a la forma en que funciona la serialización, asegúrese de almacenar los roles en un campo de su objeto de usuario, y no los recupere directamente en el Método getRoles() , de lo contrario, ¡todo eso no funcionará!

Aquí hay un ejemplo de cómo se verían los métodos específicos:

protected $roles = null; public function getRoles() { if ($this->roles == null) { $this->roles = ...; // Retrieve the fresh list of roles // from wherever they are stored here } return $this->roles; } public function isEqualTo(UserInterface $user) { if ($user instanceof YourUserClass) { // Check that the roles are the same, in any order $isEqual = count($this->getRoles()) == count($user->getRoles()); if ($isEqual) { foreach($this->getRoles() as $role) { $isEqual = $isEqual && in_array($role, $user->getRoles()); } } return $isEqual; } return false; }

Además, tenga en cuenta que cuando los roles realmente cambian y usted vuelve a cargar la página, la barra de herramientas del generador de perfiles podría indicarle que su usuario no está autenticado. Además, al analizar el generador de perfiles, es posible que los roles no se actualicen.

Descubrí que la función refrescante realmente funciona. Es solo que si no se alcanzan restricciones de autorización (no @Secure anotaciones @Secure , no se requieren roles en el firewall, etc.), la actualización no se realiza realmente y el usuario se mantiene en el estado "no autenticado".

Tan pronto como llega a una página que realiza cualquier tipo de verificación de autorización, los roles de usuario se actualizan y la barra de herramientas del generador de perfiles muestra al usuario con un punto verde y "Autentificado: sí" nuevamente.

Ese es un comportamiento aceptable para mí, espero que haya sido útil :)


Desde un controlador, después de agregar roles a un usuario y guardar en la base de datos, simplemente llame:

// Force refresh of user roles $token = $this->get(''security.context'')->getToken()->setAuthenticated(false);


Eche un vistazo here , establezca always_authenticate_before_granting en true en security.yml .


En su security.yml (o las alternativas):

security: always_authenticate_before_granting: true

El juego más fácil de mi vida.


La solución es colgar un suscriptor en un evento PostUpdate de Doctrine. Si la entidad actualizada es Usuario, el mismo usuario registrado, me autentico utilizando el servicio AuthenticationManager. Tienes que inyectar el contenedor de servicios (o servicios relacionados) al suscriptor, por supuesto. Prefiero inyectar todo el contenedor para evitar un problema de referencias circulares.

public function postUpdate(LifecycleEventArgs $ev) { $entity = $ev->getEntity(); if ($entity instanceof User) { $sc = $this->container->get(''security.context''); $user = $sc->getToken()->getUser(); if ($user === $entity) { $token = $this->container->get(''security.authentication.manager'')->authenticate($sc->getToken()); if ($token instanceof TokenInterface) { $sc->setToken($token); } } } }


Lo siento, no puedo responder en el comentario, así que vuelvo a jugar a la pregunta. Si alguien nuevo en seguridad de Symfony intenta obtener un trabajo de actualización de roles en la Autenticación de contraseña personalizada, entonces, dentro de la función authenticateToken:

if(count($token->getRoles()) > 0 ){ if ($token->getUser() == $user ){ $passwordValid=true; } }

Y no compruebe las contraseñas de DB / LDAP o en cualquier lugar. Si el usuario entra en el sistema, en $ token solo tiene un nombre de usuario y no tiene roles.


Logro este comportamiento implementando mi propio EntityUserProvider y anulando el método loadByUsername ($ username):

/** * Load an user from its username * @param string $username * @return UserInterface */ public function loadUserByUsername($username) { $user = $this->repository->findOneByEmailJoinedToCustomerAccount($username); if (null === $user) { throw new UsernameNotFoundException(sprintf(''User "%s" not found.'', $username)); } //Custom function to definassigned roles to an user $roles = $this->loadRolesForUser($user); //Set roles to the user entity $user->setRoles($roles); return $user; }

El truco es llamar a setRoles cada vez que llames loadByUsername ... Espero que ayude