feat: add DDS protection status dashboard
This commit is contained in:
@@ -61,7 +61,8 @@ class DdsPropertiesTest {
|
||||
void bindsTokenTechnicalUserSeparatelyFromApplicationUsers() {
|
||||
var source = new MapConfigurationPropertySource(Map.of(
|
||||
"dds.token.username", "dds_demo_token",
|
||||
"dds.token.password", "secret"
|
||||
"dds.token.password", "secret",
|
||||
"dds.token.data-role", "cb_dds_token_role"
|
||||
));
|
||||
|
||||
var properties = new Binder(source)
|
||||
@@ -69,6 +70,7 @@ class DdsPropertiesTest {
|
||||
.orElseThrow(() -> new AssertionError("dds token properties did not bind"));
|
||||
|
||||
assertEquals("dds_demo_token", properties.token().username());
|
||||
assertEquals("cb_dds_token_role", properties.token().dataRole());
|
||||
assertTrue(properties.token().configured());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.cloudhandson.ddsbackoffice.service;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import com.cloudhandson.ddsbackoffice.config.DdsProperties;
|
||||
import com.cloudhandson.ddsbackoffice.domain.DdsGrantInventoryEntry;
|
||||
import com.cloudhandson.ddsbackoffice.domain.DdsGrantInventorySnapshot;
|
||||
import com.cloudhandson.ddsbackoffice.domain.DdsGrantPlan;
|
||||
import com.cloudhandson.ddsbackoffice.domain.DdsProvisioningPlan;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class DdsProtectionStatusServiceTest {
|
||||
|
||||
@Test
|
||||
void neverMarksAnUnavailableInventoryAsProtected() {
|
||||
DdsGrantInventory inventory = object -> new DdsGrantInventorySnapshot(
|
||||
false, List.of(), Instant.parse("2026-06-30T00:00:00Z"), "catalog 접근 실패"
|
||||
);
|
||||
var service = new DdsProtectionStatusService(properties(), inventory, emptyPlanProvider());
|
||||
|
||||
var overview = service.overview();
|
||||
|
||||
assertEquals("확인 불가", overview.servicePath().primaryLabel());
|
||||
assertEquals("확인 불가", overview.servicePath().observationLabel());
|
||||
assertTrue(overview.servicePath().needsAction());
|
||||
}
|
||||
|
||||
@Test
|
||||
void separatesMissingTokenGrantFromDirectComparisonEvidence() {
|
||||
DdsGrantInventory inventory = object -> new DdsGrantInventorySnapshot(
|
||||
true,
|
||||
List.of(new DdsGrantInventoryEntry(
|
||||
"DDS_DEMO_BOTH_VECTOR_GRANT", "CB_DDS_VECTOR_SEARCH_DOCUMENTS", "DDS_DEMO_BOTH_ROLE")),
|
||||
Instant.parse("2026-06-30T00:00:00Z"), ""
|
||||
);
|
||||
DdsProvisionPlanProvider provider = () -> new DdsProvisioningPlan(List.of(new DdsGrantPlan(
|
||||
"both", "통합 사용자", 103L, "dds_demo_both_role", "CB_VECTOR_SEARCH_DOCUMENTS",
|
||||
"ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS", "DDS_DEMO_BOTH_VECTOR_GRANT", "1 = 1", "",
|
||||
"CREATE DATA GRANT", true, "게시 가능"
|
||||
)), List.of(), 1);
|
||||
var service = new DdsProtectionStatusService(properties(), inventory, provider);
|
||||
|
||||
var overview = service.overview();
|
||||
|
||||
assertEquals("조치 필요", overview.servicePath().primaryLabel());
|
||||
assertEquals("반영 필요", overview.servicePath().synchronizationLabel());
|
||||
var direct = service.directComparison();
|
||||
assertEquals(1, direct.size());
|
||||
assertEquals("재검증 필요", direct.getFirst().primaryLabel());
|
||||
assertEquals("선언 관측", direct.getFirst().synchronizationLabel());
|
||||
}
|
||||
|
||||
@Test
|
||||
void reportsDirectGrantDriftWhenCurrentPlanHasNoGrantButInventoryDoes() {
|
||||
DdsGrantInventory inventory = object -> new DdsGrantInventorySnapshot(
|
||||
true,
|
||||
List.of(new DdsGrantInventoryEntry(
|
||||
"DDS_DEMO_BOTH_VECTOR_GRANT", "CB_DDS_VECTOR_SEARCH_DOCUMENTS", "DDS_DEMO_BOTH_ROLE")),
|
||||
Instant.parse("2026-06-30T00:00:00Z"), ""
|
||||
);
|
||||
DdsProvisionPlanProvider provider = () -> new DdsProvisioningPlan(List.of(new DdsGrantPlan(
|
||||
"both", "통합 사용자", 103L, "dds_demo_both_role", "CB_VECTOR_SEARCH_DOCUMENTS",
|
||||
"ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS", "DDS_DEMO_BOTH_VECTOR_GRANT", "", "", "",
|
||||
false, "ALLOW 규칙 없음"
|
||||
)), List.of(), 1);
|
||||
var service = new DdsProtectionStatusService(properties(), inventory, provider);
|
||||
|
||||
var direct = service.directComparison().getFirst();
|
||||
|
||||
assertEquals("조치 필요", direct.primaryLabel());
|
||||
assertEquals("차이 발견", direct.synchronizationLabel());
|
||||
}
|
||||
|
||||
private static DdsProperties properties() {
|
||||
return new DdsProperties(
|
||||
"jdbc:test", Duration.ofSeconds(5), "ADMIN.V_DDS_CUSTOMERS_PG", "ADMIN.V_DDS_CUSTOMERS_MY",
|
||||
"ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS",
|
||||
Map.of("both", new DdsProperties.User(
|
||||
"통합 사용자", "직접 비교", "dds_demo_both", "secret", 103L, "dds_demo_both_role")),
|
||||
Map.of("CB_VECTOR_SEARCH_DOCUMENTS", "ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS"),
|
||||
new DdsProperties.Token("dds_demo_token", "secret", "cb_dds_token_role")
|
||||
);
|
||||
}
|
||||
|
||||
private static DdsProvisionPlanProvider emptyPlanProvider() {
|
||||
return () -> new DdsProvisioningPlan(List.of(), List.of(), 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user