278 lines
8.5 KiB
PHP
Executable File
278 lines
8.5 KiB
PHP
Executable File
<?php
|
|
namespace App\Services;
|
|
|
|
use App\Models\ProgramAclRule;
|
|
use App\Models\User;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
class ProgramAclService
|
|
{
|
|
/** @return array<string, array<string, mixed>> */
|
|
public function catalog(): array
|
|
{
|
|
$programs = config('program_acl.programs', []);
|
|
|
|
return is_array($programs) ? $programs : [];
|
|
}
|
|
|
|
/** @return array<string, string> */
|
|
public function programOptions(): array
|
|
{
|
|
$options = [];
|
|
|
|
foreach ($this->catalog() as $key => $definition) {
|
|
$label = trim((string) ($definition['label'] ?? $key));
|
|
$description = trim((string) ($definition['description'] ?? ''));
|
|
$options[$key] = $description !== '' ? ($label . ' - ' . $description) : $label;
|
|
}
|
|
|
|
return $options;
|
|
}
|
|
|
|
/** @return array<int, string> */
|
|
public function manageableRolesFor(User $actor): array
|
|
{
|
|
if ($actor->hasRole('super-admin')) {
|
|
return ['admin', 'amministratore', 'collaboratore', 'fornitore', 'condomino', 'inquilino', 'proprietario'];
|
|
}
|
|
|
|
if ($actor->hasRole('admin')) {
|
|
return ['amministratore', 'collaboratore', 'fornitore', 'condomino', 'inquilino', 'proprietario'];
|
|
}
|
|
|
|
if ($actor->hasRole('amministratore')) {
|
|
return ['collaboratore'];
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
public function canManageRole(User $actor, string $role): bool
|
|
{
|
|
return in_array($role, $this->manageableRolesFor($actor), true);
|
|
}
|
|
|
|
public function manageableUsersQuery(User $actor): Builder
|
|
{
|
|
$query = User::query()->with('roles')->orderBy('name');
|
|
|
|
if ($actor->hasAnyRole(['super-admin', 'admin'])) {
|
|
return $query;
|
|
}
|
|
|
|
if (! $actor->hasRole('amministratore')) {
|
|
return $query->whereRaw('1 = 0');
|
|
}
|
|
|
|
if (! Schema::hasColumn('users', 'amministratore_id')) {
|
|
return $query->whereRaw('1 = 0');
|
|
}
|
|
|
|
$amministratoreId = (int) ($actor->amministratore?->id ?? 0);
|
|
if ($amministratoreId <= 0) {
|
|
return $query->whereRaw('1 = 0');
|
|
}
|
|
|
|
return $query
|
|
->where('amministratore_id', $amministratoreId)
|
|
->whereHas('roles', static function (Builder $roleQuery): void {
|
|
$roleQuery->where('name', 'collaboratore');
|
|
});
|
|
}
|
|
|
|
/** @return array<int, string> */
|
|
public function manageableUserOptions(User $actor): array
|
|
{
|
|
return $this->manageableUsersQuery($actor)
|
|
->get(['id', 'name', 'email'])
|
|
->mapWithKeys(static function (User $user): array {
|
|
$label = trim((string) $user->name);
|
|
$email = trim((string) $user->email);
|
|
|
|
if ($label === '') {
|
|
$label = $email !== '' ? $email : ('Utente #' . (string) $user->id);
|
|
} elseif ($email !== '') {
|
|
$label .= ' <' . $email . '>';
|
|
}
|
|
|
|
return [(int) $user->id => $label];
|
|
})
|
|
->all();
|
|
}
|
|
|
|
public function canManageUser(User $actor, User $target): bool
|
|
{
|
|
if ($actor->hasAnyRole(['super-admin', 'admin'])) {
|
|
return true;
|
|
}
|
|
|
|
if (! $actor->hasRole('amministratore')) {
|
|
return false;
|
|
}
|
|
|
|
if (! $target->hasRole('collaboratore') || ! Schema::hasColumn('users', 'amministratore_id')) {
|
|
return false;
|
|
}
|
|
|
|
return (int) $target->amministratore_id === (int) ($actor->amministratore?->id ?? 0);
|
|
}
|
|
|
|
/** @return array{allow: array<int, string>, deny: array<int, string>} */
|
|
public function roleRules(string $role): array
|
|
{
|
|
if (! $this->hasRulesTable()) {
|
|
return ['allow' => [], 'deny' => []];
|
|
}
|
|
|
|
$rules = ProgramAclRule::query()
|
|
->forRole($role)
|
|
->get(['program_key', 'is_allowed']);
|
|
|
|
return [
|
|
'allow' => $rules->where('is_allowed', true)->pluck('program_key')->values()->all(),
|
|
'deny' => $rules->where('is_allowed', false)->pluck('program_key')->values()->all(),
|
|
];
|
|
}
|
|
|
|
/** @return array{allow: array<int, string>, deny: array<int, string>} */
|
|
public function userRules(int $userId): array
|
|
{
|
|
if (! $this->hasRulesTable()) {
|
|
return ['allow' => [], 'deny' => []];
|
|
}
|
|
|
|
$rules = ProgramAclRule::query()
|
|
->forUser($userId)
|
|
->get(['program_key', 'is_allowed']);
|
|
|
|
return [
|
|
'allow' => $rules->where('is_allowed', true)->pluck('program_key')->values()->all(),
|
|
'deny' => $rules->where('is_allowed', false)->pluck('program_key')->values()->all(),
|
|
];
|
|
}
|
|
|
|
/** @param array<int, string> $allow
|
|
* @param array<int, string> $deny */
|
|
public function replaceRoleRules(string $role, array $allow, array $deny): void
|
|
{
|
|
if (! $this->hasRulesTable()) {
|
|
return;
|
|
}
|
|
|
|
ProgramAclRule::query()->forRole($role)->delete();
|
|
$this->insertRules('role', $role, $allow, $deny);
|
|
}
|
|
|
|
/** @param array<int, string> $allow
|
|
* @param array<int, string> $deny */
|
|
public function replaceUserRules(int $userId, array $allow, array $deny): void
|
|
{
|
|
if (! $this->hasRulesTable()) {
|
|
return;
|
|
}
|
|
|
|
ProgramAclRule::query()->forUser($userId)->delete();
|
|
$this->insertRules('user', (string) $userId, $allow, $deny);
|
|
}
|
|
|
|
public function canAccessProgram(?User $user, string $programKey): bool
|
|
{
|
|
$catalog = $this->catalog();
|
|
if (! isset($catalog[$programKey]) || ! $user instanceof User) {
|
|
return false;
|
|
}
|
|
|
|
if ($user->hasRole('super-admin')) {
|
|
return true;
|
|
}
|
|
|
|
$roleNames = $user->getRoleNames()->values()->all();
|
|
|
|
if ($this->hasRulesTable()) {
|
|
$userRule = ProgramAclRule::query()
|
|
->forUser((int) $user->id)
|
|
->where('program_key', $programKey)
|
|
->first();
|
|
|
|
if ($userRule instanceof ProgramAclRule) {
|
|
return (bool) $userRule->is_allowed;
|
|
}
|
|
|
|
if ($roleNames !== []) {
|
|
$roleRules = ProgramAclRule::query()
|
|
->where('scope_type', 'role')
|
|
->whereIn('scope_key', $roleNames)
|
|
->where('program_key', $programKey)
|
|
->get(['is_allowed']);
|
|
|
|
if ($roleRules->contains(static fn(ProgramAclRule $rule): bool => $rule->is_allowed === false)) {
|
|
return false;
|
|
}
|
|
|
|
if ($roleRules->contains(static fn(ProgramAclRule $rule): bool => $rule->is_allowed === true)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
$defaultRoles = $catalog[$programKey]['default_roles'] ?? [];
|
|
if (! is_array($defaultRoles)) {
|
|
return false;
|
|
}
|
|
|
|
foreach ($roleNames as $roleName) {
|
|
if (in_array($roleName, $defaultRoles, true)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/** @param array<int, string> $allow
|
|
* @param array<int, string> $deny */
|
|
private function insertRules(string $scopeType, string $scopeKey, array $allow, array $deny): void
|
|
{
|
|
if (! $this->hasRulesTable()) {
|
|
return;
|
|
}
|
|
|
|
$catalogKeys = array_keys($this->catalog());
|
|
$allow = array_values(array_unique(array_intersect($catalogKeys, $allow)));
|
|
$deny = array_values(array_unique(array_diff(array_intersect($catalogKeys, $deny), $allow)));
|
|
|
|
$rows = [];
|
|
foreach ($allow as $programKey) {
|
|
$rows[] = [
|
|
'scope_type' => $scopeType,
|
|
'scope_key' => $scopeKey,
|
|
'program_key' => $programKey,
|
|
'is_allowed' => true,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
];
|
|
}
|
|
|
|
foreach ($deny as $programKey) {
|
|
$rows[] = [
|
|
'scope_type' => $scopeType,
|
|
'scope_key' => $scopeKey,
|
|
'program_key' => $programKey,
|
|
'is_allowed' => false,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
];
|
|
}
|
|
|
|
if ($rows !== []) {
|
|
ProgramAclRule::query()->insert($rows);
|
|
}
|
|
}
|
|
|
|
private function hasRulesTable(): bool
|
|
{
|
|
return Schema::hasTable('program_acl_rules');
|
|
}
|
|
}
|