Silex Firewall setup for admin path - php

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.

Related

Allowing users and anonymous to view website, but securing part of it

I got problem: I want to allow both users and anonymous view website, and allow only users to take certain actions ( which i got covered). The thing is that certain paths ( /account etc) should be accessible only to logged users.
I tried really hard to configure my secure.php but, either anonymous can access /account or I can't access logged user anywhere except /account/...
tried both :
$app['security.firewalls'] = array(
'secured' => array(
'pattern' => '/account',
'form' => array('login_path' => '/login', 'check_path' => '/account/login_check'),
'logout' => array('logout_path' => '/account/logout', 'invalidate_session' => true),
'users' => $app->share(function () use ($app) {
return new UserProvider($app['db']);
}),
),
'unsecured' => array(
'pattern'=> '/',
'anonymous' => true,
)
);
and
$app['security.firewalls'] = array(
'secured' => array(
'pattern' => '/account',
'anonymous'=> true,
'form' => array('login_path' => '/login', 'check_path' => '/account/login_check'),
'logout' => array('logout_path' => '/account/logout', 'invalidate_session' => true),
'users' => $app->share(function () use ($app) {
return new UserProvider($app['db']);
}),
),
);
You need to do that in the authorization step, so you have to configure the security.access_rules key.
You can do this with a single firewall by enabling anonymous and authenticated users in it and then, with access rules, restrict access to /accounts URIs to only allow authenticated users:
<?php
$app['security.firewalls'] = array(
'secured' => array(
'pattern' => '^.*$',
'anonymous' => true,
'form' => array('login_path' => '/login', 'check_path' => '/account/login_check'),
'logout' => array('logout_path' => '/account/logout', 'invalidate_session' => true),
'users' => $app->share(function () use ($app) {
return new UserProvider($app['db']);
}),
);
// By using authorization the access to the /account/* is protected to
// users with the ROLE_USER (you can be more creative here if you want)
// and with the second rule the whole site is allowed to non authenticated
// users (remember the /login path must not be protected!)
$app['security.access_rules'] = array(
// this could be also array('^/account', 'ROLE_USER')
array('^/account', 'IS_AUTHENTICATED_FULLY'),
array('^.*$', 'IS_AUTHENTICATED_ANONYMOUSLY')
);
See Symfony doc for more information on authorization. Also if you want to know more about access control without roles check this out
The Easiest way is to set a session in the page headers.
if(!isset($_SESSION["logged_in"])){
header("Location: http://www.example.com/");
}
This is quite primitive - Have you thought about using a MVC framework? Would save you a of a lot of time.
Why not create a Controller?

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: redirect admin to admin-home path after login

How in Silex redirect admin (ROLE_ADMIN) to /admin page after successful login and user (ROLE_USER) to / page after successful login?
My config so far:
$app['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' => $app->share(function() use ($app) {
return new App\User\UserProvider($app['db']);
}),
),
);
$app['security.access_rules'] = array(
array('^/admin', 'ROLE_ADMIN', 'http'),
array('^.*$', 'ROLE_USER'),
);
Thx in advance
I think there are a few ways to do this - I would recommend adding a new controller for /login/redirect and then sending people there after login. The controller can then perform the logic for where to send users based on their roles.
class LoginRedirect implements ControllerProviderInterface {
public function connect(Application $app) {
$controller = $app['controllers_factory'];
$controller->get('/', array($this, 'index'))->bind('login-redirect');
return $controller;
}
public function index(Application $app) {
if ($app['security']->isGranted('ROLE_ADMIN')) {
return $app->redirect($app['url_generator']->generate('admin-home'));
}
return $app->redirect($app['url_generator']->generate('non-admin-home'));
}
}
Add a route for it:
$app->mount('/login/redirect', new Controller\LoginRedirect());
And then in your security firewall settings add the options in the form section to use this route as the default target path - i.e. where all users are redirected to after login. Note that with this setting, you will loose the feature where users are redirected to the HTTP referer.
...
'form' => array(
'login_path' => '/login',
'check_path' => '/login_check',
'always_use_default_target_path' => true,
'default_target_path' => '/login/redirect'
),
...

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)

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