major update
This commit is contained in:
119
lib/Service/Access/UiAccessService.php
Normal file
119
lib/Service/Access/UiAccessService.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Service\Access;
|
||||
|
||||
final class UiAccessService
|
||||
{
|
||||
/** @var array<string, bool> */
|
||||
private array $decisionCache = [];
|
||||
|
||||
public function __construct(
|
||||
private readonly AuthorizationService $authorizationService
|
||||
) {
|
||||
}
|
||||
|
||||
public function allow(int $actorUserId, string $ability, array $context = []): bool
|
||||
{
|
||||
$ability = trim($ability);
|
||||
if ($actorUserId <= 0 || $ability === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$normalizedContext = $this->normalizeContext($context);
|
||||
$cacheKey = $actorUserId . '|' . $ability . '|' . sha1(json_encode($normalizedContext, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '');
|
||||
if (array_key_exists($cacheKey, $this->decisionCache)) {
|
||||
return $this->decisionCache[$cacheKey];
|
||||
}
|
||||
|
||||
$decision = $this->authorizationService->authorize($ability, [
|
||||
'actor_user_id' => $actorUserId,
|
||||
...$normalizedContext,
|
||||
]);
|
||||
|
||||
return $this->decisionCache[$cacheKey] = $decision->isAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string|array{ability: string, context?: array<string, mixed>}> $map
|
||||
* @param array<string, mixed> $baseContext
|
||||
* @return array<string, bool>
|
||||
*/
|
||||
public function resolveMap(int $actorUserId, array $map, array $baseContext = []): array
|
||||
{
|
||||
$resolved = [];
|
||||
$normalizedBaseContext = $this->normalizeContext($baseContext);
|
||||
|
||||
foreach ($map as $key => $definition) {
|
||||
if (is_string($definition)) {
|
||||
$resolved[$key] = $this->allow($actorUserId, $definition, $normalizedBaseContext);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_array($definition)) {
|
||||
$resolved[$key] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
$ability = $definition['ability'];
|
||||
$context = [];
|
||||
if (array_key_exists('context', $definition) && is_array($definition['context'])) {
|
||||
$context = $this->normalizeContext($definition['context']);
|
||||
}
|
||||
$resolved[$key] = $this->allow($actorUserId, $ability, [
|
||||
...$normalizedBaseContext,
|
||||
...$context,
|
||||
]);
|
||||
}
|
||||
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string|array{ability: string, context?: array<string, mixed>}> $map
|
||||
* @param array<string, mixed> $baseContext
|
||||
* @return array<string, bool>
|
||||
*/
|
||||
public function pageCapabilities(int $actorUserId, array $map, array $baseContext = []): array
|
||||
{
|
||||
return $this->resolveMap($actorUserId, $map, $baseContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool>
|
||||
*/
|
||||
public function layoutCapabilities(int $actorUserId): array
|
||||
{
|
||||
return $this->resolveMap($actorUserId, UiCapabilityMap::LAYOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $context
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function normalizeContext(array $context): array
|
||||
{
|
||||
$normalized = $this->normalizeValue($context);
|
||||
return is_array($normalized) ? $normalized : [];
|
||||
}
|
||||
|
||||
private function normalizeValue(mixed $value): mixed
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$isList = array_is_list($value);
|
||||
if ($isList) {
|
||||
foreach ($value as $idx => $entry) {
|
||||
$value[$idx] = $this->normalizeValue($entry);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
ksort($value);
|
||||
foreach ($value as $key => $entry) {
|
||||
$value[$key] = $this->normalizeValue($entry);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user