Files
sonr/test/integration/cross_module_auth_test.go
T
Prad NukalaandGitHub 13e6c3e84d Master (#1262)
* clear

* feat: Add everything

* fix: Commenht
2025-10-03 14:45:52 -04:00

821 lines
26 KiB
Go

package integration
import (
"context"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/sonr-io/sonr/bridge/handlers"
"github.com/sonr-io/sonr/crypto/ucan"
didtypes "github.com/sonr-io/sonr/x/did/types"
dwntypes "github.com/sonr-io/sonr/x/dwn/types"
svctypes "github.com/sonr-io/sonr/x/svc/types"
)
// CrossModuleAuthTestSuite tests authorization across all Sonr modules
type CrossModuleAuthTestSuite struct {
suite.Suite
ucanDelegator *handlers.UCANDelegator
signer *MockUCANSigner
// Mock module keepers
didValidator *MockDIDValidator
dwnValidator *MockDWNValidator
svcValidator *MockServiceValidator
testUserDID string
testClientDID string
}
// MockDIDKeeper implements DIDKeeperInterface for testing
type MockDIDKeeper struct {
documents map[string]*didtypes.DIDDocument
}
func (m *MockDIDKeeper) GetDIDDocument(ctx context.Context, did string) (*didtypes.DIDDocument, error) {
doc, ok := m.documents[did]
if !ok {
return nil, fmt.Errorf("DID not found: %s", did)
}
return doc, nil
}
func (m *MockDIDKeeper) GetVerificationMethod(ctx context.Context, did string, methodID string) (*didtypes.VerificationMethod, error) {
doc, err := m.GetDIDDocument(ctx, did)
if err != nil {
return nil, err
}
for _, vm := range doc.VerificationMethod {
if vm.Id == methodID {
return vm, nil
}
}
return nil, fmt.Errorf("verification method not found: %s", methodID)
}
// MockUCANSigner is a test implementation that bypasses actual crypto
type MockUCANSigner struct {
issuerDID string
didKeeper *MockDIDKeeper
tokens map[string]*ucan.Token // Store tokens by raw string
}
func (m *MockUCANSigner) Sign(token *ucan.Token) (string, error) {
// Generate a fake JWT-like token for testing
tokenStr := fmt.Sprintf("mock.token.%s.%s.%d", token.Issuer, token.Audience, time.Now().Unix())
token.Raw = tokenStr
// Store the token for later verification
if m.tokens == nil {
m.tokens = make(map[string]*ucan.Token)
}
m.tokens[tokenStr] = token
return tokenStr, nil
}
func (m *MockUCANSigner) GetIssuerDID() string {
return m.issuerDID
}
func (m *MockUCANSigner) CreateDelegationToken(
issuer string,
audience string,
attenuations []ucan.Attenuation,
proofs []ucan.Proof,
expiry time.Duration,
) (string, error) {
token := &ucan.Token{
Issuer: issuer,
Audience: audience,
ExpiresAt: time.Now().Add(expiry).Unix(),
NotBefore: time.Now().Unix(),
Attenuations: attenuations,
Proofs: proofs,
}
return m.Sign(token)
}
func (m *MockUCANSigner) VerifySignature(tokenString string) (*ucan.Token, error) {
// For testing, just return the stored token
if token, ok := m.tokens[tokenString]; ok {
// Check expiry - tokens expire when current time exceeds ExpiresAt
currentTime := time.Now().Unix()
if currentTime > token.ExpiresAt {
return nil, fmt.Errorf("token expired (current: %d, expires: %d)", currentTime, token.ExpiresAt)
}
// Check not before
if token.NotBefore > 0 && currentTime < token.NotBefore {
return nil, fmt.Errorf("token not yet valid")
}
return token, nil
}
return nil, fmt.Errorf("token not found: %s", tokenString)
}
func (m *MockUCANSigner) ValidateDelegationChain(tokens []string) error {
// Validate the delegation chain
for i, tokenStr := range tokens {
token, ok := m.tokens[tokenStr]
if !ok {
return fmt.Errorf("failed to verify token %d: token not found", i)
}
// Check chain consistency (except for first token)
if i > 0 {
prevToken := m.tokens[tokens[i-1]]
// Verify that the issuer of current token matches audience of previous
if token.Issuer != prevToken.Audience {
return fmt.Errorf("failed to verify token %d: broken chain - issuer mismatch", i)
}
// Check that current token has previous token as proof
hasProof := false
for _, proof := range token.Proofs {
if string(proof) == tokens[i-1] {
hasProof = true
break
}
}
if !hasProof && len(token.Proofs) > 0 {
// If there are proofs but wrong ones, it's a broken chain
return fmt.Errorf("failed to verify token %d: invalid proof in chain", i)
}
}
}
return nil
}
// MockDIDValidator simulates DID module UCAN validation
type MockDIDValidator struct {
didDocuments map[string]*didtypes.DIDDocument
}
func NewMockDIDValidator() *MockDIDValidator {
return &MockDIDValidator{
didDocuments: make(map[string]*didtypes.DIDDocument),
}
}
func (m *MockDIDValidator) ValidateUCANPermission(ctx context.Context, token *ucan.Token, action string, resource string) error {
// Map common action aliases
actionMap := map[string]string{
"update": "write",
"create": "write",
"delete": "write",
}
// Normalize the action
normalizedAction := action
if mapped, exists := actionMap[action]; exists {
normalizedAction = mapped
}
// Find appropriate attenuation for DID operations
for _, att := range token.Attenuations {
if att.Resource != nil && att.Resource.GetScheme() == "did" {
if att.Capability != nil {
actions := att.Capability.GetActions()
for _, a := range actions {
if a == normalizedAction || a == action || a == "*" || a == "write" && (action == "update" || action == "create") {
return nil // Authorized
}
}
}
}
}
return handlers.ErrUnauthorized
}
func (m *MockDIDValidator) GetDIDDocument(ctx context.Context, did string) (*didtypes.DIDDocument, error) {
doc, exists := m.didDocuments[did]
if !exists {
return nil, didtypes.ErrDIDNotFound
}
return doc, nil
}
// MockDWNValidator simulates DWN module UCAN validation
type MockDWNValidator struct {
records map[string]*dwntypes.DWNRecord
}
func NewMockDWNValidator() *MockDWNValidator {
return &MockDWNValidator{
records: make(map[string]*dwntypes.DWNRecord),
}
}
func (m *MockDWNValidator) ValidateUCANPermission(ctx context.Context, token *ucan.Token, action string, resource string) error {
// Map common action aliases
actionMap := map[string]string{
"records-write": "write",
"protocols-configure": "admin",
}
// Normalize the action
normalizedAction := action
if mapped, exists := actionMap[action]; exists {
normalizedAction = mapped
}
// Find appropriate attenuation for DWN operations
for _, att := range token.Attenuations {
if att.Resource != nil && att.Resource.GetScheme() == "dwn" {
if att.Capability != nil {
actions := att.Capability.GetActions()
for _, a := range actions {
if a == normalizedAction || a == action || a == "*" ||
(a == "write" && action == "records-write") ||
(a == "admin" && action == "protocols-configure") {
// Additional DWN-specific validation
return m.validateDWNSpecific(token, action, resource)
}
}
}
}
}
return handlers.ErrUnauthorized
}
func (m *MockDWNValidator) validateDWNSpecific(token *ucan.Token, action, resource string) error {
// DWN-specific business logic
switch action {
case "records-write", "write":
// Check if user has write permission to this DWN
if token.Issuer == "did:sonr:unauthorized" {
return handlers.ErrInsufficientScope
}
case "protocols-configure":
// Only DWN owner can configure protocols
if !m.isOwner(token.Issuer, resource) {
return handlers.ErrUnauthorized
}
}
return nil
}
func (m *MockDWNValidator) isOwner(userDID, dwnResource string) bool {
// Simplified ownership check
return userDID == "did:sonr:owner" || userDID == "did:sonr:test-user"
}
// MockServiceValidator simulates Service module UCAN validation
type MockServiceValidator struct {
services map[string]*svctypes.Service
}
func NewMockServiceValidator() *MockServiceValidator {
return &MockServiceValidator{
services: make(map[string]*svctypes.Service),
}
}
func (m *MockServiceValidator) ValidateUCANPermission(ctx context.Context, token *ucan.Token, action string, resource string) error {
// Map common action aliases
actionMap := map[string]string{
"register": "write",
"verify-domain": "write",
"delete": "admin",
"read": "read",
}
// Normalize the action
normalizedAction := action
if mapped, exists := actionMap[action]; exists {
normalizedAction = mapped
}
// Find appropriate attenuation for service operations
for _, att := range token.Attenuations {
if att.Resource != nil {
scheme := att.Resource.GetScheme()
if scheme == "service" || scheme == "svc" {
if att.Capability != nil {
actions := att.Capability.GetActions()
for _, a := range actions {
if a == normalizedAction || a == action || a == "*" ||
(a == "write" && (action == "register" || action == "verify-domain")) ||
(a == "admin" && action == "delete") ||
(a == "manage" && (action == "register" || action == "verify-domain")) {
return m.validateServiceSpecific(token, action, resource)
}
}
}
}
}
}
return handlers.ErrUnauthorized
}
func (m *MockServiceValidator) validateServiceSpecific(token *ucan.Token, action, resource string) error {
// Service-specific validation logic
switch action {
case "register":
// Anyone can register a service with valid DID
if token.Issuer == "" {
return handlers.ErrUnauthorized
}
case "verify-domain":
// Only service owner can verify domain
service, exists := m.services[resource]
if !exists || service.Owner != token.Issuer {
return handlers.ErrUnauthorized
}
case "delete", "admin":
// Admin actions require admin scope
return m.validateAdminAccess(token)
}
return nil
}
func (m *MockServiceValidator) validateAdminAccess(token *ucan.Token) error {
// Check for admin capabilities
for _, att := range token.Attenuations {
actions := att.Capability.GetActions()
for _, action := range actions {
if action == "admin" || action == "*" {
return nil
}
}
}
return handlers.ErrInsufficientScope
}
func (suite *CrossModuleAuthTestSuite) SetupSuite() {
// Initialize validators
suite.didValidator = NewMockDIDValidator()
suite.dwnValidator = NewMockDWNValidator()
suite.svcValidator = NewMockServiceValidator()
// Initialize UCAN components with mock DID keeper
mockDIDKeeper := &MockDIDKeeper{
documents: make(map[string]*didtypes.DIDDocument),
}
// Add test user DID document with verification method
mockDIDKeeper.documents["did:sonr:test-user"] = &didtypes.DIDDocument{
Id: "did:sonr:test-user",
VerificationMethod: []*didtypes.VerificationMethod{
{
Id: "did:sonr:test-user#key1",
VerificationMethodKind: "Ed25519VerificationKey2020",
PublicKeyMultibase: "dGVzdC1wdWJsaWMta2V5", // base64 encoded test key
},
},
}
mockDIDKeeper.documents["did:sonr:owner"] = &didtypes.DIDDocument{
Id: "did:sonr:owner",
VerificationMethod: []*didtypes.VerificationMethod{
{
Id: "did:sonr:owner#key1",
VerificationMethodKind: "Ed25519VerificationKey2020",
PublicKeyMultibase: "b3duZXItcHVibGljLWtleQ==", // base64 encoded test key
},
},
}
// Add other test DIDs used in the tests
mockDIDKeeper.documents["did:sonr:service-a"] = &didtypes.DIDDocument{
Id: "did:sonr:service-a",
VerificationMethod: []*didtypes.VerificationMethod{
{
Id: "did:sonr:service-a#key1",
VerificationMethodKind: "Ed25519VerificationKey2020",
PublicKeyMultibase: "c2VydmljZS1hLWtleQ==", // base64 encoded test key
},
},
}
mockDIDKeeper.documents["did:sonr:service-b"] = &didtypes.DIDDocument{
Id: "did:sonr:service-b",
VerificationMethod: []*didtypes.VerificationMethod{
{
Id: "did:sonr:service-b#key1",
VerificationMethodKind: "Ed25519VerificationKey2020",
PublicKeyMultibase: "c2VydmljZS1iLWtleQ==", // base64 encoded test key
},
},
}
mockDIDKeeper.documents["did:sonr:intermediate-client"] = &didtypes.DIDDocument{
Id: "did:sonr:intermediate-client",
VerificationMethod: []*didtypes.VerificationMethod{
{
Id: "did:sonr:intermediate-client#key1",
VerificationMethodKind: "Ed25519VerificationKey2020",
PublicKeyMultibase: "aW50ZXJtZWRpYXRlLWtleQ==", // base64 encoded test key
},
},
}
mockDIDKeeper.documents["did:sonr:non-owner"] = &didtypes.DIDDocument{
Id: "did:sonr:non-owner",
VerificationMethod: []*didtypes.VerificationMethod{
{
Id: "did:sonr:non-owner#key1",
VerificationMethodKind: "Ed25519VerificationKey2020",
PublicKeyMultibase: "bm9uLW93bmVyLWtleQ==", // base64 encoded test key
},
},
}
mockDIDKeeper.documents["did:sonr:different-user"] = &didtypes.DIDDocument{
Id: "did:sonr:different-user",
VerificationMethod: []*didtypes.VerificationMethod{
{
Id: "did:sonr:different-user#key1",
VerificationMethodKind: "Ed25519VerificationKey2020",
PublicKeyMultibase: "ZGlmZmVyZW50LXVzZXI=", // base64 encoded test key
},
},
}
// Use a mock signer for testing that bypasses actual crypto
mockSigner := &MockUCANSigner{
issuerDID: "did:sonr:oauth-provider",
didKeeper: mockDIDKeeper,
}
suite.signer = mockSigner
suite.ucanDelegator = handlers.NewUCANDelegator(mockSigner)
// Test identities
suite.testUserDID = "did:sonr:test-user"
suite.testClientDID = "did:sonr:test-client"
// Setup test data
suite.setupTestData()
}
func (suite *CrossModuleAuthTestSuite) setupTestData() {
// Add test DID document
suite.didValidator.didDocuments[suite.testUserDID] = &didtypes.DIDDocument{
Id: suite.testUserDID,
VerificationMethod: []*didtypes.VerificationMethod{
{
Id: suite.testUserDID + "#key1",
VerificationMethodKind: "Ed25519VerificationKey2020",
},
},
}
// Add test service
suite.svcValidator.services["test-service"] = &svctypes.Service{
Id: "test-service",
Owner: suite.testUserDID,
Domain: "example.com",
Status: svctypes.ServiceStatus_SERVICE_STATUS_ACTIVE,
}
}
// TestDIDModuleAuthorization tests DID-specific operations
func (suite *CrossModuleAuthTestSuite) TestDIDModuleAuthorization() {
ctx := context.Background()
// Create token with DID permissions
token, err := suite.ucanDelegator.CreateDelegation(
suite.testUserDID,
suite.testClientDID,
[]string{"did:read", "did:write"},
time.Now().Add(time.Hour),
)
suite.Require().NoError(err)
// Test authorized DID read
err = suite.didValidator.ValidateUCANPermission(ctx, token, "read", suite.testUserDID)
suite.NoError(err, "Should authorize DID read operation")
// Test authorized DID write
err = suite.didValidator.ValidateUCANPermission(ctx, token, "update", suite.testUserDID)
suite.NoError(err, "Should authorize DID update operation")
// Test unauthorized DID admin operation
err = suite.didValidator.ValidateUCANPermission(ctx, token, "admin", suite.testUserDID)
suite.Error(err, "Should reject unauthorized admin operation")
}
// TestDWNModuleAuthorization tests DWN-specific operations
func (suite *CrossModuleAuthTestSuite) TestDWNModuleAuthorization() {
ctx := context.Background()
// Create token with DWN permissions
token, err := suite.ucanDelegator.CreateDelegation(
suite.testUserDID,
suite.testClientDID,
[]string{"dwn:read", "dwn:write"},
time.Now().Add(time.Hour),
)
suite.Require().NoError(err)
// Test authorized DWN write
err = suite.dwnValidator.ValidateUCANPermission(ctx, token, "records-write", "dwn:"+suite.testUserDID)
suite.NoError(err, "Should authorize DWN records write")
// Test unauthorized DWN protocol configuration
err = suite.dwnValidator.ValidateUCANPermission(ctx, token, "protocols-configure", "dwn:"+suite.testUserDID)
suite.Error(err, "Should reject protocol configuration without admin scope")
// Create admin token
adminToken, err := suite.ucanDelegator.CreateDelegation(
suite.testUserDID,
suite.testClientDID,
[]string{"dwn:admin"},
time.Now().Add(time.Hour),
)
suite.Require().NoError(err)
// Test authorized protocol configuration with admin token
err = suite.dwnValidator.ValidateUCANPermission(ctx, adminToken, "protocols-configure", "dwn:"+suite.testUserDID)
suite.NoError(err, "Should authorize protocol configuration with admin scope")
}
// TestServiceModuleAuthorization tests Service-specific operations
func (suite *CrossModuleAuthTestSuite) TestServiceModuleAuthorization() {
ctx := context.Background()
// Create token with service permissions
token, err := suite.ucanDelegator.CreateDelegation(
suite.testUserDID,
suite.testClientDID,
[]string{"service:read", "service:write"},
time.Now().Add(time.Hour),
)
suite.Require().NoError(err)
// Test authorized service registration
err = suite.svcValidator.ValidateUCANPermission(ctx, token, "register", "new-service")
suite.NoError(err, "Should authorize service registration")
// Test authorized domain verification (owner)
err = suite.svcValidator.ValidateUCANPermission(ctx, token, "verify-domain", "test-service")
suite.NoError(err, "Should authorize domain verification by owner")
// Test unauthorized admin operation
err = suite.svcValidator.ValidateUCANPermission(ctx, token, "admin", "test-service")
suite.Error(err, "Should reject admin operation without admin scope")
}
// TestCrossModuleWorkflow tests a workflow spanning multiple modules
func (suite *CrossModuleAuthTestSuite) TestCrossModuleWorkflow() {
ctx := context.Background()
// Scenario: User registers a service, configures DWN, and updates DID
// Create comprehensive token with cross-module permissions
token, err := suite.ucanDelegator.CreateDelegation(
suite.testUserDID,
suite.testClientDID,
[]string{
"service:write",
"dwn:write",
"did:write",
"vault:read", // For accessing keys
},
time.Now().Add(time.Hour),
)
suite.Require().NoError(err)
// Step 1: Register new service
err = suite.svcValidator.ValidateUCANPermission(ctx, token, "register", "new-service-workflow")
suite.NoError(err, "Should authorize service registration in workflow")
// Step 2: Configure DWN for the service
err = suite.dwnValidator.ValidateUCANPermission(ctx, token, "records-write", "dwn:"+suite.testUserDID)
suite.NoError(err, "Should authorize DWN configuration in workflow")
// Step 3: Update DID document to include service
err = suite.didValidator.ValidateUCANPermission(ctx, token, "update", suite.testUserDID)
suite.NoError(err, "Should authorize DID update in workflow")
// Verify token contains all necessary capabilities
suite.Require().NotEmpty(token.Attenuations)
// Count module coverage
modules := make(map[string]bool)
for _, att := range token.Attenuations {
modules[att.Resource.GetScheme()] = true
}
suite.GreaterOrEqual(len(modules), 3, "Token should cover at least 3 modules")
suite.Contains(modules, "service")
suite.Contains(modules, "dwn")
suite.Contains(modules, "did")
}
// TestScopeAttenuation tests proper scope reduction in delegation chains
func (suite *CrossModuleAuthTestSuite) TestScopeAttenuation() {
ctx := context.Background()
// Create initial broad token
parentToken, err := suite.ucanDelegator.CreateDelegation(
suite.testUserDID,
"did:sonr:intermediate-client",
[]string{
"vault:admin", // Full vault access
"service:manage", // Full service access
"dwn:write", // DWN write access
"did:write", // DID write access
},
time.Now().Add(time.Hour),
)
suite.Require().NoError(err)
// Create attenuated token (reduced permissions)
childToken, err := suite.ucanDelegator.CreateDelegation(
"did:sonr:intermediate-client",
suite.testClientDID,
[]string{
"vault:read", // Reduced to read-only
"service:read", // Reduced to read-only
"dwn:read", // Reduced to read-only
},
time.Now().Add(time.Hour),
)
suite.Require().NoError(err)
// Child token should have fewer capabilities
suite.Less(len(childToken.Attenuations), len(parentToken.Attenuations),
"Child token should have fewer capabilities than parent")
// Verify child cannot perform admin operations
err = suite.svcValidator.ValidateUCANPermission(ctx, childToken, "admin", "test-service")
suite.Error(err, "Child token should not authorize admin operations")
// But child should be able to perform read operations
err = suite.svcValidator.ValidateUCANPermission(ctx, childToken, "read", "test-service")
suite.NoError(err, "Child token should authorize read operations")
}
// TestDelegationChainValidation tests multi-hop delegation chains
func (suite *CrossModuleAuthTestSuite) TestDelegationChainValidation() {
// Create 3-hop delegation chain: User → Service A → Service B → Service C
// Hop 1: User → Service A
serviceA := "did:sonr:service-a"
token1, err := suite.signer.CreateDelegationToken(
suite.testUserDID,
serviceA,
[]ucan.Attenuation{
{
Capability: &ucan.MultiCapability{Actions: []string{"read", "write"}},
Resource: &handlers.SimpleResource{Scheme: "vault", Value: suite.testUserDID},
},
},
nil,
time.Hour,
)
suite.Require().NoError(err)
// Hop 2: Service A → Service B (with attenuation)
serviceB := "did:sonr:service-b"
token2, err := suite.signer.CreateDelegationToken(
serviceA,
serviceB,
[]ucan.Attenuation{
{
Capability: &ucan.SimpleCapability{Action: "read"}, // Reduced to read-only
Resource: &handlers.SimpleResource{Scheme: "vault", Value: suite.testUserDID},
},
},
[]ucan.Proof{ucan.Proof(token1)},
time.Hour,
)
suite.Require().NoError(err)
// Hop 3: Service B → Service C (maintain same level)
serviceC := "did:sonr:service-c"
token3, err := suite.signer.CreateDelegationToken(
serviceB,
serviceC,
[]ucan.Attenuation{
{
Capability: &ucan.SimpleCapability{Action: "read"}, // Same level
Resource: &handlers.SimpleResource{Scheme: "vault", Value: suite.testUserDID},
},
},
[]ucan.Proof{ucan.Proof(token2)},
time.Hour,
)
suite.Require().NoError(err)
// Validate the complete chain
err = suite.signer.ValidateDelegationChain([]string{token1, token2, token3})
suite.NoError(err, "Valid 3-hop delegation chain should pass validation")
// Test broken chain (missing intermediate proof)
brokenToken, err := suite.signer.CreateDelegationToken(
serviceA, // Wrong issuer - should be serviceB
serviceC,
[]ucan.Attenuation{
{
Capability: &ucan.SimpleCapability{Action: "read"},
Resource: &handlers.SimpleResource{Scheme: "vault", Value: suite.testUserDID},
},
},
[]ucan.Proof{ucan.Proof(token1)}, // Wrong proof
time.Hour,
)
suite.Require().NoError(err)
err = suite.signer.ValidateDelegationChain([]string{token1, token2, brokenToken})
suite.Error(err, "Broken delegation chain should fail validation")
}
// TestModuleSpecificValidation tests module-specific business rules
func (suite *CrossModuleAuthTestSuite) TestModuleSpecificValidation() {
ctx := context.Background()
// Test DWN ownership validation
ownerToken, err := suite.ucanDelegator.CreateDelegation(
"did:sonr:owner", // Different user
suite.testClientDID,
[]string{"dwn:admin"},
time.Now().Add(time.Hour),
)
suite.Require().NoError(err)
// Owner should be able to configure protocols
err = suite.dwnValidator.ValidateUCANPermission(ctx, ownerToken, "protocols-configure", "dwn:owner")
suite.NoError(err, "Owner should authorize protocol configuration")
// Non-owner should not be able to configure protocols
nonOwnerToken, err := suite.ucanDelegator.CreateDelegation(
"did:sonr:non-owner",
suite.testClientDID,
[]string{"dwn:admin"},
time.Now().Add(time.Hour),
)
suite.Require().NoError(err)
err = suite.dwnValidator.ValidateUCANPermission(ctx, nonOwnerToken, "protocols-configure", "dwn:owner")
suite.Error(err, "Non-owner should not authorize protocol configuration")
// Test Service domain verification
serviceOwnerToken, err := suite.ucanDelegator.CreateDelegation(
suite.testUserDID, // Owner of test-service
suite.testClientDID,
[]string{"service:write"},
time.Now().Add(time.Hour),
)
suite.Require().NoError(err)
err = suite.svcValidator.ValidateUCANPermission(ctx, serviceOwnerToken, "verify-domain", "test-service")
suite.NoError(err, "Service owner should authorize domain verification")
// Non-owner should not be able to verify domain
nonServiceOwnerToken, err := suite.ucanDelegator.CreateDelegation(
"did:sonr:different-user",
suite.testClientDID,
[]string{"service:write"},
time.Now().Add(time.Hour),
)
suite.Require().NoError(err)
err = suite.svcValidator.ValidateUCANPermission(ctx, nonServiceOwnerToken, "verify-domain", "test-service")
suite.Error(err, "Non-owner should not authorize domain verification")
}
// TestTimeBasedValidation tests time-sensitive authorization
func (suite *CrossModuleAuthTestSuite) TestTimeBasedValidation() {
// Create short-lived token (1 second to account for Unix timestamp precision)
shortToken, err := suite.ucanDelegator.CreateDelegation(
suite.testUserDID,
suite.testClientDID,
[]string{"vault:read"},
time.Now().Add(1*time.Second), // 1 second expiration for Unix timestamp precision
)
suite.Require().NoError(err)
// Should work immediately
_, err = suite.signer.VerifySignature(shortToken.Raw)
suite.NoError(err, "Token should be valid immediately after creation")
// Wait for expiration (need to wait at least 2 seconds due to Unix timestamp rounding)
time.Sleep(2 * time.Second)
// Should fail after expiration
_, err = suite.signer.VerifySignature(shortToken.Raw)
suite.Error(err, "Token should be invalid after expiration")
// Test not-before validation
futureToken, err := suite.signer.CreateDelegationToken(
suite.testUserDID,
suite.testClientDID,
[]ucan.Attenuation{
{
Capability: &ucan.SimpleCapability{Action: "read"},
Resource: &handlers.SimpleResource{Scheme: "vault", Value: suite.testUserDID},
},
},
nil,
time.Hour,
)
suite.Require().NoError(err)
// Manually verify token structure
parsedToken, err := suite.signer.VerifySignature(futureToken)
suite.NoError(err, "Token should parse successfully")
suite.NotZero(parsedToken.ExpiresAt, "Token should have expiration time")
}
func TestCrossModuleAuthSuite(t *testing.T) {
suite.Run(t, new(CrossModuleAuthTestSuite))
}