*/ public const PERMISSIONS = [ 'create', 'view', 'edit', 'delete', ]; /** * @return array */ public static function permissions(): array { return self::PERMISSIONS; } /** * @return array */ public function permissionOptions(): array { return collect(self::PERMISSIONS) ->map(fn (string $permission): array => [ 'label' => Str::headline($permission), 'value' => $permission, ]) ->all(); } public function grantAllProjectPermissions(Project $project, User $user): void { $this->syncUserPermissions($project, $user, self::PERMISSIONS); } /** * @param array $permissions */ public function syncUserPermissions(Project $project, User $user, array $permissions): void { $sanitizedPermissions = $this->sanitizePermissions($permissions); DB::transaction(function () use ($project, $user, $sanitizedPermissions): void { DB::table('project_user_permissions') ->where('project_id', $project->id) ->where('user_id', $user->id) ->delete(); if ($sanitizedPermissions === []) { return; } DB::table('project_user_permissions')->insert( array_map( fn (string $permission): array => [ 'project_id' => $project->id, 'user_id' => $user->id, 'permission' => $permission, 'created_at' => now(), 'updated_at' => now(), ], $sanitizedPermissions, ), ); }); } /** * @param array $permissions */ public function syncTeamPermissions(Project $project, Team $team, array $permissions): void { $sanitizedPermissions = $this->sanitizePermissions($permissions); DB::transaction(function () use ($project, $team, $sanitizedPermissions): void { DB::table('project_team_permissions') ->where('project_id', $project->id) ->where('team_id', $team->id) ->delete(); if ($sanitizedPermissions === []) { return; } DB::table('project_team_permissions')->insert( array_map( fn (string $permission): array => [ 'project_id' => $project->id, 'team_id' => $team->id, 'permission' => $permission, 'created_at' => now(), 'updated_at' => now(), ], $sanitizedPermissions, ), ); }); } /** * @param array $userIds */ public function syncTeamMembers(Team $team, array $userIds): void { $team->users()->sync($userIds); } public function userHasPermission(User $user, Project $project, string $permission): bool { if ($user->is_admin) { return true; } $sanitizedPermission = Str::lower(trim($permission)); if (! in_array($sanitizedPermission, self::PERMISSIONS, true)) { return false; } $hasDirectPermission = DB::table('project_user_permissions') ->where('project_id', $project->id) ->where('user_id', $user->id) ->where('permission', $sanitizedPermission) ->exists(); if ($hasDirectPermission) { return true; } return DB::table('project_team_permissions') ->join('team_user', 'team_user.team_id', '=', 'project_team_permissions.team_id') ->where('project_team_permissions.project_id', $project->id) ->where('team_user.user_id', $user->id) ->where('project_team_permissions.permission', $sanitizedPermission) ->exists(); } public function userCanViewProject(User $user, Project $project): bool { if ($user->is_admin) { return true; } return in_array($project->id, $this->viewableProjectIds($user), true); } public function userCanManageProjectAccess(User $user, Project $project): bool { return $this->userHasPermission($user, $project, 'edit'); } /** * @return array */ public function viewableProjectIds(User $user): array { if ($user->is_admin) { return Project::query()->pluck('id')->all(); } $directProjectIds = DB::table('project_user_permissions') ->where('user_id', $user->id) ->pluck('project_id'); $teamProjectIds = DB::table('project_team_permissions') ->join('team_user', 'team_user.team_id', '=', 'project_team_permissions.team_id') ->where('team_user.user_id', $user->id) ->pluck('project_team_permissions.project_id'); return $directProjectIds ->merge($teamProjectIds) ->map(fn (mixed $projectId): int => (int) $projectId) ->unique() ->values() ->all(); } /** * @return array> */ public function userPermissionMap(Project $project): array { /** @var Collection $permissions */ $permissions = DB::table('project_user_permissions') ->select(['user_id', 'permission']) ->where('project_id', $project->id) ->orderBy('permission') ->get(); return $permissions ->groupBy(fn (object $permission): int => (int) $permission->user_id) ->map(fn (Collection $group): array => $group ->pluck('permission') ->values() ->all()) ->all(); } /** * @return array> */ public function teamPermissionMap(Project $project): array { /** @var Collection $permissions */ $permissions = DB::table('project_team_permissions') ->select(['team_id', 'permission']) ->where('project_id', $project->id) ->orderBy('permission') ->get(); return $permissions ->groupBy(fn (object $permission): int => (int) $permission->team_id) ->map(fn (Collection $group): array => $group ->pluck('permission') ->values() ->all()) ->all(); } /** * @param array $permissions * @return array */ private function sanitizePermissions(array $permissions): array { return collect($permissions) ->map(fn (mixed $permission): string => Str::lower(trim((string) $permission))) ->filter(fn (string $permission): bool => in_array($permission, self::PERMISSIONS, true)) ->unique() ->values() ->all(); } }