Add support for "username" to be set as auditor (#2661)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-09-09 17:05:05 +03:00
committed by GitHub
parent ae3a004da0
commit 6e334d4888
2 changed files with 34 additions and 4 deletions

View File

@@ -21,6 +21,8 @@ import java.util.Collection;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Stream; 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.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AccessLevel; import lombok.AccessLevel;
@@ -28,6 +30,7 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.security.SpringSecurityAuditorAware.AuditorAwarePrincipal; import org.eclipse.hawkbit.security.SpringSecurityAuditorAware.AuditorAwarePrincipal;
import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails; import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.tenancy.TenantAwareUser;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority; 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 ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final boolean FALLBACK_TO_JAVA_SERIALIZATION = 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 @Override
public String serialize(final SecurityContext securityContext) { public String serialize(final SecurityContext securityContext) {
@@ -143,8 +146,11 @@ public interface SecurityContextSerializer {
private String tenant; private String tenant;
private boolean controller; private boolean controller;
// auditor / username (authentication principal name)
private String auditor; private String auditor;
@JsonProperty(required = true)
private String[] authorities; private String[] authorities;
@JsonProperty(defaultValue = "true")
private boolean authenticated; private boolean authenticated;
SecCtxInfo(final SecurityContext securityContext) { SecCtxInfo(final SecurityContext securityContext) {
@@ -152,10 +158,10 @@ public interface SecurityContextSerializer {
if (authentication.getDetails() instanceof TenantAwareAuthenticationDetails tenantAwareDetails) { if (authentication.getDetails() instanceof TenantAwareAuthenticationDetails tenantAwareDetails) {
tenant = tenantAwareDetails.tenant(); tenant = tenantAwareDetails.tenant();
controller = tenantAwareDetails.controller(); controller = tenantAwareDetails.controller();
} else { } else if (authentication.getPrincipal() instanceof TenantAwareUser tenantAwareUser) {
tenant = null; tenant = tenantAwareUser.getTenant();
controller = false;
} }
// keep the auditor, ofr audit purposes, // keep the auditor, ofr audit purposes,
// sets principal to the resolved auditor and then deserialized authentication will return it as principal // 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 // 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(); 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() { private SecurityContext toSecurityContext() {
final SecurityContext ctx = SecurityContextHolder.createEmptyContext(); final SecurityContext ctx = SecurityContextHolder.createEmptyContext();
final Object details = tenant == null ? null : new TenantAwareAuthenticationDetails(tenant, controller); final Object details = tenant == null ? null : new TenantAwareAuthenticationDetails(tenant, controller);

View File

@@ -81,6 +81,24 @@ class SecurityContextSerializerTest {
assertThat(deserializedOld.isAuthenticated()).isEqualTo(deserializedNew.isAuthenticated()); 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) { private static String bigString(final int length) {
final StringBuilder sb = new StringBuilder(length); final StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {