package com.baiyue.lastleg.security; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import static org.assertj.core.api.Assertions.assertThat; class LastlegSecurityContractTest { @Test void securityProjectionLivesInLastlegDatabaseWithReliableInbox() throws Exception { String baseline = read("lastleg-service/src/main/resources/db/lastleg.sql"); String migration = read("deploy/sql/海外区域统一增量-MySQL8.sql"); String authSeed = read("auth/src/main/resources/db/phase1-admin-accounts.sql"); String service = read("lastleg-service/src/main/java/com/baiyue/lastleg/security/service/LastlegSecurityProjectionService.java"); String validation = read("lastleg-service/src/main/java/com/baiyue/lastleg/security/service/LastlegSecurityValidationService.java"); String validationDto = read("lastleg-service/src/main/java/com/baiyue/lastleg/security/dto/LastlegSecurityDtos.java"); String mirror = read("lastleg-service/src/main/java/com/baiyue/lastleg/security/service/LastlegSecurityProjectionRedisMirror.java"); assertThat(baseline).contains("CREATE TABLE IF NOT EXISTS ll_security_event_inbox", "PRIMARY KEY (event_id)", "payload_hash CHAR(64)", "CREATE TABLE IF NOT EXISTS ll_security_projection", "PRIMARY KEY (aggregate_type,aggregate_id)", "aggregate_version BIGINT", "permission_version BIGINT", "roles_json JSON", "authorities_json JSON"); assertThat(baseline).contains("table_name='ll_security_projection' AND column_name='roles_json'", "table_name='ll_security_projection' AND column_name='authorities_json'", "UPDATE ll_security_projection SET roles_json=JSON_ARRAY() WHERE roles_json IS NULL", "UPDATE ll_security_projection SET authorities_json=JSON_ARRAY() WHERE authorities_json IS NULL"); assertThat(baseline).contains("SET @platform_admin_overseas_roles = JSON_ARRAY('PLATFORM_ADMIN')", "SET @platform_admin_overseas_authorities = JSON_ARRAY(", "('USER','1000001',0,'ACTIVE',0,JSON_ARRAY(),JSON_ARRAY(),0", "('AUTHORIZATION','1000001',0,NULL,0,@platform_admin_overseas_roles,@platform_admin_overseas_authorities,0", "baseline-platform-admin-user-v0", "baseline-platform-admin-authorization-v0", "platform:overseas-warehouse:last-mile-order:list", "platform:overseas-warehouse:inbound-order:list", "platform:overseas-warehouse:dispatch:list", "platform:overseas-business:purchase:list", "platform:overseas-master-data:quote:list", "platform:finance:fee-source:review", "AND aggregate_version=0 AND last_event_id='baseline-platform-admin-authorization-v0'") .doesNotContain("('AUTHORIZATION','1000001',0,NULL,0,JSON_ARRAY(),JSON_ARRAY(),0", "baseline-merchant-admin"); assertThat(migration).contains( "CREATE TABLE ll_security_event_inbox (", "CREATE TABLE ll_security_projection (", "VALUES ('AUTHORIZATION','1000001',0,NULL,0,", "VALUES ('USER','1000001',0,'ACTIVE',0,'[]','[]',0", "PLATFORM_ADMIN", "baseline-platform-admin-user-v0", "baseline-platform-admin-authorization-v0", "platform:overseas-warehouse:last-mile-order:list", "platform:overseas-warehouse:inbound-order:list", "platform:overseas-warehouse:dispatch:list", "platform:overseas-business:purchase:list", "platform:overseas-master-data:quote:list", "platform:finance:fee-source:review") .doesNotContain("SET @platform_admin_overseas_roles", "UPDATE ll_security_projection", "baseline-merchant-admin"); assertThat(authSeed).contains("(1000001, 'platform_admin'", "default_tenant_id", "default_app_code"); assertThat(migration).contains("USE baiyue_lastleg;") .doesNotContain("CREATE DATABASE IF NOT EXISTS baiyue_overseas_security", "USE baiyue_overseas_security;", "CREATE TABLE IF NOT EXISTS overseas_security_event_inbox", "CREATE TABLE IF NOT EXISTS overseas_security_projection"); assertThat(service).contains("SecurityProjectionMonotonicity.decide", "Decision.APPLY"); assertThat(service).contains("EventPayloadHash.matches", "validatedStorageHash") .doesNotContain("LoadingCompletedConsumer"); assertThat(mirror).contains("TransactionPhase.AFTER_COMMIT", "Redis 镜像写入失败"); assertThat(validation).contains("SELECT permission_version", "authorities_json", "tenant_id=?", "SECURITY_PROJECTION_MISSING", "SECURITY_PROJECTION_NOT_READY", "USER_DISABLED", "TENANT_DISABLED", "PERMISSION_VERSION_STALE"); assertThat(validationDto).contains("Long userId", "Long tenantId", "Long permissionVersion") .doesNotContain("Long tokenVersion"); } @Test void genericLastlegEventEndpointWhitelistsBusinessEventsOnly() throws Exception { String controller = read("lastleg-service/src/main/java/com/baiyue/lastleg/controller/LastlegOrderController.java"); assertThat(controller).contains("FIRST_LEG_CONTAINER_SHIPPING_NODE_RECORDED", "FIRST_LEG_LOADING_PLAN_COMPLETED", "Lastleg 不消费该事件类型") .doesNotContain("IAM_USER_STATUS_CHANGED", "IAM_TENANT_STATUS_CHANGED", "IAM_PERMISSION_VERSION_CHANGED"); } @Test void internalControllerUsesLastlegOwnedPathsAndExistingTokenGuard() throws Exception { String controller = read("lastleg-service/src/main/java/com/baiyue/lastleg/security/controller/LastlegSecurityController.java"); String guard = read("lastleg-service/src/main/java/com/baiyue/lastleg/security/InternalServiceTokenGuard.java"); String application = read("lastleg-service/src/main/resources/application.yml"); assertThat(controller).contains("@RequestMapping(\"/internal/lastleg/security/v1\")", "@PostMapping(\"/events\")", "@PostMapping(\"/validate\")", "com.baiyue.lastleg.security.InternalServiceTokenGuard") .doesNotContain("/internal/overseas-security/v1"); assertThat(guard).contains("MessageDigest.isEqual", "SERVICE_UNAVAILABLE", "UNAUTHORIZED"); assertThat(application).contains("internal-service-token: ${INTERNAL_SERVICE_TOKEN:}") .doesNotContain("phase1-local-internal-token"); } private static String read(String relative) throws Exception { Path path = Path.of(relative); if (!Files.exists(path)) path = Path.of("..").resolve(relative); return Files.readString(path, StandardCharsets.UTF_8); } }