Add @PreAuthorize enabled check for applications (#1503)
Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -75,6 +75,10 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
|
||||
@@ -171,7 +171,7 @@ public class WithSpringAuthorityRule implements BeforeEachCallback, AfterEachCal
|
||||
};
|
||||
}
|
||||
|
||||
private static class SecurityContextWithUser implements SecurityContext {
|
||||
static class SecurityContextWithUser implements SecurityContext {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final WithUser annotation;
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.test.util;
|
||||
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.test.context.support.WithSecurityContext;
|
||||
import org.springframework.security.test.context.support.WithSecurityContextFactory;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -21,6 +25,7 @@ import java.lang.annotation.Target;
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@WithSecurityContext(factory = WithUser.WithUserPrincipalSecurityContextFactory.class)
|
||||
@Inherited
|
||||
public @interface WithUser {
|
||||
|
||||
@@ -74,4 +79,11 @@ public @interface WithUser {
|
||||
String[] removeFromAllPermission() default {};
|
||||
|
||||
boolean controller() default false;
|
||||
}
|
||||
|
||||
class WithUserPrincipalSecurityContextFactory implements WithSecurityContextFactory<WithUser> {
|
||||
@Override
|
||||
public SecurityContext createSecurityContext(final WithUser withUserPrincipal) {
|
||||
return new WithSpringAuthorityRule.SecurityContextWithUser(withUserPrincipal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,15 +13,41 @@ import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
|
||||
import org.eclipse.hawkbit.repository.test.TestConfiguration;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.EventVerifier;
|
||||
import org.eclipse.hawkbit.repository.test.util.CleanupTestExecutionListener;
|
||||
import org.eclipse.hawkbit.repository.test.util.JUnitTestLoggerExtension;
|
||||
import org.eclipse.hawkbit.repository.test.util.SharedSqlTestDatabaseExtension;
|
||||
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
|
||||
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
||||
import org.eclipse.hawkbit.rest.RestConfiguration;
|
||||
import org.eclipse.hawkbit.rest.filter.ExcludePathAwareShallowETagFilter;
|
||||
import org.eclipse.hawkbit.rest.util.FilterHttpResponse;
|
||||
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.util.Base64Utils;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.filter.CharacterEncodingFilter;
|
||||
|
||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.CONTROLLER_ROLE;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.SYSTEM_ROLE;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
@@ -30,24 +56,49 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
|
||||
/**
|
||||
* Test for {@link MgmtBasicAuthResource}.
|
||||
*
|
||||
*/
|
||||
@ActiveProfiles({ "test" })
|
||||
@ExtendWith({ JUnitTestLoggerExtension.class, SharedSqlTestDatabaseExtension.class })
|
||||
@SpringBootTest
|
||||
// destroy the context after each test class because otherwise we get problem
|
||||
// when context is
|
||||
// refreshed we e.g. get two instances of CacheManager which leads to very
|
||||
// strange test failures.
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
|
||||
// Cleaning repository will fire "delete" events. We won't count them to the
|
||||
// test execution. So, the order execution between EventVerifier and Cleanup is
|
||||
// important!
|
||||
@TestExecutionListeners(listeners = { EventVerifier.class, CleanupTestExecutionListener.class },
|
||||
mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
|
||||
@TestPropertySource(properties = "spring.main.allow-bean-definition-overriding=true")
|
||||
@WebAppConfiguration
|
||||
@AutoConfigureMockMvc
|
||||
@ContextConfiguration(classes = { MgmtApiConfiguration.class, RestConfiguration.class,
|
||||
RepositoryApplicationConfiguration.class, TestConfiguration.class,
|
||||
TestSupportBinderAutoConfiguration.class })
|
||||
//@TestPropertySource(locations = "classpath:/mgmt-test.properties")
|
||||
@Feature("Component Tests - Management API")
|
||||
@Story("Basic auth Userinfo Resource")
|
||||
public class MgmtBasicAuthResourceTest extends AbstractManagementApiIntegrationTest{
|
||||
public class MgmtBasicAuthResourceTest {
|
||||
|
||||
private static final String TEST_USER = "testUser";
|
||||
private static final String DEFAULT = "default";
|
||||
|
||||
// Need another mockMvc to bypass the default Basic auth security
|
||||
@Autowired
|
||||
MockMvc mockMvcWithSecurity;
|
||||
MockMvc defaultMock;
|
||||
|
||||
@Autowired
|
||||
private FilterHttpResponse filterHttpResponse;
|
||||
@Autowired
|
||||
private CharacterEncodingFilter characterEncodingFilter;
|
||||
@Autowired
|
||||
protected WebApplicationContext webApplicationContext;
|
||||
|
||||
@Test
|
||||
@Description("Test of userinfo api with basic auth validation")
|
||||
@WithUser(principal = TEST_USER)
|
||||
public void validateBasicAuthWithUserDetails() throws Exception {
|
||||
mvc.perform(get(MgmtRestConstants.AUTH_V1_REQUEST_MAPPING)).andDo(MockMvcResultPrinter.print())
|
||||
withSecurityMock().perform(get(MgmtRestConstants.AUTH_V1_REQUEST_MAPPING)).andDo(MockMvcResultPrinter.print())
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaTypes.HAL_JSON_VALUE))
|
||||
@@ -58,7 +109,7 @@ public class MgmtBasicAuthResourceTest extends AbstractManagementApiIntegrationT
|
||||
@Test
|
||||
@Description("Test of userinfo api with invalid basic auth fails")
|
||||
public void validateBasicAuthFailsWithInvalidCredentials() throws Exception {
|
||||
mockMvcWithSecurity.perform(get(MgmtRestConstants.AUTH_V1_REQUEST_MAPPING)
|
||||
defaultMock.perform(get(MgmtRestConstants.AUTH_V1_REQUEST_MAPPING)
|
||||
.header(HttpHeaders.AUTHORIZATION, getBasicAuth("wrongUser", "wrongSecret")))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isUnauthorized());
|
||||
@@ -67,4 +118,14 @@ public class MgmtBasicAuthResourceTest extends AbstractManagementApiIntegrationT
|
||||
private String getBasicAuth(final String username, final String password) {
|
||||
return "Basic " + Base64Utils.encodeToString((username + ":" + password).getBytes());
|
||||
}
|
||||
|
||||
private MockMvc withSecurityMock() throws Exception {
|
||||
return createMvcWebAppContext(webApplicationContext).build();
|
||||
}
|
||||
|
||||
private DefaultMockMvcBuilder createMvcWebAppContext(final WebApplicationContext context) {
|
||||
final DefaultMockMvcBuilder createMvcWebAppContext = MockMvcBuilders.webAppContextSetup(context);
|
||||
|
||||
return createMvcWebAppContext;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
@@ -52,7 +51,7 @@ public class DDIStart {
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, proxyTargetClass = true)
|
||||
public static class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
|
||||
public static class MethodSecurityConfig {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
@@ -52,7 +51,7 @@ public class MgmtServerStart {
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, proxyTargetClass = true)
|
||||
public static class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
|
||||
public static class MethodSecurityConfig {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2023 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.app.mgmt;
|
||||
|
||||
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.test.util.WithUser;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
|
||||
@Feature("Integration Test - Security")
|
||||
@Story("PreAuthorized enabled")
|
||||
public class PreAuthorizeEnabledTest extends AbstractSecurityTest {
|
||||
|
||||
@Test
|
||||
@Description("Tests whether request fail if a role is forbidden for the user")
|
||||
@WithUser(authorities = { SpPermission.READ_TARGET } )
|
||||
public void failIfNoRole() throws Exception {
|
||||
mvc.perform(get("/rest/v1/distributionsets")).andExpect(result ->
|
||||
assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.FORBIDDEN.value()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests whether request succeed if a role is granted for the user")
|
||||
@WithUser(authorities = { SpPermission.READ_REPOSITORY })
|
||||
public void successIfHasRole() throws Exception {
|
||||
mvc.perform(get("/rest/v1/distributionsets")).andExpect(result -> {
|
||||
assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2023 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.app;
|
||||
|
||||
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.test.util.WithUser;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
|
||||
@Feature("Integration Test - Security")
|
||||
@Story("PreAuthorized enabled")
|
||||
public class PreAuthorizeEnabledTest extends AbstractSecurityTest {
|
||||
|
||||
@Test
|
||||
@Description("Tests whether request fail if a role is forbidden for the user")
|
||||
@WithUser(authorities = { SpPermission.READ_TARGET } )
|
||||
public void failIfNoRole() throws Exception {
|
||||
mvc.perform(get("/rest/v1/distributionsets")).andExpect(result ->
|
||||
assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.FORBIDDEN.value()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests whether request succeed if a role is granted for the user")
|
||||
@WithUser(authorities = { SpPermission.READ_REPOSITORY })
|
||||
public void successIfHasRole() throws Exception {
|
||||
mvc.perform(get("/rest/v1/distributionsets")).andExpect(result -> {
|
||||
assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value());
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user