symfony2 access denied exception - php

i'm working on symfony2 project and i get this exception. anybody have an idea on what is causing it ?
Uncaught exception 'Symfony\Component\Security\Core\Exception\AccessDeniedException' with message 'Access Denied' in /data/apache/www/emploipublic-sf/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/AccessListener.php:70\n
class AccessListener implements ListenerInterface
{
private $context;
private $accessDecisionManager;
private $map;
private $authManager;
private $logger;
public function __construct(SecurityContextInterface $context, AccessDecisionManagerInterface $accessDecisionManager, AccessMapInterface $map, AuthenticationManagerInterface $authManager, LoggerInterface $logger = null)
{
$this->context = $context;
$this->accessDecisionManager = $accessDecisionManager;
$this->map = $map;
$this->authManager = $authManager;
$this->logger = $logger;
}
/**
* Handles access authorization.
*
* #param GetResponseEvent $event A GetResponseEvent instance
*/
public function handle(GetResponseEvent $event)
{
if (null === $token = $this->context->getToken()) {
throw new AuthenticationCredentialsNotFoundException('A Token was not found in the SecurityContext.');
}
$request = $event->getRequest();
list($attributes, $channel) = $this->map->getPatterns($request);
if (null === $attributes) {
return;
}
if (!$token->isAuthenticated()) {
$token = $this->authManager->authenticate($token);
$this->context->setToken($token);
}
if (!$this->accessDecisionManager->decide($token, $attributes, $request)) {
throw new AccessDeniedException(); // this is line 70
}
}
}

Look at your security.yml file (app/config/security.yml).
You may have some secure path which you do not have access to. Check out
security -> access_control
section.

Related

Anon. in Custom User Provider

I want to manage my users without Database, and check their JWT
I use Guard and a Custom Service to Validate my JWT.
I Want to use even a custom user provider, And If user doesn't have JWT I want to redirect to a new page or call an exception
this is my code
class WebserviceUserProvider implements UserProviderInterface {
/**
* #var \Symfony\Component\Routing\RouterInterface
*/
private $router;
private $requestStack;
/**
* WebserviceUserProvider constructor.
* #param RequestStack $router
*/
public function __construct($router,RequestStack $requestStack) {
$this->router = $router;
$this->requestStack = $requestStack;
}
public function refreshUser(UserInterface $user) {
if (!$user instanceof User) {
throw new UnsupportedUserException(
sprintf('Instances of "%s" are not supported.', get_class($user))
);
}
return $this->loadUserByUsername($user->getUsername());
}
public function loadUserByUsername($username) {
if (!$token = $this->requestStack->getCurrentRequest()->headers->get('Authorization') ?: $this->requestStack->getCurrentRequest()->query->get('token')) {
throw new UsernameNotFoundException('Could not find user. Sorry!');
}
$user = new \AppBundle\Entity\User('MyUsername');
$user->setJwtToken($token);
return $user;
}
public function supportsClass($class) {
return $class === 'AppBundle\Security\User\WebserviceUser';
}
}
And this is my service.yml
app.webservice_user_provider:
class: AppBundle\Security\WebserviceUserProvider
arguments: ['#router','#request_stack']
And My security.yml
user-providers
providers:
webservice:
id: app.webservice_user_provider
When I visit a url with token in queryString
http://127.0.0.1:8000/app_dev.php/?token=my.token.jwt
My User is logged.
When visit
http://127.0.0.1:8000/app_dev.php/
I have an user anon. ( My token is set to Anon. ) and I don't have an exception
If I change loadUserByUsername with
public function loadUserByUsername($username) {
if (!$token = $this->requestStack->getCurrentRequest()->headers->get('Authorization') ?: $this->requestStack->getCurrentRequest()->query->get('token')) {
$url = $this->router->generate('errorPage');
return new RedirectResponse($url);
}
$user = new \AppBundle\Entity\User('MyUsername');
$user->setJwtToken($token);
return $user;
}
I have this error
Attempted to call an undefined method named "getUsername" of class
"Symfony\Component\HttpFoundation\RedirectResponse". 500 Internal
Server Error - UndefinedMethodException
How can I redirect to a page if I dont'have a JWT ( or is not valid ), or have an exception ?

