getHeaderNames() {
+ return Collections.enumeration(headers.keySet());
+ }
+
+ @Override
+ public long getDateHeader(String name) {
+ String value = getHeader(name);
+ if (value == null) {
+ return -1L;
+ }
+ try {
+ return super.getDateHeader(name);
+ } catch (IllegalArgumentException e) {
+ return -1L;
+ }
+ }
+
+ @Override
+ public int getIntHeader(String name) {
+ String value = getHeader(name);
+ if (value == null) {
+ return -1;
+ }
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/jans-lock/lock-server/service/src/main/java/io/jans/lock/service/openid/OpenIdProtection.java b/jans-lock/lock-server/service/src/main/java/io/jans/lock/service/openid/OpenIdProtection.java
new file mode 100644
index 00000000000..475248039b8
--- /dev/null
+++ b/jans-lock/lock-server/service/src/main/java/io/jans/lock/service/openid/OpenIdProtection.java
@@ -0,0 +1,21 @@
+/*
+ * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text.
+ *
+ * Copyright (c) 2022, Janssen Project
+ */
+
+package io.jans.lock.service.openid;
+
+import io.jans.service.security.protect.BaseAuthorizationProtection;
+import jakarta.ws.rs.container.ResourceInfo;
+import jakarta.ws.rs.core.Response;
+
+public interface OpenIdProtection extends BaseAuthorizationProtection {
+
+ Response processAuthorization(String bearerToken, ResourceInfo resourceInfo);
+
+ public static Response simpleResponse(Response.Status status, String detail) {
+ return Response.status(status).entity(detail).build();
+ }
+
+}
\ No newline at end of file
diff --git a/jans-lock/lock-server/service/src/main/java/io/jans/lock/service/filter/openid/OpenIdProtectionService.java b/jans-lock/lock-server/service/src/main/java/io/jans/lock/service/openid/OpenIdProtectionService.java
similarity index 93%
rename from jans-lock/lock-server/service/src/main/java/io/jans/lock/service/filter/openid/OpenIdProtectionService.java
rename to jans-lock/lock-server/service/src/main/java/io/jans/lock/service/openid/OpenIdProtectionService.java
index 4e9e4724a97..c1d38d444d5 100644
--- a/jans-lock/lock-server/service/src/main/java/io/jans/lock/service/filter/openid/OpenIdProtectionService.java
+++ b/jans-lock/lock-server/service/src/main/java/io/jans/lock/service/openid/OpenIdProtectionService.java
@@ -1,4 +1,4 @@
-package io.jans.lock.service.filter.openid;
+package io.jans.lock.service.openid;
import static jakarta.ws.rs.core.Response.Status.FORBIDDEN;
import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
@@ -33,13 +33,11 @@
import io.jans.as.model.jwt.JwtClaimName;
import io.jans.as.model.jwt.JwtClaims;
import io.jans.lock.service.OpenIdService;
-import io.jans.lock.service.filter.OpenIdProtection;
import io.jans.service.security.api.ProtectedApi;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.container.ResourceInfo;
-import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Response;
@ApplicationScoped
@@ -76,14 +74,13 @@ private void init() {
* Accepts either a JWT or an opaque token. For opaque tokens, performs token introspection. For JWTs, validates issuer, expiration,
* cryptographic signature (HMAC-signed tokens are rejected), and required scopes for the target resource.
*
- * @param headers HTTP headers containing the Authorization header
+ * @param bearerToken Authorization bearer token
* @param resourceInfo information about the target resource used to determine required scopes
* @return a Response describing the authorization failure (UNAUTHORIZED, FORBIDDEN, or INTERNAL_SERVER_ERROR) or `null` if authorization succeeds
*/
- public Response processAuthorization(HttpHeaders headers, ResourceInfo resourceInfo) {
+ public Response processAuthorization(String bearerToken, ResourceInfo resourceInfo) {
try {
- String token = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
- boolean authFound = StringUtils.isNotEmpty(token);
+ boolean authFound = StringUtils.isNotEmpty(bearerToken);
log.debug("Authorization header{} found", authFound ? "" : " not");
if (!authFound) {
@@ -92,18 +89,18 @@ public Response processAuthorization(HttpHeaders headers, ResourceInfo resourceI
return simpleResponse(UNAUTHORIZED, "No authorization header found");
}
- token = token.replaceFirst("Bearer\\s+","");
+ bearerToken = bearerToken.replaceFirst("Bearer\\s+","");
log.debug("Validating bearer token");
List scopes = getRequestedScopes(resourceInfo);
log.debug("Call requires scopes: {}", scopes);
- Jwt jwt = tokenAsJwt(token);
+ Jwt jwt = tokenAsJwt(bearerToken);
if (jwt == null) {
// Do standard token validation
IntrospectionResponse iresp = null;
try {
- iresp = introspectionService.introspectToken("Bearer " + token, token);
+ iresp = introspectionService.introspectToken("Bearer " + bearerToken, bearerToken);
} catch (Exception e) {
log.error(e.getMessage());
}
diff --git a/jans-lock/lock-server/service/src/main/java/io/jans/lock/service/ws/rs/audit/AuditRestWebService.java b/jans-lock/lock-server/service/src/main/java/io/jans/lock/service/ws/rs/audit/AuditRestWebService.java
index d503cecb0c5..e901d4c5555 100644
--- a/jans-lock/lock-server/service/src/main/java/io/jans/lock/service/ws/rs/audit/AuditRestWebService.java
+++ b/jans-lock/lock-server/service/src/main/java/io/jans/lock/service/ws/rs/audit/AuditRestWebService.java
@@ -32,7 +32,7 @@
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
+import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
@@ -41,6 +41,8 @@
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.SecurityContext;
+import java.util.List;
+
/**
* Provides interface for audit REST web services
*
@@ -60,10 +62,11 @@ public interface AuditRestWebService {
@ApiResponse(responseCode = "500", description = "InternalServerError", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = LockApiError.class, description = "InternalServerError"))), })
@POST
@Path("/health")
+ @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- @ProtectedApi(scopes = { ApiAccessConstants.LOCK_HEALTH_WRITE_ACCESS })
+ @ProtectedApi(scopes = { ApiAccessConstants.LOCK_HEALTH_WRITE_ACCESS }, grpcMethodName = "ProcessHealth")
@ProtectedCedarlingApi(action = "Jans::Action::\"POST\"", resource = "Jans::HTTP_Request", id="lock_audit_health_write", path="/audit/health")
- Response processHealthRequest(@Context HttpServletRequest request,
+ Response processHealthRequest(HealthEntry healthEntry, @Context HttpServletRequest request,
@Context SecurityContext sec);
@Operation(summary = "Bulk save health data", description = "Bulk save health data", tags = {
@@ -77,10 +80,11 @@ Response processHealthRequest(@Context HttpServletRequest request,
@ApiResponse(responseCode = "500", description = "InternalServerError", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = LockApiError.class, description = "InternalServerError"))), })
@POST
@Path("/health/bulk")
+ @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- @ProtectedApi(scopes = { ApiAccessConstants.LOCK_HEALTH_WRITE_ACCESS })
- @ProtectedCedarlingApi(action = "Jans::Action::\"POST\"", resource = "Jans::HTTP_Request", id="lock_audit_health_write", path="/audit/health")
- Response processBulkHealthRequest(@Context HttpServletRequest request,
+ @ProtectedApi(scopes = { ApiAccessConstants.LOCK_HEALTH_WRITE_ACCESS }, grpcMethodName = "ProcessBulkHealth")
+ @ProtectedCedarlingApi(action = "Jans::Action::\"POST\"", resource = "Jans::HTTP_Request", id="lock_audit_health_write", path="/audit/health/bulk")
+ Response processBulkHealthRequest(List healthEntries, @Context HttpServletRequest request,
@Context SecurityContext sec);
@Operation(summary = "Save log data", description = "Save log data", tags = {
@@ -94,10 +98,11 @@ Response processBulkHealthRequest(@Context HttpServletRequest request,
@ApiResponse(responseCode = "500", description = "InternalServerError", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = LockApiError.class, description = "InternalServerError"))), })
@POST
@Path("/log")
+ @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- @ProtectedApi(scopes = { ApiAccessConstants.LOCK_LOG_WRITE_ACCESS })
+ @ProtectedApi(scopes = { ApiAccessConstants.LOCK_LOG_WRITE_ACCESS }, grpcMethodName = "ProcessLog")
@ProtectedCedarlingApi(action = "Jans::Action::\"POST\"", resource = "Jans::HTTP_Request", id="lock_audit_log_write", path="/audit/log")
- Response processLogRequest(@Context HttpServletRequest request,
+ Response processLogRequest(LogEntry logEntry, @Context HttpServletRequest request,
@Context SecurityContext sec);
@Operation(summary = "Bulk save log data", description = "Bulk save log data", tags = {
@@ -111,10 +116,11 @@ Response processLogRequest(@Context HttpServletRequest request,
@ApiResponse(responseCode = "500", description = "InternalServerError", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = LockApiError.class, description = "InternalServerError"))), })
@POST
@Path("/log/bulk")
+ @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- @ProtectedApi(scopes = { ApiAccessConstants.LOCK_LOG_WRITE_ACCESS })
- @ProtectedCedarlingApi(action = "Jans::Action::\"POST\"", resource = "Jans::HTTP_Request", id="lock_audit_log_write", path="/audit/log")
- Response processBulkLogRequest(@Context HttpServletRequest request,
+ @ProtectedApi(scopes = { ApiAccessConstants.LOCK_LOG_WRITE_ACCESS }, grpcMethodName = "ProcessBulkLog")
+ @ProtectedCedarlingApi(action = "Jans::Action::\"POST\"", resource = "Jans::HTTP_Request", id="lock_audit_log_write", path="/audit/log/bulk")
+ Response processBulkLogRequest(List logEntries, @Context HttpServletRequest request,
@Context SecurityContext sec);
@Operation(summary = "Save telemetry data", description = "Save telemetry data", tags = {
@@ -128,10 +134,11 @@ Response processBulkLogRequest(@Context HttpServletRequest request,
@ApiResponse(responseCode = "500", description = "InternalServerError", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = LockApiError.class, description = "InternalServerError"))), })
@POST
@Path("/telemetry")
+ @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- @ProtectedApi(scopes = { ApiAccessConstants.LOCK_TELEMETRY_WRITE_ACCESS })
+ @ProtectedApi(scopes = { ApiAccessConstants.LOCK_TELEMETRY_WRITE_ACCESS }, grpcMethodName = "ProcessTelemetry")
@ProtectedCedarlingApi(action = "Jans::Action::\"POST\"", resource = "Jans::HTTP_Request", id="lock_audit_telemetry_write", path="/audit/telemetry")
- Response processTelemetryRequest(@Context HttpServletRequest request,
+ Response processTelemetryRequest(TelemetryEntry telemetryEntry, @Context HttpServletRequest request,
@Context SecurityContext sec);
@Operation(summary = "Bulk save telemetry data", description = "Bulk save telemetry data", tags = {
@@ -145,10 +152,11 @@ Response processTelemetryRequest(@Context HttpServletRequest request,
@ApiResponse(responseCode = "500", description = "InternalServerError", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = LockApiError.class, description = "InternalServerError"))), })
@POST
@Path("/telemetry/bulk")
+ @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- @ProtectedApi(scopes = { ApiAccessConstants.LOCK_TELEMETRY_WRITE_ACCESS })
- @ProtectedCedarlingApi(action = "Jans::Action::\"POST\"", resource = "Jans::HTTP_Request", id="lock_audit_telemetry_write", path="/audit/telemetry")
- Response processBulkTelemetryRequest(@Context HttpServletRequest request,
+ @ProtectedApi(scopes = { ApiAccessConstants.LOCK_TELEMETRY_WRITE_ACCESS }, grpcMethodName = "ProcessBulkTelemetry")
+ @ProtectedCedarlingApi(action = "Jans::Action::\"POST\"", resource = "Jans::HTTP_Request", id="lock_audit_telemetry_write", path="/audit/telemetry/bulk")
+ Response processBulkTelemetryRequest(List telemetryEntries, @Context HttpServletRequest request,
@Context SecurityContext sec);
}
\ No newline at end of file
diff --git a/jans-lock/lock-server/service/src/main/java/io/jans/lock/service/ws/rs/audit/AuditRestWebServiceImpl.java b/jans-lock/lock-server/service/src/main/java/io/jans/lock/service/ws/rs/audit/AuditRestWebServiceImpl.java
index c7f057d4db5..4061c60b89b 100644
--- a/jans-lock/lock-server/service/src/main/java/io/jans/lock/service/ws/rs/audit/AuditRestWebServiceImpl.java
+++ b/jans-lock/lock-server/service/src/main/java/io/jans/lock/service/ws/rs/audit/AuditRestWebServiceImpl.java
@@ -16,14 +16,11 @@
package io.jans.lock.service.ws.rs.audit;
-import java.io.IOException;
import java.util.List;
import org.apache.http.entity.ContentType;
import org.slf4j.Logger;
-import com.fasterxml.jackson.databind.JsonNode;
-
import io.jans.lock.model.AuditEndpointType;
import io.jans.lock.model.app.audit.AuditActionType;
import io.jans.lock.model.app.audit.AuditLogEntry;
@@ -33,7 +30,6 @@
import io.jans.lock.model.config.AppConfiguration;
import io.jans.lock.model.config.AuditPersistenceMode;
import io.jans.lock.service.AuditService;
-import io.jans.lock.service.DataMapperService;
import io.jans.lock.service.app.audit.ApplicationAuditLogger;
import io.jans.lock.service.audit.AuditForwarderService;
import io.jans.lock.service.stat.StatService;
@@ -57,8 +53,6 @@
@Dependent
public class AuditRestWebServiceImpl extends BaseResource implements AuditRestWebService {
- private static final String LOG_PRINCIPAL_ID = "principalId";
- private static final String LOG_CLIENT_ID = "clientId";
private static final String LOG_DECISION_RESULT = "decisionResult";
private static final String LOG_ACTION = "action";
@@ -71,9 +65,6 @@ public class AuditRestWebServiceImpl extends BaseResource implements AuditRestWe
@Inject
private AppConfiguration appConfiguration;
- @Inject
- private DataMapperService dataMapperService;
-
@Inject
private JsonService jsonService;
@@ -90,22 +81,27 @@ public class AuditRestWebServiceImpl extends BaseResource implements AuditRestWe
private ApplicationAuditLogger applicationAuditLogger;
/**
- * Processes an incoming health audit request and delegates handling to the
- * audit processor.
+ * Processes an incoming health audit request with typed HealthEntry bean.
*
- * @return a Response containing the HTTP response to return for the health
- * audit request
+ * @param healthEntry the health entry bean
+ * @param request the HTTP servlet request
+ * @param sec the security context
+ * @return a Response containing the HTTP response to return for the health audit request
*/
@Override
- public Response processHealthRequest(HttpServletRequest request, SecurityContext sec) {
- log.info("Processing Health request - request: {}", request);
+ public Response processHealthRequest(HealthEntry healthEntry, HttpServletRequest request, SecurityContext sec) {
+ log.info("Processing Health request - healthEntry: {}", healthEntry);
- AuditLogEntry auditLogEntry = new AuditLogEntry(InetAddressUtility.getIpAddress(request),
+ if (healthEntry == null) {
+ throwBadRequestException("Health entry is required");
+ }
+
+ AuditLogEntry auditLogEntry = new AuditLogEntry(getClientIpAddress(request),
AuditActionType.AUDIT_HEALTH_WRITE);
Response response = null;
try {
- response = processAuditRequest(request, AuditEndpointType.HEALTH);
+ response = processAuditRequest(healthEntry, AuditEndpointType.HEALTH);
} finally {
applicationAuditLogger.log(auditLogEntry, getResponseResult(response));
}
@@ -114,24 +110,27 @@ public Response processHealthRequest(HttpServletRequest request, SecurityContext
}
/**
- * Handles incoming bulk health audit requests.
- *
- * Produces a Response representing the outcome of processing the bulk health
- * audit payload.
+ * Handles incoming bulk health audit requests with typed list of HealthEntry beans.
*
- * @return the Response representing the outcome of processing the bulk health
- * audit request
+ * @param healthEntries list of health entry beans
+ * @param request the HTTP servlet request
+ * @param sec the security context
+ * @return the Response representing the outcome of processing the bulk health audit request
*/
@Override
- public Response processBulkHealthRequest(HttpServletRequest request, SecurityContext sec) {
- log.info("Processing Bulk Health request - request: {}", request);
+ public Response processBulkHealthRequest(List healthEntries, HttpServletRequest request, SecurityContext sec) {
+ log.info("Processing Bulk Health request - entries count: {}", healthEntries != null ? healthEntries.size() : 0);
- AuditLogEntry auditLogEntry = new AuditLogEntry(InetAddressUtility.getIpAddress(request),
+ if (healthEntries == null || healthEntries.isEmpty()) {
+ throwBadRequestException("Health entries list is required and cannot be empty");
+ }
+
+ AuditLogEntry auditLogEntry = new AuditLogEntry(getClientIpAddress(request),
AuditActionType.AUDIT_HEALTH_BULK_WRITE);
Response response = null;
try {
- response = processAuditRequest(request, AuditEndpointType.HEALTH_BULK);
+ response = processBulkAuditRequest(healthEntries, AuditEndpointType.HEALTH_BULK);
} finally {
applicationAuditLogger.log(auditLogEntry, getResponseResult(response));
}
@@ -139,27 +138,39 @@ public Response processBulkHealthRequest(HttpServletRequest request, SecurityCon
return response;
}
+ private String getClientIpAddress(HttpServletRequest request) {
+ if (request != null) {
+ return InetAddressUtility.getIpAddress(request);
+ } else {
+ // gRPC request
+ return ServerUtil.getClientContextIpAddress();
+ }
+ }
+
/**
- * Handle an incoming audit log request for a single log event and process it
- * while reporting statistics.
+ * Handle an incoming audit log request with typed LogEntry bean.
*
- * @param request the HTTP servlet request containing the log JSON payload
- * @param response the HTTP servlet response (unused by this method but provided
- * by the servlet layer)
- * @param sec the security context for the request
- * @return a JAX-RS Response containing the processing result; `400 BAD_REQUEST`
- * on parse failure, `200 OK` on success
+ * @param logEntry the log entry bean
+ * @param request the HTTP servlet request
+ * @param sec the security context
+ * @return a JAX-RS Response containing the processing result
*/
@Override
- public Response processLogRequest(HttpServletRequest request, SecurityContext sec) {
- log.info("Processing Log request - request: {}", request);
+ public Response processLogRequest(LogEntry logEntry, HttpServletRequest request, SecurityContext sec) {
+ log.info("Processing Log request - logEntry: {}", logEntry);
+
+ if (logEntry == null) {
+ throwBadRequestException("Log entry is required");
+ }
- AuditLogEntry auditLogEntry = new AuditLogEntry(InetAddressUtility.getIpAddress(request),
+ AuditLogEntry auditLogEntry = new AuditLogEntry(getClientIpAddress(request),
AuditActionType.AUDIT_LOG_WRITE);
Response response = null;
try {
- response = processAuditRequest(request, AuditEndpointType.LOG, true, false);
+ // Report statistics for single log entry
+ reportLogStat(logEntry);
+ response = processAuditRequest(logEntry, AuditEndpointType.LOG);
} finally {
applicationAuditLogger.log(auditLogEntry, getResponseResult(response));
}
@@ -168,25 +179,31 @@ public Response processLogRequest(HttpServletRequest request, SecurityContext se
}
/**
- * Handles an incoming bulk log audit request, reports relevant statistics, and
- * delegates processing.
+ * Handles an incoming bulk log audit request with typed list of LogEntry beans.
*
- * @param request the HTTP request containing the bulk log payload
- * @param response the HTTP response
- * @param sec the security context for the request
- * @return the HTTP response representing the processing result; status
- * indicates success or failure
+ * @param logEntries list of log entry beans
+ * @param request the HTTP request
+ * @param sec the security context for the request
+ * @return the HTTP response representing the processing result
*/
@Override
- public Response processBulkLogRequest(HttpServletRequest request, SecurityContext sec) {
- log.info("Processing Bulk Log request - request: {}", request);
+ public Response processBulkLogRequest(List logEntries, HttpServletRequest request, SecurityContext sec) {
+ log.info("Processing Bulk Log request - entries count: {}", logEntries != null ? logEntries.size() : 0);
+
+ if (logEntries == null || logEntries.isEmpty()) {
+ throwBadRequestException("Log entries list is required and cannot be empty");
+ }
- AuditLogEntry auditLogEntry = new AuditLogEntry(InetAddressUtility.getIpAddress(request),
+ AuditLogEntry auditLogEntry = new AuditLogEntry(getClientIpAddress(request),
AuditActionType.AUDIT_LOG_BULK_WRITE);
Response response = null;
try {
- response = processAuditRequest(request, AuditEndpointType.LOG_BULK, true, true);
+ // Report statistics for bulk log entries
+ for (LogEntry entry : logEntries) {
+ reportLogStat(entry);
+ }
+ response = processBulkAuditRequest(logEntries, AuditEndpointType.LOG_BULK);
} finally {
applicationAuditLogger.log(auditLogEntry, getResponseResult(response));
}
@@ -195,24 +212,27 @@ public Response processBulkLogRequest(HttpServletRequest request, SecurityContex
}
/**
- * Handle an incoming telemetry audit request.
+ * Handle an incoming telemetry audit request with typed TelemetryEntry bean.
*
- * @param request the HTTP servlet request containing the telemetry payload
- * @param response the HTTP servlet response
- * @param sec the security context for the request
- * @return a Response representing the result of processing the telemetry audit
- * request
+ * @param telemetryEntry the telemetry entry bean
+ * @param request the HTTP servlet request
+ * @param sec the security context for the request
+ * @return a Response representing the result of processing the telemetry audit request
*/
@Override
- public Response processTelemetryRequest(HttpServletRequest request, SecurityContext sec) {
- log.info("Processing Telemetry request - request: {}", request);
+ public Response processTelemetryRequest(TelemetryEntry telemetryEntry, HttpServletRequest request, SecurityContext sec) {
+ log.info("Processing Telemetry request - telemetryEntry: {}", telemetryEntry);
+
+ if (telemetryEntry == null) {
+ throwBadRequestException("Telemetry entry is required");
+ }
- AuditLogEntry auditLogEntry = new AuditLogEntry(InetAddressUtility.getIpAddress(request),
+ AuditLogEntry auditLogEntry = new AuditLogEntry(getClientIpAddress(request),
AuditActionType.AUDIT_TELEMETRY_WRITE);
Response response = null;
try {
- response = processAuditRequest(request, AuditEndpointType.TELEMETRY);
+ response = processAuditRequest(telemetryEntry, AuditEndpointType.TELEMETRY);
} finally {
applicationAuditLogger.log(auditLogEntry, getResponseResult(response));
}
@@ -221,21 +241,27 @@ public Response processTelemetryRequest(HttpServletRequest request, SecurityCont
}
/**
- * Handles an incoming bulk telemetry audit request.
+ * Handles an incoming bulk telemetry audit request with typed list of TelemetryEntry beans.
*
- * @return a Response representing the result of processing the bulk telemetry
- * audit request
+ * @param telemetryEntries list of telemetry entry beans
+ * @param request the HTTP servlet request
+ * @param sec the security context
+ * @return a Response representing the result of processing the bulk telemetry audit request
*/
@Override
- public Response processBulkTelemetryRequest(HttpServletRequest request, SecurityContext sec) {
- log.info("Processing Bulk Telemetry request - request: {}", request);
+ public Response processBulkTelemetryRequest(List telemetryEntries, HttpServletRequest request, SecurityContext sec) {
+ log.info("Processing Bulk Telemetry request - entries count: {}", telemetryEntries != null ? telemetryEntries.size() : 0);
+
+ if (telemetryEntries == null || telemetryEntries.isEmpty()) {
+ throwBadRequestException("Telemetry entries list is required and cannot be empty");
+ }
- AuditLogEntry auditLogEntry = new AuditLogEntry(InetAddressUtility.getIpAddress(request),
+ AuditLogEntry auditLogEntry = new AuditLogEntry(getClientIpAddress(request),
AuditActionType.AUDIT_TELEMETRY_BULK_WRITE);
Response response = null;
try {
- response = processAuditRequest(request, AuditEndpointType.TELEMETRY_BULK);
+ response = processBulkAuditRequest(telemetryEntries, AuditEndpointType.TELEMETRY_BULK);
} finally {
applicationAuditLogger.log(auditLogEntry, getResponseResult(response));
}
@@ -244,101 +270,92 @@ public Response processBulkTelemetryRequest(HttpServletRequest request, Security
}
/**
- * Delegates processing of an audit HTTP request to the main processor using
- * default flags (do not report statistics, not bulk data).
+ * Process a single audit request with typed entry object.
*
- * @param request the incoming HTTP servlet request containing the audit
- * JSON payload
- * @param requestType the audit endpoint type indicating which audit path to
- * process
- * @return the JAX-RS response produced by processing the audit request
+ * @param entry the audit entry object (HealthEntry, LogEntry, or TelemetryEntry)
+ * @param requestType the audit endpoint type
+ * @return a JAX-RS Response
*/
- private Response processAuditRequest(HttpServletRequest request, AuditEndpointType requestType) {
- return processAuditRequest(request, requestType, false, false);
- }
-
- /**
- * Process an incoming audit HTTP request: parse its JSON payload, optionally
- * report statistics, then either forward the payload to the configured audit
- * API or persist it locally, and return an HTTP response containing the
- * operation result.
- *
- * @param request the HTTP request containing the audit JSON payload
- * @param requestType the audit endpoint type (HEALTH, LOG, TELEMETRY or their
- * bulk variants)
- * @param reportStat when true, report usage/operation statistics extracted
- * from the payload
- * @param bulkData when true, treat the payload as an array of entries for
- * bulk reporting
- * @return a JAX-RS Response whose entity is the result message from forwarding
- * or persistence; the response is marked private and no-store with a
- * Pragma: no-cache header
- */
- private Response processAuditRequest(HttpServletRequest request, AuditEndpointType requestType, boolean reportStat,
- boolean bulkData) {
- log.info("Processing request - request: {}, requestType: {}", request, requestType);
+ private Response processAuditRequest(Object entry, AuditEndpointType requestType) {
+ log.info("Processing single audit request - requestType: {}", requestType);
- JsonNode json = getJsonNode(request);
- if (json == null) {
- throwBadRequestException("Failed to parse request");
- }
+ Response.ResponseBuilder builder = Response.ok();
- if (reportStat) {
- if (bulkData) {
- reportBulkStat(json);
+ try {
+ String response;
+ if (AuditPersistenceMode.CONFIG_API.equals(appConfiguration.getAuditPersistenceMode())) {
+ String json = jsonService.objectToJson(entry);
+ response = auditForwarderService.post(builder, requestType, json, ContentType.APPLICATION_JSON);
} else {
- reportStat(json);
+ response = persistAuditData(builder, requestType, entry);
}
- }
-
- Response.ResponseBuilder builder = Response.ok();
- String response;
- if (AuditPersistenceMode.CONFIG_API.equals(appConfiguration.getAuditPersistenceMode())) {
- response = auditForwarderService.post(builder, requestType, json.toString(), ContentType.APPLICATION_JSON);
- } else {
- response = persistetAuditData(builder, requestType, json.toString());
+ builder.cacheControl(ServerUtil.cacheControlWithNoStoreTransformAndPrivate());
+ builder.header(ServerUtil.PRAGMA, ServerUtil.NO_CACHE);
+ builder.entity(response);
+
+ log.debug("Response entity: {}", response);
+ } catch (Exception ex) {
+ builder.status(Status.INTERNAL_SERVER_ERROR);
+ log.error("Failed to process audit request", ex);
+ builder.entity("Failed to process audit request");
}
- builder.cacheControl(ServerUtil.cacheControlWithNoStoreTransformAndPrivate());
- builder.header(ServerUtil.PRAGMA, ServerUtil.NO_CACHE);
-
- builder.entity(response);
- log.debug("Response entity: {}", response);
-
return builder.build();
}
- private JsonNode getJsonNode(HttpServletRequest request) {
- if (request == null) {
- return null;
- }
+ /**
+ * Process bulk audit requests with typed list of entry objects.
+ *
+ * @param entries list of audit entry objects
+ * @param requestType the audit endpoint type
+ * @return a JAX-RS Response
+ */
+ private Response processBulkAuditRequest(List> entries, AuditEndpointType requestType) {
+ log.info("Processing bulk audit request - requestType: {}, count: {}", requestType, entries.size());
+
+ Response.ResponseBuilder builder = Response.ok();
- JsonNode jsonBody = null;
try {
- jsonBody = dataMapperService.readTree(request.getInputStream());
- log.debug("Parsed request body data: {}", jsonBody);
+ String response;
+
+ if (AuditPersistenceMode.CONFIG_API.equals(appConfiguration.getAuditPersistenceMode())) {
+ String json = jsonService.objectToJson(entries);
+ response = auditForwarderService.post(builder, requestType, json, ContentType.APPLICATION_JSON);
+ } else {
+ response = persistBulkAuditData(builder, requestType, entries);
+ }
+
+ builder.cacheControl(ServerUtil.cacheControlWithNoStoreTransformAndPrivate());
+ builder.header(ServerUtil.PRAGMA, ServerUtil.NO_CACHE);
+ builder.entity(response);
+
+ log.debug("Response entity: {}", response);
} catch (Exception ex) {
- log.error("Failed to parse request", ex);
+ builder.status(Status.INTERNAL_SERVER_ERROR);
+ log.error("Failed to process bulk audit request", ex);
+ builder.entity("Failed to process bulk audit request");
}
- return jsonBody;
+ return builder.build();
}
- private void reportStat(JsonNode json) {
- boolean hasClientId = json.hasNonNull(LOG_CLIENT_ID);
- if (hasClientId) {
- statService.reportActiveClient(json.get(LOG_CLIENT_ID).asText());
+ /**
+ * Report statistics for a single log entry.
+ *
+ * @param logEntry the log entry to report statistics for
+ */
+ private void reportLogStat(LogEntry logEntry) {
+ if (logEntry.getClientId() != null) {
+ statService.reportActiveClient(logEntry.getClientId());
}
- boolean hasPrincipalId = json.hasNonNull(LOG_PRINCIPAL_ID);
- if (hasPrincipalId) {
- statService.reportActiveUser(json.get(LOG_PRINCIPAL_ID).asText());
+ if (logEntry.getPrincipalId() != null) {
+ statService.reportActiveUser(logEntry.getPrincipalId());
}
- boolean hasВecisionResult = json.hasNonNull(LOG_DECISION_RESULT);
- if (hasВecisionResult) {
- String decisionResult = json.get(LOG_DECISION_RESULT).asText();
+ if (logEntry.getDecisionResult() != null) {
+ String decisionResult = logEntry.getDecisionResult();
if (LOG_DECISION_RESULT_ALLOW.equals(decisionResult)) {
statService.reportAllow(LOG_DECISION_RESULT);
} else if (LOG_DECISION_RESULT_DENY.equals(decisionResult)) {
@@ -346,96 +363,81 @@ private void reportStat(JsonNode json) {
}
}
- boolean hasAction = json.hasNonNull(LOG_ACTION);
- if (hasAction) {
- statService.reportOpearation(LOG_ACTION, json.get(LOG_ACTION).asText());
+ if (logEntry.getAction() != null) {
+ statService.reportOpearation(LOG_ACTION, logEntry.getAction());
}
}
/**
- * Reports statistics for each element of a JSON array representing bulk audit
- * entries.
- *
- * If the provided node is not a JSON array, an error is logged and the method
- * still attempts to process its elements.
+ * Persist a single audit entry.
*
- * @param json JSON array of audit entries whose elements will be processed to
- * report statistics
+ * @param builder response builder
+ * @param requestType the audit endpoint type
+ * @param entry the audit entry object
+ * @return empty string on success, error message on failure
*/
- private void reportBulkStat(JsonNode json) {
- if (!json.isArray()) {
- log.error("Failed to calculate stat for bulk log entry: {}", json);
- }
-
- for (JsonNode jsonItem : json) {
- reportStat(jsonItem);
+ private String persistAuditData(ResponseBuilder builder, AuditEndpointType requestType, Object entry) {
+ try {
+ switch (requestType) {
+ case LOG:
+ auditService.addLogEntry((LogEntry) entry);
+ break;
+ case HEALTH:
+ auditService.addHealthEntry((HealthEntry) entry);
+ break;
+ case TELEMETRY:
+ auditService.addTelemetryEntry((TelemetryEntry) entry);
+ break;
+ default:
+ builder.status(Status.BAD_REQUEST);
+ return "Invalid request type for single entry";
+ }
+ } catch (Exception ex) {
+ builder.status(Status.INTERNAL_SERVER_ERROR);
+ log.error("Failed to persist audit data", ex);
+ return "Failed to persist data";
}
-
+ return "";
}
/**
- * Persist audit data for the given request type.
- *
- *
- * Parses the provided JSON payload into the appropriate audit entry or entries
- * based on {@code requestType} and persists them via the audit service.
- *
+ * Persist bulk audit entries.
*
- * @param builder response builder that will be updated to BAD_REQUEST on
- * parse failure
- * @param requestType the type of audit endpoint (log, health, telemetry, or
- * their bulk variants)
- * @param json the JSON payload to parse and persist
- * @return an empty string on success, or the message "Failed to parse data" if
- * parsing failed
+ * @param builder response builder
+ * @param requestType the audit endpoint type
+ * @param entries list of audit entry objects
+ * @return empty string on success, error message on failure
*/
- private String persistetAuditData(ResponseBuilder builder, AuditEndpointType requestType, String json) {
+ @SuppressWarnings("unchecked")
+ private String persistBulkAuditData(ResponseBuilder builder, AuditEndpointType requestType, List> entries) {
try {
switch (requestType) {
- case LOG:
- LogEntry logEntry = jsonService.jsonToObject(json, LogEntry.class);
- auditService.addLogEntry(logEntry);
- break;
case LOG_BULK:
- List logEntries = jsonService.jsonToObject(json,
- jsonService.getTypeFactory().constructCollectionType(List.class, LogEntry.class));
+ List logEntries = (List) entries;
for (LogEntry entry : logEntries) {
auditService.addLogEntry(entry);
}
break;
- case HEALTH:
- HealthEntry healthEntry = jsonService.jsonToObject(json, HealthEntry.class);
- auditService.addHealthEntry(healthEntry);
- break;
case HEALTH_BULK:
- List healthEntries = jsonService.jsonToObject(json,
- jsonService.getTypeFactory().constructCollectionType(List.class, HealthEntry.class));
+ List healthEntries = (List) entries;
for (HealthEntry entry : healthEntries) {
auditService.addHealthEntry(entry);
}
break;
- case TELEMETRY:
- TelemetryEntry telemetryEntry = jsonService.jsonToObject(json, TelemetryEntry.class);
- auditService.addTelemetryEntry(telemetryEntry);
- break;
case TELEMETRY_BULK:
- List telemetryEntries = jsonService.jsonToObject(json,
- jsonService.getTypeFactory().constructCollectionType(List.class, TelemetryEntry.class));
+ List telemetryEntries = (List) entries;
for (TelemetryEntry entry : telemetryEntries) {
auditService.addTelemetryEntry(entry);
}
break;
+ default:
+ builder.status(Status.BAD_REQUEST);
+ return "Invalid request type for bulk entries";
}
- } catch (IOException ex) {
- builder.status(Status.BAD_REQUEST);
- log.warn("Failed to parse data", ex);
-
- return "Failed to parse data";
} catch (Exception ex) {
builder.status(Status.INTERNAL_SERVER_ERROR);
- log.error("Failed to persist audit data", ex);
-
- return "Failed to persist data";
+ log.error("Failed to persist bulk audit data", ex);
+ return "Failed to persist bulk data";
}
return "";
}
diff --git a/jans-lock/lock-server/service/src/main/java/io/jans/lock/util/HeaderUtils.java b/jans-lock/lock-server/service/src/main/java/io/jans/lock/util/HeaderUtils.java
new file mode 100644
index 00000000000..7b0cef9e522
--- /dev/null
+++ b/jans-lock/lock-server/service/src/main/java/io/jans/lock/util/HeaderUtils.java
@@ -0,0 +1,87 @@
+/*
+ * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text.
+ *
+ * Copyright (c) 2026, Janssen Project
+ */
+
+package io.jans.lock.util;
+
+import io.grpc.Metadata;
+
+/**
+ * Utility for handling gRPC authorization headers
+ *
+ * @author Yuriy Movchan Date: 01/20/2026
+ */
+public class HeaderUtils {
+
+ /**
+ * Finds the authorization header in gRPC metadata, handling various
+ * case variations and naming conventions.
+ *
+ * @param headers The gRPC metadata headers
+ * @return The authorization header value, or null if not found
+ */
+ public static String findAuthorizationHeader(Metadata headers) {
+ if (headers == null) {
+ return null;
+ }
+
+ // Common variations of authorization header keys
+ String[] possibleKeys = {
+ "authorization",
+ "grpc-metadata-authorization",
+ "x-authorization",
+ "x-grpc-authorization"
+ };
+
+ for (String key : possibleKeys) {
+ String value = headers.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER));
+ if (value != null && !value.trim().isEmpty()) {
+ return value;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Extracts the bearer token from an authorization header.
+ *
+ * @param authHeader The full authorization header value
+ * @return The token without the "bearer " prefix, or null if invalid
+ */
+ public static String extractBearerToken(String authHeader) {
+ if (authHeader == null || authHeader.trim().isEmpty()) {
+ return null;
+ }
+
+ // Remove any extra whitespace
+ String trimmed = authHeader.trim();
+
+ // Check if it's a bearer token
+ if (trimmed.toLowerCase().startsWith("bearer ")) {
+ return trimmed.substring(7).trim(); // Remove "bearer " prefix
+ }
+
+ // Also accept "Bearer " with capital B
+ if (trimmed.startsWith("Bearer ")) {
+ return trimmed.substring(7).trim();
+ }
+
+ // If it doesn't start with bearer, return as-is (might be basic auth or other)
+ return trimmed;
+ }
+
+ /**
+ * Combines both methods: finds and extracts the bearer token.
+ *
+ * @param headers The gRPC metadata headers
+ * @return The bearer token, or null if not found/invalid
+ */
+ public static String findAndExtractBearerToken(Metadata headers) {
+ String authHeader = findAuthorizationHeader(headers);
+ return extractBearerToken(authHeader);
+ }
+
+}
\ No newline at end of file
diff --git a/jans-lock/lock-server/service/src/main/java/io/jans/lock/util/ServerUtil.java b/jans-lock/lock-server/service/src/main/java/io/jans/lock/util/ServerUtil.java
index 5abe5fff520..6ff64d32d04 100644
--- a/jans-lock/lock-server/service/src/main/java/io/jans/lock/util/ServerUtil.java
+++ b/jans-lock/lock-server/service/src/main/java/io/jans/lock/util/ServerUtil.java
@@ -32,11 +32,14 @@
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import io.grpc.Context;
+import io.grpc.Metadata;
import io.jans.util.Util;
import jakarta.ws.rs.core.CacheControl;
/**
* @author Yuriy Zabrovarnyy
+ * @author Yuriy Movchan
* @version 0.1, 10/07/2024
*/
@@ -44,10 +47,15 @@ public class ServerUtil {
private static final Logger log = LoggerFactory.getLogger(ServerUtil.class);
-
public static final String PRAGMA = "Pragma";
public static final String NO_CACHE = "no-cache";
+ private static final String UNKNOWN = "unknown";
+
+ public static final Context.Key CLIENT_IP_CONTEXT_KEY = Context.key("client-ip");
+ private static final Metadata.Key X_FORWARDED_FOR = Metadata.Key.of("x-forwarded-for", Metadata.ASCII_STRING_MARSHALLER);
+ private static final Metadata.Key X_REAL_IP = Metadata.Key.of("x-real-ip", Metadata.ASCII_STRING_MARSHALLER);
+
public static CacheControl cacheControl(boolean noStore) {
final CacheControl cacheControl = new CacheControl();
cacheControl.setNoStore(noStore);
@@ -110,4 +118,87 @@ public static String urlDecode(String str) {
return str;
}
-}
+ /**
+ * Get client IP address from current context
+ * @return client IP address or "unknown" if not determined
+ */
+ public static String getClientContextIpAddress() {
+ String clientIp = CLIENT_IP_CONTEXT_KEY.get(Context.current());
+
+ if (clientIp == null) {
+ return UNKNOWN;
+ }
+
+ return clientIp;
+ }
+
+ /**
+ * Set client IP address from current context
+ * @return
+ */
+ public static Context setClientContextIpAddress(String clientIp) {
+ return Context.current().withValue(CLIENT_IP_CONTEXT_KEY, clientIp);
+ }
+
+ /**
+ * Get client IP address when ServerCall is available (e.g., in interceptor)
+ */
+ public static String getGrpcClientIpAddress(io.grpc.ServerCall, ?> call, Metadata headers) {
+ try {
+ // Method 1: Try to get from metadata headers
+ String ipFromHeaders = getIpFromHeaders(headers);
+ if (ipFromHeaders != null) {
+ log.debug("Client IP from headers: {}", ipFromHeaders);
+ return ipFromHeaders;
+ }
+
+ // Method 2: Get from ServerCall attributes
+ if (call != null) {
+ io.grpc.Attributes attributes = call.getAttributes();
+ java.net.SocketAddress remoteAddr = attributes.get(io.grpc.Grpc.TRANSPORT_ATTR_REMOTE_ADDR);
+ if (remoteAddr instanceof java.net.InetSocketAddress) {
+ java.net.InetSocketAddress inetAddr = (java.net.InetSocketAddress) remoteAddr;
+ String ip = inetAddr.getAddress().getHostAddress();
+ log.debug("Client IP from ServerCall attributes: {}", ip);
+ return ip;
+ }
+ }
+
+ log.warn("Could not determine client IP address");
+ return UNKNOWN;
+ } catch (Exception e) {
+ log.error("Error getting client IP address", e);
+ return UNKNOWN;
+ }
+ }
+
+ /**
+ * Extract IP directly from headers (for use in interceptor)
+ */
+ public static String getIpFromHeaders(Metadata headers) {
+ if (headers == null) {
+ return null;
+ }
+
+ try {
+ // 1. Try X-Forwarded-For
+ String xForwardedFor = headers.get(X_FORWARDED_FOR);
+ if (xForwardedFor != null && !xForwardedFor.trim().isEmpty()) {
+ String[] ips = xForwardedFor.split(",");
+ return ips[0].trim();
+ }
+
+ // 2. Try X-Real-IP
+ String xRealIp = headers.get(X_REAL_IP);
+ if (xRealIp != null && !xRealIp.trim().isEmpty()) {
+ return xRealIp.trim();
+ }
+
+ return null;
+ } catch (Exception e) {
+ log.debug("Error extracting IP from headers", e);
+ return null;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/jans-lock/lock-server/service/src/main/policy/audit/health_bulk_write.json b/jans-lock/lock-server/service/src/main/policy/audit/health_bulk_write.json
new file mode 100644
index 00000000000..5c7f5fb549d
--- /dev/null
+++ b/jans-lock/lock-server/service/src/main/policy/audit/health_bulk_write.json
@@ -0,0 +1,11 @@
+@id("lock_audit_health_write")
+permit(
+ principal is Jans::Workload,
+ action in Jans::Action::"POST",
+ resource == Jans::HTTP_Request::"lock_audit_health_write"
+)
+when {
+ principal has access_token.scope &&
+ principal.access_token.scope.contains("https://jans.io/oauth/lock/health.write") &&
+ resource has url && resource.url has path && resource.url.path == "/audit/health/bulk"
+};
diff --git a/jans-lock/lock-server/service/src/main/policy/audit/log_bulk_write.json b/jans-lock/lock-server/service/src/main/policy/audit/log_bulk_write.json
new file mode 100644
index 00000000000..89017c2c707
--- /dev/null
+++ b/jans-lock/lock-server/service/src/main/policy/audit/log_bulk_write.json
@@ -0,0 +1,11 @@
+@id("lock_audit_log_write")
+permit(
+ principal is Jans::Workload,
+ action in Jans::Action::"POST",
+ resource == Jans::HTTP_Request::"lock_audit_log_write"
+)
+when {
+ principal has access_token.scope &&
+ principal.access_token.scope.contains("https://jans.io/oauth/lock/log.write") &&
+ resource has url && resource.url has path && resource.url.path == "/audit/log/bulk"
+};
diff --git a/jans-lock/lock-server/service/src/main/policy/audit/telemetry_bulk_write.json b/jans-lock/lock-server/service/src/main/policy/audit/telemetry_bulk_write.json
new file mode 100644
index 00000000000..52c5a6d8940
--- /dev/null
+++ b/jans-lock/lock-server/service/src/main/policy/audit/telemetry_bulk_write.json
@@ -0,0 +1,11 @@
+@id("lock_audit_telemetry_write")
+permit(
+ principal is Jans::Workload,
+ action in Jans::Action::"POST",
+ resource == Jans::HTTP_Request::"lock_audit_telemetry_write"
+)
+when {
+ principal has access_token.scope &&
+ principal.access_token.scope.contains("https://jans.io/oauth/lock/telemetry.write") &&
+ resource has url && resource.url has path && resource.url.path == "/audit/telemetry/bulk"
+};
diff --git a/jans-lock/lock-server/service/src/main/proto/grpc/audit.proto b/jans-lock/lock-server/service/src/main/proto/grpc/audit.proto
new file mode 100644
index 00000000000..2bee038ef11
--- /dev/null
+++ b/jans-lock/lock-server/service/src/main/proto/grpc/audit.proto
@@ -0,0 +1,93 @@
+syntax = "proto3";
+
+package io.jans.lock.audit;
+
+option java_multiple_files = true;
+option java_package = "io.jans.lock.model.audit.grpc";
+option java_outer_classname = "AuditProto";
+
+import "google/protobuf/timestamp.proto";
+
+// Health Entry Message
+message HealthEntry {
+ google.protobuf.Timestamp creation_date = 1;
+ google.protobuf.Timestamp event_time = 2;
+ string service = 3;
+ string node_name = 4;
+ string status = 5;
+ map engine_status = 6;
+}
+
+message HealthRequest {
+ HealthEntry entry = 1;
+}
+
+message BulkHealthRequest {
+ repeated HealthEntry entries = 1;
+}
+
+// Log Entry Message
+message LogEntry {
+ google.protobuf.Timestamp creation_date = 1;
+ google.protobuf.Timestamp event_time = 2;
+ string service = 3;
+ string node_name = 4;
+ string event_type = 5;
+ string severity_level = 6;
+ string action = 7;
+ string decision_result = 8;
+ string requested_resource = 9;
+ string principal_id = 10;
+ string client_id = 11;
+ string jti = 12;
+ map context_information = 13;
+}
+
+message LogRequest {
+ LogEntry entry = 1;
+}
+
+message BulkLogRequest {
+ repeated LogEntry entries = 1;
+}
+
+// Telemetry Entry Message
+message TelemetryEntry {
+ google.protobuf.Timestamp creation_date = 1;
+ google.protobuf.Timestamp event_time = 2;
+ string service = 3;
+ string node_name = 4;
+ string status = 5;
+ int64 last_policy_load_size = 6;
+ int64 policy_success_load_counter = 7;
+ int64 policy_failed_load_counter = 8;
+ int64 last_policy_evaluation_time_ns = 9;
+ int64 avg_policy_evaluation_time_ns = 10;
+ int64 memory_usage = 11;
+ int64 evaluation_requests_count = 12;
+ map policy_stats = 13;
+}
+
+message TelemetryRequest {
+ TelemetryEntry entry = 1;
+}
+
+message BulkTelemetryRequest {
+ repeated TelemetryEntry entries = 1;
+}
+
+// Common Response
+message AuditResponse {
+ bool success = 1;
+ string message = 2;
+}
+
+// Audit Service
+service AuditService {
+ rpc ProcessHealth(HealthRequest) returns (AuditResponse);
+ rpc ProcessBulkHealth(BulkHealthRequest) returns (AuditResponse);
+ rpc ProcessLog(LogRequest) returns (AuditResponse);
+ rpc ProcessBulkLog(BulkLogRequest) returns (AuditResponse);
+ rpc ProcessTelemetry(TelemetryRequest) returns (AuditResponse);
+ rpc ProcessBulkTelemetry(BulkTelemetryRequest) returns (AuditResponse);
+}
\ No newline at end of file