Symfony2 + functional test + authentication - symfony

I want to prepare simple function test in PHPUnit for my symfony 2 project: authenticate user and display some page without redirecting to login form.
I use LDAP user provider (IMAG\LdapBundle\Provider\LdapUserProvider).
My firewalls configuration in security.yml:
firewalls:
secured_area:
pattern: ^/
provider: ldap
imag_ldap:
login_path: /login
logout:
path: /logout
target: /
In my test controller I have following methods:
protected function createClientWithAuthentication($firewallName, array $options = array(), array $server = array())
{
$client = $this->createClient($options, $server);
$client->getCookieJar()->set(new \Symfony\Component\BrowserKit\Cookie(session_name(), true));
$user = $this->getCurrentUser($client);
$token = new UsernamePasswordToken($user, null, $firewallName, $user->getRoles());
$client->getContainer()->get('session')->set('_security_' . $firewallName, serialize($token));
return $client;
}
protected function getCurrentUser($client) {
$userProvider = $client->getContainer()->get('imag_ldap.security.user.provider');
$user = $userProvider->loadUserByUsername('my.login');
return $user;
}
Then I have simple test:
public function testProfile()
{
$c = $this->createClientWithAuthentication('secured_area');
$c->request('GET', '/profile');
$this->assertEquals($c->getResponse()->getStatusCode(), 200);
}
Test fails:
Failed asserting that 200 matches expected 302.
I digged it and I found out that I receive complete user from user provider (all fields are properly filled). But response is redirecting to localhost/login page.
Please tell me what else I can test... where is the place where decision is made if login page should (or not) be displayed?

I have WebTestCase base class that creates a authorized client. You can adapt it to your purpose ...
class BaseWebTestCase extends WebTestCase{
protected $client;
protected function setUp(){
$this->client=$this->createAuthorizedClient();
}
/**
* #return \Symfony\Bundle\FrameworkBundle\Client
*/
protected function createAuthorizedClient()
{
$client = static::createClient();
$container = $client->getContainer();
/** #var Person $user */
$user=$container->get("netnotes.cust.person")->getRepository()->getSuperAdmin();
$session = $container->get('session');
/** #var $userManager \FOS\UserBundle\Doctrine\UserManager */
$userManager = $container->get('fos_user.user_manager');
/** #var $loginManager \FOS\UserBundle\Security\LoginManager */
$loginManager = $container->get('fos_user.security.login_manager');
$firewallName = $container->getParameter('fos_user.firewall_name');
$user = $userManager->findUserBy(array('username' => $user->getUsername()));
$loginManager->loginUser($firewallName, $user);
// save the login token into the session and put it in a cookie
$container->get('session')->set('_security_' . $firewallName,
serialize($container->get('security.context')->getToken()));
$container->get('session')->save();
$client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
return $client;
}
}

Related

The App\Security\LoginFormAuthenticator::getUser() method must return a UserInterface. You returned Softonic\GraphQL\Response

