Symfony 2.6 - Security provider - php

I am currently developping an application using Symfony2.6 And I have some trouble. I have two kind of users customers and employee so in my Database I have a table customer and a table employee. How can I tell symfony to go look in these two tables for the user credentials when the user try to log in?
I know that you can define a provider in the security.yml file which can be the entity but how can I declare two providers? is that possible or do I have to do this another way ?
thank all for you answers

I've got something similar with (currently) two different types of users. To handle all of the login and permissions though, I'm using FosUserBundle, and assigning them roles which I can check and then fetch user-profiles based on that.
To ensure that they get the correct ROLE_* assigned on the user creation I've followed the SO question, Adding new FOSUserBundle users to a default group on creation. They pickup a session variable with the future-role they will take, and then I have an event listener to set that role to the newly minted FosUser:
<?php
class UserCreationListener implements EventSubscriberInterface
{
// lots of constructor setup, debugging, logging and checks removed from code
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_SUCCESS => 'onRegistrationSuccess',
);
}
public function onRegistrationSuccess(FormEvent $event)
{
$user = $event->getForm()->getData();
$newRole = $this->session->get('futureRole', 'ROLE_CUSTOMER');
$user->addRole($role);
$this->userManager->updateUser($user);
return $this->redirect->redirect('app_profile', 302);
}
}

So thx to someone here i've found the answer to this specific problem here
http://symfony.com/doc/current/cookbook/security/multiple_user_providers.html
Symfony allows us to declare a multiple user providers

Related

I have searched for FOSUserBundle private profile, to no avail

So I have tried to make a symfony project that contains private profiles, and I use FriendsOfSymfony, but if I create a two users, each one can see the others uploaded file. I tried to search on multiple websites and failed to find something that worked for me.
example:
Adding extended profile entity to FOS UserBundle
I want to create private profiles for each user to upload files, no one except them should be able to see them(only the admin and the specific user).
Provided you have extended
Symfony\Bundle\FrameworkBundle\Controller\Controller
or FOSUserBundle Controller you could try doing something like this:
public function publicProfileAction($handleOrHash = '')
{
if ($handleOrHash == '') {
throw new NotFoundHttpException("Unable to find this user");
}
$user = $this->getUserService()->getUserByHandleOrHash($handleOrHash);
if (!$user) {
throw new NotFoundHttpException("Unable to find this user");
}
if ($user != $this->getUser()) {
throw new AccessDeniedException("You cannot see this user profile");
}
return $this->render('MyUserBundle:Default:public-profile.html.twig',
array('user' => $user)
);
}
Where getUserService() will return an object which has access to the userRepository with a method called getUserByHandleOrHash() which uses the repository to make the doctrine query.
Question is very broad.
You need to introduce authentication to your application.
And add user id or user relation to your uploaded files. Then in controller where you list/show that files (/profile/my-uploads) - load only files which belongs to this specific (logged in) user.
For admin access - best will be creating special back-office or implement User Impersonation.
Read more about it in Symfony documentation: http://symfony.com/doc/current/components/security/authentication.html

Authorization and Policies for Guest users

Case: I'm building a forum using Laravel's Authorization as a backbone using policies. Examples of checks I run are stuff like #can('view', $forum), and #can('reply', $topic), Gate::allows('create_topic', $forum) etc. These checks basically checks if the users role has that permission for the specific forum, topic or post. That way I can give roles very specific permissions for each forum in my application.
The issue is that all of these checks go through the Gate class, specifically a method called raw() which in its first line does this:
if (! $user = $this->resolveUser()) {
return false;
}
This presents an issue when dealing with forums. Guests to my application should also be allowed to view my forum, however as you can see from the code above, Laravels Gate class automatically returns false if the user is not logged in.
I need to be able to trigger my policies even if there is no user. Say in my ForumPolicy#view method, I do if(User::guest() && $forum->hasGuestViewAccess()) { return true }
But as you can see, this method will never trigger.
Is there a way for me to still use Laravel's authorization feature with guest users?
I'm not aware of a super natural way to accomplish this, but if it's necessary, you could change the gate's user resolver, which is responsible for finding users (by default it reads from Auth::user()).
Since the resolver is protected and has no setters, you'll need to modify it on creation. The gate is instantiated in Laravel's AuthServiceProvider. You can extend this class and replace the reference to it in the app.providers config with your subclass.
It's going to be up to you what kind of guest object to return (as long as it's truthy), but I'd probably use something like an empty User model:
protected function registerAccessGate()
{
$this->app->singleton(GateContract::class, function ($app) {
return new Gate($app, function () use ($app) {
$user = $app['auth']->user();
if ($user) {
return $user;
}
return new \App\User;
});
});
}
You could go a step further and set a special property on it like $user->isGuest, or even define a special guest class or constant.
Alternatively you could adjust your process at the Auth level so that all logged-out sessions are wrapped in a call to Auth::setUser($guestUserObject).
I just released a package that allows permission logic to be applied to guest users. It slightly modifies Laravel's Authorization to return a Guest object instead of null when no user is resolved. Also every authorization check now makes it to the Gate instead of failing authorization instantly because there isn't an authenticated user.

