214 lines
6.8 KiB
Go
214 lines
6.8 KiB
Go
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))
|
|
}
|