Symfony2 (.7) : Users in firewall with active directory - php

I searched for this kind of connection but all the bundles do not work with symfony security 2.7 (Composer says packages problems) and symfony 3.0.
Note : I actually use Silex Framework.
I want to return a true or false response if the user is connected to the active directory.
I have this code for tests :
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'login' => array(
'pattern' => '^/login$',
),
'secured' => array(
'pattern' => '^/',
'anonymous' => false,
'logout' => true,
'form' => array('login_path' => '/login', 'check_path' => '/login_check'),
'users' => array(
// raw password is foo
'admin' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='),
),
),
),
));
The login with admin & foo worked but that's not what I want.
I want to replace this part of code :
'users' => array(
// raw password is foo
'admin' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='),
),
Or any others solutions to replace the value who allows the user to bypass the firewall and enter in the application.
Note :
I use adLDAP plugin to test the connection with the Active Directory (It's works perfectly).

Try to use LdapUserProvider for it. Remove 'users' => array(...) from secured config and add user provider definition for this zone.
$app['security.user_provider.secured'] = $app->share(function($app) {
return new \Symfony\Component\Security\Core\User\LdapUserProvider(
new \Symfony\Component\Ldap\LdapClient('ldap.server.com'),
'baseDn', // ex.: dc=example,dc=com
'searchDn' // ex.: CN={username},DC=example,DC=com
);
});
Or you can write your own user provider based on LdapUserProvider and adLDAP. Something like this:
namespace My\Namespace;
use Symfony\Component\Security\Core\User\LdapUserProvider;
use Symfony\Component\Security\Core\User\User;
class adLdapUserProvider extends LdapUserProvider
{
public function loadUserByUsername($username)
{
// you code, that returns user from ldap server
// $this->ldap - is object of adLDAP
$user = $this->ldap->user()->info($username);
return $this->loadUser($username, $user);
}
}
and change user provider definition
$app['security.user_provider.secured'] = $app->share(function($app) {
return new \My\Namespace\adLdapUserProvider(
new \adLDAP\adLDAP(...)
);
});

Related

Silex Custom Auth with Guard

I try to follow the tutorial on the Silex official website but I have an error when the Authenticator is registered as a service.
Catchable fatal error: Argument 1 passed to App\Security\TokenAuthenticator::__construct() must be an instance of App\Security\EncoderFactoryInterface, instance of Symfony\Component\Security\Core\Encoder\EncoderFactory given, called in C:\wamp\www\api\src\app.php on line 41 and defined in C:\wamp\www\api\src\App\Security\TokenAuthenticator.php on line 17
Here is my app.php
$app = new Silex\Application(['debug' => true]);
$app['security.firewalls'] = array(
'main' => array(
'guard' => array(
'authenticators' => array(
'app.token_authenticator'
),
// Using more than 1 authenticator, you must specify
// which one is used as entry point.
// 'entry_point' => 'app.token_authenticator',
),
// configure where your users come from. Hardcode them, or load them from somewhere
// http://silex.sensiolabs.org/doc/providers/security.html#defining-a-custom-user-provider
'users' => array(
//raw password = foo
'test' => array('ROLE_USER', '$2y$10$3i9/lVd8UOFIJ6PAMFt8gu3/r5g0qeCJvoSlLCsvMTythye19F77a'),
),
// 'anonymous' => true
),
);
$app['app.token_authenticator'] = function ($app) {
return new App\Security\TokenAuthenticator($app['security.encoder_factory']);
};
$app->register(new Silex\Provider\SecurityServiceProvider());
return $app;
The TokenAuthenticator is the same as the example. How can i configure this service with an argument that implements App\Security\EncoderFactoryInterface ? Can you help me please ?
use statement is missing :
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;

Can't register custom DBAL types

I'm trying to register a bunch of custom DBAL types. When I run the migrations:diff I get the exception:
Fatal error: Class 'App\Persistence\Models\Types\Money' not found in D:\development\projects\project\vendor\doctrine\dbal\lib\Doctrine\DBAL\Types\Type.php on line 174
I've tried to do this either by registering it after all Doctrine settings and using a event subscriber:
class DoctrineCustomTypesEventSubscriber implements Subscriber {
public function getSubscribedEvents() {
return [Events::postConnect];
}
public function postConnect(ConnectionEventArgs $args) {
Type::addType('money', "App\Persistence\Models\Types\Money");
Type::addType('geopoint', "App\Persistence\Models\Types\Point");
Type::addType('geoarea', "App\Persistence\Models\Types\Area");
}
}
$doctrineCustomTypesSubscriber = new App\Persistence\DoctrineCustomTypesEventSubscriber();
$app['db.event_manager']->addEventSubscriber($doctrineCustomTypesSubscriber);
$app->register(new Dflydev\Provider\DoctrineOrm\DoctrineOrmServiceProvider, array(
'orm.proxies_dir' => $app['APP_ROOT_DIR'].'/app/persistence/proxies',
'orm.em.options' => array(
'mappings' => array(
array(
'type' => 'annotation',
'namespace' => 'App\Persistence\Models',
'path' => $app['APP_ROOT_DIR'].'/app/persistence/models',
'use_simple_annotation_reader' => false,
),
),
),
));
Update
Placing the registration before all orm settings doesn't work either:
use Doctrine\DBAL\Types\Type;
$app->register(new Silex\Provider\DoctrineServiceProvider(), array(
'db.options' => array('url' => $app['APP_DB_CONN_URL']),
));
Type::addType('money', "App\Persistence\Models\Types\Money");
Type::addType('geopoint', "App\Persistence\Models\Types\Point");
Type::addType('geoarea', "App\Persistence\Models\Types\Area");
What am I doing wrong here?
Also can you tell me where do I put these registrations:
$em->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping(...)?
The type classes should exist and be registered in autoloader, so that they can be instantiated by FQCN associated with type.

Silex / Symfony programmatically login

I am using the Silex / Symfony security service and try to implement a automatic login when the specific parameters are passed in the request query.
I've looked into the modules and also search on the internet for a solution and always found something like the following:
$user = (new \Portal\UserProvider($app['databases']['read']))->loadUserByUsername($subscriber_id);
$token = new \Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken($user, $user->getPassword(), 'secured', $user->getRoles());
$app['security.token_storage']->setToken($token);
Unfortunately, this does not work for my app. I don't know whats wrong but the security module keeps redirecting me to /login/ as specified in the registration process:
/**
* Registers the security firewall.
*/
private function registerSecurity()
{
$this->register(new \Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'login' => array(
'pattern' => '^/(login/|terms|imprint|animation|error)',
),
'secured' => array(
'pattern' => '^/',
'form' => array(
'login_path' => '/login/',
'check_path' => '/login_check'
),
'logout' => array(
'logout_path' => '/logout'
),
'users' => $this->share(function () {
return new \Portal\UserProvider($this['databases']['read']);
}),
),
'unsecured' => array(
'anonymous' => true
),
),
'security.encoder.digest' => $this->share(function () {
return new \Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder('sha1', false, 1);
}),
'security.access_rules' => array(
array('^/login', 'ROLE_GUEST'),
),
'security.role_hierarchy' => $this->share(function () {
return array();
})
));
$this->boot();
}
Is there anything I have to consider about
reloading
order of registering the SecurityServiceProvider, SessionServiceProvider
this manual token setting
?
You're using the 'form' authentication provider, but this won't work (or maybe I'm not understanding you correctly?). In order to be able to:
try to implement a automatic login when the specific parameters are passed in the request query
You need to hook into the Security service. In order to do that you have to create a Listener and register it. You'll also need a Provider
This is not an easy path as the security component works with many concepts.
You can see a working example in this repo (which implements an OAuth service)
If your security flow is easy and you don't need roles, you can just use a before middleware (and forget about the security component) like so:
<?php
$app->before(function (Request $request, Application $app) {
$session = $request->getSession();
if (false === $session->get('logged', false)) {
if (null !== $request->get('blah', null)) {
$session->set('logged', true);
}
else {
return new RedirectResponse('/login-url');
}
}
});
You could use Silex's guard. It works well with get Request. And standard form could be use as complement.
In your secured Security.firwall, add the guard parameter :
"guard" => array ("authenticator" => array("app.myauthenticator") )
And create your custom class, to validate login.
Just read the Silex cookbook.

How does the default_target_path work in a Silex Firewall?

I have setup a firewall in Silex as follows:
$this -> register(new SecurityServiceProvider(), array(
'security.firewalls' => array(
'login' => array(
'pattern' => '^/admin/login$'
),
'admin' => array(
'pattern' => '^/admin.*$',
'form' => array(
'login_path' => '/admin/login',
'check_path' => '/admin/security/validate',
'default_target_path' => "/admin",
'always_use_default_target_path' => true
),
'logout' => array(
'logout_path' => '/admin/security/logout'
),
'users' => $app -> share(function () use ($app) {
return new \Turtle\Providers\UserProvider($app);
})
)
),
'security.access_rules' => array(
array('^/admin.*$', 'ROLE_ADMIN')
)
));
This works in that when I hit a page in the 'admin' area I get redirected to my login page. However I have started to do some authorization in my custom AuththenticationSuccess handler. I want to use the built in method determineTargeUrl to redirect on success but it keeps redirecting to '/'.
After some debugging I have found that the options in the object that the method uses has the following:
array (size=5)
'always_use_default_target_path' => boolean false
'default_target_path' => string '/' (length=1)
'login_path' => string '/login' (length=6)
'target_path_parameter' => string '_target_path' (length=12)
'use_referer' => boolean false
Clearly this is not what I have set in my firewall. It is my understanding that this should match what it is in the firewall that I have used when accessing the system. The URL I used was 'http://localhost/admin'.
So how do I make it so that the options I have set in my firewall appear in the object so that I can use the determineTargetUrl?
Thanks lots, Russell
It looks like your problem is that your login route is inside your secured area. You've defined your access rule as ^/admin.*$. This means that any route starting with /admin requires ROLE_ADMIN including your login route. To fix this you need to remove security from your login route.
Add a new access rule above your admin rule.
'security.access_rules' => array(
array('^/admin/login$', 'IS_AUTHENTICATED_ANONYMOUSLY'),
array('^/admin.*$', 'ROLE_ADMIN')
)
Edit: After reading your question again, I may have misunderstood you. it sounds like you can successfully log in but are redirected to the wrong place after a successful login. Is that correct? If so I will remove this answer.
This was my mistake. I am using custom AuthenticationSuccess and AuthenticationFailure handlers and i neglected to pass any options into them when I was declaring them:
$app['security.authentication.success_handler.admin'] = $app -> share(function() use ($app) {
return new AuthenticationSuccessHandler($app['security.http_utils'], array(), $app);
});
$app['security.authentication.failure_handler.admin'] = $app -> share(function() use ($app) {
return new AuthenticationFailureHandler($app['kernel'], $app['security.http_utils'], array(), $app);
});
So the options array that is used in determineTargetUrl on authentication success was empty and thus had default values.
By adding an array of options to the AuthenticationSuccessHandler it works. This is OK as each custom handler is linked to a different firewall.

Always being redirected to /login when using SecurityServiceProvider

I have some issues with the SecurityServiceProvider of Silex.
Basically what I want is the following structure:
/admin/ --> The administration page that is restricted to some users
(can have multiple suppages e.g. /admin/users and /admin/projects)
/admin/login --> The page that visitors can use to login to the
administration page
/admin/logout --> The page visitors see when they logged off from the
administration page
To implement this, I wrote the following code:
<?php
require_once __DIR__.'/../vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$app = new Silex\Application();
$app['debug'] = true;
$app->register(new Silex\Provider\SecurityServiceProvider());
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__.'/views',
));
$app->register(new Silex\Provider\UrlGeneratorServiceProvider());
$app->register(new Silex\Provider\SessionServiceProvider());
$app['security.firewalls'] = array(
'login' => array(
'pattern' => '^/admin/login$'
),
'logout' => array(
'pattern' => '^/admin/logout$'
),
'admin' => array(
'pattern' => '^/admin/',
'form' => array('login_path' => '/admin/login', 'check_path'
=> '/admin/login_check'),
'users' => array(
'admin' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC
+GsReLf569mSKDsfods6LYQ8t
+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), // PW is foo
),
'logout' => array('logout_path' => '/admin/logout')
),
);
$app->get('/admin/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'),
));
});
$app->get('/admin/logout', function(Request $request) use ($app) {
return $app['twig']->render('logout.html', array());
});
$app->get('/admin/', function () use ($app) {
return $app['twig']->render('admin.html', array());
});
$app->run();
?>
Now, what happens is that when I visit the page "/admin/" I always get
redirected to "/login" which is good since I am not authenticated but
it should be "/admin"login" as the "login_path" parameter in my
configuration indicates... what am I doing wrong or could this be a
bug in the SecurityServiceProvider?
The only thing first comes to my mind is that the manual tells you that:
The login_path path must always be defined outside the secured area (or if it is in the secured area, the anonymous authentication mechanism must be enabled)
See here:
Silex doc on security with form
I hope it helps you solve a basic flaw and you can carry on.

Categories