Skip to content

Commit

Permalink
Added Security Tests for ManagementAPI @PreAuthorize methods
Browse files Browse the repository at this point in the history
  • Loading branch information
vasilchev committed Jan 6, 2025
1 parent 7e7fb0c commit 727d997
Show file tree
Hide file tree
Showing 20 changed files with 2,720 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,21 @@
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;

import io.qameta.allure.Step;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.api.Assertions;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
Expand Down Expand Up @@ -58,6 +64,7 @@
import org.eclipse.hawkbit.repository.test.TestConfiguration;
import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
import org.eclipse.hawkbit.repository.test.util.RolloutTestApprovalStrategy;
import org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
Expand All @@ -69,6 +76,7 @@
import org.springframework.test.context.TestPropertySource;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@ContextConfiguration(classes = {
RepositoryApplicationConfiguration.class, TestConfiguration.class })
@Import(TestChannelBinderConfiguration.class)
Expand All @@ -79,6 +87,8 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
protected static final String NOT_EXIST_ID = "12345678990";
protected static final long NOT_EXIST_IDL = Long.parseLong(NOT_EXIST_ID);

protected static final List<String> REPOSITORY_AND_TARGET_PERMISSIONS = List.of(SpPermission.READ_REPOSITORY, SpPermission.CREATE_REPOSITORY, SpPermission.UPDATE_REPOSITORY, SpPermission.DELETE_REPOSITORY, SpPermission.READ_TARGET, SpPermission.CREATE_TARGET, SpPermission.UPDATE_TARGET, SpPermission.DELETE_TARGET);

@PersistenceContext
protected EntityManager entityManager;

Expand Down Expand Up @@ -231,6 +241,68 @@ protected void assertScheduled(final Rollout rollout, final int count) {
assertThat(running.getTotalElements()).as("Action count").isEqualTo(count);
}

/**
* Asserts that the given callable throws an InsufficientPermissionException.
* If callable succeeds without any exception or exception other than InsufficientPermissionException, it will be considered as an assert failure.
*
* @param callable the callable to call
*/
@SneakyThrows
protected void assertPermissions(final Callable<?> callable, List<String> requiredPermissions) {
final List<String> insufficiantPermissions = REPOSITORY_AND_TARGET_PERMISSIONS.stream()
.filter(p -> !requiredPermissions.contains(p)).toList();
// check if the user has the correct permissions
SecurityContextSwitch.runAs(SecurityContextSwitch.withUser("user_with_permissions", requiredPermissions.toArray(new String[0])), () -> {
assertPermissionWorks(callable);
log.info("assertPermissionWorks Passed");
return null;
});

// check if the user has the insufficient permissions
SecurityContextSwitch.runAs(SecurityContextSwitch.withUser("user_without_permissions", insufficiantPermissions.toArray(new String[0])), () -> {
assertInsufficientPermission(callable);
log.info("assertInsufficientPermission Passed");
return null;
});
}

/**
* Asserts that the given callable throws an InsufficientPermissionException.
* If callable succeeds without any exception or exception other than InsufficientPermissionException, it will be considered as an assert failure.
*
* @param callable the callable to call
*/
private void assertInsufficientPermission(final Callable<?> callable) {
try {
callable.call();
throw new AssertionError(
"Expected Exception 'InsufficientPermissionException' to be thrown, but request passed with no exception.");
} catch (Exception ex) {
assertThat(ex).isInstanceOf(InsufficientPermissionException.class);
}
}

/**
* Asserts that the given callable succeeds.
*
* Note: This method will assume that EntityNotFoundException is OK, as security tests use dummy (non-existing) IDs.
* It matters to either callable succeeds without any exception or at most EntityNotFoundException.
* All other cases will be considered as an error.
*
* @param callable the callable to call
*/
private void assertPermissionWorks(final Callable<?> callable) {
try {
callable.call();
} catch (Throwable th) {
if (th instanceof EntityNotFoundException) {
log.info("Expected (at most) EntityNotFoundException catch: {}", th.getMessage());
} else {
throw new AssertionError("Expected no Exception (other then EntityNotFound) to be thrown, but got: " + th.getMessage(), th);
}
}
}

