Files
crypto/ucan/ucan_test.go

314 lines
7.3 KiB
Go

package ucan
import (
"crypto/sha256"
"testing"
"time"
"github.com/ipfs/go-cid"
"github.com/multiformats/go-multihash"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCapabilityCreation(t *testing.T) {
testCases := []struct {
name string
actions []string
expected bool
}{
{
name: "Basic Capability Creation",
actions: []string{"read", "write"},
expected: true,
},
{
name: "Empty Actions",
actions: []string{},
expected: true,
},
{
name: "Complex Actions",
actions: []string{"create", "update", "delete", "admin"},
expected: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
capability := &MultiCapability{Actions: tc.actions}
assert.NotNil(t, capability)
assert.Equal(t, len(tc.actions), len(capability.Actions))
for _, action := range tc.actions {
assert.Contains(t, capability.Actions, action)
}
})
}
}
func TestCapabilityValidation(t *testing.T) {
testCases := []struct {
name string
actions []string
resourceScheme string
shouldPass bool
}{
{
name: "Valid Standard Actions",
actions: []string{"read", "write"},
resourceScheme: "example",
shouldPass: true,
},
{
name: "Invalid Actions",
actions: []string{"delete", "admin"},
resourceScheme: "restricted",
shouldPass: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
capability := &MultiCapability{Actions: tc.actions}
resource := &SimpleResource{
Scheme: tc.resourceScheme,
Value: "test",
URI: tc.resourceScheme + "://test",
}
attenuation := Attenuation{
Capability: capability,
Resource: resource,
}
StandardTemplate.AddAllowedActions(tc.resourceScheme, []string{"read", "write"})
err := StandardTemplate.ValidateAttenuation(attenuation)
if tc.shouldPass {
assert.NoError(t, err)
} else {
assert.Error(t, err)
}
})
}
}
func TestJWTTokenLifecycle(t *testing.T) {
testCases := []struct {
name string
actions []string
resourceScheme string
duration time.Duration
shouldPass bool
}{
{
name: "Valid Token Generation and Verification",
actions: []string{"read", "write"},
resourceScheme: "example",
duration: time.Hour,
shouldPass: true,
},
{
name: "Expired Token",
actions: []string{"read"},
resourceScheme: "test",
duration: -time.Hour, // Expired token
shouldPass: false,
},
}
// Use standard service template for testing
StandardTemplate := StandardServiceTemplate()
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
capability := &MultiCapability{Actions: tc.actions}
resource := &SimpleResource{
Scheme: tc.resourceScheme,
Value: "test",
URI: tc.resourceScheme + "://test",
}
attenuation := Attenuation{
Capability: capability,
Resource: resource,
}
// Validate attenuation against template
err := StandardTemplate.ValidateAttenuation(attenuation)
require.NoError(t, err)
// Simulate JWT token generation and verification
token := "test_token_" + time.Now().String()
if tc.shouldPass {
// Simulate verification
verifiedToken := &Token{
Raw: token,
Issuer: "did:sonr:local",
Attenuations: []Attenuation{attenuation},
ExpiresAt: time.Now().Add(tc.duration).Unix(),
}
assert.NotNil(t, verifiedToken)
assert.Equal(t, "did:sonr:local", verifiedToken.Issuer)
assert.Len(t, verifiedToken.Attenuations, 1)
assert.Equal(
t,
tc.resourceScheme+"://test",
verifiedToken.Attenuations[0].Resource.GetURI(),
)
} else {
// Simulate expired token verification
assert.True(t, time.Now().Unix() > time.Now().Add(tc.duration).Unix())
}
})
}
}
func TestCapabilityRevocation(t *testing.T) {
capability := &MultiCapability{Actions: []string{"read", "write"}}
resource := &SimpleResource{
Scheme: "example",
Value: "test",
URI: "example://test",
}
attenuation := Attenuation{
Capability: capability,
Resource: resource,
}
// Generate token
token, err := GenerateJWTToken(attenuation, time.Hour)
require.NoError(t, err)
// Revoke capability
err = RevokeCapability(attenuation)
assert.NoError(t, err)
// Attempt to verify revoked token should fail
_, err = VerifyJWTToken(token)
assert.Error(t, err)
assert.Contains(t, err.Error(), "token has been revoked")
}
func TestResourceValidation(t *testing.T) {
testCases := []struct {
name string
resourceScheme string
resourceValue string
resourceURI string
expectValid bool
}{
{
name: "Valid Resource",
resourceScheme: "sonr",
resourceValue: "test-resource",
resourceURI: "sonr://test-resource",
expectValid: true,
},
{
name: "Invalid Resource URI",
resourceScheme: "invalid",
resourceValue: "test-resource",
resourceURI: "invalid-malformed-uri",
expectValid: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
resource := &SimpleResource{
Scheme: tc.resourceScheme,
Value: tc.resourceValue,
URI: tc.resourceURI,
}
// Simplified resource validation
if tc.expectValid {
assert.Regexp(t, `^[a-z]+://[a-z-]+$`, resource.URI)
} else {
assert.NotRegexp(t, `^[a-z]+://[a-z-]+$`, resource.URI)
}
})
}
}
func TestValidateEnclaveDataCIDIntegrity(t *testing.T) {
testCases := []struct {
name string
data []byte
expectedCID string
expectError bool
errorContains string
}{
{
name: "Empty CID",
data: []byte("test data"),
expectedCID: "",
expectError: true,
errorContains: "enclave data CID cannot be empty",
},
{
name: "Empty data",
data: []byte{},
expectedCID: "QmTest",
expectError: true,
errorContains: "enclave data cannot be empty",
},
{
name: "Invalid CID format",
data: []byte("test data"),
expectedCID: "invalid-cid",
expectError: true,
errorContains: "invalid IPFS CID format",
},
{
name: "Valid CID verification - should pass",
data: []byte("test data"),
expectedCID: generateValidCIDForData([]byte("test data")),
expectError: false,
},
{
name: "Mismatched CID - should fail",
data: []byte("test data"),
expectedCID: generateValidCIDForData([]byte("different data")),
expectError: true,
errorContains: "CID verification failed",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := ValidateEnclaveDataCIDIntegrity(tc.expectedCID, tc.data)
if tc.expectError {
assert.Error(t, err)
if tc.errorContains != "" {
assert.Contains(t, err.Error(), tc.errorContains)
}
} else {
assert.NoError(t, err)
}
})
}
}
// Helper function to generate a valid CID for test data
func generateValidCIDForData(data []byte) string {
hasher := sha256.New()
hasher.Write(data)
digest := hasher.Sum(nil)
mhash, err := multihash.EncodeName(digest, "sha2-256")
if err != nil {
panic(err)
}
calculatedCID := cid.NewCidV1(cid.DagProtobuf, mhash)
return calculatedCID.String()
}