silexphp / Silex

[DEPRECATED -- Use Symfony instead] The PHP micro-framework based on the Symfony Components
https://silex.symfony.com
MIT License
3.58k stars 718 forks source link

SecurityServiceProvider pre_auth missing _proxy #950

Closed Virakal closed 8 years ago

Virakal commented 10 years ago

I apologise if I am missing something, but I'm running into problems trying to use pre_auth security. The code "works" (it runs but doesn't do what I need it to) with anonymous and http, but pre_auth gives me the following exception:

InvalidArgumentException: Identifier "security.authentication_listener.pre_auth._proto" is not defined. in /vagrant/public/silex/vendor/pimple/pimple/lib/Pimple.php on line 78

but I can't find any documentation of _proto.

My bootstrap code is:

$app->register(new Provider\SecurityServiceProvider(), array(
    'security.firewalls' => array(
        'cp' => array(
            'pattern' => '^/cp/',
            'pre_auth' => true,
            'users' => $this->share(function () {
                return new CP\UserProvider();
            }),
        ),
    ),
));
davedevelopment commented 10 years ago

Off the top of my head, I don't think there is or was at the time of writing an easy way to automatically set something up for pre_auth, so the provider requires that you do it yourself.

You'll need a Symfony\Component\Security\Http\Firewall\ListenerInterface set as security.authentication_listener.cp.pre_auth. Take a look at the Symfony\Component\Security\Http\Firewall\SimplePreAuthenticationListener, that might get you somewhere.

Good luck, blog post and/or cookbook might be welcome if you get something sorted.

weaverryan commented 10 years ago

After chatting with Dave on Twitter, we clarified that we should be able to support a simple_preauth security provider, exactly like you see in the Symfony full stack framework: http://symfony.com/doc/current/cookbook/security/api_key_authentication.html#configuration. For most use-cases, this would mean that you'd just need to create the Authenticator class and register it as a service.

I would like this :)

sschueller commented 10 years ago

Does one of you have an example on how I can get 'simple_preauth' to work in silex? I can't seem to figure out how to register a custom provider.

I would like to use the API auth example from symfony (http://symfony.com/doc/current/cookbook/security/api_key_authentication.html) for my application.

Thanks.

kwisatz commented 10 years ago

I'm actually trying to implement such a listener, but am not really having that much luck. After looking through the SecurityServiceProvider class, I came up with a way to instantiate a simple_preauth authentication listener. See https://gist.github.com/kwisatz/5242e1612ece39489269 for the implementation.

And here's part of my firewall definition:

    'security.firewalls' => array(
        'login' => array(
            'pattern' => '^/auth/login$',
            'anonymous' => true
        ),
        'api' => array(
            'stateless' => true,
            'pattern' => '^/api/.*$',
            'simple_preauth' => array(
                'authenticator' => $app['apikey.authenticator']
            )
        ),

However, Whenever I try to query a URL that would trigger that firewall, all I get is:

[2014-06-06 15:56:18] project.INFO: Matched route "_api_ordersGET_date" (parameters: "_controller": "order.controller:getAllOnDate", "date": "Today", "_route": "_api_ordersGET_date")
[2014-06-06 15:56:18] project.INFO: Authentication exception occurred; redirecting to authentication entry point (A Token was not found in the SecurityContext.)
[2014-06-06 15:56:18] project.DEBUG: Calling Authentication entry point
[2014-06-06 15:56:18] project.INFO: < 302 http://localhost:8081/login

I'm at a loss here.. it seems that my ApiKeyAuthenticator class isn't event being initialized.

Also, I wonder whether my using the _app['security.authentication_provider.dao.proto'] is correct here. At least that seems to be the provider the Silex SecurityServiceProvider is using for all listeners but "anonymous".

Would someone be able to hint at the right direction, please?

iskandar commented 10 years ago

Here's how I got something working, maybe it will help others.


$app['security.authentication_listener.pre_auth.factory'] = $app->protect(function ($type) use ($app) {
    return $app->protect(function ($name, $options) use ($app, $type) {

        $app['security.authentication_listener.'.$name.'.'.$type] = $app->share(function () use ($app, $name, $options, $type) {
            return new SimplePreAuthenticationListener(
                $app['security'],
                $app['security.authentication_manager'],
                $name,
                $app['security.'.$type.'.authenticator'],
                $app['logger']
            );
        });

        $app['security.authentication_provider.'.$name.'.'.$type] = $app->share(function () use ($app, $name, $type) {
            return new SimpleAuthenticationProvider(
                $app['security.'.$type.'.authenticator'],
                $app['security.user_provider.' . $name],
                $name
            );
        });

        return array(
            'security.authentication_provider.'.$name.'.'.$type,
            'security.authentication_listener.'.$name.'.'.$type,
            null,
            'pre_auth',
        );
    });
});

/**
 * Add an API key authenticator that looks at the `api_key` query var.
 */
$app['security.api_key.param_name'] = 'api_key';
$app['security.api_key.authenticator'] = $app->share(function() use($app) {
    // ApiKeyAuthenticator from http://symfony.com/doc/current/cookbook/security/api_key_authentication.html
    return new ApiKeyAuthenticator(
        $app['security.user_provider.api'],
        $app['security.api_key.param_name'], // The Query var name
        $app['logger']
    );
});
$app['security.authentication_listener.factory.api_key'] = $app['security.authentication_listener.pre_auth.factory']('api_key');

$app->register(new \Silex\Provider\SecurityServiceProvider(), array(
    'security.firewalls'    => array(
        'api' => array(
            'pattern'      => '^/',
            'anonymous'    => true,
            'stateless'    => true,
            'api_key'      => true, // Our simple API Key authenticator
            'users'        => $app['security.orm.user_provider'], // implements the `getUsernameForApiKey` method
        ),
    ),
));
kwisatz commented 10 years ago

I got mine working as well, when I realized that I should have used _preauth as the last element in the array to be returned by the simple_preauth authentication listener factory, since it's the position of the listener in the stack, not the listener's name.

Now, how would one go about getting this integrated into Silex? I suppose it would have to be a patch against the SecurityServiceProvider, rather than a ServiceProvider of its own?

weaverryan commented 10 years ago

And I also have an implementation here: https://github.com/knpuniversity/rest/blob/rest-ep2/src/KnpU/CodeBattle/Application.php#L201.

BUT, we should really update the provider to include this :). @kwisatz Yes, the update would be to SecurityServiceProvider I believe - following here: https://github.com/silexphp/Silex/blob/master/src/Silex/Provider/SecurityServiceProvider.php#L133. That class isn't so easy to read through, so there could be a few challenges (though having a working solution to use as a starting point is a great asset!).

There's also a test class - https://github.com/silexphp/Silex/blob/master/tests/Silex/Tests/Provider/SecurityServiceProviderTest.php - which is kind of cool because you can write a little test to make sure that the end product of your system is working.

If you or anyone has any questions about this, I'd be happy to help if I can! I really think we need this as a built-in feature to the Silex security provider, but I hope someone else will have time before I do!

Thanks!

kwisatz commented 10 years ago

@weaverryan OK… I had started implementing this as a patch against the SecurityServiceProvider before making up my mind and creating a new ServiceProvider. So I do have a fair idea of where this should go, even if I didn't see it to the end the first time around. I will see if I can make a working patch over the weekend.

In the meantime, here's another gist with a working implementation as a Silex Service Provider, inspired by some design choices that I liked in @iskandar's example: https://gist.github.com/kwisatz/cfe8d8ed89764ea899fb

weaverryan commented 10 years ago

Beautiful, thanks David - keep us posted :).