How to restrict access of action/method (of Controller) to specific user in Symfony?

Is there a way to restrict access to specific routes aka action/method of controller in Symfony based on user?
I am implementing FOSUserBundle for user management and it has roles for defining permission that works well if I have user with defined roles but if I want to restrict the page based on users I have to create role for every routes or are there any better approach.
I have looked into ACL and its perfect but I don't find solution for my case or am I missing something there.
Looking for some help and ideas.
Updates
#AJ Cerqueti - Answer can be quick fix but I am looking for better approach if any.
To be more specific is it possible to assign access permission for routes to user using ACL or some other better approach.
SYMFONY >=2.6
create a security voter
How to Use Voters to Check User Permissions
Now change the access decision strategy accordingly with the docs
SYMFONY <=2.5
For simple needs like this you can create a security voter that fit exactly what you need (ACL's are usually used for complex needs also due to the not easy implementation).
Then you can use the voter in your controller like described in the docs:
// get a Post instance
$post = ...;
// keep in mind, this will call all registered security voters
if (false === $this->get('security.authorization_checker')->isGranted('view', $post)) {
throw new AccessDeniedException('Unauthorised access!');
}
Read also How to Use Voters to Check User Permissions
UPDATE BASED ON COMMENT:
Maybe will be better to know on how many routes you want to add this behavior but in any case (and if you want to avoid to add in every controller the #AJCerqueti code) you can use a Voter like in this simple example:
Voter Class:
// src/AppBundle/Security/Authorization/Voter/RouteVoter.php
namespace AppBundle\Security\Authorization\Voter;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class RouteVoter implements VoterInterface
{
private $routes;
public function __construct(array $routes = array())
{
$this->routes = $routes;
}
public function supportsAttribute($attribute)
{
// you won't check against a user attribute, so return true
return true;
}
public function supportsClass($class)
{
// your voter supports all type of token classes, so return true
return true;
}
public function vote(TokenInterface $token, $object, array $attributes)
{
// get get allowed routes from current logged in user
$userRoutes = $token->getUser()->getRoutes();
// implement as you want the checks and return the related voter constant as below
if (...) {# your implementation
return VoterInterface::ACCESS_DENIED;
}
return VoterInterface::ACCESS_ABSTAIN;
}
}
Register the Voter:
<service id="security.access.route_voter"
class="AppBundle\Security\Authorization\Voter\RouteVoter" public="false">
<argument type="collection">
<argument>route_one</argument>
<argument>route_two</argument>
</argument>
<tag name="security.voter" />
Now change the access decision strategy accordingly with the docs.
Can this fit your needs?
Agree with previous answers that ACLs, Voters or some sort or role-based solution is definitely the best practice approach, but for this fringe case, would suggest extending your FOSUser to add a 'slug' field, and then check on that:
if('accessible_slug' !== $this->get('security.context')->getToken()->getUser()->getSlug()) {
throw new AccessDeniedException()
}
This means setting up a slug for groups of controllers/actions, and setting them to the user. Similar to roles, but without quite as much overhead. Still prefer Voters and some sort of role hierarchy, but hope this helps.
You can use AccessDeniedException of Symfony2
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
Then check logged in User By,
if (false === $this->get('security.authorization_checker')->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedException();
} else {
Continue;
}
In app/config/security.yml there is a section access_control:. There you can define access restrictions for specific paths, eg.
- { path: ^/faq/admin, roles: ROLE_FAQ_ADMIN }
path argument is a regex so the above entry will restrict access to any path starting with /faq/admin, eg. /faq/admin/show-something, /faq/admin/show-something-else etc. Only users with specified role will have access to those paths. For other users AccessDeniedException will be thrown with HTTP code 403.
There is no need to change code in actions inside controllers.

Sessions vs Configure at CakePHP

I saw some codes on internet which in order to check the permissions to access a concrete action, they use the Configure::read function in this way:
public function action1(){
if(!Configure::read('isAdmin')){
$this->redirect(array('controller' => 'depots', 'action' => 'status'));
}
//whatever
}
I was wondering, which is the difference between using Configure::read and Configure:write for this purpose and using $this->Session->read() and $this->Session->write()?
Which is a better way to check it?
Thanks.
Using the AuthComponent
If you make use of the built-in AuthComponent, CakePHP will store details of the currently logged-in user inside the session.
Getting properties of the currently logged-in User
Once logged in, you can access the information of the Used (e.g. role_id) via the AuthComponent. This can be done anywhere (also inside your Views or Models if desired);
For example;
if (123 === AuthComponent::user('role_id')) {
debug('hello admin user');
}
Or, inside a Controller:
if (123 === $this->Auth->user('role_id')) {
debug('hello admin user');
}
Accessing the logged in user
However, to dont have to repeat the group-id everywhere, it's best to creat a method for this (e.g inside your AppController);
/**
* Checks if the currently logged in user is an admin
*
* #return bool true if the current user is an admin
*/
protected function isAdmin()
{
// probably best to make the id configurable (Configure::write())?
return (123 === $this->Auth->user('role_id'));
}
Access control
To use a 'simple' authorisation, you can create your own isAuthorized() action in your Controller, which will allow you to block access to specific actions, based on the properties of the currently logged-in user;
Using ControllerAuthorize
I can't see why you would put the user role in the Configure array, as it is intended to contain application wide settings.
Personaly I have a table in my database that contain the roles. Although some roles may be added to it, there are some that I never modify (typically the administrator role).
This allows me to store its value as an application parameter in Configure and check for it later:
bootstrap.php
Configure :: write('administrator.role_id', 1);
TestController:
if($this->Auth->user('role_id') == Configure :: read('administrator.role_id'))
{
//do things specific to admin role
}
That said if the user role is stored dynamically in one way or another in Configure, it could probably work as well, but that's probably not the more elegant solution.

Multiple Instances (2) of Zend_Auth

I have a CMS built on the Zend Framework. It uses Zend_Auth for "CMS User" authentication. CMS users have roles and permissions that are enforced with Zend_Acl. I am now trying to create "Site Users" for things like an online store. For simplicity sake I would like to use a separate instance of Zend_Auth for site users. Zend_Auth is written as a singleton, so I'm not sure how to accomplish this.
Reasons I don't want to accomplish this by roles:
Pollution of the CMS Users with Site Users (visitors)
A Site User could accidentally get elevated permissions
The users are more accurately defined as different types than different roles
The two user types are stored in separate databases/tables
One user of each type could be signed in simultaneously
Different types of information are needed for the two user types
Refactoring that would need to take place on existing code
In that case, you want to create your own 'Auth' class to extend and remove the 'singleton' design pattern that exists in Zend_Auth
This is by no means complete, but you can create an instance and pass it a 'namespace'. The rest of Zend_Auth's public methods should be fine for you.
<?php
class My_Auth extends Zend_Auth
{
public function __construct($namespace) {
$this->setStorage(new Zend_Auth_Storage_Session($namespace));
// do other stuff
}
static function getInstance() {
throw new Zend_Auth_Exception('I do not support getInstance');
}
}
Then where you want to use it, $auth = new My_Auth('CMSUser'); or $auth = new My_Auth('SiteUser');
class App_Auth
{
const DEFAULT_NS = 'default';
protected static $instance = array();
protected function __clone(){}
protected function __construct() {}
static function getInstance($namespace = self::DEFAULT_NS) {
if(!isset(self::$instance[$namespace]) || is_null(self::$instance[$namespace])) {
self::$instance[$namespace] = Zend_Auth::getInstance();
self::$instance[$namespace]->setStorage(new Zend_Auth_Storage_Session($namespace));
}
return self::$instance[$namespace];
}
}
Try this one , just will need to use App_Auth instead of Zend_Auth everywhere, or App_auth on admin's area, Zend_Auth on front
that is my suggestion :
i think you are in case that you should calculate ACL , recourses , roles dynamically ,
example {md5(siteuser or cmsuser + module + controller)= random number for each roles }
and a simple plugin would this role is allowed to this recourse
or you can build like unix permission style but i guess this idea need alot of testing
one day i will build one like it in ZF :)
i hope my idea helps you
You're mixing problems. (not that I didn't when I first faced id)
Zend_Auth answers the question "is that user who he claims to be"? What you can do is to add some more info to your persistence object. Easiest option is to add one more column into your DB and add it to result.

Categories