Skip to content

Commit 5047d8a

Browse files
intel352claude
andcommitted
feat: add multi-model authorization support (ACL, ABAC, ReBAC) with capability signaling
Add AuthzProvider interface with capability signaling so each provider declares which authorization models it supports (Casbin: RBAC/ABAC/ACL, Permit.io: RBAC/ABAC/ReBAC). New step types: authz_capabilities, authz_acl_grant/revoke/check/list, authz_abac_check/add_policy, authz_rebac_add_relation/remove_relation/check/list_relations. Comprehensive tests for all four authorization models plus integration tests. Also fixes gofmt formatting in step_permit_registry.go. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b7a7ce4 commit 5047d8a

12 files changed

Lines changed: 2401 additions & 4 deletions

internal/capabilities.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package internal
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
sdk "github.com/GoCodeAlone/workflow/plugin/external/sdk"
8+
)
9+
10+
// AuthzCapability represents an authorization model supported by a provider.
11+
type AuthzCapability string
12+
13+
const (
14+
CapabilityRBAC AuthzCapability = "rbac" // Role-Based Access Control
15+
CapabilityABAC AuthzCapability = "abac" // Attribute-Based Access Control
16+
CapabilityReBAC AuthzCapability = "rebac" // Relationship-Based Access Control
17+
CapabilityACL AuthzCapability = "acl" // Access Control Lists
18+
)
19+
20+
// AuthzProvider is implemented by authorization providers to declare their
21+
// supported authorization models.
22+
type AuthzProvider interface {
23+
Capabilities() []AuthzCapability
24+
SupportsCapability(AuthzCapability) bool
25+
}
26+
27+
// Capabilities returns the authorization models supported by Casbin.
28+
func (m *CasbinModule) Capabilities() []AuthzCapability {
29+
return []AuthzCapability{CapabilityRBAC, CapabilityABAC, CapabilityACL}
30+
}
31+
32+
// SupportsCapability reports whether the Casbin module supports the given
33+
// authorization model.
34+
func (m *CasbinModule) SupportsCapability(cap AuthzCapability) bool {
35+
for _, c := range m.Capabilities() {
36+
if c == cap {
37+
return true
38+
}
39+
}
40+
return false
41+
}
42+
43+
// Capabilities returns the authorization models supported by Permit.io.
44+
func (m *PermitModule) Capabilities() []AuthzCapability {
45+
return []AuthzCapability{CapabilityRBAC, CapabilityABAC, CapabilityReBAC}
46+
}
47+
48+
// SupportsCapability reports whether the Permit module supports the given
49+
// authorization model.
50+
func (m *PermitModule) SupportsCapability(cap AuthzCapability) bool {
51+
for _, c := range m.Capabilities() {
52+
if c == cap {
53+
return true
54+
}
55+
}
56+
return false
57+
}
58+
59+
// authzCapabilitiesStep implements sdk.StepInstance. It returns the capabilities
60+
// of a given provider module.
61+
//
62+
// Config:
63+
//
64+
// module: "authz" # name of the authz.casbin or permit.provider module
65+
// provider: "casbin" # "casbin" or "permit" (default: "casbin")
66+
type authzCapabilitiesStep struct {
67+
name string
68+
moduleName string
69+
provider string
70+
registry moduleRegistry
71+
}
72+
73+
func newAuthzCapabilitiesStep(name string, config map[string]any) (*authzCapabilitiesStep, error) {
74+
s := &authzCapabilitiesStep{
75+
name: name,
76+
moduleName: "authz",
77+
provider: "casbin",
78+
registry: globalRegistry,
79+
}
80+
if v, ok := config["module"].(string); ok && v != "" {
81+
s.moduleName = v
82+
}
83+
if v, ok := config["provider"].(string); ok && v != "" {
84+
s.provider = v
85+
}
86+
return s, nil
87+
}
88+
89+
// Execute returns the capabilities of the configured provider.
90+
func (s *authzCapabilitiesStep) Execute(
91+
_ context.Context,
92+
_ map[string]any,
93+
_ map[string]map[string]any,
94+
_ map[string]any,
95+
_ map[string]any,
96+
_ map[string]any,
97+
) (*sdk.StepResult, error) {
98+
var provider AuthzProvider
99+
100+
switch s.provider {
101+
case "casbin":
102+
mod, ok := s.registry.GetEnforcer(s.moduleName)
103+
if !ok {
104+
return nil, fmt.Errorf("step.authz_capabilities %q: casbin module %q not found", s.name, s.moduleName)
105+
}
106+
provider = mod
107+
case "permit":
108+
_, ok := GetPermitClient(s.moduleName)
109+
if !ok {
110+
return nil, fmt.Errorf("step.authz_capabilities %q: permit module %q not found", s.name, s.moduleName)
111+
}
112+
// We need a PermitModule to call Capabilities; create a minimal one.
113+
provider = &PermitModule{name: s.moduleName}
114+
default:
115+
return nil, fmt.Errorf("step.authz_capabilities %q: unknown provider %q (expected \"casbin\" or \"permit\")", s.name, s.provider)
116+
}
117+
118+
caps := provider.Capabilities()
119+
capStrings := make([]any, len(caps))
120+
for i, c := range caps {
121+
capStrings[i] = string(c)
122+
}
123+
124+
return &sdk.StepResult{
125+
Output: map[string]any{
126+
"provider": s.provider,
127+
"module": s.moduleName,
128+
"capabilities": capStrings,
129+
},
130+
}, nil
131+
}

0 commit comments

Comments
 (0)