diff --git a/apps/api/internal/auth/core_independence_test.go b/apps/api/internal/auth/core_independence_test.go new file mode 100644 index 0000000..2aa6005 --- /dev/null +++ b/apps/api/internal/auth/core_independence_test.go @@ -0,0 +1,63 @@ +package auth + +import ( + "os" + "path/filepath" + "runtime" + "slices" + "strings" + "testing" +) + +func TestGatewaySourceDoesNotDependOnAuthenticationCoreVocabulary(t *testing.T) { + _, currentFile, _, ok := runtime.Caller(0) + if !ok { + t.Fatal("resolve current test file") + } + repositoryRoot := filepath.Clean(filepath.Join(filepath.Dir(currentFile), "..", "..", "..", "..")) + appsRoot := filepath.Join(repositoryRoot, "apps") + forbiddenMarkers := []string{ + strings.ToLower("key" + "cloak"), + "/rea" + "lms/", + } + sourceExtensions := []string{".go", ".js", ".json", ".ts", ".tsx", ".yaml", ".yml"} + skippedDirectories := []string{"coverage", "dist", "node_modules", "test-results"} + var violations []string + + err := filepath.WalkDir(appsRoot, func(filePath string, entry os.DirEntry, walkErr error) error { + if walkErr != nil { + return walkErr + } + if entry.IsDir() { + if filePath != appsRoot && slices.Contains(skippedDirectories, entry.Name()) { + return filepath.SkipDir + } + return nil + } + if !slices.Contains(sourceExtensions, strings.ToLower(filepath.Ext(entry.Name()))) { + return nil + } + content, readErr := os.ReadFile(filePath) + if readErr != nil { + return readErr + } + lowerContent := strings.ToLower(string(content)) + for _, marker := range forbiddenMarkers { + if strings.Contains(lowerContent, marker) { + relativePath, relativeErr := filepath.Rel(repositoryRoot, filePath) + if relativeErr != nil { + return relativeErr + } + violations = append(violations, filepath.ToSlash(relativePath)) + break + } + } + return nil + }) + if err != nil { + t.Fatalf("scan Gateway sources: %v", err) + } + if len(violations) > 0 { + t.Fatalf("Gateway source contains authentication-core-specific vocabulary: %s", strings.Join(violations, ", ")) + } +} diff --git a/apps/api/internal/store/oidc_users_integration_test.go b/apps/api/internal/store/oidc_users_integration_test.go index 5a41b77..3a3aff7 100644 --- a/apps/api/internal/store/oidc_users_integration_test.go +++ b/apps/api/internal/store/oidc_users_integration_test.go @@ -31,7 +31,7 @@ func TestResolveOrProvisionOIDCUserLifecycleAndConcurrency(t *testing.T) { suffix := time.Now().UTC().Format("20060102150405.000000000") subject := "platform-jit-" + suffix input := ResolveOrProvisionOIDCUserInput{ - Issuer: "https://auth.test.example/realms/easyai", + Issuer: "https://auth.test.example/issuer/shared", Subject: subject, Username: "jit-user-" + suffix, Roles: []string{"basic"}, @@ -176,7 +176,7 @@ func TestResolveOrProvisionOIDCUserRejectsMissingMappingWithoutWrites(t *testing subject := "platform-jit-missing-" + time.Now().UTC().Format("20060102150405.000000000") input := ResolveOrProvisionOIDCUserInput{ - Issuer: "https://auth.test.example/realms/easyai", + Issuer: "https://auth.test.example/issuer/shared", Subject: subject, Username: "missing-user", Roles: []string{"basic"}, @@ -239,7 +239,7 @@ RETURNING id::text`, tenantKey, groupID).Scan(&tenantID); err != nil { } func TestOIDCUserKeyIsStableAndDoesNotExposeClaims(t *testing.T) { - issuer := "https://auth.test.example/realms/easyai" + issuer := "https://auth.test.example/issuer/shared" subject := "platform-sensitive-subject" first := deriveOIDCUserKey(issuer, subject) second := deriveOIDCUserKey(issuer+"/", subject) diff --git a/apps/api/internal/store/security_events_integration_test.go b/apps/api/internal/store/security_events_integration_test.go index cef4e2b..8131e50 100644 --- a/apps/api/internal/store/security_events_integration_test.go +++ b/apps/api/internal/store/security_events_integration_test.go @@ -38,7 +38,7 @@ func TestSecurityEventWatermarkVerificationAndFallbackLifecycle(t *testing.T) { } defer db.Close() issuer := "https://auth.test.example/ssf" - subjectIssuer := "https://auth.test/realms/tenant" + subjectIssuer := "https://auth.test/issuer/shared" audience := "urn:easyai:ssf:receiver:" + uuid.NewString() streamID, tenantID, subject := uuid.NewString(), uuid.NewString(), uuid.NewString() t.Cleanup(func() {