Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 133 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,24 @@ class User extends Eloquent /* or ConfideUser 'wink' */{
This will do the trick to enable the relation with `Role` and the following methods `roles`, `hasRole( $name )`,
`can( $permission )`, and `ability($roles, $permissions, $options)` within your `User` model.

> This fork of the Entrust package extends the above behavior with the ability to achieve polymorphic roles and permissions on other Models. You may now add an optional Model class and instance id to the above commands to restrict the scope of the role/permission. For example, `hasRole( $name, $modelName, $modelId )`,
`can( $permission, $modelName, $modelId )` will check for a role or permission for a specific Model. See the Usage section below for further explaination.

The above discussion of roles and permissions have been restricted to users in the global scope. Often times, it is desirable to apply permissions on a specific resource within your application. This is now possibly by adding the `HasModelRole` trait to a Model. For example, consider the follow example of a Trip:

```php
<?php

use Zizaco\Entrust\HasModelRole;

class Trip extends Eloquent {
use HasModelRole; // Add this trait to the models you want to protect with roles and permissions

...
```

This will add the following methods `hasRole( $name )`, `can( $permission )`, and `ability($roles, $permissions, $options)` within your `Trip` model. These methods will work identicaly as before, but apply specifically to the model instance thus giving you fine-grain control over which resources you keep private and which you Entrust to others.

Don't forget to dump composer autoload

$ composer dump-autoload
Expand All @@ -141,7 +159,7 @@ $admin->save();

```

Next, with both roles created let's assign then to the users. Thanks to the `HasRole` trait this is as easy as:
Next, with these roles created let's assign then to the users. Thanks to the `HasRole` trait this is as easy as:

```php
$user = User::where('username','=','Zizaco')->first();
Expand All @@ -160,6 +178,11 @@ $managePosts->name = 'manage_posts';
$managePosts->display_name = 'Manage Posts';
$managePosts->save();

$viewPosts = new Permission;
$viewPosts->name = 'view_posts';
$viewPosts->display_name = 'View Posts';
$managePosts->save();

$manageUsers = new Permission;
$manageUsers->name = 'manage_users';
$manageUsers->display_name = 'Manage Users';
Expand All @@ -180,12 +203,74 @@ $user->can("manage_users"); // false

You can have as many `Role`s as you want for each `User` and vice versa.

While this is fine for many situations, there are times when you don't want to give global permissions to a user to, for example, `manage_posts`. This fork of Entrust allows us to add fine-grained access control on specific Models and Model instances. To see what we mean, let's look at our Trip Model.

Recently our example users has started to travel a bit. As a result, they are now going to start contributing to travel reviews. Since they are new to the travel review genre, they will collaborate with another author on all their content. Until they have a little experience under their belt, they should not be able to edit any Trip reviews they have not been assigned. This kind of behavior is what this fork of Entrust enables.

Let's create a new role to keep things clean.

```php

$editor = new Role;
$editor->name = 'Trip Editor';
$editor->save();
```

Now we can add the users as follows.

```php
$user = User::where('username','=','Zizaco')->first();

/* role attach alias */
$user->attachRole( $editor, 'Trip', 1 ); // Any model class can be specified. Model id should be an integer value

/* OR the eloquent's original: */
$user->roles()->attach( $admin->id, array( 'model_name' => 'Trip', 'model_id' => 1 ) ); // id and relation fields
```

Now we just need to add permissions to the `Trip Editor` Role.

```php
$manageTrips = new Permission;
$manageTrips->name = 'manage_trips';
$manageTrips->display_name = 'Manage Trips';
$manageTrips->save();

$editor->perms()->sync(array($manageTrips->id));
```

Now we can check for roles and permissions simply by doing:

```php
$trip1 = Trip::find(1);
$trip2 = Trip::find(2);

// check for Role and Permission for authenticated user on a Trip instance
$trip1->hasRole("Trip Editor"); // true
$trip2->hasRole("Trip Editor"); // false
$trip1->can("manage_trips"); // true
$trip2->can("manage_trips"); // false

// check Role for a specific user
$user->hasRole("Trip Editor"); // false - role was not given globally
$user->hasRole("Trip Editor", 'Trips'); // false - role was not given at a Model level
$user->hasRole("Trip Editor", 'Trips', 1); // true - role was given just for this Model instance
$user->hasRole("Trip Editor", 'Trips', 2); // false - role was not given for this Model instance

// check Permission for a specific user
$user->can("manage_trips"); // false - role with this permission was not given globally
$user->can("manage_trips", 'Trips'); // false - role with this permission was not given at a Model level
$user->can("manage_trips", 'Trips', 1); // true - role with this permission was given just for this Model instance
$user->can("manage_trips", 'Trips', 2); // false - role with this permission was not given for this Model instance

```

More advanced checking can be done using the awesome `ability` function. It takes in three parameters (roles, permissions, options).
`roles` is a set of roles to check. `permissions` is a set of permissions to check.
Either of the roles or permissions variable can be a comma separated string or array.

```php
$user->ability(array('Admin','Owner'), array('manage_posts','manage_users'));
$user->ability(array('Admin','Owner'), array('manage_posts','manage_users');
//or
$user->ability('Admin,Owner', 'manage_posts,manage_users');

Expand All @@ -198,13 +283,19 @@ The third parameter is an options array.
```php
$options = array(
'validate_all' => true | false (Default: false),
'return_type' => boolean | array | both (Default: boolean)
'return_type' => boolean | array | both (Default: boolean),
'model_name' => string,
'model_id' => int
);
```
`validate_all` is a boolean flag to set whether to check all the values for true, or to return true if at least one role or permission is matched.

`return_type` specifies whether to return a boolean, array of checked values, or both in an array.

`model_name` is the name of the Model class to which you wish to grant a role.

`model_id` is an instance id of a Model object.

Here's some example output.

```php
Expand Down Expand Up @@ -247,6 +338,15 @@ Entrust::routeNeedsRole( 'admin/advanced*', 'Owner' );
Entrust::routeNeedsPermission( 'admin/post*', array('manage_posts','manage_comments') );

Entrust::routeNeedsRole( 'admin/advanced*', array('Owner','Writer') );

// Only users with roles that have the 'manage_trips' permission for the
// Trip Model will be able to access routes starting with 'admin/trips'.
Entrust::routeNeedsPermission( 'admin/trips*', 'manage_trips:Trip:{tripId}' );

// Only users with roles that have a global 'Owner' role or the 'Trip Editor' role
// for the Trip Model will be able to access routes starting with 'admin/trips'.
Entrust::routeNeedsRole( 'admin/trips*', array('Owner','Trip Editor:Trip') );

```

Both of these methods accept a third parameter. If the third parameter is null then the return of a prohibited access will be `App::abort(403)`. Otherwise the third parameter will be returned. So you can use it like:
Expand Down Expand Up @@ -275,7 +375,7 @@ Entrust::routeNeedsRoleOrPermission( 'admin/advanced*', array('Owner','Writer'),

Entrust roles/permissions can be used in filters by simply using the `can` and `hasRole` methods from within the Facade.

```php
```php
Route::filter('manage_posts', function()
{
if (! Entrust::can('manage_posts') ) // Checks the current user
Expand All @@ -287,11 +387,25 @@ Route::filter('manage_posts', function()
// Only users with roles that have the 'manage_posts' permission will
// be able to access any admin/post route.
Route::when('admin/post*', 'manage_posts');
```

```php
Route::filter('manage_trips', function($tripId)
{
if (! Entrust::can('manage_trips', 'Trip', $tripId) ) // Checks the current user
{
return Redirect::to('admin');
}
});

// Only users with roles that have the 'manage_trips' permission will
// be able to access any admin/post route.
Route::when('admin/trips/{tripId}/*', 'manage_trips');
```

Using a filter to check for a role:

```php
```php
Route::filter('owner_role', function()
{
if (! Entrust::hasRole('Owner') ) // Checks the current user
Expand All @@ -304,8 +418,22 @@ Route::filter('owner_role', function()
Route::when('admin/advanced*', 'owner_role');
```

```php
Route::filter('trip_editor_role', function($tripId)
{
if (! Entrust::hasRole('Trip Editor', 'Trip', $tripId) ) // Checks the current user
{
App::abort(404);
}
});

// Only trip editors with permission to a Trip will have access to routes within admin/advanced
Route::when('admin/trips/{tripId}/*', 'trip_editor_role');
```

As you can see `Entrust::hasRole()` and `Entrust::can()` checks if the user is logged, and then if he has the role or permission. If the user is not logged the return will also be `false`.


## Troubleshooting

If you encounter an error when doing the migration that looks like:
Expand All @@ -322,23 +450,7 @@ EntrustRole->name has a length limitation set within the rules variable of the [

You can adjust it by changing your Role Model.

```php
<?php

use Zizaco\Entrust\EntrustRole;

class Role extends EntrustRole
{
/**
* Ardent validation rules
*
* @var array
*/
public static $rules = array(
'name' => 'required|between:4,255'
);
}
```

## License

Expand Down
6 changes: 2 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@
}
],
"require": {
"php": ">=5.4.0",
"illuminate/support": "~4.2"
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0",
"mockery/mockery": "~0.9",
"illuminate/database": "~4.0",
"symfony/process": "~2.3"
"orchestra/testbench": "2.2.*"
},
"suggest": {
"zizaco/confide":"Confide is an authentication solution for Laravel 4 that couples very well with Entrust"
Expand Down
48 changes: 34 additions & 14 deletions src/Entrust/Entrust.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,35 +32,47 @@ public function __construct($app)
}

/**
* Checks if the current user has a Role by its name
* Checks if a user has a Role by its name. Can optionally specify a class
* name and id to check for a role on a given class or class instance
* respectively.
*
* @param string $name Role name.
* @param string $modelName the name of the resource model to which the check for roles and permissions will apply
* @param mixed $modelId the instance identifier of the resource model
*
* @return bool
*/
public function hasRole($permission)
public function hasRole($name, $modelName=false, $modelId=false)
{
if ($user = $this->user()) {
return $user->hasRole($permission);
$user = $this->user();

if (!empty($user)) {
return $user->hasRole($name, $modelName, $modelId);
}

return false;
}

/**
* Check if the current user has a permission by its name
* Check if the current user has a permission by its name. Can optionally
* specify a class name and id to check for a role on a given class or
* class instance respectively.
*
* @param string $permission Permission string.
* @param string $modelName the name of the resource model to which the check for roles and permissions will apply
* @param mixed $modelId the instance identifier of the resource model
*
* @return bool
*/
public function can($permission)
public function can($permission, $modelName=false, $modelId=false)
{
if ($user = $this->user()) {
return $user->can($permission);
}
$user = $this->user();

return false;
if ($user) {
return $user->can($permission, $modelName, $modelId);
}

return false;
}

/**
Expand Down Expand Up @@ -98,7 +110,9 @@ public function routeNeedsRole($route, $roles, $result = null, $cumulative = tru
$result = function () use ($roles, $result, $cumulative) {
$hasARole = array();
foreach ($roles as $role) {
if ($this->hasRole($role)) {
list($roleName, $modelName, $modelId) = explode(':', $role);

if ($this->hasRole($roleName, $modelName, $modelId)) {
$hasARole[] = true;
} else {
$hasARole[] = false;
Expand Down Expand Up @@ -151,7 +165,9 @@ public function routeNeedsPermission($route, $permissions, $result = null, $cumu
$result = function () use ($permissions, $result, $cumulative) {
$hasAPermission = array();
foreach ($permissions as $permission) {
if ($this->can($permission)) {
list($permissionName, $modelName, $modelId) = explode(':', $permission);

if ($this->can($permissionName, $modelName, $modelId)) {
$hasAPermission[] = true;
} else {
$hasAPermission[] = false;
Expand Down Expand Up @@ -208,7 +224,9 @@ public function routeNeedsRoleOrPermission($route, $roles, $permissions, $result
$result = function () use ($roles, $permissions, $result, $cumulative) {
$hasARole = array();
foreach ($roles as $role) {
if ($this->hasRole($role)) {
list($roleName, $modelName, $modelId) = explode(':', $role);

if ($this->hasRole($roleName, $modelName, $modelId)) {
$hasARole[] = true;
} else {
$hasARole[] = false;
Expand All @@ -217,7 +235,9 @@ public function routeNeedsRoleOrPermission($route, $roles, $permissions, $result

$hasAPermission = array();
foreach ($permissions as $permission) {
if ($this->can($permission)) {
list($permissionName, $modelName, $modelId) = explode(':', $permission);

if ($this->can($permissionName, $modelName, $modelId)) {
$hasAPermission[] = true;
} else {
$hasAPermission[] = false;
Expand Down
4 changes: 2 additions & 2 deletions src/Entrust/EntrustRole.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function __construct(array $attributes = array())
*/
public function users()
{
return $this->belongsToMany(Config::get('auth.model'), Config::get('entrust::assigned_roles_table'));
return $this->belongsToMany(Config::get('auth.model'), Config::get('entrust::assigned_roles_table'), 'role_id', 'user_id')->withPivot('model_name', 'model_id');
}

/**
Expand All @@ -54,7 +54,7 @@ public function perms()
// To maintain backwards compatibility we'll catch the exception if the Permission table doesn't exist.
// TODO remove in a future version.
try {
return $this->belongsToMany(Config::get('entrust::permission'), Config::get('entrust::permission_role_table'));
return $this->belongsToMany(Config::get('entrust::permission'), Config::get('entrust::permission_role_table'));
} catch (Exception $e) {
// do nothing
}
Expand Down
Loading