@@ -347,6 +347,10 @@ func (e *Executor) showAccessOnWorkflow(name *ast.QualifiedName) error {
347347
348348// showSecurityMatrix handles SHOW SECURITY MATRIX [IN module].
349349func (e * Executor ) showSecurityMatrix (moduleName string ) error {
350+ if e .format == FormatJSON {
351+ return e .showSecurityMatrixJSON (moduleName )
352+ }
353+
350354 h , err := e .getHierarchy ()
351355 if err != nil {
352356 return fmt .Errorf ("failed to build hierarchy: %w" , err )
@@ -566,6 +570,145 @@ func (e *Executor) showSecurityMatrix(moduleName string) error {
566570 return nil
567571}
568572
573+ // showSecurityMatrixJSON emits the security matrix as a JSON table
574+ // with one row per access rule across entities, microflows, pages, and workflows.
575+ func (e * Executor ) showSecurityMatrixJSON (moduleName string ) error {
576+ h , err := e .getHierarchy ()
577+ if err != nil {
578+ return fmt .Errorf ("failed to build hierarchy: %w" , err )
579+ }
580+
581+ tr := & TableResult {
582+ Columns : []string {"ObjectType" , "QualifiedName" , "Roles" , "Rights" },
583+ }
584+
585+ // Entities
586+ dms , _ := e .reader .ListDomainModels ()
587+ for _ , dm := range dms {
588+ modID := h .FindModuleID (dm .ContainerID )
589+ modName := h .GetModuleName (modID )
590+ if moduleName != "" && modName != moduleName {
591+ continue
592+ }
593+ for _ , entity := range dm .Entities {
594+ for _ , rule := range entity .AccessRules {
595+ var roleStrs []string
596+ for _ , rn := range rule .ModuleRoleNames {
597+ roleStrs = append (roleStrs , rn )
598+ }
599+ if len (roleStrs ) == 0 {
600+ for _ , rid := range rule .ModuleRoles {
601+ roleStrs = append (roleStrs , string (rid ))
602+ }
603+ }
604+
605+ var rights []string
606+ if rule .AllowCreate {
607+ rights = append (rights , "C" )
608+ }
609+ rr := rule .DefaultMemberAccessRights == domainmodel .MemberAccessRightsReadOnly ||
610+ rule .DefaultMemberAccessRights == domainmodel .MemberAccessRightsReadWrite
611+ rw := rule .DefaultMemberAccessRights == domainmodel .MemberAccessRightsReadWrite
612+ for _ , ma := range rule .MemberAccesses {
613+ if ma .AccessRights == domainmodel .MemberAccessRightsReadOnly || ma .AccessRights == domainmodel .MemberAccessRightsReadWrite {
614+ rr = true
615+ }
616+ if ma .AccessRights == domainmodel .MemberAccessRightsReadWrite {
617+ rw = true
618+ }
619+ }
620+ if rr {
621+ rights = append (rights , "R" )
622+ }
623+ if rw {
624+ rights = append (rights , "W" )
625+ }
626+ if rule .AllowDelete {
627+ rights = append (rights , "D" )
628+ }
629+
630+ tr .Rows = append (tr .Rows , []any {
631+ "Entity" ,
632+ modName + "." + entity .Name ,
633+ strings .Join (roleStrs , ", " ),
634+ strings .Join (rights , "" ),
635+ })
636+ }
637+ }
638+ }
639+
640+ // Microflows
641+ mfs , _ := e .reader .ListMicroflows ()
642+ for _ , mf := range mfs {
643+ if len (mf .AllowedModuleRoles ) == 0 {
644+ continue
645+ }
646+ modID := h .FindModuleID (mf .ContainerID )
647+ modName := h .GetModuleName (modID )
648+ if moduleName != "" && modName != moduleName {
649+ continue
650+ }
651+ var roleStrs []string
652+ for _ , r := range mf .AllowedModuleRoles {
653+ roleStrs = append (roleStrs , string (r ))
654+ }
655+ tr .Rows = append (tr .Rows , []any {
656+ "Microflow" ,
657+ modName + "." + mf .Name ,
658+ strings .Join (roleStrs , ", " ),
659+ "X" ,
660+ })
661+ }
662+
663+ // Pages
664+ pages , _ := e .reader .ListPages ()
665+ for _ , pg := range pages {
666+ if len (pg .AllowedRoles ) == 0 {
667+ continue
668+ }
669+ modID := h .FindModuleID (pg .ContainerID )
670+ modName := h .GetModuleName (modID )
671+ if moduleName != "" && modName != moduleName {
672+ continue
673+ }
674+ var roleStrs []string
675+ for _ , r := range pg .AllowedRoles {
676+ roleStrs = append (roleStrs , string (r ))
677+ }
678+ tr .Rows = append (tr .Rows , []any {
679+ "Page" ,
680+ modName + "." + pg .Name ,
681+ strings .Join (roleStrs , ", " ),
682+ "X" ,
683+ })
684+ }
685+
686+ // Workflows
687+ wfs , _ := e .reader .ListWorkflows ()
688+ for _ , wf := range wfs {
689+ if len (wf .AllowedModuleRoles ) == 0 {
690+ continue
691+ }
692+ modID := h .FindModuleID (wf .ContainerID )
693+ modName := h .GetModuleName (modID )
694+ if moduleName != "" && modName != moduleName {
695+ continue
696+ }
697+ var roleStrs []string
698+ for _ , r := range wf .AllowedModuleRoles {
699+ roleStrs = append (roleStrs , string (r ))
700+ }
701+ tr .Rows = append (tr .Rows , []any {
702+ "Workflow" ,
703+ modName + "." + wf .Name ,
704+ strings .Join (roleStrs , ", " ),
705+ "X" ,
706+ })
707+ }
708+
709+ return e .writeResult (tr )
710+ }
711+
569712// describeModuleRole handles DESCRIBE MODULE ROLE Module.RoleName.
570713func (e * Executor ) describeModuleRole (name ast.QualifiedName ) error {
571714 h , err := e .getHierarchy ()
0 commit comments