Fix Sonar findings (#2553)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-07-16 10:38:47 +03:00
committed by GitHub
parent 51ba6b9a1a
commit 0d38cb5a7d
4 changed files with 197 additions and 24 deletions

View File

@@ -36,14 +36,16 @@ public class MgmtTargetGroupResource implements MgmtTargetGroupRestApi {
private final TargetManagement targetManagement;
private final TenantConfigHelper tenantConfigHelper;
public MgmtTargetGroupResource(final TargetManagement targetManagement, final SystemSecurityContext systemSecurityContext,
final TenantConfigurationManagement tenantConfigurationManagement) {
public MgmtTargetGroupResource(
final TargetManagement targetManagement,
final TenantConfigurationManagement tenantConfigurationManagement, final SystemSecurityContext systemSecurityContext) {
this.targetManagement = targetManagement;
this.tenantConfigHelper = TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement);
}
@Override
public ResponseEntity<PagedList<MgmtTarget>> getAssignedTargets(String group, int pagingOffsetParam, int pagingLimitParam, String sortParam) {
public ResponseEntity<PagedList<MgmtTarget>> getAssignedTargets(
final String group, final int pagingOffsetParam, final int pagingLimitParam, final String sortParam) {
final Pageable pageable = PagingUtility.toPageable(pagingOffsetParam, pagingLimitParam, sanitizeTargetSortParam(sortParam));
final Page<Target> targets = targetManagement.findTargetsByGroup(group, false, pageable);
@@ -53,7 +55,8 @@ public class MgmtTargetGroupResource implements MgmtTargetGroupRestApi {
}
@Override
public ResponseEntity<PagedList<MgmtTarget>> getAssignedTargetsWithSubgroups(String groupFilter, boolean subgroups, int pagingOffsetParam, int pagingLimitParam, String sortParam) {
public ResponseEntity<PagedList<MgmtTarget>> getAssignedTargetsWithSubgroups(
final String groupFilter, final boolean subgroups, final int pagingOffsetParam, final int pagingLimitParam, final String sortParam) {
final Pageable pageable = PagingUtility.toPageable(pagingOffsetParam, pagingLimitParam, sanitizeTargetSortParam(sortParam));
final Page<Target> targets = targetManagement.findTargetsByGroup(groupFilter, subgroups, pageable);
@@ -63,31 +66,29 @@ public class MgmtTargetGroupResource implements MgmtTargetGroupRestApi {
}
@Override
public ResponseEntity<Void> assignTargetsToGroup(String group, List<String> controllerIds) {
public ResponseEntity<Void> assignTargetsToGroup(final String group, final List<String> controllerIds) {
return assignTargets(group, controllerIds);
}
@Override
public ResponseEntity<Void> assignTargetsToGroupWithSubgroups(String group, List<String> controllerIds) {
public ResponseEntity<Void> assignTargetsToGroupWithSubgroups(final String group, final List<String> controllerIds) {
return assignTargets(group, controllerIds);
}
@Override
public ResponseEntity<Void> assignTargetsToGroupWithRsql(String group, String rsql) {
targetManagement.assignTargetGroupWithRsql(group, rsql);
return ResponseEntity.ok().build();
public ResponseEntity<Void> assignTargetsToGroupWithRsql(final String group, final String rsql) {
return assignTargetsToGroupWithRsql0(group, rsql);
}
@Override
public ResponseEntity<Void> unassignTargetsFromGroup(List<String> controllerIds) {
public ResponseEntity<Void> unassignTargetsFromGroup(final List<String> controllerIds) {
targetManagement.assignTargetsWithGroup(null, controllerIds);
return ResponseEntity.ok().build();
}
@Override
public ResponseEntity<Void> unassignTargetsFromGroupByRsql(String rsql) {
targetManagement.assignTargetGroupWithRsql(null, rsql);
return ResponseEntity.ok().build();
public ResponseEntity<Void> unassignTargetsFromGroupByRsql(final String rsql) {
return assignTargetsToGroupWithRsql0(null, rsql);
}
@Override
@@ -98,12 +99,16 @@ public class MgmtTargetGroupResource implements MgmtTargetGroupRestApi {
@Override
public ResponseEntity<Void> assignTargetsToGroup(final String group, final String rsql) {
targetManagement.assignTargetGroupWithRsql(group, rsql);
return ResponseEntity.ok().build();
return assignTargetsToGroupWithRsql0(group, rsql);
}
private ResponseEntity<Void> assignTargets(final String group, final List<String> controllerIds) {
targetManagement.assignTargetsWithGroup(group, controllerIds);
return ResponseEntity.ok().build();
}
}
private ResponseEntity<Void> assignTargetsToGroupWithRsql0(final String group, final String rsql) {
targetManagement.assignTargetGroupWithRsql(group, rsql);
return ResponseEntity.ok().build();
}
}

View File

@@ -9,6 +9,10 @@
*/
package org.eclipse.hawkbit.im.authentication;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Hierarchy {
public static final String DEFAULT =

View File

@@ -9,7 +9,11 @@
*/
package org.eclipse.hawkbit.security;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serial;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
@@ -18,7 +22,6 @@ import java.util.concurrent.Callable;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.im.authentication.SpRole;
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
@@ -166,7 +169,7 @@ public class SystemSecurityContext {
* wraps the original authentication object. The wrapped object contains the necessary {@link SpRole#SYSTEM_ROLE}
* which is allowed to execute all secured methods.
*/
@Getter
@SuppressWarnings("java:S4275") // java:S4275 - intentionally returns the "hold" objects
public static final class SystemCodeAuthentication implements Authentication {
@Serial
@@ -174,14 +177,14 @@ public class SystemSecurityContext {
private static final List<SimpleGrantedAuthority> AUTHORITIES = List.of(new SimpleGrantedAuthority(SpRole.SYSTEM_ROLE));
private final Object credentials;
private final Object details;
private final Object principal;
private final Holder credentials;
private final Holder details;
private final Holder principal;
private SystemCodeAuthentication(final Authentication oldAuthentication) {
credentials = oldAuthentication != null ? oldAuthentication.getCredentials() : null;
details = oldAuthentication != null ? oldAuthentication.getDetails() : null;
principal = oldAuthentication != null ? oldAuthentication.getPrincipal() : null;
credentials = new Holder(oldAuthentication != null ? oldAuthentication.getCredentials() : null);
details = new Holder(oldAuthentication != null ? oldAuthentication.getDetails() : null);
principal = new Holder(oldAuthentication != null ? oldAuthentication.getPrincipal() : null);
}
@Override
@@ -194,6 +197,21 @@ public class SystemSecurityContext {
return AUTHORITIES;
}
@Override
public Object getCredentials() {
return credentials.obj;
}
@Override
public Object getDetails() {
return details.obj;
}
@Override
public Object getPrincipal() {
return principal.obj;
}
@Override
public boolean isAuthenticated() {
return true;
@@ -203,5 +221,28 @@ public class SystemSecurityContext {
public void setAuthenticated(final boolean isAuthenticated) {
throw new UnsupportedOperationException();
}
// Serializable wrapper that ensures that the content will be serialized only if it is Serializable
private static class Holder implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private Object obj;
private Holder(final Object obj) {
this.obj = obj;
}
@Serial
private void writeObject(final ObjectOutputStream oos) throws IOException {
oos.writeObject(obj instanceof Serializable ? obj : null);
}
@Serial
private void readObject(final ObjectInputStream ois) throws IOException, ClassNotFoundException {
obj = ois.readObject();
}
}
}
}

View File

@@ -0,0 +1,123 @@
/**
* Copyright (c) 2025 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.security;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;
import org.assertj.core.api.Assertions;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.junit.jupiter.api.Test;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
class SystemCodeAuthenticationTest {
private static final SystemSecurityContext SYSTEM_SECURITY_CONTEXT = new SystemSecurityContext(new TenantAware() {
@Override
public String getCurrentTenant() {
return "tenant";
}
@Override
public String getCurrentUsername() {
return "user";
}
@Override
public <T> T runAsTenant(final String tenant, final TenantRunner<T> tenantRunner) {
return tenantRunner.run();
}
@Override
public <T> T runAsTenantAsUser(final String tenant, final String username, final TenantRunner<T> tenantRunner) {
return tenantRunner.run();
}
});
@Test
void testSerializationWithoutNull() {
final UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("test", "pass", List.of(new SimpleGrantedAuthority("anonymous")));
auth.setDetails("string details");
test(auth);
}
@Test
void testSerializationWithNullPrincipal() {
final UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(null, "pass", List.of(new SimpleGrantedAuthority("anonymous")));
auth.setDetails("string details");
test(auth);
}
@Test
void testSerializationWithNullCredentials() {
final UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("test", null, List.of(new SimpleGrantedAuthority("anonymous")));
auth.setDetails("string details");
test(auth);
}
@Test
void testSerializationWithNullDetails() {
final UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("test", "pass", List.of(new SimpleGrantedAuthority("anonymous")));
auth.setDetails(null);
test(auth);
}
@Test
void testSerializationWitAllNull() {
final UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(null, null, List.of(new SimpleGrantedAuthority("anonymous")));
auth.setDetails(null);
test(auth);
}
private static void test(final UsernamePasswordAuthenticationToken auth) {
final SecurityContext sc = SecurityContextHolder.createEmptyContext();
sc.setAuthentication(auth);
SecurityContextHolder.setContext(sc);
SYSTEM_SECURITY_CONTEXT.runAsSystemAsTenant(() -> {
final Authentication currentAuth = SecurityContextHolder.getContext().getAuthentication();
Assertions.assertThat(currentAuth.getClass().getSimpleName()).isEqualTo("SystemCodeAuthentication");
Assertions.assertThat(currentAuth.getPrincipal()).isEqualTo(auth.getPrincipal());
Assertions.assertThat(currentAuth.getCredentials()).isEqualTo(auth.getCredentials());
Assertions.assertThat(currentAuth.getAuthorities()).isEqualTo(List.of(new SimpleGrantedAuthority("ROLE_SYSTEM_CODE")));
Assertions.assertThat(currentAuth.getDetails()).isEqualTo(auth.getDetails());
final Authentication serializedAndDeserializedAuth = serializeAndDeserialize(currentAuth);
Assertions.assertThat(serializedAndDeserializedAuth.getClass().getSimpleName()).isEqualTo("SystemCodeAuthentication");
Assertions.assertThat(serializedAndDeserializedAuth.getPrincipal()).isEqualTo(auth.getPrincipal());
Assertions.assertThat(serializedAndDeserializedAuth.getCredentials()).isEqualTo(auth.getCredentials());
Assertions.assertThat(serializedAndDeserializedAuth.getAuthorities()).isEqualTo(List.of(new SimpleGrantedAuthority("ROLE_SYSTEM_CODE")));
Assertions.assertThat(serializedAndDeserializedAuth.getDetails()).isEqualTo(auth.getDetails());
return null;
}, "tenant");
SecurityContextHolder.clearContext();
}
@SuppressWarnings("unchecked")
private static <T> T serializeAndDeserialize(final T object) throws IOException, ClassNotFoundException {
try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
try (final ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(object);
oos.flush();
}
try (final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) {
return (T) ois.readObject();
}
}
}
}