-
Notifications
You must be signed in to change notification settings - Fork 333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Get Model Where User Has PermissionX #622
Comments
Couple of relatively easy options, depends on how you're using this. If you're using a mix of roles and direct abilities, then use getAbilities() and filter on the model you're wanting to look at. Then add the role's abilities in. You can either trust it or filter it further through an each() loop. Or if you're using scopes then that's a little easier. Or if you're using the Owns approach then you should already have the approach for that. May be the easiest solution for you? |
I do something similar to this in a scope: // Example to filter entities I'm allowed to "view":
$user = Auth::user(); // Currently logged in user (could be any user with roles and permissions).
$abilities = $user->getAbilities() // Get all the abilities (including the ones inherited from roles).
->where('entity_type', get_class(Entity::class)) // Filter permissions that concern this model.
->where('name', 'view') // Filter specific permission.
->pluck('id'); // Get all ID's of the resulting entities.
Entity::whereIn('id', $abilities)->get(); // Expected output: Enities where user has permissions to "view". For anyone interested here's my scope: My scope methodRun <?php
namespace App\Models\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Support\Facades\Auth;
class PermissionScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*/
public function apply(Builder $builder, Model $model): void
{
if (Auth::hasUser()) {
// Exclude forbidden items.
$builder->whereNotIn('id', Auth::user()->getForbiddenAbilities()->where('entity_type', get_class($model))->pluck('entity_id'));
// Filter explicit allowed items.
$allowed = Auth::user()->getAbilities()->where('entity_type', get_class($model));
if ($allowed->whereNotNull('entity_id')->isNotEmpty() && $allowed->where('name', '*')->whereNull('entity_id')->isEmpty()) {
$builder->whereIn('id', $allowed->pluck('entity_id'));
}
}
}
} What this scope does:
I then put it in a trait:
<?php
namespace App\Concerns;
use App\Models\Scopes\PermissionScope;
trait FilterAllowed {
/**
* Boot the filter allowed trait for a model.
*/
public static function bootFilterAllowed(): void
{
static::addGlobalScope(new PermissionScope);
}
}
use App\Concerns\FilterAllowed;
class Entity extends Model
{
use FilterAllowed;
} Now, whenever I want the records automatically to be filtered I just add the trait to the model. |
I have something similiar to @timyourivh in my app: /**
* Class PermissionConstrained
*
* Implements the Scope interface to apply permission constraints to a given Eloquent query builder.
*/
class PermissionConstrained implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* @param Builder $builder The Eloquent query builder instance.
* @param Model&IsPermissionConstrained $model The model instance to which the scope is applied.
*/
public function apply(Builder $builder, Model $model): void
{
// If checkPermissions is not true or the user is a guest,
// return without applying the scope.
if (! $model::$checkPermissions || auth()->guest()) {
return;
}
// Get the permissions that the authenticated user is forbidden from viewing.
$forbidden = $this->getPermissions($model, false);
// If the user is forbidden from viewing all records of this model,
// return an empty result set.
if ($forbidden->contains(fn (Ability $ability) => $ability->entity_id === null)) {
$builder->where($model->getKeyName(), null);
return;
}
// Get the permissions that the authenticated user is allowed to view.
$allowed = $this->getPermissions($model);
// If the user is allowed to view all records of this model, return without applying the scope.
if ($allowed->contains(fn (Ability $ability) => $ability->entity_id === null)) {
return;
}
// Apply the scope to the query builder.
$builder->whereIn("{$model->getTable()}.{$model->getKeyName()}", $allowed->pluck('entity_id')->toArray());
$builder->whereNotIn("{$model->getTable()}.{$model->getKeyName()}", $forbidden->pluck('entity_id')->toArray());
}
/**
* Extend the query builder with the scope.
*
* @param Builder $builder The query builder instance.
*/
public function extend(Builder $builder): void
{
$builder->macro('ignorePermissions', function (Builder $builder): Builder {
return $builder->withoutGlobalScope($this);
});
}
/**
* Get the permissions for the given model and allowed flag.
*
* @param Model $model The model instance for which to get the permissions.
* @param bool $allowed The flag indicating whether to get allowed or forbidden permissions.
* @return Collection The collection of permissions.
*/
private function getPermissions(Model $model, bool $allowed = true): Collection
{
return Cache::driver('array')->sear(
$this->getCacheKey($model, $allowed),
function () use ($model, $allowed) {
return auth()->user()?->{$allowed ? 'getAbilities' : 'getForbiddenAbilities'}()
->whereIn('name', ['*', 'view'])
->whereIn('entity_type', ['*', $model::class]);
}
);
}
/**
* Get the cache key for the given model and allowed flag.
*
* @param Model $model The model instance for which to get the cache key.
* @param bool $allowed The flag indicating whether to include the allowed or forbidden permissions in the cache key.
* @return string The cache key for the model and allowed flag.
*/
private function getCacheKey(Model $model, bool $allowed = true): string
{
return implode(':', ['permissions', $model->getMorphClass(), $allowed ? 'a' : 'f']);
} and trait: trait IsPermissionConstrained
{
public static bool $checkPermissions = true;
public static function bootIsPermissionConstrained(): void
{
self::addGlobalScope(new PermissionConstrained);
}
public static function withoutPermissions(Closure $closure): void
{
self::$checkPermissions = false;
$closure();
self::$checkPermissions = true;
}
} I have 2 ways to disable permissions. First is Scope caches all permissions in array driver - so only for current request (might not work with Octane). This speeds up abilities retrieval greatly with many permissions. |
Hello!
I am probably missing something simple here but is there a way i can use a users role or ability as a where clause on an eloquent query?
Thanks!
The text was updated successfully, but these errors were encountered: