CakePHP ACL Individual Object Access Control [duplicate] - php

Currently I am writing an application where I have multiple users. They have data that should only be visible to them and not the other authenticated users in the system. I also have administrators who manage the system and have access to all of the information. What is the best way to limit users to their data without limiting admin users?
Currently I am using a callback to limit the queries by user, but the admin will get the same limits. So I need to know a better way to do it. More importantly, the right way to do it.
For example, I want the standard user to be able to see their user information only and be limited to CRUD operations on their information only. The admin, however, should be able to see ALL users and CRUD ALL user data. Any ideas?

You need:
Information about the current user
Information about the item in question
You combine them with something like this (simple example):
$user = $this->Auth->user();
$book = $this->Book->find(…);
if ($user['type'] != 'admin' && $user['id'] != $book['Book']['creator_id']) {
$this->Session->setFlash("You're not allowed to view this item");
$this->redirect('somewhere');
}
You could make a method in your model like
function userCanAccessItem($item, $user)
to centralize the logic for the access check and call it from your controller.
Better yet, if you're using Cake's admin routing, you can omit all checking in the admin_ actions and only apply normal user access privilege checking in the user accessible actions.
You may also want to look at ACLs for more fine-grained access control.

You can get the current user info this way: $this->Auth->user(). You can use the user group id in your callback to limit the query. Also take a loot at WhoDidIt Behavior.

For any one else who comes here this is how I set it up.
First I set up a basic Role based ACL
Then I deny access to reports/all for normal users
$config['rules']['deny'][reports/all'] = 'Role/default' ;
Then in the model that I wanted to protect I added this:
public function beforeFind($queryData){
//TODO:make this cleaner
App::uses('ComponentCollection', 'Controller');
App::uses('AclComponent', 'Controller/Component');
$collection = new ComponentCollection();
$this->Acl = new AclComponent($collection);
if(!$this->Acl->check(array('User'=>AuthComponent::user()),'/reports/all/')){ // must be a user (not admin)
$this->bindModel(array('hasOne' => array('ReportsUser')));
$queryData['conditions']['ReportsUser.user_id'] = AuthComponent::user('id');
$queryData['recursive'] = 2;
}
return $queryData;
}
On the cases where ACL doesn't allow access to reports/all we add a condition to any find queries so it only shows reports with the correct user_id.

Related

Laravel policies: How to make a "on behalf of" behaivour

This is maybe a question on how Laravel works internally.
I'm writting an app. Only a logged user can create certain kind of records, that's easy, you just add $this->middleware('auth') to the controller, and that's it.
Now I want something more complex, the users with the role admin can create/edit that kind of records on behalf of some user. Imagine something like StackOverflow where a user can edit the question another user made, but for creation. That's it, an admin can create a post on behalf of the user():
So I have my create() in my PronController, it is something like:
function create($sid, $uid=NULL) {
// $sid is section id, where the post is going to be created... don't mind...
// if $uid (user id) is null, it will take the user from Auth::user()->id
$user = empty($uid) ? Auth::user() : User::findOrFail($uid);
// I want that only "admin" can use this $uid parameter, so I plan to use
// a Policy:
$this->authorize('create', $user);
}
The policy in PronPolicy is quite simple:
function create(User $authUser, User $user) {
return ($authUser->id === $user->id) || $authUser->isAdmin;
}
Now, I thought this should work, but it doesn't. It never reaches this edit() (I placed Log's)
So what I did is to change the $this->authorize() line to:
$this->authorize('createpron', $user);
And change the UserPolicy() (The UserPolicy!!!) to:
function createpron(User $authUser, User $user) {
return ($authUser->id === $user->id) || $authUser->isAdmin;
}
Now this works as I wanted. But I don't know why. Looks like Laravel searches for the object type in the parameter and then it activates the policy for that parameter, is it correct?
I don't know, although my code is working, it seems to me a bit dirty since the create "Pron" should be a policy of Pron, not user. Am I doing something conceptually wrong? what would be the right way to implement this?
Looks like Laravel searches for the object type in the parameter and then it activates the policy for that parameter, is it correct?
Correct! The docs mention this:
Policies are classes that organize authorization logic around a particular model or resource. For example, if your application is a blog, you may have a Post model and a corresponding PostPolicy to authorize user actions such as creating or updating posts.
By passing in the $user argument to $this->authorize(), you're asking if the current user can take an action against that particular record.
What you're doing isn't conceptually wrong (it is working), it just mixes a couple different authorizations together and as such feels kind of disorganized or unclear. Here's how I'd improve things:
Start by separating authorizations. You really have two separate but related permission checks happening:
Can the current User act on behalf of another User?
Can the end user (current or on-behalf-of) create a Pron?
#1 can be enacted as either a Gate or Policy, depending on if you want to pass in the on-behalf-of User for part of the check. That would be useful if, say, you can only act on behalf of Users within your own organization. The UserPolicy would be a good place for it.
#2 would be implemented as if you didn't have any on-behalf functionality. So maybe it's just return true because anyone can create them, or whatever your app's needs require for the ability to create a Pron.
Then, enact them separately.
function create($sid, $uid = null)
{
$user = Auth::user();
if (!empty($uid)) {
$user = User::findOrFail($uid);
$this->authorize('on-behalf-of', $user);
}
$this->authorizeForUser($user, 'create', new Pron());
// Continue your create logic...
}
Some benefits this provides:
Controller reads a little more explicitly for what authorization actions are happening, and how they're related
Gates and policies don't have to rely as much on mixing record types, and can strictly compare a permission to the specified user without arguments
Tighter control on permissions (e.g. if User X can't create posts, User Y acting on their behalf still can't create posts)
Possible down sides:
Opposite of tighter controls above: if you want to combine permission checks in order to modify them, this doesn't exactly solve that. For example, User X cannot create posts, but if an Admin is acting on their behalf then they CAN, the above doesn't exactly solve that

