From 8193f3d99e60c22a30ab39385c7769ec1af492d7 Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Fri, 9 Feb 2024 16:56:37 +0100 Subject: [PATCH] refactor: introduction of ProtocolTokenValidator (#3839) refactor: extracted BaseProtocolService into ProtocolTokenValidator --- .../ContractNegotiationIntegrationTest.java | 11 +- .../ControlPlaneServicesExtension.java | 19 +- .../catalog/CatalogProtocolServiceImpl.java | 26 ++- ...ontractNegotiationProtocolServiceImpl.java | 17 +- ...e.java => ProtocolTokenValidatorImpl.java} | 42 +--- .../TransferProcessProtocolServiceImpl.java | 18 +- .../CatalogProtocolServiceImplTest.java | 21 +- ...actNegotiationProtocolServiceImplTest.java | 45 ++--- ...ransferProcessProtocolServiceImplTest.java | 183 +++++++++--------- .../IdentityAndTrustService.java | 14 +- .../service/IdentityAndTrustServiceTest.java | 3 +- .../identity/Oauth2ServiceImplTest.java | 1 - .../iam/oauth2/daps/DapsIntegrationTest.java | 1 - .../eclipse/edc/spi/iam/IdentityService.java | 18 -- .../edc/spi/iam/VerificationContext.java | 17 +- .../spi/protocol/ProtocolTokenValidator.java | 39 ++++ 16 files changed, 225 insertions(+), 250 deletions(-) rename core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/protocol/{BaseProtocolService.java => ProtocolTokenValidatorImpl.java} (63%) create mode 100644 spi/control-plane/control-plane-spi/src/main/java/org/eclipse/edc/connector/spi/protocol/ProtocolTokenValidator.java diff --git a/core/control-plane/contract-core/src/test/java/org/eclipse/edc/connector/contract/negotiation/ContractNegotiationIntegrationTest.java b/core/control-plane/contract-core/src/test/java/org/eclipse/edc/connector/contract/negotiation/ContractNegotiationIntegrationTest.java index 3c57b5c7eec..80e8fa94147 100644 --- a/core/control-plane/contract-core/src/test/java/org/eclipse/edc/connector/contract/negotiation/ContractNegotiationIntegrationTest.java +++ b/core/control-plane/contract-core/src/test/java/org/eclipse/edc/connector/contract/negotiation/ContractNegotiationIntegrationTest.java @@ -37,15 +37,14 @@ import org.eclipse.edc.connector.policy.spi.store.PolicyDefinitionStore; import org.eclipse.edc.connector.service.contractnegotiation.ContractNegotiationProtocolServiceImpl; import org.eclipse.edc.connector.spi.contractnegotiation.ContractNegotiationProtocolService; +import org.eclipse.edc.connector.spi.protocol.ProtocolTokenValidator; import org.eclipse.edc.junit.annotations.ComponentTest; import org.eclipse.edc.policy.model.Action; import org.eclipse.edc.policy.model.Duty; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.policy.model.PolicyType; import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.iam.IdentityService; import org.eclipse.edc.spi.iam.TokenRepresentation; -import org.eclipse.edc.spi.iam.VerificationContext; import org.eclipse.edc.spi.message.RemoteMessageDispatcherRegistry; import org.eclipse.edc.spi.monitor.ConsoleMonitor; import org.eclipse.edc.spi.protocol.ProtocolWebhook; @@ -117,7 +116,7 @@ class ContractNegotiationIntegrationTest { private final ConsumerOfferResolver offerResolver = mock(); private final RemoteMessageDispatcherRegistry providerDispatcherRegistry = mock(); private final RemoteMessageDispatcherRegistry consumerDispatcherRegistry = mock(); - private final IdentityService identityService = mock(); + private final ProtocolTokenValidator protocolTokenValidator = mock(); private final ProtocolWebhook protocolWebhook = () -> "http://dummy"; protected ClaimToken token = ClaimToken.Builder.newInstance().build(); protected TokenRepresentation tokenRepresentation = TokenRepresentation.Builder.newInstance().build(); @@ -153,9 +152,9 @@ void init() { .protocolWebhook(protocolWebhook) .build(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(token)); - consumerService = new ContractNegotiationProtocolServiceImpl(consumerStore, new NoopTransactionContext(), validationService, offerResolver, identityService, mock(), new ContractNegotiationObservableImpl(), monitor, mock()); - providerService = new ContractNegotiationProtocolServiceImpl(providerStore, new NoopTransactionContext(), validationService, offerResolver, identityService, mock(), new ContractNegotiationObservableImpl(), monitor, mock()); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), any(), any())).thenReturn(ServiceResult.success(token)); + consumerService = new ContractNegotiationProtocolServiceImpl(consumerStore, new NoopTransactionContext(), validationService, offerResolver, protocolTokenValidator, new ContractNegotiationObservableImpl(), monitor, mock()); + providerService = new ContractNegotiationProtocolServiceImpl(providerStore, new NoopTransactionContext(), validationService, offerResolver, protocolTokenValidator, new ContractNegotiationObservableImpl(), monitor, mock()); } @AfterEach diff --git a/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/ControlPlaneServicesExtension.java b/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/ControlPlaneServicesExtension.java index 90aea0c8380..018ee184de4 100644 --- a/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/ControlPlaneServicesExtension.java +++ b/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/ControlPlaneServicesExtension.java @@ -37,6 +37,7 @@ import org.eclipse.edc.connector.service.contractnegotiation.ContractNegotiationServiceImpl; import org.eclipse.edc.connector.service.policydefinition.PolicyDefinitionEventListener; import org.eclipse.edc.connector.service.policydefinition.PolicyDefinitionServiceImpl; +import org.eclipse.edc.connector.service.protocol.ProtocolTokenValidatorImpl; import org.eclipse.edc.connector.service.transferprocess.TransferProcessProtocolServiceImpl; import org.eclipse.edc.connector.service.transferprocess.TransferProcessServiceImpl; import org.eclipse.edc.connector.spi.asset.AssetService; @@ -47,6 +48,7 @@ import org.eclipse.edc.connector.spi.contractnegotiation.ContractNegotiationProtocolService; import org.eclipse.edc.connector.spi.contractnegotiation.ContractNegotiationService; import org.eclipse.edc.connector.spi.policydefinition.PolicyDefinitionService; +import org.eclipse.edc.connector.spi.protocol.ProtocolTokenValidator; import org.eclipse.edc.connector.spi.transferprocess.TransferProcessProtocolService; import org.eclipse.edc.connector.spi.transferprocess.TransferProcessService; import org.eclipse.edc.connector.transfer.spi.TransferProcessManager; @@ -148,6 +150,9 @@ public class ControlPlaneServicesExtension implements ServiceExtension { @Inject private PolicyEngine policyEngine; + @Inject(required = false) + private ProtocolTokenValidator protocolTokenValidator; + @Override public String name() { return NAME; @@ -168,7 +173,7 @@ public CatalogService catalogService() { @Provider public CatalogProtocolService catalogProtocolService(ServiceExtensionContext context) { return new CatalogProtocolServiceImpl(datasetResolver, participantAgentService, dataServiceRegistry, - identityService, policyEngine, monitor, context.getParticipantId(), transactionContext); + protocolTokenValidator(), context.getParticipantId(), transactionContext); } @Provider @@ -191,7 +196,7 @@ public ContractNegotiationService contractNegotiationService() { @Provider public ContractNegotiationProtocolService contractNegotiationProtocolService() { return new ContractNegotiationProtocolServiceImpl(contractNegotiationStore, - transactionContext, contractValidationService, consumerOfferResolver, identityService, policyEngine, contractNegotiationObservable, + transactionContext, contractValidationService, consumerOfferResolver, protocolTokenValidator(), contractNegotiationObservable, monitor, telemetry); } @@ -211,6 +216,14 @@ public TransferProcessService transferProcessService() { @Provider public TransferProcessProtocolService transferProcessProtocolService() { return new TransferProcessProtocolServiceImpl(transferProcessStore, transactionContext, contractNegotiationStore, - contractValidationService, identityService, policyEngine, dataAddressValidator, transferProcessObservable, clock, monitor, telemetry); + contractValidationService, protocolTokenValidator(), dataAddressValidator, transferProcessObservable, clock, monitor, telemetry); + } + + @Provider + public ProtocolTokenValidator protocolTokenValidator() { + if (protocolTokenValidator == null) { + protocolTokenValidator = new ProtocolTokenValidatorImpl(identityService, policyEngine, monitor); + } + return protocolTokenValidator; } } diff --git a/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/catalog/CatalogProtocolServiceImpl.java b/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/catalog/CatalogProtocolServiceImpl.java index bd26793130c..70611155b3a 100644 --- a/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/catalog/CatalogProtocolServiceImpl.java +++ b/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/catalog/CatalogProtocolServiceImpl.java @@ -19,13 +19,13 @@ import org.eclipse.edc.catalog.spi.DataServiceRegistry; import org.eclipse.edc.catalog.spi.Dataset; import org.eclipse.edc.catalog.spi.DatasetResolver; -import org.eclipse.edc.connector.service.protocol.BaseProtocolService; import org.eclipse.edc.connector.spi.catalog.CatalogProtocolService; -import org.eclipse.edc.policy.engine.spi.PolicyEngine; +import org.eclipse.edc.connector.spi.protocol.ProtocolTokenValidator; +import org.eclipse.edc.policy.engine.spi.PolicyScope; +import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.agent.ParticipantAgentService; -import org.eclipse.edc.spi.iam.IdentityService; +import org.eclipse.edc.spi.iam.ClaimToken; import org.eclipse.edc.spi.iam.TokenRepresentation; -import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.result.ServiceResult; import org.eclipse.edc.transaction.spi.TransactionContext; import org.jetbrains.annotations.NotNull; @@ -33,7 +33,10 @@ import static java.lang.String.format; import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; -public class CatalogProtocolServiceImpl extends BaseProtocolService implements CatalogProtocolService { +public class CatalogProtocolServiceImpl implements CatalogProtocolService { + + @PolicyScope + public static final String CATALOGING_REQUEST_SCOPE = "request.catalog"; private static final String PARTICIPANT_ID_PROPERTY_KEY = "participantId"; @@ -43,20 +46,18 @@ public class CatalogProtocolServiceImpl extends BaseProtocolService implements C private final String participantId; private final TransactionContext transactionContext; - private PolicyEngine policyEngine; + private final ProtocolTokenValidator protocolTokenValidator; public CatalogProtocolServiceImpl(DatasetResolver datasetResolver, ParticipantAgentService participantAgentService, DataServiceRegistry dataServiceRegistry, - IdentityService identityService, - PolicyEngine policyEngine, - Monitor monitor, + ProtocolTokenValidator protocolTokenValidator, String participantId, TransactionContext transactionContext) { - super(identityService, policyEngine, monitor); this.datasetResolver = datasetResolver; this.participantAgentService = participantAgentService; this.dataServiceRegistry = dataServiceRegistry; + this.protocolTokenValidator = protocolTokenValidator; this.participantId = participantId; this.transactionContext = transactionContext; } @@ -93,4 +94,9 @@ public ServiceResult getCatalog(CatalogRequestMessage message, TokenRep return ServiceResult.success(dataset); })); } + + private ServiceResult verifyToken(TokenRepresentation tokenRepresentation) { + return protocolTokenValidator.verifyToken(tokenRepresentation, CATALOGING_REQUEST_SCOPE, Policy.Builder.newInstance().build()); + } } + diff --git a/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/contractnegotiation/ContractNegotiationProtocolServiceImpl.java b/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/contractnegotiation/ContractNegotiationProtocolServiceImpl.java index 9e1bbff852e..2f699435c47 100644 --- a/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/contractnegotiation/ContractNegotiationProtocolServiceImpl.java +++ b/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/contractnegotiation/ContractNegotiationProtocolServiceImpl.java @@ -30,13 +30,11 @@ import org.eclipse.edc.connector.contract.spi.validation.ContractValidationService; import org.eclipse.edc.connector.contract.spi.validation.ValidatableConsumerOffer; import org.eclipse.edc.connector.contract.spi.validation.ValidatedConsumerOffer; -import org.eclipse.edc.connector.service.protocol.BaseProtocolService; import org.eclipse.edc.connector.spi.contractnegotiation.ContractNegotiationProtocolService; -import org.eclipse.edc.policy.engine.spi.PolicyEngine; +import org.eclipse.edc.connector.spi.protocol.ProtocolTokenValidator; import org.eclipse.edc.policy.engine.spi.PolicyScope; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.iam.IdentityService; import org.eclipse.edc.spi.iam.TokenRepresentation; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.result.ServiceResult; @@ -51,15 +49,15 @@ import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation.Type.PROVIDER; -public class ContractNegotiationProtocolServiceImpl extends BaseProtocolService implements ContractNegotiationProtocolService { +public class ContractNegotiationProtocolServiceImpl implements ContractNegotiationProtocolService { @PolicyScope - private static final String CONTRACT_NEGOTIATION_REQUEST_SCOPE = "request.contract.negotiation"; + public static final String CONTRACT_NEGOTIATION_REQUEST_SCOPE = "request.contract.negotiation"; private final ContractNegotiationStore store; private final TransactionContext transactionContext; private final ContractValidationService validationService; private final ConsumerOfferResolver consumerOfferResolver; - + private final ProtocolTokenValidator protocolTokenValidator; private final ContractNegotiationObservable observable; private final Monitor monitor; private final Telemetry telemetry; @@ -68,15 +66,14 @@ public ContractNegotiationProtocolServiceImpl(ContractNegotiationStore store, TransactionContext transactionContext, ContractValidationService validationService, ConsumerOfferResolver consumerOfferResolver, - IdentityService identityService, - PolicyEngine policyEngine, + ProtocolTokenValidator protocolTokenValidator, ContractNegotiationObservable observable, Monitor monitor, Telemetry telemetry) { - super(identityService, policyEngine, monitor); this.store = store; this.transactionContext = transactionContext; this.validationService = validationService; this.consumerOfferResolver = consumerOfferResolver; + this.protocolTokenValidator = protocolTokenValidator; this.observable = observable; this.monitor = monitor; this.telemetry = telemetry; @@ -326,7 +323,7 @@ private ServiceResult verifyRequest(TokenRepresentation token } private ServiceResult verifyRequest(TokenRepresentation tokenRepresentation, Policy policy, ContractNegotiation contractNegotiation) { - var result = verifyToken(tokenRepresentation, CONTRACT_NEGOTIATION_REQUEST_SCOPE, policy); + var result = protocolTokenValidator.verifyToken(tokenRepresentation, CONTRACT_NEGOTIATION_REQUEST_SCOPE, policy); if (result.failed()) { monitor.debug(() -> "Verification Failed: %s".formatted(result.getFailureDetail())); return ServiceResult.notFound("Not found"); diff --git a/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/protocol/BaseProtocolService.java b/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/protocol/ProtocolTokenValidatorImpl.java similarity index 63% rename from core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/protocol/BaseProtocolService.java rename to core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/protocol/ProtocolTokenValidatorImpl.java index 40e5a141457..07126e40690 100644 --- a/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/protocol/BaseProtocolService.java +++ b/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/protocol/ProtocolTokenValidatorImpl.java @@ -14,6 +14,7 @@ package org.eclipse.edc.connector.service.protocol; +import org.eclipse.edc.connector.spi.protocol.ProtocolTokenValidator; import org.eclipse.edc.policy.engine.spi.PolicyContextImpl; import org.eclipse.edc.policy.engine.spi.PolicyEngine; import org.eclipse.edc.policy.model.Policy; @@ -25,13 +26,11 @@ import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.result.ServiceResult; -import java.util.List; - /** - * Base class for all protocol service implementation. This will contain common logic such as validating the JWT token - * and extracting the {@link ClaimToken} + * Implementation of {@link ProtocolTokenValidator} which uses the {@link PolicyEngine} for extracting + * the scope from the {@link Policy} within a scope */ -public abstract class BaseProtocolService { +public class ProtocolTokenValidatorImpl implements ProtocolTokenValidator { private final IdentityService identityService; @@ -39,7 +38,7 @@ public abstract class BaseProtocolService { private final Monitor monitor; - protected BaseProtocolService(IdentityService identityService, PolicyEngine policyEngine, Monitor monitor) { + public ProtocolTokenValidatorImpl(IdentityService identityService, PolicyEngine policyEngine, Monitor monitor) { this.identityService = identityService; this.monitor = monitor; this.policyEngine = policyEngine; @@ -49,21 +48,13 @@ protected BaseProtocolService(IdentityService identityService, PolicyEngine poli * Validate and extract the {@link ClaimToken} from the input {@link TokenRepresentation} by using the {@link IdentityService} * * @param tokenRepresentation The input {@link TokenRepresentation} + * @param policyScope The policy scope + * @param policy The {@link Policy} * @return The {@link ClaimToken} if success, failure otherwise */ - //TODO remove once this lands https://github.com/eclipse-edc/Connector/issues/3819 - protected ServiceResult verifyToken(TokenRepresentation tokenRepresentation) { - // TODO: since we are pushing here the invocation of the IdentityService we don't know the audience here - // The audience removal will be tackle next. IdentityService that relies on this parameter would not work - // for the time being. - - // TODO: policy extractors will be handled next - var verificationContext = VerificationContext.Builder.newInstance() - .policy(Policy.Builder.newInstance().build()) - .scopes(List.of()) - .build(); - - return verifyToken(tokenRepresentation, verificationContext); + @Override + public ServiceResult verifyToken(TokenRepresentation tokenRepresentation, String policyScope, Policy policy) { + return verifyToken(tokenRepresentation, createVerificationContext(policyScope, policy)); } protected ServiceResult verifyToken(TokenRepresentation tokenRepresentation, VerificationContext verificationContext) { @@ -76,19 +67,6 @@ protected ServiceResult verifyToken(TokenRepresentation tokenReprese return ServiceResult.success(result.getContent()); } - - /** - * Validate and extract the {@link ClaimToken} from the input {@link TokenRepresentation} by using the {@link IdentityService} - * - * @param tokenRepresentation The input {@link TokenRepresentation} - * @param scope The policy scope - * @param policy The {@link Policy} - * @return The {@link ClaimToken} if success, failure otherwise - */ - protected ServiceResult verifyToken(TokenRepresentation tokenRepresentation, String scope, Policy policy) { - return verifyToken(tokenRepresentation, createVerificationContext(scope, policy)); - } - private VerificationContext createVerificationContext(String scope, Policy policy) { var requestScopeBuilder = RequestScope.Builder.newInstance(); var policyContext = PolicyContextImpl.Builder.newInstance() diff --git a/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/transferprocess/TransferProcessProtocolServiceImpl.java b/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/transferprocess/TransferProcessProtocolServiceImpl.java index 471512bad93..d0f925cd90f 100644 --- a/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/transferprocess/TransferProcessProtocolServiceImpl.java +++ b/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/service/transferprocess/TransferProcessProtocolServiceImpl.java @@ -18,7 +18,7 @@ import io.opentelemetry.instrumentation.annotations.WithSpan; import org.eclipse.edc.connector.contract.spi.negotiation.store.ContractNegotiationStore; import org.eclipse.edc.connector.contract.spi.validation.ContractValidationService; -import org.eclipse.edc.connector.service.protocol.BaseProtocolService; +import org.eclipse.edc.connector.spi.protocol.ProtocolTokenValidator; import org.eclipse.edc.connector.spi.transferprocess.TransferProcessProtocolService; import org.eclipse.edc.connector.transfer.spi.observe.TransferProcessObservable; import org.eclipse.edc.connector.transfer.spi.observe.TransferProcessStartedData; @@ -31,10 +31,8 @@ import org.eclipse.edc.connector.transfer.spi.types.protocol.TransferRequestMessage; import org.eclipse.edc.connector.transfer.spi.types.protocol.TransferStartMessage; import org.eclipse.edc.connector.transfer.spi.types.protocol.TransferTerminationMessage; -import org.eclipse.edc.policy.engine.spi.PolicyEngine; import org.eclipse.edc.policy.engine.spi.PolicyScope; import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.iam.IdentityService; import org.eclipse.edc.spi.iam.TokenRepresentation; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.result.ServiceResult; @@ -55,16 +53,19 @@ import static org.eclipse.edc.connector.transfer.spi.types.TransferProcess.Type.CONSUMER; import static org.eclipse.edc.connector.transfer.spi.types.TransferProcess.Type.PROVIDER; -public class TransferProcessProtocolServiceImpl extends BaseProtocolService implements TransferProcessProtocolService { +public class TransferProcessProtocolServiceImpl implements TransferProcessProtocolService { @PolicyScope - private static final String TRANSFER_PROCESS_REQUEST_SCOPE = "request.transfer.process"; + public static final String TRANSFER_PROCESS_REQUEST_SCOPE = "request.transfer.process"; private final TransferProcessStore transferProcessStore; private final TransactionContext transactionContext; private final ContractNegotiationStore negotiationStore; private final ContractValidationService contractValidationService; private final DataAddressValidatorRegistry dataAddressValidator; private final TransferProcessObservable observable; + + private final ProtocolTokenValidator protocolTokenValidator; + private final Clock clock; private final Monitor monitor; private final Telemetry telemetry; @@ -72,15 +73,14 @@ public class TransferProcessProtocolServiceImpl extends BaseProtocolService impl public TransferProcessProtocolServiceImpl(TransferProcessStore transferProcessStore, TransactionContext transactionContext, ContractNegotiationStore negotiationStore, ContractValidationService contractValidationService, - IdentityService identityService, - PolicyEngine policyEngine, + ProtocolTokenValidator protocolTokenValidator, DataAddressValidatorRegistry dataAddressValidator, TransferProcessObservable observable, Clock clock, Monitor monitor, Telemetry telemetry) { - super(identityService, policyEngine, monitor); this.transferProcessStore = transferProcessStore; this.transactionContext = transactionContext; this.negotiationStore = negotiationStore; this.contractValidationService = contractValidationService; + this.protocolTokenValidator = protocolTokenValidator; this.dataAddressValidator = dataAddressValidator; this.observable = observable; this.clock = clock; @@ -248,7 +248,7 @@ private ServiceResult fetchRequestContext(T i } private ServiceResult verifyRequest(TokenRepresentation tokenRepresentation, TransferRequestMessageContext context) { - var result = verifyToken(tokenRepresentation, TRANSFER_PROCESS_REQUEST_SCOPE, context.agreement().getPolicy()); + var result = protocolTokenValidator.verifyToken(tokenRepresentation, TRANSFER_PROCESS_REQUEST_SCOPE, context.agreement().getPolicy()); if (result.failed()) { monitor.debug(() -> "Verification Failed: %s".formatted(result.getFailureDetail())); return ServiceResult.notFound("Not found"); diff --git a/core/control-plane/control-plane-aggregate-services/src/test/java/org/eclipse/edc/connector/service/catalog/CatalogProtocolServiceImplTest.java b/core/control-plane/control-plane-aggregate-services/src/test/java/org/eclipse/edc/connector/service/catalog/CatalogProtocolServiceImplTest.java index 1feacffad69..00195049c5b 100644 --- a/core/control-plane/control-plane-aggregate-services/src/test/java/org/eclipse/edc/connector/service/catalog/CatalogProtocolServiceImplTest.java +++ b/core/control-plane/control-plane-aggregate-services/src/test/java/org/eclipse/edc/connector/service/catalog/CatalogProtocolServiceImplTest.java @@ -20,16 +20,15 @@ import org.eclipse.edc.catalog.spi.Dataset; import org.eclipse.edc.catalog.spi.DatasetResolver; import org.eclipse.edc.catalog.spi.Distribution; +import org.eclipse.edc.connector.spi.protocol.ProtocolTokenValidator; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.agent.ParticipantAgent; import org.eclipse.edc.spi.agent.ParticipantAgentService; import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.iam.IdentityService; import org.eclipse.edc.spi.iam.TokenRepresentation; -import org.eclipse.edc.spi.iam.VerificationContext; import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.spi.result.ServiceFailure; +import org.eclipse.edc.spi.result.ServiceResult; import org.eclipse.edc.transaction.spi.NoopTransactionContext; import org.eclipse.edc.transaction.spi.TransactionContext; import org.junit.jupiter.api.Test; @@ -40,12 +39,12 @@ import static java.util.Collections.emptyMap; import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.connector.service.catalog.CatalogProtocolServiceImpl.CATALOGING_REQUEST_SCOPE; import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; import static org.eclipse.edc.spi.result.ServiceFailure.Reason.NOT_FOUND; import static org.eclipse.edc.spi.result.ServiceFailure.Reason.UNAUTHORIZED; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -56,11 +55,11 @@ class CatalogProtocolServiceImplTest { private final DatasetResolver datasetResolver = mock(); private final ParticipantAgentService participantAgentService = mock(); private final DataServiceRegistry dataServiceRegistry = mock(); - private final IdentityService identityService = mock(); + private final ProtocolTokenValidator protocolTokenValidator = mock(); private final TransactionContext transactionContext = spy(new NoopTransactionContext()); private final CatalogProtocolServiceImpl service = new CatalogProtocolServiceImpl(datasetResolver, - participantAgentService, dataServiceRegistry, identityService, mock(), mock(), "participantId", + participantAgentService, dataServiceRegistry, protocolTokenValidator, "participantId", transactionContext); @Test @@ -72,7 +71,7 @@ void getCatalog_shouldReturnCatalogWithConnectorDataServiceAndItsDataset() { var participantAgent = createParticipantAgent(); var dataService = DataService.Builder.newInstance().build(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(token)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CATALOGING_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(token)); when(dataServiceRegistry.getDataServices()).thenReturn(List.of(dataService)); when(datasetResolver.query(any(), any())).thenReturn(Stream.of(createDataset())); when(participantAgentService.createFor(any())).thenReturn(participantAgent); @@ -94,7 +93,7 @@ void getCatalog_shouldFail_whenTokenValidationFails() { var message = CatalogRequestMessage.Builder.newInstance().protocol("protocol").querySpec(querySpec).build(); var tokenRepresentation = createTokenRepresentation(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.failure("unauthorized")); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CATALOGING_REQUEST_SCOPE), any())).thenReturn(ServiceResult.unauthorized("unauthorized")); var result = service.getCatalog(message, tokenRepresentation); @@ -109,7 +108,7 @@ void getDataset_shouldReturnDataset() { var participantAgent = createParticipantAgent(); var dataset = createDataset(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CATALOGING_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(participantAgentService.createFor(any())).thenReturn(participantAgent); when(datasetResolver.getById(any(), any())).thenReturn(dataset); @@ -127,7 +126,7 @@ void getDataset_shouldFail_whenDatasetIsNull() { var tokenRepresentation = createTokenRepresentation(); var participantAgent = createParticipantAgent(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CATALOGING_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(participantAgentService.createFor(any())).thenReturn(participantAgent); when(datasetResolver.getById(any(), any())).thenReturn(null); @@ -141,7 +140,7 @@ void getDataset_shouldFail_whenTokenValidationFails() { var querySpec = QuerySpec.none(); var tokenRepresentation = createTokenRepresentation(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.failure("unauthorized")); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CATALOGING_REQUEST_SCOPE), any())).thenReturn(ServiceResult.unauthorized("unauthorized")); var result = service.getDataset("datasetId", tokenRepresentation); diff --git a/core/control-plane/control-plane-aggregate-services/src/test/java/org/eclipse/edc/connector/service/contractnegotiation/ContractNegotiationProtocolServiceImplTest.java b/core/control-plane/control-plane-aggregate-services/src/test/java/org/eclipse/edc/connector/service/contractnegotiation/ContractNegotiationProtocolServiceImplTest.java index 98a756577f8..1be9946a24a 100644 --- a/core/control-plane/control-plane-aggregate-services/src/test/java/org/eclipse/edc/connector/service/contractnegotiation/ContractNegotiationProtocolServiceImplTest.java +++ b/core/control-plane/control-plane-aggregate-services/src/test/java/org/eclipse/edc/connector/service/contractnegotiation/ContractNegotiationProtocolServiceImplTest.java @@ -31,11 +31,10 @@ import org.eclipse.edc.connector.contract.spi.validation.ValidatableConsumerOffer; import org.eclipse.edc.connector.contract.spi.validation.ValidatedConsumerOffer; import org.eclipse.edc.connector.spi.contractnegotiation.ContractNegotiationProtocolService; +import org.eclipse.edc.connector.spi.protocol.ProtocolTokenValidator; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.iam.IdentityService; import org.eclipse.edc.spi.iam.TokenRepresentation; -import org.eclipse.edc.spi.iam.VerificationContext; import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.spi.result.ServiceFailure; import org.eclipse.edc.spi.result.ServiceResult; @@ -71,6 +70,7 @@ import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiationStates.REQUESTED; import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiationStates.TERMINATED; import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiationStates.VERIFIED; +import static org.eclipse.edc.connector.service.contractnegotiation.ContractNegotiationProtocolServiceImpl.CONTRACT_NEGOTIATION_REQUEST_SCOPE; import static org.eclipse.edc.connector.service.contractnegotiation.ContractNegotiationProtocolServiceImplTest.TestFunctions.contractOffer; import static org.eclipse.edc.connector.service.contractnegotiation.ContractNegotiationProtocolServiceImplTest.TestFunctions.createPolicy; import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; @@ -98,14 +98,14 @@ class ContractNegotiationProtocolServiceImplTest { private final ConsumerOfferResolver consumerOfferResolver = mock(); private final ContractNegotiationListener listener = mock(); - private final IdentityService identityService = mock(); + private final ProtocolTokenValidator protocolTokenValidator = mock(); private ContractNegotiationProtocolService service; @BeforeEach void setUp() { var observable = new ContractNegotiationObservableImpl(); observable.registerListener(listener); - service = new ContractNegotiationProtocolServiceImpl(store, transactionContext, validationService, consumerOfferResolver, identityService, mock(), + service = new ContractNegotiationProtocolServiceImpl(store, transactionContext, validationService, consumerOfferResolver, protocolTokenValidator, observable, mock(), mock()); } @@ -125,7 +125,7 @@ void notifyRequested_shouldInitiateNegotiation_whenNegotiationDoesNotExist() { when(validatableOffer.getContractPolicy()).thenReturn(createPolicy()); when(consumerOfferResolver.resolveOffer(contractOffer.getId())).thenReturn(ServiceResult.success(validatableOffer)); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findByIdAndLease(any())).thenReturn(StoreResult.notFound("not found")); when(validationService.validateInitialOffer(claimToken, validatableOffer)).thenReturn(Result.success(validatedOffer)); @@ -168,7 +168,7 @@ void notifyRequested_shouldTransitionToRequested_whenNegotiationFound() { when(validatableOffer.getContractPolicy()).thenReturn(createPolicy()); when(consumerOfferResolver.resolveOffer(contractOffer.getId())).thenReturn(ServiceResult.success(validatableOffer)); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById(any())).thenReturn(negotiation); when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(negotiation)); when(validationService.validateInitialOffer(claimToken, validatableOffer)).thenReturn(Result.success(validatedOffer)); @@ -233,7 +233,7 @@ void notifyOffered_shouldTransitionToOffered_whenNegotiationFound() { .build(); var negotiation = createContractNegotiationRequested(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById(processId)).thenReturn(negotiation); when(store.findByIdAndLease(processId)).thenReturn(StoreResult.success(negotiation)); when(validationService.validateRequest(claimToken, negotiation)).thenReturn(Result.success()); @@ -264,7 +264,7 @@ void notifyAccepted_shouldTransitionToAccepted() { .type(ContractNegotiationEventMessage.Type.ACCEPTED) .policy(Policy.Builder.newInstance().build()) .build(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById(any())).thenReturn(contractNegotiation); when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(contractNegotiation)); when(validationService.validateRequest(eq(claimToken), any(ContractNegotiation.class))).thenReturn(Result.success()); @@ -296,7 +296,7 @@ void notifyAgreed_shouldTransitionToAgreed() { .contractAgreement(contractAgreement) .build(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById(any())).thenReturn(negotiationConsumerRequested); when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(negotiationConsumerRequested)); when(validationService.validateConfirmed(eq(claimToken), eq(contractAgreement), any(ContractOffer.class))).thenReturn(Result.success()); @@ -331,7 +331,7 @@ void notifyVerified_shouldTransitionToVerified() { .providerPid("providerPid") .build(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById(any())).thenReturn(negotiation); when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(negotiation)); when(validationService.validateRequest(any(), any(ContractNegotiation.class))).thenReturn(Result.success()); @@ -363,7 +363,7 @@ void notifyFinalized_shouldTransitionToFinalized() { var claimToken = ClaimToken.Builder.newInstance().build(); var tokenRepresentation = tokenRepresentation(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById(any())).thenReturn(negotiation); when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(negotiation)); when(validationService.validateRequest(any(), any(ContractNegotiation.class))).thenReturn(Result.success()); @@ -394,7 +394,7 @@ void notifyTerminated_shouldTransitionToTerminated() { var claimToken = claimToken(); var tokenRepresentation = tokenRepresentation(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById(any())).thenReturn(negotiation); when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(negotiation)); when(validationService.validateRequest(any(), any(ContractNegotiation.class))).thenReturn(Result.success()); @@ -418,7 +418,7 @@ void findById_shouldReturnNegotiation_whenValidCounterParty() { var contractOffer = contractOffer(); var negotiation = contractNegotiationBuilder().id(id).type(PROVIDER).contractOffer(contractOffer).state(VERIFIED.code()).build(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById(id)).thenReturn(negotiation); when(validationService.validateRequest(claimToken, negotiation)).thenReturn(Result.success()); @@ -434,7 +434,7 @@ void findById_shouldReturnNotFound_whenNegotiationNotFound() { var claimToken = claimToken(); var tokenRepresentation = tokenRepresentation(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById(any())).thenReturn(null); var result = service.findById("invalidId", tokenRepresentation); @@ -452,7 +452,7 @@ void findById_shouldReturnBadRequest_whenCounterPartyUnauthorized() { var tokenRepresentation = tokenRepresentation(); var contractOffer = contractOffer(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); var negotiation = contractNegotiationBuilder().id(id).type(PROVIDER).contractOffer(contractOffer).state(VERIFIED.code()).build(); @@ -472,7 +472,7 @@ void findById_shouldReturnBadRequest_whenCounterPartyUnauthorized() { void notify_shouldReturnNotFound_whenNotFound(MethodCall methodCall, M message) { var claimToken = claimToken(); var tokenRepresentation = tokenRepresentation(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findByIdAndLease(any())).thenReturn(StoreResult.notFound("not found")); when(store.findByCorrelationIdAndLease(any())).thenReturn(StoreResult.notFound("not found")); @@ -495,7 +495,7 @@ void notify_shouldReturnBadRequest_whenValidationFails when(validatableOffer.getContractPolicy()).thenReturn(createPolicy()); when(consumerOfferResolver.resolveOffer(any())).thenReturn(ServiceResult.success(validatableOffer)); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById(any())).thenReturn(createContractNegotiationOffered()); when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(createContractNegotiationOffered())); when(validationService.validateRequest(any(), any(ContractNegotiation.class))).thenReturn(Result.failure("validation error")); @@ -518,7 +518,7 @@ void notify_shouldReturnNotFound_whenTokenValidationFa when(validatableOffer.getContractPolicy()).thenReturn(createPolicy()); when(consumerOfferResolver.resolveOffer(any())).thenReturn(ServiceResult.success(validatableOffer)); when(store.findById(any())).thenReturn(createContractNegotiationOffered()); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.failure("unauthorized")); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.unauthorized("unauthorized")); var result = methodCall.call(service, message, tokenRepresentation); @@ -666,8 +666,7 @@ void notify_shouldStoreReceivedMessageId(Method when(validatableOffer.getContractPolicy()).thenReturn(createPolicy()); when(consumerOfferResolver.resolveOffer(any())).thenReturn(ServiceResult.success(validatableOffer)); - when(identityService.verifyJwtToken(any(), isA(VerificationContext.class))) - .thenReturn(Result.success(claimToken())); + when(protocolTokenValidator.verifyToken(any(), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken())); when(store.findById(any())).thenReturn(negotiation); when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(negotiation)); when(validationService.validateRequest(any(), any(ContractNegotiation.class))).thenReturn(Result.success()); @@ -696,8 +695,7 @@ void notify_shouldIgnoreMessage_whenAlreadyRece when(validatableOffer.getContractPolicy()).thenReturn(createPolicy()); when(consumerOfferResolver.resolveOffer(any())).thenReturn(ServiceResult.success(validatableOffer)); - when(identityService.verifyJwtToken(any(), isA(VerificationContext.class))) - .thenReturn(Result.success(claimToken())); + when(protocolTokenValidator.verifyToken(any(), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken())); when(store.findById(any())).thenReturn(negotiation); when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(negotiation)); when(validationService.validateRequest(any(), any(ContractNegotiation.class))).thenReturn(Result.success()); @@ -722,8 +720,7 @@ void notify_shouldIgnoreMessage_whenFinalState( when(validatableOffer.getContractPolicy()).thenReturn(createPolicy()); when(consumerOfferResolver.resolveOffer(any())).thenReturn(ServiceResult.success(validatableOffer)); - when(identityService.verifyJwtToken(any(), isA(VerificationContext.class))) - .thenReturn(Result.success(claimToken())); + when(protocolTokenValidator.verifyToken(any(), eq(CONTRACT_NEGOTIATION_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken())); when(store.findById(any())).thenReturn(negotiation); when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(negotiation)); when(validationService.validateRequest(any(), any(ContractNegotiation.class))).thenReturn(Result.success()); diff --git a/core/control-plane/control-plane-aggregate-services/src/test/java/org/eclipse/edc/connector/service/transferprocess/TransferProcessProtocolServiceImplTest.java b/core/control-plane/control-plane-aggregate-services/src/test/java/org/eclipse/edc/connector/service/transferprocess/TransferProcessProtocolServiceImplTest.java index 87987b702e7..0fd4a985e03 100644 --- a/core/control-plane/control-plane-aggregate-services/src/test/java/org/eclipse/edc/connector/service/transferprocess/TransferProcessProtocolServiceImplTest.java +++ b/core/control-plane/control-plane-aggregate-services/src/test/java/org/eclipse/edc/connector/service/transferprocess/TransferProcessProtocolServiceImplTest.java @@ -17,6 +17,7 @@ import org.eclipse.edc.connector.contract.spi.negotiation.store.ContractNegotiationStore; import org.eclipse.edc.connector.contract.spi.validation.ContractValidationService; +import org.eclipse.edc.connector.spi.protocol.ProtocolTokenValidator; import org.eclipse.edc.connector.spi.transferprocess.TransferProcessProtocolService; import org.eclipse.edc.connector.transfer.observe.TransferProcessObservableImpl; import org.eclipse.edc.connector.transfer.spi.observe.TransferProcessListener; @@ -30,12 +31,9 @@ import org.eclipse.edc.connector.transfer.spi.types.protocol.TransferRequestMessage; import org.eclipse.edc.connector.transfer.spi.types.protocol.TransferStartMessage; import org.eclipse.edc.connector.transfer.spi.types.protocol.TransferTerminationMessage; -import org.eclipse.edc.policy.engine.spi.PolicyEngine; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.iam.ClaimToken; -import org.eclipse.edc.spi.iam.IdentityService; import org.eclipse.edc.spi.iam.TokenRepresentation; -import org.eclipse.edc.spi.iam.VerificationContext; import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.spi.result.ServiceFailure; import org.eclipse.edc.spi.result.ServiceResult; @@ -62,6 +60,7 @@ import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.connector.service.transferprocess.TransferProcessProtocolServiceImpl.TRANSFER_PROCESS_REQUEST_SCOPE; import static org.eclipse.edc.connector.transfer.dataplane.spi.TransferDataPlaneConstants.HTTP_PROXY; import static org.eclipse.edc.connector.transfer.spi.types.TransferProcess.Type.CONSUMER; import static org.eclipse.edc.connector.transfer.spi.types.TransferProcess.Type.PROVIDER; @@ -98,9 +97,8 @@ class TransferProcessProtocolServiceImplTest { private final ContractValidationService validationService = mock(); private final DataAddressValidatorRegistry dataAddressValidator = mock(); private final TransferProcessListener listener = mock(); - private final IdentityService identityService = mock(); - private final PolicyEngine policyEngine = mock(); + private final ProtocolTokenValidator protocolTokenValidator = mock(); private TransferProcessProtocolService service; @@ -109,10 +107,8 @@ void setUp() { var observable = new TransferProcessObservableImpl(); observable.registerListener(listener); service = new TransferProcessProtocolServiceImpl(store, transactionContext, negotiationStore, validationService, - identityService, policyEngine, dataAddressValidator, observable, mock(), mock(), mock()); + protocolTokenValidator, dataAddressValidator, observable, mock(), mock(), mock()); - - when(policyEngine.evaluate(any(), any(), any())).thenReturn(Result.success()); } @Test @@ -128,7 +124,7 @@ void notifyRequested_validAgreement_shouldInitiateTransfer() { .dataDestination(DataAddress.Builder.newInstance().type("any").build()) .build(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(negotiationStore.findContractAgreement(any())).thenReturn(contractAgreement()); when(validationService.validateAgreement(any(), any())).thenReturn(Result.success(null)); when(dataAddressValidator.validateDestination(any())).thenReturn(ValidationResult.success()); @@ -159,7 +155,7 @@ void notifyRequested_doNothingIfProcessAlreadyExist() { var claimToken = claimToken(); var tokenRepresentation = tokenRepresentation(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(negotiationStore.findContractAgreement(any())).thenReturn(contractAgreement()); when(validationService.validateAgreement(any(), any())).thenReturn(Result.success(null)); when(dataAddressValidator.validateDestination(any())).thenReturn(ValidationResult.success()); @@ -184,7 +180,7 @@ void notifyRequested_invalidAgreement_shouldNotInitiateTransfer() { var claimToken = claimToken(); var tokenRepresentation = tokenRepresentation(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(negotiationStore.findContractAgreement(any())).thenReturn(contractAgreement()); when(validationService.validateAgreement(any(), any())).thenReturn(Result.failure("error")); when(dataAddressValidator.validateDestination(any())).thenReturn(ValidationResult.success()); @@ -209,7 +205,7 @@ void notifyRequested_invalidDestination_shouldNotInitiateTransfer() { .build(); when(negotiationStore.findContractAgreement(any())).thenReturn(contractAgreement()); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(dataAddressValidator.validateDestination(any())).thenReturn(ValidationResult.failure(violation("invalid data address", "path"))); var result = service.notifyRequested(message, tokenRepresentation); @@ -231,7 +227,7 @@ void notifyRequested_missingDestination_shouldInitiateTransfer() { .callbackAddress("http://any") .build(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(negotiationStore.findContractAgreement(any())).thenReturn(contractAgreement()); when(validationService.validateAgreement(any(), any())).thenReturn(Result.success(null)); @@ -264,7 +260,7 @@ void notifyStarted_shouldTransitionToStarted() { var agreement = contractAgreement(); var transferProcess = transferProcess(STARTED, "transferProcessId"); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById("correlationId")).thenReturn(transferProcess); when(store.findByIdAndLease("correlationId")).thenReturn(StoreResult.success(transferProcess)); when(negotiationStore.findContractAgreement(any())).thenReturn(agreement); @@ -297,7 +293,7 @@ void notifyStarted_shouldReturnConflict_whenTransferCannotBeStarted() { .build(); var agreement = contractAgreement(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById("correlationId")).thenReturn(transferProcess); when(store.findByIdAndLease("correlationId")).thenReturn(StoreResult.success(transferProcess)); when(negotiationStore.findContractAgreement(any())).thenReturn(agreement); @@ -326,7 +322,7 @@ void notifyStarted_shouldReturnBadRequest_whenCounterPartyUnauthorized() { var agreement = contractAgreement(); var transferProcess = transferProcess(REQUESTED, "transferProcessId"); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById("correlationId")).thenReturn(transferProcess); when(store.findByIdAndLease("correlationId")).thenReturn(StoreResult.success(transferProcess)); when(negotiationStore.findContractAgreement(any())).thenReturn(agreement); @@ -358,7 +354,7 @@ void notifyCompleted_shouldTransitionToCompleted() { var transferProcess = transferProcess(STARTED, "transferProcessId"); when(store.findById("correlationId")).thenReturn(transferProcess); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findByIdAndLease("correlationId")).thenReturn(StoreResult.success(transferProcess)); when(negotiationStore.findContractAgreement(any())).thenReturn(agreement); when(validationService.validateRequest(claimToken, agreement)).thenReturn(Result.success()); @@ -386,7 +382,7 @@ void notifyCompleted_shouldReturnConflict_whenStatusIsNotValid() { .build(); var agreement = contractAgreement(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById("correlationId")).thenReturn(transferProcess); when(store.findByIdAndLease("correlationId")).thenReturn(StoreResult.success(transferProcess)); when(negotiationStore.findContractAgreement(any())).thenReturn(agreement); @@ -415,7 +411,7 @@ void notifyCompleted_shouldReturnBadRequest_whenCounterPartyUnauthorized() { var agreement = contractAgreement(); var transferProcess = transferProcess(STARTED, "transferProcessId"); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById("correlationId")).thenReturn(transferProcess); when(store.findByIdAndLease("correlationId")).thenReturn(StoreResult.success(transferProcess)); when(negotiationStore.findContractAgreement(any())).thenReturn(agreement); @@ -448,7 +444,7 @@ void notifyTerminated_shouldTransitionToTerminated() { var agreement = contractAgreement(); var transferProcess = transferProcess(STARTED, "transferProcessId"); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById("correlationId")).thenReturn(transferProcess); when(store.findByIdAndLease("correlationId")).thenReturn(StoreResult.success(transferProcess)); when(negotiationStore.findContractAgreement(any())).thenReturn(agreement); @@ -478,7 +474,7 @@ void notifyTerminated_shouldReturnConflict_whenTransferProcessCannotBeTerminated .reason("TestReason") .build(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById("correlationId")).thenReturn(transferProcess); when(store.findByIdAndLease("correlationId")).thenReturn(StoreResult.success(transferProcess)); when(negotiationStore.findContractAgreement(any())).thenReturn(agreement); @@ -508,7 +504,7 @@ void notifyTerminated_shouldReturnBadRequest_whenCounterPartyUnauthorized() { .reason("TestReason") .build(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById("correlationId")).thenReturn(transferProcess); when(store.findByIdAndLease("correlationId")).thenReturn(StoreResult.success(transferProcess)); when(negotiationStore.findContractAgreement(any())).thenReturn(agreement); @@ -533,7 +529,7 @@ void findById_shouldReturnTransferProcess_whenValidCounterParty() { var transferProcess = transferProcess(INITIAL, processId); var agreement = contractAgreement(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById(processId)).thenReturn(transferProcess); when(negotiationStore.findContractAgreement(any())).thenReturn(agreement); when(validationService.validateRequest(claimToken, agreement)).thenReturn(Result.success()); @@ -550,7 +546,7 @@ void findById_shouldReturnNotFound_whenNegotiationNotFound() { var claimToken = claimToken(); var tokenRepresentation = tokenRepresentation(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById(any())).thenReturn(null); var result = service.findById("invalidId", tokenRepresentation); @@ -569,7 +565,7 @@ void findById_shouldReturnBadRequest_whenCounterPartyUnauthorized() { var tokenRepresentation = tokenRepresentation(); var agreement = contractAgreement(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findById(processId)).thenReturn(transferProcess); when(negotiationStore.findContractAgreement(any())).thenReturn(agreement); when(validationService.validateRequest(claimToken, agreement)).thenReturn(Result.failure("error")); @@ -588,7 +584,7 @@ void notify_shouldFail_whenTransferProcessNotFound(Met var claimToken = claimToken(); var tokenRepresentation = tokenRepresentation(); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.success(claimToken)); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken)); when(store.findByIdAndLease(any())).thenReturn(StoreResult.notFound("not found")); when(store.findByCorrelationIdAndLease(any())).thenReturn(StoreResult.notFound("not found")); @@ -607,7 +603,7 @@ void notify_shouldFail_whenTokenValidationFails(Method when(store.findById(any())).thenReturn(transferProcessBuilder().build()); when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(transferProcessBuilder().build())); when(negotiationStore.findContractAgreement(any())).thenReturn(contractAgreement()); - when(identityService.verifyJwtToken(eq(tokenRepresentation), isA(VerificationContext.class))).thenReturn(Result.failure("unauthorized")); + when(protocolTokenValidator.verifyToken(eq(tokenRepresentation), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.unauthorized("unauthorized")); var result = methodCall.call(service, message, tokenRepresentation); @@ -616,73 +612,6 @@ void notify_shouldFail_whenTokenValidationFails(Method verifyNoInteractions(listener); } - - @Nested - class IdempotencyProcessStateReplication { - - @ParameterizedTest - @ArgumentsSource(NotifyArguments.class) - void notify_shouldStoreReceivedMessageId(MethodCall methodCall, M message, - TransferProcess.Type type, - TransferProcessStates currentState) { - var transferProcess = transferProcessBuilder().state(currentState.code()).type(type).build(); - when(identityService.verifyJwtToken(any(), isA(VerificationContext.class))).thenReturn(Result.success(claimToken())); - when(store.findById(any())).thenReturn(transferProcess); - when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(transferProcess)); - when(negotiationStore.findContractAgreement(any())).thenReturn(contractAgreement()); - when(validationService.validateAgreement(any(), any())).thenAnswer(i -> Result.success(i.getArgument(1))); - when(validationService.validateRequest(any(), isA(ContractAgreement.class))).thenReturn(Result.success()); - - var result = methodCall.call(service, message, tokenRepresentation()); - - assertThat(result).isSucceeded(); - var captor = ArgumentCaptor.forClass(TransferProcess.class); - verify(store).save(captor.capture()); - var storedTransferProcess = captor.getValue(); - assertThat(storedTransferProcess.getProtocolMessages().isAlreadyReceived(message.getId())).isTrue(); - } - - @ParameterizedTest - @ArgumentsSource(NotifyArguments.class) - void notify_shouldIgnoreMessage_whenAlreadyReceived(MethodCall methodCall, M message, - TransferProcess.Type type, - TransferProcessStates currentState) { - var transferProcess = transferProcessBuilder().state(currentState.code()).type(type).build(); - transferProcess.protocolMessageReceived(message.getId()); - when(identityService.verifyJwtToken(any(), isA(VerificationContext.class))).thenReturn(Result.success(claimToken())); - when(store.findById(any())).thenReturn(transferProcess); - when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(transferProcess)); - when(negotiationStore.findContractAgreement(any())).thenReturn(contractAgreement()); - when(validationService.validateAgreement(any(), any())).thenAnswer(i -> Result.success(i.getArgument(1))); - when(validationService.validateRequest(any(), isA(ContractAgreement.class))).thenReturn(Result.success()); - - var result = methodCall.call(service, message, tokenRepresentation()); - - assertThat(result).isSucceeded(); - verify(store, never()).save(any()); - verifyNoInteractions(listener); - } - - @ParameterizedTest - @ArgumentsSource(NotifyArguments.class) - void notify_shouldIgnoreMessage_whenFinalState(MethodCall methodCall, M message, - TransferProcess.Type type) { - var transferProcess = transferProcessBuilder().state(COMPLETED.code()).type(type).build(); - when(identityService.verifyJwtToken(any(), isA(VerificationContext.class))).thenReturn(Result.success(claimToken())); - when(store.findById(any())).thenReturn(transferProcess); - when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(transferProcess)); - when(negotiationStore.findContractAgreement(any())).thenReturn(contractAgreement()); - when(validationService.validateAgreement(any(), any())).thenAnswer(i -> Result.success(i.getArgument(1))); - when(validationService.validateRequest(any(), isA(ContractAgreement.class))).thenReturn(Result.success()); - - var result = methodCall.call(service, message, tokenRepresentation()); - - assertThat(result).isSucceeded(); - verify(store, never()).save(any()); - verifyNoInteractions(listener); - } - } - private TransferProcess transferProcess(TransferProcessStates state, String id) { return transferProcessBuilder() .id(id) @@ -756,4 +685,70 @@ private M build(TransferRemoteMessage.Builder< return builder.protocol("protocol").counterPartyAddress("http://any").processId("correlationId").build(); } } + + @Nested + class IdempotencyProcessStateReplication { + + @ParameterizedTest + @ArgumentsSource(NotifyArguments.class) + void notify_shouldStoreReceivedMessageId(MethodCall methodCall, M message, + TransferProcess.Type type, + TransferProcessStates currentState) { + var transferProcess = transferProcessBuilder().state(currentState.code()).type(type).build(); + when(protocolTokenValidator.verifyToken(any(), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken())); + when(store.findById(any())).thenReturn(transferProcess); + when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(transferProcess)); + when(negotiationStore.findContractAgreement(any())).thenReturn(contractAgreement()); + when(validationService.validateAgreement(any(), any())).thenAnswer(i -> Result.success(i.getArgument(1))); + when(validationService.validateRequest(any(), isA(ContractAgreement.class))).thenReturn(Result.success()); + + var result = methodCall.call(service, message, tokenRepresentation()); + + assertThat(result).isSucceeded(); + var captor = ArgumentCaptor.forClass(TransferProcess.class); + verify(store).save(captor.capture()); + var storedTransferProcess = captor.getValue(); + assertThat(storedTransferProcess.getProtocolMessages().isAlreadyReceived(message.getId())).isTrue(); + } + + @ParameterizedTest + @ArgumentsSource(NotifyArguments.class) + void notify_shouldIgnoreMessage_whenAlreadyReceived(MethodCall methodCall, M message, + TransferProcess.Type type, + TransferProcessStates currentState) { + var transferProcess = transferProcessBuilder().state(currentState.code()).type(type).build(); + transferProcess.protocolMessageReceived(message.getId()); + when(protocolTokenValidator.verifyToken(any(), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken())); + when(store.findById(any())).thenReturn(transferProcess); + when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(transferProcess)); + when(negotiationStore.findContractAgreement(any())).thenReturn(contractAgreement()); + when(validationService.validateAgreement(any(), any())).thenAnswer(i -> Result.success(i.getArgument(1))); + when(validationService.validateRequest(any(), isA(ContractAgreement.class))).thenReturn(Result.success()); + + var result = methodCall.call(service, message, tokenRepresentation()); + + assertThat(result).isSucceeded(); + verify(store, never()).save(any()); + verifyNoInteractions(listener); + } + + @ParameterizedTest + @ArgumentsSource(NotifyArguments.class) + void notify_shouldIgnoreMessage_whenFinalState(MethodCall methodCall, M message, + TransferProcess.Type type) { + var transferProcess = transferProcessBuilder().state(COMPLETED.code()).type(type).build(); + when(protocolTokenValidator.verifyToken(any(), eq(TRANSFER_PROCESS_REQUEST_SCOPE), any())).thenReturn(ServiceResult.success(claimToken())); + when(store.findById(any())).thenReturn(transferProcess); + when(store.findByIdAndLease(any())).thenReturn(StoreResult.success(transferProcess)); + when(negotiationStore.findContractAgreement(any())).thenReturn(contractAgreement()); + when(validationService.validateAgreement(any(), any())).thenAnswer(i -> Result.success(i.getArgument(1))); + when(validationService.validateRequest(any(), isA(ContractAgreement.class))).thenReturn(Result.success()); + + var result = methodCall.call(service, message, tokenRepresentation()); + + assertThat(result).isSucceeded(); + verify(store, never()).save(any()); + verifyNoInteractions(listener); + } + } } diff --git a/extensions/common/iam/identity-trust/identity-trust-service/src/main/java/org/eclipse/edc/iam/identitytrust/IdentityAndTrustService.java b/extensions/common/iam/identity-trust/identity-trust-service/src/main/java/org/eclipse/edc/iam/identitytrust/IdentityAndTrustService.java index 91b24c41156..48ab924a1fd 100644 --- a/extensions/common/iam/identity-trust/identity-trust-service/src/main/java/org/eclipse/edc/iam/identitytrust/IdentityAndTrustService.java +++ b/extensions/common/iam/identity-trust/identity-trust-service/src/main/java/org/eclipse/edc/iam/identitytrust/IdentityAndTrustService.java @@ -14,7 +14,6 @@ package org.eclipse.edc.iam.identitytrust; -import com.nimbusds.jwt.SignedJWT; import org.eclipse.edc.iam.identitytrust.validation.rules.HasValidIssuer; import org.eclipse.edc.iam.identitytrust.validation.rules.HasValidSubjectIds; import org.eclipse.edc.iam.identitytrust.validation.rules.IsNotExpired; @@ -38,7 +37,6 @@ import org.eclipse.edc.util.string.StringUtils; import org.jetbrains.annotations.NotNull; -import java.text.ParseException; import java.time.Clock; import java.time.Instant; import java.time.temporal.ChronoUnit; @@ -144,16 +142,6 @@ public Result verifyJwtToken(TokenRepresentation tokenRepresentation var accessToken = claimToken.getStringClaim(PRESENTATION_ACCESS_TOKEN_CLAIM); var issuer = claimToken.getStringClaim(ISSUER); - /* TODO: DEMO the scopes should be extracted elsewhere. replace this section!!############################*/ - //TODO remove once this lands https://github.com/eclipse-edc/Connector/issues/3819 - var scopes = new ArrayList(); - try { - var scope = SignedJWT.parse(accessToken).getJWTClaimsSet().getStringClaim("scope"); - scopes.add(scope); - } catch (ParseException e) { - throw new RuntimeException(e); - } - var siTokenClaims = Map.of(PRESENTATION_ACCESS_TOKEN_CLAIM, accessToken, ISSUED_AT, Instant.now().toString(), AUDIENCE, issuer, @@ -168,7 +156,7 @@ public Result verifyJwtToken(TokenRepresentation tokenRepresentation // get CS Url, execute VP request var vpResponse = credentialServiceUrlResolver.resolve(issuer) - .compose(url -> credentialServiceClient.requestPresentation(url, siTokenString, scopes)); + .compose(url -> credentialServiceClient.requestPresentation(url, siTokenString, context.getScopes().stream().toList())); if (vpResponse.failed()) { return vpResponse.mapTo(); diff --git a/extensions/common/iam/identity-trust/identity-trust-service/src/test/java/org/eclipse/edc/iam/identitytrust/service/IdentityAndTrustServiceTest.java b/extensions/common/iam/identity-trust/identity-trust-service/src/test/java/org/eclipse/edc/iam/identitytrust/service/IdentityAndTrustServiceTest.java index 2aeb7ec4d84..9f2394dede7 100644 --- a/extensions/common/iam/identity-trust/identity-trust-service/src/test/java/org/eclipse/edc/iam/identitytrust/service/IdentityAndTrustServiceTest.java +++ b/extensions/common/iam/identity-trust/identity-trust-service/src/test/java/org/eclipse/edc/iam/identitytrust/service/IdentityAndTrustServiceTest.java @@ -71,7 +71,7 @@ class IdentityAndTrustServiceTest { public static final String EXPECTED_OWN_DID = "did:web:test"; - + public static final String CONSUMER_DID = "did:web:consumer"; private final SecureTokenService mockedSts = mock(); private final PresentationVerifier mockedVerifier = mock(); @@ -96,7 +96,6 @@ void setup() { private VerificationContext verificationContext() { return VerificationContext.Builder.newInstance() - .audience("test-audience") .policy(Policy.Builder.newInstance().build()) .build(); } diff --git a/extensions/common/iam/oauth2/oauth2-core/src/test/java/org/eclipse/edc/iam/oauth2/identity/Oauth2ServiceImplTest.java b/extensions/common/iam/oauth2/oauth2-core/src/test/java/org/eclipse/edc/iam/oauth2/identity/Oauth2ServiceImplTest.java index a269c4b284a..b506dfb128a 100644 --- a/extensions/common/iam/oauth2/oauth2-core/src/test/java/org/eclipse/edc/iam/oauth2/identity/Oauth2ServiceImplTest.java +++ b/extensions/common/iam/oauth2/oauth2-core/src/test/java/org/eclipse/edc/iam/oauth2/identity/Oauth2ServiceImplTest.java @@ -76,7 +76,6 @@ class Oauth2ServiceImplTest { private static final String ENDPOINT_AUDIENCE = "endpoint-audience-test"; private static final VerificationContext VERIFICATION_CONTEXT = VerificationContext.Builder.newInstance() - .audience(ENDPOINT_AUDIENCE) .policy(Policy.Builder.newInstance().build()) .build(); private static final String OAUTH2_SERVER_URL = "http://oauth2-server.com"; diff --git a/extensions/common/iam/oauth2/oauth2-daps/src/test/java/org/eclipse/edc/iam/oauth2/daps/DapsIntegrationTest.java b/extensions/common/iam/oauth2/oauth2-daps/src/test/java/org/eclipse/edc/iam/oauth2/daps/DapsIntegrationTest.java index dd60006f838..6b725e84487 100644 --- a/extensions/common/iam/oauth2/oauth2-daps/src/test/java/org/eclipse/edc/iam/oauth2/daps/DapsIntegrationTest.java +++ b/extensions/common/iam/oauth2/oauth2-daps/src/test/java/org/eclipse/edc/iam/oauth2/daps/DapsIntegrationTest.java @@ -63,7 +63,6 @@ void retrieveTokenAndValidate(IdentityService identityService) { assertThat(tokenResult.succeeded()).withFailMessage(tokenResult::getFailureDetail).isTrue(); var verificationContext = VerificationContext.Builder.newInstance() - .audience("audience") .policy(Policy.Builder.newInstance().build()) .build(); diff --git a/spi/common/core-spi/src/main/java/org/eclipse/edc/spi/iam/IdentityService.java b/spi/common/core-spi/src/main/java/org/eclipse/edc/spi/iam/IdentityService.java index 6398f4097af..518d4989e76 100644 --- a/spi/common/core-spi/src/main/java/org/eclipse/edc/spi/iam/IdentityService.java +++ b/spi/common/core-spi/src/main/java/org/eclipse/edc/spi/iam/IdentityService.java @@ -16,7 +16,6 @@ package org.eclipse.edc.spi.iam; -import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; import org.eclipse.edc.spi.result.Result; @@ -44,21 +43,4 @@ public interface IdentityService { */ Result verifyJwtToken(TokenRepresentation tokenRepresentation, VerificationContext context); - /** - * Verifies a JWT bearer token. - * - * @param tokenRepresentation A token representation including the token to verify. - * @param audience The audience. - * @return Result of the validation. - * @deprecated please use {@link #verifyJwtToken(TokenRepresentation, VerificationContext)} - */ - - @Deprecated(since = "0.4.2", forRemoval = true) - default Result verifyJwtToken(TokenRepresentation tokenRepresentation, String audience) { - var context = VerificationContext.Builder.newInstance() - .audience(audience) - .policy(Policy.Builder.newInstance().build()) - .build(); - return verifyJwtToken(tokenRepresentation, context); - } } diff --git a/spi/common/core-spi/src/main/java/org/eclipse/edc/spi/iam/VerificationContext.java b/spi/common/core-spi/src/main/java/org/eclipse/edc/spi/iam/VerificationContext.java index 30c1ef661c0..8650a399197 100644 --- a/spi/common/core-spi/src/main/java/org/eclipse/edc/spi/iam/VerificationContext.java +++ b/spi/common/core-spi/src/main/java/org/eclipse/edc/spi/iam/VerificationContext.java @@ -28,24 +28,14 @@ */ public class VerificationContext { - // TODO it will be removed with the TokenGenerator/Verification refactor - @Deprecated - private String audience; + private final Map, Object> additional; private Policy policy; - private Set scopes = new HashSet<>(); - private final Map, Object> additional; private VerificationContext() { additional = new HashMap<>(); } - /** - * Returns the audience or null if not available. - */ - public String getAudience() { - return audience; - } /** * Returns the {@link Policy} associated with the verification context @@ -88,11 +78,6 @@ public static Builder newInstance() { return new Builder(); } - public Builder audience(String audience) { - context.audience = audience; - return this; - } - public Builder data(Class clazz, Object object) { context.additional.put(clazz, object); return this; diff --git a/spi/control-plane/control-plane-spi/src/main/java/org/eclipse/edc/connector/spi/protocol/ProtocolTokenValidator.java b/spi/control-plane/control-plane-spi/src/main/java/org/eclipse/edc/connector/spi/protocol/ProtocolTokenValidator.java new file mode 100644 index 00000000000..b14fba9342e --- /dev/null +++ b/spi/control-plane/control-plane-spi/src/main/java/org/eclipse/edc/connector/spi/protocol/ProtocolTokenValidator.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.connector.spi.protocol; + +import org.eclipse.edc.policy.model.Policy; +import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; +import org.eclipse.edc.spi.iam.ClaimToken; +import org.eclipse.edc.spi.iam.TokenRepresentation; +import org.eclipse.edc.spi.result.ServiceResult; + +/** + * Token validator to be used in protocol layer for verifying the token according the + * input policy and policy scope + */ +@ExtensionPoint +public interface ProtocolTokenValidator { + + /** + * Verify the {@link TokenRepresentation} in the context of a policy + * + * @param tokenRepresentation The token + * @param policyScope The policy scope + * @param policy The policy + * @return Returns the extracted {@link ClaimToken} if successful, failure otherwise + */ + ServiceResult verifyToken(TokenRepresentation tokenRepresentation, String policyScope, Policy policy); +}