Authenticate users via access token in Symfony 3

The problem
I want users to be authenticated via an access token that is supplied as a GET parameter to the first request.
I have never implemented such a thing in Symfony, so I followed the steps outlined in How to Create a custom Authentication Provider, but it 'doesn't work'. The authenticate method of the AuthenticationProviderInterface is not triggered.
What I have tried
Because it is a lot of configuration mostly, I don't even know how to debug this. This is what I have concluded so far: Only the AccessTokenProvider gets constructed, nothing else.
The code
These are the relevant parts of the system:
security.yml
security:
# Snip default (empty) in_memory provider
firewalls:
# Snip dev and main (symfony default)
accesstoken_secured:
pattern: ^/admin/
accesstoken: true
services.yml
services:
accesstoken.security.authentication.provider:
class: AppBundle\Security\Authentication\Provider\AccessTokenProvider
arguments:
- '' # User Provider
- '%kernel.cache_dir%/security/nonces'
public: false
accesstoken.security.authentication.listener:
class: AppBundle\Security\Firewall\AccessTokenListener
arguments: ['#security.token_storage', '#security.authentication.manager']
public: false
AccessTokenFactory
class AccessTokenFactory implements SecurityFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$providerId = 'security.authentication.provider.accesstoken.'.$id;
$container
->setDefinition($providerId, new DefinitionDecorator('accesstoken.security.authentication.provider'))
->replaceArgument(0, new Reference($userProvider))
;
$listenerId = 'security.authentication.listener.accesstoken.'.$id;
$container->setDefinition($listenerId, new DefinitionDecorator('accesstoken.security.authentication.listener'));
return array($providerId, $listenerId, $defaultEntryPoint);
}
public function getPosition()
{
return 'pre_auth';
}
public function getKey()
{
return 'accesstoken';
}
public function addConfiguration(NodeDefinition $node)
{
}
}
AccessTokenProvider
class AccessTokenProvider implements AuthenticationProviderInterface
{
private $userProvider;
public function __construct(UserProviderInterface $userProvider)
{
$this->userProvider = $userProvider;
}
public function authenticate(TokenInterface $token)
{
$user = $this->userProvider->loadUserByAccessToken($token->getAttribute('token'));
if ($this->isTokenValid($token)) {
$authenticatedToken = new AccessToken(['role_user']);
$authenticatedToken->setUser($user);
return $authenticatedToken;
}
throw new AuthenticationException('The WSSE authentication failed.');
}
protected function isTokenValid(AccessToken $token)
{
//TODO: Implement
return (bool)$token->token;
}
public function supports(TokenInterface $token)
{
return $token instanceof AccessToken;
}
}
AccessTokenListener
class AccessTokenListener
{
protected $tokenStorage;
protected $authenticationManager;
/**
* AccessTokenListener constructor.
* #param TokenStorageInterface $tokenStorage
* #param AuthenticationManagerInterface $authenticationManager
*/
public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager)
{
$this->tokenStorage = $tokenStorage;
$this->authenticationManager = $authenticationManager;
}
public function handle(GetResponseEvent $event)
{
$request = $event->getRequest();
$accesstoken = $request->get('accesstoken');
$token = new AccessToken();
$token->token = $accesstoken;
try {
$authToken = $this->authenticationManager->authenticate($token);
$this->tokenStorage->setToken($authToken);
return;
} catch (AuthenticationException $failed) {
// ... you might log something here
}
// By default deny authorization
$response = new Response();
$response->setStatusCode(Response::HTTP_FORBIDDEN);
$event->setResponse($response);
}
}
AccessToken
class AccessToken extends AbstractToken
{
public $token;
/**
* AccessToken constructor.
* #param array $roles
*/
public function __construct(array $roles = array())
{
parent::__construct($roles);
// If the user has roles, consider it authenticated
$this->setAuthenticated(count($roles) > 0);
}
/**
* Returns the user credentials.
*
* #return mixed The user credentials
*/
public function getCredentials()
{
return '';
}
}
I eventually tried to implement it in another way, using the tutorial at How to Create a Custom Authentication System with Guard.
This uses Symfony's new Guard system. It was actually very easy to setup!