protected void finishAction(final Action action) {
controllerManagement
.addUpdateActionStatus(entityFactory.actionStatus().create(action.getId()).status(Action.Status.FINISHED));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* Copyright (c) 2022 Bosch.IO GmbH and others
*
* 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.repository.jpa;

import java.util.List;

import io.qameta.allure.Description;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.RepositoryManagement;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Pageable;

public abstract class AbstractRepositoryManagementSecurityTest<T, C, U> extends AbstractJpaIntegrationTest {

/**
* @return the repository management to test with
*/
protected abstract RepositoryManagement<T, C, U> getRepositoryManagement();

/**
* @return the object to create
*/
protected abstract C getCreateObject();

/**
* @return the object to update
*/
protected abstract U getUpdateObject();

@Test
@Description("Tests RepositoryManagement PreAuthorized method with correct and insufficient permissions.")
void createCollectionPermissionCheck() {
assertPermissions(() -> getRepositoryManagement().create(List.of(getCreateObject())), List.of(SpPermission.CREATE_REPOSITORY, SpPermission.READ_REPOSITORY));
}

@Test
@Description("Tests RepositoryManagement PreAuthorized method with correct and insufficient permissions.")
void createPermissionCheck() {
assertPermissions(() -> getRepositoryManagement().create(getCreateObject()), List.of(SpPermission.CREATE_REPOSITORY, SpPermission.READ_REPOSITORY));
}

@Test
@Description("Tests RepositoryManagement PreAuthorized method with correct and insufficient permissions.")
void updatePermissionCheck() {
assertPermissions(() -> getRepositoryManagement().update(getUpdateObject()), List.of(SpPermission.UPDATE_REPOSITORY));
}

@Test
@Description("Tests RepositoryManagement PreAuthorized method with correct and insufficient permissions.")
void deletePermissionCheck() {
assertPermissions(() -> {
getRepositoryManagement().delete(1L);
return null;
}, List.of(SpPermission.DELETE_REPOSITORY));
}

@Test
@Description("Tests RepositoryManagement PreAuthorized method with correct and insufficient permissions.")
public void countPermissionCheck() {
assertPermissions(() -> getRepositoryManagement().count(), List.of(SpPermission.READ_REPOSITORY));
}


@Test
@Description("Tests RepositoryManagement PreAuthorized method with correct and insufficient permissions.")
public void deleteCollectionRepositoryManagement() {
assertPermissions(() -> {
getRepositoryManagement().delete(List.of(1L));
return null;
}, List.of(SpPermission.DELETE_REPOSITORY));
}

@Test
@Description("Tests RepositoryManagement PreAuthorized method with correct and insufficient permissions.")
public void getPermissionCheck() {
assertPermissions(() -> getRepositoryManagement().get(1L), List.of(SpPermission.READ_REPOSITORY));
}

@Test
@Description("Tests RepositoryManagement PreAuthorized method with correct and insufficient permissions.")
public void getCollectionPermissionCheck() {
assertPermissions(() -> getRepositoryManagement().get(List.of(1L)), List.of(SpPermission.READ_REPOSITORY));
}

@Test
@Description("Tests RepositoryManagement PreAuthorized method with correct and insufficient permissions.")
public void existsCollectionPermissionCheck() {
assertPermissions(() -> getRepositoryManagement().exists(1L), List.of(SpPermission.READ_REPOSITORY));
}

@Test
@Description("Tests RepositoryManagement PreAuthorized method with correct and insufficient permissions.")
public void findAllPermissionCheck() {
assertPermissions(() -> getRepositoryManagement().findAll(Pageable.ofSize(1)), List.of(SpPermission.READ_REPOSITORY));
}

@Test
@Description("Tests RepositoryManagement PreAuthorized method with correct and insufficient permissions.")
public void findByRsqlPermissionCheck() {
assertPermissions(() -> getRepositoryManagement().findByRsql(Pageable.ofSize(1), "(name==*)"), List.of(SpPermission.READ_REPOSITORY));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* Copyright (c) 2022 Bosch.IO GmbH and others
*
* 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.repository.jpa.management;

import java.io.ByteArrayInputStream;
import java.util.List;

import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.ArtifactUpload;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.jupiter.api.Test;

@Feature("SecurityTests - ArtifactManagement")
@Story("SecurityTests ArtifactManagement")
class ArtifactManagementSecurityTest extends AbstractJpaIntegrationTest {

@Test
@Description("Tests ArtifactManagement#count() method")
@WithUser(principal = "user", authorities = { SpPermission.READ_REPOSITORY })
void countPermissionCheck() {
assertPermissions(() -> artifactManagement.count(), List.of(SpPermission.READ_REPOSITORY));
}

@Test
@Description("Tests ArtifactManagement#create() method")
void createPermissionCheck() {
ArtifactUpload artifactUpload = new ArtifactUpload(new ByteArrayInputStream("RandomString".getBytes()), 1L, "filename", false, 1024);
assertPermissions(() -> artifactManagement.create(artifactUpload), List.of(SpPermission.CREATE_REPOSITORY));
}

@Test
@Description("Tests ArtifactManagement#delete() method")
void deletePermissionCheck() {
assertPermissions(() -> {
artifactManagement.delete(1);
return null;
}, List.of(SpPermission.DELETE_REPOSITORY));
}

@Test
@Description("Tests ArtifactManagement#get() method")
void getPermissionCheck() {
assertPermissions(() -> artifactManagement.get(1L), List.of(SpPermission.READ_REPOSITORY));
}

@Test
@Description("Tests ArtifactManagement#getByFilenameAndSoftwareModule() method")
void getByFilenameAndSoftwareModulePermissionCheck() {
assertPermissions(() -> artifactManagement.getByFilenameAndSoftwareModule("filename", 1L),
List.of(SpPermission.READ_REPOSITORY));
}

@Test
@Description("Tests ArtifactManagement#findFirstBySHA1() method")
void findFirstBySHA1PermissionCheck() {
assertPermissions(() -> artifactManagement.findFirstBySHA1("sha1"), List.of(SpPermission.READ_REPOSITORY));
}

@Test
@Description("Tests ArtifactManagement#getByFilename() method")
void getByFilenamePermissionCheck() {
assertPermissions(() -> artifactManagement.getByFilename("filename"), List.of(SpPermission.READ_REPOSITORY));
}

@Test
@Description("Tests ArtifactManagement#findBySoftwareModule() method")
void findBySoftwareModulePermissionCheck() {
assertPermissions(() -> artifactManagement.findBySoftwareModule(PAGE, 1L), List.of(SpPermission.READ_REPOSITORY));
}

@Test
@Description("Tests ArtifactManagement#countBySoftwareModule() method")
void countBySoftwareModulePermissionCheck() {
assertPermissions(() -> artifactManagement.countBySoftwareModule(1L), List.of(SpPermission.READ_REPOSITORY));
}

@Test
@Description("Tests ArtifactManagement#loadArtifactBinary() method")
void loadArtifactBinaryPermissionCheck() {
assertPermissions(() -> artifactManagement.loadArtifactBinary("sha1", 1L, false), List.of(SpPermission.DOWNLOAD_REPOSITORY_ARTIFACT));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Copyright (c) 2022 Bosch.IO GmbH and others
*
* 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.repository.jpa.management;

import java.util.List;

import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.junit.jupiter.api.Test;

@Feature("SecurityTests - ConfirmationManagement")
@Story("SecurityTests ConfirmationManagement")
class ConfirmationManagementSecurityTest extends AbstractJpaIntegrationTest {

@Test
@Description("Tests ConfirmationManagement#findActiveActionsWaitingConfirmation() method")
void findActiveActionsWaitingConfirmationPermissionsCheck() {
assertPermissions(() -> confirmationManagement.findActiveActionsWaitingConfirmation("controllerId"), List.of(SpPermission.READ_TARGET));
}

@Test
@Description("Tests ConfirmationManagement#activateAutoConfirmation() method")
void activateAutoConfirmationPermissionsCheck() {
assertPermissions(() -> confirmationManagement.activateAutoConfirmation("controllerId", "initiator", "remark"),
List.of(SpPermission.READ_REPOSITORY, SpPermission.UPDATE_TARGET));
}

@Test
@Description("Tests ConfirmationManagement#getStatus() method")
void getStatusPermissionsCheck() {
assertPermissions(() -> confirmationManagement.getStatus("controllerId"), List.of(SpPermission.READ_TARGET));
}

@Test
@Description("Tests ConfirmationManagement#autoConfirmActiveActions() method")
void autoConfirmActiveActionsPermissionsCheck() {
assertPermissions(() -> confirmationManagement.autoConfirmActiveActions("controllerId"),
List.of(SpPermission.READ_REPOSITORY, SpPermission.UPDATE_TARGET));
}

@Test
@Description("Tests ConfirmationManagement#confirmAction() method")
void confirmActionPermissionsCheck() {
assertPermissions(() -> confirmationManagement.confirmAction(1L, null, null),
List.of(SpPermission.READ_REPOSITORY, SpPermission.UPDATE_TARGET));
}

@Test
@Description("Tests ConfirmationManagement#denyAction() method")
void denyActionPermissionsCheck() {
assertPermissions(() -> confirmationManagement.denyAction(1L, null, null),
List.of(SpPermission.READ_REPOSITORY, SpPermission.UPDATE_TARGET));
}

@Test
@Description("Tests ConfirmationManagement#deactivateAutoConfirmation() method")
void deactivateAutoConfirmationPermissionsCheck() {
assertPermissions(() -> {
confirmationManagement.deactivateAutoConfirmation("controllerId");
return null;
}, List.of(SpPermission.READ_REPOSITORY, SpPermission.UPDATE_TARGET));
}

}
Loading

0 comments on commit 727d997

Please sign in to comment.