I'm trying to customize the login with a graphql query to load the User data and it shows me that error.
I use php bin / console make: auth and it works correctly when I query the $ user variable from MySQL but when I load the $ user variable from GRAPHQL it shows me that error.
This is the code:
public function getUser($credentials, UserProviderInterface $userProvider)
{
if ($credentials["csrf_token"] != "") {
$client = new GH();
$headers = ['Content-Type' => 'application/x-www-form-urlencoded'];
$result = $client->request('POST', 'https://graphql.clientecliente.com/get-token', [
'json' => [
'usuario' => $credentials["email"],
'clave' => $credentials["password"]
]
]);
$data = $result->getBody()->getContents();
$objetodata=(json_decode($data));
$token = $objetodata->token;
}
//Conexion servidor GraphQL
$servidor = $this->params->get('enlace_graphql');
//Codigo debe salir desde la base de datos
$codigoAuth = $token;
$options = [
'headers' => [
'Authorization' => 'Bearer ' . $codigoAuth,
],
];
$client = \Softonic\GraphQL\ClientBuilder::build($servidor, $options);
$gpl1 = <<<'QUERY'
query ($limit: Int!, $offset: Int!, $CODIGO_USUARIO: String) {
obt_usuarios(limit: $limit, offset: $offset, CODIGO_USUARIO: $CODIGO_USUARIO) {
totalCount
OBT_USUARIOS {
CODIGO_USUARIO
CLAVE_USUARIO
CODIGO_EMPRESA
CODIGO_CLIENTE
CODIGO_PASAJERO
ES_ADMINISTRADOR
}
}
}
QUERY;
$variables1 = [
'limit' => 10,
'offset' => 0,
'CODIGO_USUARIO' => $credentials["email"]
];
//$user = $this->entityManager->getRepository(Usuario::class)->findOneBy(['email' => $credentials['email']]);
$user = $client->query($gpl1,$variables1);
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('El usuario no existe');
}
return $user;
}
Maybe, Anything idea?
Update.
Now create the custom user provider. I use this command: php bin/console make:user;
<?php
//src/Security/UserProvider.php
namespace App\Security;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
//Consulta API
use GuzzleHttp\Client as GH;
use GuzzleHttp\Pool;
//Consulta GraphQL
use Softonic\GraphQL;
use Softonic\GraphQL\Client;
use Softonic\GraphQL\ClientBuilder;
use function GuzzleHttp\json_decode;
class UserProvider implements UserProviderInterface
{
/**
* Symfony calls this method if you use features like switch_user
* or remember_me.
*
* If you're not using these features, you do not need to implement
* this method.
*
* #return UserInterface
*
* #throws UsernameNotFoundException if the user is not found
*/
public function loadUserByUsername($username)
{
$client = new GH();
$headers = ['Content-Type' => 'application/x-www-form-urlencoded'];
$result = $client->request('POST', 'https://graphql.cliente.com.ec/get-token', [
'json' => [
'usuario' => 'test',
'clave' => 'test1234',
]
]);
$data = $result->getBody()->getContents();
$objetodata=(json_decode($data));
//var_dump($objetodata->token);
//exit;
$token = $objetodata->token;
$servidor = "https://graphql.cliente.com/graphql";
//Codigo debe salir desde la base de datos
//$codigoAuth = $token;
$codigoAuth= "12345678912345678mlvIjoiT0JUX0NjIxOTI1ODN9.8dNiZI6iZsYS0plVU0fuqFlhkTDSOt9OFy5B-WZiRmk";
//var_dump($codigoAuth);
//exit;
//echo $codigoAuth; exit;
$options = [
'headers' => [
'Authorization' => 'Bearer ' . $codigoAuth,
],
];
$client = \Softonic\GraphQL\ClientBuilder::build($servidor, $options);
$gpl1 = <<<'QUERY'
query ($limit: Int!, $offset: Int!, $CODIGO_USUARIO: String) {
obt_usuarios(limit: $limit, offset: $offset, CODIGO_USUARIO: $CODIGO_USUARIO) {
totalCount
OBT_USUARIOS {
CODIGO_USUARIO
CLAVE_USUARIO
CODIGO_EMPRESA
CODIGO_CLIENTE
CODIGO_PASAJERO
ES_ADMINISTRADOR
}
}
}
QUERY;
$variables1 = [
'limit' => 10,
'offset' => 0,
'CODIGO_USUARIO' => 'test'
];
$user=$client->query($gpl1,$variables1);
//var_dump($user);exit;
//$username=$user;
return $user;
//throw new \Exception('TODO: fill in loadUserByUsername() inside '.__FILE__);
}
/**
* Refreshes the user after being reloaded from the session.
*
* When a user is logged in, at the beginning of each request, the
* User object is loaded from the session and then this method is
* called. Your job is to make sure the user's data is still fresh by,
* for example, re-querying for fresh User data.
*
* If your firewall is "stateless: true" (for a pure API), this
* method is not called.
*
* #return UserInterface
*/
public function refreshUser(UserInterface $user)
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Invalid user class "%s".', get_class($user)));
}
// Return a User object after making sure its data is "fresh".
// Or throw a UsernameNotFoundException if the user no longer exists.
throw new \Exception('TODO: fill in refreshUser() inside '.__FILE__);
return $this->loadUserByUsername($user->getUsername());
}
/**
* Tells Symfony to use this provider for this User class.
*/
public function supportsClass($class)
{
return User::class === $class;
}
}
config/packages/security.yaml:
providers:
app_user_provider:
id: App\Security\UserProvider
firewalls:
secured_area:
anonymous: true
form_login:
login_path: loginanterior
check_path: login_check
default_target_path: index
provider: app_user_provider
remember_me: true
logout:
path: logout
target: index
remember_me:
secret: '%kernel.secret%'
lifetime: 604800 # 1 week in seconds
path: /
src/Security/User.php
<?php
namespace App\Security;
use Symfony\Component\Security\Core\User\UserInterface;
class User implements UserInterface
{
private $email;
private $roles = [];
/**
* #var string The hashed password
*/
private $password;
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUsername(): string
{
return (string) $this->email;
}
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #see UserInterface
*/
public function getPassword(): string
{
return (string) $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* #see UserInterface
*/
public function getSalt()
{
// not needed when using the "bcrypt" algorithm in security.yaml
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
}
However, after this it does not show me any errors or anything. Just go back to the login again
You have to map your Softonic\GraphQL\Response to your User model. This should be done in your custom UserProvider so Authenticator doesn't know where the user actually comes from. You can also look at existing user providers code for inspiration.
Thanks to the help of Ion Bazan
I understood that I had to make a custom UserProvider.
Final steps encoder correction
config/packages/security.yaml
encoders:
App\Security\User:bcrypt
GraphQL result mapping on USER object by sending user, ROL and password (must be sent in the encryption correctly)
App/src/Security/UserProvider.php
$userData=$client->query($gpl1,$variables1)->getData();
//var_dump($userData);
$user = new User();
$user->setEmail($userData['obt_usuarios']["OBT_USUARIOS"][0]["CODIGO_USUARIO"]);
$user->setRoles(array("ROLE_USER"));
$user->setPassword('$2a$10$JSMRqSFX9Xm1gAbc/YzVcu5gETsPF8HJ3k5Zra/RZlx4IXfadwmW.');
return $user;
}
/**
* Refreshes the user after being reloaded from the session.
*
* When a user is logged in, at the beginning of each request, the
* User object is loaded from the session and then this method is
* called. Your job is to make sure the user's data is still fresh by,
* for example, re-querying for fresh User data.
*
* If your firewall is "stateless: true" (for a pure API), this
* method is not called.
*
* #return UserInterface
*/
public function refreshUser(UserInterface $user)
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Invalid user class "%s".', get_class($user)));
}
// Return a User object after making sure its data is "fresh".
// Or throw a UsernameNotFoundException if the user no longer exists.
//throw new \Exception('TODO: fill in refreshUser() inside '.__FILE__);
return $this->loadUserByUsername($user);
}
With this finally it worked

