From 72685ad241b5b65abe5aca9f200c1a801734c636 Mon Sep 17 00:00:00 2001 From: Mike Mason Date: Wed, 7 Aug 2024 11:54:56 -0500 Subject: [PATCH] better represent structure in mermaid graph (#263) This better represents the action and action relationship lookups in mermaid graphs. Before: ```mermaid erDiagram rolebinding }o--o{ rolev2 : role rolebinding }o--o{ user : subject rolebinding }o--o{ client : subject rolebinding }o--o{ group : subject user { id_prefix idntusr } client { id_prefix idntclt } role { id_prefix permrol } role }o--o{ subject: subject tenant { id_prefix tnntten } tenant }o--o{ tenant: parent tenant }o--o{ rolebinding: grant group { id_prefix idntgrp subgroup_action member } group }o--o{ rolebinding: grant group }o--o{ group: parent group }o--o{ tenant: parent group }o--o{ user: direct_member group }o--o{ client: direct_member group }o--o{ group: subgroup loadbalancer { id_prefix loadbal action loadbalancer_get action loadbalancer_update action loadbalancer_delete } loadbalancer }o--o{ resourceowner: owner loadbalancer }o--o{ rolebinding: grant resourceowner { } resourceowner ||--|| tenant: alias resourcemanager { action role_create action role_get action role_list action role_update action role_delete action loadbalancer_create action loadbalancer_get action loadbalancer_list action loadbalancer_update action loadbalancer_delete } resourcemanager ||--|| tenant: alias resourcemanager ||--|| group: alias subject { } subject ||--|| user: alias subject ||--|| client: alias ``` After: ```mermaid erDiagram rolebinding }o--o{ rolev2 : role rolebinding }o--o{ user : subject rolebinding }o--o{ client : subject rolebinding }o--o{ group : subject user { id_prefix idntusr } client { id_prefix idntclt } role { id_prefix permrol } role }o--o{ subject: subject tenant { id_prefix tnntten } tenant }o--o{ tenant: parent tenant }o--o{ rolebinding: grant group { id_prefix idntgrp action member "direct_member> | subgroup>member" } group }o--o{ rolebinding: grant group }o--o{ group: parent group }o--o{ tenant: parent group }o--o{ user: direct_member group }o--o{ client: direct_member group }o--o{ group: subgroup loadbalancer { id_prefix loadbal action loadbalancer_delete "self" action loadbalancer_get "self" action loadbalancer_update "self" } loadbalancer }o--o{ resourceowner: owner loadbalancer }o--o{ rolebinding: grant resourceowner { } resourceowner ||--|| tenant: alias resourcemanager { action loadbalancer_create "self" action loadbalancer_delete "self" action loadbalancer_get "self" action loadbalancer_list "self" action loadbalancer_update "self" action role_create "self" action role_delete "self" action role_get "self" action role_list "self" action role_update "self" } resourcemanager ||--|| tenant: alias resourcemanager ||--|| group: alias subject { } subject ||--|| user: alias subject ||--|| client: alias ``` Signed-off-by: Mike Mason --- cmd/schema_mermaid.go | 113 +++++++++++++++++++++++++++++++----------- 1 file changed, 83 insertions(+), 30 deletions(-) diff --git a/cmd/schema_mermaid.go b/cmd/schema_mermaid.go index 7e41a64e..fe283819 100644 --- a/cmd/schema_mermaid.go +++ b/cmd/schema_mermaid.go @@ -3,6 +3,7 @@ package cmd import ( "bytes" "fmt" + "slices" "text/template" "go.infratographer.com/permissions-api/internal/iapl" @@ -11,22 +12,39 @@ import ( var ( mermaidTemplate = `erDiagram {{- if ne .RBAC nil}} - {{ .RBAC.RoleBindingResource }} }o--o{ {{ .RBAC.RoleResource }} : role + {{ .RBAC.RoleBindingResource.Name }} }o--o{ {{ .RBAC.RoleResource.Name }} : role {{- range $subj := .RBAC.RoleBindingSubjects }} - {{ $.RBAC.RoleBindingResource }} }o--o{ {{ $subj.Name }} : subject + {{ $.RBAC.RoleBindingResource.Name }} }o--o{ {{ $subj.Name }} : subject {{- end }} {{- end }} {{- range $resource := .ResourceTypes }} {{ $resource.Name }} { id_prefix {{ $resource.IDPrefix }} - {{- range $action := index $.Actions $resource.Name }} + {{- range $action, $relations := index $.Actions $resource.Name }} action {{ $action }} - {{- end }} - {{- range $relation, $actions := index $.RelatedActions $resource.Name }} - {{- range $action := $actions }} - {{ $relation }}_action {{ $action }} - {{- end }} + {{- $quoted := false }} + {{- with index $relations "!!SELF!!" }} + {{- " \"self" }} + {{- $quoted = true }} + {{- end }} + {{- range $relation := $.RelationOrder }} + {{- with index $relations $relation }} + {{- range $i, $rel_action := . }} + {{- if not $quoted }} + {{- " \"" }} + {{- $quoted = true }} + {{- else }} + {{- " | " }} + {{- end }} + + {{- $relation }}>{{- $rel_action }} + {{- end }} + {{- end }} + {{- end }} + {{- if $quoted }} + {{- "\"" }} + {{- end }} {{- end }} } {{- range $rel := $resource.Relationships }} @@ -39,13 +57,30 @@ var ( {{- end }} {{- range $union := .Unions }} {{ $union.Name }} { - {{- range $action := index $.Actions $union.Name }} + {{- range $action, $relations := index $.Actions $union.Name }} action {{ $action }} - {{- end }} - {{- range $relation, $actions := index $.RelatedActions $union.Name }} - {{- range $action := $actions }} - {{ $relation }}_action {{ $action }} - {{- end }} + {{- $quoted := false }} + {{- with index $relations "!!SELF!!" }} + {{- " \"self" }} + {{- $quoted = true }} + {{- end }} + {{- range $relation := $.RelationOrder }} + {{- with index $relations $relation }} + {{- range $i, $rel_action := . }} + {{- if not $quoted }} + {{- " \"" }} + {{- $quoted = true }} + {{- else }} + {{- " | " }} + {{- end }} + + {{- $relation }}>{{- $rel_action }} + {{- end }} + {{- end }} + {{- end }} + {{- if $quoted }} + {{- "\"" }} + {{- end }} {{- end }} } {{- range $typ := $union.ResourceTypes }} @@ -59,11 +94,11 @@ var ( ) type mermaidContext struct { - ResourceTypes []iapl.ResourceType - Unions []iapl.Union - Actions map[string][]string - RelatedActions map[string]map[string][]string - RBAC *iapl.RBAC + ResourceTypes []iapl.ResourceType + Unions []iapl.Union + Actions map[string]map[string]map[string][]string + RelationOrder []string + RBAC *iapl.RBAC } func outputPolicyMermaid(dirPath string, markdown bool) { @@ -81,31 +116,49 @@ func outputPolicyMermaid(dirPath string, markdown bool) { policy = iapl.DefaultPolicyDocument() } - actions := map[string][]string{} - relatedActions := map[string]map[string][]string{} + actions := map[string]map[string]map[string][]string{} + relations := []string{} for _, binding := range policy.ActionBindings { for _, cond := range binding.Conditions { if cond.RoleBinding != nil { - actions[binding.TypeName] = append(actions[binding.TypeName], binding.ActionName) + if _, ok := actions[binding.TypeName]; !ok { + actions[binding.TypeName] = make(map[string]map[string][]string) + } + + if _, ok := actions[binding.TypeName][binding.ActionName]; !ok { + actions[binding.TypeName][binding.ActionName] = make(map[string][]string) + } + + actions[binding.TypeName][binding.ActionName]["!!SELF!!"] = append(actions[binding.TypeName][binding.ActionName]["!!SELF!!"], binding.ActionName) } if cond.RelationshipAction != nil { - if _, ok := relatedActions[binding.TypeName]; !ok { - relatedActions[binding.TypeName] = make(map[string][]string) + if _, ok := actions[binding.TypeName]; !ok { + actions[binding.TypeName] = make(map[string]map[string][]string) + } + + if _, ok := actions[binding.TypeName][binding.ActionName]; !ok { + actions[binding.TypeName][binding.ActionName] = make(map[string][]string) } - relatedActions[binding.TypeName][cond.RelationshipAction.Relation] = append(relatedActions[binding.TypeName][cond.RelationshipAction.Relation], cond.RelationshipAction.ActionName) + actions[binding.TypeName][binding.ActionName][cond.RelationshipAction.Relation] = append(actions[binding.TypeName][binding.ActionName][cond.RelationshipAction.Relation], cond.RelationshipAction.ActionName) + + if !slices.Contains(relations, cond.RelationshipAction.Relation) { + relations = append(relations, cond.RelationshipAction.Relation) + } } } } + slices.Sort(relations) + ctx := mermaidContext{ - ResourceTypes: policy.ResourceTypes, - Unions: policy.Unions, - Actions: actions, - RelatedActions: relatedActions, - RBAC: nil, + ResourceTypes: policy.ResourceTypes, + Unions: policy.Unions, + Actions: actions, + RelationOrder: relations, + RBAC: nil, } if policy.RBAC != nil {