Skip to content

Commit

Permalink
feat: support "targets"
Browse files Browse the repository at this point in the history
Signed-off-by: Lukas Frey <[email protected]>
  • Loading branch information
lukas-frey committed Apr 25, 2024
1 parent 9e4bd84 commit e1e0fa5
Show file tree
Hide file tree
Showing 12 changed files with 169 additions and 65 deletions.
2 changes: 2 additions & 0 deletions database/migrations/create_permissions_table.php.stub
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ return new class extends Migration
Schema::create(config('simple-permissions.tables.permissions'), function (Blueprint $table) {
$table->id();
$table->morphs('permissionable');
$table->nullableMorphs('targettable');
$table->string('permission');
});
Schema::create(config('simple-permissions.tables.roles'), function (Blueprint $table) {
$table->id();
$table->morphs('roleable');
$table->nullableMorphs('targettable');
$table->string('role');
});
}
Expand Down
20 changes: 10 additions & 10 deletions src/Concerns/HasAccessControl.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@
namespace Guava\SimplePermissions\Concerns;

use Guava\SimplePermissions\Contracts\Permission;
use Guava\SimplePermissions\Contracts\Role;
use Guava\SimplePermissions\Facades\SimplePermissions;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;

trait HasAccessControl
{
use HasPermissions;
use HasRoles;
use HasPermissions {
HasPermissions::hasPermission as hasPermissionViaModel;
}
use HasRoles {
HasRoles::hasPermission as hasPermissionViaRole;
}

/**
* Determine if the entity has the given abilities.
Expand All @@ -28,16 +32,12 @@ public function can($abilities, $arguments = []): bool
);
}

public function hasPermission(string $permission): bool
public function hasPermission(Permission $permission, ?Model $target = null): bool
{
$permission = SimplePermissions::permissionFromString($permission);

if (in_array($permission, $this->permissions->toArray())) {
if ($this->hasPermissionViaModel($permission, $target)) {
return true;
}

return (bool) $this->roles->first(
fn (Role $role) => in_array($permission, $role->permissions()),
);
return $this->hasPermissionViaRole($permission, $target);
}
}
17 changes: 17 additions & 0 deletions src/Concerns/HasName.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Guava\SimplePermissions\Concerns;

trait HasName
{

public function getName(): string
{
return class_basename(static::class);
}

public function __toString(): string
{
return $this->getName();
}
}
75 changes: 49 additions & 26 deletions src/Concerns/HasPermissions.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,59 +5,82 @@
use Guava\SimplePermissions\Contracts\Permission;
use Guava\SimplePermissions\Facades\SimplePermissions;
use Guava\SimplePermissions\Models\Permissionable;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Support\Collection;

trait HasPermissions
{
public function addPermission(Permission $permission): void
public function addPermission(Permission $permission, ?Model $target = null): void
{
if ($this->permissions()->where('permission', SimplePermissions::make($permission))->exists()) {
$query = $this->permissions();

if ($this->modifyPermissionsQuery($query, $permission, $target)->exists()) {
return;
}

$this->permissions()->create([
$query->create([
'permission' => $permission,
'targettable_type' => $target?->getMorphClass(),
'targettable_id' => $target?->getKey(),
]);
}

public function removePermission(Permission $permission): void
public function removePermission(Permission $permission, ?Model $target = null): void
{
/** @var Permissionable $record */
if ($record = $this->permissions()->where('permission', SimplePermissions::make($permission))->first()) {
if ($record = $this->modifyPermissionsQuery($this->permissions(), $permission, $target)->first()) {
$record->delete();
}
}

public function hasPermission(Permission $permission): bool
public function hasPermission(Permission $permission, ?Model $target = null): bool
{
return $this->permissions()
->where('permission', SimplePermissions::make($permission))
->exists()
;
return $this->modifyPermissionsQuery($this->permissions(), $permission, $target)->exists();
}

public function setPermissionsAttribute(array | Collection $permissions): void
public function getPermissions(?Model $target = null): array
{
$permissions = collect($permissions);

$this->permissions()->delete();
$this->permissions()->createMany(
$permissions
->filter(fn ($permission) => $permission instanceof Permission)
->map(fn (Permission $permission) => ['permission' => $permission])
->unique()
->toArray()
);
return $this->permissions()
->where('targettable_type', $target?->getMorphClass())
->where('targettable_id', $target?->getKey())
->get()
->pluck('permission')
->toArray();
}

public function getPermissionsAttribute(): Collection
{
return $this->permissions()->pluck('permission');
}
// public function setPermissionsAttribute(array | Collection $permissions): void
// {
// $permissions = collect($permissions);
//
// $this->permissions()->delete();
// $this->permissions()->createMany(
// $permissions
// ->filter(fn ($permission) => $permission instanceof Permission)
// ->map(fn (Permission $permission) => [
// 'permission' => $permission,
// config('simple-permissions.tenancy.column') => null,
// ])
// ->unique()
// ->toArray()
// );
// }

// public function getPermissionsAttribute(): Collection
// {
// return $this->permissions()->pluck('permission');
// }

public function permissions(): MorphMany
{
return $this->morphMany(Permissionable::class, 'permissionable');
}

private function modifyPermissionsQuery(Builder $query, Permission $permission, ?Model $target = null): Builder
{
return $query
->where('permission', SimplePermissions::make($permission))
->where('targettable_type', $target?->getMorphClass())
->where('targettable_id', $target?->getKey());
}
}
71 changes: 53 additions & 18 deletions src/Concerns/HasRoles.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,85 @@