Symfony - Guard authenticator lost user after success login

I'm implementing a login system with guard authentication in my symfony application. I've already started to implement the system but I must be doing something incorrectly.
I will start by showing what I have implemented and in the end explain what is happening ...
security.yml
security:
encoders:
UserBundle\Entity\User: bcrypt
providers:
custom_own_provider:
entity:
class: UserBundle:User
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
custom:
pattern: ^/ad/
anonymous: ~
provider: custom_own_provider
remember_me:
name: 'nothing'
secure: true
httponly: true
secret: '%secret%'
lifetime: 604800 # 1 week in seconds
path: /
domain: ~
guard:
authenticators:
- app.authenticator.form
services.yml
services:
app.authenticator.form:
class: UserBundle\Security\LoginFormAuthenticator
autowire: true
arguments: ["#service_container"]
LoginController
/**
* #Route("/login", name="login")
*/
public function loginAction(Request $request) {
$authenticationUtils = $this->get('security.authentication_utils');
$error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render(
'AppBundle:login:index.html.twig',
[
'error' => $error ? $error->getMessage() : NULL,
'last_username' => $lastUsername
]
);
}
Public controller with homepage: After login success, the user is redirected here. It happens that here, when validated if the user is authenticated I am unsuccessful. the 'getuser' from the security token is returning 'anon'.
/**
* #Route("/", name="homepage")
*/
public function publicHomepageAction (){
// DEBUG: This method gets a user from the Security Token Storage. The user here comes as 'anon'.
$user = $this->getUser();
// If user is already logged show internal homepage
$securityContext = $this->container->get('security.authorization_checker');
if($securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED')
|| $securityContext->isGranted('IS_AUTHENTICATED_FULLY')
){
// Code if user is authenticated
...
}
return $this->render('splash_page/homepage.html.twig');
}
And finally, my FormAuthenticator:
class LoginFormAuthenticator extends AbstractGuardAuthenticator
{
private $container;
/**
* Default message for authentication failure.
*
* #var string
*/
private $failMessage = 'Invalid credentials';
/**
* Creates a new instance of FormAuthenticator
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/**
* {#inheritdoc}
*/
public function getCredentials(Request $request)
{
if ($request->getPathInfo() != '/login' || !$request->isMethod('POST')) {
return;
}
return array(
'email' => $request->request->get('email'),
'password' => $request->request->get('password'),
);
}
/**
* {#inheritdoc}
*/
public function getUser($credentials, UserProviderInterface $userProvider)
{
$email = $credentials['email'];
return $userProvider->loadUserByUsername($email);
}
/**
* {#inheritdoc}
*/
public function checkCredentials($credentials, UserInterface $user)
{
$plainPassword = $credentials['password'];
$encoder = $this->container->get('security.password_encoder');
if (!$encoder->isPasswordValid($user, $plainPassword)) {
throw new CustomUserMessageAuthenticationException($this->failMessage);
}
return true;
}
/**
* {#inheritdoc}
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
$url = $this->container->get('router')->generate('homepage');
return new RedirectResponse($url);
}
/**
* {#inheritdoc}
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception);
$url = $this->container->get('router')->generate('login');
return new RedirectResponse($url);
}
/**
* {#inheritdoc}
*/
public function start(Request $request, AuthenticationException $authException = null)
{
$url = $this->container->get('router')->generate('login');
return new RedirectResponse($url);
}
/**
* {#inheritdoc}
*/
public function supportsRememberMe()
{
return true;
}
/**
* Does the authenticator support the given Request?
*
* If this returns false, the authenticator will be skipped.
*
* #param Request $request
*
* #return bool
*/
public function supports (Request $request){
return $request->request->has('_username') && $request->request->has('_password');
}
}
My User Entity implements UserInterface.
Although on the homepage it indicates to me that I am authenticated as 'anon', if I try to login again, through the debug, I noticed that it detects that I am authenticated and redirects to the homepage controller but in this controller it says that I am like anon and back to redirect to the splash page.
It gives the feeling that somewhere between validation and redirection to the controller loses token storage.
Any idea?

