[Developer] #617 apply DDS MCP end-user authorization

This commit is contained in:
devmrko
2026-07-02 09:34:58 +09:00
parent fd09622c82
commit ef1331be4b
53 changed files with 2040 additions and 337 deletions

View File

@@ -0,0 +1,40 @@
package com.cloudhandson.ddsbackoffice.config;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.time.Duration;
import org.junit.jupiter.api.Test;
class DdsMcpIamPropertiesTest {
@Test
void derivesTheOciIdentityDomainTokenEndpoint() {
var properties = properties("https://idcs-example.identity.oraclecloud.com", "");
assertTrue(properties.configured());
assertEquals("https://idcs-example.identity.oraclecloud.com/oauth2/v1/token",
properties.tokenEndpoint().toString());
}
@Test
void doesNotReportPartialOauthConfigurationAsReady() {
var properties = new DdsMcpIamProperties("", "", "client", "secret", "", Duration.ofSeconds(10), Duration.ofSeconds(60));
assertFalse(properties.configured());
}
@Test
void rejectsANonHttpsTokenEndpoint() {
var properties = properties("", "http://identity.example.test/oauth2/v1/token");
assertThrows(IllegalStateException.class, properties::tokenEndpoint);
}
private DdsMcpIamProperties properties(String domainUrl, String tokenUri) {
return new DdsMcpIamProperties(domainUrl, tokenUri, "client", "secret", "database-scope",
Duration.ofSeconds(10), Duration.ofSeconds(60));
}
}

View File

@@ -0,0 +1,26 @@
package com.cloudhandson.ddsbackoffice.service;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.cloudhandson.vpdbackoffice.service.DdsAuthorizationChangeNotifier;
import com.cloudhandson.vpdbackoffice.service.DdsAuthorizationSynchronizer;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
class DdsMcpAuthorizationChangeListenerTest {
@Test
void bridgesACommittedAuthorizationChangeToOneBulkMcpPublish() {
DdsMcpEndUserPublisher publisher = org.mockito.Mockito.mock(DdsMcpEndUserPublisher.class);
when(publisher.publish()).thenReturn(new com.cloudhandson.ddsbackoffice.domain.DdsMcpEndUserPublishResult(3, 0, 0));
DdsMcpAuthorizationChangeListener listener = new DdsMcpAuthorizationChangeListener(publisher);
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerSingleton("ddsSynchronizer", (DdsAuthorizationSynchronizer) listener);
new DdsAuthorizationChangeNotifier(factory.getBeanProvider(DdsAuthorizationSynchronizer.class))
.changed("PERMISSION_SAVED");
verify(publisher).publish();
}
}

View File

@@ -0,0 +1,19 @@
package com.cloudhandson.ddsbackoffice.service;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import org.junit.jupiter.api.Test;
class DdsMcpJdbcApiTest {
@Test
void shipsTheOracleJdbcDdsContextApiNeededByTheMcpExecutor() {
assertDoesNotThrow(() -> {
Class<?> context = Class.forName("oracle.jdbc.EndUserSecurityContext");
context.getMethod("createWithName", CharSequence.class, String.class, CharSequence.class);
Class<?> connection = Class.forName("oracle.jdbc.OracleConnection");
connection.getMethod("setEndUserSecurityContext", context);
connection.getMethod("clearEndUserSecurityContext");
});
}
}

View File

@@ -0,0 +1,34 @@
package com.cloudhandson.ddsbackoffice.service;
import static org.assertj.core.api.Assertions.assertThat;
import com.cloudhandson.ddsbackoffice.domain.DdsMcpAuthenticatedUser;
import com.cloudhandson.ddsbackoffice.domain.DdsMcpEndUserPrincipal;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
class DdsMcpSseServiceTest {
private final ObjectMapper objectMapper = new ObjectMapper();
@Test
void exposesNoBearerTokenToolParameter() throws Exception {
// tools/list never calls the query service; null collaborators keep this
// schema-contract test independent from bytecode-agent based mocks.
var service = new DdsMcpSseService(objectMapper,
new DdsMcpVectorSearchService(null, null, null, null));
var user = new DdsMcpAuthenticatedUser(101L, "agent", new DdsMcpEndUserPrincipal(
101L, "DDS_U_101", "DDS_U_101_ROLE", "DDS_OCI_IAM_CLIENT_SECRET_DERIVED_V1"));
var request = objectMapper.readTree("""
{"jsonrpc":"2.0","id":1,"method":"tools/list"}
""");
var response = service.handle(user, request);
var schema = response.path("result").path("tools").get(0).path("inputSchema");
assertThat(response.path("error").isMissingNode()).isTrue();
assertThat(schema.path("required").get(0).asText()).isEqualTo("query");
assertThat(schema.path("properties").has("bearerToken")).isFalse();
assertThat(schema.path("properties").path("embeddingMode").path("enum").toString())
.isEqualTo("[\"DEMO\",\"AI\"]");
}
}