Files
chill-bundles/src/Bundle/ChillMainBundle/Security/RoleDumper.php

87 lines
3.0 KiB
PHP

<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Security;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
final readonly class RoleDumper
{
public function __construct(
private RoleProvider $roleProvider,
private RoleHierarchyInterface $roleHierarchy,
private TranslatorInterface $translator,
) {}
public function dumpAsMarkdown(): string
{
$roles = $this->roleProvider->getRoles();
$rolesWithoutScopes = $this->roleProvider->getRolesWithoutScopes();
// Group roles by title
$groups = [];
foreach ($roles as $role) {
$title = $this->roleProvider->getRoleTitle($role);
$title ??= 'Other';
$groups[$title][] = $role;
}
// Sort groups by title
ksort($groups, SORT_NATURAL | SORT_FLAG_CASE);
$lines = [];
foreach ($groups as $title => $roleList) {
// Sort roles by translated label for deterministic output
usort($roleList, function (string $a, string $b): int {
$ta = $this->translator->trans($a);
$tb = $this->translator->trans($b);
return strcasecmp($ta, $tb);
});
$translatedTitle = $this->translator->trans($title);
$lines[] = '## '.$translatedTitle;
foreach ($roleList as $role) {
// Translate primary role
$translatedRole = $this->translator->trans($role);
// Scope marker: (S) if needs scope, (~~S~~) if no scope required
$needsScope = !in_array($role, $rolesWithoutScopes, true);
$scopeMarker = $needsScope ? '(S)' : '(~~S~~)';
// Compute dependent roles from hierarchy (exclude itself)
$reachable = $this->roleHierarchy->getReachableRoleNames([$role]);
$dependents = array_values(array_filter($reachable, static fn (string $r): bool => $r !== $role));
// Translate dependents and sort deterministically
$translatedDependents = array_map(fn (string $r) => $this->translator->trans($r), $dependents);
sort($translatedDependents, SORT_NATURAL | SORT_FLAG_CASE);
if (count($translatedDependents) > 0) {
$lines[] = sprintf('- **%s** %s: %s', $translatedRole, $scopeMarker, implode(', ', $translatedDependents));
} else {
$lines[] = sprintf('- **%s** %s', $translatedRole, $scopeMarker);
}
}
// Add a blank line between groups
$lines[] = '';
}
// Trim possible trailing blank line
$markdown = rtrim(implode("\n", $lines));
return $markdown."\n"; // End with newline for POSIX friendliness
}
}