authorizationChecker->isGranted in a phpUnit test

I'm currently creating my unit test for my application but it's the first time I do it.
I want to test this function :
/**
* This method search if the user is already in a team for the same tournament than the one passed in argument
* #param User $user
* #param Team $team
* #return bool|Team|mixed
*/
public function isAlreadyApplicant($user, Team $team) {
if (!$user || !$this->authorizationChecker->isGranted("ROLE_USER")) {
return false;
}
foreach ($user->getApplications() as $userTeam) {
/** #var Team $userTeam */
if ($userTeam->getTournament()->getId() === $team->getTournament()->getId()) {
return $userTeam;
}
}
foreach ($user->getTeams() as $userTeam) {
/** #var Team $userTeam */
if ($userTeam->getTournament()->getId() === $team->getTournament()->getId()) {
return $userTeam;
}
}
foreach ($user->getManagedTeam() as $userTeam) {
/** #var Team $userTeam */
if ($userTeam->getTournament()->getId() === $team->getTournament()->getId()) {
return $userTeam;
}
}
return false;
}
As you can see, the first test is to check if the user have the ROLE_USER.
When I try to "log my user", I have this message :
Fatal error: Call to a member function getToken() on null in \vendor\symfony\symfony\src\Symfony\Component\Security\Core\Authorization\AuthorizationChecker.php on line 56
I tried what I found in the Symfony doc but I must miss something. This is my test Class:
class ApplicationCheckerTest extends WebTestCase
{
protected $client = null;
public function setUp() {
$this->client = static::createClient();
/** #var User $user */
$user = $this->client->getContainer()->get('doctrine')->getManager()->getRepository('MGDUserBundle:User')->findOneBy(array("email" => 'Player11.Player11#test.com'));
$this->loginUser("main", $user);
}
protected function loginUser($firewallName, UserInterface $user, array $options = array(), array $server = array())
{
$this->client = static::createClient();
$token = new UsernamePasswordToken($user, null, $firewallName, $user->getRoles());
static::$kernel->getContainer()->get('security.token_storage')->setToken($token);
$session = $this->client->getContainer()->get('session');
$session->set('_security_'.$firewallName, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
public function testIsAlreadyApplicantIsNotConnected()
{
$user = new User();
$team = new Team();
$router = $this->createMock(Router::class);
$authorizationChecker = $this->createMock(AuthorizationChecker::class);
$applicationChecker = new ApplicationChecker($router, $authorizationChecker);
$applicationChecker->isAlreadyApplicant($user, $team);
}
}
And my security.yml looks like :
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
anonymous: true
provider: main
form_login:
login_path: fos_user_security_login
check_path: fos_user_security_check
logout:
path: fos_user_security_logout
target: /
remember_me:
secret: '%secret%'
I try to connect a user I created with fixtures.
I'm not sure about the way I try to do it, maybe I'm on the wrong path, don't hesitate to correct me if I'm wrong!
For information, I'm in Symfony 3.2.13
Have a good day
The WebTestCase and the example from the Symfony documentation that you're using should be used for a functional test of your controller.
The way you have set up your authentication for the client is correct. But you are not using that client to make a request to the bootstrapped Symfony kernel in your test case. You are just making a simple unit test on a manually created instance of your service, which is fine.
You can simply use the PHPUnit\Framework\TestCase for that.
It is possible to use the WebTestCase to test your service and overwrite the TokenStorage which is used in the AuthorizationChecker in the kernel container instead of the client container, and then also fetch your service from the kernel container instead of instantiating it yourself. But I don't see much benefit in it. There is no need to test the Symfony component (if isGranted is working). That is in the scope of the Symfony project and most likely already covered there.
Your error
The reason for your error is, that a final method can't be mocked. As a workaround you can set the dependency in your ApplicationChecker constructor to AuthorizationCheckerInterface and then create a mock from the interface in your test.
$authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
$authorizationChecker->method('isGranted')->willReturn(true);

symfony3 guard login form doesn't authenticate [duplicate]

This question already has an answer here:
Symfony & Guard: "The security token was removed due to an AccountStatusException"
(1 answer)
Closed 5 years ago.
I try to make a form login authentication with guard (symfony 3.2) but it doesn't work.
The authentication is working, but when I'm redirected to the home page (accueil), I'm redirected to the login page without anthentication.
If I put in the controler of my home page
$user = $this->get('security.token_storage')->getToken();
dump($user); die;
I can see my user, the role but he is not authenticated.
DashboardController.php on line 23:
PostAuthenticationGuardToken {#133 ▼
-providerKey: "main"
-user: User {#457 ▶}
-roles: array:1 [▼
0 => Role {#120 ▼
-role: "ROLE_SUPERADMIN"
}
]
-authenticated: false
-attributes: []
}
What I've missed ?
Security.ym
security:
encoders:
EntBundle\Entity\User\User:
algorithm: bcrypt
providers:
database:
entity:
class: EntBundle:User\User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
anonymous: ~
logout: ~
guard:
authenticators:
- ent.login_authenticator
TestAuthenticator.php
namespace EntBundle\Security;
use Doctrine\ORM\EntityManager;
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\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
class TestAuthenticator extends AbstractGuardAuthenticator
{
private $em;
private $router;
public function __construct(EntityManager $em, RouterInterface $router)
{
$this->em = $em;
$this->router = $router;
}
public function getCredentials(Request $request)
{
if ($request->getPathInfo() != '/login' || !$request->isMethod('POST')) {
return;
}
return [
'username' => $request->request->get('_username'),
'password' => $request->request->get('_password'),
];
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$username = $credentials['username'];
return $this->em->getRepository('EntBundle:User\User')->findOneBy(['username' => $username]);
}
public function checkCredentials($credentials, UserInterface $user)
{
// this is just for test
return true;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception);
$url = $this->router->generate('login');
return new RedirectResponse($url);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
$url = $this->router->generate('accueil');
return new RedirectResponse($url);
}
public function start(Request $request, AuthenticationException $authException = null)
{
$url = $this->router->generate('login');
return new RedirectResponse($url);
}
public function supportsRememberMe()
{
return false;
}
}
DashboardController.php
namespace EntBundle\Controller\Dashboard;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class DashboardController extends Controller
{
/**
* #Route("/accueil", name="accueil")
*/
public function indexAction()
{
$user = $this->get('security.token_storage')->getToken();
dump($user); die;
return $this->render('EntBundle:dashboard:dashboard_structure.html.twig');
}
/**
* #Route("/login", name="login")
*/
public function loginAction()
{
$authenticationUtils = $this->get('security.authentication_utils');
$error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('EntBundle::login.html.twig', [
'last_username' => $lastUsername,
'error' => $error,
]);
}
/**
* #Route("/logout", name="logout")
*/
public function logoutAction()
{
}
}
EDIT:
Thanks leo_ap for your help but the problem doesnt come from there.
The config session is like this :
session:
handler_id: session.handler.native_file
save_path: "%kernel.root_dir%/../var/sessions/%kernel.environment%"
and if I check in the save path folder I have session file created but not authenticated.
_sf2_attributes|a:1:{s:26:"_security.main.target_path";s:29:"http://localhost:8000/accueil";}_sf2_flashes|a:0:{}_sf2_meta|a:3:{s:1:"u";i:1488245179;s:1:"c";i:1488244922;s:1:"l";s:1:"0";}
If I try the normal login_form with security.yml it's working fine...
I've try with handler_id and save_path at null with no success.
EDIT2:
I've found why I'm always redirected to the login page, because I'm logged out!
[2017-02-28 09:16:34] security.INFO: The security token was removed due to an AccountStatusException. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationExpiredException(code: 0): at /home/philippe/Documents/symfony/vendor/symfony/symfony/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php:86)"}
and in GuardAuthenticationProvider.php (86)
The listener *only* passes PreAuthenticationGuardToken instances.
This means that an authenticated token (e.g.PostAuthenticationGuardToken)
is being passed here, which happens if that token becomes "not authenticated" (e.g. happens if the user changes between requests).
In this case, the user should be logged out, so we will return an AnonymousToken to accomplish that.
But Why ???
May be your Session that isn't persisting the token. Check your Session configuration, inside: config.yml. in the framework option, there is session. See how the handler_id and save_path are configured. It may be that your php instalation is unable to handle the sessions on the configured path. Try to put null to handler_id and save_path to force php use its own build in configurations to handle sessions.
config.yml file:
framework:
{ .. Other configurations ..}
session:
handler_id: null
save_path: null
{ .. More configurations ..}

Sonata admin and Custom Security handler

I wanna write a custom Security handler and this will be a simple ACL which restrict data by user id. I don't want use a standart ACL, no need to use all functional and create aditional database with permissions.
So I create my new handler and now I recieve $object as Admin class. With Admin class I can restrict access to services but can't restrict any rows in service.
The question is how I can recieve Entities and check permission on Entities like this:
public function isGranted(AdminInterface $admin, $attributes, $object = null)
{
if ($object->getUserId()==5){
return true
}
}
Overwrite the security handler in sonata config:
sonata_admin:
title: "Admin"
security:
handler: custom.sonata.security.handler.role
Create your service:
custom.sonata.security.handler.role:
class: MyApp\MyBundle\Security\Handler\CustomRoleSecurityHandler
arguments:
- #security.context
- [ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_USER]
- %security.role_hierarchy.roles%
Last step, but not less important is to create your class, retrieve your user and based by his credentials allow/deny access:
/**
* Class CustomRoleSecurityHandler
*/
class CustomRoleSecurityHandler extends RoleSecurityHandler
{
protected $securityContext;
protected $superAdminRoles;
protected $roles;
/**
* #param \Symfony\Component\Security\Core\SecurityContextInterface $securityContext
* #param array $superAdminRoles
* #param $roles
*/
public function __construct(SecurityContextInterface $securityContext, array $superAdminRoles, $roles)
{
$this->securityContext = $securityContext;
$this->superAdminRoles = $superAdminRoles;
$this->roles = $roles;
}
/**
* {#inheritDoc}
*/
public function isGranted(AdminInterface $admin, $attributes, $object = null)
{
/** #var $user User */
$user = $this->securityContext->getToken()->getUser();
if ($user->hasRole('ROLE_ADMIN')){
return true;
}
// do your stuff
}
}

Resources