Skip to content

[Security][SecurityBundle] Dump role hierarchy as mermaid chart #61034

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: 7.4
Choose a base branch
from

Conversation

damienfern
Copy link
Contributor

Q A
Branch? 7.4
Bug fix? no
New feature? yes
Deprecations? no
License MIT

From a developer POV, Roles and the SecurityBundle are so convenient, specially role hierarchy in the config. But as a web app grows, the number of roles also grows and with role inheritance, it can be painful to clearly see which role implies another which implies another which implies another (and its possible consequences).

I'm proposing a new command in the security bundle to generate a Mermaid flowchart, easing the understanding of the roles and its hierarchy. An example of the graph generated from the role hierarchy in the doc :

# config/packages/security.yaml
security:
    # ...

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
graph TB
    ROLE_ADMIN
    ROLE_USER
    ROLE_SUPER_ADMIN
    ROLE_ALLOWED_TO_SWITCH
    ROLE_ADMIN --> ROLE_USER
    ROLE_SUPER_ADMIN --> ROLE_ADMIN
    ROLE_SUPER_ADMIN --> ROLE_ALLOWED_TO_SWITCH
Loading

For now, only the mermaid format is suggested (the only format I know) but as the worfklow dump command, we could implement graphviz and plantuml format later.

@damienfern damienfern requested a review from chalasr as a code owner July 4, 2025 10:01
@carsonbot carsonbot added this to the 7.4 milestone Jul 4, 2025
@carsonbot carsonbot changed the title [Security] [SecurityBundle] Dump role hierarchy as mermaid chart [Security][SecurityBundle] Dump role hierarchy as mermaid chart Jul 4, 2025
];

public function __construct(
private readonly string $direction = self::DIRECTION_TOP_TO_BOTTOM,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving $direction argument to dump method would allow to dump multiple times the same role hierarchy in different directions without the need to instanciate multiple dumpers. WDYT?

@94noni
Copy link
Contributor

94noni commented Jul 6, 2025

Nice one 👍🏻
Question: would it be possible to include this in the profiler like the workflow dump ?
We can even imagine each role box colored depending on the current user roles from this config map

@damienfern
Copy link
Contributor Author

Nice one 👍🏻
Question: would it be possible to include this in the profiler like the workflow dump ?

Yes, I had also this in mind for a second PR, once this one gets merged.

We can even imagine each role box colored depending on the current user roles from this config map

Great idea ! I'll keep it in mind :)

$this
->setDefinition([
new InputOption('direction', 'd', InputOption::VALUE_REQUIRED, 'The direction of the flowchart ['.implode('|', MermaidDumper::VALID_DIRECTIONS).']', MermaidDumper::DIRECTION_TOP_TO_BOTTOM),
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'The output format', 'mermaid'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The command implementation being strongly tight to mermaid at the moment, I suggest not to add this format option until we consider supporting another format.

* file that was distributed with this source code.
*/

namespace Symfony\Component\Security\Core\Dumper;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this dumper really "core" inside this component ?
namespace feels weird

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the role hierarchy belongs to symfony/security-core, so it makes sense to put the dumper there as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i was seeing this class tooling more like a "debug or dev" class, that is what I wanted to comment (perhaps poorly ^^)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK, it's the only "debug" class of security component for now, and it is quite tied to roles and role hierarchy. IMO, it can stay here for now.

class SecurityRoleHierarchyDumpCommand extends Command
{
public function __construct(
private readonly ?RoleHierarchyInterface $roleHierarchy = null,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how can it be null via service declaration service() ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We remove the role hierarchy service if you don't configure any hierarchy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we then better remove this command as well ? useless to register a command if its to dump nothing

{
$this
->setDefinition([
new InputOption('direction', 'd', InputOption::VALUE_REQUIRED, 'The direction of the flowchart ['.implode('|', MermaidDumper::VALID_DIRECTIONS).']', MermaidDumper::DIRECTION_TOP_TO_BOTTOM),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add support for completion by providing the valid choices

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the completion method in the command okay for this ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, I missed that you implemented this method. For static values, it is better to configure the completion values in the definition (and let symfony/console provide them) as it is simpler and also visible directly when looking at the definition that completion is supported.

protected function execute(InputInterface $input, OutputInterface $output): int
{
if (null === $this->roleHierarchy) {
$output->writeln('<comment>No role hierarchy is configured.</comment>');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be written to STDERR as it is about human-readable message. this is especially important for that command which will generally send the output to a file.

When using the SymfonyStyle class, you have a method to access easily the error output, and you can also use the usual symfony style for command output.

$format = $input->getOption('format');

if ('mermaid' !== $format) {
$output->writeln('<error>Only "mermaid" format is currently supported.</error>');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should also be written to STDERR.

}

if (!in_array($direction, MermaidDumper::VALID_DIRECTIONS, true)) {
$output->writeln('<error>Invalid direction. Available options: '.implode(', ', MermaidDumper::VALID_DIRECTIONS).'</error>');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should also be written to STDERR.

public const DIRECTION_TOP_DOWN = 'TD';
public const DIRECTION_BOTTOM_TO_TOP = 'BT';
public const DIRECTION_RIGHT_TO_LEFT = 'RL';
public const DIRECTION_LEFT_TO_RIGHT = 'LR';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

those look like a perfect use case for a backed enum instead of using string constants in that class (and then redoing lots of manual validation)

{
$reflection = new \ReflectionClass($roleHierarchy);

if ($reflection->hasProperty('hierarchy')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the RoleHierarchyInterface is not a child of RoleHierarchy, it could have a hierarchy property that has a totally unrelated type.

And if the RoleHierarchyInterface is a child of RoleHierarchy, this check will return false as the property is private: https://3v4l.org/jtqTG#vnull

You should check $roleHierarchy instanceof RoleHierarchy and then create the ReflectionClass for RoleHierarchy::class

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy