I use Entrust before to control ACL in Laravel when my project is still Laravel 4, and now with Laravel 5.2 Entrust no longer work, especialy in route filtering.
And then I find this package and trying to use it, but still got a lot of question, so to make it more simple I will explain my use case when I use entrust:
First I want to make a permission for create, view, update and delete for article, in Entrust I will create permission like create_article, view_article, update_article and delete_article.
But now in Kodeine when I create permission there is "slug" so I tried to do this like in documentation say
$permUser = $permission->create([
'name' => 'article',
'slug' => [ // pass an array of permissions.
'create' => true,
'view' => true,
'update' => true,
'delete' => true
],
'description' => 'Manange article'
]);
So from what I read it will be just grouping all of my article permission into one place and there is slug with each parameters view, create, update, delete.
The problem I see is, if I want to make my users to only can view article, how to do that based on permission that I created up there?
Since from documentation the to assignPermission is only give permission name and that mean it will include all slug in there and it will be all true?
So if I want to make users only can view article I need to create something like
$permUser = $permission->create([
'name' => 'article_view',
'slug' => [ // pass an array of permissions.
'view' => true,
],
'description' => 'view article'
]);
And if I want to make users only can create article then I will mean I need to create
$permUser = $permission->create([
'name' => 'article_create',
'slug' => [ // pass an array of permissions.
'create' => true,
],
'description' => 'create article'
]);
then what's the point of slug - is it just pretty much the same like role but with parameter in slug?
As I wrote in your github issue, I suggest you to keep all your article permissions as you alredy have it, in one big group but all of them set to false (keep in mind that you may need to change your 'most_permissive_wins' variable in your acl config file). You can create a "child" group permission for your users role using Inheritance, setting to true all of those permissions your users need. You can then asign that child group to your users role (not the big one) and the user role tou your specific user. To clarify my answer, lets say you have this group:
$permArticles = $permission->create([
'name' => 'articles',
'slug' => [ // pass an array of permissions.
'create' => false,
'view' => false,
'update' => false,
'delete' => false,
],
'description' => 'All articles module permissions'
]);
then you can create something like:
$articlesPermUser = Permission::create([
'name' => 'articles.user',
'slug' => [ // an array of permissions only for student
'view' => true,
],
// we use permission inheriting.
'inherit_id' => $permArticles->getKey(),
'description' => 'user articles permissions'
]);
then you assign your new permission to your user role (I am assuming you alredy have a role name 'user'):
$userRole = Role::where('slug', 'user')->first();
$userRole->assignPermission('articles.user');
And finally you assign that role to... let say your logged user:
Auth::user()->assignRole($userRole);
You can also solve this problem by overwriting the permission, this could be done assigning a specific permission value to a user (but yes, you would need to do this for every single user in your app if needed, so I dont like this solution at all).
Lets say we keep our big group:
$permArticles = $permission->create([
'name' => 'articles',
'slug' => [ // pass an array of permissions.
'create' => false,
'view' => false,
'update' => false,
'delete' => false,
],
'description' => 'All articles module permissions'
]);
As this group says, any rol with your article permission assgined will not be able to do anything in your articles module. Lets say your user role alredy has this permission, but you want a certain user (lets say the logged one) be able to update an article. You can set the specific update permission value to true like so:
Auth::user()->addPermission('update.articles', true);
//or
Auth::user()->addPermission('articles', [
'update' => true,
]);
Thank you for the answer but over the time, I already find a perfect solution that match what I need. It is not much different from what I do in entrust.
So first I will just create a permission like this for view article
$class = 'article';
$permission = new Kodeine\Acl\Models\Eloquent\Permission();
$permUser = $permission->create([
'name' => $class.'_view',
'slug' => [
'view' => true,
],
'description' => 'View '.$class
]);
and then another one for example create article
$class = 'article';
$permission = new Kodeine\Acl\Models\Eloquent\Permission();
$permUser = $permission->create([
'name' => $class.'_create',
'slug' => [
'create' => true,
],
'description' => 'Create '.$class
]);
and later just assign those permission to user role, for example I want to make this user role to be can view article
$roleAdmin = Kodeine\Acl\Models\Eloquent\Role::where('name','=','user_1');
$roleAdmin->assignPermission('article_view');
I still don't understand about Inheritance feature, and I needed to do this quickly. It maybe not an ideal way, but it's works for me.
Related
I've performed a long-overdue update on a Laravel project from v5.7 (with Spatie Permissions 2.21) to v9 with Spatie 5.5.0. I'm not getting any error but the hasRole() function no longer ever returns true for users who definitely have the role. Echoing Auth::user()->getRoleNames() for the user just returns an empty array. Any guidance would be greatly appreciated.
Looking at my old commits, it seems that aside from the composer.json additions and database migrations, that only things I needed to do were a User model edit:
...
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
use HasRoles;
...
And this config/permission.php (comments stripped):
<?php
return [
'models' => [
'permission' => Spatie\Permission\Models\Permission::class,
'role' => Spatie\Permission\Models\Role::class,
],
'table_names' => [
'roles' => 'roles',
'permissions' => 'permissions',
'model_has_permissions' => 'model_has_permissions',
'model_has_roles' => 'model_has_roles',
'role_has_permissions' => 'role_has_permissions',
],
'column_names' => [
'role_pivot_key' => null, //default 'role_id',
'permission_pivot_key' => null, //default 'permission_id',
'model_morph_key' => 'model_id',
'team_foreign_key' => 'team_id',
],
'register_permission_check_method' => true,
'teams' => false,
'display_permission_in_exception' => false,
'display_role_in_exception' => false,
'enable_wildcard_permission' => false,
'cache' => [
'expiration_time' => \DateInterval::createFromDateString('24 hours'),
'key' => 'spatie.permission.cache',
'store' => 'default',
],
];
Below few things i would like to try
$user->assignRole($this->roles)
Try echo below line just after assign
echo $user()->getRoleNames()
Also try fetch with relations
Dump($user->with('roles')->get()
It will tell you atleast roles assignment is working.
So it turns out this was connected to another problem I was having that fortunately I was trying to fix at the same time:
Laravel upgrade broke model paths
The cause of this permissions issue was in the database but not table / field names, but actually the field contents.
In the model_has_roles table, the old App\User namespace was used (hopefully I'm using "namespace" correctly there!) and should have been App\Models\User in line with the new namespace. Then it all worked fine.
In my new Laravel 5.8 app I read next article to use roles with jwt extention :
https://scotch.io/tutorials/role-based-authentication-in-laravel-with-jwt
and in seed I add several roles, like :
\DB::table('roles')->insert(array (
0 =>
array (
'id' => 1,
'name' => 'Admin',
'display_name' => 'Admin',
'description' => 'Administrator. Can do all operations in system',
'created_at' => '2019-04-29 11:03:50',
),
1 =>
array (
'id' => 2,
'name' => 'Manager',
'display_name' => 'Manager. Can do all operations in frontend and CRUD for Hostels/CMS items in Backend',
'description' => 'Manager description...',
'created_at' => '2019-04-29 11:03:50',
),
2 =>
array (
'id' => 3,
'name' => 'Customer',
'display_name' => 'Customer. Can do all operations in frontend',
'description' => 'Customer description...',
'created_at' => '2019-04-29 11:03:50',
),
));
and I see assignRole method, when I need to assign some role to user.
What is unclear for me what for Permissions and “permission_role” data.
Do I need them for my simple app structure? If yes, please provide some examples...
Thanks!
We can't answer your question if you don't specify what your application needs to do. You can have roles without permissions or permissions without roles depending on your application. But if you want the provided tutorial to work you need them.
Permissions are specific actions of a role.
Example Permisson table data:
Permission-id: 1
Permission-name: 'Create Blog Post'
You can assign this permission to a role 'Admin' (Role-id: 1).
Your permission_role table contains the relationship between those two. So for the above example:
permission_role
'permission_id': 1, 'role_id': 1;
Which means that The admin role can 'Create Blog Post' in this case.
In my CakePHP application, I have applied Url validations so that admin can access only those actions which are defined for admin and same as with users.
In my application, "surveylist" is the action of admin and when any user directly access that action(surveylist), URL validations work(Unauthorized access msg is displayed).
But below that message ctp file of surveylist executes forcefully and show errors because I have validated URL through the try-catch block and it cannot get the set variables of action.
I want that ctp file should not execute if unauthorize error comes.
My code for surveylist is:-
public function surveylist($pg=null){
try{
if($this->checkPageAccess($this->params['controller'] . '/' . $this->params['action'])){
$this->Paginator->settings = array(
'Survey' => array(
'limit' => 5,
'order' => 'created desc',
'conditions'=>array('is_deleted'=> 0),
'page' => $pg
)
);
$numbers = $this->Paginator->paginate('Survey');
$this->set(compact('numbers'));
}else{
$this->Flash->set(__('Unauthorised access'));
}
}catch(Exception $e){
$this->Flash->set(__($e->getMessage()));
}
}
I don't want the ctp file of surveylist to execute if control comes to else.
Plz, help me out......
Thanx in advance...
I suppose you are using prefix to separate admin and users, if not please do that it is great way to handle and restrict methods.
After doing that you have to make condition to check which prefix(admin, user) is currently active and according that load Auth component and allow action in allow() method of Auth.
Example:
$this->loadComponent('Auth',[
/*'authorize' => [
'Acl.Actions' => ['actionPath' => 'controllers/']
],*/
'loginRedirect' => [
'controller' => 'Users',
'action' => 'index'
],
'authenticate' => [
'Form' => [
'fields' => [
'username' => 'email',
'password' => 'password'
]
]
],
'loginAction' => [
'controller' => 'Users',
'action' => 'login'
],
'unauthorizedRedirect' => [
'controller' => 'Users',
'action' => 'login',
'prefix' => false
],
'authError' => 'You are not authorized to access that location.',
]);
if ($this->request->params['prefix']=='admin') {
// Put actions you want to access to admin in allow method's array
$this->Auth->allow(array('add', 'edit', etc...));
} else if ($this->request->params['prefix']=='user') {
// Put actions you want to access to user in allow method's array
$this->Auth->allow(array('login', 'view', etc...));
}
This way you can restrict actions for particular role.
Hope this helps!
i am using " romanbican - bicon roles ", i don't see sufficient information for the " Creating Roles ",
code is available but i don't know where i paste this code, please suggest quick steps to implement permissions.
I used this same laravel package and simply created a new controller and route pointing to the the following controller methods:
public function getRoleAdmin()
{
$adminRole = Role::create([
'name' => 'Admin',
'slug' => 'admin',
'description' => 'System Administrator', // optional
'level' => 1, // optional, set to 1 by default
]);
}
public function getRoleModerator()
{
$moderatorRole = Role::create([
'name' => 'Forum Moderator',
'slug' => 'forum.moderator',
'description' => 'Forum Moderator',
'level' => 1,
]);
}
I then created a simple view with a button for each calling the appropriate route/controller/method to create either a new moderator or administrator role. You will see that calling Role::create simply creates a new record in the roles table with these attributes which you could easily perform with a standard DB call to insert into the table. I used the same approach for creating/deleting permissions.
I have two cakephp2 applications running on same database, but having different Auth tables and different $this->Auth->userModel values accordingly.
Authentication works well and users from one app can't log into other.
BUT.. as apps uses same CAKEPHP session cookie, this happens:
when user from app 'one' logs in, it can access any Auth protected action in app 'two'!
I will probably use different user roles and cookie names.
But still, why Auth component is ignoring Auth->userModel settings when checking the session? Is there a way to configure it to work right in this situation?
Thanks in advance for any suggestions.
If not configured otherwise, AuthComponent will write the authenticated user record to the Auth.User session key in CakePHP 2. But it can be changed:
AuthComponent::sessionKey
The session key name where the record of the current user is stored. If unspecified, it will be "Auth.User".
(In CakePHP 1.3 this was different: Auth.{$userModel name})
So, if your apps share a Session, which they do, if cookie name and Security.salt match, the logged in record will be shared.
There are two possibilities to solve this:
Separate the logins
Simply set a different AuthComponent::sessionKey for your two models. This will allow them to keep the logged in user separately
Separate the sessions
Configure different Cookie names and Salts for both apps, so their sessions cannot override each other. This is probably the cleaner solution, because it also covers the risk of other session keys being double-used.
I have a similar issue which is why I've started a bounty on this question. Basically I have a public facing part of the application which lets users login from one table and an administrative part of the application which lets admins login using a different table. My AppController looks something like this:
public $components = array(
'Session',
'Auth' => array(
'autoRedirect' => false,
'authenticate' => array(
'Form' => array(
'userModel' => 'User'
)
),
'loginAction' => array('controller' => 'users', 'action' => 'login'),
'loginRedirect' => array('controller' => 'users', 'action' => 'overview'),
'logoutRedirect' => array('controller' => 'users', 'action' => 'loggedout')
)
);
and I have another AdminController where I have this:
public $components = array(
'Session',
'Auth' => array(
'authenticate' => array(
'CustomForm' => array(
'userModel' => 'Admin'
)
),
'loginAction' => array('controller' => 'admin', 'action' => 'login'),
'loginRedirect' => array('controller' => 'admin', 'action' => 'index'),
'logoutRedirect' => array('controller' => 'home', 'action' => 'index')
)
);
But as mentioned in this question, sessions from the two don't get along and overwrite each other. What's the best way to overcome this?
Extend the Model/Datasource/Session/DatabaseSession.php session handler with something like MyDatabaseSession and overwrite the write and read methods. Maybe simply copy the existing code of both methods and add something like
'app_id' => Configure::read('App.appId')
to the read() conditions and do the same in the write method. And do not forget to add the field to your session database schema and to configure the session to use your handler.
<?php
App::uses('DatabaseSession', 'Model/Datasource/Session');
class ExtendedDatabaseSession extends DatabaseSession {
public function read($id) {
$row = $this->_model->find('first', array(
'conditions' => array(
'app_id' => Configure::read('App.appId'),
$this->_model->primaryKey => $id)));
if (empty($row[$this->_model->alias]['data'])) {
return false;
}
return $row[$this->_model->alias]['data'];
}
public function write($id, $data) {
if (!$id) {
return false;
}
$expires = time() + $this->_timeout;
$record = compact('id', 'data', 'expires');
$record[$this->_model->primaryKey] = $id;
$record['app_id'] = Configure::read('App.appId');
return $this->_model->save($record);
}
}
I do not know your app, so were you write the app id to the config data is up to you, bootstrap or beforeFilter() maybe. You should add it before the session gets initialized I think or you'll need to re-init the session or something. I leave it up to you to look the right point up. :)