namespace Guava\SimplePermissions\Concerns;

use Guava\SimplePermissions\Contracts\Permission;
use Guava\SimplePermissions\Contracts\Role;
use Guava\SimplePermissions\Models\Roleable;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Support\Collection;

trait HasRoles
{
public function addRole(string | Role $role): void
public function addRole(string | Role $role, ?Model $target = null): void
{
$role = $role instanceof Role ? $role::class : $role;

if ($this->roles()->where('role', $role)->exists()) {
if ($this->modifyRolesQuery($this->roles(), $role, $target)->exists()) {
return;
}

$this->roles()->create([
'role' => $role,
'targettable_type' => $target?->getMorphClass(),
'targettable_id' => $target?->getKey(),
]);
}

public function removeRole(string | Role $role): void
public function removeRole(string | Role $role, ?Model $target = null): void
{
$role = $role instanceof Role ? $role::class : $role;
/** @var Roleable $record */
if ($record = $this->roles()->where('role', $role)->first()) {
if ($record = $this->modifyRolesQuery($this->roles(), $role, $target)->first()) {
$record->delete();
}
}

public function hasRole(string | Role $role): bool
public function hasRole(string | Role $role, ?Model $target = null): bool
{
$role = $role instanceof Role ? $role::class : $role;

return $this->modifyRolesQuery($this->roles(), $role, $target)->exists();
}

public function getRoles(?Model $target = null): Collection
{
return $this->roles()
->where('role', $role)
->exists()
->where('targettable_type', $target?->getMorphClass())
->where('targettable_id', $target?->getKey())
->get()
->pluck('role')
->map(fn (string $role) => class_exists($role) ? new $role : $role)
;
}

public function setRolesAttribute(array | Collection $roles): void
public function hasPermission(Permission $permission, ?Model $target = null): bool
{
$roles = collect($roles);

$this->roles()->delete();
$this->roles()->createMany(
$roles
->filter(fn (string | Role $role) => $role instanceof Role || class_exists($role))
->map(fn (string | Role $role) => ['role' => is_string($role) ? $role : $role::class])
->unique()
->toArray()
);
return (bool) $this->roles()
->where('targettable_type', $target?->getMorphClass())
->where('targettable_id', $target?->getKey())
->get()
->pluck('role')
->first(
fn (Role $role) => in_array($permission, $role->permissions()),
)
;
}

// public function setRolesAttribute(array | Collection $roles): void
// {
// $roles = collect($roles);
//
// $this->roles()->delete();
// $this->roles()->createMany(
// $roles
// ->filter(fn (string | Role $role) => $role instanceof Role || class_exists($role))
// ->map(fn (string | Role $role) => ['role' => is_string($role) ? $role : $role::class])
// ->unique()
// ->toArray()
// );
// }

public function getRolesAttribute(): Collection
{
return $this->roles()->pluck('role');
Expand All @@ -64,4 +90,13 @@ public function roles(): MorphMany
{
return $this->morphMany(Roleable::class, 'roleable');
}

private function modifyRolesQuery(Builder $query, string $role, ?Model $target = null): Builder
{
return $query
->where('role', $role)
->where('targettable_type', $target?->getMorphClass())
->where('targettable_id', $target?->getKey())
;
}
}
4 changes: 2 additions & 2 deletions src/Contracts/Role.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

interface Role
{
// public function id(): string;

/**
* @return Permission[]
*/
public function permissions(): array;

public function __toString(): string;
}
5 changes: 4 additions & 1 deletion src/Filament/Concerns/HasAuthorization.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ public static function getPermissions(): string
public static function can(string $action, ?Model $record = null): bool
{
if ($permission = static::getPermissions()::tryFrom($action)) {
return Filament::auth()->user()->can(SimplePermissions::make($permission));
// dd($permission, static::isScopedToTenant() ? Filament::getTenant() : null);
return Filament::auth()->user()->can($permission, [
'target' => static::isScopedToTenant() ? Filament::getTenant() : null,
]);
}

return parent::can($action, $record);
Expand Down
7 changes: 7 additions & 0 deletions src/Models/Permissionable.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class Permissionable extends Model

protected $fillable = [
'permission',
'targettable_type',
'targettable_id',
];

protected $casts = [
Expand All @@ -23,6 +25,11 @@ public function permissionable(): MorphTo
return $this->morphTo();
}

public function targettable(): MorphTo
{
return $this->morphTo();
}

public function getTable()
{
return config('simple-permissions.tables.permissions', parent::getTable());
Expand Down
7 changes: 7 additions & 0 deletions src/Models/Roleable.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class Roleable extends Model

protected $fillable = [
'role',
'targettable_type',
'targettable_id',
];

protected $casts = [
Expand All @@ -23,6 +25,11 @@ public function roleable(): MorphTo
return $this->morphTo();
}

public function targettable(): MorphTo
{
return $this->morphTo();
}

public function getTable()
{
return config('simple-permissions.tables.roles', parent::getTable());
Expand Down
Loading

0 comments on commit e1e0fa5

Please sign in to comment.