getAdditionalGroupProperties();
diff --git a/src/main/java/org/tailormap/api/security/TailormapUserDetailsImpl.java b/src/main/java/org/tailormap/api/security/TailormapUserDetailsImpl.java
index 0db39e66..0303601d 100644
--- a/src/main/java/org/tailormap/api/security/TailormapUserDetailsImpl.java
+++ b/src/main/java/org/tailormap/api/security/TailormapUserDetailsImpl.java
@@ -5,30 +5,77 @@
*/
package org.tailormap.api.security;
+import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import org.jspecify.annotations.NonNull;
import org.springframework.security.core.GrantedAuthority;
-class TailormapUserDetailsImpl implements TailormapUserDetails {
+/**
+ * Internal {@link TailormapUserDetails} implementation.
+ *
+ * Do not use this class directly; always depend on the {@link TailormapUserDetails} interface instead. This class is
+ * {@code public} only to support JSON/session deserialization (e.g. by Jackson / Spring Session). Changing its
+ * visibility back to package-private would break that deserialization.
+ */
+public class TailormapUserDetailsImpl implements TailormapUserDetails {
@Serial
private static final long serialVersionUID = 1L;
- private Collection authorities;
- private String username;
- private String password;
- private ZonedDateTime validUntil;
- private boolean enabled;
- private String organisation;
+ private final Collection authorities;
+ private final String username;
+ private final String password;
+ private final ZonedDateTime validUntil;
+ private final boolean enabled;
+ private final String organisation;
private final Collection additionalProperties = new ArrayList<>();
private final Collection additionalGroupProperties = new ArrayList<>();
+ /**
+ * Constructor for Jackson deserialization.
+ *
+ * @param authorities the authorities
+ * @param username the username
+ * @param password the password
+ * @param validUntil the valid until date
+ * @param enabled whether the user is enabled
+ * @param organisation the organisation
+ * @param additionalProperties the additional properties
+ * @param additionalGroupProperties the additional group properties
+ */
+ @SuppressWarnings("unused")
+ TailormapUserDetailsImpl(
+ @JsonProperty("authorities") Collection authorities,
+ @JsonProperty("username") String username,
+ @JsonProperty("password") String password,
+ @JsonProperty("validUntil") ZonedDateTime validUntil,
+ @JsonProperty("enabled") boolean enabled,
+ @JsonProperty("organisation") String organisation,
+ @JsonProperty("additionalProperties") Collection additionalProperties,
+ @JsonProperty("additionalGroupProperties")
+ Collection additionalGroupProperties) {
+ this.authorities = authorities;
+ this.username = username;
+ this.password = password;
+ this.validUntil = validUntil;
+ this.enabled = enabled;
+ this.organisation = organisation;
+ if (additionalProperties != null) {
+ this.additionalProperties.addAll(additionalProperties);
+ }
+ if (additionalGroupProperties != null) {
+ this.additionalGroupProperties.addAll(additionalGroupProperties);
+ }
+ }
+
@Override
- public Collection extends GrantedAuthority> getAuthorities() {
+ @NonNull public Collection extends GrantedAuthority> getAuthorities() {
return authorities;
}
@@ -38,7 +85,7 @@ public String getPassword() {
}
@Override
- public String getUsername() {
+ @NonNull public String getUsername() {
return username;
}
@@ -59,11 +106,11 @@ public String getOrganisation() {
@Override
public Collection getAdditionalProperties() {
- return additionalProperties;
+ return Collections.unmodifiableCollection(additionalProperties);
}
@Override
public Collection getAdditionalGroupProperties() {
- return additionalGroupProperties;
+ return Collections.unmodifiableCollection(additionalGroupProperties);
}
}
diff --git a/src/main/java/org/tailormap/api/security/TailormapUserDetailsImplMixin.java b/src/main/java/org/tailormap/api/security/TailormapUserDetailsImplMixin.java
new file mode 100644
index 00000000..cc3a186a
--- /dev/null
+++ b/src/main/java/org/tailormap/api/security/TailormapUserDetailsImplMixin.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 B3Partners B.V.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+package org.tailormap.api.security;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import java.time.ZonedDateTime;
+import java.util.Collection;
+import org.springframework.security.core.GrantedAuthority;
+
+@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
+@JsonAutoDetect(
+ fieldVisibility = JsonAutoDetect.Visibility.ANY,
+ getterVisibility = JsonAutoDetect.Visibility.NONE,
+ isGetterVisibility = JsonAutoDetect.Visibility.NONE)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public abstract class TailormapUserDetailsImplMixin {
+
+ @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
+ public TailormapUserDetailsImplMixin(
+ @SuppressWarnings("unused") @JsonProperty("authorities") Collection authorities,
+ @SuppressWarnings("unused") @JsonProperty("username") String username,
+ @SuppressWarnings("unused") @JsonProperty("password") String password,
+ @SuppressWarnings("unused") @JsonProperty("validUntil") ZonedDateTime validUntil,
+ @SuppressWarnings("unused") @JsonProperty("enabled") boolean enabled,
+ @SuppressWarnings("unused") @JsonProperty("organisation") String organisation,
+ @SuppressWarnings("unused") @JsonProperty("additionalProperties")
+ Collection additionalProperties,
+ @SuppressWarnings("unused") @JsonProperty("additionalGroupProperties")
+ Collection additionalGroupProperties) {
+ // mixin constructor only for Jackson; no implementation
+ }
+
+ /** prevents serializing the password. */
+ @JsonIgnore
+ public abstract String getPassword();
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 4423f5ef..32d8d8af 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -36,7 +36,7 @@ spring.datasource.username=planmonitor-wonen
spring.datasource.password=planmonitor-wonen
# Actuator
-management.endpoints.enabled-by-default=true
+management.endpoints.access.default=read_only
management.endpoints.web.base-path=/api/planmonitorwonen/actuator
management.endpoints.web.exposure.include=info,health,prometheus,loggers,logfile,mappings
@@ -63,5 +63,7 @@ logging.level.org.springframework.boot.autoconfigure=INFO
logging.level.org.springframework.test.context=INFO
logging.level.org.springframework.security.web.authentication=DEBUG
-logging.level.nl.b3p.planmonitorwonen.api=DEBUG
+logging.group.planmonitorwonen=nl.b3p.planmonitorwonen.api
+logging.group.auth=org.tailormap.api.security,nl.b3p.planmonitorwonen.api.configuration.JdbcSessionConfiguration
+logging.group.geotools=org.geotools
From ced324bf81a661473606d7c7aebf89385118f5c6 Mon Sep 17 00:00:00 2001
From: Mark Prins <1165786+mprins@users.noreply.github.com>
Date: Thu, 29 Jan 2026 10:56:46 +0100
Subject: [PATCH 2/3] Disable Object to json serialisation for now; it is not
used in teh application because sessions are read-only
---
.../JdbcSessionConfiguration.java | 22 ++++++++++---------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/src/main/java/nl/b3p/planmonitorwonen/api/configuration/JdbcSessionConfiguration.java b/src/main/java/nl/b3p/planmonitorwonen/api/configuration/JdbcSessionConfiguration.java
index b7b6c5da..37dd75e0 100644
--- a/src/main/java/nl/b3p/planmonitorwonen/api/configuration/JdbcSessionConfiguration.java
+++ b/src/main/java/nl/b3p/planmonitorwonen/api/configuration/JdbcSessionConfiguration.java
@@ -155,16 +155,18 @@ public ConversionService springSessionConversionService() {
final GenericConversionService converter = new GenericConversionService();
// Object -> byte[] (serialize to JSON bytes)
- converter.addConverter(Object.class, byte[].class, source -> {
- try {
- logger.debug("Serializing Spring Session: {}", source);
- return mapper.writerFor(Object.class).writeValueAsBytes(source);
- } catch (JacksonException e) {
- logger.error("Error serializing Spring Session object: {}", source, e);
- throw new ConversionFailedException(
- TypeDescriptor.forObject(source), TypeDescriptor.valueOf(byte[].class), source, e);
- }
- });
+ // this is not actually done in the application because sessions are
+ // read-only, so this is commented out but left here for possible future use
+ // converter.addConverter(Object.class, byte[].class, source -> {
+ // try {
+ // logger.debug("Serializing Spring Session: {}", source);
+ // return mapper.writerFor(Object.class).writeValueAsBytes(source);
+ // } catch (JacksonException e) {
+ // logger.error("Error serializing Spring Session object: {}", source, e);
+ // throw new ConversionFailedException(
+ // TypeDescriptor.forObject(source), TypeDescriptor.valueOf(byte[].class), source, e);
+ // }
+ // });
// byte[] -> Object (deserialize from JSON bytes)
converter.addConverter(byte[].class, Object.class, source -> {
try {
From 8ed8e65a512b4ab6069f7554e79e68834152c140 Mon Sep 17 00:00:00 2001
From: Mark Prins <1165786+mprins@users.noreply.github.com>
Date: Thu, 29 Jan 2026 10:57:46 +0100
Subject: [PATCH 3/3] PMW-60: update versienummer naar volgende minor
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 32353b72..7e706848 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,7 +13,7 @@ SPDX-License-Identifier: MIT
nl.b3p.pmw
planmonitor-wonen-api
- 1.2.6-SNAPSHOT
+ 1.3.0-SNAPSHOT
jar
Planmonitor Wonen API
Planmonitor Wonen API provides the backend for Planmonitor Wonen