Laravel enable route for only the current user

I have two user types admin and member. Now, members cannot view other profiles except their own. Only admins have access to all user profiles.
Now my route for the UsersController#show (only accessible by admins) is http://laravel.test/user/username
However if a user accesses that url with his own username, it should grant the request.
This applies to all other functions such as edit and update.
Now I could create another controller designated for the current user only, call it MyController. I could copy the code from the UsersController, just changing a few codes so that it gets the current user from auth(). But I would prefer not to.
Any help?
if you have your own conditions you should a little change this but logic is this
public function show($username)
{
$user = User::where('username',$username)->get();
if($user->id == Auth::id()){
// show profile
}
}

User specific content in Symfony

I am currently working on a webapplication in Symfony. The application involves many clients which all have their own account to login with. For the user management I use Friends of Symfony User Bundle which I'm more than happy with.
In the application an Admin needs to see the information belonging to all the clients, where a client needs to get restricted to only be able to see his own information.
At this point FOS assigns the role "User" to all the accounts I register. I have considered to only give the client access to information belonging to him by using a query like;
$query = $this->getEntityManager()
->createQuery(
'SELECT c FROM clientsBundle:client c
WHERE c.name LIKE :string'
)->setParameter('string', '%'.$string.'%');
Then give var clientID the value of the clients id, and then get all the information needed according to that value (id).
But to be honest I don't really think this is the best practice.
I was wondering if there is maybe an (easier) option to not only allow the user to access certain pages through security.yml in Symfony, but also make the content in that page user specific.
Any help would be much appreciated.
we're currently working on a large web app that holds accounting information for users. So a similar thing where you only want your user to have access to their own data.
we simply write a number of verifyAccessTo methods that check that the entity that theyre trying to access belongs to their organisation (in our case a school) by assigning a relationship to that entity.
It might look something like this.
public function verifyAccessToTag( School $school, Tag $entry ) {
return $entry->getSchool()->getId() == $school->getId();
}
then when we're listing out data, we'll query specifically for entities relating to that school.
If you have specific area of a site that need particular access, such as admin areas, then in the firewall is the place to do it (security.yml).
If you need to sift and restrict on data, then the way you query and validate access on the specific data is probably the only way.
The whole world of security is a massive one, but heres some resources that might help.
securing services <-- this is really useful for ranked systems
deny access to content
voters
Hope that helps.
You can get the user's id and use that:
$user = $this->container->get('security.context')->getToken()->getUser();
$user->getId();

Multiple authentication systems

