-
-
Save umpirsky/1000349 to your computer and use it in GitHub Desktop.
security.authentication.listener.url: | |
class: Мз\Security\Http\Firewall\UrlAuthenticationListener | |
arguments: [@security.context, @security.authentication.manager, @security.authentication.session_strategy, 'secured_area', {login_path: '/not-auth', check_path: '/auth/customer', default_target_path: '/хоме'}] | |
tags: | |
- { name: monolog.logger, channel: security } |
security: | |
encoders: | |
Symfony\Component\Security\Core\User\User: plaintext | |
providers: | |
in_memory: | |
users: | |
admin: { password: admin, roles: 'ROLE_ADMIN' } | |
access_control: | |
- { path: ^/, roles: ROLE_ADMIN } | |
factories: | |
UrlSecurityFactory: %kernel.root_dir%/../src/My/Resources/config/security_factories.xml | |
firewalls: | |
profiler: | |
pattern: ^/_profiler | |
security: false | |
wdt: | |
pattern: ^/_wdt | |
security: false | |
notauth: | |
pattern: ^/not-auth | |
security: false | |
secured_area: | |
pattern: ^/ | |
security: true | |
url_login: ~ |
<?xml version="1.0" ?> | |
<container xmlns="http://symfony.com/schema/dic/services" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> | |
<services> | |
<service id="security.authentication.factory.url" class="My\Security\Factory\UrlLoginFactory"> | |
<tag name="security.listener.factory" /> | |
</service> | |
</services> | |
</container> |
<?php | |
namespace My\Security\Factory; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\Security\Core\Exception\BadCredentialsException; | |
use Symfony\Component\Security\Http\Firewall\AbstractPreAuthenticatedListener; | |
use Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener; | |
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; | |
use Symfony\Component\Security\Core\SecurityContextInterface; | |
class UrlAuthenticationListener extends AbstractAuthenticationListener | |
{ | |
protected function attemptAuthentication(Request $request) | |
{ | |
$username = 'admin'; | |
$password = 'admin'; | |
$request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username); | |
return $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey)); | |
} |
<?php | |
namespace My\Security\Factory; | |
use Symfony\Component\DependencyInjection\DefinitionDecorator; | |
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface; | |
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory; | |
use Symfony\Component\DependencyInjection\ContainerBuilder; | |
use Symfony\Component\DependencyInjection\Definition; | |
use Symfony\Component\DependencyInjection\Parameter; | |
use Symfony\Component\DependencyInjection\Reference; | |
use Symfony\Component\Config\Definition\Builder\NodeDefinition; | |
/** | |
* UrlLoginFactory creates services for url login authentication. | |
* | |
* @author umpirsky | |
*/ | |
class UrlLoginFactory extends AbstractFactory | |
{ | |
/** | |
* @see Symfony\Bundle\FrameworkBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface::getPosition() | |
*/ | |
public function getPosition() | |
{ | |
return 'pre_auth'; | |
} | |
/** | |
* @see Symfony\Bundle\FrameworkBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface::getKey() | |
*/ | |
public function getKey() | |
{ | |
return 'url-login'; | |
} | |
protected function getListenerId() | |
{ | |
return 'security.authentication.listener.url'; | |
} | |
protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId) | |
{ | |
$provider = 'security.authentication.provider.dao.'.$id; | |
$container | |
->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.dao')) | |
->replaceArgument(0, new Reference($userProviderId)) | |
->replaceArgument(2, 'secured_area') | |
->addArgument($id) | |
; | |
return $provider; | |
} | |
protected function createListener($container, $id, $config, $userProvider) | |
{ | |
$listenerId = $this->getListenerId(); | |
$listener = new DefinitionDecorator($listenerId); | |
$listenerId .= '.'.$id; | |
$container->setDefinition($listenerId, $listener); | |
return $listenerId; | |
} | |
protected function createEntryPoint($container, $id, $config, $defaultEntryPointId) | |
{ | |
return $defaultEntryPointId; | |
} | |
} |
I fixed createListener() method, and now I get "A Token was not found in the SecurityContext. ".
This probably means that your provider is wrong.
I got in memmory provider defined in https://gist.github.com/1000349#file_security.yml.
Is your URL behind a firewall ?
Yes. But maybe in https://gist.github.com/1000349#file_url_login_factory.php createAuthProvider() is not ok. I don't have some good example on how this part should look like, and I'm not sure what security.authentication.provider.dao is. But I would like to test this with in memmory provider and migrate to entity provider later.
Fixed createAuthProvider(). Now I get Fatal error: Call to undefined method Symfony\Component\Security\Core\User\InMemoryUserProvider::supports() in Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php on line 56
You messed the authentication provider and the user provider. The AuthenticationProviderManager should receive some authentication providers. InMemoryUserProvider is a user provider (as stated in its name)
Ugh, you are right. Thanks. It's so hard when there is no documentation. I'm trying to implement simple authentication via url. Can you suggest which authentication provider should I use?
I think the good choice is PreAuthenticatedAuthenticationProvider, as you are defining a pre_auth listener. check the X509Factory to see how it is done.
i did that in createAuthProvider() but still getting "A Token was not found in the SecurityContext. " error.
I came to an interesting result. After hours of debugging. I have chaged https://gist.github.com/1000349#file_cnofig.yml and find out that it works if I set 3rd argument of my listener to 'secured_area' value. Don't know why it compares to 'secured_area', but when I set that value, it always authenticates me as admin when I put 'admin' for username in https://gist.github.com/1000349#file_url_authentication_listener.php no mattter what I enter for credentials, it only check username. If I change username to something other then 'admin' it gives "A Token was not found in the SecurityContext." again. Also, the interesting thing is that after clearing the session it auto authenticates on all urls. Can you help put me on right way? I feel I'm close to the solution. Thanks.
the point is that your listener always return array('admin', 'admin')
. It does not check if the URL contains something.
I know, I just want to simulate auth with hardcoded values. Thse hardcoded array('admin', 'admin') I will get from request ofc. I expected that it checks credentials for me. As you can see, I have in memmory user provider with user admin: { password: admin, roles: 'ROLE_ADMIN' }. so auth should fail if I hardcode array('admin', 'admin2'), but it authenticates in that case too. If I hardcode array('admin2', 'admin') it fails with "A Token was not found in the SecurityContext. " error. I wonder why that happens?
Maybe I should extend my listener from AbstractAuthenticationListener instead AbstractPreAuthenticatedListener. But whenever I try that, I end up with damn "A Token was not found in the SecurityContext." :) It should more or less work like UsernamePasswordFormAuthenticationListener, but get username and password from GET instead from POST. Later I can add some sha1 with secret key.
How are the username and password passed to the app ? can you give an example of url ?
For now they are hardcoded in https://gist.github.com/1000349#file_url_authentication_listener.php. But they will be passed like /auth/username/admin/hash/foo via url. But I'm hardcoding for now to keep it simpler for testing. Passing user/pass is not the problem I'm trying to solve now. Problem is authentication itself.
With current listener implementation https://gist.github.com/1000349#file_url_authentication_listener.php with 'admin' username it is always authenticated, no matter what password (credentials) I enter. And it triggers authentication on all urls, but I guess that's how AbstractPreAuthenticatedListener works.
If we ever solve this, I will write detailed instructions about implementing custom authentication system with Symfony2.
OK, I kind of sorted it all out. I switched to AbstractAuthenticationListener instead AbstractPreAuthenticatedListener. Added provider key and config to https://gist.github.com/1000349#file_cnofig.yml. And add some magic in createAuthProvider() method. Now it works.
The only thing that are not clear to me are is what is providerKey? I hardcoded it in createAuthProvider() and config.yml, but don't know what is it and how to use it?
Thanks.
Strange is that I don't get usual "Full authenticvation required to access this resource" when I'm unauthorised and go to some protected url, but get "A Token was not found in the SecurityContext.".
Hi umpirsky, did you figure out how to solve this?
@kayue Yes, but it was very hard work, and I suggest you to give up, you can make it on your own better. Maybe situation is now better since stable Symfony2 is released.
I was trying to create a Wordpress Bridge (https://github.com/kayue/WordpressBundle).
I figured out the "A Token was not found in the SecurityContext." could be fixed if I add anonymous to my firewall.
Don't know are we talking about the same issue.
@kayue I think not :)
Thankyou! Add "anonymous" to firewall
The definition of the
security.authentication.listener.url
service is wrong. It does not correspond to the argument you need to pass to it (its constructor has some requirements).