package services import ( "context" "errors" "net/http/httptest" "testing" "code.tczkiot.com/wlw/ai-agent/identity" "code.tczkiot.com/wlw/ai-agent/internal/pkg/constants" "github.com/gin-gonic/gin" ) func TestExternalAuthDelegatesOperationToHost(t *testing.T) { SetQuerySubjects(func(_ context.Context, query identity.Query) ([]identity.Subject, error) { if !query.Current { return nil, nil } return []identity.Subject{{ Type: identity.SubjectAdmin, Category: identity.CategorySystem, ID: 9, Username: "admin", Name: "Admin", Enabled: true, }}, nil }) var gotOperation string SetAuthorize(func(_ context.Context, operation string) error { gotOperation = operation return nil }) ctx, _ := gin.CreateTestContext(httptest.NewRecorder()) ctx.Request = httptest.NewRequest("GET", "/api/dashboard/conversation/list", nil) principal, err := AuthService.RequirePermission(ctx, constants.PermissionConversationView) if err != nil { t.Fatalf("RequirePermission() error = %v", err) } if principal.UserID != 9 || principal.SubjectType != identity.SubjectAdmin { t.Fatalf("principal = %#v", principal) } if gotOperation != constants.PermissionConversationView.Code { t.Fatalf("operation = %q, want %q", gotOperation, constants.PermissionConversationView.Code) } } func TestExternalAuthRejectsHostDeniedOperation(t *testing.T) { SetQuerySubjects(func(_ context.Context, query identity.Query) ([]identity.Subject, error) { if !query.Current { return nil, nil } return []identity.Subject{{ Type: identity.SubjectAgent, Category: identity.CategorySystem, ID: 10, Enabled: true, }}, nil }) SetAuthorize(func(_ context.Context, _ string) error { return errors.New("denied by host") }) ctx, _ := gin.CreateTestContext(httptest.NewRecorder()) ctx.Request = httptest.NewRequest("POST", "/api/dashboard/ai-config/delete", nil) if _, err := AuthService.RequirePermission(ctx, constants.PermissionAIConfigDelete); err == nil { t.Fatal("RequirePermission() error = nil, want forbidden") } } func TestExternalAuthRejectsAgentAsDashboardOperator(t *testing.T) { SetQuerySubjects(func(_ context.Context, query identity.Query) ([]identity.Subject, error) { if !query.Current { return nil, nil } return []identity.Subject{{ Type: identity.SubjectAgent, Category: identity.CategorySystem, ID: 10, Enabled: true, }}, nil }) SetAuthorize(func(_ context.Context, _ string) error { return nil }) ctx, _ := gin.CreateTestContext(httptest.NewRecorder()) ctx.Request = httptest.NewRequest("GET", "/api/dashboard/conversation/list", nil) if _, err := AuthService.Authenticate(ctx); err == nil { t.Fatal("Authenticate() error = nil, want agent dashboard access rejected") } } func TestAgentIdentityActsAsExternalCustomer(t *testing.T) { SetQuerySubjects(func(_ context.Context, query identity.Query) ([]identity.Subject, error) { if !query.Current { return nil, nil } return []identity.Subject{{ Type: identity.SubjectAgent, Category: identity.CategoryUser, ID: 12, Name: "Agent Customer", Enabled: true, }}, nil }) external, err := SubjectService.CurrentExternal(context.Background()) if err != nil { t.Fatalf("CurrentExternal() error = %v", err) } if external.ExternalID != "agent:12" || external.ExternalName != "Agent Customer" { t.Fatalf("external = %#v", external) } } func TestAnonymousGuestIdentityFallback(t *testing.T) { SetQuerySubjects(func(_ context.Context, query identity.Query) ([]identity.Subject, error) { if query.Current { return nil, nil } return nil, nil }) external, err := SubjectService.ResolveExternal(context.Background(), "guest_123", "Web Visitor") if err != nil { t.Fatalf("ResolveExternal() error = %v", err) } if external.ExternalSource != "guest" || external.ExternalID != "guest_123" || external.ExternalName != "Web Visitor" { t.Fatalf("external = %#v", external) } } func TestAnonymousGuestIdentityRequiresOpaqueID(t *testing.T) { SetQuerySubjects(func(_ context.Context, query identity.Query) ([]identity.Subject, error) { return nil, nil }) if _, err := SubjectService.ResolveExternal(context.Background(), "", "Web Visitor"); err == nil { t.Fatal("ResolveExternal() error = nil, want missing guest id rejected") } }