Multiple Instances (2) of Zend_Auth - php

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.

Related

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.

Symfony 2.6 - Security provider

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

Handling mysql queries in php mvc

I'm working on an application written in PHP. I decided to follow the MVC architecture.
However, as the code gets bigger and bigger, I realized that some code gets duplicated in some cases. Also, I'm still confused whether I should use static functions when quering the database or not.
Let's take an example on how I do it :
class User {
private id;
private name;
private age;
}
Now, inside this class I will write methods that operate on a single user instance (CRUD operations). On the other hand, I added general static functions to deal with multiple users like :
public static function getUsers()
The main problem that I'm facing is that I have to access fields through the results when I need to loop through users in my views. for example :
$users = User::getUsers();
// View
foreach($users as $user) {
echo $user['firstname'];
echo $user['lastname'];
}
I decided to do this because I didn't feel it's necessary to create a single user instance for all the users just to do some simple data processing like displaying their informations. But, what if I change the table fields names ? I have to go through all the code and change those fields, and this is what bothers me.
So my question is, how do you deal with database queries like that, and is it fine to use static functions when querying the database. And finally, where is it logical to store those "displaying" functions like the one I talked about ?
Your approach seems fine, howerver I would still use caching like memcached to cache values and then you can remove static.
public function getUsers() {
$users = $cacheObj->get('all_users');
if ($users === false) {
//use your query to grab users and set it to cache
$users = "FROM QUERY";
$cacheObj->set('all_users', $users);
}
return $users;
}
(M)odel (V)iew (C)ontroller is a great choice choice, but my advice is look at using a framework. The con is they can have a step learning curve, pro is it does a lot of heavy lifting. But if you want to proceed on your own fair play, it can be tough to do it yourself.
Location wise you have a choice because the model is not clearly define:
You'll hear the term "business logic" used, basically Model has everything baring views and the controllers. The controllers should be lean only moving data then returning it to the view.
You model houses DB interaction, data conversions, timezone changes, general day to day functions.
Moudle
/User
/Model
/DB or (Entities and Mapper)
/Utils
I use Zend and it uses table gateways for standard CRUD to avoid repetition.
Where you have the getUsers() method you just pass a array to it, and it becomes really reusable and you'd just have different arrays in various controller actions and it builds the queries for you from the array info.
Example:
$data = array ('id' => 26)
$userMapper->getUsers($data);
to get user 26
enter code here
$data = array ('active' => 1, 'name' => 'Darren')
$userMapper->getUsers($data);`
to get active users named Darren
I hope this help.

Dynamic custom ACL in zend framework?

I need a solution where authenticated users are allowed access to certain Controllers/Actions based not on their user type :ie. admin or normal user (although I may add this using standard ACL later) but according to the current status of their user.
For example :
Have they been a member of the site for more than 1 week?
Have they filled in their profile fully?
Actually, now that I think about it, kind of like they have on this site with their priviledges and badges.
For dynamic condition-based tests like you are describing, you can use dynamic assertions in your Zend_Acl rules.
For example:
class My_Acl_IsProfileComplete implements Zend_Acl_Assert_Interface
{
protected $user;
public function __construct($user)
{
$this->user = $user;
}
public function assert(Zend_Acl $acl,
Zend_Acl_Role_Interface $role = null,
Zend_Acl_Resource_Interface $resource = null,
$privilege = null)
{
// check the user's profile
if (null === $this->user){
return false;
}
return $this->user->isProfileComplete(); // for example
}
}
Then when defining your Acl object:
$user = Zend_Auth::getInstance()->getIdentity();
$assertion = new My_Acl_Assertion_IsProfileComplete($user);
$acl->allow($role, $resource, $privilege, $assertion);
Of course, some of the details depend upon the specifics of what you need to check and what you can use in your depend upon what you store in your Zend_Auth::setIdentity() call - only a user Id, a full user object, etc. And the roles, resources, and privileges are completely app-specific. But hopefully this gives the idea.
Also, since the assertion object requires a user object at instantiation, this dynamic rule cannot be added at Bootstrap. But, you can create the core Acl instance with static rules during bootstrap and then register a front controller plugin (to run at preDispatch(), say) that adds the dynamic assertion. This way, the Acl is fully populated by the time you get to your controllers where presumably you would be checking them.
Just thinking out loud.

Assign multiple roles in Zend_Navigation using Zend_ACL in Zend Framework PHP?

I can't get my Zend_Navigation to work properly,
When logging in user with AUth/Doctrine, I am pulling out the roles assigned to the user (usually it's a few of them) from a Many-to-many table,
Then in the bootstrap.php on line:
$view->navigation($navContainer)->setAcl($this->_acl)->setRole($this->_role);
I get error:
'$role must be a string, null, or an instance of Zend_Acl_Role_Interface; array given'
However if I loop through the roles with foreach - the previous roles are being overwritten by the following ones and I get the nav only for last role,
Does anyone have any logical solution for this ?
Really appreciate,
Adam
I had the same problem but approached the solution from a slightly different angle. Instead of modifying the Zend_Navigation object to accept two or more roles, I extended Zend_Acl and modified the isAllowed() method to check against all those roles. The Zend_Navigation objects use the isAllowed() method, so overriding this solved the issue.
My_Acl.php
<pre><code>
class My_Acl extends Zend_Acl
{
public function isAllowed($role = null, $resource = null, $privilege = null)
{
// Get all the roles to check against
$userRoles = Zend_Registry::get('aclUserRoles');
$isAllowed = false;
// Loop through them one by one and check if they're allowed
foreach ($userRoles as $role)
{
// Using the actual ACL isAllowed method here
if (parent::isAllowed($role->code, $resource))
{
$isAllowed = true;
}
}
return $isAllowed;
}
}
</code></pre>
Then, instead of creating an instance of Zend_Acl, use My_Acl, pass that to your navigation object and it should work.
You should really never, ever override isAllowed(), and yes there is a solution. Create a class that implements Zend_Acl_Role_Interface and if memory serves it requires defining a single method getRole(), this could, in fact, be your model that you use to authenticate a user against and allow that class to handle determining the role. A user should only have a single role. If access to the resource should be granted to users of multiple roles but only under certain conditions, then you should use an assertion, thats why they are there.

Categories