package ucan import ( "code.sonr.org/go/ucan/pkg/policy" "code.sonr.org/go/ucan/pkg/policy/literal" "github.com/ipld/go-ipld-prime" ) // PolicyBuilder provides a fluent API for constructing UCAN policies. // Policies are arrays of statements that form an implicit AND. type PolicyBuilder struct { constructors []policy.Constructor } // NewPolicy creates a new PolicyBuilder. func NewPolicy() *PolicyBuilder { return &PolicyBuilder{ constructors: make([]policy.Constructor, 0), } } // Build constructs the final Policy from all added statements. func (b *PolicyBuilder) Build() (Policy, error) { return policy.Construct(b.constructors...) } // MustBuild constructs the final Policy, panicking on error. func (b *PolicyBuilder) MustBuild() Policy { return policy.MustConstruct(b.constructors...) } // Equal adds an equality constraint: selector == value func (b *PolicyBuilder) Equal(selector string, value any) *PolicyBuilder { node, err := toIPLDNode(value) if err != nil { // Store error for Build() to return b.constructors = append(b.constructors, func() (policy.Statement, error) { return nil, err }) return b } b.constructors = append(b.constructors, policy.Equal(selector, node)) return b } // NotEqual adds an inequality constraint: selector != value func (b *PolicyBuilder) NotEqual(selector string, value any) *PolicyBuilder { node, err := toIPLDNode(value) if err != nil { b.constructors = append(b.constructors, func() (policy.Statement, error) { return nil, err }) return b } b.constructors = append(b.constructors, policy.NotEqual(selector, node)) return b } // GreaterThan adds a comparison constraint: selector > value func (b *PolicyBuilder) GreaterThan(selector string, value int64) *PolicyBuilder { b.constructors = append(b.constructors, policy.GreaterThan(selector, literal.Int(value))) return b } // GreaterThanOrEqual adds a comparison constraint: selector >= value func (b *PolicyBuilder) GreaterThanOrEqual(selector string, value int64) *PolicyBuilder { b.constructors = append(b.constructors, policy.GreaterThanOrEqual(selector, literal.Int(value))) return b } // LessThan adds a comparison constraint: selector < value func (b *PolicyBuilder) LessThan(selector string, value int64) *PolicyBuilder { b.constructors = append(b.constructors, policy.LessThan(selector, literal.Int(value))) return b } // LessThanOrEqual adds a comparison constraint: selector <= value func (b *PolicyBuilder) LessThanOrEqual(selector string, value int64) *PolicyBuilder { b.constructors = append(b.constructors, policy.LessThanOrEqual(selector, literal.Int(value))) return b } // Like adds a glob pattern constraint: selector matches pattern // Use * for wildcard, \* for literal asterisk. func (b *PolicyBuilder) Like(selector, pattern string) *PolicyBuilder { b.constructors = append(b.constructors, policy.Like(selector, pattern)) return b } // Not negates a statement func (b *PolicyBuilder) Not(stmt policy.Constructor) *PolicyBuilder { b.constructors = append(b.constructors, policy.Not(stmt)) return b } // And adds a logical AND of multiple statements func (b *PolicyBuilder) And(stmts ...policy.Constructor) *PolicyBuilder { b.constructors = append(b.constructors, policy.And(stmts...)) return b } // Or adds a logical OR of multiple statements func (b *PolicyBuilder) Or(stmts ...policy.Constructor) *PolicyBuilder { b.constructors = append(b.constructors, policy.Or(stmts...)) return b } // All adds a universal quantifier: all elements at selector must satisfy statement func (b *PolicyBuilder) All(selector string, stmt policy.Constructor) *PolicyBuilder { b.constructors = append(b.constructors, policy.All(selector, stmt)) return b } // Any adds an existential quantifier: at least one element at selector must satisfy statement func (b *PolicyBuilder) Any(selector string, stmt policy.Constructor) *PolicyBuilder { b.constructors = append(b.constructors, policy.Any(selector, stmt)) return b } // toIPLDNode converts a Go value to an IPLD node for policy evaluation. func toIPLDNode(value any) (ipld.Node, error) { // Handle IPLD nodes directly if node, ok := value.(ipld.Node); ok { return node, nil } // Use literal package for conversion return literal.Any(value) } // --- Sonr-specific Policy Helpers --- // VaultPolicy creates a policy that restricts operations to a specific vault. // The vault is identified by its CID. func VaultPolicy(vaultCID string) Policy { return NewPolicy().Equal(".vault", vaultCID).MustBuild() } // DIDPolicy creates a policy that restricts operations to DIDs matching a pattern. // Use glob patterns: "did:sonr:*" matches all Sonr DIDs. func DIDPolicy(didPattern string) Policy { return NewPolicy().Like(".did", didPattern).MustBuild() } // ChainPolicy creates a policy that restricts operations to a specific chain. func ChainPolicy(chainID string) Policy { return NewPolicy().Equal(".chain_id", chainID).MustBuild() } // AccountPolicy creates a policy that restricts operations to a specific account address. func AccountPolicy(address string) Policy { return NewPolicy().Equal(".address", address).MustBuild() } // RecordTypePolicy creates a policy for DWN operations on specific record types. func RecordTypePolicy(recordType string) Policy { return NewPolicy().Equal(".record_type", recordType).MustBuild() } // CombinePolicies merges multiple policies into one (implicit AND). func CombinePolicies(policies ...Policy) Policy { combined := make(Policy, 0) for _, p := range policies { combined = append(combined, p...) } return combined } // EmptyPolicy returns an empty policy (no constraints). func EmptyPolicy() Policy { return Policy{} } // --- Policy Constructor Helpers --- // These return policy.Constructor for use with And/Or/Not/All/Any // EqualTo creates an equality constructor for nested policy building. func EqualTo(selector string, value any) policy.Constructor { return func() (policy.Statement, error) { node, err := toIPLDNode(value) if err != nil { return nil, err } return policy.Equal(selector, node)() } } // NotEqualTo creates an inequality constructor for nested policy building. func NotEqualTo(selector string, value any) policy.Constructor { return func() (policy.Statement, error) { node, err := toIPLDNode(value) if err != nil { return nil, err } return policy.NotEqual(selector, node)() } } // Matches creates a glob pattern constructor for nested policy building. func Matches(selector, pattern string) policy.Constructor { return policy.Like(selector, pattern) } // GreaterThanValue creates a comparison constructor. func GreaterThanValue(selector string, value int64) policy.Constructor { return policy.GreaterThan(selector, literal.Int(value)) } // LessThanValue creates a comparison constructor. func LessThanValue(selector string, value int64) policy.Constructor { return policy.LessThan(selector, literal.Int(value)) }