diff --git a/openespi-authserver/src/main/java/org/greenbuttonalliance/espi/authserver/config/AuthorizationServerConfig.java b/openespi-authserver/src/main/java/org/greenbuttonalliance/espi/authserver/config/AuthorizationServerConfig.java index e89ab524..61171d1f 100644 --- a/openespi-authserver/src/main/java/org/greenbuttonalliance/espi/authserver/config/AuthorizationServerConfig.java +++ b/openespi-authserver/src/main/java/org/greenbuttonalliance/espi/authserver/config/AuthorizationServerConfig.java @@ -326,8 +326,26 @@ public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTe .build()) .build(); + // DataCustodian resource-server introspection client. The DC resource server calls + // POST /oauth2/introspect with these credentials (client_secret_basic) to validate opaque + // tokens; any registered confidential client may introspect. Matches DC's default + // espi.authorization-server.client-id / client-secret (datacustodian / datacustodian-secret), + // so the local sandbox introspects out of the box. + RegisteredClient dataCustodianIntrospection = RegisteredClient.withId(UUID.randomUUID().toString()) + .clientId("datacustodian") + .clientName("DataCustodian Introspection") + .clientSecret("{noop}datacustodian-secret") + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) + .clientIdIssuedAt(Instant.now()) + .clientSettings(ClientSettings.builder() + .requireAuthorizationConsent(false) + .build()) + .build(); + // Seed default clients, and reconcile existing ones to the code definition (see below). - initializeDefaultClients(repository, datacustodianAdmin, thirdPartyClient, thirdPartyAdmin); + initializeDefaultClients(repository, datacustodianAdmin, thirdPartyClient, thirdPartyAdmin, + dataCustodianIntrospection); return repository; } diff --git a/openespi-authserver/src/test/java/org/greenbuttonalliance/espi/authserver/integration/MySqlTestcontainersIntegrationTest.java b/openespi-authserver/src/test/java/org/greenbuttonalliance/espi/authserver/integration/MySqlTestcontainersIntegrationTest.java index f002da15..5dd1d6a0 100644 --- a/openespi-authserver/src/test/java/org/greenbuttonalliance/espi/authserver/integration/MySqlTestcontainersIntegrationTest.java +++ b/openespi-authserver/src/test/java/org/greenbuttonalliance/espi/authserver/integration/MySqlTestcontainersIntegrationTest.java @@ -263,7 +263,7 @@ void shouldFindAllClientsCorrectly() { List allClientIds = registeredClientAdminDao.findAllClientIds(); // Then - assertThat(allClientIds).hasSize(5); // 2 test clients + 3 default clients + assertThat(allClientIds).hasSize(6); // 2 test clients + 4 default clients (incl. datacustodian introspection) assertThat(allClientIds).contains("test-client-1", "test-client-2"); } diff --git a/openespi-datacustodian/src/main/resources/application-dev-mysql.yml b/openespi-datacustodian/src/main/resources/application-dev-mysql.yml index c12d5ad3..60e84ef4 100644 --- a/openespi-datacustodian/src/main/resources/application-dev-mysql.yml +++ b/openespi-datacustodian/src/main/resources/application-dev-mysql.yml @@ -46,5 +46,8 @@ espi: datacustodian: base-url: http://localhost:8081/DataCustodian authorization-server: - issuer-uri: http://localhost:8080 - jwk-set-uri: http://localhost:8080/.well-known/jwks.json \ No newline at end of file + # Local sandbox AS runs on :9999. Opaque-token introspection (ESPI standard) points here; + # client-id/secret default to datacustodian/datacustodian-secret (seeded on the AS). + issuer-uri: http://localhost:9999 + introspection-endpoint: http://localhost:9999/oauth2/introspect + jwk-set-uri: http://localhost:9999/.well-known/jwks.json # unused (opaque tokens; no JWT/JWK) \ No newline at end of file diff --git a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/config/JaxbMarshallingConfiguration.java b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/config/JaxbMarshallingConfiguration.java new file mode 100644 index 00000000..cabfdd0c --- /dev/null +++ b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/config/JaxbMarshallingConfiguration.java @@ -0,0 +1,53 @@ +/* + * + * Copyright (c) 2025 Green Button Alliance, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.greenbuttonalliance.espi.thirdparty.config; + +import jakarta.xml.bind.Marshaller; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.oxm.jaxb.Jaxb2Marshaller; + +import java.util.Map; + +/** + * JAXB marshalling configuration for the Third Party. + * + *

Defines the {@code atomMarshaller} {@link Jaxb2Marshaller} that the resource-fetch layer + * (e.g. {@code ResourceRESTRepositoryImpl}) requires to (un)marshal ESPI Atom feeds/entries fetched + * from the Data Custodian. The bean was missing, so the TP context failed to start (#146); this + * supplies it, scanning the shared ESPI DTO (JAXB) packages — the same context the Data Custodian's + * marshaller uses.

+ */ +@Configuration +public class JaxbMarshallingConfiguration { + + @Bean(name = "atomMarshaller") + public Jaxb2Marshaller atomMarshaller() { + Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); + // Domain entities carry no JAXB annotations (per the project's JPA/JAXB separation rule); + // the JAXB-annotated DTOs (incl. the Atom envelope DTOs) live under common.dto. + marshaller.setPackagesToScan( + "org.greenbuttonalliance.espi.common.domain", + "org.greenbuttonalliance.espi.common.dto"); + marshaller.setMarshallerProperties(Map.of( + Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE, + Marshaller.JAXB_ENCODING, "UTF-8")); + return marshaller; + } +}