I've 2 tables, Users and Customers and 2 different actions for them. so I need 2 authentication system. Users login with username and password, and customers login with and id and password.
I know cake default AuthComponent can handle just 1 model, User model. because 'userModel' can be string not array (isn't it?).
How can I use it with 2 models (and tables) with 2 login pages and 2 .... .
(note: I can't combine the 2 tables. they have different schemas)
Instead of trying to maintain 2 different Auth systems, I highly recommend you look into ACLs and AROs. This will allow you to associate users with different access groups - in your case you could have groups like 'Internal'1 and 'Customers', and each new user account is a member of one group or the other.
You can then grant permissions at a group level. Customer users have access to their content, Internal users have access to different content.
There is a good tutorial in the new CakePHP book: Simple Acl controlled Application
1 I assume when you refer to 'users' generically you mean internal users, but feel free to adapt the terminology and group names to your particular situation.
I would presume 2 separate logins areas or a radio button to suggest which login they are attempting.
Use that input as which table to auth against then your code should follow the same path.
if (customer) {
do customer stuff;
} elseif (user) {
do user stuff;
} else {
you didn't login;
}
When you create the registration forms for them try to get their data into different databases. So that you can treat them differently. For this purpose check the submit button ids and direct the data to appropriate tables. When inserting put id's as auto increments.
I would advise against discriminating users from customers in this situation. These issues can be resolved using roles. Before jumping to design, please read about Authentication, Authorization and Audit and Role-Based Access Control.
From my understanding, this is an overview of the authentication process (note that I am not a security expert):
The identification phase is concerned with obtaining user credentials. A simple form that retreives user's name and password would suffice.
The authentication phase represents the process that maps user credentials to an user. Basically, it identifies a identity provider and uses it to obtain an user id for the user credentials provided.
Viewed like services, they look something like this:
// You can have many IdentityProviders. This mecanism allows you to extend your
// authentication system so you can use any mechanism (WebDav, Kerberos, etc).
IIdentityProvider
{
// Returns a pozitive id if the user is found and the credentials are valid
// or zero if user credentials are invalid (or negative numbers that represent
// error codes).
UserID GetUser(UserCredentials)
}
IAuthenticationService
{
Session SignIn(UserCredentials)
void SignOut(Session)
}
DefaultIdentityProvider : IIdentityProvider
{
// Search the user in your database.
UserID GetUser(UserCredentials credentials)
}
AuthenticationService : IAuthenticationService
{
IIdentityProvider[] identityProviders
Session SignIn(UserCredentials credentials)
{
IIdentityProvider provider = identityProviders[credentials.Type]
Session session = null
if (provider)
{
UserID userID = provider.GetUser(credentials)
if (userID > 0)
{
session = new Session
session.UserID = userID
}
}
return session
}
void SignOut(Session session)
{
delete session
}
}
The autorization system says what can a user do with a resource. Resources can be any entity that your application manages. They have a type and an ID. Optionally, they can be part of one or more categories. Users can execute certain operations on a resource, depending on its type and categories. This is defined by a permission. Permissions are grouped in roles. You can assign zero or more roles to an user.
In your example, a customer is a role. A resource is, for example, a product. A product is represented by the Product type, it has an ID and can have some categories associated ("Electronics" and "Dangerous"). Operations can be viewed as variations of Create/Read/Update/Delete verbs. Now, the Customer role would contain a set of permissions that explicitely states what a user having this role can do with the managed resources. For example, a Customer can only Read certain informations about the product, but cannot Create, Update or Delete a product. Note that if a user has more than one role associated, it gains all the permissions from that roles (an union operation, not an intersection).
This only scratches the surface. For further reading, you can find on the Internet more articles that explain these concepts alot better. This is meant to point you in the right direction.

Creating a User Login System: Put logic in Code or Database

I am trying to create a login system thats generic so that it can be adapted for use in various apps. I decided that 2 main "parts" of the system will be User Meta Data & Roles/Resources/ACL.
1. Metadata
I thought of keeping most data like what meta data are available for users in the database, so that admins can manage them using some GUI.
Problem is how can I then configue how I want inputs to render (textbox, checkbox, radios etc.). Then another problem is validation, filters.
2. ACL
I think for simple ACL it will work fine. But suppose I want say users to be able to modify posts they own. In Zend_ACL that is accomplished with Assertions. I thought that will make a "simple" login system overlly complex? Also it will be hard to build I suppose?
Currently I have my database like
Logging in users: I recommend using a separate controller (call it Auth for instance) that has loginAction and logoutAction. Zend_Auth (Zend_Auth using database) will check the database for the right credentials. Once the user is verified, you will save it in the global accessible place(the Zend_Auth class has methods to do this). This is also a good moment to query which roles the user has and store them.
Metadata part of your application: I'm not sure what the question is exactly but I assume you want to store dynamic information about user and have a GUI for admins to manage this. Why you would render different types of controls? Validating the information can be done by defining a lot of the most common metadata (like Twitter) and create rules for them. In the save action for the metadata you would validate using these rules.
ACL: Resources rarely change, you are better off putting them in a configuration file (for speed). You should give a lot thought to resources: what are they exactly to you? Controllers? Modules? Create a plugin that will preDispatch every request checking the role of the logged in user against the requested resource. E.g.:
$action = $request->getActionName();
$controller = $request->getControllerName();
// role, resource, privilage
if (!$acl->isAllowed($user->role, $controller, $action) {
//go to access denied page!
}
Now that Zend_ACL is used for the global access rules, you are better off checking for specific access inside the action itself (like if ($loggedInUser == $article->author) {//edit the article};).
Also don't forget Zend_ACL can be integrated with Zend_Navigation to hide menu items users are not allowed to use (and more).

Categories