diff --git a/app/Http/Controllers/Campaign/RoleController.php b/app/Http/Controllers/Campaign/RoleController.php index 8b31c1bcd9..295ad4c244 100644 --- a/app/Http/Controllers/Campaign/RoleController.php +++ b/app/Http/Controllers/Campaign/RoleController.php @@ -8,20 +8,24 @@ use App\Http\Requests\StoreCampaignRole; use App\Models\Campaign; use App\Models\CampaignRole; +use App\Services\Permissions\RolePermissionService; use Illuminate\Http\Request; class RoleController extends Controller { protected string $view = 'campaigns.roles'; + protected RolePermissionService $service; + /** * Create a new controller instance. * @return void */ - public function __construct() + public function __construct(RolePermissionService $rolePermissionService) { $this->middleware('auth'); $this->middleware('campaign.member'); + $this->service = $rolePermissionService; } /** @@ -132,6 +136,7 @@ public function show(Campaign $campaign, CampaignRole $campaignRole) 'role' => $campaignRole, 'campaign' => $campaign, 'members' => $members, + 'permissionService' => $this->service->role($campaignRole) ]); } @@ -176,7 +181,7 @@ public function savePermissions(Request $request, Campaign $campaign, CampaignRo $this->authorize('view', [$campaignRole, $campaign]); $this->authorize('update', $campaignRole); - $campaignRole->savePermissions($request->post('permissions', [])); + $this->service->role($campaignRole)->savePermissions($request->post('permissions', [])); return redirect()->route('campaign_roles.show', [$campaign, 'campaign_role' => $campaignRole]) ->with('success', trans('crud.permissions.success')); @@ -249,7 +254,7 @@ public function toggle(Campaign $campaign, CampaignRole $campaignRole, int $enti abort(404); } - $enabled = $campaignRole->toggle($entityType, $action); + $enabled = $this->service->role($campaignRole)->toggle($entityType, $action); return response()->json([ 'success' => true, 'status' => $enabled, diff --git a/app/Models/CampaignRole.php b/app/Models/CampaignRole.php index 66330f8b64..9f9e329cf1 100644 --- a/app/Models/CampaignRole.php +++ b/app/Models/CampaignRole.php @@ -131,49 +131,6 @@ public function rolePermissions() ->whereNull('entity_id'); } - /** - */ - public function savePermissions(array $permissions = []) - { - // Load existing - $existing = []; - foreach ($this->rolePermissions as $permission) { - if (empty($permission->entity_type_id)) { - $existing['campaign_' . $permission->action] = $permission; - continue; - } - $existing[$permission->entity_type_id . '_' . $permission->action] = $permission; - } - - // Loop on submitted form - if (empty($permissions)) { - $permissions = []; - } - - foreach ($permissions as $key => $module) { - // Check if exists$ - if (isset($existing[$key])) { - // Do nothing - unset($existing[$key]); - } else { - $action = Str::after($key, '_'); - if ($module === 'campaign') { - $module = 0; - } - - $this->add($module, (int) $action); - } - } - - // Delete existing that weren't updated - foreach ($existing as $permission) { - // Only delete if it's a "general" and not an entity specific permission - if (!is_numeric($permission->entityId())) { - $permission->delete(); - } - } - } - /** */ public function scopeSearch(Builder $builder, string $search = null): Builder @@ -182,44 +139,6 @@ public function scopeSearch(Builder $builder, string $search = null): Builder ->where('name', 'like', "%{$search}%"); } - /** - * Toggle an entity's action permission - */ - public function toggle(int $entityType, int $action): bool - { - $perm = $this->permissions() - ->where('entity_type_id', $entityType) - ->where('action', $action) - ->whereNull('entity_id') - ->first(); - - if ($perm) { - $perm->delete(); - return false; - } - - $this->add($entityType, $action); - return true; - } - - /** - * Add a campaign permission for the role - */ - protected function add(int $entityType, int $action): CampaignPermission - { - if ($entityType === 0) { - $entityType = null; - } - return CampaignPermission::create([ - //'key' => $key, - 'campaign_role_id' => $this->id, - //'table_name' => $value, - 'access' => true, - 'action' => $action, - 'entity_type_id' => $entityType - //'campaign_id' => $campaign->id, - ]); - } /** */ public function url(string $sub): string diff --git a/app/Models/Quest.php b/app/Models/Quest.php index e7179e67c4..f8ad0f9a70 100644 --- a/app/Models/Quest.php +++ b/app/Models/Quest.php @@ -114,6 +114,7 @@ public function scopePreparedWith(Builder $query): Builder 'entity', 'entity.image', 'entity.calendarDateEvents', + 'entity.calendarDate', 'quests', 'instigator', //'elements', diff --git a/app/Models/Relations/CampaignRelations.php b/app/Models/Relations/CampaignRelations.php index 2f405c04f8..a097a42912 100644 --- a/app/Models/Relations/CampaignRelations.php +++ b/app/Models/Relations/CampaignRelations.php @@ -111,6 +111,14 @@ public function members() return $this->hasMany('App\Models\CampaignUser'); } + public function nonAdmins() + { + return $this + ->members() + ->withoutAdmins() + ->with(['user', 'user.campaignRoles']) + ; + } /** */ public function roles() diff --git a/app/Services/BulkService.php b/app/Services/BulkService.php index efa8ccf4ed..84f1a66fca 100644 --- a/app/Services/BulkService.php +++ b/app/Services/BulkService.php @@ -11,6 +11,7 @@ use App\Services\Entity\MoveService; use App\Services\Entity\TagService; use App\Services\Entity\TransformService; +use App\Services\Permissions\BulkPermissionService; use App\Traits\CampaignAware; use Illuminate\Support\Arr; use Illuminate\Support\Collection; @@ -25,7 +26,7 @@ class BulkService protected EntityService $entityService; - protected PermissionService $permissionService; + protected BulkPermissionService $permissionService; protected TransformService $transformService; @@ -46,7 +47,7 @@ class BulkService public function __construct( EntityService $entityService, - PermissionService $permissionService, + BulkPermissionService $permissionService, TransformService $transformService, MoveService $moveService ) { @@ -129,7 +130,10 @@ public function permissions(array $permissions = [], bool $override = true): int foreach ($this->ids as $id) { $entity = $model->findOrFail($id); if (auth()->user()->can('update', $entity)) { - $this->permissionService->change($permissions, $entity->entity, $override); + $this->permissionService + ->entity($entity->entity) + ->override($override) + ->change($permissions); $this->count++; } } diff --git a/app/Services/PermissionService.php b/app/Services/PermissionService.php index 8f1d417649..c91dff267a 100644 --- a/app/Services/PermissionService.php +++ b/app/Services/PermissionService.php @@ -7,6 +7,8 @@ use App\Models\Entity; use App\Models\EntityType; use App\Traits\CampaignAware; +use App\Traits\RoleAware; +use App\Traits\UserAware; use Illuminate\Support\Arr; /** @@ -16,14 +18,13 @@ class PermissionService { use CampaignAware; - - /** @var mixed Users with a role */ - private mixed $users; + use RoleAware; + use UserAware; /** @var int */ private $type; - private CampaignRole $role; + protected int $action; /** * Permissions setup on the campaign @@ -42,194 +43,12 @@ public function type(int $type): self return $this; } - /** - * Set the role - * @return $this - */ - public function role(CampaignRole $role): self + public function action(int $action): self { - $this->role = $role; + $this->action = $action; return $this; } - /** - * Get the campaign role permissions. First key is the entity type - */ - public function permissions(): array - { - $permissions = []; - - $campaignRolePermissions = []; - foreach ($this->role->rolePermissions as $perm) { - $campaignRolePermissions[$perm->entity_type_id . '_' . $perm->action] = 1; - } - - $entityActions = [ - CampaignPermission::ACTION_READ, CampaignPermission::ACTION_EDIT, - CampaignPermission::ACTION_ADD, CampaignPermission::ACTION_DELETE, - CampaignPermission::ACTION_POSTS, CampaignPermission::ACTION_PERMS - ]; - $icons = [ - CampaignPermission::ACTION_READ => [ - 'fa-solid fa-eye', - 'read', - ], - CampaignPermission::ACTION_EDIT => [ - 'fa-solid fa-pen', - 'edit', - ], - CampaignPermission::ACTION_ADD => [ - 'fa-solid fa-plus-square', - 'add', - ], - CampaignPermission::ACTION_DELETE => [ - 'fa-solid fa-trash-alt', - 'delete', - ], - CampaignPermission::ACTION_POSTS => [ - 'fa-solid fa-file', - 'entity-note', - ], - CampaignPermission::ACTION_PERMS => [ - 'fa-solid fa-cog', - 'permission', - ], - ]; - - // Public actions - if ($this->role->isPublic()) { - //$actions = ['read']; - $entityActions = [CampaignPermission::ACTION_READ]; - } - - foreach (EntityType::get() as $entityType) { - foreach ($entityActions as $action) { - if (!isset($permissions[$entityType->id])) { - $permissions[$entityType->id] = [ - 'entityType' => $entityType, - 'permissions' => [] - ]; - } - $key = "{$entityType->id}_{$action}"; - $permissions[$entityType->id]['permissions'][] = [ - 'action' => $action, - 'key' => $key, - 'icon' => Arr::first($icons[$action]), - 'label' => Arr::last($icons[$action]), - 'enabled' => isset($campaignRolePermissions[$key]), - ]; - } - } - - return $permissions; - } - - /** - * Determine if the loaded role has the permission to do a specific action on the - * specified entity type (->type()) - */ - public function can(int $action = CampaignPermission::ACTION_READ): bool - { - return $this->role->permissions - ->where('entity_type_id', $this->type) - ->where('action', $action) - ->where('access', true) - ->count() === 1; - } - - /** - * Campaign Permissions - */ - public function campaignPermissions(): array - { - $permissions = []; - - $campaignRolePermissions = []; - foreach ($this->role->permissions as $perm) { - if ($perm->entity_type_id || $perm->isGallery()) { - continue; - } - $campaignRolePermissions["campaign_" . $perm->action] = 1; - } - - $entityActions = [ - CampaignPermission::ACTION_MANAGE, CampaignPermission::ACTION_DASHBOARD, - CampaignPermission::ACTION_MEMBERS - ]; - $icons = [ - CampaignPermission::ACTION_MANAGE => [ - 'fa-solid fa-cog', - 'manage', - ], - CampaignPermission::ACTION_DASHBOARD => [ - 'fa-solid fa-columns','dashboard', - ], - CampaignPermission::ACTION_MEMBERS => [ - 'fa-solid fa-users', 'members', - ], - ]; - - foreach ($entityActions as $action) { - if (!isset($permissions['campaign'])) { - $permissions['campaign'] = []; - } - $key = "campaign_{$action}"; - $permissions['campaign'][] = [ - 'action' => $action, - 'key' => $key, - 'icon' => Arr::first($icons[$action]), - 'label' => Arr::last($icons[$action]), - 'enabled' => isset($campaignRolePermissions[$key]), - ]; - } - return $permissions; - } - - public function galleryPermissions(): array - { - $permissions = []; - - $campaignRolePermissions = []; - foreach ($this->role->permissions as $perm) { - if ($perm->entity_type_id || !$perm->isGallery()) { - continue; - } - $campaignRolePermissions["campaign_" . $perm->action] = 1; - } - - $entityActions = [ - CampaignPermission::ACTION_GALLERY, - CampaignPermission::ACTION_GALLERY_BROWSE, - CampaignPermission::ACTION_GALLERY_UPLOAD - ]; - $icons = [ - CampaignPermission::ACTION_GALLERY => [ - 'fa-solid fa-cog', 'gallery.manage', - ], - CampaignPermission::ACTION_GALLERY_BROWSE => [ - 'fa-solid fa-eye','gallery.browse', - ], - CampaignPermission::ACTION_GALLERY_UPLOAD => [ - 'fa-solid fa-upload', 'gallery.upload', - ], - ]; - - foreach ($entityActions as $action) { - if (!isset($permissions['campaign'])) { - $permissions['campaign'] = []; - } - $key = "campaign_{$action}"; - $permissions['campaign'][] = [ - 'action' => $action, - 'key' => $key, - 'icon' => Arr::first($icons[$action]), - 'label' => Arr::last($icons[$action]), - 'enabled' => isset($campaignRolePermissions[$key]), - ]; - } - return $permissions; - } - /** */ public function saveEntity(array $request, Entity $entity): void @@ -345,121 +164,6 @@ public function saveEntity(array $request, Entity $entity): void } } - /** - * @param array $request - */ - public function change($request, Entity $entity, bool $override = true): void - { - // First, let's get all the stuff for this entity - $permissions = $this->clearEntityPermissions()->entityPermissions($entity); - - // Next, start looping the data - if (!empty($request['role'])) { - foreach ($request['role'] as $roleId => $data) { - foreach ($data as $perm => $action) { - if ($action == 'allow') { - if (empty($permissions['role'][$roleId][$perm])) { - CampaignPermission::create([ - 'campaign_role_id' => $roleId, - 'campaign_id' => $entity->campaign_id, - 'entity_id' => $entity->id, - 'misc_id' => $entity->child->id, - 'action' => $perm, - 'access' => true, - ]); - } else { - $permissions['role'][$roleId][$perm]->update(['access' => true]); - unset($permissions['role'][$roleId][$perm]); - } - } elseif ($action == 'remove') { - if (!empty($permissions['role'][$roleId][$perm])) { - $permissions['role'][$roleId][$perm]->delete(); - unset($permissions['role'][$roleId][$perm]); - } - } elseif ($action === 'deny') { - if (empty($permissions['role'][$roleId][$perm])) { - CampaignPermission::create([ - 'campaign_role_id' => $roleId, - 'campaign_id' => $entity->campaign_id, - 'entity_id' => $entity->id, - 'misc_id' => $entity->child->id, - 'action' => $perm, - 'access' => false, - ]); - } else { - $permissions['role'][$roleId][$perm]->update(['access' => false]); - unset($permissions['role'][$roleId][$perm]); - } - } elseif ($action == 'inherit') { - // Inherit? Remove it if it exists - if (!empty($permissions['role'][$roleId][$perm])) { - $permissions['role'][$roleId][$perm]->delete(); - } - } - } - } - } - if (!empty($request['user'])) { - foreach ($request['user'] as $userId => $data) { - foreach ($data as $perm => $action) { - if ($action == 'allow') { - if (empty($permissions['user'][$userId][$perm])) { - CampaignPermission::create([ - //'key' => $entity->type() . '_' . $perm . '_' . $entity->child->id, - 'user_id' => $userId, - 'campaign_id' => $entity->campaign_id, - 'entity_id' => $entity->id, - //'entity_type_id' => $entity->type_id, - 'misc_id' => $entity->child->id, - 'action' => $perm, - 'access' => true, - ]); - } else { - $permissions['user'][$userId][$perm]->update(['access' => true]); - unset($permissions['user'][$userId][$perm]); - } - } elseif ($action == 'remove') { - if (!empty($permissions['user'][$userId][$perm])) { - $permissions['user'][$userId][$perm]->delete(); - unset($permissions['user'][$userId][$perm]); - } - } elseif ($action === 'deny') { - if (empty($permissions['user'][$userId][$perm])) { - CampaignPermission::create([ - //'key' => $entity->type() . '_' . $perm . '_' . $entity->child->id, - 'user_id' => $userId, - 'campaign_id' => $entity->campaign_id, - //'table_name' => $entity->pluralType(), - 'entity_id' => $entity->id, - //'entity_type_id' => $entity->type_id, - 'misc_id' => $entity->child->id, - 'action' => $perm, - 'access' => false - ]); - } else { - $permissions['user'][$userId][$perm]->update(['access' => false]); - } - } elseif ($action == 'inherit') { - // Inherit? Remove it if it exists - if (!empty($permissions['user'][$userId][$perm])) { - $permissions['user'][$userId][$perm]->delete(); - } - } - } - } - } - - // If the user requested an override, any permissions that was not specifically set will be deleted. - if ($override) { - foreach ($permissions as $type => $data) { - foreach ($data as $user => $actions) { - foreach ($actions as $action => $perm) { - $perm->delete(); - } - } - } - } - } /** * Get the permissions of an entity @@ -483,7 +187,7 @@ public function entityPermissions(Entity $entity): array /** */ - protected function clearEntityPermissions(): self + public function clearEntityPermissions(): self { unset($this->cachedPermissions); return $this; @@ -491,7 +195,7 @@ protected function clearEntityPermissions(): self /** */ - public function inherited(int $action, int $role = 0, int $user = 0): bool + public function inherited(): bool { if (empty($this->type)) { return false; @@ -524,56 +228,49 @@ public function inherited(int $action, int $role = 0, int $user = 0): bool } } - $key = $this->type . '_' . $action; - if (!empty($role)) { - return Arr::has($this->basePermissions, "roles.{$role}.{$key}"); + $key = $this->type . '_' . $this->action; + if (isset($this->role)) { + return Arr::has($this->basePermissions, "roles.{$this->role->id}.{$key}"); } - return Arr::has($this->basePermissions, "users.{$user}.{$key}"); + return Arr::has($this->basePermissions, "users.{$this->user->id}.{$key}"); } /** */ - public function inheritedRoleName(int $action, int $user): string + public function inheritedRoleName(): string { - $key = $this->type . '_' . $action; - return $this->basePermissions['users'][$user][$key]['role']; + $key = $this->type . '_' . $this->action; + return $this->basePermissions['users'][$this->user->id][$key]['role']; } /** */ - public function inheritedRoleAccess(int $action, int $user): bool + public function inheritedRoleAccess(): bool { - $key = $this->type . '_' . $action; - return $this->basePermissions['users'][$user][$key]['access']; + $key = $this->type . '_' . $this->action; + return $this->basePermissions['users'][$this->user->id][$key]['access']; } /** */ - public function selected(string $type, int $user, int $action): string + public function selected(string $type): string { if (!isset($this->cachedPermissions)) { return 'inherit'; } - $value = Arr::get($this->cachedPermissions, $type . '.' . $user . '.' . $action, null); + $user = isset($this->user) ? $this->user->id : $this->role->id; + $value = Arr::get($this->cachedPermissions, $type . '.' . $user . '.' . $this->action, null); if ($value === null) { return 'inherit'; } return $value->access ? 'allow' : 'deny'; } - /** - * @return array - */ - public function users() + public function reset(): self { - if (isset($this->users)) { - return $this->users; - } - $this->users = $this->campaign - ->members() - ->withoutAdmins() - ->with(['user', 'user.campaignRoles']) - ->get(); - return $this->users; + unset($this->user); + unset($this->role); + unset($this->action); + return $this; } } diff --git a/app/Services/Permissions/BulkPermissionService.php b/app/Services/Permissions/BulkPermissionService.php new file mode 100644 index 0000000000..a3e65586f5 --- /dev/null +++ b/app/Services/Permissions/BulkPermissionService.php @@ -0,0 +1,161 @@ +service = $permissionService; + } + + public function override(bool $override): self + { + $this->override = $override; + return $this; + } + + public function change(array $request): void + { + // First, let's get all the stuff for this entity + $this->permissions = $this->service + ->clearEntityPermissions() + ->entityPermissions($this->entity); + + $this + ->roles($request) + ->users($request) + ->cleanup(); + } + + protected function roles(array $request): self + { + if (empty($request['role'])) { + return $this; + } + foreach ($request['role'] as $roleId => $data) { + foreach ($data as $perm => $action) { + if ($action == 'allow') { + if (empty($this->permissions['role'][$roleId][$perm])) { + CampaignPermission::create([ + 'campaign_role_id' => $roleId, + 'campaign_id' => $this->entity->campaign_id, + 'entity_id' => $this->entity->id, + 'misc_id' => $this->entity->child->id, + 'action' => $perm, + 'access' => true, + ]); + } else { + $this->permissions['role'][$roleId][$perm]->update(['access' => true]); + unset($this->permissions['role'][$roleId][$perm]); + } + } elseif ($action == 'remove') { + if (!empty($this->permissions['role'][$roleId][$perm])) { + $this->permissions['role'][$roleId][$perm]->delete(); + unset($this->permissions['role'][$roleId][$perm]); + } + } elseif ($action === 'deny') { + if (empty($this->permissions['role'][$roleId][$perm])) { + CampaignPermission::create([ + 'campaign_role_id' => $roleId, + 'campaign_id' => $this->entity->campaign_id, + 'entity_id' => $this->entity->id, + 'misc_id' => $this->entity->child->id, + 'action' => $perm, + 'access' => false, + ]); + } else { + $this->permissions['role'][$roleId][$perm]->update(['access' => false]); + unset($this->permissions['role'][$roleId][$perm]); + } + } elseif ($action == 'inherit') { + // Inherit? Remove it if it exists + if (!empty($this->permissions['role'][$roleId][$perm])) { + $this->permissions['role'][$roleId][$perm]->delete(); + } + } + } + } + return $this; + } + protected function users(array $request): self + { + if (empty($request['user'])) { + return $this; + } + foreach ($request['user'] as $userId => $data) { + foreach ($data as $perm => $action) { + if ($action == 'allow') { + if (empty($this->permissions['user'][$userId][$perm])) { + CampaignPermission::create([ + //'key' => $this->entity->type() . '_' . $perm . '_' . $this->entity->child->id, + 'user_id' => $userId, + 'campaign_id' => $this->entity->campaign_id, + 'entity_id' => $this->entity->id, + //'entity_type_id' => $this->entity->type_id, + 'misc_id' => $this->entity->child->id, + 'action' => $perm, + 'access' => true, + ]); + } else { + $this->permissions['user'][$userId][$perm]->update(['access' => true]); + unset($this->permissions['user'][$userId][$perm]); + } + } elseif ($action == 'remove') { + if (!empty($this->permissions['user'][$userId][$perm])) { + $this->permissions['user'][$userId][$perm]->delete(); + unset($this->permissions['user'][$userId][$perm]); + } + } elseif ($action === 'deny') { + if (empty($this->permissions['user'][$userId][$perm])) { + CampaignPermission::create([ + //'key' => $this->entity->type() . '_' . $perm . '_' . $this->entity->child->id, + 'user_id' => $userId, + 'campaign_id' => $this->entity->campaign_id, + //'table_name' => $this->entity->pluralType(), + 'entity_id' => $this->entity->id, + //'entity_type_id' => $this->entity->type_id, + 'misc_id' => $this->entity->child->id, + 'action' => $perm, + 'access' => false + ]); + } else { + $this->permissions['user'][$userId][$perm]->update(['access' => false]); + } + } elseif ($action == 'inherit') { + // Inherit? Remove it if it exists + if (!empty($this->permissions['user'][$userId][$perm])) { + $this->permissions['user'][$userId][$perm]->delete(); + } + } + } + } + return $this; + } + + protected function cleanup(): void + { + // If the user requested an override, any permissions that was not specifically set will be deleted. + if (!$this->override) { + return; + } + foreach ($this->permissions as $type => $data) { + foreach ($data as $user => $actions) { + foreach ($actions as $action => $perm) { + $perm->delete(); + } + } + } + } +} diff --git a/app/Services/Permissions/EntityPermission.php b/app/Services/Permissions/EntityPermission.php index 8478b091c5..9f86089670 100644 --- a/app/Services/Permissions/EntityPermission.php +++ b/app/Services/Permissions/EntityPermission.php @@ -14,34 +14,15 @@ class EntityPermission { - /** - * @var MiscModel - */ - protected $model; - - /** - * @var \Illuminate\Foundation\Application|mixed - */ - protected $app; - - /** - * @var array - */ - protected $cached = []; + protected array $cached = []; - /** - * @var array|bool - */ - protected $roleIds = false; + protected array|bool $roleIds; /** - * @var array|bool|Collection The roles of the user + * The roles of the user */ - protected $roles = []; + protected array|bool|Collection $roles = []; - /** - * @var array Entity Ids - */ protected array $cachedEntityIds = []; /** @@ -50,91 +31,15 @@ class EntityPermission protected $userIsAdmin = null; /** - * @var bool permissions were loaded + * Permissions were loaded */ protected bool $loadedAll = false; /** - * @var int campaign id of the loaded permissions (required for when moving entities between campaigns) + * Campaign id of the loaded permissions (required for when moving entities between campaigns) */ protected int $loadedCampaignId = 0; - /** - * Creates new instance. - */ - public function __construct() - { - $this->app = app(); - } - - /** - * @return bool - */ - public function canView(Entity $entity, Campaign $campaign = null) - { - // Make sure we can see the entity we're trying to show the user. We do it this way because we - // are looping through entities which doesn't allow using the acl trait before hand. - if (auth()->check()) { - return auth()->user()->can('view', $entity->child); - } elseif (!empty($entity->child)) { - return self::hasPermission($entity->type_id, CampaignPermission::ACTION_READ, null, $entity->child, $campaign); - } - return false; - } - - /** - * Get list of entity ids for a given model type that the user can access. - * @param string $action = 'read' - */ - public function entityIds(string $modelName, string $action = 'read'): array - { - // Check if we have this model type at all - $modelIds = Arr::get($this->cachedEntityIds, $modelName, []); - if (empty($modelIds)) { - return []; - } - $ids = []; - foreach ($modelIds as $id => $data) { - if (!is_array($data)) { - // This will throw an error - } - foreach ($data as $perm => $access) { - if ($perm === $action && $access) { - $ids[] = $id; - } - } - } - return $ids; - } - - /** - * Entity IDs the user specifically doesn't have access to - */ - public function deniedEntityIds(string $modelName, string $action = 'read'): array - { - // This function is called in the VisibleTrait of the model, but for example in the search, no permissions are - // already loaded, so we need to call this again to get the user's permissions - $this->loadAllPermissions(auth()->user()); - - // Check if we have this model type at all - $modelIds = Arr::get($this->cachedEntityIds, $modelName, []); - if (empty($modelIds)) { - return []; - } - $ids = []; - foreach ($modelIds as $id => $data) { - if (!is_array($data)) { - // This will throw an error - } - foreach ($data as $perm => $access) { - if ($perm === $action && !$access) { - $ids[] = $id; - } - } - } - return $ids; - } - /** * Determine the permission for a user to interact with an entity * @param MiscModel|Entity|null $entity @@ -192,35 +97,35 @@ public function hasPermission( /** * Check the roles of the user. If the user is an admin, always return true - * @return array|bool */ - protected function getRoleIds(Campaign $campaign, User $user = null) + protected function getRoleIds(Campaign $campaign, User $user = null): array|bool { // If we haven't built a list of roles yet, build it. - if ($this->roleIds === false) { - $this->roles = false; - // If we have a user, get the user's role for this campaign - if ($user) { - $this->roles = UserCache::user($user)->roles(); - } + if (isset($this->roleIds)) { + return $this->roleIds; + } - // If we don't have a user, or our user has no specified role yet, use the public role. - if ($this->roles === false || $this->roles->count() == 0) { - // Use the campaign's public role - $this->roles = $campaign->roles()->where('is_public', true)->get(); - } + $this->roles = false; + // If we have a user, get the user's role for this campaign + if ($user) { + $this->roles = UserCache::user($user)->roles(); + } - // Save all the role ids. If one of them is an admin, stop there. - $this->roleIds = []; - foreach ($this->roles as $role) { - if ($role['is_admin']) { - $this->roleIds = true; - return true; - } - $this->roleIds[] = $role['id']; - } + // If we don't have a user, or our user has no specified role yet, use the public role. + if ($this->roles === false || $this->roles->count() == 0) { + // Use the campaign's public role + $this->roles = $campaign->roles()->where('is_public', true)->get(); } + // Save all the role ids. If one of them is an admin, stop there. + $this->roleIds = []; + foreach ($this->roles as $role) { + if ($role['is_admin']) { + $this->roleIds = true; + return true; + } + $this->roleIds[] = $role['id']; + } return $this->roleIds; } @@ -243,9 +148,8 @@ public function canRole(string $action, string $modelName, $user = null, Campaig /** * It's way easier to just load all permissions of the user once and "cache" them, rather than try and be * optional on each query. - * @return void */ - protected function loadAllPermissions(User $user = null, Campaign $campaign = null) + protected function loadAllPermissions(User $user = null, Campaign $campaign = null): void { // If no campaign was provided, get the one in the url. One is provided when moving entities between campaigns if (empty($campaign)) { @@ -324,7 +228,7 @@ public function resetPermissions(): void // Reset the values keeping score $this->loadedAll = true; $this->cached = []; - $this->roleIds = false; + unset($this->roleIds); $this->userIsAdmin = false; } } diff --git a/app/Services/Permissions/RolePermission.php b/app/Services/Permissions/RolePermission.php index a9e6aaf490..bcd4ccc827 100644 --- a/app/Services/Permissions/RolePermission.php +++ b/app/Services/Permissions/RolePermission.php @@ -4,37 +4,16 @@ use App\Models\CampaignPermission; use App\Models\CampaignRole; +use App\Traits\RoleAware; use Illuminate\Database\Eloquent\Collection; class RolePermission { - /** - * @var \Illuminate\Foundation\Application|mixed - */ - protected $app; + use RoleAware; - protected CampaignRole $role; - - /** */ protected array $permissions = []; - protected array $rolesPermissions = []; - - /** - * - */ - public function __construct() - { - $this->app = app(); - } - /** - * @return $this - */ - public function role(CampaignRole $role): self - { - $this->role = $role; - return $this; - } + protected array $rolesPermissions = []; /** * @return Collection|CampaignPermission[]|array @@ -48,7 +27,7 @@ public function permissions() return $this->permissions[$this->role->id] = $this->role->permissions; } - public function rolesPermissions(array $roles) + public function rolesPermissions(array $roles): mixed { $key = implode('-', $roles); if (isset($this->rolesPermissions[$key])) { diff --git a/app/Services/Permissions/RolePermissionService.php b/app/Services/Permissions/RolePermissionService.php new file mode 100644 index 0000000000..9539c7e72c --- /dev/null +++ b/app/Services/Permissions/RolePermissionService.php @@ -0,0 +1,280 @@ +type = $type; + return $this; + } + + /** + * Get the campaign role permissions. First key is the entity type + */ + public function permissions(): array + { + $permissions = []; + + $campaignRolePermissions = []; + foreach ($this->role->rolePermissions as $perm) { + $campaignRolePermissions[$perm->entity_type_id . '_' . $perm->action] = 1; + } + + $entityActions = [ + CampaignPermission::ACTION_READ, CampaignPermission::ACTION_EDIT, + CampaignPermission::ACTION_ADD, CampaignPermission::ACTION_DELETE, + CampaignPermission::ACTION_POSTS, CampaignPermission::ACTION_PERMS + ]; + $icons = [ + CampaignPermission::ACTION_READ => [ + 'fa-solid fa-eye', + 'read', + ], + CampaignPermission::ACTION_EDIT => [ + 'fa-solid fa-pen', + 'edit', + ], + CampaignPermission::ACTION_ADD => [ + 'fa-solid fa-plus-square', + 'add', + ], + CampaignPermission::ACTION_DELETE => [ + 'fa-solid fa-trash-alt', + 'delete', + ], + CampaignPermission::ACTION_POSTS => [ + 'fa-solid fa-file', + 'entity-note', + ], + CampaignPermission::ACTION_PERMS => [ + 'fa-solid fa-cog', + 'permission', + ], + ]; + + // Public actions + if ($this->role->isPublic()) { + //$actions = ['read']; + $entityActions = [CampaignPermission::ACTION_READ]; + } + + foreach (EntityType::get() as $entityType) { + foreach ($entityActions as $action) { + if (!isset($permissions[$entityType->id])) { + $permissions[$entityType->id] = [ + 'entityType' => $entityType, + 'permissions' => [] + ]; + } + $key = "{$entityType->id}_{$action}"; + $permissions[$entityType->id]['permissions'][] = [ + 'action' => $action, + 'key' => $key, + 'icon' => Arr::first($icons[$action]), + 'label' => Arr::last($icons[$action]), + 'enabled' => isset($campaignRolePermissions[$key]), + ]; + } + } + + return $permissions; + } + /** + * Campaign Permissions + */ + public function campaignPermissions(): array + { + $permissions = []; + + $campaignRolePermissions = []; + foreach ($this->role->permissions as $perm) { + if ($perm->entity_type_id || $perm->isGallery()) { + continue; + } + $campaignRolePermissions["campaign_" . $perm->action] = 1; + } + + $entityActions = [ + CampaignPermission::ACTION_MANAGE, CampaignPermission::ACTION_DASHBOARD, + CampaignPermission::ACTION_MEMBERS + ]; + $icons = [ + CampaignPermission::ACTION_MANAGE => [ + 'fa-solid fa-cog', + 'manage', + ], + CampaignPermission::ACTION_DASHBOARD => [ + 'fa-solid fa-columns','dashboard', + ], + CampaignPermission::ACTION_MEMBERS => [ + 'fa-solid fa-users', 'members', + ], + ]; + + foreach ($entityActions as $action) { + if (!isset($permissions['campaign'])) { + $permissions['campaign'] = []; + } + $key = "campaign_{$action}"; + $permissions['campaign'][] = [ + 'action' => $action, + 'key' => $key, + 'icon' => Arr::first($icons[$action]), + 'label' => Arr::last($icons[$action]), + 'enabled' => isset($campaignRolePermissions[$key]), + ]; + } + return $permissions; + } + + public function galleryPermissions(): array + { + $permissions = []; + + $campaignRolePermissions = []; + foreach ($this->role->permissions as $perm) { + if ($perm->entity_type_id || !$perm->isGallery()) { + continue; + } + $campaignRolePermissions["campaign_" . $perm->action] = 1; + } + + $entityActions = [ + CampaignPermission::ACTION_GALLERY, + CampaignPermission::ACTION_GALLERY_BROWSE, + CampaignPermission::ACTION_GALLERY_UPLOAD + ]; + $icons = [ + CampaignPermission::ACTION_GALLERY => [ + 'fa-solid fa-cog', 'gallery.manage', + ], + CampaignPermission::ACTION_GALLERY_BROWSE => [ + 'fa-solid fa-eye','gallery.browse', + ], + CampaignPermission::ACTION_GALLERY_UPLOAD => [ + 'fa-solid fa-upload', 'gallery.upload', + ], + ]; + + foreach ($entityActions as $action) { + if (!isset($permissions['campaign'])) { + $permissions['campaign'] = []; + } + $key = "campaign_{$action}"; + $permissions['campaign'][] = [ + 'action' => $action, + 'key' => $key, + 'icon' => Arr::first($icons[$action]), + 'label' => Arr::last($icons[$action]), + 'enabled' => isset($campaignRolePermissions[$key]), + ]; + } + return $permissions; + } + + public function savePermissions(array $permissions = []): void + { + // Load existing + $existing = []; + foreach ($this->role->rolePermissions as $permission) { + if (empty($permission->entity_type_id)) { + $existing['campaign_' . $permission->action] = $permission; + continue; + } + $existing[$permission->entity_type_id . '_' . $permission->action] = $permission; + } + + // Loop on submitted form + if (empty($permissions)) { + $permissions = []; + } + + foreach ($permissions as $key => $module) { + // Check if exists$ + if (isset($existing[$key])) { + // Do nothing + unset($existing[$key]); + } else { + $action = Str::after($key, '_'); + if ($module === 'campaign') { + $module = 0; + } + + $this->add($module, (int) $action); + } + } + + // Delete existing that weren't updated + foreach ($existing as $permission) { + // Only delete if it's a "general" and not an entity specific permission + if (!is_numeric($permission->entityId())) { + $permission->delete(); + } + } + } + + /** + * Toggle an entity's action permission + */ + public function toggle(int $entityType, int $action): bool + { + $perm = $this->role->permissions() + ->where('entity_type_id', $entityType) + ->where('action', $action) + ->whereNull('entity_id') + ->first(); + + if ($perm) { + $perm->delete(); + return false; + } + + $this->add($entityType, $action); + return true; + } + + /** + * Determine if the loaded role has the permission to do a specific action on the + * specified entity type (->type()) + */ + public function can(int $action = CampaignPermission::ACTION_READ): bool + { + return $this->role->permissions + ->where('entity_type_id', $this->type) + ->where('action', $action) + ->where('access', true) + ->count() === 1; + } + + /** + * Add a campaign permission for the role + */ + protected function add(int $entityType, int $action): CampaignPermission + { + if ($entityType === 0) { + $entityType = null; + } + return CampaignPermission::create([ + //'key' => $key, + 'campaign_role_id' => $this->role->id, + //'table_name' => $value, + 'access' => true, + 'action' => $action, + 'entity_type_id' => $entityType + //'campaign_id' => $campaign->id, + ]); + } +} diff --git a/app/Traits/RoleAware.php b/app/Traits/RoleAware.php new file mode 100644 index 0000000000..c90ff5cea9 --- /dev/null +++ b/app/Traits/RoleAware.php @@ -0,0 +1,16 @@ +role = $role; + return $this; + } +} diff --git a/resources/views/campaigns/roles/_pretty.blade.php b/resources/views/campaigns/roles/_pretty.blade.php index 19491b2269..233feb1954 100644 --- a/resources/views/campaigns/roles/_pretty.blade.php +++ b/resources/views/campaigns/roles/_pretty.blade.php @@ -1,12 +1,12 @@