How to get the current logged user in unit test with Symfony 2?

I have this unit test :
class ProjectControllerTest extends WebTestCase
{
private $client = null;
private $projectName = null;
/**
* #var \Doctrine\ORM\EntityManager
*/
private $em;
public function setUp()
{
$kernel = static::createKernel();
$kernel->boot();
$this->client = $this->createAuthorizeClient($kernel);
$this->em = $kernel->getContainer()->get('doctrine.orm.entity_manager');
}
public function testProjectNameEdition()
{
$project = new Project();
$project
->setName(uniqid())
->setComment('test')
;
$this->em->persist($project);
$this->em->flush();
$crawler = $this->client->request('GET', '/project/' . $project->getId() . '/edit');
$form = $crawler->selectButton('codex_gui_project_submit')->form();
$form['codex_gui_project[name]'] = $this->projectName . '1';
$this->client->submit($form);
$editProject = $this->em->getRepository('DatawordsCodexGuiBundle:Project')->findOneByName($oldProjectName . '1');
$this->assertEquals($this->projectName . '1', $editProject->getName());
}
public function createAuthorizeClient($kernel)
{
$client = static::createClient();
$container = $kernel->getContainer();
$session = $container->get('session');
$user = $kernel
->getContainer()->get('doctrine')
->getRepository('DatawordsCodexCoreBundle:User')
->findOneByUsername('Nico')
;
$token = new UsernamePasswordToken($user, $user->getUserName(), 'main', $user->getRoles());
$session->set('_security_main', serialize($token));
$session->save();
$client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
return $client;
}
}
Then when the new entity is persisted, the current user is getting in the ProjectListenner :
class ProjectListener
{
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/**
* Prepersist a creation of project
*
* #param \Doctrine\ORM\Event\LifecycleEventArgs $args
*/
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if ($entity instanceof Project) {
// Save the user and the created date
$usr = $this->container->get('security.context')->getToken()->getUser();
$entity->setCreated(new \DateTime());
$entity->setCreator($usr);
}
}
So this error happened when I run the test :
...PHP Fatal error: Call to a member function getUser() on a non-object in
/var/www/codex_gui/vendor/acme/foo/Acme/foo/fooBundle/Listener/ProjectListener.php
on line 32
You have some errors in how you are creating your user and logging them in. I do not know why you are doing so many static:: calls, but you should only be doing that for creating the client. For example:
private $container;
public function setUp()
{
$this->client = static::createClient();
$this->container = $this->client->getContainer();
$this->em = $this->container->get('doctrine.orm.entity_manager');
$this->createAuthorizeClient();
}
You do not need to pass $this->client to your createAuthorizeClient() function. It should look more like
public function createAuthorizeClient()
{
$session = $this->container->get('session');
$user = $this->em->getRepository('AcmeFooBundle:User')
->findOneByUsername('Nico');
// rest of the class here
}
There might be other issues, but this is just a base set of things you can do. Take a look at http://symfony.com/doc/current/cookbook/testing/simulating_authentication.html as well for how they create their user for their functional test.

Symfony2 custom Authenticator do something when not authenticated

