Files
at-container-registry/pkg/auth/hold_local_test.go
T
2025-10-28 22:06:03 -05:00

389 lines
11 KiB
Go

package auth
import (
"context"
"os"
"path/filepath"
"testing"
"atcr.io/pkg/hold/pds"
)
// Shared PDS instances for read-only tests
var (
sharedEmptyPDS *pds.HoldPDS
sharedPublicPDS *pds.HoldPDS
sharedPrivatePDS *pds.HoldPDS
sharedAllowCrewPDS *pds.HoldPDS
sharedTempDir string
)
// TestMain sets up shared test fixtures
func TestMain(m *testing.M) {
// Create temp directory for shared keys
var err error
sharedTempDir, err = os.MkdirTemp("", "hold_local_test")
if err != nil {
panic(err)
}
defer os.RemoveAll(sharedTempDir)
ctx := context.Background()
// Create shared empty PDS (not bootstrapped)
emptyKeyPath := filepath.Join(sharedTempDir, "empty-key")
sharedEmptyPDS, err = pds.NewHoldPDS(ctx, "did:web:hold.example.com", "http://hold.example.com", ":memory:", emptyKeyPath, false)
if err != nil {
panic(err)
}
// Create shared public PDS
publicKeyPath := filepath.Join(sharedTempDir, "public-key")
sharedPublicPDS, err = pds.NewHoldPDS(ctx, "did:web:hold.example.com", "http://hold.example.com", ":memory:", publicKeyPath, false)
if err != nil {
panic(err)
}
err = sharedPublicPDS.Bootstrap(ctx, nil, "did:plc:owner123", true, false, "")
if err != nil {
panic(err)
}
// Create shared private PDS
privateKeyPath := filepath.Join(sharedTempDir, "private-key")
sharedPrivatePDS, err = pds.NewHoldPDS(ctx, "did:web:hold.example.com", "http://hold.example.com", ":memory:", privateKeyPath, false)
if err != nil {
panic(err)
}
err = sharedPrivatePDS.Bootstrap(ctx, nil, "did:plc:owner123", false, false, "")
if err != nil {
panic(err)
}
// Create shared allowAllCrew PDS
allowCrewKeyPath := filepath.Join(sharedTempDir, "allowcrew-key")
sharedAllowCrewPDS, err = pds.NewHoldPDS(ctx, "did:web:hold.example.com", "http://hold.example.com", ":memory:", allowCrewKeyPath, false)
if err != nil {
panic(err)
}
err = sharedAllowCrewPDS.Bootstrap(ctx, nil, "did:plc:owner123", false, true, "")
if err != nil {
panic(err)
}
// Run tests
code := m.Run()
os.Exit(code)
}
// Helper function to create a per-test HoldPDS (for tests that modify state)
func createTestHoldPDS(t *testing.T, ownerDID string, public bool, allowAllCrew bool) *pds.HoldPDS {
t.Helper()
ctx := context.Background()
// Create temp directory for keys
tmpDir := t.TempDir()
keyPath := filepath.Join(tmpDir, "signing-key")
// Create in-memory PDS
holdPDS, err := pds.NewHoldPDS(ctx, "did:web:hold.example.com", "http://hold.example.com", ":memory:", keyPath, false)
if err != nil {
t.Fatalf("Failed to create test HoldPDS: %v", err)
}
// Bootstrap with owner if provided
if ownerDID != "" {
err = holdPDS.Bootstrap(ctx, nil, ownerDID, public, allowAllCrew, "")
if err != nil {
t.Fatalf("Failed to bootstrap HoldPDS: %v", err)
}
}
return holdPDS
}
func TestNewLocalHoldAuthorizer(t *testing.T) {
authorizer := NewLocalHoldAuthorizer(sharedEmptyPDS)
if authorizer == nil {
t.Fatal("Expected non-nil authorizer")
}
// Verify it's the correct type
localAuth, ok := authorizer.(*LocalHoldAuthorizer)
if !ok {
t.Fatal("Expected LocalHoldAuthorizer type")
}
if localAuth.pds == nil {
t.Error("Expected pds to be set")
}
}
func TestNewLocalHoldAuthorizerFromInterface_Success(t *testing.T) {
authorizer := NewLocalHoldAuthorizerFromInterface(sharedEmptyPDS)
if authorizer == nil {
t.Fatal("Expected non-nil authorizer")
}
// Verify it's the correct type
_, ok := authorizer.(*LocalHoldAuthorizer)
if !ok {
t.Fatal("Expected LocalHoldAuthorizer type")
}
}
func TestNewLocalHoldAuthorizerFromInterface_InvalidType(t *testing.T) {
// Test with wrong type - should return nil
authorizer := NewLocalHoldAuthorizerFromInterface("not a pds")
if authorizer != nil {
t.Error("Expected nil authorizer for invalid type")
}
}
func TestNewLocalHoldAuthorizerFromInterface_Nil(t *testing.T) {
// Test with nil - should return nil
authorizer := NewLocalHoldAuthorizerFromInterface(nil)
if authorizer != nil {
t.Error("Expected nil authorizer for nil input")
}
}
func TestLocalHoldAuthorizer_GetCaptainRecord_Success(t *testing.T) {
holdDID := "did:web:hold.example.com"
ownerDID := "did:plc:owner123"
authorizer := NewLocalHoldAuthorizer(sharedPublicPDS)
ctx := context.Background()
record, err := authorizer.GetCaptainRecord(ctx, holdDID)
if err != nil {
t.Fatalf("GetCaptainRecord() error = %v", err)
}
if record == nil {
t.Fatal("Expected non-nil captain record")
}
if !record.Public {
t.Error("Expected public=true")
}
if record.Owner != ownerDID {
t.Errorf("Expected owner=%s, got %s", ownerDID, record.Owner)
}
}
func TestLocalHoldAuthorizer_GetCaptainRecord_DIDMismatch(t *testing.T) {
authorizer := NewLocalHoldAuthorizer(sharedPublicPDS)
ctx := context.Background()
// Request with different DID
_, err := authorizer.GetCaptainRecord(ctx, "did:web:different.example.com")
if err == nil {
t.Error("Expected error for DID mismatch")
}
}
func TestLocalHoldAuthorizer_GetCaptainRecord_NoCaptain(t *testing.T) {
holdDID := "did:web:hold.example.com"
// Use empty PDS (no captain record)
authorizer := NewLocalHoldAuthorizer(sharedEmptyPDS)
ctx := context.Background()
_, err := authorizer.GetCaptainRecord(ctx, holdDID)
if err == nil {
t.Error("Expected error when captain record doesn't exist")
}
}
func TestLocalHoldAuthorizer_IsCrewMember_Success(t *testing.T) {
holdDID := "did:web:hold.example.com"
ownerDID := "did:plc:owner123"
userDID := "did:plc:alice123"
// Create per-test PDS since we're adding crew members
holdPDS := createTestHoldPDS(t, ownerDID, false, false)
// Add user as crew member
ctx := context.Background()
_, err := holdPDS.AddCrewMember(ctx, userDID, "member", []string{"blob:read", "blob:write"})
if err != nil {
t.Fatalf("Failed to add crew member: %v", err)
}
authorizer := NewLocalHoldAuthorizer(holdPDS)
isMember, err := authorizer.IsCrewMember(ctx, holdDID, userDID)
if err != nil {
t.Fatalf("IsCrewMember() error = %v", err)
}
if !isMember {
t.Error("Expected user to be crew member")
}
}
func TestLocalHoldAuthorizer_IsCrewMember_NotMember(t *testing.T) {
holdDID := "did:web:hold.example.com"
ownerDID := "did:plc:owner123"
userDID := "did:plc:alice123"
// Create per-test PDS since we're adding crew members
holdPDS := createTestHoldPDS(t, ownerDID, false, false)
// Add different user as crew member
ctx := context.Background()
_, err := holdPDS.AddCrewMember(ctx, "did:plc:bob456", "member", []string{"blob:read"})
if err != nil {
t.Fatalf("Failed to add crew member: %v", err)
}
authorizer := NewLocalHoldAuthorizer(holdPDS)
isMember, err := authorizer.IsCrewMember(ctx, holdDID, userDID)
if err != nil {
t.Fatalf("IsCrewMember() error = %v", err)
}
if isMember {
t.Error("Expected user NOT to be crew member")
}
}
func TestLocalHoldAuthorizer_IsCrewMember_DIDMismatch(t *testing.T) {
authorizer := NewLocalHoldAuthorizer(sharedPrivatePDS)
ctx := context.Background()
_, err := authorizer.IsCrewMember(ctx, "did:web:different.example.com", "did:plc:alice123")
if err == nil {
t.Error("Expected error for DID mismatch")
}
}
func TestLocalHoldAuthorizer_CheckReadAccess_PublicHold(t *testing.T) {
holdDID := "did:web:hold.example.com"
authorizer := NewLocalHoldAuthorizer(sharedPublicPDS)
ctx := context.Background()
// Public hold should allow read access for anyone (including empty DID)
hasAccess, err := authorizer.CheckReadAccess(ctx, holdDID, "")
if err != nil {
t.Fatalf("CheckReadAccess() error = %v", err)
}
if !hasAccess {
t.Error("Expected read access for public hold")
}
}
func TestLocalHoldAuthorizer_CheckReadAccess_PrivateHold(t *testing.T) {
holdDID := "did:web:hold.example.com"
authorizer := NewLocalHoldAuthorizer(sharedPrivatePDS)
ctx := context.Background()
// Private hold should deny anonymous access
hasAccess, err := authorizer.CheckReadAccess(ctx, holdDID, "")
if err != nil {
t.Fatalf("CheckReadAccess() error = %v", err)
}
if hasAccess {
t.Error("Expected NO read access for private hold with no user")
}
}
func TestLocalHoldAuthorizer_CheckWriteAccess_Owner(t *testing.T) {
holdDID := "did:web:hold.example.com"
ownerDID := "did:plc:owner123"
authorizer := NewLocalHoldAuthorizer(sharedPrivatePDS)
ctx := context.Background()
// Owner should have write access (owner is automatically added as crew by Bootstrap)
hasAccess, err := authorizer.CheckWriteAccess(ctx, holdDID, ownerDID)
if err != nil {
t.Fatalf("CheckWriteAccess() error = %v", err)
}
if !hasAccess {
t.Error("Expected write access for owner")
}
}
func TestLocalHoldAuthorizer_CheckWriteAccess_NonOwner(t *testing.T) {
holdDID := "did:web:hold.example.com"
userDID := "did:plc:alice123"
authorizer := NewLocalHoldAuthorizer(sharedPrivatePDS)
ctx := context.Background()
// Non-owner, non-crew should NOT have write access
hasAccess, err := authorizer.CheckWriteAccess(ctx, holdDID, userDID)
if err != nil {
t.Fatalf("CheckWriteAccess() error = %v", err)
}
if hasAccess {
t.Error("Expected NO write access for non-owner, non-crew")
}
}
func TestLocalHoldAuthorizer_CheckWriteAccess_CrewMember(t *testing.T) {
holdDID := "did:web:hold.example.com"
ownerDID := "did:plc:owner123"
userDID := "did:plc:alice123"
// Create per-test PDS with allowAllCrew=true since we're adding crew members
holdPDS := createTestHoldPDS(t, ownerDID, false, true)
// Add user as crew member
ctx := context.Background()
_, err := holdPDS.AddCrewMember(ctx, userDID, "member", []string{"blob:read", "blob:write"})
if err != nil {
t.Fatalf("Failed to add crew member: %v", err)
}
authorizer := NewLocalHoldAuthorizer(holdPDS)
// Crew member with allowAllCrew=true should have write access
hasAccess, err := authorizer.CheckWriteAccess(ctx, holdDID, userDID)
if err != nil {
t.Fatalf("CheckWriteAccess() error = %v", err)
}
if !hasAccess {
t.Error("Expected write access for crew member with allowAllCrew=true")
}
}
func TestLocalHoldAuthorizer_CheckReadAccess_CrewMember(t *testing.T) {
holdDID := "did:web:hold.example.com"
ownerDID := "did:plc:owner123"
userDID := "did:plc:alice123"
// Create per-test PDS since we're adding crew members
holdPDS := createTestHoldPDS(t, ownerDID, false, false)
// Add user as crew member
ctx := context.Background()
_, err := holdPDS.AddCrewMember(ctx, userDID, "member", []string{"blob:read"})
if err != nil {
t.Fatalf("Failed to add crew member: %v", err)
}
authorizer := NewLocalHoldAuthorizer(holdPDS)
// Crew member should have read access even on private hold
hasAccess, err := authorizer.CheckReadAccess(ctx, holdDID, userDID)
if err != nil {
t.Fatalf("CheckReadAccess() error = %v", err)
}
if !hasAccess {
t.Error("Expected read access for crew member on private hold")
}
}