can't secure my path with silex securityServiceProvider - php

I am trying to build a simple authentication code for my Silex app but I can't make it work.
I spent hours trying to figure out where is the mistake but can't find it..
Here is the code:
use Symfony\Component\Security\Core\User\User;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$app = new Silex\Application();
$app['debug'] = true;
$app->register(new Silex\Provider\SessionServiceProvider());
$app->register(new Silex\Provider\UrlGeneratorServiceProvider());
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'admin' => array(
'pattern' => '^/admin.*',
'http' => true,
'form' => array(
'login_path' => '/login',
'check_path' => '/admin/login_check',
),
'logout' => array(
'logout_path' => '/admin/logout',
'invalidate_session' => true
),
'users' => $usersData,
),
)
));
$app->get('/login', function(Request $request) use ($app) {
return $app['twig']->render('login.html', array(
'error' => $app['security.last_error']($request),
'last_username' => $app['session']- >get('_security.last_username'),
));
});
The app is not blocking any path, I really don't understand what I'm missing as I already did this in another app and everything works fine there...
Thank you in advance for any help.

I had a problem with the provisioning of my vagrant machine, rebuilt everything from scratch and now it works flawlessly.

Related

Silex Firewall setup for admin path

i need to setup my silex firewall like:
www.mysite.com/* => access to all users
www.mysite.com/admin/* => access to only logged in users
i use this set up but it does not work as expected:
$app->register(new SecurityServiceProvider(), array(
'security.firewalls' => array(
'secure' => [
'pattern' => '^/.*$',
'anonymous' => true,
'form' => array(
'login_path' => '/admin/login',
'check_path' => '/admin/auth'
),
'logout' => array(
'logout_path' => '/admin/logout'
),
'users' => $app->share(function() use ($app) {
return new AuthenticationSuccessHandler($app['db']);
}),
]
),
'security.access_rules' => array(
array('^/admin$', 'ROLE_ADMIN')
)
));
Any help?
Many Thanks!! ;-)
'users' => $app->share(function() use ($app) {
return new AuthenticationSuccessHandler($app['db']);
}),
The above function needs to return an object which implements
Symfony\Component\Security\Core\User\UserProviderInterface
Check here for custom user provider documentation
It may also be appropriate to move login_path outside the secured area. Another way of configuring would be:
$app['security.firewalls'] = array(
'secure' => array(
'pattern' => '^/admin/',
'form' => array('login_path' => '/login', 'check_path' => '/admin/auth'),
'users' => $app->share(function () use ($app) {
return new MyUserProvider($app['db']);
}),
),
),
);
$app['security.access_rules'] = array(
array('^/admin', 'ROLE_ADMIN')
);
Make sure you register doctrine dbal.

Route defined in Silex but 404 error

I use Silex framework in my project.
I defined a road / login that points to my form of authentication to connect to the app but when I do: ipsrv / login (ipsrv being the ip web server) he does not know the road (404 ).
My app.php file (extract):
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'login' => array(
'pattern' => '^/login',
'anonymous' => true,
'form' => array('login_path' => '/login', 'check_path' => '/login_check'),
'users' => $app->share(function () use ($app) {
return new Nautilus\DAO\UserDAO($app['db']);
})
),
'general' => array(
'pattern' => '^/',
'anonymous' => false,
'logout' => true
),
),
));
My routes.php file (extract):
$app->match('/login', function(Request $request) use ($app) {
return $app['twig']->render('login.html.twig', array(
'error' => $app['security.last_error']($request),
'last_username' => $app['session']->get('_security.last_username'),
));
})->bind('login');
How do you use these 2 files? Check the server is configured to execute your silex app, and both files are actually included there.
It works perfectly well in single-script app:
<?php
// web/index.php
require_once __DIR__.'/../vendor/autoload.php';
$app = new Silex\Application();
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'login' => array(
'pattern' => '^/login',
'anonymous' => true,
'form' => array('login_path' => '/login', 'check_path' => '/login_check'),
'users' => $app->share(function () use ($app) {
return new \Symfony\Component\Security\Core\User\InMemoryUserProvider();
})
),
'general' => array(
'pattern' => '^/',
'anonymous' => false,
'logout' => true
),
),
));
$app->match('/login', function(\Symfony\Component\HttpFoundation\Request $request) use ($app) {
return 'login form';
})->bind('login');
$app->run();
When you run web server as
php -S localhost:8080 -t ./web/
curl http://localhost:8080/login returns "login form";
File routes.php in full :
<?php
use Symfony\Component\HttpFoundation\Request;
// Index
$app->match('/', function () use ($app){
return $app['twig']->render('aff_index.html.twig');
})->bind('home');
// Login form
$app->match('/login', function(Request $request) use ($app) {
return $app['twig']->render('login.html.twig', array(
'error' => $app['security.last_error']($request),
'last_username' => $app['session']->get('_security.last_username'),
));
})->bind('login');
File app.php in full :
<?php
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\ExceptionHandler;
// Register global error and exception handlers
ErrorHandler::register();
ExceptionHandler::register();
// Register service providers.
$app->register(new Silex\Provider\DoctrineServiceProvider());
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => array(
__DIR__.'/../views/layout',
__DIR__.'/../views/admin_bdd',
__DIR__.'/../views/admin_preferences',
__DIR__.'/../views/admin_users',
__DIR__.'/../views/app_connexion',
__DIR__.'/../views/app_main',
__DIR__.'/../views/audits_mesedits',
__DIR__.'/../views/reporting_lb',
__DIR__.'/../views/reporting_meteo',
__DIR__.'/../views/menu'
)));
$app->register(new Silex\Provider\UrlGeneratorServiceProvider());
$app->register(new Silex\Provider\SessionServiceProvider());
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'login' => array(
'pattern' => '^/login',
'anonymous' => true,
'form' => array('login_path' => '/login', 'check_path' => '/login_check'),
'users' => $app->share(function () use ($app) {
return new Nautilus\DAO\UserDAO($app['db']);
})
),
'general' => array(
'pattern' => '^/',
'anonymous' => false,
'logout' => true
),
),
));
$app->register(new Silex\Provider\MonologServiceProvider(), array(
'monolog.logfile' => __DIR__.'/../var/logs/nautilus.log',
'monolog.name' => 'Nautilus',
'monolog.level' => $app['monolog.level']
));
$app->register(new Silex\Provider\ServiceControllerServiceProvider());
if (isset($app['debug']) && $app['debug']) {
$app->register(new Silex\Provider\HttpFragmentServiceProvider());
$app->register(new Silex\Provider\WebProfilerServiceProvider(), array(
'profiler.cache_dir' => __DIR__.'/../var/cache/profiler'
));
}
$app['dao.user'] = $app->share(function ($app) {
return new Nautilus\DAO\UserDAO($app['db']);
});
Index.php :
<?php
require_once __DIR__.'/../vendor/autoload.php';
$app = new Silex\Application();
require __DIR__.'/../app/config/dev.php';
require __DIR__.'/../app/app.php';
require __DIR__.'/../app/routes.php';
$app->run();
For information I use Wamp (latest version) on Windows 10 Pro x64 with a MySQL database. And I use PhpStorm 10.0.
Problem solved. Xampp is incompatible with WIndows 10 (impossible to create a user: Error) and Wamp does not handle URL rewriting Windows 10. I have tested with Windows 7 and Xampp with URL rewriting and virtual hosts go well and no worries Silex roads.

Silex Security Provider - Token failing to be set

I'm working for the first time with Silex's Security Provider and I'm having issues with the process. I currently have the basic HTTP auth working (using the coded example user as shown here in the docs).
When switching HTTP out for the form option however the login form is submitting, and returning to itself. I have created a UserProvider class and the loadUserByUsername method is being successfully called, however the email isn't being passed in (being set to "NONE_PROVIDED" - altered from username). This I found when working through the vendor code is because the token isn't being set ($app['security']->getToken() returning null at all points). I've trawled through all the docs I can but I can't find any mention of this.
The main code is included below, let me know if there is anything else, thanks!
Security Provider Configuration
// Protects all routes within /auth, redirecting to /login successfully
$app->register(new SecurityServiceProvider(), array(
'security.firewalls' => array(
'unauth_area' => array(
'pattern' => '^/(?!auth)'
),
'auth_area' => array(
'pattern' => '^/.*$',
'form' => array(
'login_path' => '/login',
'check_path' => '/auth/login_check',
'default_target_path' => '/auth/overview',
),
'users' => $app->share(function () use ($app) {
return new UserProvider($app['db']);
}),
),
),
'access_control' => array(
array('path' => '^/.*$', 'role' => 'ROLE_USER'),
// Include the following line to also secure the /admin path itself
// array('path' => '^/admin$', 'role' => 'ROLE_ADMIN'),
),
));
(My Custom) method - UserProvider class
public function loadUserByUsername($email) {
// Dying at this point shows it reaches here, but $email is null
$stmt = $this->conn->executeQuery('SELECT * FROM user WHERE email = ?', array(strtolower($email)));
if (!$user = $stmt->fetch()) {
throw new UsernameNotFoundException(sprintf('Email "%s" does not exist.', $email));
}
return new User($user['email'], $user['password'], explode(',', $user['roles']), true, true, true, true);
}
Form Class
class LoginType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('_username', 'text', array(
'required' => true,
'constraints' => array(
new Assert\NotBlank(),
new Assert\Email(),
)
))
->add('_password', 'password', array(
'required' => true,
'constraints' => array(
new Assert\NotBlank(),
),
))
->add('Login', 'submit');
}
public function getName() {
return 'login';
}
}
Silex Security Provider docs
It has nothing to do with the token… I just had the same problem with
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'admin' => array(
'pattern' => '^/admin',
'form' => array(
'login_path' => '/',
'check_path' => '/admin/login_check',
'username_parameter'=> 'mail',
'password_parameter' => 'password',
),
'logout' => array('logout_path' => '/logout'),
//'anonymous' => true,
'users' => function () use ($app) {
return new UserProvider($app['db']);
},
)
),
'security.access_rules' => array(
array('^/$', 'IS_AUTHENTICATED_ANONYMOUSLY'),
array('^/admin', 'ROLE_USER')
)
));
After a couple hours trying and testing, I checked the name attribute in my form's input… Saw form[mail]
So I tried
'username_parameter'=> 'form[mail]',
'password_parameter' => 'form[password]',
And … ALLELUIA!!!!! had my mail in loadUserByUsername($mail)

Redirect loop issue in Silex SecurityServiceProvider

I've got a issue creating a login form to authenticate users. I followed exactly the example in this page: http://silex.sensiolabs.org/doc/providers/security.html#defining-more-than-one-firewall, but I've got a redirect loop when I try to access my site.
I'd like to secure my entire website, so I wrote this lines:
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'login' => array(
'pattern' => '^/login$',
),
'secured' => array(
'pattern' => '^.*$',
'form' => array('login_path' => '/login', 'check_path' => '/login_check'),
'logout' => array('logout_path' => '/logout'),
'users' => array(
// password is foo
'user1' => array('ROLE_USER', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='),
),
),
),
));
$app->mount('/login', include '../src/login.php');
Then I created a login.php file:
$controllers->get('/', function(Silex\Application $app, Request $request) {
return $app->render('login.html.twig', array(
'error' => $app['security.last_error']($request),
'last_username' => $app['session']->get('_security.last_username'),
));
});
When I try to go to my homepage http://localhost I've got a redirect loop message by the browser.
Where am I wrong?
Thanks all!
Ok I spotted the issue: it's on the mount behaviour with /login path. I had to change those lines in:
$app->mount('/', include '../src/login.php');
And in the login.php:
$controllers->get('/login', ...

Display authenticated users on non-secure (anonymous) routes

I use PHP and Silex to build a web app and I implemented basic authentication via SecurityServiceProvider like this:
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'private' => array(
'remember_me' => array(
'key' => $config['secret_key'],
'lifetime' => $config['remember_me_duration'],
),
'pattern' => '^/admin',
'form' => array('login_path' => '/login', 'check_path' => '/admin/login_check'),
'logout' => array('logout_path' => '/admin/logout'),
'users' => $app->share(function () use ($app) {
// ...
}),
),
'public' => array(
'pattern' => '^/$',
'anonymous' => true,
),
'login' => array(
'pattern' => '^/login$',
'anonymous' => true,
),
),
));
As you can see the /admin paths are secured, there I can use $app['security']->getToken()->getUser(); to get the actual user that is authenticated and display something like Logged in as $username, but if I do this on the /login or / routes the user will always be anon. even if I'm authenticated.
How can I get the authenticated user, if there is one, otherwise the anon. string, on the / and /login routes in order to display a message like: Logged in as $username ?
I also tried to use is_granted('IS_AUTHENTICATED_FULLY' function in the twig templates to check if the user is authenticated, but on /login and / it returns false (even if I'm authenticated) since the anonymous user takes precedence over the authenticated ones, so no success here.
In order to help other people, I solved the issue as described below.
Silex doesn't use access_control, but access_rules (dammit).
As Pazi (+1ed) suggested I combined everything under a single firewall and used access_rules:
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'main' => array(
'remember_me' => array(
'key' => $config['secret_key'],
'lifetime' => $config['remember_me_duration'],
),
'pattern' => '^/',
'anonymous' => true,
'form' => array(
'login_path' => '/login',
'check_path' => '/admin/login_check',
),
'logout' => array('logout_path' => '/admin/logout'),
'users' => $app->share(function () use ($app) {
// ...
}),
),
),
'security.access_rules' => array(array('^/admin/files', 'ROLE_ADMIN'),
),
));
Everything must be under the same firewall and you have to use access control.
#Paul
I'd prefer to add the comment but I can't do it yet, so as an answer than:
It looks like taking out
'pattern' => '^/admin'
string from the firewall pattern you have opened anonymous access to the /admin/login_check, and this is not right.
My suggestion is to take it back and remove
'anonymous' => 'true'
line from the firewall. Should work the same but would be more secured.

Categories