kwisatz commented 10 years ago

Well, I'd say it's almost working, but I do have one serious issue, and some questions regarding test stubs.

So, here are the changes, including tests

First, the issue: Even though I have the stateless parameter set to true and I have verified that there is indeed no context_listener added, this still seems not to be stateless. Maybe someone has more insight into the Security Component to see what is missing.

Next, the questions: As you can see, I added a SecurityServiceProviderTest directory in the Provider test directory that holds a SampleUserProvider and a SampleAuthenticator. I was looking at using the InMemoryUserProvider, but as we need to identify users by a third parameter that is neither the username nor password, that wouldn't work. Suggestions as to how to improve this are greatly appreciated of course!

I'm not in a hurry to make a PR, so don't let yourselves feel pressurized either ;)

gaving commented 9 years ago

:+1: @kwisatz

Been searching endlessly for a key auth method and yours is the best resource I've found.

kwisatz commented 9 years ago

Hmm.. thanks @gaving Seems I completely forgot about this issue once I got it working on my end and then we decided we wouldn't need this after all… I'll try to get back to it asap, but if anyone wants to take it from here and create the PR against the Silex project, feel free!

alaczi commented 9 years ago

@kwisatz Any chance to do this PR in the near future? :)

kwisatz commented 9 years ago

@alaczi Unfortunately, I haven't received any feedback on my questions above. I don't think a PR would make sense with those issues unresolved. I'm also really busy these days, so near future, as in this WE or this week seems rather unlikely. I'll make a note though to remind me of looking back into this whenever I need a break from some other stuff.

alaczi commented 9 years ago

@kwisatz Thanks, I will also take a look on it. I solve it in a hacky way in our projects but it would be better to use in a proper way.

ragboyjr commented 8 years ago

Can we close this then?

fabpot commented 8 years ago

Doesn't the new Guard support in Silex 2.0 fixes this issue?

Virakal commented 8 years ago

I no longer have access to the project from the original issue, but it seems as though Guard would simplify things and pre_auth is no longer documented (was it ever? I may have just imagined it at the time :) ) so if the official solution to "something else has already authenticated me before I ever got to Silex but I want to use the security components anyway" is to write your own custom Guard auth system, I think that's a good enough reason to close the issue, personally.