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
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 ?
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!
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.
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.
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'));