How to manage Full authentication is required to access this resource.?
I want to redirect user when he is not authenticated.
I have custom uthenticater which authenticate user depending on session data, and i want to redirect user when hes not authenticatet.
My authenticator class:
/**
* #Service("sso_authenticator")
*/
class SsoAuthenticator implements SimplePreAuthenticatorInterface
{
/**
* #var SsoUserProvider
*/
protected $userProvider;
/**
* #InjectParams({
* "userProvider" = #Inject("sso_user_provider")
* })
*/
public function __construct(SsoUserProvider $userProvider)
{
$this->userProvider = $userProvider;
}
public function createToken(Request $request, $providerKey)
{
$user = $request->getSession()->get('sso_user');
if (!$user) {
throw new BadCredentialsException('No user found');
}
return new PreAuthenticatedToken(
'anon.', $user, $providerKey
);
}
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
{
$user = $token->getCredentials();
if (!is_array($user)) {
$user = $token->getUser();
}
if (!$user) {
throw new AuthenticationException('User does not exist.');
}
$ssoUser = $this->userProvider->loadUser($user);
return new PreAuthenticatedToken(
$ssoUser, $user, $providerKey, $ssoUser->getRoles()
);
}
public function supportsToken(TokenInterface $token, $providerKey)
{
return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
}
}
i set the login path to logout path like this:
secured_area:
form_login:
login_path : main_user_logout
then i wrote custom logout handler:
/**
* #Service("sso_authentication_handler")
*/
class SsoAuthenticationHandler implements LogoutSuccessHandlerInterface
{
/**
* #var Router
*/
private $router;
/**
* #var array
*/
protected $ssoUrls;
/**
* #InjectParams({
* "ssoUrls" = #Inject("%wordpress_sso%"),
* "router" = #Inject("router")
* })
*/
public function __construct(array $ssoUrls, Router $router)
{
$this->ssoUrls = $ssoUrls;
$this->router = $router;
}
public function onLogoutSuccess(Request $request)
{
$locale = $request->getLocale();
if ($locale === 'pl') {
$url = $this->ssoUrls[$locale];
} else {
$url = $this->ssoUrls['en'];
}
$url .= '?returnUrl=' . $this->router->generate('main');
return new RedirectResponse($url);
}
}
so with this combination i achive behavior like when youser is not authenticated or when he logout i will redirect him to other site to login, in my example to wordpress.

getting authenticated user onAuthenticationSuccess

I have the following authentication handler:
class LoginAuthSuccessHandler implements AuthenticationSuccessHandlerInterface, AuthenticationFailureHandlerInterface
{
private $router;
private $container;
/**
* Constructor
* #param RouterInterface $router
*/
public function __construct(RouterInterface $router, $container)
{
$this->router = $router;
$this->container = $container;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
if ($request->isXmlHttpRequest()) {
$user = $this->container->get('security.context')->getToken()->getUser();
$result = array('success' => true, 'user' => $user);
return new Response(json_encode($result));
} else {
$route = $this->router->generate('ShopiousMainBundle_profile');
$referrer_url = $request->server->get('HTTP_REFERER');
if (strstr($referrer_url, '/items/')) {
$route = $referrer_url;
}
return new RedirectResponse($route);
}
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
if ($request->isXmlHttpRequest()) {
$result = array('success' => false, 'message' => $exception->getMessage());
return new Response(json_encode($result));
} else {
// Handle non XmlHttp request here
}
}
}
why is it that:
$user = $this->container->get('security.context')->getToken()->getUser();
returns null? how do i get the authenticated user at this point?
You should use the $token variable you are receiving as an argument instead of $this->container->get('security.context')->getToken().
$user = $token->getUser();
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$user = $token->getUser();
}
that's all ;)
maibe you don't set your provider in security.yml.
security:
provider:
example:
entity: {class Acme\AuctionBundle\Entity\User, property: username}
Replace the Bundle and the entity by yours.
The only way I found was to inject the entityManager,
calls:
- [setEntityManager,[#doctrine.orm.entity_manager]]
get the username from the request and query for the user, using that username.
$userRepo->findByEmail($request->get('_username'));

Categories