mirror of
https://github.com/sonr-io/crypto.git
synced 2026-08-02 15:31:38 +00:00
No commit suggestions generated
This commit is contained in:
@@ -0,0 +1,860 @@
|
||||
// Package ucan provides User-Controlled Authorization Networks (UCAN) implementation
|
||||
// for decentralized authorization and capability delegation in the Sonr network.
|
||||
// This package handles JWT-based tokens, cryptographic verification, and resource capabilities.
|
||||
package ucan
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Token represents a UCAN JWT token with parsed claims
|
||||
type Token struct {
|
||||
Raw string `json:"raw"`
|
||||
Issuer string `json:"iss"`
|
||||
Audience string `json:"aud"`
|
||||
ExpiresAt int64 `json:"exp,omitempty"`
|
||||
NotBefore int64 `json:"nbf,omitempty"`
|
||||
Attenuations []Attenuation `json:"att"`
|
||||
Proofs []Proof `json:"prf,omitempty"`
|
||||
Facts []Fact `json:"fct,omitempty"`
|
||||
}
|
||||
|
||||
// Attenuation represents a UCAN capability attenuation
|
||||
type Attenuation struct {
|
||||
Capability Capability `json:"can"`
|
||||
Resource Resource `json:"with"`
|
||||
}
|
||||
|
||||
// Proof represents a UCAN delegation proof (either JWT or CID)
|
||||
type Proof string
|
||||
|
||||
// Fact represents arbitrary facts in UCAN tokens
|
||||
type Fact struct {
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
// Capability defines what actions can be performed
|
||||
type Capability interface {
|
||||
// GetActions returns the list of actions this capability grants
|
||||
GetActions() []string
|
||||
// Grants checks if this capability grants the required abilities
|
||||
Grants(abilities []string) bool
|
||||
// Contains checks if this capability contains another capability
|
||||
Contains(other Capability) bool
|
||||
// String returns a string representation
|
||||
String() string
|
||||
}
|
||||
|
||||
// Resource defines what resource the capability applies to
|
||||
type Resource interface {
|
||||
// GetScheme returns the resource scheme (e.g., "https", "ipfs")
|
||||
GetScheme() string
|
||||
// GetValue returns the resource value/path
|
||||
GetValue() string
|
||||
// GetURI returns the full URI string
|
||||
GetURI() string
|
||||
// Matches checks if this resource matches another resource
|
||||
Matches(other Resource) bool
|
||||
}
|
||||
|
||||
// SimpleCapability implements Capability for single actions
|
||||
type SimpleCapability struct {
|
||||
Action string `json:"action"`
|
||||
}
|
||||
|
||||
// GetActions returns the single action
|
||||
func (c *SimpleCapability) GetActions() []string {
|
||||
return []string{c.Action}
|
||||
}
|
||||
|
||||
// Grants checks if the capability grants all required abilities
|
||||
func (c *SimpleCapability) Grants(abilities []string) bool {
|
||||
if len(abilities) != 1 {
|
||||
return false
|
||||
}
|
||||
return c.Action == abilities[0] || c.Action == "*"
|
||||
}
|
||||
|
||||
// Contains checks if this capability contains another capability
|
||||
func (c *SimpleCapability) Contains(other Capability) bool {
|
||||
if c.Action == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
otherActions := other.GetActions()
|
||||
if len(otherActions) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
return c.Action == otherActions[0]
|
||||
}
|
||||
|
||||
// String returns string representation
|
||||
func (c *SimpleCapability) String() string {
|
||||
return c.Action
|
||||
}
|
||||
|
||||
// MultiCapability implements Capability for multiple actions
|
||||
type MultiCapability struct {
|
||||
Actions []string `json:"actions"`
|
||||
}
|
||||
|
||||
// GetActions returns all actions
|
||||
func (c *MultiCapability) GetActions() []string {
|
||||
return c.Actions
|
||||
}
|
||||
|
||||
// Grants checks if the capability grants all required abilities
|
||||
func (c *MultiCapability) Grants(abilities []string) bool {
|
||||
actionSet := make(map[string]bool)
|
||||
for _, action := range c.Actions {
|
||||
actionSet[action] = true
|
||||
}
|
||||
|
||||
// Check if we have wildcard permission
|
||||
if actionSet["*"] {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check each required ability
|
||||
for _, ability := range abilities {
|
||||
if !actionSet[ability] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Contains checks if this capability contains another capability
|
||||
func (c *MultiCapability) Contains(other Capability) bool {
|
||||
actionSet := make(map[string]bool)
|
||||
for _, action := range c.Actions {
|
||||
actionSet[action] = true
|
||||
}
|
||||
|
||||
// Wildcard contains everything
|
||||
if actionSet["*"] {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if all other actions are contained
|
||||
for _, otherAction := range other.GetActions() {
|
||||
if !actionSet[otherAction] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// String returns string representation
|
||||
func (c *MultiCapability) String() string {
|
||||
return strings.Join(c.Actions, ",")
|
||||
}
|
||||
|
||||
// SimpleResource implements Resource for basic URI resources
|
||||
type SimpleResource struct {
|
||||
Scheme string `json:"scheme"`
|
||||
Value string `json:"value"`
|
||||
URI string `json:"uri"`
|
||||
}
|
||||
|
||||
// GetScheme returns the resource scheme
|
||||
func (r *SimpleResource) GetScheme() string {
|
||||
return r.Scheme
|
||||
}
|
||||
|
||||
// GetValue returns the resource value
|
||||
func (r *SimpleResource) GetValue() string {
|
||||
return r.Value
|
||||
}
|
||||
|
||||
// GetURI returns the full URI
|
||||
func (r *SimpleResource) GetURI() string {
|
||||
return r.URI
|
||||
}
|
||||
|
||||
// Matches checks if resources are equivalent
|
||||
func (r *SimpleResource) Matches(other Resource) bool {
|
||||
return r.URI == other.GetURI()
|
||||
}
|
||||
|
||||
// VaultResource represents vault-specific resources with metadata
|
||||
type VaultResource struct {
|
||||
SimpleResource
|
||||
VaultAddress string `json:"vault_address,omitempty"`
|
||||
EnclaveDataCID string `json:"enclave_data_cid,omitempty"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// ServiceResource represents service-specific resources
|
||||
type ServiceResource struct {
|
||||
SimpleResource
|
||||
ServiceID string `json:"service_id"`
|
||||
Domain string `json:"domain"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// CreateSimpleAttenuation creates a basic attenuation
|
||||
func CreateSimpleAttenuation(action, resourceURI string) Attenuation {
|
||||
return Attenuation{
|
||||
Capability: &SimpleCapability{Action: action},
|
||||
Resource: parseResourceURI(resourceURI),
|
||||
}
|
||||
}
|
||||
|
||||
// CreateMultiAttenuation creates an attenuation with multiple actions
|
||||
func CreateMultiAttenuation(actions []string, resourceURI string) Attenuation {
|
||||
return Attenuation{
|
||||
Capability: &MultiCapability{Actions: actions},
|
||||
Resource: parseResourceURI(resourceURI),
|
||||
}
|
||||
}
|
||||
|
||||
// CreateVaultAttenuation creates a vault-specific attenuation
|
||||
func CreateVaultAttenuation(actions []string, enclaveDataCID, vaultAddress string) Attenuation {
|
||||
resource := &VaultResource{
|
||||
SimpleResource: SimpleResource{
|
||||
Scheme: "ipfs",
|
||||
Value: enclaveDataCID,
|
||||
URI: fmt.Sprintf("ipfs://%s", enclaveDataCID),
|
||||
},
|
||||
VaultAddress: vaultAddress,
|
||||
EnclaveDataCID: enclaveDataCID,
|
||||
}
|
||||
|
||||
return Attenuation{
|
||||
Capability: &MultiCapability{Actions: actions},
|
||||
Resource: resource,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateServiceAttenuation creates a service-specific attenuation
|
||||
func CreateServiceAttenuation(actions []string, serviceID, domain string) Attenuation {
|
||||
resourceURI := fmt.Sprintf("service://%s", serviceID)
|
||||
resource := &ServiceResource{
|
||||
SimpleResource: SimpleResource{
|
||||
Scheme: "service",
|
||||
Value: serviceID,
|
||||
URI: resourceURI,
|
||||
},
|
||||
ServiceID: serviceID,
|
||||
Domain: domain,
|
||||
}
|
||||
|
||||
return Attenuation{
|
||||
Capability: &MultiCapability{Actions: actions},
|
||||
Resource: resource,
|
||||
}
|
||||
}
|
||||
|
||||
// parseResourceURI creates a Resource from URI string
|
||||
func parseResourceURI(uri string) Resource {
|
||||
parts := strings.SplitN(uri, "://", 2)
|
||||
if len(parts) != 2 {
|
||||
return &SimpleResource{
|
||||
Scheme: "unknown",
|
||||
Value: uri,
|
||||
URI: uri,
|
||||
}
|
||||
}
|
||||
|
||||
return &SimpleResource{
|
||||
Scheme: parts[0],
|
||||
Value: parts[1],
|
||||
URI: uri,
|
||||
}
|
||||
}
|
||||
|
||||
// CapabilityTemplate provides validation and construction utilities
|
||||
type CapabilityTemplate struct {
|
||||
AllowedActions map[string][]string `json:"allowed_actions"` // resource_type -> []actions
|
||||
DefaultExpiration time.Duration `json:"default_expiration"` // default token lifetime
|
||||
MaxExpiration time.Duration `json:"max_expiration"` // maximum allowed lifetime
|
||||
}
|
||||
|
||||
// NewCapabilityTemplate creates a new capability template
|
||||
func NewCapabilityTemplate() *CapabilityTemplate {
|
||||
return &CapabilityTemplate{
|
||||
AllowedActions: make(map[string][]string),
|
||||
DefaultExpiration: 24 * time.Hour,
|
||||
MaxExpiration: 30 * 24 * time.Hour, // 30 days
|
||||
}
|
||||
}
|
||||
|
||||
// AddAllowedActions adds allowed actions for a resource type
|
||||
func (ct *CapabilityTemplate) AddAllowedActions(resourceType string, actions []string) {
|
||||
ct.AllowedActions[resourceType] = actions
|
||||
}
|
||||
|
||||
// ValidateAttenuation validates an attenuation against the template
|
||||
func (ct *CapabilityTemplate) ValidateAttenuation(att Attenuation) error {
|
||||
resourceType := att.Resource.GetScheme()
|
||||
allowedActions, exists := ct.AllowedActions[resourceType]
|
||||
|
||||
if !exists {
|
||||
// Allow unknown resource types for backward compatibility
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create action set for efficient lookup
|
||||
actionSet := make(map[string]bool)
|
||||
for _, action := range allowedActions {
|
||||
actionSet[action] = true
|
||||
}
|
||||
|
||||
// Check if all capability actions are allowed
|
||||
for _, action := range att.Capability.GetActions() {
|
||||
if action == "*" {
|
||||
// Wildcard requires explicit permission
|
||||
if !actionSet["*"] {
|
||||
return fmt.Errorf("wildcard action not allowed for resource type %s", resourceType)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !actionSet[action] {
|
||||
return fmt.Errorf("action %s not allowed for resource type %s", action, resourceType)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateExpiration validates token expiration time
|
||||
func (ct *CapabilityTemplate) ValidateExpiration(expiresAt int64) error {
|
||||
if expiresAt == 0 {
|
||||
return nil // No expiration is allowed
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
expiry := time.Unix(expiresAt, 0)
|
||||
|
||||
if expiry.Before(now) {
|
||||
return fmt.Errorf("token expiration is in the past")
|
||||
}
|
||||
|
||||
if expiry.Sub(now) > ct.MaxExpiration {
|
||||
return fmt.Errorf("token expiration exceeds maximum allowed duration")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDefaultExpirationTime returns the default expiration timestamp
|
||||
func (ct *CapabilityTemplate) GetDefaultExpirationTime() int64 {
|
||||
return time.Now().Add(ct.DefaultExpiration).Unix()
|
||||
}
|
||||
|
||||
// StandardVaultTemplate returns a standard template for vault operations
|
||||
func StandardVaultTemplate() *CapabilityTemplate {
|
||||
template := NewCapabilityTemplate()
|
||||
template.AddAllowedActions(
|
||||
"ipfs",
|
||||
[]string{"read", "write", "sign", "export", "import", "delete", VaultAdminAction},
|
||||
)
|
||||
template.AddAllowedActions(
|
||||
"vault",
|
||||
[]string{"read", "write", "sign", "export", "import", "delete", "admin", "*"},
|
||||
)
|
||||
return template
|
||||
}
|
||||
|
||||
// StandardServiceTemplate returns a standard template for service operations
|
||||
func StandardServiceTemplate() *CapabilityTemplate {
|
||||
template := NewCapabilityTemplate()
|
||||
template.AddAllowedActions(
|
||||
"service",
|
||||
[]string{"read", "write", "admin", "register", "update", "delete"},
|
||||
)
|
||||
template.AddAllowedActions("https", []string{"read", "write"})
|
||||
template.AddAllowedActions("http", []string{"read", "write"})
|
||||
return template
|
||||
}
|
||||
|
||||
// AttenuationList provides utilities for working with multiple attenuations
|
||||
type AttenuationList []Attenuation
|
||||
|
||||
// Contains checks if the list contains attenuations for a specific resource
|
||||
func (al AttenuationList) Contains(resourceURI string) bool {
|
||||
for _, att := range al {
|
||||
if att.Resource.GetURI() == resourceURI {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetCapabilitiesForResource returns all capabilities for a specific resource
|
||||
func (al AttenuationList) GetCapabilitiesForResource(resourceURI string) []Capability {
|
||||
var capabilities []Capability
|
||||
for _, att := range al {
|
||||
if att.Resource.GetURI() == resourceURI {
|
||||
capabilities = append(capabilities, att.Capability)
|
||||
}
|
||||
}
|
||||
return capabilities
|
||||
}
|
||||
|
||||
// CanPerform checks if the attenuations allow specific actions on a resource
|
||||
func (al AttenuationList) CanPerform(resourceURI string, actions []string) bool {
|
||||
capabilities := al.GetCapabilitiesForResource(resourceURI)
|
||||
for _, cap := range capabilities {
|
||||
if cap.Grants(actions) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsSubsetOf checks if this list is a subset of another list
|
||||
func (al AttenuationList) IsSubsetOf(parent AttenuationList) bool {
|
||||
for _, childAtt := range al {
|
||||
if !parent.containsAttenuation(childAtt) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// containsAttenuation checks if the list contains an equivalent attenuation
|
||||
func (al AttenuationList) containsAttenuation(att Attenuation) bool {
|
||||
for _, parentAtt := range al {
|
||||
if parentAtt.Resource.Matches(att.Resource) {
|
||||
if parentAtt.Capability.Contains(att.Capability) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Module-Specific Capability Types
|
||||
|
||||
// DIDCapability implements Capability for DID module operations
|
||||
type DIDCapability struct {
|
||||
Action string `json:"action"`
|
||||
Actions []string `json:"actions,omitempty"`
|
||||
Caveats []string `json:"caveats,omitempty"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// GetActions returns the actions this DID capability grants
|
||||
func (c *DIDCapability) GetActions() []string {
|
||||
if len(c.Actions) > 0 {
|
||||
return c.Actions
|
||||
}
|
||||
return []string{c.Action}
|
||||
}
|
||||
|
||||
// Grants checks if this capability grants the required abilities
|
||||
func (c *DIDCapability) Grants(abilities []string) bool {
|
||||
if c.Action == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
grantedActions := make(map[string]bool)
|
||||
for _, action := range c.GetActions() {
|
||||
grantedActions[action] = true
|
||||
}
|
||||
|
||||
for _, ability := range abilities {
|
||||
if !grantedActions[ability] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Contains checks if this capability contains another capability
|
||||
func (c *DIDCapability) Contains(other Capability) bool {
|
||||
if c.Action == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
ourActions := make(map[string]bool)
|
||||
for _, action := range c.GetActions() {
|
||||
ourActions[action] = true
|
||||
}
|
||||
|
||||
for _, otherAction := range other.GetActions() {
|
||||
if !ourActions[otherAction] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// String returns string representation
|
||||
func (c *DIDCapability) String() string {
|
||||
if len(c.Actions) > 1 {
|
||||
return strings.Join(c.Actions, ",")
|
||||
}
|
||||
return c.Action
|
||||
}
|
||||
|
||||
// DWNCapability implements Capability for DWN module operations
|
||||
type DWNCapability struct {
|
||||
Action string `json:"action"`
|
||||
Actions []string `json:"actions,omitempty"`
|
||||
Caveats []string `json:"caveats,omitempty"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// GetActions returns the actions this DWN capability grants
|
||||
func (c *DWNCapability) GetActions() []string {
|
||||
if len(c.Actions) > 0 {
|
||||
return c.Actions
|
||||
}
|
||||
return []string{c.Action}
|
||||
}
|
||||
|
||||
// Grants checks if this capability grants the required abilities
|
||||
func (c *DWNCapability) Grants(abilities []string) bool {
|
||||
if c.Action == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
grantedActions := make(map[string]bool)
|
||||
for _, action := range c.GetActions() {
|
||||
grantedActions[action] = true
|
||||
}
|
||||
|
||||
for _, ability := range abilities {
|
||||
if !grantedActions[ability] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Contains checks if this capability contains another capability
|
||||
func (c *DWNCapability) Contains(other Capability) bool {
|
||||
if c.Action == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
ourActions := make(map[string]bool)
|
||||
for _, action := range c.GetActions() {
|
||||
ourActions[action] = true
|
||||
}
|
||||
|
||||
for _, otherAction := range other.GetActions() {
|
||||
if !ourActions[otherAction] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// String returns string representation
|
||||
func (c *DWNCapability) String() string {
|
||||
if len(c.Actions) > 1 {
|
||||
return strings.Join(c.Actions, ",")
|
||||
}
|
||||
return c.Action
|
||||
}
|
||||
|
||||
// DEXCapability implements Capability for DEX module operations
|
||||
type DEXCapability struct {
|
||||
Action string `json:"action"`
|
||||
Actions []string `json:"actions,omitempty"`
|
||||
Caveats []string `json:"caveats,omitempty"`
|
||||
MaxAmount string `json:"max_amount,omitempty"` // For swap limits
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// GetActions returns the actions this DEX capability grants
|
||||
func (c *DEXCapability) GetActions() []string {
|
||||
if len(c.Actions) > 0 {
|
||||
return c.Actions
|
||||
}
|
||||
return []string{c.Action}
|
||||
}
|
||||
|
||||
// Grants checks if this capability grants the required abilities
|
||||
func (c *DEXCapability) Grants(abilities []string) bool {
|
||||
if c.Action == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
grantedActions := make(map[string]bool)
|
||||
for _, action := range c.GetActions() {
|
||||
grantedActions[action] = true
|
||||
}
|
||||
|
||||
for _, ability := range abilities {
|
||||
if !grantedActions[ability] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Contains checks if this capability contains another capability
|
||||
func (c *DEXCapability) Contains(other Capability) bool {
|
||||
if c.Action == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
ourActions := make(map[string]bool)
|
||||
for _, action := range c.GetActions() {
|
||||
ourActions[action] = true
|
||||
}
|
||||
|
||||
for _, otherAction := range other.GetActions() {
|
||||
if !ourActions[otherAction] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// String returns string representation
|
||||
func (c *DEXCapability) String() string {
|
||||
if len(c.Actions) > 1 {
|
||||
return strings.Join(c.Actions, ",")
|
||||
}
|
||||
return c.Action
|
||||
}
|
||||
|
||||
// Module-Specific Resource Types
|
||||
|
||||
// DIDResource represents DID-specific resources
|
||||
type DIDResource struct {
|
||||
SimpleResource
|
||||
DIDMethod string `json:"did_method,omitempty"`
|
||||
DIDSubject string `json:"did_subject,omitempty"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// DWNResource represents DWN-specific resources
|
||||
type DWNResource struct {
|
||||
SimpleResource
|
||||
RecordType string `json:"record_type,omitempty"`
|
||||
Protocol string `json:"protocol,omitempty"`
|
||||
Owner string `json:"owner,omitempty"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// DEXResource represents DEX-specific resources
|
||||
type DEXResource struct {
|
||||
SimpleResource
|
||||
PoolID string `json:"pool_id,omitempty"`
|
||||
AssetPair string `json:"asset_pair,omitempty"`
|
||||
OrderID string `json:"order_id,omitempty"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// Enhanced ServiceResource adds delegation capabilities
|
||||
func (r *ServiceResource) SupportsDelegate() bool {
|
||||
return r.Metadata != nil && r.Metadata["supports_delegation"] == "true"
|
||||
}
|
||||
|
||||
// Module-Specific Capability Templates
|
||||
|
||||
// StandardDIDTemplate returns a standard template for DID operations
|
||||
func StandardDIDTemplate() *CapabilityTemplate {
|
||||
template := NewCapabilityTemplate()
|
||||
template.AddAllowedActions("did", []string{
|
||||
"create", "register", "update", "deactivate", "revoke",
|
||||
"add-verification-method", "remove-verification-method",
|
||||
"add-service", "remove-service", "issue-credential",
|
||||
"revoke-credential", "link-wallet", "register-webauthn", "*",
|
||||
})
|
||||
return template
|
||||
}
|
||||
|
||||
// StandardDWNTemplate returns a standard template for DWN operations
|
||||
func StandardDWNTemplate() *CapabilityTemplate {
|
||||
template := NewCapabilityTemplate()
|
||||
template.AddAllowedActions("dwn", []string{
|
||||
"records-write", "records-delete", "protocols-configure",
|
||||
"permissions-grant", "permissions-revoke", "create", "read",
|
||||
"update", "delete", "*",
|
||||
})
|
||||
return template
|
||||
}
|
||||
|
||||
// EnhancedServiceTemplate returns enhanced service template with delegation support
|
||||
func EnhancedServiceTemplate() *CapabilityTemplate {
|
||||
template := NewCapabilityTemplate()
|
||||
template.AddAllowedActions("service", []string{
|
||||
"register", "update", "delete", "verify-domain",
|
||||
"initiate-domain-verification", "delegate", "*",
|
||||
})
|
||||
template.AddAllowedActions("svc", []string{
|
||||
"register", "verify-domain", "delegate", "*",
|
||||
})
|
||||
template.AddAllowedActions("https", []string{"read", "write"})
|
||||
template.AddAllowedActions("http", []string{"read", "write"})
|
||||
return template
|
||||
}
|
||||
|
||||
// StandardDEXTemplate returns a standard template for DEX operations
|
||||
func StandardDEXTemplate() *CapabilityTemplate {
|
||||
template := NewCapabilityTemplate()
|
||||
template.AddAllowedActions("dex", []string{
|
||||
"register-account", "swap", "provide-liquidity", "remove-liquidity",
|
||||
"create-limit-order", "cancel-order", "*",
|
||||
})
|
||||
template.AddAllowedActions("pool", []string{
|
||||
"swap", "provide-liquidity", "remove-liquidity", "*",
|
||||
})
|
||||
return template
|
||||
}
|
||||
|
||||
// Module-Specific Attenuation Constructors
|
||||
|
||||
// CreateDIDAttenuation creates a DID-specific attenuation
|
||||
func CreateDIDAttenuation(actions []string, didPattern string, caveats []string) Attenuation {
|
||||
resourceURI := fmt.Sprintf("did:%s", didPattern)
|
||||
resource := &DIDResource{
|
||||
SimpleResource: SimpleResource{
|
||||
Scheme: "did",
|
||||
Value: didPattern,
|
||||
URI: resourceURI,
|
||||
},
|
||||
}
|
||||
|
||||
return Attenuation{
|
||||
Capability: &DIDCapability{
|
||||
Actions: actions,
|
||||
Caveats: caveats,
|
||||
},
|
||||
Resource: resource,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateDWNAttenuation creates a DWN-specific attenuation
|
||||
func CreateDWNAttenuation(actions []string, recordPattern string, caveats []string) Attenuation {
|
||||
resourceURI := fmt.Sprintf("dwn:records/%s", recordPattern)
|
||||
resource := &DWNResource{
|
||||
SimpleResource: SimpleResource{
|
||||
Scheme: "dwn",
|
||||
Value: fmt.Sprintf("records/%s", recordPattern),
|
||||
URI: resourceURI,
|
||||
},
|
||||
RecordType: recordPattern,
|
||||
}
|
||||
|
||||
return Attenuation{
|
||||
Capability: &DWNCapability{
|
||||
Actions: actions,
|
||||
Caveats: caveats,
|
||||
},
|
||||
Resource: resource,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateDEXAttenuation creates a DEX-specific attenuation
|
||||
func CreateDEXAttenuation(actions []string, poolPattern string, caveats []string, maxAmount string) Attenuation {
|
||||
resourceURI := fmt.Sprintf("dex:pool/%s", poolPattern)
|
||||
resource := &DEXResource{
|
||||
SimpleResource: SimpleResource{
|
||||
Scheme: "dex",
|
||||
Value: fmt.Sprintf("pool/%s", poolPattern),
|
||||
URI: resourceURI,
|
||||
},
|
||||
PoolID: poolPattern,
|
||||
}
|
||||
|
||||
return Attenuation{
|
||||
Capability: &DEXCapability{
|
||||
Actions: actions,
|
||||
Caveats: caveats,
|
||||
MaxAmount: maxAmount,
|
||||
},
|
||||
Resource: resource,
|
||||
}
|
||||
}
|
||||
|
||||
// Cross-Module Capability Composition
|
||||
|
||||
// CrossModuleCapability allows composing capabilities across modules
|
||||
type CrossModuleCapability struct {
|
||||
Modules map[string]Capability `json:"modules"`
|
||||
}
|
||||
|
||||
// GetActions returns all actions across all modules
|
||||
func (c *CrossModuleCapability) GetActions() []string {
|
||||
var actions []string
|
||||
for _, cap := range c.Modules {
|
||||
actions = append(actions, cap.GetActions()...)
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
// Grants checks if required abilities are granted across modules
|
||||
func (c *CrossModuleCapability) Grants(abilities []string) bool {
|
||||
allActions := make(map[string]bool)
|
||||
for _, cap := range c.Modules {
|
||||
for _, action := range cap.GetActions() {
|
||||
allActions[action] = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, ability := range abilities {
|
||||
if !allActions[ability] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Contains checks if this cross-module capability contains another
|
||||
func (c *CrossModuleCapability) Contains(other Capability) bool {
|
||||
// For cross-module capabilities, check each module
|
||||
if otherCross, ok := other.(*CrossModuleCapability); ok {
|
||||
for module, otherCap := range otherCross.Modules {
|
||||
if ourCap, exists := c.Modules[module]; exists {
|
||||
if !ourCap.Contains(otherCap) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// For single capabilities, check if any module contains it
|
||||
for _, cap := range c.Modules {
|
||||
if cap.Contains(other) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// String returns string representation
|
||||
func (c *CrossModuleCapability) String() string {
|
||||
var moduleStrs []string
|
||||
for module, cap := range c.Modules {
|
||||
moduleStrs = append(moduleStrs, fmt.Sprintf("%s:%s", module, cap.String()))
|
||||
}
|
||||
return strings.Join(moduleStrs, ";")
|
||||
}
|
||||
|
||||
// Gasless Transaction Support
|
||||
|
||||
// GaslessCapability wraps other capabilities with gasless transaction support
|
||||
type GaslessCapability struct {
|
||||
Capability
|
||||
AllowGasless bool `json:"allow_gasless"`
|
||||
GasLimit uint64 `json:"gas_limit,omitempty"`
|
||||
}
|
||||
|
||||
// SupportsGasless returns whether this capability supports gasless transactions
|
||||
func (c *GaslessCapability) SupportsGasless() bool {
|
||||
return c.AllowGasless
|
||||
}
|
||||
|
||||
// GetGasLimit returns the gas limit for gasless transactions
|
||||
func (c *GaslessCapability) GetGasLimit() uint64 {
|
||||
return c.GasLimit
|
||||
}
|
||||
Reference in New Issue
Block a user