Skip to content
Snippets Groups Projects
SecurityConfigurationITCase.java 4.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch das Ministerium für Energiewende, Klimaschutz, Umwelt und Natur
     * Zentrales IT-Management
     *
     * Lizenziert unter der EUPL, Version 1.2 oder - sobald
     * diese von der Europäischen Kommission genehmigt wurden -
     * Folgeversionen der EUPL ("Lizenz");
     * Sie dürfen dieses Werk ausschließlich gemäß
     * dieser Lizenz nutzen.
     * Eine Kopie der Lizenz finden Sie hier:
     *
     * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
     *
     * Sofern nicht durch anwendbare Rechtsvorschriften
     * gefordert oder in schriftlicher Form vereinbart, wird
     * die unter der Lizenz verbreitete Software "so wie sie
     * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
     * ausdrücklich oder stillschweigend - verbreitet.
     * Die sprachspezifischen Genehmigungen und Beschränkungen
     * unter der Lizenz sind dem Lizenztext zu entnehmen.
     */
    package de.ozgcloud.admin.security;
    
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
    
    
    import org.apache.http.HttpHeaders;
    
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Nested;
    
    import org.junit.jupiter.api.Test;
    
    import org.junit.jupiter.params.ParameterizedTest;
    import org.junit.jupiter.params.provider.ValueSource;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
    
    import org.springframework.http.HttpStatus;
    
    import org.springframework.security.test.context.support.WithMockUser;
    
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.ResultActions;
    
    
    import de.ozgcloud.admin.common.user.UserRole;
    
    import de.ozgcloud.common.test.DataITCase;
    import lombok.SneakyThrows;
    
    @DataITCase
    @AutoConfigureMockMvc
    
    class SecurityConfigurationITCase {
    
    
    	@Autowired
    	private MockMvc mockMvc;
    
    
    	@DisplayName("without authentication")
    
    	class TestWithoutAuthentication {
    
    
    		@DisplayName("allow for not found")
    		@SneakyThrows
    		@ParameterizedTest
    		@ValueSource(strings = {
    				"/actuator", "/actuator/x", "/actuator/x/y",
    				"/configserver", "/configserver/x",
    		})
    		void shouldAllowForNotFound(String path) {
    			var result = doPerform(path);
    
    			result.andExpect(status().isNotFound());
    		}
    
    		@DisplayName("allow")
    		@SneakyThrows
    		@ParameterizedTest
    		@ValueSource(strings = {
    
    				"/configserver/name/profile"
    		})
    		void shouldAllow(String path) {
    			var result = doPerform(path);
    
    			result.andExpect(status().isOk());
    		}
    
    		@SneakyThrows
    		@ParameterizedTest
    		@ValueSource(strings = {
    
    				"/api", "/api/configuration", "/api/configuration/settings",
    
    				"/unknown",
    		})
    		void shouldDeny(String path) {
    			var result = doPerform(path);
    
    			result.andExpect(status().isUnauthorized());
    		}
    
    
    		@DisplayName("deny with problem details")
    		@Nested
    		class TestDenyWithProblemDetails {
    
    			@SneakyThrows
    			@Test
    			void shouldHaveStatus() {
    				var result = doPerform("/api");
    
    				result.andExpect(jsonPath("$.status").value(HttpStatus.UNAUTHORIZED.value()));
    			}
    
    			@SneakyThrows
    			@Test
    			void shouldHaveInstanceURI() {
    				var result = doPerform("/api");
    
    				result.andExpect(jsonPath("$.instance").value("/api"));
    			}
    
    
    			@Test
    			@SneakyThrows
    
    			void shouldHaveErrorDetailInBody() {
    
    				var result = doPerform("/api");
    
    				result.andExpect(jsonPath("$.detail").value("Full authentication is required to access this resource"));
    
    			void shouldHaveHeader() {
    
    				var result = doPerform("/api");
    
    				result.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, "Bearer realm=\"Restricted Content\""));
    			}
    
    		@SneakyThrows
    		private ResultActions doPerform(String path) {
    			return mockMvc.perform(get(path).header("Authorization", "invalid"));
    		}
    	}
    
    
    	@DisplayName("with authentication")
    
    	class TestWithAuthentication {
    
    
    		static final String CLAIMS = """
    				{
    				  "preferredUsername": "testUser",
    				  "scope": "openid testscope"
    				}""";
    
    		@SneakyThrows
    		@ParameterizedTest
    		@ValueSource(strings = {
    
    				"/configserver/name/profile",
    
    				"/api", "/api/configuration"
    
    		})
    		@WithJwt(CLAIMS)
    		void shouldAllow(String path) {
    			var result = doPerformAuthenticated(path);
    
    			result.andExpect(status().isOk());
    		}
    
    
    		@Test
    		@SneakyThrows
    		@WithJwt(CLAIMS)
    		void shouldForbid() {
    			var result = doPerformAuthenticated("/api/configuration/settings");
    
    			result.andExpect(status().isForbidden());
    		}
    
    
    		@SneakyThrows
    		private ResultActions doPerformAuthenticated(String path) {
    			return mockMvc.perform(get(path));
    		}
    	}
    
    
    	@DisplayName("with admin role")
    	@Nested
    	class TestWithAdminRole {
    		@Test
    		@SneakyThrows
    
    		@WithMockUser(roles = UserRole.ADMIN_USER)
    
    		void shouldAllow() {
    			var result = mockMvc.perform(get("/api/configuration/settings"));
    
    			result.andExpect(status().isOk());
    		}
    	}