Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
af98234
start work on request-uuid extration
neiroukh May 12, 2026
996884e
finish AuditLogger foundation
neiroukh May 15, 2026
bbd2810
add support for access-logging
neiroukh May 18, 2026
764115b
LogContext: remove AUDIT context
neiroukh May 19, 2026
b9a31ef
expand AuditLogger
neiroukh May 20, 2026
ea3f61c
small refactor
neiroukh May 20, 2026
b00bba2
AuditLogger: operation handling
neiroukh May 20, 2026
6c7454c
Audit service: work on JSON output
neiroukh May 21, 2026
cabd434
refactor
neiroukh May 21, 2026
2f5b9f9
AuditLog: target-logging and ObjectMapper support
neiroukh May 22, 2026
5b5f39f
AuditLog: convert MultivaluedMap-headers to Map<String, Object> for b…
neiroukh May 22, 2026
ce758f1
keep property order by using LinkedHashMap
neiroukh May 26, 2026
46b875d
move audit-init to ApiRequestDispatcher
neiroukh May 26, 2026
45453a2
AuditLogResponseFilter for final logging
neiroukh May 27, 2026
2c1ea6d
init audit-log module
neiroukh May 27, 2026
60a1bc6
decrease complexity of interface and implementation
neiroukh May 28, 2026
03cb3f7
write logs with ResourceStore
neiroukh May 28, 2026
baee5d0
move to xtraplatform-services
neiroukh May 29, 2026
0775fa5
move to xtraplatform-services
neiroukh May 29, 2026
d69ce7e
init work on global config
neiroukh May 29, 2026
4118c2f
explicit log creation
neiroukh Jun 2, 2026
a12eed3
global config: retries
neiroukh Jun 2, 2026
5379717
global config: pathPrefix
neiroukh Jun 2, 2026
1134972
global config: type
neiroukh Jun 2, 2026
ab6147d
global config: type (without default values)
neiroukh Jun 2, 2026
e260434
remove debugging code
neiroukh Jun 2, 2026
12b815a
global config: add getClaims to User
neiroukh Jun 3, 2026
db41d5c
global config: claims (without default values)
neiroukh Jun 3, 2026
5c17543
global config: httpStatus (without default values)
neiroukh Jun 3, 2026
ec5b42a
abort response on logging-error
neiroukh Jun 3, 2026
94e09af
global config: add docs
neiroukh Jun 3, 2026
0017639
global config: fix merging of default lists
neiroukh Jun 5, 2026
e463ec1
remove debugging code
neiroukh Jun 5, 2026
4c1f0d7
global config: throw error on IO-Exception
neiroukh Jun 5, 2026
2a898da
AuditLog: extend by logIsAvaible and isEnabled
neiroukh Jun 5, 2026
f16484d
AuditLog: add abort method
neiroukh Jun 8, 2026
5da7593
refactor
neiroukh Jun 9, 2026
361203f
landingpage instead of homepage on missing api
neiroukh Jun 9, 2026
7f46a10
Set anonymous user automatically if actor is missing
neiroukh Jun 9, 2026
920f5ef
wait for entity
neiroukh Jun 9, 2026
ae80fea
Refactor
neiroukh Jun 10, 2026
8e6cb2b
Merge branch 'master' into ldp-95-audit-logging-new-module
azahnen Jun 11, 2026
5135ea6
api config: includePropertyValues
neiroukh Jun 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ public Optional<User> parseToken(String token) {
.permissions(readList(claimsJws, claimsProvider.getClaims().getPermissions()))
.apiPermissions(
readListPerApi(claimsJws, claimsProvider.getClaims().getPermissions()))
.claims(claimsJws)
.build();

if (LOGGER.isTraceEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ enum PolicyDecision {

Map<String, Set<String>> getApiPermissions();

Map<String, Object> getClaims();

@Value.Default
default Role getRole() {
return Role.NONE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,14 @@ && getJobs().getMaxConcurrent() == 1) {
@Valid
public abstract RedisConfiguration getRedis();

/**
* @langEn See [AuditLog](120-auditLog.md).
* @langDe Siehe [AuditLog](120-auditLog.md).
*/
@JsonProperty("auditLog")
@Valid
public abstract AuditLogConfiguration getAuditLog();

@JsonIgnore
@Override
public void setServerFactory(ServerFactory factory) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
/*
* Copyright 2026 interactive instruments GmbH
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package de.ii.xtraplatform.base.domain;

import com.fasterxml.jackson.annotation.JsonMerge;
import com.fasterxml.jackson.annotation.OptBoolean;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import de.ii.xtraplatform.docs.DocFile;
import de.ii.xtraplatform.docs.DocStep;
import de.ii.xtraplatform.docs.DocStep.Step;
import de.ii.xtraplatform.docs.DocTable;
import de.ii.xtraplatform.docs.DocTable.ColumnSet;
import java.util.List;
import org.immutables.value.Value;
import org.immutables.value.Value.Default;

/**
* @langEn # AuditLog
* <p>## Options
* <p>{@docTable:properties}
* @langDe # AuditLog
* <p>## Optionen
* <p>{@docTable:properties}
* @ref:cfgProperties {@link de.ii.xtraplatform.base.domain.ImmutableAuditLogConfiguration}
*/
@DocFile(
path = "application/20-configuration",
name = "120-auditLog.md",
tables = {
@DocTable(
name = "properties",
rows = {
@DocStep(type = Step.TAG_REFS, params = "{@ref:cfgProperties}"),
@DocStep(type = Step.JSON_PROPERTIES)
},
columnSet = ColumnSet.JSON_PROPERTIES)
})
@Value.Immutable
@Value.Modifiable
@JsonDeserialize(as = ModifiableAuditLogConfiguration.class)
public interface AuditLogConfiguration {

/**
* @langEn If `true`, audit logging is enabled; otherwise, it is disabled.
* @langDe Wenn `true`, wird das Audit-Logging eingeschaltet, ansonsten deaktiviert.
* @default false
*/
@Default
default boolean getEnabled() {
return false;
}

/**
* @langEn Indicates how often the write process should be retried on errors. Should be set to 0
* if no retries are desired.
* @langDe Gibt an, wie oft der Schreibprozess bei Fehlern wiederholt werden soll. Sollte auf 0
* gesetzt werden, falls keine Neuversuche erwünscht sind.
* @default 3
*/
@Default
default int getRetries() {
return 3;
}

/**
* @langEn Specifies the path to prepend to the log file. `{api}` and `{date}` are replaced with
* the API ID and the request's ISO date, respectively. If the request is API-independent,
* `{api}` is replaced with "landingpage". For example, log files for `{api}/foo/{date}/bar`
* could be stored at `resources/logs/audit/vineyards/foo/2026-06-03/bar`.
* @langDe Gibt den Pfad an, der vor der Log-Datei angehängt werden soll. Dabei werden `{api}` und
* `{date}` jeweils mit der API-ID bzw. dem ISO-Datum der Anfrage ersetzt. Falls die Anfrage
* API-unabhängig ist, wird `{api}` mit "landingpage" ersetzt. Beispielsweise könnten die
* Log-Dateien für `{api}/foo/{date}/bar` unter
* `resources/logs/audit/vineyards/foo/2026-06-03/bar` gespeichert werden.
* @default {api}/{date}
*/
@Default
default String getPathPrefix() {
return "{api}/{date}";
}

/**
* @langEn Specifies the format in which logs are stored. Currently supported: `JSON` and
* `JSON_PRETTY` (formatted JSON).
* @langDe Gibt an, in welchem Format die Logs gespeichert werden. Unterstützt werden momentan
* `JSON` und `JSON_PRETTY` (formatiertes JSON).
* @default JSON
*/
@Default
default TYPE getType() {
return TYPE.JSON;
}

/**
* @langEn The `included` list specifies which headers should be logged. The `excluded` list
* specifies which headers would be logged according to `included` but should not be logged.
* The special value `*` can be used for both lists and covers all headers. If `excluded = [
* '*' ]`, no headers are logged.
* @langDe Die `included`-Liste gibt an, welche Header geloggt werden sollen. Die `excluded`-Liste
* gibt an, welche Header gemäß `included` geloggt würden, aber nicht geloggt werden sollen.
* Der spezielle Wert `*` kann für beide Listen verwendet werden und umfasst alle Header. Wenn
* `excluded = [ '*' ]`, werden keine Header geloggt.
* @default included: [ '*' ]\nexcluded: []
*/
@Default
default HeadersConfiguration getHeaders() {
return ModifiableHeadersConfiguration.create();
}

/**
* @langEn Specifies which claims from the token should be logged and which should explicitly not
* be logged by using `included`/`excluded` lists. The exact logic is the same as in
* `headers`.
* @langDe Gibt mit `included`/`excluded`-Listen an, welche Claims aus dem Token geloggt werden
* sollen bzw. explizit nicht geloggt werden dürfen. Die genaue Logik entspricht der in
* `headers`.
* @default included: [ '*' ]\nexcluded: []
*/
@Default
default ClaimsConfiguration getClaims() {
return ModifiableClaimsConfiguration.create();
}

/**
* @langEn Specifies for which HTTP status codes requests should be logged and which should
* explicitly not be logged by using `included`/`excluded` lists. The exact logic is the same
* as in `headers`.
* @langDe Gibt mit `included`/`excluded`-Listen an, bei welchen HTTP-Status-Codes Anfragen
* geloggt werden sollen bzw. explizit nicht geloggt werden dürfen. Die genaue Logik
* entspricht der in `headers`.
* @default included: [ '200' ]\nexcluded: []
*/
@Default
default HttpStatusConfiguration getHttpStatus() {
return ModifiableHttpStatusConfiguration.create();
}

enum TYPE {
JSON,
JSON_PRETTY
}

@Value.Immutable
@Value.Modifiable
@JsonDeserialize(as = ModifiableHeadersConfiguration.class)
interface HeadersConfiguration {
@Value.Default
@JsonMerge(OptBoolean.FALSE)
default List<String> getIncluded() {
return List.of("*");
}

@Value.Default
@JsonMerge(OptBoolean.FALSE)
default List<String> getExcluded() {
return List.of();
}
}

@Value.Immutable
@Value.Modifiable
@JsonDeserialize(as = ModifiableClaimsConfiguration.class)
interface ClaimsConfiguration {
@Value.Default
@JsonMerge(OptBoolean.FALSE)
default List<String> getIncluded() {
return List.of();
}

@Value.Default
@JsonMerge(OptBoolean.FALSE)
default List<String> getExcluded() {
return List.of();
}
}

@Value.Immutable
@Value.Modifiable
@JsonDeserialize(as = ModifiableHttpStatusConfiguration.class)
interface HttpStatusConfiguration {
@Value.Default
@JsonMerge(OptBoolean.FALSE)
default List<String> getIncluded() {
return List.of("200");
}

@Value.Default
@JsonMerge(OptBoolean.FALSE)
default List<String> getExcluded() {
return List.of();
}
}
}
17 changes: 16 additions & 1 deletion xtraplatform-base/src/main/resources/cfg.base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,19 @@ logging:
metrics:
frequency: 1 minute
reporters: [ ]
reportOnStop: false
reportOnStop: false

auditLog:
enabled: false
retries: 3
type: ${TYPE:-JSON}
pathPrefix: '{api}/{date}'
headers:
included: [ '*' ]
excluded: [ ]
claims:
included: [ ]
excluded: [ ]
httpStatus:
included: [ '200' ]
excluded: [ ]
2 changes: 1 addition & 1 deletion xtraplatform-services/build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

maturity = 'MATURE'
maintenance = 'FULL'
description = 'Service entities, background tasks.'
Expand All @@ -9,6 +8,7 @@ dependencies {
provided project(':xtraplatform-entities')
provided project(':xtraplatform-values')
provided project(':xtraplatform-openapi')
provided project(':xtraplatform-blobs')

embedded libs.cron4j
}
Loading