mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-20 09:14:16 +00:00
389 lines
11 KiB
Go
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")
|
|
}
|
|
}
|