From 6e334d4888e31d8153b254c43a38905a54b63dc7 Mon Sep 17 00:00:00 2001 From: Avgustin Marinov Date: Tue, 9 Sep 2025 17:05:05 +0300 Subject: [PATCH] Add support for "username" to be set as auditor (#2661) Signed-off-by: Avgustin Marinov --- .../security/SecurityContextSerializer.java | 20 +++++++++++++++---- .../SecurityContextSerializerTest.java | 18 +++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SecurityContextSerializer.java b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SecurityContextSerializer.java index 43c388545..025354938 100644 --- a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SecurityContextSerializer.java +++ b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SecurityContextSerializer.java @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.Objects; import java.util.stream.Stream; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.AccessLevel; @@ -28,6 +30,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.eclipse.hawkbit.security.SpringSecurityAuditorAware.AuditorAwarePrincipal; import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails; +import org.eclipse.hawkbit.tenancy.TenantAwareUser; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; @@ -103,7 +106,7 @@ public interface SecurityContextSerializer { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final boolean FALLBACK_TO_JAVA_SERIALIZATION = - !Boolean.getBoolean("hawkbit.security.contextSerializer.json.no-fallback-to-java"); + !Boolean.getBoolean("org.hawkbit.security.contextSerializer.json.no-fallback-to-java"); @Override public String serialize(final SecurityContext securityContext) { @@ -143,8 +146,11 @@ public interface SecurityContextSerializer { private String tenant; private boolean controller; + // auditor / username (authentication principal name) private String auditor; + @JsonProperty(required = true) private String[] authorities; + @JsonProperty(defaultValue = "true") private boolean authenticated; SecCtxInfo(final SecurityContext securityContext) { @@ -152,10 +158,10 @@ public interface SecurityContextSerializer { if (authentication.getDetails() instanceof TenantAwareAuthenticationDetails tenantAwareDetails) { tenant = tenantAwareDetails.tenant(); controller = tenantAwareDetails.controller(); - } else { - tenant = null; - controller = false; + } else if (authentication.getPrincipal() instanceof TenantAwareUser tenantAwareUser) { + tenant = tenantAwareUser.getTenant(); } + // keep the auditor, ofr audit purposes, // sets principal to the resolved auditor and then deserialized authentication will return it as principal // since the class is not known to auditor aware - it shall used default - principal as auditor @@ -164,6 +170,12 @@ public interface SecurityContextSerializer { authenticated = authentication.isAuthenticated(); } + // allows setting for auditor also as username (so supported auditor/username in json) + @JsonSetter("username") + private void setUsername(final String username) { + this.auditor = username; + } + private SecurityContext toSecurityContext() { final SecurityContext ctx = SecurityContextHolder.createEmptyContext(); final Object details = tenant == null ? null : new TenantAwareAuthenticationDetails(tenant, controller); diff --git a/hawkbit-security-core/src/test/java/org/eclipse/hawkbit/security/SecurityContextSerializerTest.java b/hawkbit-security-core/src/test/java/org/eclipse/hawkbit/security/SecurityContextSerializerTest.java index edbc19724..bb9e93192 100644 --- a/hawkbit-security-core/src/test/java/org/eclipse/hawkbit/security/SecurityContextSerializerTest.java +++ b/hawkbit-security-core/src/test/java/org/eclipse/hawkbit/security/SecurityContextSerializerTest.java @@ -81,6 +81,24 @@ class SecurityContextSerializerTest { assertThat(deserializedOld.isAuthenticated()).isEqualTo(deserializedNew.isAuthenticated()); } + @Test + void testUsername() { + final SecurityContext securityContext = SecurityContextHolder.getContext(); + final UsernamePasswordAuthenticationToken userPassAuthentication = new UsernamePasswordAuthenticationToken( + "user", null, AUTHORITIES.stream().map(SimpleGrantedAuthority::new).toList()); + final TenantAwareAuthenticationDetails details = new TenantAwareAuthenticationDetails("my_tenant", false); + userPassAuthentication.setDetails(details); + securityContext.setAuthentication(userPassAuthentication); + + final String serialized = JSON_SERIALIZATION.serialize(securityContext).replace("auditor", "username"); + final SecurityContext deserialized = JSON_SERIALIZATION.deserialize(serialized); + final Authentication authentication = deserialized.getAuthentication(); + assertThat(SpringSecurityAuditorAware.resolveAuditor(authentication)).hasToString("user"); + assertThat(authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList()).isEqualTo(AUTHORITIES); + assertThat(authentication.isAuthenticated()).isTrue(); + assertThat(authentication.getDetails()).isEqualTo(details); + } + private static String bigString(final int length) { final StringBuilder sb = new StringBuilder(length); for (int i = 0; i < length; i++) {