Skip to content

Commit

Permalink
Merge branch 'feature/436058-auditoria' into 'develop'
Browse files Browse the repository at this point in the history
Feature/436058 auditoria

See merge request upm-inesdata/inesdata-connector!27
  • Loading branch information
ralconada-gmv committed Jul 5, 2024
2 parents fcdea1f + 4df735f commit ee77925
Show file tree
Hide file tree
Showing 13 changed files with 288 additions and 0 deletions.
13 changes: 13 additions & 0 deletions extensions/audit-configuration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Audit Extension

This extension provides the capability to log audit events for HTTP requests made to the connector management API. The `AuditExtension` registers an `HttpRequestInterceptor` that logs details about the user making the request and the request URI.

## Features

- Logs audit details for incoming HTTP requests.
- Extracts and verifies JWT tokens to log the username.
- Configurable participant ID for audit logging.

## Configuration

To configure the audit logging, you need to ensure that the `HttpRequestInterceptor` is registered with the web service. This is done within the `AuditExtension` class.
17 changes: 17 additions & 0 deletions extensions/audit-configuration/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
plugins {
`java-library`
id("com.gmv.inesdata.edc-application")
}

dependencies {
api(libs.edc.auth.spi)

implementation(libs.edc.iam.oauth2.core)
implementation(libs.jakarta.rsApi)

testImplementation(libs.edc.core.junit)
testImplementation(libs.assertj)
testImplementation(libs.mockito.core)
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.upm.inesdata.audit;

import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.runtime.metamodel.annotation.Provides;
import org.eclipse.edc.spi.iam.IdentityService;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.web.spi.WebService;
import org.eclipse.edc.web.spi.configuration.ApiContext;

/**
* Service extension for configuring audit logging.
* Registers the {@link HttpRequestInterceptor} as a resource with the web service.
*/
@Provides(HttpRequestInterceptor.class)
@Extension(value = AuditExtension.NAME)
public class AuditExtension implements ServiceExtension {

public static final String NAME = "Audit configuration";

@Inject
private WebService webService;

@Inject
private IdentityService identityService;

/**
* Returns the name of the extension.
*
* @return the name of the extension
*/
@Override
public String name() {
return NAME;
}

/**
* Initializes the service extension by registering the {@link HttpRequestInterceptor} with the web service.
*
* @param context the service extension context providing configuration and services
*/
@Override
public void initialize(ServiceExtensionContext context) {
webService.registerResource(ApiContext.MANAGEMENT, new HttpRequestInterceptor(context.getMonitor(), identityService, context.getParticipantId()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package org.upm.inesdata.audit;

import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.ext.Provider;
import org.eclipse.edc.spi.iam.IdentityService;
import org.eclipse.edc.spi.iam.TokenRepresentation;
import org.eclipse.edc.spi.monitor.Monitor;

import java.text.MessageFormat;
import java.util.List;

/**
* Intercepts HTTP requests to audit user actions.
* Logs details about the user and the request URI.
*/
@Provider
public class HttpRequestInterceptor implements ContainerRequestFilter {

private static final String TEMPLATE_AUDIT_LOG = "[AUDIT][{0}][MANAGEMENT] User ''{1}'' calls ''{2}''";
private static final String BEARER_PREFIX = "Bearer ";
private static final String TOKEN_PROPERTY_PREFERRED_USERNAME = "preferred_username";
private static final String TOKEN_USER_NOT_VALID_USER = "NotValidUser";
private static final String TOKEN_USER_NOT_AUTHENTICATED_USER = "NotAuthenticatedUser";

private final Monitor monitor;
private final IdentityService identityService;
private final String participantId;

/**
* Constructor for HttpRequestInterceptor.
*
* @param monitor the monitor interface used for logging
* @param identityService the identity service for token verification
* @param participantId the participant ID for audit log entries
*/
public HttpRequestInterceptor(Monitor monitor, IdentityService identityService, String participantId) {
this.monitor = monitor;
this.identityService = identityService;
this.participantId = participantId;
}

/**
* Filters the HTTP request context to log audit details.
*
* @param requestContext the container request context
*/
@Override
public void filter(ContainerRequestContext requestContext) {
String user = getUserFromRequest(requestContext);
String auditLog = MessageFormat.format(TEMPLATE_AUDIT_LOG, participantId, user, requestContext.getUriInfo().getRequestUri().toString());
monitor.info(auditLog);
}

/**
* Extracts the user from the HTTP request context by decoding the JWT token.
*
* @param requestContext the container request context
* @return the username extracted from the token, or a default value if not authenticated or token is invalid
*/
private String getUserFromRequest(ContainerRequestContext requestContext) {
List<String> authorizationHeaders = requestContext.getHeaders().get("Authorization");
if (authorizationHeaders != null && !authorizationHeaders.isEmpty()) {
String authHeader = authorizationHeaders.get(0);
if (authHeader.startsWith(BEARER_PREFIX)) {
String token = authHeader.replace(BEARER_PREFIX, "");
return decodeJWT(token);
}
}
return TOKEN_USER_NOT_AUTHENTICATED_USER;
}

/**
* Decodes the JWT token to extract the username.
*
* @param token the JWT token
* @return the username if token is valid, otherwise a default value indicating the token is not valid
*/
private String decodeJWT(String token) {
var tokenRepresentation = TokenRepresentation.Builder.newInstance().token(token).build();
var tokenValidation = identityService.verifyJwtToken(tokenRepresentation, null);
if (tokenValidation.failed()) {
return TOKEN_USER_NOT_VALID_USER;
}
return (String) tokenValidation.getContent().getClaims().get(TOKEN_PROPERTY_PREFERRED_USERNAME);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.upm.inesdata.audit.AuditExtension
14 changes: 14 additions & 0 deletions extensions/audit-event-configuration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# AuditEventSubscriptionExtension

This extension provides the capability to subscribe to audit events related to contract negotiations and transfer processes. The `AuditEventSubscriber` logs details about these events using the provided monitoring interface.

## Features

- Logs audit details for contract negotiation and transfer process events.
- Registers the `AuditEventSubscriber` with the event router for both asynchronous and synchronous event handling.

## Configuration

To configure the `AuditEventSubscriptionExtension`, ensure that the `AuditEventSubscriber` is registered with the event router. This is done within the `AuditEventSubscriptionExtension` class.


18 changes: 18 additions & 0 deletions extensions/audit-event-configuration/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
plugins {
`java-library`
id("com.gmv.inesdata.edc-application")
}

dependencies {
api(libs.edc.auth.spi)

implementation(libs.jakarta.rsApi)
implementation(libs.edc.contract.spi)
implementation(libs.edc.transfer.spi)

testImplementation(libs.edc.core.junit)
testImplementation(libs.assertj)
testImplementation(libs.mockito.core)
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.upm.inesdata.auditevent;

import org.eclipse.edc.connector.controlplane.contract.spi.event.contractnegotiation.ContractNegotiationEvent;
import org.eclipse.edc.connector.controlplane.transfer.spi.event.TransferProcessEvent;
import org.eclipse.edc.spi.event.Event;
import org.eclipse.edc.spi.event.EventEnvelope;
import org.eclipse.edc.spi.event.EventSubscriber;
import org.eclipse.edc.spi.monitor.Monitor;

import java.text.MessageFormat;

/**
* Subscriber for auditing contract negotiation and transfer process events.
* Logs event details using a specified monitoring interface.
*/
public class AuditEventSubscriber implements EventSubscriber {

private static final String NEGOTIATION_TEMPLATE_AUDIT_LOG = "[AUDIT][{0}][DSP] ''{1}'' from counterPartyId ''{2}'' with contractNegotiationId ''{3}''";
private static final String TRANSFER_TEMPLATE_AUDIT_LOG = "[AUDIT][{0}][DSP] ''{1}'' from contractId ''{2}'' with assetId ''{3}'' for type ''{4}''";
private final Monitor monitor;
private final String participantId;

/**
* Constructor for AuditEventSubscriber.
*
* @param monitor the monitor interface used for logging
* @param participantId the participant ID for audit log entries
*/
public AuditEventSubscriber(Monitor monitor, String participantId) {
this.monitor = monitor;
this.participantId = participantId;
}

/**
* Processes the received event envelope and logs relevant details based on event type.
*
* @param event the event envelope containing the event to be processed
* @param <E> the type of the event
*/
@Override
public <E extends Event> void on(EventEnvelope<E> event) {
if (event.getPayload() instanceof ContractNegotiationEvent) {
String simpleName = event.getPayload().getClass().getSimpleName();
ContractNegotiationEvent payload = (ContractNegotiationEvent) event.getPayload();
monitor.info(MessageFormat.format(NEGOTIATION_TEMPLATE_AUDIT_LOG, participantId, simpleName, payload.getCounterPartyId(), payload.getContractNegotiationId()));
} else if (event.getPayload() instanceof TransferProcessEvent) {
String simpleName = event.getPayload().getClass().getSimpleName();
TransferProcessEvent payload = (TransferProcessEvent) event.getPayload();
monitor.info(MessageFormat.format(TRANSFER_TEMPLATE_AUDIT_LOG, participantId, simpleName, payload.getContractId(), payload.getAssetId(), payload.getType()));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.upm.inesdata.auditevent;

import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.spi.event.Event;
import org.eclipse.edc.spi.event.EventRouter;
import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;

/**
* Service extension for subscribing to audit events.
* Registers the {@link AuditEventSubscriber} with the event router for both asynchronous and synchronous event handling.
*/
public class AuditEventSubscriptionExtension implements ServiceExtension {
@Inject
private EventRouter eventRouter;
@Inject
private Monitor monitor;

/**
* Initializes the service extension by registering the {@link AuditEventSubscriber} with the event router.
*
* @param context the service extension context providing configuration and services
*/
@Override
public void initialize(ServiceExtensionContext context) {
eventRouter.register(Event.class, new AuditEventSubscriber(monitor, context.getParticipantId()));
eventRouter.registerSync(Event.class, new AuditEventSubscriber(monitor, context.getParticipantId()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.upm.inesdata.auditevent.AuditEventSubscriptionExtension
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ edc-auth-spi = { module = "org.eclipse.edc:auth-spi", version.ref = "edc" }
edc-build-plugin = { module = "org.eclipse.edc.edc-build:org.eclipse.edc.edc-build.gradle.plugin", version.ref = "edc" }
edc-configuration-filesystem = { module = "org.eclipse.edc:configuration-filesystem", version.ref = "edc" }
edc-connector-core = { module = "org.eclipse.edc:connector-core", version.ref = "edc" }
edc-contract-spi = { module = "org.eclipse.edc:contract-spi", version.ref = "edc" }
edc-control-plane-api = { module = "org.eclipse.edc:control-plane-api", version.ref = "edc" }
edc-control-plane-api-client = { module = "org.eclipse.edc:control-plane-api-client", version.ref = "edc" }
edc-control-plane-core = { module = "org.eclipse.edc:control-plane-core", version.ref = "edc" }
Expand Down Expand Up @@ -62,6 +63,7 @@ edc-transaction-local = { module = "org.eclipse.edc:transaction-local", version.
edc-transaction-spi = { module = "org.eclipse.edc:transaction-spi", version.ref = "edc" }
edc-transfer-data-plane-signaling = { module = "org.eclipse.edc:transfer-data-plane-signaling", version.ref = "edc" }
edc-transfer-pull-http-receiver = { module = "org.eclipse.edc:transfer-pull-http-dynamic-receiver", version.ref = "edc" }
edc-transfer-spi = { module = "org.eclipse.edc:transfer-spi", version.ref = "edc" }
edc-validator-data-address-http-data = { module = "org.eclipse.edc:validator-data-address-http-data", version.ref = "edc" }
edc-validator-spi = { module = "org.eclipse.edc:validator-spi", version.ref = "edc" }
edc-vault-hashicorp = { module = "org.eclipse.edc:vault-hashicorp", version.ref = "edc" }
Expand Down
4 changes: 4 additions & 0 deletions launchers/connector/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ dependencies {
//Data plane public api
implementation(project(":extensions:extended-data-plane-public-api"))

// Audit
implementation(project(":extensions:audit-configuration"))
implementation(project(":extensions:audit-event-configuration"))

runtimeOnly(libs.edc.transaction.local)
runtimeOnly(libs.postgres)
}
Expand Down
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ include(":extensions:federated-catalog-cache-api")
include(":extensions:count-elements-api")
include(":extensions:count-elements-sql")
include(":extensions:extended-data-plane-public-api")
include(":extensions:audit-configuration")
include(":extensions:audit-event-configuration")

// Connector
include(":launchers:connector")

0 comments on commit ee77925

Please sign in to comment.