attributes = principal.getAttributes();
+ assertThat(attributes.get(OAuth2TokenIntrospectionClaimNames.ACTIVE)).isEqualTo(opaqueTokenResponse.isActive());
+ assertThat(attributes.get(OAuth2TokenIntrospectionClaimNames.SCOPE)).isEqualTo(opaqueTokenResponse.getScope());
+ assertThat(attributes.get(OAuth2TokenIntrospectionClaimNames.CLIENT_ID)).isEqualTo(opaqueTokenResponse.getClientId());
+ assertThat(attributes.get(OAuth2TokenIntrospectionClaimNames.USERNAME)).isEqualTo(opaqueTokenResponse.getUsername());
+ assertThat(attributes.get(OAuth2TokenIntrospectionClaimNames.TOKEN_TYPE)).isEqualTo(opaqueTokenResponse.getTokenType());
+ assertThat((Instant) attributes.get(OAuth2TokenIntrospectionClaimNames.IAT)).isAfter(Instant.EPOCH);
+ assertThat((Instant) attributes.get(OAuth2TokenIntrospectionClaimNames.EXP)).isEqualTo(Instant.ofEpochSecond(expiresAt));
+ assertThat(attributes.get(OAuth2TokenIntrospectionClaimNames.SUB)).isEqualTo(opaqueTokenResponse.getSubject());
+ assertThat(attributes.get(OAuth2TokenIntrospectionClaimNames.AUD)).isEqualTo(opaqueTokenResponse.getAudience());
+ }
+
+ @Test
+ void introspect_with_null_expires_at_constructs_principal_with_default_expires_at() {
+ /* prepare */
+ Instant now = Instant.now();
+ OpaqueTokenResponse opaqueTokenResponse = createOpaqueTokenResponse(Boolean.TRUE, null);
+ when(restTemplate.postForObject(eq(INTROSPECTION_URI), any(), eq(OpaqueTokenResponse.class))).thenReturn(opaqueTokenResponse);
+ Collection extends GrantedAuthority> authorities = Set.of(new SimpleGrantedAuthority(TestRoles.USER));
+ when(userDetailsService.loadUserByUsername(SUBJECT)).thenReturn(new TestUserDetails(authorities, SUBJECT));
+
+ /* execute */
+ OAuth2AuthenticatedPrincipal principal = introspectorToTest.introspect(OPAQUE_TOKEN);
+
+ /* assert */
+ Instant actual = (Instant) principal.getAttributes().get(OAuth2TokenIntrospectionClaimNames.EXP);
+ assertThat(actual).isNotNull();
+ assertThat(actual).isAfterOrEqualTo(now.plusSeconds(DEFAULT_EXPIRES_IN_SECONDS));
+ }
+
+ /* @formatter:off */
+ private static OpaqueTokenResponse createOpaqueTokenResponse(Boolean isActive, Long expiresAt) {
+ return new OpaqueTokenResponse(
+ isActive,
+ "scope",
+ "client-id",
+ "client-type",
+ SUBJECT,
+ "token-type",
+ expiresAt,
+ SUBJECT,
+ "aud",
+ "group-type"
+ );
+ }
+ /* @formatter:on */
+
+ private static class Base64OAuth2OpaqueTokenIntrospectorSingleNullArgumentProvider implements ArgumentsProvider {
+ @Override
+ public Stream extends Arguments> provideArguments(ExtensionContext extensionContext) throws Exception {
+ return Stream.of(Arguments.of(null, INTROSPECTION_URI, CLIENT_ID, CLIENT_SECRET, userDetailsService, "Parameter restTemplate must not be null"),
+ Arguments.of(restTemplate, null, CLIENT_ID, CLIENT_SECRET, userDetailsService, "Parameter introspectionUri must not be null"),
+ Arguments.of(restTemplate, INTROSPECTION_URI, null, CLIENT_SECRET, userDetailsService, "Parameter clientId must not be null"),
+ Arguments.of(restTemplate, INTROSPECTION_URI, CLIENT_ID, null, userDetailsService, "Parameter clientSecret must not be null"),
+ Arguments.of(restTemplate, INTROSPECTION_URI, CLIENT_ID, CLIENT_SECRET, null, "Parameter userDetailsService must not be null"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtIntegrationTest.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtIntegrationTest.java
new file mode 100644
index 0000000000..1635c03529
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtIntegrationTest.java
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.spring.security;
+
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.http.HttpHeaders;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+
+import com.mercedesbenz.sechub.testframework.spring.YamlPropertyLoaderFactory;
+
+/**
+ * This test class verifies the integration of Spring Security OAuth2 components
+ * in JWT mode.
+ *
+ *
+ * Unlike {@link SecurityConfigurationTest}, which primarily tests if a endpoint
+ * is secured on an abstract level, this class exercises the full OAuth2 flow
+ * with real OAuth2 mechanisms. We do that by relying on the
+ * {@link AbstractSecurityConfiguration}.
+ *
+ *
+ *
+ * In a typical setup, the
+ * {@link org.springframework.security.oauth2.jwt.JwtDecoder} decodes JWT tokens
+ * by integrating with a identity provider. With this configuration, however, we
+ * mock the identity provider to avoid external dependencies. Additionally, we
+ * mock the user's roles, which are otherwise fetched from the database.
+ *
+ *
+ *
+ * Note: This test class is not intended for verifying whether security
+ * is enabled on specific endpoints. For that, use
+ * {@link SecurityConfigurationTest}.
+ *
+ *
+ * @see AbstractSecurityConfiguration
+ * @see org.springframework.security.core.userdetails.UserDetailsService
+ * @see OAuth2JwtAuthenticationProvider
+ * @see org.springframework.security.oauth2.jwt.JwtDecoder
+ * @see SecurityConfigurationTest
+ *
+ * @author hamidonos
+ */
+@WebMvcTest
+@TestPropertySource(locations = "classpath:application-jwt-test.yml", factory = YamlPropertyLoaderFactory.class)
+@ActiveProfiles("oauth2")
+class OAuth2JwtIntegrationTest {
+
+ /**
+ * For this test we call the API endpoint /api/user. It is just a mock endpoint
+ * to test the OAuth2 integration. It could also be any other endpoint.
+ */
+ private static final String API_USER = "/api/user";
+
+ private final MockMvc mockMvc;
+
+ @Autowired
+ OAuth2JwtIntegrationTest(MockMvc mockMvc) {
+ this.mockMvc = mockMvc;
+ }
+
+ @Test
+ void api_user_is_not_accessible_anonymously() throws Exception {
+ /* execute & test */
+ /* @formatter:off */
+ mockMvc
+ .perform(MockMvcRequestBuilders.get(API_USER))
+ .andExpect(status().isUnauthorized());
+ /* @formatter:on */
+ }
+
+ @Test
+ void api_user_is_accessible_as_superadmin() throws Exception {
+ /* prepare */
+ String authHeader = TestOAuth2JwtSecurityConfiguration.createJwtAuthHeader(Set.of(TestRoles.SUPERADMIN));
+
+ /* execute & test */
+ /* @formatter:off */
+ mockMvc
+ .perform(MockMvcRequestBuilders.get(API_USER).header(HttpHeaders.AUTHORIZATION, authHeader))
+ .andExpect(status().isOk());
+ /* @formatter:on */
+ }
+
+ @Test
+ void api_user_is_accessible_as_superadmin_owner() throws Exception {
+ /* prepare */
+ String authHeader = TestOAuth2JwtSecurityConfiguration.createJwtAuthHeader(Set.of(TestRoles.SUPERADMIN, TestRoles.OWNER));
+
+ /* execute & test */
+ /* @formatter:off */
+ mockMvc
+ .perform(MockMvcRequestBuilders.get(API_USER).header(HttpHeaders.AUTHORIZATION, authHeader))
+ .andExpect(status().isOk());
+ /* @formatter:on */
+ }
+
+ @Test
+ void api_user_is_not_accessible_as_owner() throws Exception {
+ /* prepare */
+ String authHeader = TestOAuth2JwtSecurityConfiguration.createJwtAuthHeader(Set.of(TestRoles.OWNER));
+
+ /* execute & test */
+ /* @formatter:off */
+ mockMvc
+ .perform(MockMvcRequestBuilders.get(API_USER).header(HttpHeaders.AUTHORIZATION, authHeader))
+ .andExpect(status().isForbidden());
+ /* @formatter:on */
+ }
+
+ @Test
+ void api_user_is_accessible_as_user() throws Exception {
+ /* prepare */
+ String authHeader = TestOAuth2JwtSecurityConfiguration.createJwtAuthHeader(Set.of(TestRoles.USER));
+
+ /* execute & test */
+ /* @formatter:off */
+ mockMvc
+ .perform(MockMvcRequestBuilders.get(API_USER).header(HttpHeaders.AUTHORIZATION, authHeader))
+ .andExpect(status().isOk());
+ /* @formatter:on */
+ }
+
+ @Configuration
+ @Import({ TestSecurityConfiguration.class, TestOAuth2JwtSecurityConfiguration.class, OAuth2JwtPropertiesConfiguration.class })
+ static class TestConfig {
+
+ @Bean
+ TestSecurityController testSecurityController() {
+ return new TestSecurityController();
+ }
+ }
+}
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtPropertiesTest.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtPropertiesTest.java
new file mode 100644
index 0000000000..4c2dacf1f2
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtPropertiesTest.java
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.spring.security;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.TestPropertySource;
+
+import com.mercedesbenz.sechub.testframework.spring.YamlPropertyLoaderFactory;
+
+@SpringBootTest
+@ActiveProfiles("oauth2")
+@TestPropertySource(locations = "classpath:application-jwt-test.yml", factory = YamlPropertyLoaderFactory.class)
+class OAuth2JwtPropertiesTest {
+
+ private final OAuth2JwtProperties oAuth2JwtProperties;
+
+ OAuth2JwtPropertiesTest(@Autowired OAuth2JwtProperties oAuth2JwtProperties) {
+ this.oAuth2JwtProperties = oAuth2JwtProperties;
+ }
+
+ @Test
+ void construct_properties_with_jwt_enabled_succeeds() {
+ assertThat(oAuth2JwtProperties.getJwkSetUri()).isEqualTo("https://example.org/jwk-set-uri");
+ }
+
+ /* @formatter:off */
+ @Test
+ void construct_properties_with_jwk_set_uri_null_fails() {
+ assertThatThrownBy(() -> new OAuth2JwtProperties(null))
+ .isInstanceOf(NullPointerException.class)
+ .hasMessageContaining("Property 'sechub.security.oauth2.jwt.jwk-set-uri' must not be null");
+ }
+ /* @formatter:on */
+
+ @Configuration
+ @Import({ OAuth2JwtPropertiesConfiguration.class, OAuth2OpaqueTokenPropertiesConfiguration.class })
+ static class TestConfig {
+ }
+}
\ No newline at end of file
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenIntegrationTest.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenIntegrationTest.java
new file mode 100644
index 0000000000..313cafbfb8
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenIntegrationTest.java
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.spring.security;
+
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.http.HttpHeaders;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+
+import com.mercedesbenz.sechub.testframework.spring.YamlPropertyLoaderFactory;
+
+/**
+ * This test class verifies the integration of Spring Security OAuth2 components
+ * in opaque token mode.
+ *
+ *
+ * Unlike {@link SecurityConfigurationTest}, which primarily tests if a endpoint
+ * is secured on an abstract level, this class exercises the full OAuth2 flow
+ * with real OAuth2 mechanisms. We do that by relying on the
+ * {@link AbstractSecurityConfiguration}.
+ *
+ *
+ *
+ * In a typical setup, the
+ * {@link org.springframework.security.oauth2.jwt.JwtDecoder} decodes JWT tokens
+ * by integrating with a identity provider. With this configuration, however, we
+ * mock the identity provider to avoid external dependencies. Additionally, we
+ * mock the user's roles, which are otherwise fetched from the database.
+ *
+ *
+ *
+ * Note: This test class is not intended for verifying whether security
+ * is enabled on specific endpoints. For that, use
+ * {@link SecurityConfigurationTest}.
+ *
+ *
+ * @see AbstractSecurityConfiguration
+ * @see com.mercedesbenz.sechub.domain.authorization.AuthUserDetailsService
+ * @see OAuth2JwtAuthenticationProvider
+ * @see org.springframework.security.oauth2.jwt.JwtDecoder
+ * @see SecurityConfigurationTest
+ *
+ * @author hamidonos
+ */
+@SuppressWarnings("JavadocReference")
+@WebMvcTest
+@TestPropertySource(locations = "classpath:application-opaque-token-test.yml", factory = YamlPropertyLoaderFactory.class)
+@ActiveProfiles("oauth2")
+class OAuth2OpaqueTokenIntegrationTest {
+
+ /**
+ * For this test we call the API endpoint /api/user. It is just a mock endpoint
+ * to test the OAuth2 integration. It could also be any other endpoint.
+ */
+ private static final String API_USER = "/api/user";
+
+ private final MockMvc mockMvc;
+
+ @Autowired
+ OAuth2OpaqueTokenIntegrationTest(MockMvc mockMvc) {
+ this.mockMvc = mockMvc;
+ }
+
+ @Test
+ void api_user_is_not_accessible_anonymously() throws Exception {
+ /* execute & test */
+ /* @formatter:off */
+ mockMvc
+ .perform(MockMvcRequestBuilders.get(API_USER))
+ .andExpect(status().isUnauthorized());
+ /* @formatter:on */
+ }
+
+ @Test
+ void api_user_is_accessible_as_superadmin() throws Exception {
+ /* prepare */
+ String authHeader = TestOAuth2OpaqueTokenSecurityConfiguration.createOpaqueTokenHeader(Set.of(TestRoles.SUPERADMIN));
+
+ /* execute & test */
+ /* @formatter:off */
+ mockMvc
+ .perform(MockMvcRequestBuilders.get(API_USER).header(HttpHeaders.AUTHORIZATION, authHeader))
+ .andExpect(status().isOk());
+ /* @formatter:on */
+ }
+
+ @Test
+ void api_user_is_accessible_as_superadmin_owner() throws Exception {
+ /* prepare */
+ String authHeader = TestOAuth2OpaqueTokenSecurityConfiguration.createOpaqueTokenHeader(Set.of(TestRoles.SUPERADMIN, TestRoles.OWNER));
+
+ /* execute & test */
+ /* @formatter:off */
+ mockMvc
+ .perform(MockMvcRequestBuilders.get(API_USER).header(HttpHeaders.AUTHORIZATION, authHeader))
+ .andExpect(status().isOk());
+ /* @formatter:on */
+ }
+
+ @Test
+ void api_user_is_not_accessible_as_owner() throws Exception {
+ /* prepare */
+ String authHeader = TestOAuth2OpaqueTokenSecurityConfiguration.createOpaqueTokenHeader(Set.of(TestRoles.OWNER));
+
+ /* execute & test */
+ /* @formatter:off */
+ mockMvc
+ .perform(MockMvcRequestBuilders.get(API_USER).header(HttpHeaders.AUTHORIZATION, authHeader))
+ .andExpect(status().isForbidden());
+ /* @formatter:on */
+ }
+
+ @Test
+ void api_user_is_accessible_as_user() throws Exception {
+ /* prepare */
+ String authHeader = TestOAuth2OpaqueTokenSecurityConfiguration.createOpaqueTokenHeader(Set.of(TestRoles.USER));
+
+ /* execute & test */
+ /* @formatter:off */
+ mockMvc
+ .perform(MockMvcRequestBuilders.get(API_USER).header(HttpHeaders.AUTHORIZATION, authHeader))
+ .andExpect(status().isOk());
+ /* @formatter:on */
+ }
+
+ @Configuration
+ @Import({ TestSecurityConfiguration.class, TestOAuth2OpaqueTokenSecurityConfiguration.class, OAuth2OpaqueTokenPropertiesConfiguration.class })
+ static class TestConfig {
+
+ @Bean
+ TestSecurityController testSecurityController() {
+ return new TestSecurityController();
+ }
+ }
+}
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenPropertiesTest.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenPropertiesTest.java
new file mode 100644
index 0000000000..08995e6aa0
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenPropertiesTest.java
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.spring.security;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junit.jupiter.params.provider.ArgumentsSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.TestPropertySource;
+
+import com.mercedesbenz.sechub.testframework.spring.YamlPropertyLoaderFactory;
+
+@SpringBootTest
+@ActiveProfiles("oauth2")
+@TestPropertySource(locations = "classpath:application-opaque-token-test.yml", factory = YamlPropertyLoaderFactory.class)
+class OAuth2OpaqueTokenPropertiesTest {
+
+ private final OAuth2OpaqueTokenProperties oAuth2OpaqueTokenProperties;
+
+ OAuth2OpaqueTokenPropertiesTest(@Autowired OAuth2OpaqueTokenProperties oAuth2OpaqueTokenProperties) {
+ this.oAuth2OpaqueTokenProperties = oAuth2OpaqueTokenProperties;
+ }
+
+ @Test
+ void construct_properties_with_opaque_token_enabled_succeeds() {
+ assertThat(oAuth2OpaqueTokenProperties.getIntrospectionUri()).isEqualTo("https://example.org/introspection-uri");
+ assertThat(oAuth2OpaqueTokenProperties.getClientId()).isEqualTo("example-client-id");
+ assertThat(oAuth2OpaqueTokenProperties.getClientSecret()).isEqualTo("example-client-secret");
+ }
+
+ /* @formatter:off */
+ @ParameterizedTest
+ @ArgumentsSource(InvalidOAuth2OpaqueTokenPropertiesProvider.class)
+ void construct_properties_with_null_arguments_fails(String introspectionUri,
+ String clientId,
+ String clientSecret,
+ String errMsg) {
+ assertThatThrownBy(() -> new OAuth2OpaqueTokenProperties(introspectionUri, clientId, clientSecret))
+ .isInstanceOf(NullPointerException.class)
+ .hasMessageContaining(errMsg);
+ }
+ /* @formatter:on */
+
+ @Configuration
+ @Import({ OAuth2OpaqueTokenPropertiesConfiguration.class, OAuth2JwtPropertiesConfiguration.class })
+ static class TestConfig {
+ }
+
+ private static class InvalidOAuth2OpaqueTokenPropertiesProvider implements ArgumentsProvider {
+ @Override
+ public Stream extends Arguments> provideArguments(ExtensionContext extensionContext) {
+ /* @formatter:off */
+ return Stream.of(
+ Arguments.of(null, "example-client-id", "example-client-secret", "Property 'sechub.security.oauth2.opaque-token.introspection-uri' must not be null"),
+ Arguments.of("https://example.org/introspection-uri", null, "example-client-secret", "Property 'sechub.security.oauth2.opaque-token.client-id' must not be null"),
+ Arguments.of("https://example.org/introspection-uri", "example-client-id", null, "Property 'sechub.security.oauth2.opaque-token.client-secret' must not be null")
+ );
+ /* @formatter:on */
+ }
+ }
+}
\ No newline at end of file
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OpaqueTokenResponseTest.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OpaqueTokenResponseTest.java
new file mode 100644
index 0000000000..1b9e5935eb
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OpaqueTokenResponseTest.java
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.spring.security;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.time.Instant;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junit.jupiter.params.provider.ArgumentsSource;
+import org.opentest4j.TestAbortedException;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.exc.ValueInstantiationException;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.jayway.jsonpath.JsonPath;
+
+class OpaqueTokenResponseTest {
+
+ private static final String opaqueTokenResponseJson;
+ private static final ObjectMapper objectMapper = new ObjectMapper();
+
+ static {
+ try {
+ opaqueTokenResponseJson = Files.readString(Paths.get("src/test/resources/opaque-token-response.json"));
+ } catch (IOException e) {
+ throw new TestAbortedException("Failed to prepare test", e);
+ }
+ }
+
+ @Test
+ void construct_opaque_token_response_from_valid_json_is_successful() throws JsonProcessingException {
+ /* prepare */
+ Instant epoch = Instant.EPOCH;
+
+ /* execute */
+ OpaqueTokenResponse opaqueTokenResponse = objectMapper.readValue(opaqueTokenResponseJson, OpaqueTokenResponse.class);
+
+ // assert
+ assertThat(opaqueTokenResponse).isNotNull();
+ assertThat(opaqueTokenResponse.isActive()).isEqualTo(JsonPath.read(opaqueTokenResponseJson, "$.active"));
+ assertThat(opaqueTokenResponse.getScope()).isEqualTo(JsonPath.read(opaqueTokenResponseJson, "$.scope"));
+ assertThat(opaqueTokenResponse.getClientId()).isEqualTo(JsonPath.read(opaqueTokenResponseJson, "$.client_id"));
+ assertThat(opaqueTokenResponse.getClientType()).isEqualTo(JsonPath.read(opaqueTokenResponseJson, "$.client_type"));
+ assertThat(opaqueTokenResponse.getUsername()).isEqualTo(JsonPath.read(opaqueTokenResponseJson, "$.username"));
+ assertThat(opaqueTokenResponse.getTokenType()).isEqualTo(JsonPath.read(opaqueTokenResponseJson, "$.token_type"));
+ assertThat(opaqueTokenResponse.getExpiresAt()).isAfter(epoch);
+ assertThat(opaqueTokenResponse.getSubject()).isEqualTo(JsonPath.read(opaqueTokenResponseJson, "$.sub"));
+ assertThat(opaqueTokenResponse.getAudience()).isEqualTo(JsonPath.read(opaqueTokenResponseJson, "$.aud"));
+ assertThat(opaqueTokenResponse.getGroupType()).isEqualTo(JsonPath.read(opaqueTokenResponseJson, "$.group_type"));
+ }
+
+ @ParameterizedTest
+ @ArgumentsSource(ValidOpaqueTokenResponseProvider.class)
+ void construct_opaque_token_response_from_valid_json_with_nullable_properties_is_successful(String validOpaqueTokenResponseJson) {
+ /* execute & test */
+ assertDoesNotThrow(() -> objectMapper.readValue(validOpaqueTokenResponseJson, OpaqueTokenResponse.class));
+ }
+
+ @ParameterizedTest
+ @ArgumentsSource(InvalidOpaqueTokenResponseProvider.class)
+ void construct_opaque_token_response_from_invalid_json_fails(String invalidOpaqueTokenResponseJson, String errMsg) throws JsonProcessingException {
+ /* execute & test */
+
+ /* @formatter:off */
+ assertThatThrownBy(() -> objectMapper.readValue(invalidOpaqueTokenResponseJson, OpaqueTokenResponse.class))
+ .isInstanceOf(ValueInstantiationException.class)
+ .hasMessageContaining(errMsg);
+ /* @formatter:on */
+ }
+
+ private static String removeJsonKeyAndValue(String key) throws JsonProcessingException {
+ ObjectMapper objectMapper = new ObjectMapper();
+ JsonNode rootNode = objectMapper.readTree(opaqueTokenResponseJson);
+
+ if (rootNode instanceof ObjectNode) {
+ ((ObjectNode) rootNode).remove(key);
+ } else {
+ throw new IllegalArgumentException("Invalid JSON");
+ }
+
+ return objectMapper.writeValueAsString(rootNode);
+ }
+
+ /* @formatter:off */
+ private static class ValidOpaqueTokenResponseProvider implements ArgumentsProvider {
+ @Override
+ public Stream extends Arguments> provideArguments(ExtensionContext extensionContext) throws Exception {
+ return Stream.of(
+ Arguments.of(removeJsonKeyAndValue("scope")),
+ Arguments.of(removeJsonKeyAndValue("client_id")),
+ Arguments.of(removeJsonKeyAndValue("client_type")),
+ Arguments.of(removeJsonKeyAndValue("username")),
+ Arguments.of(removeJsonKeyAndValue("token_type")),
+ Arguments.of(removeJsonKeyAndValue("exp")),
+ Arguments.of(removeJsonKeyAndValue("aud")),
+ Arguments.of(removeJsonKeyAndValue("group_type"))
+ );
+ }
+ }
+
+ /* @formatter:off */
+ private static class InvalidOpaqueTokenResponseProvider implements ArgumentsProvider {
+ @Override
+ public Stream extends Arguments> provideArguments(ExtensionContext extensionContext) throws Exception {
+ return Stream.of(
+ Arguments.of(removeJsonKeyAndValue("active"), "Property 'active' must not be null"),
+ Arguments.of(removeJsonKeyAndValue("sub"), "Property 'sub' must not be null")
+ );
+ }
+ }
+ /* @formatter:on */
+}
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/SecurityConfigurationTest.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/SecurityConfigurationTest.java
new file mode 100644
index 0000000000..3e2dccb3f8
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/SecurityConfigurationTest.java
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.spring.security;
+
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+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.request.MockMvcRequestBuilders;
+
+/**
+ * This test class makes sure that the defined API security rules from
+ * {@link AbstractSecurityConfiguration} are working properly.
+ *
+ *
+ * Using {@link WithMockUser} to set up a mocked
+ * {@link org.springframework.security.core.context.SecurityContext}, we can
+ * test how the endpoints behave when accessed by different roles.
+ *
+ *
+ *
+ * Note: Here we don't test the integration of OAuth2 or Basic Auth. For
+ * that, see {@link OAuth2JwtIntegrationTest}. This test class is only concerned
+ * with verifying if the security rules are correctly applied on an abstract
+ * level.
+ *
+ *
+ * @see WithMockUser
+ * @see OAuth2JwtIntegrationTest
+ * @see AbstractSecurityConfiguration
+ *
+ * @author hamidonos
+ */
+@WebMvcTest
+class SecurityConfigurationTest {
+
+ private static final String SUPERADMIN = "SUPERADMIN";
+ private static final String USER = "USER";
+ private static final String OWNER = "OWNER";
+
+ private final MockMvc mockMvc;
+
+ @Autowired
+ SecurityConfigurationTest(MockMvc mockMvc) {
+ this.mockMvc = mockMvc;
+ }
+
+ /* Super Admin */
+
+ @Test
+ @WithMockUser(roles = SUPERADMIN)
+ void api_admin_is_accessible_with_superadmin_role() throws Exception {
+ getAndExpect("/api/admin", HttpStatus.OK);
+ }
+
+ @Test
+ @WithMockUser(roles = SUPERADMIN)
+ void api_user_is_accessible_with_superadmin_role() throws Exception {
+ getAndExpect("/api/user", HttpStatus.OK);
+ }
+
+ @Test
+ @WithMockUser(roles = SUPERADMIN)
+ void api_owner_is_accessible_with_superadmin_role() throws Exception {
+ getAndExpect("/api/owner", HttpStatus.OK);
+ }
+
+ /* User */
+
+ @Test
+ @WithMockUser(roles = USER)
+ void api_admin_is_not_accessible_with_user_role() throws Exception {
+ getAndExpect("/api/admin", HttpStatus.FORBIDDEN);
+ }
+
+ @Test
+ @WithMockUser(roles = USER)
+ void api_user_is_accessible_with_user_role() throws Exception {
+ getAndExpect("/api/user", HttpStatus.OK);
+ }
+
+ @Test
+ @WithMockUser(roles = USER)
+ void api_owner_is_not_accessible_with_user_role() throws Exception {
+ getAndExpect("/api/owner", HttpStatus.FORBIDDEN);
+ }
+
+ /* Owner */
+
+ @Test
+ @WithMockUser(roles = OWNER)
+ void api_admin_is_not_accessible_with_owner_role() throws Exception {
+ getAndExpect("/api/admin", HttpStatus.FORBIDDEN);
+ }
+
+ @Test
+ @WithMockUser(roles = OWNER)
+ void api_user_is_not_accessible_with_owner_role() throws Exception {
+ getAndExpect("/api/user", HttpStatus.FORBIDDEN);
+ }
+
+ @Test
+ @WithMockUser(roles = OWNER)
+ void api_owner_is_accessible_with_owner_role() throws Exception {
+ getAndExpect("/api/owner", HttpStatus.OK);
+ }
+
+ /* Anonymous */
+
+ @Test
+ void api_admin_is_not_accessible_anonymously() throws Exception {
+ getAndExpect("/api/admin", HttpStatus.UNAUTHORIZED);
+ }
+
+ @Test
+ void api_user_is_not_accessible_anonymously() throws Exception {
+ getAndExpect("/api/user", HttpStatus.UNAUTHORIZED);
+ }
+
+ @Test
+ void api_owner_is_not_accessible_anonymously() throws Exception {
+ getAndExpect("/api/owner", HttpStatus.UNAUTHORIZED);
+ }
+
+ @Test
+ void api_anonymous_is_accessible_anonymously() throws Exception {
+ getAndExpect("/api/anonymous", HttpStatus.OK);
+ }
+
+ private void getAndExpect(String path, HttpStatus httpStatus) throws Exception {
+ /* @formatter:off */
+ mockMvc
+ .perform(MockMvcRequestBuilders.get(path))
+ .andExpect(status().is(httpStatus.value()));
+ /* @formatter:on */
+ }
+
+ @Configuration
+ @Import(TestSecurityConfiguration.class)
+ static class TestConfig {
+
+ @Bean
+ TestSecurityController testSecurityController() {
+ return new TestSecurityController();
+ }
+ }
+}
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestOAuth2JwtSecurityConfiguration.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestOAuth2JwtSecurityConfiguration.java
new file mode 100644
index 0000000000..06395eb7a0
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestOAuth2JwtSecurityConfiguration.java
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.spring.security;
+
+import static com.mercedesbenz.sechub.spring.security.TestRoles.OWNER;
+import static com.mercedesbenz.sechub.spring.security.TestRoles.SUPERADMIN;
+import static com.mercedesbenz.sechub.spring.security.TestRoles.USER;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+import org.opentest4j.TestAbortedException;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.security.oauth2.jwt.JwtException;
+
+/**
+ * This configuration class provides the necessary beans to test Springs OAuth2
+ * integration with SecHub components in JWT mode.
+ *
+ * @author hamidonos
+ */
+@Configuration
+public class TestOAuth2JwtSecurityConfiguration {
+
+ public static final String BEARER_PREFIX = OAuth2AccessToken.TokenType.BEARER.getValue() + " ";
+
+ private static final String ADMIN_JWT = "admin-jwt-token";
+ private static final String ADMIN_OWNER_JWT = "admin-owner-jwt-token";
+ private static final String ADMIN_USER_JWT = "admin-user-jwt-token";
+ private static final String OWNER_JWT = "owner-jwt-token";
+ private static final String OWNER_USER_JWT = "owner-user-jwt-token";
+ private static final String USER_JWT = "user-jwt-token";
+
+ private static final String ADMIN_ID = UUID.randomUUID().toString();
+ private static final String ADMIN_OWNER_ID = UUID.randomUUID().toString();
+ private static final String ADMIN_USER_ID = UUID.randomUUID().toString();
+ private static final String OWNER_ID = UUID.randomUUID().toString();
+ private static final String OWNER_USER_ID = UUID.randomUUID().toString();
+ private static final String USER_ID = UUID.randomUUID().toString();
+
+ private static final String ALGORITHM = "alg";
+ private static final String ALGORITHM_NONE = "none";
+
+ /**
+ * This bean provides a {@link JwtDecoder} that decodes the JWT token and
+ * returns a {@link Jwt} object. The behaviour is completely mocked and the
+ * possible JWT tokens are pre-defined. Every possible JWT value is mapped to a
+ * specific subject (or user id). The subject will be returned as part of the
+ * JWT decode process. To keep testing as simple as possible, we map only ONE
+ * role to ONE user and provide here no combinations.
+ */
+ @Bean
+ JwtDecoder jwtDecoder() {
+ JwtDecoder jwtDecoder = mock();
+ when(jwtDecoder.decode(anyString())).thenAnswer(invocation -> {
+ String jwtTokenValue = invocation.getArgument(0);
+ Jwt.Builder builder = Jwt.withTokenValue(jwtTokenValue).header(ALGORITHM, ALGORITHM_NONE);
+ if (ADMIN_JWT.equals(jwtTokenValue)) {
+ return builder.subject(ADMIN_ID).build();
+ }
+ if (ADMIN_OWNER_JWT.equals(jwtTokenValue)) {
+ return builder.subject(ADMIN_OWNER_ID).build();
+ }
+ if (ADMIN_USER_JWT.equals(jwtTokenValue)) {
+ return builder.subject(ADMIN_USER_ID).build();
+ }
+ if (OWNER_JWT.equals(jwtTokenValue)) {
+ return builder.subject(OWNER_ID).build();
+ }
+ if (OWNER_USER_JWT.equals(jwtTokenValue)) {
+ return builder.subject(OWNER_USER_ID).build();
+ }
+ if (USER_JWT.equals(jwtTokenValue)) {
+ return builder.subject(USER_ID).build();
+ }
+
+ throw new JwtException("Invalid JWT token");
+ });
+ return jwtDecoder;
+ }
+
+ /**
+ * Here we mock the {@link UserDetailsService} to return a
+ * {@link TestUserDetails} object based on the user id (or subject). The subject
+ * is determined by the {@link TestOAuth2JwtSecurityConfiguration#jwtDecoder()}
+ * bean. Depending on the user id, the {@link TestUserDetails} object will
+ * contain the corresponding authorities.
+ */
+ @Bean
+ UserDetailsService userDetailsService() {
+ UserDetailsService userDetailsService = mock();
+ when(userDetailsService.loadUserByUsername(anyString())).thenAnswer(invocation -> {
+ String username = invocation.getArgument(0);
+ if (!Set.of(ADMIN_ID, ADMIN_OWNER_ID, ADMIN_USER_ID, OWNER_ID, OWNER_USER_ID, USER_ID).contains(username)) {
+ throw new UsernameNotFoundException("User %s not found".formatted(username));
+ }
+
+ Collection authorities = new HashSet<>();
+
+ if (ADMIN_ID.equals(username)) {
+ authorities.add(new SimpleGrantedAuthority(SUPERADMIN));
+ }
+ if (ADMIN_OWNER_ID.equals(username)) {
+ authorities.add(new SimpleGrantedAuthority(SUPERADMIN));
+ authorities.add(new SimpleGrantedAuthority(OWNER));
+ }
+ if (ADMIN_USER_ID.equals(username)) {
+ authorities.add(new SimpleGrantedAuthority(SUPERADMIN));
+ authorities.add(new SimpleGrantedAuthority(USER));
+ }
+ if (OWNER_ID.equals(username)) {
+ authorities.add(new SimpleGrantedAuthority(OWNER));
+ }
+ if (OWNER_USER_ID.equals(username)) {
+ authorities.add(new SimpleGrantedAuthority(OWNER));
+ authorities.add(new SimpleGrantedAuthority(USER));
+ }
+ if (USER_ID.equals(username)) {
+ authorities.add(new SimpleGrantedAuthority(USER));
+ }
+
+ return new TestUserDetails(authorities, username);
+ });
+
+ return userDetailsService;
+ }
+
+ public static String createJwtAuthHeader(Set roles) {
+ if (roles.isEmpty()) {
+ throw new TestAbortedException("Roles cannot be empty");
+ }
+
+ if (roles.equals(Set.of(SUPERADMIN))) {
+ return BEARER_PREFIX + ADMIN_JWT;
+ }
+
+ if (roles.equals(Set.of(SUPERADMIN, OWNER))) {
+ return BEARER_PREFIX + ADMIN_OWNER_JWT;
+ }
+
+ if (roles.equals(Set.of(SUPERADMIN, USER))) {
+ return BEARER_PREFIX + ADMIN_USER_JWT;
+ }
+
+ if (roles.equals(Set.of(OWNER))) {
+ return BEARER_PREFIX + OWNER_JWT;
+ }
+
+ if (roles.equals(Set.of(OWNER, USER))) {
+ return BEARER_PREFIX + OWNER_USER_JWT;
+ }
+
+ if (roles.equals(Set.of(USER))) {
+ return BEARER_PREFIX + USER_JWT;
+ }
+
+ throw new TestAbortedException("Invalid roles");
+ }
+
+}
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestOAuth2OpaqueTokenSecurityConfiguration.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestOAuth2OpaqueTokenSecurityConfiguration.java
new file mode 100644
index 0000000000..ddfd8749a7
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestOAuth2OpaqueTokenSecurityConfiguration.java
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.spring.security;
+
+import static com.mercedesbenz.sechub.spring.security.TestRoles.OWNER;
+import static com.mercedesbenz.sechub.spring.security.TestRoles.SUPERADMIN;
+import static com.mercedesbenz.sechub.spring.security.TestRoles.USER;
+import static java.util.Objects.requireNonNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.time.Instant;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+import org.opentest4j.TestAbortedException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpEntity;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * This configuration class provides the necessary beans to test Springs OAuth2
+ * integration with SecHub components in opaque token mode.
+ *
+ * @author hamidonos
+ */
+@Configuration
+public class TestOAuth2OpaqueTokenSecurityConfiguration {
+
+ public static final String BEARER_PREFIX = OAuth2AccessToken.TokenType.BEARER.getValue() + " ";
+
+ private static final String TOKEN = "token";
+ private static final String ADMIN_OPAQUE_TOKEN = "admin-opaque-token";
+ private static final String ADMIN_OWNER_OPAQUE_TOKEN = "admin-owner-opaque-token";
+ private static final String ADMIN_USER_OPAQUE_TOKEN = "admin-user-opaque-token";
+ private static final String OWNER_OPAQUE_TOKEN = "owner-opaque-token";
+ private static final String OWNER_USER_OPAQUE_TOKEN = "owner-user-opaque-token";
+ private static final String USER_OPAQUE_TOKEN = "user-opaque-token";
+
+ private static final String ADMIN_ID = UUID.randomUUID().toString();
+ private static final String ADMIN_OWNER_ID = UUID.randomUUID().toString();
+ private static final String ADMIN_USER_ID = UUID.randomUUID().toString();
+ private static final String OWNER_ID = UUID.randomUUID().toString();
+ private static final String OWNER_USER_ID = UUID.randomUUID().toString();
+ private static final String USER_ID = UUID.randomUUID().toString();
+
+ /* @formatter:off */
+ @Autowired
+ TestOAuth2OpaqueTokenSecurityConfiguration(RestTemplate restTemplate,
+ OAuth2OpaqueTokenProperties oAuth2OpaqueTokenProperties) {
+
+ when(restTemplate.postForObject(eq(oAuth2OpaqueTokenProperties.getIntrospectionUri()), any(), eq(OpaqueTokenResponse.class))).thenAnswer(invocation -> {
+ HttpEntity> request = invocation.getArgument(1);
+ String token = requireNonNull(request.getBody()).getFirst(TOKEN);
+ boolean isActive = false;
+ String subject = "";
+
+ if (ADMIN_OPAQUE_TOKEN.equals(token)) {
+ isActive = true;
+ subject = ADMIN_ID;
+ }
+ if (ADMIN_OWNER_OPAQUE_TOKEN.equals(token)) {
+ isActive = true;
+ subject = ADMIN_OWNER_ID;
+ }
+ if (ADMIN_USER_OPAQUE_TOKEN.equals(token)) {
+ isActive = true;
+ subject = ADMIN_USER_ID;
+ }
+ if (OWNER_OPAQUE_TOKEN.equals(token)) {
+ isActive = true;
+ subject = OWNER_ID;
+ }
+ if (OWNER_USER_OPAQUE_TOKEN.equals(token)) {
+ isActive = true;
+ subject = OWNER_USER_ID;
+ }
+ if (USER_OPAQUE_TOKEN.equals(token)) {
+ isActive = true;
+ subject = USER_ID;
+ }
+
+ return new OpaqueTokenResponse(isActive,
+ "scope",
+ "client-id",
+ "client-type",
+ subject,
+ "token-type",
+ Instant.now().plusSeconds(60L).getEpochSecond(),
+ subject,
+ "aud",
+ "group-type");
+ });
+ }
+ /* @formatter:on */
+
+ /**
+ * Here we mock the {@link UserDetailsService} to return a
+ * {@link TestUserDetails} object based on the user id (or subject). The subject
+ * is determined by the {@link Base64OAuth2OpaqueTokenIntrospector} component.
+ * Depending on the user id, the {@link TestUserDetails} object will contain the
+ * corresponding authorities.
+ */
+ @Bean
+ UserDetailsService userDetailsService() {
+ UserDetailsService userDetailsService = mock();
+ when(userDetailsService.loadUserByUsername(anyString())).thenAnswer(invocation -> {
+ String username = invocation.getArgument(0);
+ if (!Set.of(ADMIN_ID, ADMIN_OWNER_ID, ADMIN_USER_ID, OWNER_ID, OWNER_USER_ID, USER_ID).contains(username)) {
+ throw new UsernameNotFoundException("User %s not found".formatted(username));
+ }
+
+ Collection authorities = new HashSet<>();
+
+ if (ADMIN_ID.equals(username)) {
+ authorities.add(new SimpleGrantedAuthority(SUPERADMIN));
+ }
+ if (ADMIN_OWNER_ID.equals(username)) {
+ authorities.add(new SimpleGrantedAuthority(SUPERADMIN));
+ authorities.add(new SimpleGrantedAuthority(OWNER));
+ }
+ if (ADMIN_USER_ID.equals(username)) {
+ authorities.add(new SimpleGrantedAuthority(SUPERADMIN));
+ authorities.add(new SimpleGrantedAuthority(USER));
+ }
+ if (OWNER_ID.equals(username)) {
+ authorities.add(new SimpleGrantedAuthority(OWNER));
+ }
+ if (OWNER_USER_ID.equals(username)) {
+ authorities.add(new SimpleGrantedAuthority(OWNER));
+ authorities.add(new SimpleGrantedAuthority(USER));
+ }
+ if (USER_ID.equals(username)) {
+ authorities.add(new SimpleGrantedAuthority(USER));
+ }
+
+ return new TestUserDetails(authorities, username);
+ });
+
+ return userDetailsService;
+ }
+
+ public static String createOpaqueTokenHeader(Set roles) {
+ if (roles.isEmpty()) {
+ throw new TestAbortedException("Roles cannot be empty");
+ }
+
+ if (roles.equals(Set.of(SUPERADMIN))) {
+ return BEARER_PREFIX + ADMIN_OPAQUE_TOKEN;
+ }
+
+ if (roles.equals(Set.of(SUPERADMIN, OWNER))) {
+ return BEARER_PREFIX + ADMIN_OWNER_OPAQUE_TOKEN;
+ }
+
+ if (roles.equals(Set.of(SUPERADMIN, USER))) {
+ return BEARER_PREFIX + ADMIN_USER_OPAQUE_TOKEN;
+ }
+
+ if (roles.equals(Set.of(OWNER))) {
+ return BEARER_PREFIX + OWNER_OPAQUE_TOKEN;
+ }
+
+ if (roles.equals(Set.of(OWNER, USER))) {
+ return BEARER_PREFIX + OWNER_USER_OPAQUE_TOKEN;
+ }
+
+ if (roles.equals(Set.of(USER))) {
+ return BEARER_PREFIX + USER_OPAQUE_TOKEN;
+ }
+
+ throw new TestAbortedException("Invalid roles");
+ }
+}
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestRoles.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestRoles.java
new file mode 100644
index 0000000000..5d01a7a5d8
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestRoles.java
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.spring.security;
+
+class TestRoles {
+ static final String SUPERADMIN = "ROLE_SUPERADMIN";
+ static final String USER = "ROLE_USER";
+ static final String OWNER = "ROLE_OWNER";
+}
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestSecurityConfiguration.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestSecurityConfiguration.java
new file mode 100644
index 0000000000..0b44ca475e
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestSecurityConfiguration.java
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.spring.security;
+
+import static org.mockito.Mockito.mock;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
+import org.springframework.web.client.RestTemplate;
+
+/* @formatter:off */
+/**
+ * The TestSecurityConfiguration
class extends the {@link AbstractSecurityConfiguration} and provides
+ * security configuration for testing purposes. It defines security rules for different test API paths.
+ *
+ *
+ * The following API paths are configured:
+ *
+ *
+ * /api/admin/**
- Accessible only to users having the SUPERADMIN
authority.
+ * /api/user/**
- Accessible to users having the USER
or SUPERADMIN
authority.
+ * /api/owner/**
- Accessible to users with having the OWNER
or SUPERADMIN
authority.
+ * /api/anonymous/**
- Accessible to all users without authentication.
+ * /
- All other paths are denied access.
+ *
+ *
+ * @see AbstractSecurityConfiguration
+ *
+ * @author hamidonos
+ */
+/* @formatter:on */
+@Configuration
+class TestSecurityConfiguration extends AbstractSecurityConfiguration {
+
+ private final Environment environment;
+
+ TestSecurityConfiguration(Environment environment) {
+ this.environment = environment;
+ }
+
+ @Bean
+ RestTemplate restTemplate() {
+ return mock();
+ }
+
+ @Override
+ protected boolean isOAuth2Enabled() {
+ return environment.matchesProfiles("oauth2");
+ }
+
+ @Override
+ protected Customizer.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequests() {
+ /* @formatter:off */
+ return (auth) -> auth.
+ requestMatchers("/api/admin" + "/**").hasAnyAuthority(TestRoles.SUPERADMIN).
+ requestMatchers("/api/user"+ "/**").hasAnyAuthority(TestRoles.USER, TestRoles.SUPERADMIN).
+ requestMatchers("/api/owner"+ "/**").hasAnyAuthority(TestRoles.OWNER, TestRoles.SUPERADMIN).
+ requestMatchers("/api/anonymous"+ "/**").permitAll().
+ requestMatchers("/**").denyAll();
+ /* @formatter:on */
+ }
+}
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestSecurityController.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestSecurityController.java
new file mode 100644
index 0000000000..64bdc01153
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestSecurityController.java
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.spring.security;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * This controller spins up a mock API for testing the
+ * {@link AbstractSecurityConfiguration} of the SecHub application.
+ *
+ *
+ * Note: The sechub-shared-kernel module is a library that does
+ * not contain the actual implementation of the endpoints, hence a mock
+ * controller is used here.
+ *
+ *
+ * @author hamidonos
+ */
+@RestController
+class TestSecurityController {
+
+ private static final String OK = HttpStatus.OK.getReasonPhrase();
+
+ @GetMapping("/api/admin")
+ String apiAdmin() {
+ return OK;
+ }
+
+ @GetMapping("/api/user")
+ String apiUser() {
+ return OK;
+ }
+
+ @GetMapping("/api/owner")
+ String apiOwner() {
+ return OK;
+ }
+
+ @GetMapping("/api/anonymous")
+ String apiAnonymous() {
+ return OK;
+ }
+
+ @GetMapping("/error")
+ String errorPage() {
+ return OK;
+ }
+
+ @GetMapping("/actuator")
+ String actuator() {
+ return OK;
+ }
+}
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestUserDetails.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestUserDetails.java
new file mode 100644
index 0000000000..cbbae4a901
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestUserDetails.java
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.spring.security;
+
+import java.util.Collection;
+
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+/**
+ * Test implementation of {@link UserDetails} for use in Spring Security related
+ * tests.
+ *
+ *
+ * Defines an authenticated user that expects a username and a collection of
+ * granted authorities in the constructor.
+ *
+ *
+ *
+ * For Basic Auth purposes, the password can be set as well. Otherwise it will
+ * contain a default value.
+ *
+ *
+ * @author hamidonos
+ */
+class TestUserDetails implements UserDetails {
+
+ private static final String DEFAULT_PASSWORD = "";
+ private static final boolean DEFAULT_IS_ACCOUNT_NON_EXPIRED = true;
+ private static final boolean DEFAULT_IS_ACCOUNT_NON_LOCKED = true;
+ private static final boolean DEFAULT_IS_CREDENTIALS_NON_EXPIRED = true;
+ private static final boolean DEFAULT_IS_ENABLED = true;
+
+ private final Collection extends GrantedAuthority> authorities;
+ private final String username;
+ private final String password;
+
+ TestUserDetails(Collection extends GrantedAuthority> authorities, String username) {
+ this.authorities = authorities;
+ this.username = username;
+ this.password = DEFAULT_PASSWORD;
+ }
+
+ TestUserDetails(Collection extends GrantedAuthority> authorities, String username, String password) {
+ this.authorities = authorities;
+ this.username = username;
+ this.password = password;
+ }
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+ return authorities;
+ }
+
+ @Override
+ public String getPassword() {
+ return password;
+ }
+
+ @Override
+ public String getUsername() {
+ return username;
+ }
+
+ @Override
+ public boolean isAccountNonExpired() {
+ return DEFAULT_IS_ACCOUNT_NON_EXPIRED;
+ }
+
+ @Override
+ public boolean isAccountNonLocked() {
+ return DEFAULT_IS_ACCOUNT_NON_LOCKED;
+ }
+
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return DEFAULT_IS_CREDENTIALS_NON_EXPIRED;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return DEFAULT_IS_ENABLED;
+ }
+}
diff --git a/sechub-commons-security-spring/src/test/resources/application-jwt-test.yml b/sechub-commons-security-spring/src/test/resources/application-jwt-test.yml
new file mode 100644
index 0000000000..d8590ccf3c
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/resources/application-jwt-test.yml
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: MIT
+
+sechub:
+ security:
+ oauth2:
+ mode: JWT
+ jwt:
+ jwk-set-uri: https://example.org/jwk-set-uri
diff --git a/sechub-commons-security-spring/src/test/resources/application-opaque-token-test.yml b/sechub-commons-security-spring/src/test/resources/application-opaque-token-test.yml
new file mode 100644
index 0000000000..fcaf903119
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/resources/application-opaque-token-test.yml
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: MIT
+
+sechub:
+ security:
+ oauth2:
+ mode: OPAQUE_TOKEN
+ opaque-token:
+ introspection-uri: https://example.org/introspection-uri
+ client-id: example-client-id
+ client-secret: example-client-secret
diff --git a/sechub-commons-security-spring/src/test/resources/opaque-token-response.json b/sechub-commons-security-spring/src/test/resources/opaque-token-response.json
new file mode 100644
index 0000000000..98890e105e
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/resources/opaque-token-response.json
@@ -0,0 +1,12 @@
+{
+ "active": true,
+ "scope": "scope",
+ "client_id": "client_id",
+ "client_type": "client_type",
+ "username": "username",
+ "token_type": "token_type",
+ "exp": 1,
+ "sub": "sub",
+ "aud": "aud",
+ "group_type": "group_type"
+}
\ No newline at end of file
diff --git a/sechub-developertools/build.gradle b/sechub-developertools/build.gradle
index fbba4299a1..b7e3887037 100644
--- a/sechub-developertools/build.gradle
+++ b/sechub-developertools/build.gradle
@@ -44,12 +44,13 @@ task buildDeveloperAdminUI(type: Jar, dependsOn: build) {
group 'sechub'
description 'Builds the SecHub Developer Admin tool as standalone executable jar. Use launch-developer-admin-ui script to execute'
archiveBaseName = 'sechub-developer-admin-ui'
-
+ /* TODO: This is a 'dirty' fix for the standard archive entries limit of 64K. We should refactor this module to make it leaner */
+ zip64 = true
+
manifest {
attributes 'Main-Class': 'com.mercedesbenz.sechub.developertools.admin.ui.DeveloperAdministrationUI'
}
-
from {
configurations.runtimeClasspath.collect {
it.isDirectory() ? it : zipTree(it)
diff --git a/sechub-developertools/scripts/sdc.sh b/sechub-developertools/scripts/sdc.sh
index fdbfe820a2..003ca50a2d 100755
--- a/sechub-developertools/scripts/sdc.sh
+++ b/sechub-developertools/scripts/sdc.sh
@@ -314,15 +314,7 @@ fi
if [[ "$FORMAT_CODE_ALL" = "YES" ]]; then
startJob "Format all sourcecode"
- openApiFilePath="$SECHUB_ROOT_DIR/sechub-doc/build/api-spec/openapi3.json"
- if [ -f "$openApiFilePath" ]; then
- echo ">>> Open API file exists"
- else
- echo ">>> Open API file DOES NOT exist - must be generated."
- # Problem detected: open api file must be generated to avoid problems with gradle configuration lifecycle for open api generator!
- ./gradlew generateOpenapi
- fi
- ./gradlew spotlessApply -Dsechub.build.stage=all
+ ./gradlew spotlessApply
fi
diff --git a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/DeveloperAdministration.java b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/DeveloperAdministration.java
index 7e0304fcc5..5afb595157 100644
--- a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/DeveloperAdministration.java
+++ b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/DeveloperAdministration.java
@@ -22,9 +22,11 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.mercedesbenz.sechub.commons.model.JSONConverter;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition;
import com.mercedesbenz.sechub.commons.pds.PDSDefaultParameterKeyConstants;
import com.mercedesbenz.sechub.developertools.admin.ui.ConfigurationSetup;
import com.mercedesbenz.sechub.developertools.admin.ui.UIContext;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetDetailData;
import com.mercedesbenz.sechub.domain.scan.product.pds.PDSProductExecutorKeyConstants;
import com.mercedesbenz.sechub.domain.scan.product.pds.SecHubProductExecutionPDSKeyProvider;
import com.mercedesbenz.sechub.integrationtest.api.AsPDSUser;
@@ -811,4 +813,49 @@ public SecHubEncryptionStatus fetchEncryptionStatus() {
return asTestUser().fetchEncryptionStatus();
}
+ public TemplateDefinition fetchTemplateOrNull(String templateId) {
+ return asTestUser().fetchTemplateDefinitionOrNull(templateId);
+
+ }
+
+ public void createOrUpdateTemplate(String templateId, TemplateDefinition templateDefinition) {
+ asTestUser().createOrUpdateTemplate(templateId, templateDefinition);
+ }
+
+ public void assignTemplateToProject(String templateId, String projectId) {
+ asTestUser().assignTemplateToProject(templateId, new FixedTestProject(projectId));
+ }
+
+ public void unassignTemplateFromProject(String templateId, String projectId) {
+ asTestUser().unassignTemplateFromProject(templateId, new FixedTestProject(projectId));
+ }
+
+ public List fetchAllTemplateIdentifiers() {
+ return asTestUser().fetchTemplateList();
+ }
+
+ public List fetchAllAssetIdentifiers() {
+ return asTestUser().fetchAllAssetIds();
+ }
+
+ public void uploadAssetFile(String assetId, File file) {
+ asTestUser().uploadAssetFile(assetId, file);
+ }
+
+ public AssetDetailData fetchAssetDetails(String assetId) {
+ return asTestUser().fetchAssetDetails(assetId);
+ }
+
+ public void deleteAsset(String assetId) {
+ asTestUser().deleteAsset(assetId);
+ }
+
+ public void deleteAssetFile(String assetId, String fileName) {
+ asTestUser().deleteAssetFile(assetId, fileName);
+ }
+
+ public File downloadAssetFile(String assetId, String fileName) {
+ return asTestUser().downloadAssetFile(assetId, fileName);
+ }
+
}
diff --git a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/CommandUI.java b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/CommandUI.java
index 11a85dc139..cdbefba643 100644
--- a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/CommandUI.java
+++ b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/CommandUI.java
@@ -20,6 +20,7 @@
import com.mercedesbenz.sechub.developertools.admin.ui.action.ActionSupport;
import com.mercedesbenz.sechub.developertools.admin.ui.action.adapter.ShowProductExecutorTemplatesDialogAction;
+import com.mercedesbenz.sechub.developertools.admin.ui.action.asset.ManageAssetsAction;
import com.mercedesbenz.sechub.developertools.admin.ui.action.client.TriggerSecHubClientSynchronousScanAction;
import com.mercedesbenz.sechub.developertools.admin.ui.action.config.ConfigureAutoCleanupAction;
import com.mercedesbenz.sechub.developertools.admin.ui.action.config.ConfigurePDSAutoCleanupAction;
@@ -96,6 +97,10 @@
import com.mercedesbenz.sechub.developertools.admin.ui.action.scheduler.RefreshSchedulerStatusAction;
import com.mercedesbenz.sechub.developertools.admin.ui.action.status.CheckStatusAction;
import com.mercedesbenz.sechub.developertools.admin.ui.action.status.ListStatusEntriesAction;
+import com.mercedesbenz.sechub.developertools.admin.ui.action.template.AssignTemplateToProjectAction;
+import com.mercedesbenz.sechub.developertools.admin.ui.action.template.CreateOrUpdateTemplateAction;
+import com.mercedesbenz.sechub.developertools.admin.ui.action.template.FetchAllTemplateIdentifiersAction;
+import com.mercedesbenz.sechub.developertools.admin.ui.action.template.UnassignTemplateFromProjectAction;
import com.mercedesbenz.sechub.developertools.admin.ui.action.user.AcceptUserSignupAction;
import com.mercedesbenz.sechub.developertools.admin.ui.action.user.AnonymousRequestNewAPITokenUserAction;
import com.mercedesbenz.sechub.developertools.admin.ui.action.user.AnonymousSigninNewUserAction;
@@ -250,6 +255,17 @@ public void createConfigMenu() {
add(mappingsMenu, new UpdateGlobalMappingAction(context));
menu.add(new ConfigureAutoCleanupAction(context));
+
+ JMenu templatesMenu = new JMenu("Templates");
+ menu.add(templatesMenu);
+ add(templatesMenu, new CreateOrUpdateTemplateAction(context));
+ add(templatesMenu, new FetchAllTemplateIdentifiersAction(context));
+
+ add(templatesMenu, new AssignTemplateToProjectAction(context));
+ add(templatesMenu, new UnassignTemplateFromProjectAction(context));
+
+ menu.add(new ManageAssetsAction(context));
+
}
public void createEncryptionMenu() {
diff --git a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/ManageAssetsDialogUI.java b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/ManageAssetsDialogUI.java
new file mode 100644
index 0000000000..24abb248c4
--- /dev/null
+++ b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/ManageAssetsDialogUI.java
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.developertools.admin.ui;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.util.List;
+import java.util.Optional;
+
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTextArea;
+import javax.swing.JToolBar;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+
+import com.mercedesbenz.sechub.developertools.admin.ui.action.AbstractUIAction;
+import com.mercedesbenz.sechub.developertools.admin.ui.cache.InputCacheIdentifier;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetDetailData;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetFileData;
+
+public class ManageAssetsDialogUI {
+
+ private JFrame frame;
+ private UIContext context;
+ private DefaultMutableTreeNode root;
+ private JTree tree;
+ private JTextArea textArea;
+
+ public ManageAssetsDialogUI(UIContext context) {
+ this.context = context;
+
+ frame = new JFrame();
+ frame.setLayout(new BorderLayout());
+
+ createMenuBar(context);
+
+ createToolBar(context);
+
+ frame.setTitle("Manage assets");
+
+ frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+
+ frame.setSize(800, 600);
+ frame.setLocationRelativeTo(null);
+
+ root = new DefaultMutableTreeNode(new AssetRootElement());
+ DefaultTreeModel model = new DefaultTreeModel(root);
+ tree = new JTree(model);
+
+ textArea = new JTextArea();
+ JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, new JScrollPane(tree), new JScrollPane(textArea));
+ frame.add(splitPane, BorderLayout.CENTER);
+
+ tree.addMouseListener(new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
+ if (node == null) {
+ return;
+ }
+
+ Object userObject = node.getUserObject();
+ if (userObject instanceof AssetRootElement) {
+ refreshModel();
+ return;
+ }
+ if (userObject instanceof AssetElement) {
+ node.removeAllChildren();
+
+ AssetElement element = (AssetElement) userObject;
+ String assetId = element.assetId;
+ AssetDetailData detailData = context.getAdministration().fetchAssetDetails(assetId);
+ textArea.setText(detailData.toFormattedJSON());
+ int added = 0;
+ for (AssetFileData info : detailData.getFiles()) {
+ node.add(new DefaultMutableTreeNode(new AssetFileElement(assetId, info.getFileName(), info.getChecksum())));
+ added++;
+ }
+ element.info = added + " files";
+ }
+ tree.repaint();
+ }
+ }
+ });
+ }
+
+ private void createToolBar(UIContext context) {
+ JToolBar toolbar = new JToolBar();
+ toolbar.add(new RefresAction(context));
+ toolbar.addSeparator();
+ toolbar.add(new UploadAssetFileAction(context));
+ toolbar.add(new DownloadAssetFileAction(context));
+ toolbar.addSeparator();
+ toolbar.add(new DeleteAction(context));
+
+ frame.add(toolbar, BorderLayout.NORTH);
+ }
+
+ private void createMenuBar(UIContext context) {
+ JMenuBar menuBar = new JMenuBar();
+ JMenu menu1 = new JMenu("Actions");
+ menuBar.add(menu1);
+ menu1.add(new RefresAction(context));
+ menu1.addSeparator();
+ menu1.add(new UploadAssetFileAction(context));
+ menu1.add(new DownloadAssetFileAction(context));
+ menu1.addSeparator();
+ menu1.add(new DeleteAction(context));
+
+ frame.setJMenuBar(menuBar);
+ }
+
+ public static void main(String[] args) {
+ new ManageAssetsDialogUI(null).show();
+ }
+
+ public void show() {
+ frame.setVisible(true);
+ }
+
+ private void refreshModel() {
+ root.removeAllChildren();
+ List assetIdentifiers = context.getAdministration().fetchAllAssetIdentifiers();
+ textArea.setText("Loaded asset identifiers:\n" + assetIdentifiers);
+ AssetRootElement rootElement = (AssetRootElement) root.getUserObject();
+ rootElement.info = "Loaded: " + assetIdentifiers.size();
+
+ for (String assetId : assetIdentifiers) {
+ root.add(new DefaultMutableTreeNode(new AssetElement(assetId)));
+ }
+ tree.setModel(new DefaultTreeModel(root));
+ }
+
+ private class AssetRootElement {
+ private String info = "Double click to load";
+
+ @Override
+ public String toString() {
+ return "assets (" + info + ")";
+ }
+ }
+
+ private class AssetElement {
+ private String assetId;
+ private String info = "Double click to load";
+
+ private AssetElement(String assetId) {
+ this.assetId = assetId;
+ }
+
+ @Override
+ public String toString() {
+ return assetId + "(" + info + ")";
+ }
+
+ }
+
+ private class AssetFileElement {
+
+ private String checksum;
+ private String fileName;
+ private String assetId;
+
+ public AssetFileElement(String assetId, String fileName, String checksum) {
+ this.assetId = assetId;
+ this.fileName = fileName;
+ this.checksum = checksum;
+ }
+
+ @Override
+ public String toString() {
+ return fileName + " (" + checksum + ")";
+ }
+
+ }
+
+ private class RefresAction extends AbstractUIAction {
+
+ public RefresAction(UIContext context) {
+ super("Refresh", context);
+ }
+
+ private static final long serialVersionUID = 7392018849800602872L;
+
+ @Override
+ protected void execute(ActionEvent e) throws Exception {
+ refreshModel();
+ }
+
+ }
+
+ private class UploadAssetFileAction extends AbstractUIAction {
+
+ public UploadAssetFileAction(UIContext context) {
+ super("Upload", context);
+ }
+
+ private static final long serialVersionUID = 7392018849800602872L;
+
+ @Override
+ protected void execute(ActionEvent e) throws Exception {
+
+ Optional assetIdOpt = getUserInput("Select asset id", InputCacheIdentifier.ASSET_ID);
+ if (assetIdOpt.isEmpty()) {
+ return;
+ }
+ File file = getContext().getDialogUI().selectFile(null);
+ if (file == null) {
+ return;
+ }
+ String assetId = assetIdOpt.get();
+ getContext().getAdministration().uploadAssetFile(assetId, file);
+ }
+
+ }
+
+ private class DownloadAssetFileAction extends AbstractUIAction {
+
+ public DownloadAssetFileAction(UIContext context) {
+ super("Download", context);
+ }
+
+ private static final long serialVersionUID = 7392018849800602872L;
+
+ @Override
+ protected void execute(ActionEvent e) throws Exception {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
+ Object userObject = node.getUserObject();
+
+ if (userObject instanceof AssetFileElement) {
+ AssetFileElement assetElement = (AssetFileElement) userObject;
+ File file = getContext().getAdministration().downloadAssetFile(assetElement.assetId, assetElement.fileName);
+ textArea.setText("Downloaded to:\n" + file.getAbsolutePath());
+ }
+ }
+
+ }
+
+ private class DeleteAction extends AbstractUIAction {
+
+ public DeleteAction(UIContext context) {
+ super("Delete", context);
+ }
+
+ private static final long serialVersionUID = 7392018849800602872L;
+
+ @Override
+ protected void execute(ActionEvent e) throws Exception {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
+ Object userObject = node.getUserObject();
+ if (userObject instanceof AssetElement) {
+ AssetElement assetElement = (AssetElement) userObject;
+ String assetId = assetElement.assetId;
+ if (!getContext().getDialogUI().confirm("Do you really want to delete complete asset:" + assetId)) {
+ return;
+ }
+ getContext().getAdministration().deleteAsset(assetId);
+
+ } else if (userObject instanceof AssetFileElement) {
+ AssetFileElement assetElement = (AssetFileElement) userObject;
+ String assetId = assetElement.assetId;
+ String fileName = assetElement.fileName;
+
+ if (!getContext().getDialogUI().confirm("Do you really want to delete file:" + fileName + " from asset:" + assetId)) {
+ return;
+ }
+ getContext().getAdministration().deleteAssetFile(assetId, fileName);
+ }
+ }
+
+ }
+
+}
diff --git a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/asset/ManageAssetsAction.java b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/asset/ManageAssetsAction.java
new file mode 100644
index 0000000000..0368e97e66
--- /dev/null
+++ b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/asset/ManageAssetsAction.java
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.developertools.admin.ui.action.asset;
+
+import java.awt.event.ActionEvent;
+
+import com.mercedesbenz.sechub.developertools.admin.ui.ManageAssetsDialogUI;
+import com.mercedesbenz.sechub.developertools.admin.ui.UIContext;
+import com.mercedesbenz.sechub.developertools.admin.ui.action.AbstractUIAction;
+
+public class ManageAssetsAction extends AbstractUIAction {
+ private static final long serialVersionUID = 1L;
+
+ public ManageAssetsAction(UIContext context) {
+ super("Manage assets", context);
+ }
+
+ @Override
+ public void execute(ActionEvent e) {
+
+ ManageAssetsDialogUI ui = new ManageAssetsDialogUI(getContext());
+ ui.show();
+
+ }
+
+}
\ No newline at end of file
diff --git a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/template/AssignTemplateToProjectAction.java b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/template/AssignTemplateToProjectAction.java
new file mode 100644
index 0000000000..10b63857a1
--- /dev/null
+++ b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/template/AssignTemplateToProjectAction.java
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.developertools.admin.ui.action.template;
+
+import java.awt.event.ActionEvent;
+import java.util.Optional;
+
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition;
+import com.mercedesbenz.sechub.developertools.admin.ui.UIContext;
+import com.mercedesbenz.sechub.developertools.admin.ui.action.AbstractUIAction;
+import com.mercedesbenz.sechub.developertools.admin.ui.cache.InputCacheIdentifier;
+
+public class AssignTemplateToProjectAction extends AbstractUIAction {
+ private static final long serialVersionUID = 1L;
+
+ public AssignTemplateToProjectAction(UIContext context) {
+ super("Assign template to project", context);
+ }
+
+ @Override
+ public void execute(ActionEvent e) {
+ Optional templateIdOpt = getUserInput("Please enter templateId", InputCacheIdentifier.TEMPLATE_ID);
+ if (!templateIdOpt.isPresent()) {
+ return;
+ }
+ String templateId = templateIdOpt.get();
+ TemplateDefinition foundTemplate = getContext().getAdministration().fetchTemplateOrNull(templateId);
+ if (foundTemplate == null) {
+ error("The template " + templateId + " does not exist!");
+ return;
+ }
+ Optional projectIdOpt = getUserInput("Please enter projectId", InputCacheIdentifier.PROJECT_ID);
+ if (!projectIdOpt.isPresent()) {
+ return;
+ }
+ String projectId = projectIdOpt.get();
+ String projectInfo = getContext().getAdministration().fetchProjectInfo(projectId);
+
+ if (projectInfo == null) {
+ error("The project " + projectId + " does not exist!");
+ return;
+ }
+
+ getContext().getAdministration().assignTemplateToProject(templateId, projectId);
+
+ }
+
+}
\ No newline at end of file
diff --git a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/template/CreateOrUpdateTemplateAction.java b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/template/CreateOrUpdateTemplateAction.java
new file mode 100644
index 0000000000..3aa8002b15
--- /dev/null
+++ b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/template/CreateOrUpdateTemplateAction.java
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.developertools.admin.ui.action.template;
+
+import java.awt.event.ActionEvent;
+import java.util.Optional;
+
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition.TemplateVariable;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition.TemplateVariableValidation;
+import com.mercedesbenz.sechub.commons.model.template.TemplateType;
+import com.mercedesbenz.sechub.developertools.admin.ui.UIContext;
+import com.mercedesbenz.sechub.developertools.admin.ui.action.AbstractUIAction;
+import com.mercedesbenz.sechub.developertools.admin.ui.cache.InputCacheIdentifier;
+
+public class CreateOrUpdateTemplateAction extends AbstractUIAction {
+ private static final long serialVersionUID = 1L;
+
+ public CreateOrUpdateTemplateAction(UIContext context) {
+ super("Create or update template", context);
+ }
+
+ @Override
+ public void execute(ActionEvent e) {
+ Optional templateIdOpt = getUserInput("Please enter templateId", InputCacheIdentifier.TEMPLATE_ID);
+ if (!templateIdOpt.isPresent()) {
+ return;
+ }
+
+ String dialogTitle = null;
+ String templateId = templateIdOpt.get();
+ TemplateDefinition templateDefinition = getContext().getAdministration().fetchTemplateOrNull(templateId);
+ if (templateDefinition == null) {
+ /* we create an example here */
+
+ String title = "Create new template:" + templateId;
+ String message = "Please enter template type";
+ Optional templateTypeOpt = getUserInputFromCombobox(title, TemplateType.WEBSCAN_LOGIN, message, TemplateType.values());
+ if (!templateTypeOpt.isPresent()) {
+ return;
+ }
+ templateDefinition = TemplateDefinition.builder().templateId(templateId).templateType(templateTypeOpt.get()).assetId("example-asset-id").build();
+ TemplateVariable exampleVariable = new TemplateVariable();
+ exampleVariable.setName("example-variable");
+ exampleVariable.setOptional(true);
+ TemplateVariableValidation validation = new TemplateVariableValidation();
+ validation.setMinLength(2);
+ validation.setMaxLength(100);
+ validation.setRegularExpression("[0-9a-z].*");
+
+ exampleVariable.setValidation(validation);
+ templateDefinition.getVariables().add(exampleVariable);
+
+ dialogTitle = "New Template:" + templateId + " (by example)";
+
+ } else {
+ dialogTitle = "Change existing template:" + templateId;
+ }
+
+ Optional templateDefInputOpt = getUserInputFromTextArea(dialogTitle, templateDefinition.toFormattedJSON());
+ if (templateDefInputOpt.isEmpty()) {
+ return;
+ }
+
+ TemplateDefinition updatedTemplateDefinition = TemplateDefinition.from(templateDefInputOpt.get());
+
+ getContext().getAdministration().createOrUpdateTemplate(templateId, updatedTemplateDefinition);
+ }
+
+}
\ No newline at end of file
diff --git a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/template/FetchAllTemplateIdentifiersAction.java b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/template/FetchAllTemplateIdentifiersAction.java
new file mode 100644
index 0000000000..763a9b163b
--- /dev/null
+++ b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/template/FetchAllTemplateIdentifiersAction.java
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.developertools.admin.ui.action.template;
+
+import java.awt.event.ActionEvent;
+import java.util.List;
+
+import com.mercedesbenz.sechub.developertools.admin.ui.UIContext;
+import com.mercedesbenz.sechub.developertools.admin.ui.action.AbstractUIAction;
+
+public class FetchAllTemplateIdentifiersAction extends AbstractUIAction {
+ private static final long serialVersionUID = 1L;
+
+ public FetchAllTemplateIdentifiersAction(UIContext context) {
+ super("Fetch all template identifiers", context);
+ }
+
+ @Override
+ public void execute(ActionEvent e) {
+ List identifiers = getContext().getAdministration().fetchAllTemplateIdentifiers();
+ output("Found template identiiers:\n" + identifiers.toString());
+ }
+
+}
\ No newline at end of file
diff --git a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/template/UnassignTemplateFromProjectAction.java b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/template/UnassignTemplateFromProjectAction.java
new file mode 100644
index 0000000000..2ec9ef90c3
--- /dev/null
+++ b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/template/UnassignTemplateFromProjectAction.java
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.developertools.admin.ui.action.template;
+
+import java.awt.event.ActionEvent;
+import java.util.Optional;
+
+import com.mercedesbenz.sechub.developertools.admin.ui.UIContext;
+import com.mercedesbenz.sechub.developertools.admin.ui.action.AbstractUIAction;
+import com.mercedesbenz.sechub.developertools.admin.ui.cache.InputCacheIdentifier;
+
+public class UnassignTemplateFromProjectAction extends AbstractUIAction {
+ private static final long serialVersionUID = 1L;
+
+ public UnassignTemplateFromProjectAction(UIContext context) {
+ super("Unassign template from project", context);
+ }
+
+ @Override
+ public void execute(ActionEvent e) {
+ Optional templateIdOpt = getUserInput("Please enter templateId", InputCacheIdentifier.TEMPLATE_ID);
+ if (!templateIdOpt.isPresent()) {
+ return;
+ }
+ String templateId = templateIdOpt.get();
+ Optional projectIdOpt = getUserInput("Please enter projectId", InputCacheIdentifier.PROJECT_ID);
+ if (!projectIdOpt.isPresent()) {
+ return;
+ }
+ String projectId = projectIdOpt.get();
+
+ getContext().getAdministration().unassignTemplateFromProject(templateId, projectId);
+
+ }
+
+}
\ No newline at end of file
diff --git a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/cache/InputCacheIdentifier.java b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/cache/InputCacheIdentifier.java
index e6a9238208..931ff7b8d1 100644
--- a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/cache/InputCacheIdentifier.java
+++ b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/cache/InputCacheIdentifier.java
@@ -50,4 +50,8 @@ public enum InputCacheIdentifier {
PAGE_SIZE,
+ TEMPLATE_ID,
+
+ ASSET_ID,
+
}
\ No newline at end of file
diff --git a/sechub-doc/build.gradle b/sechub-doc/build.gradle
index b72cb03032..2a7a277fa1 100644
--- a/sechub-doc/build.gradle
+++ b/sechub-doc/build.gradle
@@ -17,6 +17,7 @@ dependencies {
'sechub-pds-tools', /* only pds tooling + avoid cycles */
'sechub-api-java', /* the api project needs sechub-doc tests (and compile) for open api json files. So we may not have this as relation! */
'sechub-systemtest', /* avoid cyclic dependency, see AdoptedSystemTestDefaultFallbacks javadoc for more information */
+ 'sechub-wrapper-owasp-zap',
]
/* fetch all sub projects, except unwanted and all only used for testing */
@@ -53,6 +54,7 @@ dependencies {
for (String wantedProjectName: wanted){
implementation project(":${wantedProjectName}")
}
+ implementation project(':sechub-shared-kernel')
implementation library.apache_commons_io
implementation spring_boot_dependency.jackson_core
diff --git a/sechub-doc/helperscripts/publish+git-add-releasedocs.sh b/sechub-doc/helperscripts/publish+git-add-releasedocs.sh
index 5358bb49b2..6a951e7e70 100755
--- a/sechub-doc/helperscripts/publish+git-add-releasedocs.sh
+++ b/sechub-doc/helperscripts/publish+git-add-releasedocs.sh
@@ -1,42 +1,97 @@
#!/bin/bash
# SPDX-License-Identifier: MIT
-FILE_LIST=""
-SOURCE_DIR="build/docs/final-html"
-DEST_DIR="../docs/latest"
-IMAGE_DIR="images"
GIT_RELEASE_BRANCH="master"
+DOCS_FILE_LIST=""
+DOCS_SOURCE_DIR="build/docs/final-html"
+DEST_DIR="../docs"
+DOCS_DEST_DIR="$DEST_DIR/latest"
+DOCS_IMAGE_DIR="images"
+WEBSITE_HOME="../sechub-website"
+WEBSITE_BUILD_DIR="$WEBSITE_HOME/.output/public"
+WEBSITE_FILE_LIST=""
-function add_changed_images {
- pushd "$SOURCE_DIR/" >/dev/null 2>&1
- local imagefiles=`ls $IMAGE_DIR/*`
+function website_add_changed_files {
+ pushd "$WEBSITE_BUILD_DIR/" >/dev/null 2>&1
+ local website_files=`/bin/ls | grep -v '_nuxt'`
+ popd >/dev/null 2>&1
+
+ echo -n "# Adding changed or new website files:"
+ for website_file in $website_files ; do
+ if ! cmp --silent "$WEBSITE_BUILD_DIR/$website_file" "$DEST_DIR/$website_file" ; then
+ echo -n " '$website_file'"
+ WEBSITE_FILE_LIST="$WEBSITE_FILE_LIST $website_file"
+ fi
+ done
+
+ for file in $WEBSITE_FILE_LIST ; do
+ /bin/cp "$WEBSITE_BUILD_DIR/$file" "$DEST_DIR/$file"
+ echo "git add \"$DEST_DIR/$file\""
+ git add "$DEST_DIR/$file"
+ done
+ echo
+}
+
+function website_add_nuxt_files {
+ local nuxt_dir="$DEST_DIR/_nuxt"
+
+ if [ -d "$nuxt_dir" ] ; then
+ echo "# Wiping _nuxt destination directory"
+ # because file names include checksums and thus change each time
+ git rm -rf "$nuxt_dir/"
+ fi
+
+ echo "# Copying _nuxt directory"
+ /bin/cp -r "$WEBSITE_BUILD_DIR/_nuxt" "$DEST_DIR"
+
+ pushd "$DEST_DIR/" >/dev/null 2>&1
+ local nuxt_files=`find _nuxt/ -type f`
+
+ for file in $nuxt_files ; do
+ echo "git add \"$file\""
+ git add "$file"
+ done
+ popd >/dev/null 2>&1
+}
+
+function docs_collect_changed_images {
+ pushd "$DOCS_SOURCE_DIR/" >/dev/null 2>&1
+ local imagefiles=`ls $DOCS_IMAGE_DIR/*`
popd >/dev/null 2>&1
echo -n "# Adding changed or new image files:"
for imagefile in $imagefiles ; do
- if ! cmp --silent "$SOURCE_DIR/$imagefile" "$DEST_DIR/$imagefile" ; then
+ if ! cmp --silent "$DOCS_SOURCE_DIR/$imagefile" "$DOCS_DEST_DIR/$imagefile" ; then
echo -n " '$imagefile'"
- FILE_LIST="$FILE_LIST $imagefile"
+ DOCS_FILE_LIST="$DOCS_FILE_LIST $imagefile"
fi
done
echo
}
-function add_changed_html_files {
- pushd "$SOURCE_DIR/" >/dev/null 2>&1
+function docs_collect_changed_html_files {
+ pushd "$DOCS_SOURCE_DIR/" >/dev/null 2>&1
local htmlfiles=`ls *.html`
popd >/dev/null 2>&1
echo -n "# Adding changed or new html files:"
for htmlfile in $htmlfiles ; do
- if ! cmp --silent "$SOURCE_DIR/$htmlfile" "$DEST_DIR/$htmlfile" ; then
+ if ! cmp --silent "$DOCS_SOURCE_DIR/$htmlfile" "$DOCS_DEST_DIR/$htmlfile" ; then
echo -n " '$htmlfile'"
- FILE_LIST="$FILE_LIST $htmlfile"
+ DOCS_FILE_LIST="$DOCS_FILE_LIST $htmlfile"
fi
done
echo
}
+function docs_add_changed_files {
+ for file in $DOCS_FILE_LIST ; do
+ /bin/cp "$DOCS_SOURCE_DIR/$file" "$DOCS_DEST_DIR/$file"
+ echo "git add -f \"$DOCS_DEST_DIR/$file\""
+ git add -f "$DOCS_DEST_DIR/$file"
+ done
+}
+
#######################
cd "`dirname $0`/.."
@@ -47,17 +102,23 @@ if [ "$BRANCH" != "$GIT_RELEASE_BRANCH" ] ; then
exit 1
fi
-# Update images directory (changed files only)
-add_changed_images
+echo "# SecHub website: Build and publish"
+"$WEBSITE_HOME"/build-sechub-website.sh
+echo
+
+# Get list of changes files
+website_add_changed_files
+
+# Update _nuxt directory
+website_add_nuxt_files
+
+# Docs: Update images directory (changed files only)
+docs_collect_changed_images
-# Add changed html files
-add_changed_html_files
+# Docs: Add changed html files
+docs_collect_changed_html_files
-# Copy files to destination and stage them for commit
-for file in $FILE_LIST ; do
- /bin/cp "$SOURCE_DIR/$file" "$DEST_DIR/$file"
- echo "git add -f \"$DEST_DIR/$file\""
- git add -f "$DEST_DIR/$file"
-done
+# Copy docs files to destination and stage them for commit
+docs_add_changed_files
# Important: We do no git commit here - so everything the script does, can be undone.
diff --git a/sechub-doc/src/docs/asciidoc/diagrams/diagram_templates-and-assets-big-picture.puml b/sechub-doc/src/docs/asciidoc/diagrams/diagram_templates-and-assets-big-picture.puml
new file mode 100644
index 0000000000..559f69408c
--- /dev/null
+++ b/sechub-doc/src/docs/asciidoc/diagrams/diagram_templates-and-assets-big-picture.puml
@@ -0,0 +1,39 @@
+@startuml
+
+skinparam linetype ortho
+
+actor user
+
+rectangle "SecHub configuration file" as config #lightgreen
+
+rectangle "Template data" as templateData #lightgreen
+
+rectangle "SecHub\nJob" as sechub
+
+rectangle "Access to extracted\nproduct specific template files\n(from asset)" as assetFile #darkorange
+
+rectangle "PDS\nJob" as pds
+rectangle "PDS launcher script\nfor PDS product" as pdsLauncherScript
+
+rectangle "Template\ndefinition" as templateDefinition
+rectangle "Project" as project
+rectangle "Asset" as asset
+
+user->config
+config ->sechub
+config o--templateData
+
+pds ..> asset
+
+sechub ..> project
+project ..> templateDefinition
+sechub -> pds
+
+templateDefinition .> asset
+
+pds -> pdsLauncherScript
+
+pdsLauncherScript ..> assetFile
+
+@enduml
+
diff --git a/sechub-doc/src/docs/asciidoc/diagrams/diagram_templates-mapping-to-projects-events.puml b/sechub-doc/src/docs/asciidoc/diagrams/diagram_templates-mapping-to-projects-events.puml
new file mode 100644
index 0000000000..509fbc4266
--- /dev/null
+++ b/sechub-doc/src/docs/asciidoc/diagrams/diagram_templates-mapping-to-projects-events.puml
@@ -0,0 +1,52 @@
+' SPDX-License-Identifier: MIT
+@startuml
+
+'Hide empty parts:
+hide empty fields
+hide empty methods
+
+'You can find more examles at https://plantuml.com/class-diagram
+
+
+node scan {
+
+ class ScanProjectConfig {
+ String projectId
+ String key
+ String value
+ }
+}
+
+
+node administration{
+ class Project {
+ String projectId
+ List templates
+ }
+
+}
+
+node eventBus as eventBus{
+}
+
+administration --> eventBus: REQUEST_ASSIGN_TEMPLATE_TO_PROJECT (1)
+
+eventBus--> scan: REQUEST_ASSIGN_TEMPLATE_TO_PROJECT (2)
+
+scan --> eventBus: RESULT_ASSIGN_TEMPLATE_TO_PROJECT (3)
+
+
+note top of ScanProjectConfig
+If the there exists already an entry for key "TEMPLATE_$templateType"
+the config entry will be replaced. otherwise created.
+end note
+
+note top of administration
+After template mapping result is returned by scan domain,
+the assigned template list will be updated.
+
+If a failure happend - e.g. template with given id does
+not exist, the scan domain will return a failure
+and this failure will be returned to caller side.
+end note
+@enduml
diff --git a/sechub-doc/src/docs/asciidoc/diagrams/diagram_templates-mapping-to-projects.puml b/sechub-doc/src/docs/asciidoc/diagrams/diagram_templates-mapping-to-projects.puml
new file mode 100644
index 0000000000..c88bb9438b
--- /dev/null
+++ b/sechub-doc/src/docs/asciidoc/diagrams/diagram_templates-mapping-to-projects.puml
@@ -0,0 +1,61 @@
+' SPDX-License-Identifier: MIT
+@startuml
+
+'Hide empty parts:
+hide empty fields
+hide empty methods
+
+'You can find more examles at https://plantuml.com/class-diagram
+
+
+node scan {
+
+ class Template {
+ String id
+ TemplateDefinition definition
+ }
+
+ class TemplateDefinition {
+ TemplateType type
+ ...
+
+ }
+
+ class ScanProjectConfig {
+ String projectId
+ String key
+ String value
+ }
+}
+
+
+node administration{
+ class Project {
+ String projectId
+ List templates
+ }
+
+}
+
+database "Scan\ndatabase" as DB1 {
+}
+
+database "Administration\ndatabase" as DB2 {
+}
+
+TemplateDefinition . Template
+Template --> DB1
+ScanProjectConfig --> DB1
+
+Project --> DB2
+
+
+note top of ScanProjectConfig
+We use the existing concept of scan project
+configuration to handle project related
+template setup:
+
+The key will be always "TEMPLATE_$templateType"
+and the value is the template identifier.
+end note
+@enduml
diff --git a/sechub-doc/src/docs/asciidoc/documents/pds/pds_config.adoc b/sechub-doc/src/docs/asciidoc/documents/pds/pds_config.adoc
index d4bc584562..9e798d1065 100644
--- a/sechub-doc/src/docs/asciidoc/documents/pds/pds_config.adoc
+++ b/sechub-doc/src/docs/asciidoc/documents/pds/pds_config.adoc
@@ -48,7 +48,7 @@ Either use system properties
pds.techuser.userid
pds.techuser.apitoken
----
-or env entries
+or environment variables
----
PDS_TECHUSER_USERID
PDS_TECHUSER_APITOKEN
@@ -187,6 +187,12 @@ how the data is gathered.
|PDS_JOB_SOURCECODE_ZIP_FILE | The absolute path to the uploaded "sourcecode.zip" file
|PDS_JOB_EXTRACTED_SOURCES_FOLDER | When auto extracting is enabled (default) the uploaded source code is extracted to this folder
|PDS_JOB_EXTRACTED_BINARIES_FOLDER | When auto extracting is enabled (default) the uploaded binaries are extracted to this folder
+|PDS_JOB_EXTRACTED_ASSETS_FOLDER | The absolute path to the extracted assets. +
+ +
+ Files for template types are located in dedicated sub folders which are named by the id of the template type. + +
+ +
+ For example: WEBSCAN_LOGIN template type has id: `webscan-login`. To access the file `custom-login.groovy` a script
+ or a wrapper application can simply fetch the file via `$PDS_JOB_EXTRACTED_ASSETS_FOLDER/webscan-login/custom-login.groovy`.
|PDS_SCAN_TARGET_URL | Target URL for current scan (e.g webscan). Will not be set in all scan types. E.g. for a code scan this environemnt variable will not be available
|PDS_SCAN_CONFIGURATION | Contains the SecHub configuration as JSON _(but reduced to current scan type, so e.g. a web scan will have no code scan configuration data available)_ +
+
@@ -216,10 +222,15 @@ include::../gen/gen_pds_executor_config_parameters.adoc[]
==== File locations
===== Upload
-`$PDS_JOB_WORKSPACE_LOCATION/upload/`
+Content from uploaded user archives is extracted to:
+
+`PDS_JOB_EXTRACTED_SOURCES_FOLDER`,
+
+`PDS_JOB_EXTRACTED_BINARIES_FOLDER`
+
+Content from uploaded asset files is extracted to:
+ `PDS_JOB_EXTRACTED_ASSETS_FOLDER`,
-Automatically unzipped content is available inside +
-`$PDS_JOB_WORKSPACE_LOCATION/upload/unzipped`
===== Output
diff --git a/sechub-doc/src/docs/asciidoc/documents/shared/concepts/concept_templates_and_assets.adoc b/sechub-doc/src/docs/asciidoc/documents/shared/concepts/concept_templates_and_assets.adoc
index 2e9492cfa9..bd1361d3c7 100644
--- a/sechub-doc/src/docs/asciidoc/documents/shared/concepts/concept_templates_and_assets.adoc
+++ b/sechub-doc/src/docs/asciidoc/documents/shared/concepts/concept_templates_and_assets.adoc
@@ -7,70 +7,101 @@ and - depending on the product - very hard to provide a complete generic approac
only.
===== Example situation
-To clarify this abstract situation here an example: +
+To clarify this abstract situation here an example: Think about a wide range of applications inside
+a corporation which want to use a common single sign on with multi factor authentication and some
+other specific parts. The login configuration in {sechub} configuration file by defining explicit steps, pages etc.
+would become complex and error prone because :
-Think about a wide range of applications inside a corporation which want to use a common
-single sign on with multi factor authentication and some other specific parts.
-
-The login configuration would become complex and error prone because not every user would
-understand exactly what to do. If the single-signon mechanism would change, every project/user
-would need to change their {sechub} configuration files! When talking about hundreds of projects
-this would be a great effort - for users and for support!
+- not every user would understand exactly what to do
+- if the single signon mechanism ever changes, every project/user would need to change their {sechub} configuration files!
+ When talking about hundreds of projects this would be a great effort - for users and for support!
And let us assume the used product has hundreds of options which cannot be configured
by a generic approach easily (e.g. form login definition in configuration): How could this product
-be provided in a desired way then?
-
-And what if there are some specific parts which are not 100% same in every of these projects?
+be provided in a desired way then? And what if there are some specific parts which are not 100% same
+in every of these projects?
This is the reason for the "templates and assets" concept which is described below.
+===== Big picture
+plantuml::./diagrams/diagram_templates-and-assets-big-picture.puml[format=svg, title="Big picture of templates and assets"]
+
===== Templates
-{sechub} can have multiple templates . Every template has a type and can contains a set of
-assets and also a variable definition. It can be administrated via
-REST end points:
+{sechub} can have multiple templates. Every template has a type and can contains a set of
+assets and also a variable definition. It can be administrated via REST end points.
-```
-PUT /api/administration/template/$template-id
-GET /api/administration/template/$template-id
-DELETE /api/administration/template/$template-id
-```
+====== Template id format
+A template id
-PUT will contain following body:
+- has a minimum length of 3
+- has a maximum length of 40
+- can contain only `a-z` `A-Z`, `0-9` or `-` or `_`
+
+====== Template definition
+The template definitions are hold inside {sechub} database.
+A template is defined by following json syntax:
+.Template definition syntax
[source,json]
----
-{
- "type" : "webscan-login",//<1>
- "assets" : ["asset-id-1"]//<2>
- "variables" : {//<3>
- "username" : "mandatory",//<4>
- "password" : "mandatory",
- "tip-of-the-day" : "optional"//<5>
- }
-}
+include::../snippet/template-definition-syntax.json[]
----
-<1> The type of the template. Currently possible: `webscan-login`
-<2> Array with asset identifiers assigned to the template
-<3> Variable definitions as list of key and value pairs which can be mandatory or optional.
+<1> The template identifier
+<2> The type of the template. Must be set when a template is created. Will
+ be ignored on updates. Possible values are: `webscan-login`
+<3> Asset identifier for the template
+<4> Variable definitions as list of key and value pairs which can be mandatory or optional.
Via the type {sechub} server is able to check if the configuration is valid and give
response to users when job starts.
+<5> Name of the variable
+<6> Describes if the variable is optional. +
+ When `false` configuration must contain the variable inside template data. When `true`
+ the configuration is valid without the variable. The default is `false`.
+<7> Variable content validation definition ((optional)
+<8> Minimum length (optional)
+<9> Maximum length (optional)
+<10> Regular expression (optional). If defined, the content must match the given regular expression
+ to be valid.
+
+[CAUTION]
+====
+The validation section inside the definition is only for a "first simple check" on {sechub} side, to
+stop before any {pds} job is created.
-===== Mapping templates to projects
-```
-PUT /api/admin/project/$projectId/template/$templateid
-DELETE /api/admin/project/$projectId/template/$templateid
-GET /api/admin/project/$projectId/template/list
-```
+*But {pds} solutions which are using templates must ensure,that the given user content
+(variable) is correct and not some malicious injected data!*
+====
+====== Mapping templates to projects
-[IMPORTANT]
+plantuml::./diagrams/diagram_templates-mapping-to-projects.puml[format=svg, title="Mapping of templates and projects"]
+
+As shown in the figure above, the template data is hold inside domain "scan". The reason is, that
+template details are necessary inside the scan operation and no where else.
+
+The association between project and used templates is done in administration domain because the project
+entity and its details are used here. At the `administration` domain only the template identifiers
+used by this project are necessary.
+
+A mapping of templates to a project is dependent on the template type: Same type of a template cannot
+be added twice to a project! Because the `administration` domain does not know about the template details,
+a synchronous event to `scan` domain will be sent to assign the template to the project.
+
+The scan domain will update this information inside `ScanProjectConfig` entities and will drop
+a former assigned template of same type from the config.
+
+After this is done as a syncron result the scan domain will return all assigned
+templates, as shown in next figure:
+
+plantuml::./diagrams/diagram_templates-mapping-to-projects-events.puml[format=svg, title="Mapping of templates and projects (Events)"]
+
+[NOTE]
====
The mapping is stored with template type as part of the composite key:
If two template definitions with same type are uploaded, the last one will
overwrite the first one.
====
-===== SecHub configuration file
+====== Templates in SecHub configuration file
The users can now define in their {sechub} configuration file that they want to use the
configured template for web scanning by defining a templateData section inside the login
configuration.
@@ -84,52 +115,29 @@ include::../configuration/sechub_config_example22_webscan_with_template.json[]
is not able to define which template is used.
<2> Setup template variables by a list of key and value pairs.
-[IMPORTANT]
-====
If the user has defined a `templateData` section in the configuration but no template of that type is assigned to the project, a
dedicated error message must be returned to the user and the {sechub} job will always fail.
-====
-
-===== Asset storage
-The assets are initially stored in {sechub} database, together with the asset file checksum.
-
-When a template is used inside a {sechub} configuration, the {sechub} start mechanism will
-check if the file from database is already available at storage (S3/NFS) which represents
-a cache for the assets.
-
-If available, the cached part will be used. Otherwise it will be uploaded
-(see <> )
-
-
-[TIP]
-====
-Main reason why we store always in DB and use storage (S3/NFS) as a cache only:
-
-1. Restore a DB backup on a fresh installation: +
- Administrators can just apply the database backup and everything works again
-2. Storage could be volatile/be deleted etc. (we rely to the database)
-3. For having two clusters ( {sechub} and {pds} having not same database we need
- a way to exchange.
-====
-
[IMPORTANT]
====
-For templates and assets we use the shared storage between {sechub} and {pds} .
-solutions and not to provide own storage for {pds}. Diffeent storages are not supported here.
+For templates and assets we must use shared storage between {sechub} and {pds}. {pds} solutions may
+not to provide own storage for {pds} - different storages are not supported here.
====
-`/assets/$assetId/`
+===== Assets
+====== Asset id format
+An asset id
-An asset id has
-
-- maximum length of 40
+- has a minimum length of 3
+- has a maximum length of 40
- can contain only `a-z` `A-Z`, `0-9` or `-` or `_`
-At the storage for each product (as named in the `pds-config.json` of the {pds} solution), there will be a ZIP file having the
-product identifier as filename
+====== {sechub} server uses product identifiers for asset ids
+The {sechub} server will use the product identifiers from {pds} executor configurations
+as file names when storing asset data inside database and storage. The product identifiers
+are also defined inside the {pds} server configuration files.
-For example:
+Here an example for storage paths:
```
/assets/
asset-id-1/
@@ -138,39 +146,62 @@ For example:
OTHER_PRODUCT_X.zip
OTHER_PRODUCT_X.zip.sha256
```
-[TIP]
+[IMPORTANT]
====
-The content and the structure inside the ZIP files is absolute solution specific :
-The PDS solution defines which kind of template data is necessary and how the
-structure looks like!
+The content and the structure inside the ZIP files is absolute solution specific:
+Means the {pds} solution defines which kind of template data is necessary and how the
+structure looks like. Because of this necessary template variables, file structure for
+asset zip files etc. must be documented inside the {pds} solution!
====
-===== Administration REST end points for storage
+====== Asset operations
+*General*
+
+*Usecases*
+
+- Admin creates or updates asset
+-
+
Over REST API Administrators will be able to
-- list (ZIP) file names +
- `GET /api/administration/asset/$assetId/list`
-- upload asset ZIP files by REST API and create checksum file +
- `PUT /api/administration/asset/$assetId/$productId`
-- download asset files by REST API +
- `GET /api/administration/asset/$assetId/$productId`
-- delete single asset file by REST API +
- `DELETE /api/administration/asset/$assetId/$productId`
-- delete all asset files by REST API +
- `DELETE /api/administration/asset/$assetId`
+- upload asset ZIP files by REST API and create checksum file
+- download asset files by REST API
+- list asset details (filenames and checksums)
+- delete single asset file by REST API
+- delete all asset data by REST API
The REST API will always create an audit log entry
+*Upload*
+
+The upload and delete operations will always handle DB and storage (S3/NFS)!
+If an asset ZIP file already exists, the operation will overwrite the file and the
+old sha256 checksum in both locations.
+
+[TIP]
+====
+Main reason why we store always in DB and use storage (S3/NFS) as a cache only:
+
+1. Restore a DB backup on a fresh installation: +
+ Administrators can just apply the database backup and everything works again
+2. Storage could be volatile/be deleted etc. (we rely to the database)
+3. For having multiple and also different cluster types ( {sechub} cluster and multiple {pds} clusters)
+ having not same database we need a common way to exchange data.
+ Each {pds} cluster and also the {sechub} cluster have their own
+ database - means cannot be used for exchange.
+====
+
+
[IMPORTANT]
====
This works only when the storage is shared between {sechub} and {pds}.
If the {pds} uses its own storage (which should NOT be done for production, but only
-for PDS solution development) the assets must be uploaded directly to the PDS storage
-location!
+for PDS solution development) the assets would needed to be uploaded directly to the PDS storage
+location with correct checksum etc.
====
-===== SecHub template and asset handling
-====== Validation
+===== SecHub runtime
+====== Validate config uses valid template location
When a {sechub} job starts and there is a template data definition inside the configuration,
{sechub} will validate if the project has a template assigned for the location inside
the configuration. e.g. templates with type `webscan-login` may only be defined inside
@@ -179,51 +210,43 @@ web scan login configuration).
If this validation fails, the complete {sechub} job will fail and stop processing.
[[sechub-concept-asset-upload-lazy]]
-====== Upload in storage as cache lazily
-When validation did not fail, {sechub} will check if the current version of the asset is already uploaded to
-storage (S3/NFS) already.
+====== Validate config uses valid template location
+
+When validation did not fail, {sechub} will check if the current version of the
+necessary product specific asset ZIP file is exsting in storage.
-When not uploaded to storage, the file will be uploaded before the job is further processed.
+When not uploaded to storage, the file will be uploaded before the job is
+further processed.
====== Template PDS parameter calculation
-If validation and lazy upload did not fail, {sechub} will calculate a
-{pds} parameter : `pds.template.metadata` with JSON (see parameter syntax for details).
+If the job configuration has valid template data and the template
+is available in storage, {sechub} will calculate the
+{pds} parameter : `pds.config.template.metadata.list` with JSON:
-.PDS parameter syntax for template meta data
+[[sechub-concept-template-metadata-example]]
+.PDS parameter syntax for template meta data
[source,json]
----
-{
- "template-metadata" : [ //<1>
- {
- "template" : "single-singon", //<2>
- "type": "webscan-login",//<3>
- "assets" : [ //<4>
- {
- "id" : "asset-id-1", //<5>
- "sha256" : "434c6c6ec1b0ed9844149069d7d45ac18e72505b" //<6>
- }
- ]
- }]
- }
-}
-
+include::../snippet/pds-param-template-metadata-syntax.json[]
----
<1> Meta data array
<2> Template identifier - just as an information for logging etc.
<3> Template type
<4> Asset information array
-<5> ID of the asset, necessary for storage download
-<6> checksum of the asset ZIP file, necessary to check after download
+<5> Asset identifier
+<6> Name of file (inside asset)
+<7> Checksum of the file (SHA256)
-===== PDS asset handling
+===== PDS runtime
+====== Asset handling
{sechub} calls {pds}, with {pds} parameter `pds.template.metadata` (syntax is described above).
-The {pds} intance will fetch all wanted asset ZIP file for the current product
-from storage (S3 or NFS) and extract it to `$workspaceFolder/assets/`.
+The {pds} instance will fetch all defined files from storage (S3 or NFS)
+and extract/copy it to `$workspaceFolder/assets/`.
-Before extraction is done a checksum for the downloaded ZIP file is created and compared
-with the checksum from template meta data. If it is different the {pds} job will fail
-with a dedicated error message.
+Before extraction is done a checksum for the downloaded file is created and compared
+with the checksum from template meta data. The checksum algorithm is SHA256.
+If it is different the {pds} job will fail with a dedicated error message.
If the checksum is valid, the assets ZIP file will be unzipped below a subfolder with the
template type:
@@ -238,66 +261,74 @@ template type:
An example:
-The `MY_PRODUCT.zip` file contains
+The `WEBSCAN_PRODUCT_ID.zip` file contains
[source,text]
----
-/script.js
+/script.groovy
/development/
debug-settings.json
----
and the template meta data looks like this:
-[source,text]
+[source,json]
----
-{
- "template-metadata" : {
- {
- "type: "webscan-login",
- "assets" : [
- {
- "id" : "asset-id-1",
- "sha256" : "434c6c6ec1b0ed9844149069d7d45ac18e72505b"
- }
- ]
- }
- }
-}
+include::../snippet/pds-param-template-metadata-example1.json[]
----
+The extraction is done into folder:
+`$workspaceFolder/assets/$templateType/` which will be made available to launcher scripts (and wrapper
+applications) by environmnet variable `PDS_JOB_EXTRACTED_ASSETS_FOLDER`.
-It will be extracted the following way.
+For the former example `WEBSCAN_PRODUCT_ID.zip` will be extracted the following way:
[source,text]
----
`$workspaceFolder/
assets/
webscan-login/
- script.js
- develoment/
+ script.groovy
+ development/
debug-settings.json
----
-====== Pseudo code for usage inside PDS solutions/wrappers
+====== Example code for usage inside PDS solutions/wrappers
-Here an example (but pseudo code) how a product could use the assets inside:
+Here an example how a product could use the assets inside
+a wrapper application for a PDS solution:
[source,java]
----
-WebScanTemplateData data = util.fetchWebScanTemplateData(sechubConfig);
-if (data!=null){
- // folder=/$workspaceFolder/assets
- File folder = util.getAssetFolder();
+import java.nio.file.*;
+import com.mercedesbenz.sechub.commons.model.JSONConverter;
+import com.mercedesbenz.sechub.commons.model.template.*;
+// ...
+
+String sechubConfigAsJson = System.getenv("PDS_SCAN_CONFIGURATION");
+SecHubConfigurationModel sechubConfig = JSONConverter.get().fromJSON(SecHubConfigurationModel.class,sechubConfigAsJson);
+
+TemplateDataResolver resolver = new TemplateDataResolver();
+
+TemplateData templateData = resolver.resolveTemplateData(TemplateType.WEBSCAN_LOGIN, sechubConfig);
+if (templateData!=null){
+
+ Map variables = templateData.getVariables();
+
+ // read template script file from extracted folder
+ String assetExtractionFolder = System.getenv("PDS_JOB_EXTRACTED_ASSETS_FOLDER");
+ Path path = Paths.get(assetExtractionFolder, TemplateType.WEBSCAN_LOGIN.getId(), "script.groovy");
+ String scriptTemplate = Files.readString(path);
- // in example: it is a web scan...
- script = folder.getChild("webscan-login/script.js");
+ // replace variable parts inside template
+ String script = scriptTemplate ;
+ script = script.replaceAll("#user", variales.get("user");
+ script = script.replaceAll("#pwd", variales.get("password");
- script = replaceVariableInScript(data.getVariable("username"), script);
- script = replaceVariableInScript(data.getVariable("password"), script);
+ // execute script
+ // ...
- // use script....
}
----
diff --git a/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config.adoc b/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config.adoc
index 39c0d463a6..1a0df3d6d2 100644
--- a/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config.adoc
+++ b/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config.adoc
@@ -328,6 +328,15 @@ The currently available hash algorithms are:
- `HMAC_SHA1`
- `HMAC_SHA256`
- `HMAC_SHA512`
+<6> The `encodingType` is an __optional__ field, representing the encoding of the __mandatory__ field `seed`.
+{sechub} has a default configured if nothing is specified or the encoding type is not known.
+The default value is `AUTODETECT` where {sechub} tries to detect the encoding of one of the four other available types.
+ The currently available encoding types for `seed` are, which are treated case-insensitive:
+- `BASE64`
+- `BASE32`
+- `HEX`
+- `PLAIN`
+- `AUTODETECT`
[[sechub-config-example-webscan-openapi]]
====== Example OpenAPI scan
diff --git a/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config_example21_webscan_login_form_with_totp.json b/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config_example21_webscan_login_form_with_totp.json
index 989a54b318..110468808a 100644
--- a/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config_example21_webscan_login_form_with_totp.json
+++ b/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config_example21_webscan_login_form_with_totp.json
@@ -27,7 +27,8 @@
"seed" : "example-seed", //<2>
"validityInSeconds" : 60, //<3>
"tokenLength" : 8, //<4>
- "hashAlgorithm" : "HMAC_SHA256" //<5>
+ "hashAlgorithm" : "HMAC_SHA256", //<5>
+ "encodingType" : "base64" //<6>
}
}
}
diff --git a/sechub-doc/src/docs/asciidoc/documents/shared/snippet/pds-param-template-metadata-example1.json b/sechub-doc/src/docs/asciidoc/documents/shared/snippet/pds-param-template-metadata-example1.json
new file mode 100644
index 0000000000..9f67da8c86
--- /dev/null
+++ b/sechub-doc/src/docs/asciidoc/documents/shared/snippet/pds-param-template-metadata-example1.json
@@ -0,0 +1,9 @@
+[ {
+ "templateId" : "single-singon",
+ "templateType" : "webscan-login",
+ "assetData" : {
+ "assetId" : "custom-webscan-setup",
+ "fileName" : "WEBSCAN_PRODUCT_ID.zip",
+ "checksum" : "434c6c6ec1b0ed9844149069d7d45ac18e72505b"
+ }
+} ]
\ No newline at end of file
diff --git a/sechub-doc/src/docs/asciidoc/documents/shared/snippet/pds-param-template-metadata-syntax.json b/sechub-doc/src/docs/asciidoc/documents/shared/snippet/pds-param-template-metadata-syntax.json
new file mode 100644
index 0000000000..85e400cf1b
--- /dev/null
+++ b/sechub-doc/src/docs/asciidoc/documents/shared/snippet/pds-param-template-metadata-syntax.json
@@ -0,0 +1,13 @@
+ [ //<1>
+ {
+ "templateId" : "templateId", //<2>
+ "templateType": "WEBSCAN_LOGIN", //<3>
+
+ "assetData" : { //<4>
+ "assetId" : "assetId", //<5>
+ "fileName" : "fileName", //<6>
+ "checksum" : "fileChecksum" //<7>
+ }
+
+ }
+ ]
diff --git a/sechub-doc/src/docs/asciidoc/documents/shared/snippet/template-definition-syntax.json b/sechub-doc/src/docs/asciidoc/documents/shared/snippet/template-definition-syntax.json
new file mode 100644
index 0000000000..b15de0e9fa
--- /dev/null
+++ b/sechub-doc/src/docs/asciidoc/documents/shared/snippet/template-definition-syntax.json
@@ -0,0 +1,26 @@
+{
+ "templateDefinition" : {
+
+ "id" : "$templateId", //<1>
+
+ "type" : "$templateType", //<2>
+
+ "assetId" : "$assetId", //<3>
+
+ "variables" : [ //<4>
+ {
+ "name" : "$variableName", // <5>
+ "optional": false, // <6>
+
+ "validation" : { // <7>
+
+ "minLength" : 1, // <8>
+ "maxLength" : 100, // <9>
+
+ "regularExpression" : "$regularExpression" // <10>
+ }
+ }
+ ]
+ }
+
+}
\ No newline at end of file
diff --git a/sechub-doc/src/docs/asciidoc/sechub-getting-started.adoc b/sechub-doc/src/docs/asciidoc/sechub-getting-started.adoc
index bfc703cf73..3cf24bcf26 100644
--- a/sechub-doc/src/docs/asciidoc/sechub-getting-started.adoc
+++ b/sechub-doc/src/docs/asciidoc/sechub-getting-started.adoc
@@ -186,9 +186,10 @@ Setup of GoSec complete:
Now you are ready to do scans!
-== Scan using SecHub client
-=== Install SecHub client
-The SecHub client is needed to scan. In later sections of this guide, the client is used to scan an example. The command below, will download the latest version and put it in your `/usr/local/bin` folder.
+== Scan using SecHub Client
+=== Install SecHub Client
+The SecHub Client is needed to scan. In later sections of this guide, the client is used to scan an example.
+The command below, will download the latest version and put it in your `/usr/local/bin` folder.
[source, bash]
--
@@ -196,24 +197,21 @@ The SecHub client is needed to scan. In later sections of this guide, the client
CLIENT_VERSION=`curl -s https://mercedes-benz.github.io/sechub/latest/client-download.html | grep https://github.com/mercedes-benz/sechub/ | awk -F '-' '{print $NF}' | sed 's/.zip">//'`
# Download the zipped binary
-curl -L -o sechub-cli.zip https://github.com/mercedes-benz/sechub/releases/download/v$CLIENT_VERSION-client/sechub-cli-$CLIENT_VERSION.zip
+wget https://github.com/mercedes-benz/sechub/releases/download/v$CLIENT_VERSION-client/sechub-cli-$CLIENT_VERSION.zip
# Verify the binary
-curl -L -o sechub-cli.zip.sha256 https://github.com/mercedes-benz/sechub/releases/download/v$CLIENT_VERSION-client/sechub-cli-$CLIENT_VERSION.zip.sha256
-sha256sum --check sechub-cli.zip.sha256
+wget https://github.com/mercedes-benz/sechub/releases/download/v$CLIENT_VERSION-client/sechub-cli-$CLIENT_VERSION.zip.sha256
+sha256sum --check sechub-cli-$CLIENT_VERSION.zip.sha256
# Extract
-unzip sechub-cli.zip
+unzip sechub-cli-$CLIENT_VERSION.zip
# Depending on your architecture and OS, you will have to copy a different binary file:
-# For linux x86-64
+# Example for Linux x86-64
sudo cp platform/linux-amd64/sechub /usr/local/bin
-# For linux arm-64
-sudo cp platform/linux-arm64/sechub /usr/local/bin
-
# Cleanup
-rm -rf sechub-cli.zip.sha256 sechub-cli.zip platform/
+rm -rf sechub-cli-$CLIENT_VERSION.zip sechub-cli-$CLIENT_VERSION.zip.sha256 platform/
--
=== Scan
@@ -249,10 +247,19 @@ Now you can do a scan, type `sechub scan`, this will create a file which contain
If you want the report in HTML format instead, add `-reportformat html` as an option: `sechub -reportformat html scan`
== Optional
-=== Install SecHub's VSCodium Plugin (OPTIONAL)
-SecHub's VSCodium Plugin helps you to work faster with the SecHub report.
-You can go to the exact code line and fix the problem.
+=== SecHub's IDE plugins
+SecHub's IDE plugins help you to work faster with the SecHub report.
+You can jump to the exact code lines and fix the problem.
+
+==== Eclipse Plugin
+You can get the SecHub plugin the usual way from the https://marketplace.eclipse.org/content/sechub[Eclipse Marketplace].
+
+==== IntelliJ Plugin
+You can get the SecHub plugin the usual way from the https://plugins.jetbrains.com/plugin/23379-sechub[IntelliJ Marketplace].
+
+==== VS-Codium / Visual Studio Code Plugin
+You can get the plugin from the https://open-vsx.org/extension/mercedes-benz/sechub[OpenVSX Registry].
-You can get the plugin from https://open-vsx.org/extension/mercedes-benz/sechub[here].
+To install it, search for `sechub` in the Extensions manager and choose the one from "mercedes-benz".
-And to install it, open VSCodium and in the `Command Palette` (Usually can be opened with `Ctrl+Shift+P`) type `install vsix`, and in the pop-up menu, choose the plugin.
+For Visual Studio Code users: Download the Plugin and install it manually from file.
diff --git a/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/AsciidocGenerator.java b/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/AsciidocGenerator.java
index 58f90fed04..3d2019c74d 100644
--- a/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/AsciidocGenerator.java
+++ b/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/AsciidocGenerator.java
@@ -5,6 +5,8 @@
import java.io.IOException;
import java.util.Map;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
import org.apache.commons.io.FileUtils;
import org.slf4j.LoggerFactory;
@@ -347,10 +349,33 @@ public void fetchDomainMessagingParts() {
}
public void generateSecHubSystemPropertiesDescription(GenContext context) throws IOException {
- String text = propertiesGenerator.generate(getCollector().fetchMustBeDocumentParts(), context.sechubEnvVariableRegistry);
+ String text = propertiesGenerator.generate(getCollector().fetchMustBeDocumentParts(), context.sechubEnvVariableRegistry, createCustomPropertiesMap());
writer.writeTextToFile(context.systemProperitesFile, text);
}
+ /**
+ * Creates a map with custom properties for the system properties documentation
+ * This is a workaround for @ConfigurationProperties classes, since currently
+ * there is no support for auto-doc generation for these classes.
+ *
+ *
+ * TODO: This should be removed in the future
+ *
+ */
+ private Map> createCustomPropertiesMap() {
+ /* @formatter:off */
+ return Map.of("oauth2-jwt", new TreeSet<>(
+ Set.of(
+ new SystemPropertiesDescriptionGenerator.TableRow("sechub.security.oauth2.mode", null, "The OAuth2 mode to use for authentication. Must be either 'JWT' or 'OPAQUE_TOKEN'.", false),
+ new SystemPropertiesDescriptionGenerator.TableRow("sechub.security.oauth2.jwt.jwk-set-uri", null, "The URI to the JWK set of the identity provider. Required in 'JWT' mode.", false),
+ new SystemPropertiesDescriptionGenerator.TableRow("sechub.security.oauth2.opaque-token.introspection-uri", null, "Opaque token introspection endpoint of the identity provider. Required in 'OPAQUE_TOKEN' mode.", false),
+ new SystemPropertiesDescriptionGenerator.TableRow("sechub.security.oauth2.opaque-token.client-id", null, "Client ID to use when authenticating against the identity provider. Required in 'OPAQUE_TOKEN' mode.", false),
+ new SystemPropertiesDescriptionGenerator.TableRow("sechub.security.oauth2.opaque-token.client-secret", null, "Client secret to use when authenticating against the identity provider. Required in 'OPAQUE_TOKEN' mode.", false)
+ ))
+ );
+ /* @formatter:on */
+ }
+
public void generatePDSSystemPropertiesDescription(GenContext context) throws IOException {
String text = propertiesGenerator.generate(getCollector().fetchPDSMustBeDocumentParts(), context.pdsEnvVariableRegistry);
writer.writeTextToFile(context.pdsSystemProperitesFile, text);
diff --git a/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/spring/SystemPropertiesDescriptionGenerator.java b/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/spring/SystemPropertiesDescriptionGenerator.java
index aa05dbef87..5e261e76a2 100644
--- a/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/spring/SystemPropertiesDescriptionGenerator.java
+++ b/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/spring/SystemPropertiesDescriptionGenerator.java
@@ -25,16 +25,27 @@ public SystemPropertiesDescriptionGenerator() {
}
public String generate(List list, SecureEnvironmentVariableKeyValueRegistry registry) {
- return generate(list, registry, AcceptAllSpringValueFilter.INSTANCE);
+ return generate(list, registry, AcceptAllSpringValueFilter.INSTANCE, null);
}
- protected String generate(List list, SecureEnvironmentVariableKeyValueRegistry registry, SpringValueFilter filter) {
+ public String generate(List list, SecureEnvironmentVariableKeyValueRegistry registry,
+ Map> customPropertiesMap) {
+ return generate(list, registry, AcceptAllSpringValueFilter.INSTANCE, customPropertiesMap);
+ }
+
+ protected String generate(List list, SecureEnvironmentVariableKeyValueRegistry registry, SpringValueFilter filter,
+ Map> customPropertiesMap) {
if (list == null || list.isEmpty()) {
return "";
}
StringBuilder sb = new StringBuilder();
Map> rowMap = new TreeMap<>();
+
+ if (customPropertiesMap != null) {
+ rowMap.putAll(customPropertiesMap);
+ }
+
for (DocAnnotationData data : list) {
if (SimpleStringUtils.isEmpty(data.springValue)) {
continue;
@@ -94,12 +105,22 @@ private String buildTitle(String key) {
return "Scope '" + key + "'";
}
- class TableRow implements Comparable {
+ public static class TableRow implements Comparable {
String propertyKey;
String defaultValue;
String description;
boolean hasDefaultValue;
+ public TableRow() {
+ }
+
+ public TableRow(String propertyKey, String defaultValue, String description, boolean hasDefaultValue) {
+ this.propertyKey = propertyKey;
+ this.defaultValue = defaultValue;
+ this.description = description;
+ this.hasDefaultValue = hasDefaultValue;
+ }
+
@Override
public int compareTo(TableRow o) {
return getPropertyKey().compareTo(o.getPropertyKey());
@@ -116,7 +137,6 @@ public String getPropertyKey() {
public int hashCode() {
final int prime = 31;
int result = 1;
- result = prime * result + getOuterType().hashCode();
result = prime * result + ((defaultValue == null) ? 0 : defaultValue.hashCode());
result = prime * result + ((description == null) ? 0 : description.hashCode());
result = prime * result + ((propertyKey == null) ? 0 : propertyKey.hashCode());
@@ -132,8 +152,6 @@ public boolean equals(Object obj) {
if (getClass() != obj.getClass())
return false;
TableRow other = (TableRow) obj;
- if (!getOuterType().equals(other.getOuterType()))
- return false;
if (defaultValue == null) {
if (other.defaultValue != null)
return false;
@@ -151,9 +169,5 @@ public boolean equals(Object obj) {
return false;
return true;
}
-
- private SystemPropertiesDescriptionGenerator getOuterType() {
- return SystemPropertiesDescriptionGenerator.this;
- }
}
}
diff --git a/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/usecase/UseCaseRestDocModelDataCollector.java b/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/usecase/UseCaseRestDocModelDataCollector.java
index dd68855f7e..21c98cb548 100644
--- a/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/usecase/UseCaseRestDocModelDataCollector.java
+++ b/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/usecase/UseCaseRestDocModelDataCollector.java
@@ -192,8 +192,8 @@ private File scanForSpringRestDocGenFolder(UseCaseRestDocEntry entry) {
Maybe you
- forgot to implement the RESTDOC test for the usecase or for one of its variants
- used two differet names for the variant inside your test (annotation + code in test method)
- - forgot to add the documentation calls inside a RESTDOC test, or
- - you accidently used another class when calling UseCaseRestDoc.Factory.createPath(...) or
+ - forgot to add the documentation calls inside a RESTDOC test
+ - accidently used another class when calling UseCaseRestDoc.Factory.createPath(...)
- executed not `gradlew sechub-doc:test` before
Details:
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/ExampleFilesValidTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/ExampleFilesValidTest.java
index d7c8500f5c..eff696a547 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/ExampleFilesValidTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/ExampleFilesValidTest.java
@@ -11,9 +11,32 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
-import com.mercedesbenz.sechub.commons.model.*;
-import com.mercedesbenz.sechub.commons.model.login.*;
+import com.mercedesbenz.sechub.commons.model.ClientCertificateConfiguration;
+import com.mercedesbenz.sechub.commons.model.HTTPHeaderConfiguration;
+import com.mercedesbenz.sechub.commons.model.JSONConverter;
+import com.mercedesbenz.sechub.commons.model.JSONConverterException;
+import com.mercedesbenz.sechub.commons.model.SecHubDataConfiguration;
+import com.mercedesbenz.sechub.commons.model.SecHubScanConfiguration;
+import com.mercedesbenz.sechub.commons.model.SecHubSourceDataConfiguration;
+import com.mercedesbenz.sechub.commons.model.SecHubTimeUnit;
+import com.mercedesbenz.sechub.commons.model.SecHubWebScanApiConfiguration;
+import com.mercedesbenz.sechub.commons.model.SecHubWebScanApiType;
+import com.mercedesbenz.sechub.commons.model.SecHubWebScanConfiguration;
+import com.mercedesbenz.sechub.commons.model.WebScanDurationConfiguration;
+import com.mercedesbenz.sechub.commons.model.login.Action;
+import com.mercedesbenz.sechub.commons.model.login.ActionType;
+import com.mercedesbenz.sechub.commons.model.login.BasicLoginConfiguration;
+import com.mercedesbenz.sechub.commons.model.login.EncodingType;
+import com.mercedesbenz.sechub.commons.model.login.FormLoginConfiguration;
+import com.mercedesbenz.sechub.commons.model.login.Page;
+import com.mercedesbenz.sechub.commons.model.login.Script;
+import com.mercedesbenz.sechub.commons.model.login.TOTPHashAlgorithm;
+import com.mercedesbenz.sechub.commons.model.login.WebLoginConfiguration;
+import com.mercedesbenz.sechub.commons.model.login.WebLoginTOTPConfiguration;
+import com.mercedesbenz.sechub.commons.model.template.TemplateType;
import com.mercedesbenz.sechub.commons.pds.PDSDefaultParameterKeyConstants;
+import com.mercedesbenz.sechub.commons.pds.data.PDSTemplateMetaData;
+import com.mercedesbenz.sechub.commons.pds.data.PDSTemplateMetaData.PDSAssetData;
import com.mercedesbenz.sechub.pds.commons.core.config.PDSProductParameterDefinition;
import com.mercedesbenz.sechub.pds.commons.core.config.PDSProductParameterSetup;
import com.mercedesbenz.sechub.pds.commons.core.config.PDSProductSetup;
@@ -63,8 +86,8 @@ void check_pds_config_example1_can_be_loaded_and_is_valid() throws Exception {
}
@ParameterizedTest
- @EnumSource(ExampleFile.class)
- void every_sechub_config_file_is_valid(ExampleFile file) {
+ @EnumSource(SecHubConfigExampleFile.class)
+ void every_sechub_config_file_is_valid(SecHubConfigExampleFile file) {
/* prepare */
String json = TestFileReader.readTextFromFile(file.getPath());
SecHubScanConfiguration config = null;
@@ -81,10 +104,10 @@ void every_sechub_config_file_is_valid(ExampleFile file) {
}
@ParameterizedTest
- @EnumSource(value = ExampleFile.class, names = { "WEBSCAN_ANONYMOUS", "WEBSCAN_BASIC_AUTH", "WEBSCAN_FORM_BASED_SCRIPT_AUTH",
+ @EnumSource(value = SecHubConfigExampleFile.class, names = { "WEBSCAN_ANONYMOUS", "WEBSCAN_BASIC_AUTH", "WEBSCAN_FORM_BASED_SCRIPT_AUTH",
"WEBSCAN_OPENAPI_WITH_DATA_REFERENCE", "WEBSCAN_HEADER_SCAN", "WEBSCAN_CLIENT_CERTIFICATE",
"WEBSCAN_FORM_BASED_SCRIPT_AUTH_WITH_TOTP" }, mode = EnumSource.Mode.INCLUDE)
- void every_sechub_config_webscan_file_is_valid_and_has_a_target_uri(ExampleFile file) {
+ void every_sechub_config_webscan_file_is_valid_and_has_a_target_uri(SecHubConfigExampleFile file) {
/* prepare */
String json = TestFileReader.readTextFromFile(file.getPath());
@@ -102,7 +125,7 @@ void every_sechub_config_webscan_file_is_valid_and_has_a_target_uri(ExampleFile
@Test
void webscan_anonymous_can_be_read_and_contains_expected_config() {
/* prepare */
- String json = TestFileReader.readTextFromFile(ExampleFile.WEBSCAN_ANONYMOUS.getPath());
+ String json = TestFileReader.readTextFromFile(SecHubConfigExampleFile.WEBSCAN_ANONYMOUS.getPath());
/* execute */
SecHubScanConfiguration config = SecHubScanConfiguration.createFromJSON(json);
@@ -121,7 +144,7 @@ void webscan_anonymous_can_be_read_and_contains_expected_config() {
@Test
void webscan_basic_auth_can_be_read_and_contains_expected_config() {
/* prepare */
- String json = TestFileReader.readTextFromFile(ExampleFile.WEBSCAN_BASIC_AUTH.getPath());
+ String json = TestFileReader.readTextFromFile(SecHubConfigExampleFile.WEBSCAN_BASIC_AUTH.getPath());
/* execute */
SecHubScanConfiguration config = SecHubScanConfiguration.createFromJSON(json);
@@ -144,7 +167,7 @@ void webscan_basic_auth_can_be_read_and_contains_expected_config() {
@Test
void webscan_form_based_script_auth_can_be_read_and_contains_expected_config() {
/* prepare */
- String json = TestFileReader.readTextFromFile(ExampleFile.WEBSCAN_FORM_BASED_SCRIPT_AUTH.getPath());
+ String json = TestFileReader.readTextFromFile(SecHubConfigExampleFile.WEBSCAN_FORM_BASED_SCRIPT_AUTH.getPath());
/* execute */
SecHubScanConfiguration config = SecHubScanConfiguration.createFromJSON(json);
@@ -168,7 +191,7 @@ void webscan_form_based_script_auth_can_be_read_and_contains_expected_config() {
@Test
void webscan_openapi_with_data_reference_can_be_read_and_contains_expected_config() {
/* prepare */
- String json = TestFileReader.readTextFromFile(ExampleFile.WEBSCAN_OPENAPI_WITH_DATA_REFERENCE.getPath());
+ String json = TestFileReader.readTextFromFile(SecHubConfigExampleFile.WEBSCAN_OPENAPI_WITH_DATA_REFERENCE.getPath());
/* execute */
SecHubScanConfiguration config = SecHubScanConfiguration.createFromJSON(json);
@@ -185,7 +208,7 @@ void webscan_openapi_with_data_reference_can_be_read_and_contains_expected_confi
@Test
void webscan_client_certificate_with_data_reference_can_be_read_and_contains_expected_config() {
/* prepare */
- String json = TestFileReader.readTextFromFile(ExampleFile.WEBSCAN_CLIENT_CERTIFICATE_WITH_OPENAPI.getPath());
+ String json = TestFileReader.readTextFromFile(SecHubConfigExampleFile.WEBSCAN_CLIENT_CERTIFICATE_WITH_OPENAPI.getPath());
/* execute */
SecHubScanConfiguration config = SecHubScanConfiguration.createFromJSON(json);
@@ -205,7 +228,7 @@ void webscan_client_certificate_with_data_reference_can_be_read_and_contains_exp
@Test
void webscan_client_certificate_with_openapi_can_be_read_and_contains_expected_config() {
/* prepare */
- String json = TestFileReader.readTextFromFile(ExampleFile.WEBSCAN_CLIENT_CERTIFICATE.getPath());
+ String json = TestFileReader.readTextFromFile(SecHubConfigExampleFile.WEBSCAN_CLIENT_CERTIFICATE.getPath());
/* execute */
SecHubScanConfiguration config = SecHubScanConfiguration.createFromJSON(json);
@@ -222,7 +245,7 @@ void webscan_client_certificate_with_openapi_can_be_read_and_contains_expected_c
@Test
void webscan_header_scan_can_be_read_and_contains_expected_config() {
/* prepare */
- String json = TestFileReader.readTextFromFile(ExampleFile.WEBSCAN_HEADER_SCAN.getPath());
+ String json = TestFileReader.readTextFromFile(SecHubConfigExampleFile.WEBSCAN_HEADER_SCAN.getPath());
/* execute */
SecHubScanConfiguration config = SecHubScanConfiguration.createFromJSON(json);
@@ -250,7 +273,7 @@ void webscan_header_scan_can_be_read_and_contains_expected_config() {
@Test
void webscan_header_from_data_reference_can_be_read_and_contains_expected_config() {
/* prepare */
- String json = TestFileReader.readTextFromFile(ExampleFile.WEBSCAN_HEADER_FROM_DATA_REFERENCE.getPath());
+ String json = TestFileReader.readTextFromFile(SecHubConfigExampleFile.WEBSCAN_HEADER_FROM_DATA_REFERENCE.getPath());
/* execute */
SecHubScanConfiguration config = SecHubScanConfiguration.createFromJSON(json);
@@ -283,7 +306,7 @@ void webscan_header_from_data_reference_can_be_read_and_contains_expected_config
@Test
void webscan_form_based_script_auth_with_totp_can_be_read_and_contains_expected_config() {
/* prepare */
- String json = TestFileReader.readTextFromFile(ExampleFile.WEBSCAN_FORM_BASED_SCRIPT_AUTH_WITH_TOTP.getPath());
+ String json = TestFileReader.readTextFromFile(SecHubConfigExampleFile.WEBSCAN_FORM_BASED_SCRIPT_AUTH_WITH_TOTP.getPath());
/* execute */
SecHubScanConfiguration config = SecHubScanConfiguration.createFromJSON(json);
@@ -307,6 +330,29 @@ void webscan_form_based_script_auth_with_totp_can_be_read_and_contains_expected_
assertEquals(60, totp.getValidityInSeconds());
assertEquals(8, totp.getTokenLength());
assertEquals(TOTPHashAlgorithm.HMAC_SHA256, totp.getHashAlgorithm());
+ assertEquals(EncodingType.BASE64, totp.getEncodingType());
+ }
+
+ @Test
+ void pds_param_template_metadata_array_syntax_example_is_valid() {
+ /* prepare */
+ String json = TestFileReader.readTextFromFile(PDSDataExampleFile.PDS_PARAM_TEMPLATE_META_DATA_SYNTAX.getPath());
+
+ /* execute */
+ List result = JSONConverter.get().fromJSONtoListOf(PDSTemplateMetaData.class, json);
+
+ /* test */
+ assertEquals(1, result.size());
+ PDSTemplateMetaData data = result.iterator().next();
+ assertEquals("templateId", data.getTemplateId());
+ assertEquals(TemplateType.WEBSCAN_LOGIN, data.getTemplateType());
+
+ PDSAssetData assetData = data.getAssetData();
+ assertNotNull(assetData);
+ assertEquals("assetId", assetData.getAssetId());
+ assertEquals("fileChecksum", assetData.getChecksum());
+ assertEquals("fileName", assetData.getFileName());
+
}
private void assertDefaultValue(PDSProductSetup setup, boolean isMandatory, String parameterKey, String expectedDefault) {
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/PDSDataExampleFile.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/PDSDataExampleFile.java
new file mode 100644
index 0000000000..b4dc4be87f
--- /dev/null
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/PDSDataExampleFile.java
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub;
+
+public enum PDSDataExampleFile {
+
+ PDS_PARAM_TEMPLATE_META_DATA_SYNTAX("src/docs/asciidoc/documents/shared/snippet/pds-param-template-metadata-syntax.json");
+ ;
+
+ private String path;
+
+ private PDSDataExampleFile(String path) {
+ this.path = path;
+ }
+
+ public String getPath() {
+ return path;
+ }
+}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/ExampleFile.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/SecHubConfigExampleFile.java
similarity index 96%
rename from sechub-doc/src/test/java/com/mercedesbenz/sechub/ExampleFile.java
rename to sechub-doc/src/test/java/com/mercedesbenz/sechub/SecHubConfigExampleFile.java
index 36bac1cf81..95ae922289 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/ExampleFile.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/SecHubConfigExampleFile.java
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
package com.mercedesbenz.sechub;
-public enum ExampleFile {
+public enum SecHubConfigExampleFile {
DATA_SECTION_EXAMPLE_1("src/docs/asciidoc/documents/shared/configuration/sechub_config_data_section_general_example1.json"),
@@ -45,7 +45,7 @@ public enum ExampleFile {
private String path;
- private ExampleFile(String path) {
+ private SecHubConfigExampleFile(String path) {
this.path = path;
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/docgen/AsciidocGeneratorTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/docgen/AsciidocGeneratorTest.java
index e45ad83beb..c5afc81a6f 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/docgen/AsciidocGeneratorTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/docgen/AsciidocGeneratorTest.java
@@ -29,6 +29,7 @@ public void before() throws Exception {
generatorToTest.writer = mock(DocGenTextFileWriter.class);
when(generatorToTest.propertiesGenerator.generate(any(), any())).thenReturn("properties-test");
+ when(generatorToTest.propertiesGenerator.generate(any(), any(), any())).thenReturn("properties-test");
when(generatorToTest.scheduleDescriptionGenerator.generate(generatorToTest.collector)).thenReturn("schedule-test");
}
@@ -69,7 +70,7 @@ public void calls_properties_generator_and_saves() throws Exception {
generatorToTest.generateSecHubSystemPropertiesDescription(genContext);
/* test */
- verify(generatorToTest.propertiesGenerator).generate(any(), any());
+ verify(generatorToTest.propertiesGenerator).generate(any(), any(), any());
verify(generatorToTest.writer).writeTextToFile(genContext.systemProperitesFile, "properties-test");
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AdminShowsScanLogsForProjectRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AdminShowsScanLogsForProjectRestDocTest.java
index 819cefd01e..756e76590f 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AdminShowsScanLogsForProjectRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AdminShowsScanLogsForProjectRestDocTest.java
@@ -22,11 +22,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -41,7 +40,6 @@
import com.mercedesbenz.sechub.domain.scan.log.ProjectScanLogSummary;
import com.mercedesbenz.sechub.domain.scan.log.ScanLogRestController;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.admin.project.UseCaseAdminShowsScanLogsForProject;
@@ -50,8 +48,9 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(ProjectAdministrationRestController.class)
-@ContextConfiguration(classes = { ScanLogRestController.class, AdminShowsScanLogsForProjectRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { ProjectAdministrationRestController.class, ScanLogRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles(Profiles.TEST)
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -123,10 +122,4 @@ public void restdoc_admin_downloads_scan_logs_for_project() throws Exception {
/* @formatter:on */
}
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
-
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AnonymousCheckAliveRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AnonymousCheckAliveRestDocTest.java
index cc759dc18f..51b2097c3d 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AnonymousCheckAliveRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AnonymousCheckAliveRestDocTest.java
@@ -11,11 +11,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
-import org.springframework.boot.test.context.TestConfiguration;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
@@ -25,7 +23,6 @@
import com.mercedesbenz.sechub.docgen.util.RestDocFactory;
import com.mercedesbenz.sechub.server.core.AnonymousCheckAliveRestController;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.anonymous.UseCaseAnonymousCheckAlive;
import com.mercedesbenz.sechub.test.ExampleConstants;
@@ -33,8 +30,9 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(AnonymousCheckAliveRestController.class)
-@ContextConfiguration(classes = { AnonymousCheckAliveRestController.class, AnonymousCheckAliveRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { AnonymousCheckAliveRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser
@ActiveProfiles(Profiles.TEST)
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -91,11 +89,4 @@ public void calling_check_alive_get_returns_HTTP_200() throws Exception {
/* @formatter:on */
}
-
- @TestConfiguration
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AnonymousSignupRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AnonymousSignupRestControllerRestDocTest.java
index 368506db41..baa234e2e5 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AnonymousSignupRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AnonymousSignupRestControllerRestDocTest.java
@@ -12,12 +12,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
-import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -30,7 +28,6 @@
import com.mercedesbenz.sechub.domain.administration.signup.AnonymousSignupRestController;
import com.mercedesbenz.sechub.domain.administration.signup.SignupJsonInputValidator;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.user.UseCaseUserSignup;
import com.mercedesbenz.sechub.sharedkernel.validation.ApiVersionValidationFactory;
@@ -41,9 +38,10 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(AnonymousSignupRestController.class)
+@WebMvcTest
@ContextConfiguration(classes = { AnonymousSignupRestController.class, SignupJsonInputValidator.class, UserIdValidationImpl.class,
- ApiVersionValidationFactory.class, EmailValidationImpl.class, AnonymousSignupRestControllerRestDocTest.SimpleTestConfiguration.class })
+ ApiVersionValidationFactory.class, EmailValidationImpl.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser
@ActiveProfiles(Profiles.TEST)
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -93,11 +91,4 @@ public void calling_with_api_1_0_and_valid_userid_and_email_returns_HTTP_200() t
/* @formatter:on */
}
- @TestConfiguration
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
-
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AnonymousUserGetAPITokenByOneTimeTokenRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AnonymousUserGetAPITokenByOneTimeTokenRestControllerRestDocTest.java
index 2308ffa3b3..7350f9c36c 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AnonymousUserGetAPITokenByOneTimeTokenRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AnonymousUserGetAPITokenByOneTimeTokenRestControllerRestDocTest.java
@@ -15,11 +15,10 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithAnonymousUser;
import org.springframework.test.context.ActiveProfiles;
@@ -31,7 +30,6 @@
import com.mercedesbenz.sechub.domain.administration.user.AnonymousUserGetAPITokenByOneTimeTokenService;
import com.mercedesbenz.sechub.domain.administration.user.AnonymousUserGetApiTokenByOneTimeTokenRestController;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.user.UseCaseUserClicksLinkToGetNewAPIToken;
import com.mercedesbenz.sechub.test.ExampleConstants;
@@ -39,9 +37,9 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@ExtendWith(SpringExtension.class)
-@WebMvcTest(AnonymousUserGetApiTokenByOneTimeTokenRestController.class)
-@ContextConfiguration(classes = { AnonymousUserGetApiTokenByOneTimeTokenRestController.class,
- AnonymousUserGetAPITokenByOneTimeTokenRestControllerRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { AnonymousUserGetApiTokenByOneTimeTokenRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@ActiveProfiles(Profiles.TEST)
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
public class AnonymousUserGetAPITokenByOneTimeTokenRestControllerRestDocTest implements TestIsNecessaryForDocumentation {
@@ -86,10 +84,4 @@ public void restdoc_user_clicks_link_to_get_NewApiToken() throws Exception {
/* @formatter:on */
}
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
-
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AnonymousUserRequestsNewApiTokenRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AnonymousUserRequestsNewApiTokenRestDocTest.java
index 30d6ae44be..9b790faf86 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AnonymousUserRequestsNewApiTokenRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AnonymousUserRequestsNewApiTokenRestDocTest.java
@@ -13,12 +13,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
-import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -30,7 +28,6 @@
import com.mercedesbenz.sechub.domain.administration.user.AnonymousUserRequestNewApiTokenRestController;
import com.mercedesbenz.sechub.domain.administration.user.AnonymousUserRequestsNewApiTokenService;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.user.UseCaseUserRequestsNewApiToken;
import com.mercedesbenz.sechub.test.ExampleConstants;
@@ -38,9 +35,9 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(AnonymousUserRequestNewApiTokenRestController.class)
-@ContextConfiguration(classes = { AnonymousUserRequestNewApiTokenRestController.class,
- AnonymousUserRequestsNewApiTokenRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { AnonymousUserRequestNewApiTokenRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser
@ActiveProfiles(Profiles.TEST)
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -82,11 +79,4 @@ public void calling_with_api_1_0_and_valid_userid_and_email_returns_HTTP_200() t
/* @formatter:on */
}
- @TestConfiguration
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
-
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AssetRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AssetRestControllerRestDocTest.java
new file mode 100644
index 0000000000..34fb89c4bc
--- /dev/null
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/AssetRestControllerRestDocTest.java
@@ -0,0 +1,295 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.restdoc;
+
+import static com.mercedesbenz.sechub.commons.core.CommonConstants.*;
+import static com.mercedesbenz.sechub.restdoc.RestDocumentation.*;
+import static com.mercedesbenz.sechub.test.RestDocPathParameter.*;
+import static com.mercedesbenz.sechub.test.SecHubTestURLBuilder.*;
+import static org.mockito.Mockito.*;
+import static org.springframework.restdocs.headers.HeaderDocumentation.*;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
+import static org.springframework.restdocs.payload.PayloadDocumentation.*;
+import static org.springframework.restdocs.request.RequestDocumentation.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.Import;
+import org.springframework.http.MediaType;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+
+import com.mercedesbenz.sechub.docgen.util.RestDocFactory;
+import com.mercedesbenz.sechub.docgen.util.RestDocTestFileSupport;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetDetailData;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetFileData;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetFileRepository;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetRestController;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetService;
+import com.mercedesbenz.sechub.sharedkernel.Profiles;
+import com.mercedesbenz.sechub.sharedkernel.logging.AuditLogService;
+import com.mercedesbenz.sechub.sharedkernel.logging.LogSanitizer;
+import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
+import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
+import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc.SpringRestDocOutput;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminDeletesAssetCompletely;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminDeletesOneFileFromAsset;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminDownloadsAssetFile;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminFetchesAssetDetails;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminFetchesAssetIds;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminUploadsAssetFile;
+import com.mercedesbenz.sechub.test.ExampleConstants;
+import com.mercedesbenz.sechub.test.TestIsNecessaryForDocumentation;
+import com.mercedesbenz.sechub.test.TestPortProvider;
+
+@RunWith(SpringRunner.class)
+@WebMvcTest
+@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
+@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
+@ContextConfiguration(classes = { AssetRestController.class, AssetRestControllerRestDocTest.class })
+@Import(TestRestDocSecurityConfiguration.class)
+@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
+public class AssetRestControllerRestDocTest implements TestIsNecessaryForDocumentation {
+
+ private static final String TEST_CHECKSUM1 = "c6965634c4ec8e4f5e72dffd36ea725860e8b485216260264a0973073805e422";
+
+ private static final int PORT_USED = TestPortProvider.DEFAULT_INSTANCE.getRestDocTestPort();
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @MockBean
+ private AssetFileRepository assetFileRepository;
+
+ @MockBean
+ AssetService assetService;
+
+ @MockBean
+ AuditLogService auditLogService;
+
+ @MockBean
+ LogSanitizer logSanitizer;
+
+ private static final String TEST_ASSET_ID1 = "asset-1";
+ private static final String TEST_ASSET_ID2 = "asset-2";
+
+ private static final String TEST_FILENAME1 = "PRODUCT1.zip";
+
+ @Before
+ public void before() {
+ }
+
+ @Test
+ @UseCaseRestDoc(useCase = UseCaseAdminDeletesOneFileFromAsset.class)
+ public void restdoc_admin_deletes_one_file_from_asset() throws Exception {
+ /* prepare */
+ String apiEndpoint = https(PORT_USED).buildAdminDeletesAssetFile(ASSET_ID.pathElement(), FILE_NAME.pathElement());
+ Class extends Annotation> useCase = UseCaseAdminDeletesOneFileFromAsset.class;
+
+ /* execute + test @formatter:off */
+ this.mockMvc.perform(
+ delete(apiEndpoint, TEST_ASSET_ID1, TEST_FILENAME1).
+ header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
+ ).
+ andExpect(status().isOk()).
+ andDo(defineRestService().
+ with().
+ useCaseData(useCase).
+ tag(RestDocFactory.extractTag(apiEndpoint)).
+ requestSchema(OpenApiSchema.ASSETS.getSchema()).
+ and().
+ document(
+ pathParameters(
+ parameterWithName(ASSET_ID.paramName()).description("The asset identifier"),
+ parameterWithName(FILE_NAME.paramName()).description("The name of the file to delete inside the asset")
+ )
+ ));
+
+ /* @formatter:on */
+ }
+
+ @Test
+ @UseCaseRestDoc(useCase = UseCaseAdminDeletesAssetCompletely.class)
+ public void restdoc_admin_deletes_asset_completely() throws Exception {
+ /* prepare */
+ String apiEndpoint = https(PORT_USED).buildAdminDeletesAsset(ASSET_ID.pathElement());
+ Class extends Annotation> useCase = UseCaseAdminDeletesAssetCompletely.class;
+
+ /* execute + test @formatter:off */
+ this.mockMvc.perform(
+ delete(apiEndpoint, TEST_ASSET_ID1).
+ contentType(MediaType.APPLICATION_JSON_VALUE).
+ header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
+ ).
+ andExpect(status().isOk()).
+ andDo(defineRestService().
+ with().
+ useCaseData(useCase).
+ tag(RestDocFactory.extractTag(apiEndpoint)).
+ requestSchema(OpenApiSchema.ASSETS.getSchema()).
+ and().
+ document(
+ pathParameters(
+ parameterWithName(ASSET_ID.paramName()).description("The asset identifier for the asset which shall be deleted completely")
+ )
+ ));
+
+ /* @formatter:on */
+ }
+
+ @Test
+ @UseCaseRestDoc(useCase = UseCaseAdminFetchesAssetIds.class)
+ public void restdoc_admin_fetches_all_asset_ids() throws Exception {
+ /* prepare */
+ when(assetService.fetchAllAssetIds()).thenReturn(List.of(TEST_ASSET_ID1, TEST_ASSET_ID2));
+
+ String apiEndpoint = https(PORT_USED).buildAdminFetchesAllAssetIds();
+ Class extends Annotation> useCase = UseCaseAdminFetchesAssetIds.class;
+
+ /* execute + test @formatter:off */
+ this.mockMvc.perform(
+ get(apiEndpoint).
+ header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
+ ).
+ andExpect(status().isOk()).
+ andDo(defineRestService().
+ with().
+ useCaseData(useCase).
+ tag(RestDocFactory.extractTag(apiEndpoint)).
+ requestSchema(OpenApiSchema.ASSETS.getSchema()).
+ and().
+ document(
+ responseFields(
+ fieldWithPath("[]").description("Array contains all existing asset identifiers")
+ )
+ ));
+
+ /* @formatter:on */
+ }
+
+ @Test
+ @UseCaseRestDoc(useCase = UseCaseAdminFetchesAssetDetails.class)
+ public void restdoc_admin_fetches_asset_details() throws Exception {
+ AssetDetailData asset1Details = new AssetDetailData();
+ asset1Details.setAssetId(TEST_ASSET_ID1);
+ AssetFileData fileInfo = new AssetFileData();
+ fileInfo.setChecksum(TEST_CHECKSUM1);
+ fileInfo.setFileName(TEST_FILENAME1);
+ asset1Details.getFiles().add(fileInfo);
+ /* prepare */
+ when(assetService.fetchAssetDetails(TEST_ASSET_ID1)).thenReturn(asset1Details);
+
+ String apiEndpoint = https(PORT_USED).buildAdminFetchesAssetDetails(ASSET_ID.pathElement());
+ Class extends Annotation> useCase = UseCaseAdminFetchesAssetDetails.class;
+
+ /* execute + test @formatter:off */
+ this.mockMvc.perform(
+ get(apiEndpoint, TEST_ASSET_ID1).
+ contentType(MediaType.APPLICATION_JSON_VALUE).
+ header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
+ ).
+ andExpect(status().isOk()).
+ andDo(defineRestService().
+ with().
+ useCaseData(useCase).
+ tag(RestDocFactory.extractTag(apiEndpoint)).
+ requestSchema(OpenApiSchema.ASSETS.getSchema()).
+ and().
+ document(
+ responseFields(
+ fieldWithPath("assetId").description("The asset identifier"),
+ fieldWithPath("files[]").description("Array containing data about files from asset"),
+ fieldWithPath("files[].fileName").description("Name of file"),
+ fieldWithPath("files[].checksum").description("Checksum for file")
+ )
+ ));
+
+ /* @formatter:on */
+ }
+
+ @Test
+ @UseCaseRestDoc(useCase = UseCaseAdminUploadsAssetFile.class)
+ public void restDoc_admin_uploads_assetfile() throws Exception {
+ /* prepare */
+ String apiEndpoint = https(PORT_USED).buildAdminUploadsAssetFile(ASSET_ID.pathElement());
+ Class extends Annotation> useCase = UseCaseAdminUploadsAssetFile.class;
+
+ InputStream inputStreamTo = RestDocTestFileSupport.getTestfileSupport().getInputStreamTo("upload/zipfile_contains_only_test1.txt.zip");
+ MockMultipartFile file1 = new MockMultipartFile("file", inputStreamTo);
+ /* execute + test @formatter:off */
+ this.mockMvc.perform(
+ multipart(apiEndpoint, TEST_ASSET_ID1).
+ file(file1).
+ queryParam(MULTIPART_CHECKSUM, TEST_CHECKSUM1)
+ ).
+ andExpect(status().isOk()).
+ andDo(defineRestService().
+ with().
+ useCaseData(useCase).
+ tag(RestDocFactory.extractTag(apiEndpoint)).
+ and().
+ document(
+ requestHeaders(
+ ),
+ pathParameters(
+ parameterWithName(ASSET_ID.paramName()).description("The id of the asset to which the uploaded file belongs to")
+ ),
+ queryParameters(
+ parameterWithName(MULTIPART_CHECKSUM).description("A sha256 checksum for file upload validation")
+ ),
+ requestParts(
+ partWithName(MULTIPART_FILE).description("The asset file to upload")
+ )
+ ));
+
+ /* @formatter:on */
+ }
+
+ @Test
+ @UseCaseRestDoc(useCase = UseCaseAdminDownloadsAssetFile.class, wanted = { SpringRestDocOutput.PATH_PARAMETERS, SpringRestDocOutput.REQUEST_FIELDS,
+ SpringRestDocOutput.CURL_REQUEST })
+ public void restdoc_admin_downloads_assetfile() throws Exception {
+ /* prepare */
+ String apiEndpoint = https(PORT_USED).buildAdminDownloadsAssetFile(ASSET_ID.pathElement(), FILE_NAME.pathElement());
+ Class extends Annotation> useCase = UseCaseAdminDownloadsAssetFile.class;
+
+ /* execute + test @formatter:off */
+ this.mockMvc.perform(
+ get(apiEndpoint,TEST_ASSET_ID1, TEST_FILENAME1).
+ contentType(MediaType.APPLICATION_JSON_VALUE).
+ header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
+ ).
+ andExpect(status().isOk()).
+ andDo(defineRestService().
+ with().
+ useCaseData(useCase).
+ tag(RestDocFactory.extractTag(apiEndpoint)).
+ responseSchema(OpenApiSchema.ASSETS.getSchema()).
+ and().
+ document(
+ requestHeaders(
+
+ ),
+ pathParameters(
+ parameterWithName(ASSET_ID.paramName()).description("The asset identifier"),
+ parameterWithName(FILE_NAME.paramName()).description("The name of the file to download from asset")
+ )
+ ));
+
+ /* @formatter:on */
+ }
+
+}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ConfigAdministrationRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ConfigAdministrationRestControllerRestDocTest.java
index 1ad8676f10..bef9b8e805 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ConfigAdministrationRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ConfigAdministrationRestControllerRestDocTest.java
@@ -14,11 +14,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -30,9 +29,7 @@
import com.mercedesbenz.sechub.domain.administration.autocleanup.AdministrationAutoCleanupConfig;
import com.mercedesbenz.sechub.domain.administration.config.AdministrationConfigService;
import com.mercedesbenz.sechub.domain.administration.config.ConfigAdministrationRestController;
-import com.mercedesbenz.sechub.domain.administration.scheduler.SchedulerAdministrationRestController;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminFetchesAutoCleanupConfiguration;
@@ -42,8 +39,9 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(SchedulerAdministrationRestController.class)
-@ContextConfiguration(classes = { ConfigAdministrationRestController.class, ConfigAdministrationRestControllerRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { ConfigAdministrationRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -122,10 +120,4 @@ public void restdoc_admin_fetches_auto_cleanup_configuration() throws Exception
/* @formatter:on */
}
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
-
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/DownloadsFullScanDataForJobRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/DownloadsFullScanDataForJobRestDocTest.java
index a853ca63dc..4f579de8b5 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/DownloadsFullScanDataForJobRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/DownloadsFullScanDataForJobRestDocTest.java
@@ -17,11 +17,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -30,7 +29,6 @@
import org.springframework.test.web.servlet.MockMvc;
import com.mercedesbenz.sechub.docgen.util.RestDocFactory;
-import com.mercedesbenz.sechub.domain.administration.project.ProjectAdministrationRestController;
import com.mercedesbenz.sechub.domain.scan.admin.FullScanData;
import com.mercedesbenz.sechub.domain.scan.admin.FullScanDataRestController;
import com.mercedesbenz.sechub.domain.scan.admin.FullScanDataService;
@@ -39,7 +37,6 @@
import com.mercedesbenz.sechub.sharedkernel.Profiles;
import com.mercedesbenz.sechub.sharedkernel.logging.AuditLogService;
import com.mercedesbenz.sechub.sharedkernel.logging.LogSanitizer;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc.SpringRestDocOutput;
@@ -49,8 +46,9 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(ProjectAdministrationRestController.class)
-@ContextConfiguration(classes = { FullScanDataRestController.class, DownloadsFullScanDataForJobRestDocTest.SimpleTestConfiguration.class, LogSanitizer.class })
+@WebMvcTest
+@ContextConfiguration(classes = { FullScanDataRestController.class, LogSanitizer.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles(Profiles.TEST)
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -123,10 +121,4 @@ public void restdoc_admin_downloads_fullscan_data_for_job() throws Exception {
/* @formatter:on */
}
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
-
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/EncryptionAdministrationRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/EncryptionAdministrationRestControllerRestDocTest.java
index e85a5223b3..7ce2d0dd7e 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/EncryptionAdministrationRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/EncryptionAdministrationRestControllerRestDocTest.java
@@ -22,6 +22,7 @@
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -43,7 +44,6 @@
import com.mercedesbenz.sechub.sharedkernel.encryption.SecHubEncryptionDataValidator;
import com.mercedesbenz.sechub.sharedkernel.encryption.SecHubEncryptionStatus;
import com.mercedesbenz.sechub.sharedkernel.encryption.SecHubPasswordSource;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.encryption.UseCaseAdminFetchesEncryptionStatus;
@@ -54,10 +54,11 @@
@RunWith(SpringRunner.class)
@WebMvcTest(JobAdministrationRestController.class)
-@ContextConfiguration(classes = { EncryptionAdministrationRestController.class, SecHubEncryptionDataValidator.class,
- EncryptionAdministrationRestControllerRestDocTest.SimpleTestConfiguration.class })
+@ContextConfiguration(classes = { EncryptionAdministrationRestController.class, SecHubEncryptionDataValidator.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
+@EnableAutoConfiguration
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
public class EncryptionAdministrationRestControllerRestDocTest implements TestIsNecessaryForDocumentation {
@@ -94,22 +95,22 @@ public void restdoc_admin_starts_encryption_rotation() throws Exception {
/* execute + test @formatter:off */
this.mockMvc.perform(
- post(apiEndpoint).
- contentType(MediaType.APPLICATION_JSON_VALUE).
- content(data.toFormattedJSON()).
- header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
+ post(apiEndpoint).
+ contentType(MediaType.APPLICATION_JSON_VALUE).
+ content(data.toFormattedJSON()).
+ header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
).
- andExpect(status().isOk()).
- andDo(defineRestService().
- with().
- useCaseData(useCase).
- tag(extractTag(apiEndpoint)).
- and().
- document(
- requestHeaders(
-
- )
- ));
+ andExpect(status().isOk()).
+ andDo(defineRestService().
+ with().
+ useCaseData(useCase).
+ tag(extractTag(apiEndpoint)).
+ and().
+ document(
+ requestHeaders(
+
+ )
+ ));
/* @formatter:on */
}
@@ -131,37 +132,37 @@ public void restdoc_admin_fetches_encryption_status() throws Exception {
String domains = SecHubEncryptionStatus.PROPERTY_DOMAINS+"[].";
String domainData = domains+SecHubDomainEncryptionStatus.PROPERTY_DATA+"[].";
- this.mockMvc.perform(
- get(apiEndpoint).
- contentType(MediaType.APPLICATION_JSON_VALUE).
- header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
- ).
- andExpect(status().isOk()).
- andDo(defineRestService().
- with().
- useCaseData(useCase).
- tag(extractTag(apiEndpoint)).
- responseSchema(OpenApiSchema.ENCRYPTION_STATUS.getSchema()).
- and().
- document(
- requestHeaders(
-
- ),
- responseFields(
- fieldWithPath(SecHubEncryptionStatus.PROPERTY_TYPE).description("The type description of the json content"),
- fieldWithPath(domains+SecHubDomainEncryptionStatus.PROPERTY_NAME).description("Name of the domain which will provide this encryption data elements"),
- fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_ID).description("Unique identifier"),
- fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_ALGORITHM).description("Algorithm used for encryption"),
- fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_PASSWORDSOURCE+"."+ SecHubPasswordSource.PROPERTY_TYPE).description("Type of password source. Can be "+List.of(SecHubCipherPasswordSourceType.values())),
- fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_PASSWORDSOURCE+"."+ SecHubPasswordSource.PROPERTY_DATA).description("Data for password source. If type is "+SecHubCipherPasswordSourceType.ENVIRONMENT_VARIABLE+" then it is the the name of the environment variable."),
- fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_USAGE).description("Map containing information about usage of this encryption"),
- fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_USAGE+".*").description("Key value data"),
- fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_CREATED).description("Creation timestamp"),
- fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_CREATED_FROM).description("User id of admin who created the encryption entry")
- )
- ));
-
- /* @formatter:on */
+ this.mockMvc.perform(
+ get(apiEndpoint).
+ contentType(MediaType.APPLICATION_JSON_VALUE).
+ header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
+ ).
+ andExpect(status().isOk()).
+ andDo(defineRestService().
+ with().
+ useCaseData(useCase).
+ tag(extractTag(apiEndpoint)).
+ responseSchema(OpenApiSchema.ENCRYPTION_STATUS.getSchema()).
+ and().
+ document(
+ requestHeaders(
+
+ ),
+ responseFields(
+ fieldWithPath(SecHubEncryptionStatus.PROPERTY_TYPE).description("The type description of the json content"),
+ fieldWithPath(domains+SecHubDomainEncryptionStatus.PROPERTY_NAME).description("Name of the domain which will provide this encryption data elements"),
+ fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_ID).description("Unique identifier"),
+ fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_ALGORITHM).description("Algorithm used for encryption"),
+ fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_PASSWORDSOURCE+"."+ SecHubPasswordSource.PROPERTY_TYPE).description("Type of password source. Can be "+List.of(SecHubCipherPasswordSourceType.values())),
+ fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_PASSWORDSOURCE+"."+ SecHubPasswordSource.PROPERTY_DATA).description("Data for password source. If type is "+SecHubCipherPasswordSourceType.ENVIRONMENT_VARIABLE+" then it is the the name of the environment variable."),
+ fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_USAGE).description("Map containing information about usage of this encryption"),
+ fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_USAGE+".*").description("Key value data"),
+ fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_CREATED).description("Creation timestamp"),
+ fieldWithPath(domainData+SecHubDomainEncryptionData.PROPERTY_CREATED_FROM).description("User id of admin who created the encryption entry")
+ )
+ ));
+
+ /* @formatter:on */
}
private SecHubEncryptionStatus createEncryptionStatusExample() {
@@ -188,9 +189,4 @@ private SecHubEncryptionStatus createEncryptionStatusExample() {
return status;
}
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
-
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/FalsePositiveRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/FalsePositiveRestControllerRestDocTest.java
index 755823b43c..a77d3c65d9 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/FalsePositiveRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/FalsePositiveRestControllerRestDocTest.java
@@ -25,11 +25,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.security.test.context.support.WithMockUser;
@@ -45,7 +44,6 @@
import com.mercedesbenz.sechub.domain.scan.project.*;
import com.mercedesbenz.sechub.domain.scan.report.ScanReportRepository;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.security.UserContextService;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
@@ -59,8 +57,9 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(FalsePositiveRestController.class)
-@ContextConfiguration(classes = { FalsePositiveRestController.class, FalsePositiveRestControllerRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { FalsePositiveRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -158,7 +157,7 @@ public void restdoc_mark_false_positives() throws Exception {
fieldWithPath(PROPERTY_JOBDATA+"[]."+ PROPERTY_FINDINGID).description("SecHub finding identifier - identifies problem inside the job which shall be markeda as a false positive."),
fieldWithPath(PROPERTY_JOBDATA+"[]."+ FalsePositiveJobData.PROPERTY_COMMENT).optional().description("A comment describing why this is a false positive"),
- fieldWithPath(PROPERTY_PROJECTDATA).description("Porject data list containing false positive setup for the project"),
+ fieldWithPath(PROPERTY_PROJECTDATA).description("Project data list containing false positive setup for the project"),
fieldWithPath(PROPERTY_PROJECTDATA+"[]."+ PROPERTY_ID).description("Identifier which is used to update or remove the respective false positive entry."),
fieldWithPath(PROPERTY_PROJECTDATA+"[]."+ FalsePositiveProjectData.PROPERTY_COMMENT).optional().description("A comment describing why this is a false positive."),
fieldWithPath(PROPERTY_PROJECTDATA+"[]."+ PROPERTY_WEBSCAN).optional().description("Defines a section for false positives which occur during webscans."),
@@ -353,7 +352,7 @@ public void user_fetches_false_positive_configuration() throws Exception {
fieldWithPath(PROPERTY_FALSE_POSITIVES+"[]."+FalsePositiveEntry.PROPERTY_JOBDATA+"."+PROPERTY_FINDINGID).description("SecHub finding identifier - identifies problem inside the job which shall be markeda as a false positive. *ATTENTION*: at the moment only code scan false positive handling is supported. Infra and web scan findings will lead to a non accepted error!"),
fieldWithPath(PROPERTY_FALSE_POSITIVES+"[]."+FalsePositiveEntry.PROPERTY_JOBDATA+"."+FalsePositiveJobData.PROPERTY_COMMENT).optional().description("A comment from author describing why this was marked as a false positive"),
- fieldWithPath(PROPERTY_FALSE_POSITIVES+"[]."+PROPERTY_PROJECTDATA).optional().description("Porject data list containing false positive setup for the project."),
+ fieldWithPath(PROPERTY_FALSE_POSITIVES+"[]."+PROPERTY_PROJECTDATA).optional().description("Project data list containing false positive setup for the project."),
fieldWithPath(PROPERTY_FALSE_POSITIVES+"[]."+PROPERTY_PROJECTDATA+"."+ PROPERTY_ID).description("Identifier which is used to update or remove the respective false positive entry."),
fieldWithPath(PROPERTY_FALSE_POSITIVES+"[]."+PROPERTY_PROJECTDATA+"."+ FalsePositiveProjectData.PROPERTY_COMMENT).optional().description("A comment describing why this is a false positive."),
fieldWithPath(PROPERTY_FALSE_POSITIVES+"[]."+PROPERTY_PROJECTDATA+"."+ PROPERTY_WEBSCAN).optional().description("Defines a section for false positives which occur during webscans."),
@@ -370,10 +369,4 @@ public void user_fetches_false_positive_configuration() throws Exception {
/* @formatter:on */
}
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
-
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/JobAdministrationRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/JobAdministrationRestControllerRestDocTest.java
index 9f1e1bc123..79791039db 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/JobAdministrationRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/JobAdministrationRestControllerRestDocTest.java
@@ -21,10 +21,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -40,7 +40,6 @@
import com.mercedesbenz.sechub.domain.administration.job.JobRestartRequestService;
import com.mercedesbenz.sechub.domain.administration.job.JobStatus;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.job.UseCaseAdminCancelsJob;
@@ -52,8 +51,9 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(JobAdministrationRestController.class)
-@ContextConfiguration(classes = { JobAdministrationRestController.class, JobAdministrationRestControllerRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { JobAdministrationRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -230,9 +230,4 @@ private static String inArray(String field) {
return "[]." + field;
}
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
-
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/MappingAdministrationRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/MappingAdministrationRestControllerRestDocTest.java
index f421f8b05f..d512589bce 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/MappingAdministrationRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/MappingAdministrationRestControllerRestDocTest.java
@@ -18,11 +18,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -36,7 +34,6 @@
import com.mercedesbenz.sechub.domain.administration.status.StatusAdministrationRestController;
import com.mercedesbenz.sechub.domain.administration.status.StatusEntry;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.admin.status.UseCaseAdminListsStatusInformation;
@@ -45,8 +42,8 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(StatusAdministrationRestController.class)
-@ContextConfiguration(classes = { StatusAdministrationRestController.class, MappingAdministrationRestControllerRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { StatusAdministrationRestController.class })
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -121,10 +118,4 @@ public void restdoc_admin_lists_status_information() throws Exception {
/* @formatter:on */
}
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
-
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/OpenApiSchema.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/OpenApiSchema.java
index 70ac1df00c..b2ddd75914 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/OpenApiSchema.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/OpenApiSchema.java
@@ -64,6 +64,10 @@ enum OpenApiSchema {
ENCRYPTION_STATUS("EncryptionStatus"),
+ TEMPLATES("Templates"),
+
+ ASSETS("Assets"),
+
;
private final Schema schema;
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProductExecutionProfileRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProductExecutionProfileRestControllerRestDocTest.java
index c9885de327..124a0565bc 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProductExecutionProfileRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProductExecutionProfileRestControllerRestDocTest.java
@@ -18,12 +18,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
-import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -50,7 +48,6 @@
import com.mercedesbenz.sechub.sharedkernel.ProductIdentifier;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
import com.mercedesbenz.sechub.sharedkernel.logging.AuditLogService;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminAssignsExecutionProfileToProject;
@@ -70,8 +67,9 @@
import com.mercedesbenz.sechub.test.executorconfig.TestExecutorSetupJobParam;
@RunWith(SpringRunner.class)
-@WebMvcTest(ProductExecutionProfileRestController.class)
-@ContextConfiguration(classes = { ProductExecutionProfileRestController.class, ProductExecutionProfileRestControllerRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { ProductExecutionProfileRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -437,10 +435,4 @@ public void restDoc_admin_fetches_profiles_list() throws Exception {
/* @formatter:on */
}
- @TestConfiguration
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProductExecutorConfigRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProductExecutorConfigRestControllerRestDocTest.java
index 3e168c113a..b42fbd925c 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProductExecutorConfigRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProductExecutorConfigRestControllerRestDocTest.java
@@ -19,12 +19,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
-import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -51,7 +49,6 @@
import com.mercedesbenz.sechub.sharedkernel.ProductIdentifier;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
import com.mercedesbenz.sechub.sharedkernel.logging.AuditLogService;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminCreatesExecutorConfiguration;
@@ -66,8 +63,9 @@
import com.mercedesbenz.sechub.test.executorconfig.TestExecutorSetupJobParam;
@RunWith(SpringRunner.class)
-@WebMvcTest(ProductExecutorConfigRestController.class)
-@ContextConfiguration(classes = { ProductExecutorConfigRestController.class, ProductExecutorConfigRestControllerRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { ProductExecutorConfigRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -365,11 +363,4 @@ public void restDoc_admin_fetches_executor_config_list() throws Exception {
/* @formatter:on */
}
-
- @TestConfiguration
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProjectAdministrationRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProjectAdministrationRestControllerRestDocTest.java
index 1a30bb2ff3..bcf9ba4b80 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProjectAdministrationRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProjectAdministrationRestControllerRestDocTest.java
@@ -1,25 +1,17 @@
// SPDX-License-Identifier: MIT
package com.mercedesbenz.sechub.restdoc;
-import static com.mercedesbenz.sechub.restdoc.RestDocumentation.defineRestService;
-import static com.mercedesbenz.sechub.test.RestDocPathParameter.PROJECT_ACCESS_LEVEL;
-import static com.mercedesbenz.sechub.test.RestDocPathParameter.PROJECT_ID;
-import static com.mercedesbenz.sechub.test.RestDocPathParameter.USER_ID;
-import static com.mercedesbenz.sechub.test.SecHubTestURLBuilder.https;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders;
-import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete;
-import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
-import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
-import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
-import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
-import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
-import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
-import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
-import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static com.mercedesbenz.sechub.restdoc.RestDocumentation.*;
+import static com.mercedesbenz.sechub.test.RestDocPathParameter.*;
+import static com.mercedesbenz.sechub.test.SecHubTestURLBuilder.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+import static org.springframework.restdocs.headers.HeaderDocumentation.*;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
+import static org.springframework.restdocs.payload.PayloadDocumentation.*;
+import static org.springframework.restdocs.request.RequestDocumentation.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.lang.annotation.Annotation;
import java.net.URI;
@@ -34,11 +26,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -63,15 +54,17 @@
import com.mercedesbenz.sechub.domain.administration.project.ProjectJsonInput.ProjectWhiteList;
import com.mercedesbenz.sechub.domain.administration.project.ProjectMetaDataEntity;
import com.mercedesbenz.sechub.domain.administration.project.ProjectRepository;
+import com.mercedesbenz.sechub.domain.administration.project.ProjectTemplateService;
import com.mercedesbenz.sechub.domain.administration.project.ProjectUnassignUserService;
import com.mercedesbenz.sechub.domain.administration.project.ProjectUpdateWhitelistService;
import com.mercedesbenz.sechub.domain.administration.user.User;
import com.mercedesbenz.sechub.server.SecHubWebMvcConfigurer;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
import com.mercedesbenz.sechub.sharedkernel.project.ProjectAccessLevel;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminAssignsTemplateToProject;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminUnassignsTemplateFromProject;
import com.mercedesbenz.sechub.sharedkernel.usecases.admin.project.UseCaseAdminChangesProjectAccessLevel;
import com.mercedesbenz.sechub.sharedkernel.usecases.admin.project.UseCaseAdminChangesProjectDescription;
import com.mercedesbenz.sechub.sharedkernel.usecases.admin.project.UseCaseAdminCreatesProject;
@@ -86,9 +79,9 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(ProjectAdministrationRestController.class)
-@ContextConfiguration(classes = { ProjectAdministrationRestController.class, ProjectAdministrationRestControllerRestDocTest.SimpleTestConfiguration.class,
- SecHubWebMvcConfigurer.class })
+@WebMvcTest
+@ContextConfiguration(classes = { ProjectAdministrationRestController.class, SecHubWebMvcConfigurer.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -135,6 +128,9 @@ public class ProjectAdministrationRestControllerRestDocTest implements TestIsNec
@MockBean
ProjectChangeAccessLevelService projectChangeAccessLevelService;
+ @MockBean
+ ProjectTemplateService projectTemplateService;
+
@Before
public void before() {
when(createProjectInputvalidator.supports(ProjectJsonInput.class)).thenReturn(true);
@@ -246,7 +242,7 @@ public void restdoc_delete_project() throws Exception {
),
pathParameters(
- parameterWithName(PROJECT_ID.paramName()).description("The id for project to delete")
+ parameterWithName(PROJECT_ID.paramName()).description("The project id to delete")
)
));
/* @formatter:on */
@@ -276,7 +272,7 @@ public void restdoc_change_project_owner() throws Exception {
),
pathParameters(
- parameterWithName(PROJECT_ID.paramName()).description("The id for project"),
+ parameterWithName(PROJECT_ID.paramName()).description("The project id"),
parameterWithName(USER_ID.paramName()).description("The user id of the user to assign to project as the owner")
)
));
@@ -324,7 +320,7 @@ public void restdoc_change_project_access_level() throws Exception {
),
pathParameters(
- parameterWithName(PROJECT_ID.paramName()).description("The id for project"),
+ parameterWithName(PROJECT_ID.paramName()).description("The project id"),
parameterWithName(PROJECT_ACCESS_LEVEL.paramName()).description("The new project access level. "+acceptedValues)
)
));
@@ -356,7 +352,7 @@ public void restdoc_assign_user2project() throws Exception {
),
pathParameters(
- parameterWithName(PROJECT_ID.paramName()).description("The id for project"),
+ parameterWithName(PROJECT_ID.paramName()).description("The project id"),
parameterWithName(USER_ID.paramName()).description("The user id of the user to assign to project")
)
));
@@ -388,7 +384,7 @@ public void restdoc_unassign_userFromProject() throws Exception {
),
pathParameters(
- parameterWithName(PROJECT_ID.paramName()).description("The id for project"),
+ parameterWithName(PROJECT_ID.paramName()).description("The project id"),
parameterWithName(USER_ID.paramName()).description("The user id of the user to unassign from project")
)
));
@@ -456,13 +452,14 @@ public void restdoc_show_project_details() throws Exception {
),
pathParameters(
- parameterWithName(PROJECT_ID.paramName()).description("The id for project to show details for")
+ parameterWithName(PROJECT_ID.paramName()).description("The project id to show details for")
),
responseFields(
fieldWithPath(ProjectDetailInformation.PROPERTY_PROJECT_ID).description("The name of the project"),
fieldWithPath(ProjectDetailInformation.PROPERTY_USERS).description("A list of all users having access to the project"),
fieldWithPath(ProjectDetailInformation.PROPERTY_OWNER).description("Username of the owner of this project. An owner is the person in charge."),
fieldWithPath(ProjectDetailInformation.PROPERTY_WHITELIST).description("A list of all whitelisted URIs. Only these ones can be scanned for the project!"),
+ fieldWithPath(ProjectDetailInformation.PROPERTY_TEMPLATE_IDS).description("A list of all templates assigned to the project"),
fieldWithPath(ProjectDetailInformation.PROPERTY_METADATA).description("An JSON object containing metadata key-value pairs defined for this project."),
fieldWithPath(ProjectDetailInformation.PROPERTY_METADATA + ".key1").description("An arbitrary metadata key"),
fieldWithPath(ProjectDetailInformation.PROPERTY_ACCESSLEVEL).description("The project access level"),
@@ -535,15 +532,17 @@ public void restdoc_change_project_description() throws Exception {
),
pathParameters(
- parameterWithName(PROJECT_ID.paramName()).description("The id for project to change details for")
+ parameterWithName(PROJECT_ID.paramName()).description("The project id to change details for")
),
responseFields(
fieldWithPath(ProjectDetailInformation.PROPERTY_PROJECT_ID).description("The name of the project."),
fieldWithPath(ProjectDetailInformation.PROPERTY_USERS).description("A list of all users having access to the project."),
fieldWithPath(ProjectDetailInformation.PROPERTY_OWNER).description("Username of the owner of this project. An owner is the person in charge."),
fieldWithPath(ProjectDetailInformation.PROPERTY_WHITELIST).description("A list of all whitelisted URIs. Only these ones can be scanned for the project!"),
+ fieldWithPath(ProjectDetailInformation.PROPERTY_TEMPLATE_IDS).description("A list of all templates assigned to the project"),
fieldWithPath(ProjectDetailInformation.PROPERTY_METADATA).description("An JSON object containing metadata key-value pairs defined for this project."),
- fieldWithPath(ProjectDetailInformation.PROPERTY_METADATA + ".key1").description("An arbitrary metadata key."),
+ fieldWithPath(ProjectDetailInformation.PROPERTY_METADATA + ".key1").description("An arbitrary metadata key"),
+ fieldWithPath(ProjectDetailInformation.PROPERTY_ACCESSLEVEL).description("The project access level"),
fieldWithPath(ProjectDetailInformation.PROPERTY_ACCESSLEVEL).description("The project access level"),
fieldWithPath(ProjectDetailInformation.PROPERTY_DESCRIPTION).description("The project description.")
)
@@ -552,10 +551,68 @@ public void restdoc_change_project_description() throws Exception {
/* @formatter:on */
}
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
+ @Test
+ @UseCaseRestDoc(useCase = UseCaseAdminAssignsTemplateToProject.class)
+ public void restdoc_assign_template2project() throws Exception {
+ /* prepare */
+ String apiEndpoint = https(PORT_USED).buildAdminAssignsTemplateToProjectUrl(TEMPLATE_ID.pathElement(), PROJECT_ID.pathElement());
+ Class extends Annotation> useCase = UseCaseAdminAssignsTemplateToProject.class;
+
+ /* execute + test @formatter:off */
+ mockMvc.perform(
+ put(apiEndpoint, "projectId1", "template1").
+ contentType(MediaType.APPLICATION_JSON_VALUE).
+ header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
+ ).
+ andExpect(status().isOk()).
+ andDo(defineRestService().
+ with().
+ useCaseData(useCase).
+ tag(RestDocFactory.extractTag(apiEndpoint)).
+ and().
+ document(
+ requestHeaders(
+ ),
+ pathParameters(
+ parameterWithName(PROJECT_ID.paramName()).description("The project id"),
+ parameterWithName(TEMPLATE_ID.paramName()).description("The id of the template to assign to project")
+ )
+ ));
+
+ /* @formatter:on */
+ }
+
+ @Test
+ @UseCaseRestDoc(useCase = UseCaseAdminUnassignsTemplateFromProject.class)
+ public void restdoc_unassign_templateFromproject() throws Exception {
+ /* prepare */
+ String apiEndpoint = https(PORT_USED).buildAdminUnAssignsTemplateToProjectUrl(TEMPLATE_ID.pathElement(), PROJECT_ID.pathElement());
+ Class extends Annotation> useCase = UseCaseAdminUnassignsTemplateFromProject.class;
+
+ /* execute + test @formatter:off */
+ mockMvc.perform(
+ delete(apiEndpoint, "projectId1", "template1").
+ contentType(MediaType.APPLICATION_JSON_VALUE).
+ header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
+ ).
+ andExpect(status().isOk()).
+ andDo(defineRestService().
+ with().
+ useCaseData(useCase).
+ tag(RestDocFactory.extractTag(apiEndpoint)).
+ and().
+ document(
+ requestHeaders(
+
+ ),
+ pathParameters(
+ parameterWithName(PROJECT_ID.paramName()).description("The project id"),
+ parameterWithName(TEMPLATE_ID.paramName()).description("The id of the template to unassign from project")
+ )
+ ));
+
+ /* @formatter:on */
}
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProjectRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProjectRestControllerRestDocTest.java
index 0e19c77d5c..b03ded0b61 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProjectRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProjectRestControllerRestDocTest.java
@@ -43,7 +43,7 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(ProjectRestController.class)
+@WebMvcTest
@ContextConfiguration(classes = { ProjectRestController.class })
@WithMockUser(roles = RoleConstants.ROLE_USER)
@ExtendWith(RestDocumentationExtension.class)
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProjectUpdateAdministrationRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProjectUpdateAdministrationRestControllerRestDocTest.java
index 072c1a2c0f..492bfb9baf 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProjectUpdateAdministrationRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ProjectUpdateAdministrationRestControllerRestDocTest.java
@@ -17,11 +17,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -38,7 +37,6 @@
import com.mercedesbenz.sechub.domain.administration.project.ProjectUpdateWhitelistService;
import com.mercedesbenz.sechub.domain.administration.project.UpdateProjectInputValidator;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.admin.project.UseCaseUpdateProjectMetaData;
@@ -48,9 +46,9 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(ProjectUpdateAdministrationRestController.class)
-@ContextConfiguration(classes = { ProjectUpdateAdministrationRestController.class,
- ProjectUpdateAdministrationRestControllerRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { ProjectUpdateAdministrationRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -154,10 +152,4 @@ public void restdoc_update_metadata_for_project() throws Exception {
/* @formatter:on */
}
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
-
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ScanProjectMockDataRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ScanProjectMockDataRestControllerRestDocTest.java
index af47f1c69c..c3272643ae 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ScanProjectMockDataRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ScanProjectMockDataRestControllerRestDocTest.java
@@ -14,11 +14,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
-import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -33,7 +32,6 @@
import com.mercedesbenz.sechub.domain.scan.project.ScanProjectMockDataConfigurationService;
import com.mercedesbenz.sechub.domain.scan.project.ScanProjectMockDataRestController;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.user.UseCaseUserDefinesProjectMockdata;
import com.mercedesbenz.sechub.sharedkernel.usecases.user.UseCaseUserRetrievesProjectMockdata;
@@ -43,8 +41,9 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(ScanProjectMockDataRestController.class)
-@ContextConfiguration(classes = { ScanProjectMockDataRestController.class, ScanProjectMockDataRestControllerRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { ScanProjectMockDataRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@ActiveProfiles({ Profiles.MOCKED_PRODUCTS, Profiles.TEST })
public class ScanProjectMockDataRestControllerRestDocTest implements TestIsNecessaryForDocumentation {
@@ -140,12 +139,6 @@ public void get_project_mock_configuration() throws Exception {
/* @formatter:on */
}
- @TestConfiguration
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
-
@Before
public void before() throws Exception {
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/SchedulerAdministrationRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/SchedulerAdministrationRestControllerRestDocTest.java
index dcb5773924..f87d1588d1 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/SchedulerAdministrationRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/SchedulerAdministrationRestControllerRestDocTest.java
@@ -13,11 +13,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -30,7 +29,6 @@
import com.mercedesbenz.sechub.domain.administration.scheduler.SwitchSchedulerJobProcessingService;
import com.mercedesbenz.sechub.domain.administration.scheduler.TriggerSchedulerStatusRefreshService;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.admin.schedule.UseCaseAdminDisablesSchedulerJobProcessing;
@@ -41,8 +39,9 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(SchedulerAdministrationRestController.class)
-@ContextConfiguration(classes = { SchedulerAdministrationRestController.class, SchedulerAdministrationRestControllerRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { SchedulerAdministrationRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -147,10 +146,4 @@ public void restdoc_admin_enables_scheduler_job_processing() throws Exception {
/* @formatter:on */
}
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
-
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/SchedulerRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/SchedulerRestControllerRestDocTest.java
index 97c1cea3b8..3ec02f1493 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/SchedulerRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/SchedulerRestControllerRestDocTest.java
@@ -3,17 +3,17 @@
import static com.mercedesbenz.sechub.commons.core.CommonConstants.*;
import static com.mercedesbenz.sechub.commons.model.SecHubConfigurationModel.*;
-import static com.mercedesbenz.sechub.commons.model.TestSecHubConfigurationBuilder.*;
-import static com.mercedesbenz.sechub.restdoc.RestDocumentation.*;
+import static com.mercedesbenz.sechub.commons.model.TestSecHubConfigurationBuilder.configureSecHub;
+import static com.mercedesbenz.sechub.restdoc.RestDocumentation.defineRestService;
import static com.mercedesbenz.sechub.test.RestDocPathParameter.*;
-import static com.mercedesbenz.sechub.test.SecHubTestURLBuilder.*;
+import static com.mercedesbenz.sechub.test.SecHubTestURLBuilder.https;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.restdocs.headers.HeaderDocumentation.*;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.restdocs.request.RequestDocumentation.*;
-import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.io.InputStream;
@@ -26,12 +26,10 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
-import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.restdocs.RestDocumentationExtension;
@@ -42,63 +40,30 @@
import org.springframework.util.StringUtils;
import com.mercedesbenz.sechub.commons.core.CommonConstants;
-import com.mercedesbenz.sechub.commons.model.ClientCertificateConfiguration;
-import com.mercedesbenz.sechub.commons.model.HTTPHeaderConfiguration;
-import com.mercedesbenz.sechub.commons.model.SecHubCodeScanConfiguration;
-import com.mercedesbenz.sechub.commons.model.SecHubConfigurationMetaData;
-import com.mercedesbenz.sechub.commons.model.SecHubDataConfiguration;
-import com.mercedesbenz.sechub.commons.model.SecHubDataConfigurationUsageByName;
-import com.mercedesbenz.sechub.commons.model.SecHubFileSystemConfiguration;
-import com.mercedesbenz.sechub.commons.model.SecHubInfrastructureScanConfiguration;
-import com.mercedesbenz.sechub.commons.model.SecHubSourceDataConfiguration;
-import com.mercedesbenz.sechub.commons.model.SecHubTimeUnit;
-import com.mercedesbenz.sechub.commons.model.SecHubWebScanApiConfiguration;
-import com.mercedesbenz.sechub.commons.model.SecHubWebScanApiType;
-import com.mercedesbenz.sechub.commons.model.SecHubWebScanConfiguration;
-import com.mercedesbenz.sechub.commons.model.TrafficLight;
-import com.mercedesbenz.sechub.commons.model.WebScanDurationConfiguration;
+import com.mercedesbenz.sechub.commons.model.*;
import com.mercedesbenz.sechub.commons.model.job.ExecutionResult;
import com.mercedesbenz.sechub.commons.model.job.ExecutionState;
-import com.mercedesbenz.sechub.commons.model.login.ActionType;
-import com.mercedesbenz.sechub.commons.model.login.FormLoginConfiguration;
-import com.mercedesbenz.sechub.commons.model.login.TOTPHashAlgorithm;
-import com.mercedesbenz.sechub.commons.model.login.WebLoginConfiguration;
-import com.mercedesbenz.sechub.commons.model.login.WebLoginTOTPConfiguration;
+import com.mercedesbenz.sechub.commons.model.login.*;
import com.mercedesbenz.sechub.docgen.util.RestDocFactory;
import com.mercedesbenz.sechub.docgen.util.RestDocTestFileSupport;
-import com.mercedesbenz.sechub.domain.schedule.ScheduleJobStatus;
-import com.mercedesbenz.sechub.domain.schedule.SchedulerApproveJobService;
-import com.mercedesbenz.sechub.domain.schedule.SchedulerBinariesUploadService;
-import com.mercedesbenz.sechub.domain.schedule.SchedulerCreateJobService;
-import com.mercedesbenz.sechub.domain.schedule.SchedulerGetJobStatusService;
-import com.mercedesbenz.sechub.domain.schedule.SchedulerRestController;
-import com.mercedesbenz.sechub.domain.schedule.SchedulerResult;
-import com.mercedesbenz.sechub.domain.schedule.SchedulerSourcecodeUploadService;
+import com.mercedesbenz.sechub.domain.schedule.*;
import com.mercedesbenz.sechub.domain.schedule.access.ScheduleAccess;
import com.mercedesbenz.sechub.domain.schedule.access.ScheduleAccess.ProjectAccessCompositeKey;
import com.mercedesbenz.sechub.domain.schedule.access.ScheduleAccessRepository;
-import com.mercedesbenz.sechub.domain.schedule.job.ScheduleSecHubJob;
-import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobInfoForUser;
-import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobInfoForUserListPage;
-import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobInfoForUserService;
-import com.mercedesbenz.sechub.domain.schedule.job.SecHubJobRepository;
+import com.mercedesbenz.sechub.domain.schedule.job.*;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
import com.mercedesbenz.sechub.sharedkernel.configuration.SecHubConfiguration;
import com.mercedesbenz.sechub.sharedkernel.configuration.SecHubConfigurationValidator;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.job.UseCaseUserListsJobsForProject;
-import com.mercedesbenz.sechub.sharedkernel.usecases.user.execute.UseCaseUserApprovesJob;
-import com.mercedesbenz.sechub.sharedkernel.usecases.user.execute.UseCaseUserChecksJobStatus;
-import com.mercedesbenz.sechub.sharedkernel.usecases.user.execute.UseCaseUserCreatesNewJob;
-import com.mercedesbenz.sechub.sharedkernel.usecases.user.execute.UseCaseUserUploadsBinaries;
-import com.mercedesbenz.sechub.sharedkernel.usecases.user.execute.UseCaseUserUploadsSourceCode;
+import com.mercedesbenz.sechub.sharedkernel.usecases.user.execute.*;
import com.mercedesbenz.sechub.test.ExampleConstants;
import com.mercedesbenz.sechub.test.TestIsNecessaryForDocumentation;
import com.mercedesbenz.sechub.test.TestPortProvider;
-@WebMvcTest(SchedulerRestController.class)
-@ContextConfiguration(classes = { SchedulerRestController.class, SchedulerRestControllerRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { SchedulerRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser
@ExtendWith(RestDocumentationExtension.class)
@ActiveProfiles(Profiles.TEST)
@@ -755,7 +720,7 @@ public void restDoc_userCreatesNewJob_webScan_login_form_script_and_totp_as_seco
webConfig().
addURI("https://localhost/mywebapp").
login("https://localhost/mywebapp/login").
- totp("example-seed", 30, TOTPHashAlgorithm.HMAC_SHA1, 6).
+ totp("example-seed", 30, TOTPHashAlgorithm.HMAC_SHA1, 6, EncodingType.BASE32).
formScripted("username1","password1").
createPage().
createAction().
@@ -817,6 +782,7 @@ public void restDoc_userCreatesNewJob_webScan_login_form_script_and_totp_as_seco
fieldWithPath(PROPERTY_WEB_SCAN+"."+SecHubWebScanConfiguration.PROPERTY_LOGIN+"."+WebLoginConfiguration.PROPERTY_TOTP+"."+WebLoginTOTPConfiguration.PROPERTY_VALIDITY_IN_SECONDS).description("The time in seconds the generated TOTP is valid. In most cases nothing is specified and the default of '"+WebLoginTOTPConfiguration.DEFAULT_VALIDITY_IN_SECONDS+"' seconds is used.").optional(),
fieldWithPath(PROPERTY_WEB_SCAN+"."+SecHubWebScanConfiguration.PROPERTY_LOGIN+"."+WebLoginConfiguration.PROPERTY_TOTP+"."+WebLoginTOTPConfiguration.PROPERTY_TOKEN_LENGTH).description("The length of the generated TOTP. In most cases nothing is specified and the default length '"+WebLoginTOTPConfiguration.DEFAULT_TOKEN_LENGTH+"' is used.").optional(),
fieldWithPath(PROPERTY_WEB_SCAN+"."+SecHubWebScanConfiguration.PROPERTY_LOGIN+"."+WebLoginConfiguration.PROPERTY_TOTP+"."+WebLoginTOTPConfiguration.PROPERTY_HASH_ALGORITHM).description("The hash algorithm to generate the TOTP. In most cases nothing is specified and the default hash algorithm '"+WebLoginTOTPConfiguration.DEFAULT_HASH_ALGORITHM+"' is used. Currently available values are: 'HMAC_SHA1', 'HMAC_SHA256', 'HMAC_SHA512'").optional(),
+ fieldWithPath(PROPERTY_WEB_SCAN+"."+SecHubWebScanConfiguration.PROPERTY_LOGIN+"."+WebLoginConfiguration.PROPERTY_TOTP+"."+WebLoginTOTPConfiguration.PROPERTY_ENCODING_TYPE).description("The encoding type of the 'seed'. The default value is '"+WebLoginTOTPConfiguration.DEFAULT_ENCODING_TYPE+"'. Currently available values are: 'BASE64', 'BASE32', 'HEX', 'PLAIN', 'AUTODETECT'").optional(),
fieldWithPath(PROPERTY_WEB_SCAN+"."+SecHubWebScanConfiguration.PROPERTY_LOGIN+".url").description("Login URL").optional(),
fieldWithPath(PROPERTY_WEB_SCAN+"."+SecHubWebScanConfiguration.PROPERTY_LOGIN+"."+FORM).description("form login definition").optional(),
fieldWithPath(PROPERTY_WEB_SCAN+"."+SecHubWebScanConfiguration.PROPERTY_LOGIN+"."+FORM+"."+SCRIPT).description("script").optional(),
@@ -1225,11 +1191,4 @@ public void before() {
when(sechubConfigurationValidator.supports(SecHubConfiguration.class)).thenReturn(true);
}
-
- @TestConfiguration
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ServerInfoAdministrationRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ServerInfoAdministrationRestControllerRestDocTest.java
index 7b8451a531..8ad177272a 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ServerInfoAdministrationRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/ServerInfoAdministrationRestControllerRestDocTest.java
@@ -15,11 +15,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -32,7 +31,6 @@
import com.mercedesbenz.sechub.server.core.ServerInfoAdministrationRestController;
import com.mercedesbenz.sechub.server.core.ServerRuntimeData;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.admin.status.UseCaseAdminFetchesServerRuntimeData;
@@ -41,9 +39,9 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(ServerInfoAdministrationRestController.class)
-@ContextConfiguration(classes = { ServerInfoAdministrationRestController.class,
- ServerInfoAdministrationRestControllerRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { ServerInfoAdministrationRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -95,9 +93,4 @@ public void restdoc_admin_get_server_version_as_Json() throws Exception {
/* @formatter:on */
}
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/SignupAdministrationRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/SignupAdministrationRestControllerRestDocTest.java
index d02576634e..db7eba9903 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/SignupAdministrationRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/SignupAdministrationRestControllerRestDocTest.java
@@ -19,11 +19,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -37,7 +36,6 @@
import com.mercedesbenz.sechub.domain.administration.signup.SignupDeleteService;
import com.mercedesbenz.sechub.domain.administration.signup.SignupRepository;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.admin.signup.UseCaseAdminDeletesSignup;
@@ -48,8 +46,9 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(SignupAdministrationRestController.class)
-@ContextConfiguration(classes = { SignupAdministrationRestController.class, SignupAdministrationRestControllerRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { SignupAdministrationRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -150,10 +149,4 @@ public void restdoc_delete_signup() throws Exception {
/* @formatter:on */
}
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
-
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/StatusAdministrationRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/StatusAdministrationRestControllerRestDocTest.java
index b59d5ed4dc..3c4d476e66 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/StatusAdministrationRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/StatusAdministrationRestControllerRestDocTest.java
@@ -18,11 +18,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
@@ -38,7 +37,6 @@
import com.mercedesbenz.sechub.domain.administration.mapping.UpdateMappingService;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
import com.mercedesbenz.sechub.sharedkernel.mapping.MappingIdentifier;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdmiUpdatesMappingConfiguration;
@@ -49,8 +47,9 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(MappingAdministrationRestController.class)
-@ContextConfiguration(classes = { MappingAdministrationRestController.class, MappingAdministrationRestControllerRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { MappingAdministrationRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -161,9 +160,4 @@ public void restdoc_admin_updates_mapping_configuration() throws Exception {
/* @formatter:on */
}
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/TemplateRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/TemplateRestControllerRestDocTest.java
new file mode 100644
index 0000000000..f9e84c6f30
--- /dev/null
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/TemplateRestControllerRestDocTest.java
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.restdoc;
+
+import static com.mercedesbenz.sechub.commons.model.template.TemplateDefinition.*;
+import static com.mercedesbenz.sechub.restdoc.RestDocumentation.*;
+import static com.mercedesbenz.sechub.test.RestDocPathParameter.*;
+import static com.mercedesbenz.sechub.test.SecHubTestURLBuilder.*;
+import static org.mockito.Mockito.*;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
+import static org.springframework.restdocs.payload.PayloadDocumentation.*;
+import static org.springframework.restdocs.request.RequestDocumentation.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.Import;
+import org.springframework.http.MediaType;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition.TemplateVariable;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition.TemplateVariableValidation;
+import com.mercedesbenz.sechub.commons.model.template.TemplateType;
+import com.mercedesbenz.sechub.docgen.util.RestDocFactory;
+import com.mercedesbenz.sechub.domain.scan.template.TemplateRepository;
+import com.mercedesbenz.sechub.domain.scan.template.TemplateRestController;
+import com.mercedesbenz.sechub.domain.scan.template.TemplateService;
+import com.mercedesbenz.sechub.sharedkernel.Profiles;
+import com.mercedesbenz.sechub.sharedkernel.logging.AuditLogService;
+import com.mercedesbenz.sechub.sharedkernel.logging.LogSanitizer;
+import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
+import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminCreatesOrUpdatesTemplate;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminDeletesTemplate;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminFetchesAllTemplateIds;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminFetchesTemplate;
+import com.mercedesbenz.sechub.test.ExampleConstants;
+import com.mercedesbenz.sechub.test.TestIsNecessaryForDocumentation;
+import com.mercedesbenz.sechub.test.TestPortProvider;
+
+@RunWith(SpringRunner.class)
+@WebMvcTest(TemplateRestController.class)
+@ContextConfiguration(classes = { TemplateRestController.class, TemplateRestControllerRestDocTest.class })
+@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
+@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
+@Import(TestRestDocSecurityConfiguration.class)
+@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
+public class TemplateRestControllerRestDocTest implements TestIsNecessaryForDocumentation {
+
+ private static final int PORT_USED = TestPortProvider.DEFAULT_INSTANCE.getRestDocTestPort();
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @MockBean
+ private TemplateRepository templateRepository;
+
+ @MockBean
+ TemplateService templateService;
+
+ @MockBean
+ AuditLogService auditLogService;
+
+ @MockBean
+ LogSanitizer logSanitizer;
+
+ private TemplateDefinition definition;
+
+ private TemplateVariable usernameVariable;
+
+ private TemplateVariable passwordVariable;
+
+ private static final String TEST_TEMPLATE_ID1 = "template1";
+ private static final String TEST_TEMPLATE_ID2 = "template2";
+
+ @Before
+ public void before() {
+ definition = new TemplateDefinition();
+ definition.setType(TemplateType.WEBSCAN_LOGIN);
+ definition.setAssetId("asset-id1");
+
+ usernameVariable = new TemplateVariable();
+ usernameVariable.setName("username");
+ TemplateVariableValidation usernameValidation = new TemplateVariableValidation();
+ usernameValidation.setMinLength(3);
+ usernameValidation.setMaxLength(15);
+ usernameValidation.setRegularExpression("[a-zA-Z0-9_-].*");
+
+ usernameVariable.setValidation(usernameValidation);
+
+ passwordVariable = new TemplateVariable();
+ passwordVariable.setName("password");
+ TemplateVariableValidation passwordValidation = new TemplateVariableValidation();
+ passwordValidation.setMaxLength(20);
+
+ passwordVariable.setValidation(passwordValidation);
+
+ definition.getVariables().add(usernameVariable);
+ }
+
+ @Test
+ @UseCaseRestDoc(useCase = UseCaseAdminCreatesOrUpdatesTemplate.class)
+ public void restdoc_admin_creates_or_updates_template() throws Exception {
+ /* prepare */
+ String apiEndpoint = https(PORT_USED).buildAdminCreatesOrUpdatesTemplate(TEMPLATE_ID.pathElement());
+ Class extends Annotation> useCase = UseCaseAdminCreatesOrUpdatesTemplate.class;
+
+ String content = definition.toFormattedJSON();
+
+ /* execute + test @formatter:off */
+ this.mockMvc.perform(
+ put(apiEndpoint, TEST_TEMPLATE_ID1).
+ contentType(MediaType.APPLICATION_JSON_VALUE).
+ content(content).
+ header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
+ ).
+ andExpect(status().isOk()).
+ andDo(defineRestService().
+ with().
+ useCaseData(useCase).
+ tag(RestDocFactory.extractTag(apiEndpoint)).
+ requestSchema(OpenApiSchema.TEMPLATES.getSchema()).
+ and().
+ document(
+ requestFields(
+ fieldWithPath(PROPERTY_TYPE).description("The template type. Must be be defined when a new template is created. An update will ignore changes of this property because the type is immutable! Currently supported types are: "+ TemplateType.values()),
+
+ fieldWithPath(PROPERTY_ASSET_ID).description("The asset id used by the template"),
+ fieldWithPath(PROPERTY_VARIABLES+"[]."+ TemplateVariable.PROPERTY_NAME).description("The variable name"),
+ fieldWithPath(PROPERTY_VARIABLES+"[]."+ TemplateVariable.PROPERTY_OPTIONAL).optional().description("Defines if the variable is optional. The default is false"),
+ fieldWithPath(PROPERTY_VARIABLES+"[]."+ TemplateVariable.PROPERTY_VALIDATION).optional().description("Defines a simple validation segment."),
+ fieldWithPath(PROPERTY_VARIABLES+"[]."+ TemplateVariable.PROPERTY_VALIDATION+"."+ TemplateVariableValidation.PROPERTY_MIN_LENGTH).optional().description("The minimum content length of this variable"),
+ fieldWithPath(PROPERTY_VARIABLES+"[]."+ TemplateVariable.PROPERTY_VALIDATION+"."+ TemplateVariableValidation.PROPERTY_MAX_LENGTH).optional().description("The maximum content length of this variable"),
+ fieldWithPath(PROPERTY_VARIABLES+"[]."+ TemplateVariable.PROPERTY_VALIDATION+"."+ TemplateVariableValidation.PROPERTY_REGULAR_EXPRESSION).optional().description("A regular expression which must match to accept the user input inside the variable")
+ ),
+ pathParameters(
+ parameterWithName(TEMPLATE_ID.paramName()).description("The (unique) template id")
+ )
+ ));
+
+ /* @formatter:on */
+ }
+
+ @Test
+ @UseCaseRestDoc(useCase = UseCaseAdminDeletesTemplate.class)
+ public void restdoc_admin_deletes_template() throws Exception {
+ /* prepare */
+ String apiEndpoint = https(PORT_USED).buildAdminDeletesTemplate(TEMPLATE_ID.pathElement());
+ Class extends Annotation> useCase = UseCaseAdminDeletesTemplate.class;
+
+ /* execute + test @formatter:off */
+ this.mockMvc.perform(
+ delete(apiEndpoint, TEST_TEMPLATE_ID1).
+ contentType(MediaType.APPLICATION_JSON_VALUE).
+ header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
+ ).
+ andExpect(status().isOk()).
+ andDo(defineRestService().
+ with().
+ useCaseData(useCase).
+ tag(RestDocFactory.extractTag(apiEndpoint)).
+ requestSchema(OpenApiSchema.TEMPLATES.getSchema()).
+ and().
+ document(
+ pathParameters(
+ parameterWithName(TEMPLATE_ID.paramName()).description("The (unique) template id")
+ )
+ ));
+
+ /* @formatter:on */
+ }
+
+ @Test
+ @UseCaseRestDoc(useCase = UseCaseAdminFetchesTemplate.class)
+ public void restdoc_admin_fetches_template() throws Exception {
+ /* prepare */
+ definition.setId(TEST_TEMPLATE_ID1); // to have this in result as well, for create/delete it was not necessary, but
+ // here we want it
+ when(templateService.fetchTemplateDefinition(TEST_TEMPLATE_ID1)).thenReturn(definition);
+
+ String apiEndpoint = https(PORT_USED).buildAdminFetchesTemplate(TEMPLATE_ID.pathElement());
+ Class extends Annotation> useCase = UseCaseAdminFetchesTemplate.class;
+
+ /* execute + test @formatter:off */
+ this.mockMvc.perform(
+ get(apiEndpoint, TEST_TEMPLATE_ID1).
+ contentType(MediaType.APPLICATION_JSON_VALUE).
+ header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
+ ).
+ andExpect(status().isOk()).
+ andDo(defineRestService().
+ with().
+ useCaseData(useCase).
+ tag(RestDocFactory.extractTag(apiEndpoint)).
+ requestSchema(OpenApiSchema.TEMPLATES.getSchema()).
+ and().
+ document(
+ responseFields(
+ fieldWithPath(PROPERTY_TYPE).description("The template type. Currently supported types are: "+ TemplateType.values()),
+
+ fieldWithPath(PROPERTY_ID).description("The (unique) template id"),
+ fieldWithPath(PROPERTY_ASSET_ID).description("The asset id used by the template"),
+ fieldWithPath(PROPERTY_VARIABLES+"[]."+ TemplateVariable.PROPERTY_NAME).description("The variable name"),
+ fieldWithPath(PROPERTY_VARIABLES+"[]."+ TemplateVariable.PROPERTY_OPTIONAL).optional().description("Defines if the variable is optional. The default is false"),
+ fieldWithPath(PROPERTY_VARIABLES+"[]."+ TemplateVariable.PROPERTY_VALIDATION).optional().description("Defines a simple validation segment."),
+ fieldWithPath(PROPERTY_VARIABLES+"[]."+ TemplateVariable.PROPERTY_VALIDATION+"."+ TemplateVariableValidation.PROPERTY_MIN_LENGTH).optional().description("The minimum content length of this variable"),
+ fieldWithPath(PROPERTY_VARIABLES+"[]."+ TemplateVariable.PROPERTY_VALIDATION+"."+ TemplateVariableValidation.PROPERTY_MAX_LENGTH).optional().description("The maximum content length of this variable"),
+ fieldWithPath(PROPERTY_VARIABLES+"[]."+ TemplateVariable.PROPERTY_VALIDATION+"."+ TemplateVariableValidation.PROPERTY_REGULAR_EXPRESSION).optional().description("A regular expression which must match to accept the user input inside the variable")
+ ),
+ pathParameters(
+ parameterWithName(TEMPLATE_ID.paramName()).description("The (unique) template id")
+ )
+ ));
+
+ /* @formatter:on */
+ }
+
+ @Test
+ @UseCaseRestDoc(useCase = UseCaseAdminFetchesAllTemplateIds.class)
+ public void restdoc_admin_fetches_templatelist() throws Exception {
+ /* prepare */
+ when(templateService.fetchAllTemplateIds()).thenReturn(List.of(TEST_TEMPLATE_ID1, TEST_TEMPLATE_ID2));
+
+ String apiEndpoint = https(PORT_USED).buildAdminFetchesTemplateList();
+ Class extends Annotation> useCase = UseCaseAdminFetchesAllTemplateIds.class;
+
+ /* execute + test @formatter:off */
+ this.mockMvc.perform(
+ get(apiEndpoint, TEST_TEMPLATE_ID1).
+ contentType(MediaType.APPLICATION_JSON_VALUE).
+ header(AuthenticationHelper.HEADER_NAME, AuthenticationHelper.getHeaderValue())
+ ).
+ andExpect(status().isOk()).
+ andDo(defineRestService().
+ with().
+ useCaseData(useCase).
+ tag(RestDocFactory.extractTag(apiEndpoint)).
+ requestSchema(OpenApiSchema.TEMPLATES.getSchema()).
+ and().
+ document(
+ responseFields(
+ fieldWithPath("[]").description("Array contains all existing template identifiers")
+ )
+ ));
+
+ /* @formatter:on */
+ }
+}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/TestRestDocSecurityConfiguration.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/TestRestDocSecurityConfiguration.java
new file mode 100644
index 0000000000..78f1d9c9a9
--- /dev/null
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/TestRestDocSecurityConfiguration.java
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.restdoc;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+import org.springframework.web.client.RestTemplate;
+
+import com.mercedesbenz.sechub.sharedkernel.security.SecHubSecurityConfiguration;
+
+@Import(SecHubSecurityConfiguration.class)
+class TestRestDocSecurityConfiguration {
+
+ @Bean
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+}
diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/UserAdministrationRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/UserAdministrationRestControllerRestDocTest.java
index ded75b8140..15f074a33d 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/UserAdministrationRestControllerRestDocTest.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/UserAdministrationRestControllerRestDocTest.java
@@ -21,11 +21,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Import;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
@@ -46,7 +45,6 @@
import com.mercedesbenz.sechub.domain.administration.user.UserListService;
import com.mercedesbenz.sechub.domain.administration.user.UserRevokeSuperAdminRightsService;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
import com.mercedesbenz.sechub.sharedkernel.usecases.UseCaseRestDoc;
import com.mercedesbenz.sechub.sharedkernel.usecases.admin.signup.UseCaseAdminAcceptsSignup;
@@ -64,8 +62,9 @@
import com.mercedesbenz.sechub.test.TestPortProvider;
@RunWith(SpringRunner.class)
-@WebMvcTest(UserAdministrationRestController.class)
-@ContextConfiguration(classes = { UserAdministrationRestController.class, UserAdministrationRestControllerRestDocTest.SimpleTestConfiguration.class })
+@WebMvcTest
+@ContextConfiguration(classes = { UserAdministrationRestController.class })
+@Import(TestRestDocSecurityConfiguration.class)
@WithMockUser(roles = RoleConstants.ROLE_SUPERADMIN)
@ActiveProfiles({ Profiles.TEST, Profiles.ADMIN_ACCESS })
@AutoConfigureRestDocs(uriScheme = "https", uriHost = ExampleConstants.URI_SECHUB_SERVER, uriPort = 443)
@@ -436,10 +435,4 @@ public void restdoc_show_user_details_for_email_address() throws Exception {
/* @formatter:on */
}
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
-
- }
-
}
diff --git a/sechub-integrationtest/pds/product-scripts/integrationtest-webscan.sh b/sechub-integrationtest/pds/product-scripts/integrationtest-webscan.sh
index e049d6a539..c89235bf74 100755
--- a/sechub-integrationtest/pds/product-scripts/integrationtest-webscan.sh
+++ b/sechub-integrationtest/pds/product-scripts/integrationtest-webscan.sh
@@ -16,6 +16,15 @@ info:PDS_SCAN_CONFIGURATION=$PDS_SCAN_CONFIGURATION
dumpPDSVariables
+
+# We added pds.config.templates.metadata.list as optional parameter here for testing
+# So we can dump the variable here - used in scenario12 integration test
+dumpVariable "PDS_CONFIG_TEMPLATE_METADATA_LIST"
+
+ASSET_FILE1="$PDS_JOB_EXTRACTED_ASSETS_FOLDER/webscan-login/testfile1.txt"
+TEST_CONTENT_FROM_ASSETFILE=$(cat $ASSET_FILE1)
+# Afterwards TEST_CONTENT_FROM_ASSETFILE=i am "testfile1.txt" for scenario12 integration tests
+dumpVariable "TEST_CONTENT_FROM_ASSETFILE"
if [[ "$PDS_TEST_KEY_VARIANTNAME" = "a" ]]; then
diff --git a/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/AsUser.java b/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/AsUser.java
index 1e26c38bf5..dd5d0c08cd 100644
--- a/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/AsUser.java
+++ b/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/AsUser.java
@@ -24,7 +24,10 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
+import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestClientException;
@@ -37,6 +40,9 @@
import com.mercedesbenz.sechub.commons.model.JSONConverter;
import com.mercedesbenz.sechub.commons.model.SecHubConfigurationModel;
import com.mercedesbenz.sechub.commons.model.TrafficLight;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition;
+import com.mercedesbenz.sechub.domain.administration.project.ProjectDetailInformation;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetDetailData;
import com.mercedesbenz.sechub.domain.scan.project.FalsePositiveProjectData;
import com.mercedesbenz.sechub.integrationtest.JSONTestSupport;
import com.mercedesbenz.sechub.integrationtest.internal.IntegrationTestContext;
@@ -1425,4 +1431,111 @@ public SecHubEncryptionStatus fetchEncryptionStatus() {
return SecHubEncryptionStatus.fromString(json);
}
+ public AsUser createOrUpdateTemplate(String templateId, TemplateDefinition definition) {
+ String url = getUrlBuilder().buildAdminCreatesOrUpdatesTemplate(templateId);
+ getRestHelper().putJSON(url, definition.toFormattedJSON());
+ return this;
+ }
+
+ public TemplateDefinition fetchTemplateDefinitionOrNull(String templateId) {
+ String url = getUrlBuilder().buildAdminFetchesTemplate(templateId);
+ try {
+ String json = getRestHelper().getJSON(url);
+ return TemplateDefinition.from(json);
+
+ } catch (HttpClientErrorException e) {
+ HttpStatusCode statusCode = e.getStatusCode();
+ if (statusCode.equals(HttpStatus.NOT_FOUND)) {
+ return null;
+ }
+ throw e;
+ }
+ }
+
+ public void assignTemplateToProject(String templateid, TestProject project) {
+ String url = getUrlBuilder().buildAdminAssignsTemplateToProjectUrl(templateid, project.getProjectId());
+ getRestHelper().put(url);
+ }
+
+ public void unassignTemplateFromProject(String templateid, TestProject project) {
+ String url = getUrlBuilder().buildAdminUnAssignsTemplateToProjectUrl(templateid, project.getProjectId());
+ getRestHelper().delete(url);
+ }
+
+ public ProjectDetailInformation fetchProjectDetailInformation(TestProject project) {
+ String url = getUrlBuilder().buildAdminFetchProjectInfoUrl(project.getProjectId());
+ String json = getRestHelper().getJSON(url);
+ ProjectDetailInformation result = JSONConverter.get().fromJSON(ProjectDetailInformation.class, json);
+ return result;
+ }
+
+ public void deleteTemplate(String templateId) {
+ String url = getUrlBuilder().buildAdminDeletesTemplate(templateId);
+ getRestHelper().delete(url);
+ }
+
+ public List fetchTemplateList() {
+ String url = getUrlBuilder().buildAdminFetchesTemplateList();
+ String json = getRestHelper().getJSON(url);
+ return JSONConverter.get().fromJSONtoListOf(String.class, json);
+ }
+
+ public AsUser uploadAssetFile(String assetId, File file) {
+ String url = getUrlBuilder().buildAdminUploadsAssetFile(assetId);
+ String checkSum = TestAPI.createSHA256Of(file);
+ /* @formatter:off */
+ autoDumper.execute(() -> getRestHelper().upload(url,file,checkSum)
+ );
+ /* @formatter:on */
+ return this;
+ }
+
+ public AsUser uploadAssetFiles(String assetId, File... files) {
+ for (File file : files) {
+ uploadAssetFile(assetId, file);
+ }
+ return this;
+ }
+
+ public File downloadAssetFile(String assetId, String fileName) {
+ String url = getUrlBuilder().buildAdminDownloadsAssetFile(assetId, fileName);
+ /* @formatter:off */
+ RequestCallback requestCallback = request -> request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
+
+ ResponseExtractor responseExtractor = response -> {
+ Path path = TestUtil.createTempFileInBuildSubFolder("assets/"+assetId, fileName);
+ Files.copy(response.getBody(), path, StandardCopyOption.REPLACE_EXISTING);
+ if (TestUtil.isDeletingTempFiles()) {
+ path.toFile().deleteOnExit();
+ }
+ return path.toFile();
+ };
+ RestTemplate template = getRestHelper().getTemplate();
+ File downloadedAssetFile = template.execute(url, HttpMethod.GET, requestCallback, responseExtractor);
+
+ return downloadedAssetFile;
+ }
+
+ public List fetchAllAssetIds() {
+ String url = getUrlBuilder().buildAdminFetchesAllAssetIds();
+ String json = getRestHelper().getJSON(url);
+ return JSONConverter.get().fromJSONtoListOf(String.class, json);
+ }
+
+ public AssetDetailData fetchAssetDetails(String assetId) {
+ String url = getUrlBuilder().buildAdminFetchesAssetDetails(assetId);
+ String json = getRestHelper().getJSON(url);
+ return JSONConverter.get().fromJSON(AssetDetailData.class, json);
+ }
+
+ public void deleteAssetFile(String assetId, String fileName) {
+ String url = getUrlBuilder().buildAdminDeletesAssetFile(assetId, fileName);
+ getRestHelper().delete(url);
+ }
+
+ public void deleteAsset(String assetId) {
+ String url = getUrlBuilder().buildAdminDeletesAsset(assetId);
+ getRestHelper().delete(url);
+ }
+
}
diff --git a/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/TestAPI.java b/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/TestAPI.java
index f83e330099..08289ece95 100644
--- a/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/TestAPI.java
+++ b/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/TestAPI.java
@@ -42,6 +42,7 @@
import com.mercedesbenz.sechub.commons.model.SecHubMessagesList;
import com.mercedesbenz.sechub.commons.pds.data.PDSJobStatusState;
import com.mercedesbenz.sechub.domain.scan.admin.FullScanData;
+import com.mercedesbenz.sechub.domain.scan.project.ScanProjectConfig;
import com.mercedesbenz.sechub.integrationtest.internal.DefaultTestExecutionProfile;
import com.mercedesbenz.sechub.integrationtest.internal.IntegrationTestContext;
import com.mercedesbenz.sechub.integrationtest.internal.IntegrationTestDefaultProfiles;
@@ -588,6 +589,17 @@ public static void executeUntilSuccessOrTimeout(TestExecutable testExecutable) {
return;
}
+ /**
+ * Tries to execute runnable with default maximum time and retry (4 times a 500
+ * milliseconds) Shortcut for
+ * executeRunnableAndAcceptAssertionsMaximumTimes(4,runnable, 500);
+ *
+ * @param runnable
+ */
+ public static void executeResilient(Runnable runnable) {
+ executeRunnableAndAcceptAssertionsMaximumTimes(4, runnable, 500);
+ }
+
public static void executeRunnableAndAcceptAssertionsMaximumTimes(int tries, Runnable runnable, int millisBeforeNextRetry) {
executeCallableAndAcceptAssertionsMaximumTimes(tries, () -> {
runnable.run();
@@ -1694,4 +1706,10 @@ public static boolean isSecHubTerminating() {
return getSuperAdminRestHelper().getBooleanFromURL(url);
}
+ public static List fetchScanProjectConfigurations(TestProject project) {
+ String url = getURLBuilder().buildIntegrationTestFetchScanProjectConfigurations(project.getProjectId());
+ String json = getSuperAdminRestHelper().getJSON(url);
+ return JSONConverter.get().fromJSONtoListOf(ScanProjectConfig.class, json);
+
+ }
}
diff --git a/sechub-integrationtest/src/main/resources/pds-config-integrationtest.json b/sechub-integrationtest/src/main/resources/pds-config-integrationtest.json
index 9b4651eaf0..79b6966008 100644
--- a/sechub-integrationtest/src/main/resources/pds-config-integrationtest.json
+++ b/sechub-integrationtest/src/main/resources/pds-config-integrationtest.json
@@ -5,7 +5,7 @@
"id" : "PDS_INTTEST_PRODUCT_CODESCAN",
"path" : "./../sechub-integrationtest/pds/product-scripts/integrationtest-codescan.sh",
"scanType" : "codeScan",
- "envWhitelist" : [ "INTEGRATIONTEST_SCRIPT_ENV_ACCEPTED"],
+ "envWhitelist" : [ "INTEGRATIONTEST_SCRIPT_ENV_ACCEPTED" ],
"description" : "This is only a fake code scan - used by integration tests. The code scan will just return data from uploaded zip file",
"parameters" : {
"mandatory" : [ {
@@ -71,6 +71,9 @@
"optional" : [ {
"key" : "pds.test.key.variantname",
"description" : "a parameter from configuration - will be different in each integration test config from sechub integration test server"
+ }, {
+ "key" : "pds.config.template.metadata.list",
+ "description" : "normally this parameter is NOT sent to script, but for testing we add this parameter, so we can check by TestAPI..."
} ]
}
}, {
@@ -209,7 +212,7 @@
"path" : "./../sechub-integrationtest/pds/product-scripts/integrationtest-prepare.sh",
"scanType" : "prepare",
"description" : "This is a fake prepare scan - used by integration tests.",
- "envWhitelist" : [ "PDS_STORAGE_*"],
+ "envWhitelist" : [ "PDS_STORAGE_*" ],
"parameters" : {
"optional" : [ {
"key" : "pds.test.key.variantname",
diff --git a/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario1/AssetScenario1IntTest.java b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario1/AssetScenario1IntTest.java
new file mode 100644
index 0000000000..f7417b9579
--- /dev/null
+++ b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario1/AssetScenario1IntTest.java
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.integrationtest.scenario1;
+
+import static com.mercedesbenz.sechub.integrationtest.api.TestAPI.*;
+import static com.mercedesbenz.sechub.integrationtest.api.TestAPI.as;
+import static org.assertj.core.api.Assertions.*;
+
+import java.io.File;
+import java.util.List;
+import java.util.UUID;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import org.springframework.web.client.HttpClientErrorException.NotFound;
+
+import com.mercedesbenz.sechub.domain.scan.asset.AssetDetailData;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetFileData;
+import com.mercedesbenz.sechub.integrationtest.api.IntegrationTestSetup;
+import com.mercedesbenz.sechub.integrationtest.api.TestAPI;
+import com.mercedesbenz.sechub.test.TestFileReader;
+
+public class AssetScenario1IntTest {
+
+ @Rule
+ public IntegrationTestSetup setup = IntegrationTestSetup.forScenario(Scenario1.class);
+
+ @Rule
+ public Timeout timeOut = Timeout.seconds(600);
+
+ @Before
+ public void before() {
+
+ }
+
+ @Test
+ public void asset_crud_operation_working_as_expected() {
+ /* ------- */
+ /* prepare */
+ /* ------- */
+ File uploadedFile1 = new File("./src/test/resources/asset/examples-1/asset1.txt");
+ File uploadedFile2 = new File("./src/test/resources/asset/examples-1/asset2.txt");
+
+ String assetId = "crud" + UUID.randomUUID().toString();
+
+ /* --------------- */
+ /* execute + test */
+ /* --------------- */
+ as(SUPER_ADMIN).uploadAssetFiles(assetId, uploadedFile1, uploadedFile2);
+
+ // fetch all asset ids
+ List allAssetIds = as(SUPER_ADMIN).fetchAllAssetIds();
+ assertThat(allAssetIds).contains(assetId);
+
+ /* download files */
+ File downloadedAssetFile1 = as(SUPER_ADMIN).downloadAssetFile(assetId, uploadedFile1.getName());
+
+ String output = TestFileReader.readTextFromFile(downloadedAssetFile1);
+ assertThat(output).isEqualTo("I am text file \"asset1.txt\"");
+
+ /* fetch asset details and check content is as expected */
+ AssetDetailData detailData = as(SUPER_ADMIN).fetchAssetDetails(assetId);
+ assertThat(detailData.getAssetId()).isEqualTo(assetId);
+
+ String checksum1 = TestAPI.createSHA256Of(uploadedFile1);
+ AssetFileData expectedInfo1 = new AssetFileData();
+ expectedInfo1.setChecksum(checksum1);
+ expectedInfo1.setFileName("asset1.txt");
+
+ String checksum2 = TestAPI.createSHA256Of(uploadedFile2);
+ AssetFileData expectedInfo2 = new AssetFileData();
+ expectedInfo2.setChecksum(checksum2);
+ expectedInfo2.setFileName("asset2.txt");
+
+ assertThat(detailData.getFiles()).contains(expectedInfo1, expectedInfo2).hasSize(2);
+
+ /* delete single file from asset */
+ as(SUPER_ADMIN).deleteAssetFile(assetId, "asset1.txt");
+
+ /* check asset still exists in list and details contain only asset2.txt */
+ assertThat(as(SUPER_ADMIN).fetchAllAssetIds()).contains(assetId);
+ assertThat(as(SUPER_ADMIN).fetchAssetDetails(assetId).getFiles()).containsOnly(expectedInfo2);
+
+ /*
+ * Upload asset 2 again, but with different content - we use other file from
+ * examples-2 instead of examples-1. Will override existing asset file.
+ */
+ File uploadedFile2changed = new File("./src/test/resources/asset/examples-2/asset2.txt");
+ String checksum2changed = TestAPI.createSHA256Of(uploadedFile2changed);
+ assertThat(checksum2changed).as("precondition-check that files are different").isNotEqualTo(checksum2);
+
+ as(SUPER_ADMIN).uploadAssetFile(assetId, uploadedFile2changed);
+
+ AssetFileData expectedInfo2Canged = new AssetFileData();
+ expectedInfo2Canged.setChecksum(checksum2changed);
+ expectedInfo2Canged.setFileName("asset2.txt");
+
+ assertThat(as(SUPER_ADMIN).fetchAssetDetails(assetId).getFiles()).containsOnly(expectedInfo2Canged);
+
+ output = TestFileReader.readTextFromFile(as(SUPER_ADMIN).downloadAssetFile(assetId, "asset2.txt"));
+ assertThat(output).isEqualTo("I am text file \"asset2.txt\" - but from folder example-2");
+
+ /* delete complete asset */
+ as(SUPER_ADMIN).deleteAsset(assetId);
+
+ assertThat(as(SUPER_ADMIN).fetchAllAssetIds()).doesNotContain(assetId);
+ assertThatThrownBy(() -> as(SUPER_ADMIN).fetchAssetDetails(assetId)).isInstanceOf(NotFound.class);
+ }
+
+}
diff --git a/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario1/TemplateScenario1IntTest.java b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario1/TemplateScenario1IntTest.java
new file mode 100644
index 0000000000..cc97b09d0c
--- /dev/null
+++ b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario1/TemplateScenario1IntTest.java
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.integrationtest.scenario1;
+
+import static com.mercedesbenz.sechub.integrationtest.api.TestAPI.*;
+import static com.mercedesbenz.sechub.integrationtest.api.TestAPI.as;
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition.TemplateVariable;
+import com.mercedesbenz.sechub.commons.model.template.TemplateType;
+import com.mercedesbenz.sechub.domain.scan.project.ScanProjectConfig;
+import com.mercedesbenz.sechub.domain.scan.project.ScanProjectConfigID;
+import com.mercedesbenz.sechub.integrationtest.api.IntegrationTestSetup;
+
+public class TemplateScenario1IntTest {
+
+ @Rule
+ public IntegrationTestSetup setup = IntegrationTestSetup.forScenario(Scenario1.class);
+
+ @Rule
+ public Timeout timeOut = Timeout.seconds(600);
+
+ private String templateId;
+
+ private TemplateDefinition createDefinition;
+
+ private TemplateDefinition definitionWithId;
+ private TemplateDefinition updateDefinition;
+
+ @Before
+ public void before() {
+
+ templateId = "template-1_" + System.nanoTime();
+
+ /* @formatter:off */
+ TemplateDefinition fullTemplateDefinition = TemplateDefinition.builder().
+ templateId(templateId).
+ templateType(TemplateType.WEBSCAN_LOGIN).
+ assetId("asset1").
+ build();
+ /* @formatter:on */
+ TemplateVariable usernameVariable = new TemplateVariable();
+ usernameVariable.setName("username");
+
+ TemplateVariable passwordVariable = new TemplateVariable();
+ passwordVariable.setName("password");
+
+ fullTemplateDefinition.getVariables().add(usernameVariable);
+ fullTemplateDefinition.getVariables().add(passwordVariable);
+
+ String fullTemplateDefinitionJson = fullTemplateDefinition.toFormattedJSON();
+ createDefinition = TemplateDefinition.from(fullTemplateDefinitionJson.replace(templateId, "does-not-matter-will-be-overriden"));
+
+ definitionWithId = TemplateDefinition.from(fullTemplateDefinitionJson);
+
+ updateDefinition = TemplateDefinition.from(fullTemplateDefinitionJson.replace(templateId, "will-not-be-changed-by-update"));
+ }
+
+ @Test
+ public void template_crud_test() {
+ /* prepare */
+ as(SUPER_ADMIN).createProject(Scenario1.PROJECT_1, SUPER_ADMIN); // not done in this scenario automatically
+
+ /* check preconditions */
+ assertTemplateNotInsideTemplateList();
+
+ /* execute + test */
+ assertTemplateCanBeCreated();
+
+ assertTemplateCanBeUpdated();
+
+ assertTemplateCanBeAssignedToProject();
+
+ assertTemplateCanBeUnassignedFromProject();
+
+ assertTemplateCanBeAssignedToProject();
+
+ assertTemplateCanBeDeletedAndAssignmentIsPurged();
+
+ assertTemplateCanBeRecreatedWithSameId();
+
+ assertTemplateCanBeAssignedToProject();
+
+ assertProjectDeleteDoesPurgeTemplateAssignment();
+
+ assertTemplateExistsInTemplateListAndCanBeFetched();
+
+ /*
+ * cleanup - we remove the re-created template finally to have no garbage after
+ * test
+ */
+ as(SUPER_ADMIN).deleteTemplate(templateId);
+
+ // check cleanup worked
+ assertTemplateNotInsideTemplateList();
+
+ }
+
+ private void assertTemplateNotInsideTemplateList() {
+ List templateIds = as(SUPER_ADMIN).fetchTemplateList();
+ executeResilient(() -> assertThat(templateIds).doesNotContain(templateId));
+ }
+
+ private void assertTemplateExistsInTemplateListAndCanBeFetched() {
+ // check template list still contains the test template */
+ executeResilient(() -> assertThat(as(SUPER_ADMIN).fetchTemplateList()).contains(templateId));
+ executeResilient(() -> assertThat(as(SUPER_ADMIN).fetchTemplateDefinitionOrNull(templateId)).isNotNull());
+ }
+
+ private void assertProjectDeleteDoesPurgeTemplateAssignment() {
+ /* execute 7 - delete project */
+ as(SUPER_ADMIN).deleteProject(Scenario1.PROJECT_1);
+
+ /* test 7 - configuration for project is removed */
+ executeResilient(() -> assertThat(fetchScanProjectConfigurations(Scenario1.PROJECT_1)).isEmpty());
+
+ }
+
+ private void assertTemplateCanBeRecreatedWithSameId() {
+ /* execute 6 - create template with same id again */
+ as(SUPER_ADMIN).createOrUpdateTemplate(templateId, createDefinition);
+
+ /* test 6 - template is recreated */
+ executeResilient(() -> assertThat(as(SUPER_ADMIN).fetchTemplateDefinitionOrNull(templateId)).isNotNull());
+ }
+
+ private void assertTemplateCanBeDeletedAndAssignmentIsPurged() {
+ /* execute 5 - delete template */
+ as(SUPER_ADMIN).deleteTemplate(templateId);
+
+ /* test 5.1 check delete unassigns template */
+ executeResilient(() -> assertThat(as(SUPER_ADMIN).fetchProjectDetailInformation(Scenario1.PROJECT_1).getTemplateIds()).contains(templateId));
+
+ /* test 5.2 check template no longer exists */
+ executeResilient(() -> assertThat(as(SUPER_ADMIN).fetchTemplateDefinitionOrNull(templateId)).isNull());
+ }
+
+ private void assertTemplateCanBeUnassignedFromProject() {
+ /* execute 4 - unassign */
+ as(SUPER_ADMIN).unassignTemplateFromProject(templateId, Scenario1.PROJECT_1);
+
+ /* test 4 - check assignment */
+ executeResilient(() -> assertThat(as(SUPER_ADMIN).fetchProjectDetailInformation(Scenario1.PROJECT_1).getTemplateIds()).isEmpty());
+ executeResilient(() -> assertThat(fetchScanProjectConfigurations(Scenario1.PROJECT_1)).isEmpty());
+ }
+
+ private void assertTemplateCanBeAssignedToProject() {
+
+ /* execute 3- assign */
+ as(SUPER_ADMIN).assignTemplateToProject(templateId, Scenario1.PROJECT_1);
+
+ /* test 3.1 - check assignment by project details in domain administration */
+ executeResilient(() -> assertThat(as(SUPER_ADMIN).fetchProjectDetailInformation(Scenario1.PROJECT_1).getTemplateIds()).contains(templateId));
+
+ /* test 3.2 - check project scan configuration in domain scan */
+ executeResilient(() -> {
+ List configurations = fetchScanProjectConfigurations(Scenario1.PROJECT_1);
+ assertThat(configurations).isNotEmpty().hasSize(1)
+ .contains(new ScanProjectConfig(ScanProjectConfigID.TEMPLATE_WEBSCAN_LOGIN, Scenario1.PROJECT_1.getProjectId()));
+ assertThat(configurations.iterator().next().getData()).isEqualTo(templateId);
+ });
+ }
+
+ private void assertTemplateCanBeUpdated() {
+ /* prepare 2 - update */
+ updateDefinition.setAssetId("asset2");
+
+ /* execute 2 - update */
+ as(SUPER_ADMIN).createOrUpdateTemplate(templateId, updateDefinition);
+
+ /* test 2 - update works */
+ executeResilient(() -> {
+ TemplateDefinition loadedTemplate = as(SUPER_ADMIN).fetchTemplateDefinitionOrNull(templateId);
+ assertThat(loadedTemplate.getAssetId()).isEqualTo("asset2");
+ assertThat(loadedTemplate.getType()).isEqualTo(TemplateType.WEBSCAN_LOGIN);
+ assertThat(loadedTemplate.getId()).isEqualTo(templateId);
+ });
+ }
+
+ private void assertTemplateCanBeCreated() {
+ /* execute 1 - create */
+ as(SUPER_ADMIN).createOrUpdateTemplate(templateId, createDefinition);
+
+ /* test 1 - created definition has content as expected and contains id */
+ executeResilient(
+ () -> assertThat(as(SUPER_ADMIN).fetchTemplateDefinitionOrNull(templateId).toFormattedJSON()).isEqualTo(definitionWithId.toFormattedJSON()));
+ }
+
+}
diff --git a/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario12/PDSWebScanJobScenario12IntTest.java b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario12/PDSWebScanJobScenario12IntTest.java
index 67a8317c98..f813483f04 100644
--- a/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario12/PDSWebScanJobScenario12IntTest.java
+++ b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario12/PDSWebScanJobScenario12IntTest.java
@@ -2,12 +2,16 @@
package com.mercedesbenz.sechub.integrationtest.scenario12;
import static com.mercedesbenz.sechub.integrationtest.api.TestAPI.*;
+import static com.mercedesbenz.sechub.integrationtest.api.TestAPI.as;
import static com.mercedesbenz.sechub.integrationtest.scenario12.Scenario12.*;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.*;
+import java.io.File;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -15,7 +19,18 @@
import org.junit.Test;
import org.junit.rules.Timeout;
-import com.mercedesbenz.sechub.commons.model.*;
+import com.mercedesbenz.sechub.commons.model.ClientCertificateConfiguration;
+import com.mercedesbenz.sechub.commons.model.HTTPHeaderConfiguration;
+import com.mercedesbenz.sechub.commons.model.JSONConverter;
+import com.mercedesbenz.sechub.commons.model.SecHubMessageType;
+import com.mercedesbenz.sechub.commons.model.SecHubScanConfiguration;
+import com.mercedesbenz.sechub.commons.model.SecHubWebScanApiConfiguration;
+import com.mercedesbenz.sechub.commons.model.SecHubWebScanApiType;
+import com.mercedesbenz.sechub.commons.model.SecHubWebScanConfiguration;
+import com.mercedesbenz.sechub.commons.model.Severity;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition.TemplateVariable;
+import com.mercedesbenz.sechub.commons.model.template.TemplateType;
import com.mercedesbenz.sechub.integrationtest.api.IntegrationTestSetup;
import com.mercedesbenz.sechub.integrationtest.api.TestProject;
import com.mercedesbenz.sechub.integrationtest.internal.IntegrationTestFileSupport;
@@ -35,18 +50,68 @@ public class PDSWebScanJobScenario12IntTest {
@Rule
public Timeout timeOut = Timeout.seconds(600);
+ /* @formatter:off
+ *
+ * This is test is a web scan integration test which
+ * tests multiple features.
+ *
+ * The test prepares
+ * - web scan in general with dedicated setup
+ * - uses a SecHub configuration with template data inside
+ * - creates an asset, creates a template which uses the asset, assigns template
+ *
+ * The tests checks following:
+ *
+ * - PDS web scan has expected info finding, with
+ * - given target URL
+ * - product level information
+ * - SecHub web configuration parts
+ *
+ * - PDS parameter for template meta data configuration is correct and transmitted to PDS
+ * The parameter "pds.config.template.metadata.list" is normally not available inside
+ * the scripts, but for testing we added the parameter inside server configuration so it
+ * will be added to script level and can be checked by TestAPI
+ *
+ * - PDS will download and extract the uploaded asset file automatically and the
+ * extracted content is available inside the test bash script (executed by PDS)
+ *
+ *
+ * @formatter:on
+ */
@Test
- public void pds_web_scan_has_expected_info_finding_with_given_target_url_and_product2_level_information_and_sechub_web_config_parts() {
+ public void pds_web_scan_can_be_executed_and_works() throws Exception {
/* @formatter:off */
/* prepare */
+ String assetId="asset-s12-pds-inttest-webscan";
+ File productZipFile = IntegrationTestFileSupport.getTestfileSupport().createFileFromResourcePath("/asset/scenario12/PDS_INTTEST_PRODUCT_WEBSCAN.zip");
+
String configurationAsJson = IntegrationTestFileSupport.getTestfileSupport().loadTestFile("sechub-integrationtest-webscanconfig-all-options.json");
SecHubScanConfiguration configuration = SecHubScanConfiguration.createFromJSON(configurationAsJson);
configuration.setProjectId("myTestProject");
TestProject project = PROJECT_1;
String targetURL = configuration.getWebScan().get().getUrl().toString();
- as(SUPER_ADMIN).updateWhiteListForProject(project, Arrays.asList(targetURL));
+
+ TemplateVariable userNameVariable = new TemplateVariable();
+ userNameVariable.setName("username");
+
+ TemplateVariable passwordVariable = new TemplateVariable();
+ passwordVariable.setName("password");
+
+ TemplateDefinition templateDefinition = new TemplateDefinition();
+ templateDefinition.setAssetId(assetId);
+ templateDefinition.setType(TemplateType.WEBSCAN_LOGIN);
+ templateDefinition.getVariables().add(userNameVariable);
+ templateDefinition.getVariables().add(passwordVariable);
+
+ String templateId = "template-scenario12-1";
+ as(SUPER_ADMIN).
+ updateWhiteListForProject(project, Arrays.asList(targetURL)).
+ uploadAssetFile(assetId, productZipFile).
+ createOrUpdateTemplate(templateId, templateDefinition).
+ assignTemplateToProject(templateId, project)
+ ;
/* execute */
UUID jobUUID = as(USER_1).withSecHubClient().startAsynchronScanFor(project, configuration).getJobUUID();
@@ -101,13 +166,13 @@ public void pds_web_scan_has_expected_info_finding_with_given_target_url_and_pro
assertTrue(includes.contains("/customer/<*>"));
assertTrue(excludes.contains("<*>/admin/<*>"));
- // config must contain the expected headers
+ // web configuration must contain the expected headers
assertExpectedHeaders(webConfiguration);
- // config must contain the expected client certificate
+ // web configuration must contain the expected client certificate
assertExpectedClientCertificate(webConfiguration);
- // config must contain the expected openApi definition
+ // web configuration must contain the expected openApi definition
assertExpectedOpenApiDefinition(webConfiguration);
/* additional testing : messages*/
@@ -119,6 +184,14 @@ public void pds_web_scan_has_expected_info_finding_with_given_target_url_and_pro
hasMessage(SecHubMessageType.ERROR,"error from webscan by PDS for sechub job uuid: "+jobUUID).
hasMessage(SecHubMessageType.INFO, "another-token.txtbearer-token.txtcertificate.p12openapi.json");
+ UUID pdsJobUUID = waitForFirstPDSJobOfSecHubJobAndReturnPDSJobUUID(jobUUID);
+ Map variables = fetchPDSVariableTestOutputMap(pdsJobUUID);
+
+ String expectedMetaDataListJson = """
+ [{"templateId":"template-scenario12-1","templateType":"WEBSCAN_LOGIN","assetData":{"assetId":"asset-s12-pds-inttest-webscan","fileName":"PDS_INTTEST_PRODUCT_WEBSCAN.zip","checksum":"ff06430bfc2d8c698ab8effa41b914525b8cca1c1eecefa76d248b25cc598fba"}}]
+ """.trim();
+ assertThat(variables.get("PDS_CONFIG_TEMPLATE_METADATA_LIST")).isEqualTo(expectedMetaDataListJson);
+ assertThat(variables.get("TEST_CONTENT_FROM_ASSETFILE")).isEqualTo("i am \"testfile1.txt\" for scenario12 integration tests");
/* @formatter:on */
}
diff --git a/sechub-integrationtest/src/test/resources/asset/examples-1/asset1.txt b/sechub-integrationtest/src/test/resources/asset/examples-1/asset1.txt
new file mode 100644
index 0000000000..c9f4187cc1
--- /dev/null
+++ b/sechub-integrationtest/src/test/resources/asset/examples-1/asset1.txt
@@ -0,0 +1 @@
+I am text file "asset1.txt"
\ No newline at end of file
diff --git a/sechub-integrationtest/src/test/resources/asset/examples-1/asset2.txt b/sechub-integrationtest/src/test/resources/asset/examples-1/asset2.txt
new file mode 100644
index 0000000000..a47cb9bda8
--- /dev/null
+++ b/sechub-integrationtest/src/test/resources/asset/examples-1/asset2.txt
@@ -0,0 +1 @@
+I am text file "asset2.txt"
\ No newline at end of file
diff --git a/sechub-integrationtest/src/test/resources/asset/examples-2/asset2.txt b/sechub-integrationtest/src/test/resources/asset/examples-2/asset2.txt
new file mode 100644
index 0000000000..7ccc0dabcc
--- /dev/null
+++ b/sechub-integrationtest/src/test/resources/asset/examples-2/asset2.txt
@@ -0,0 +1 @@
+I am text file "asset2.txt" - but from folder example-2
\ No newline at end of file
diff --git a/sechub-integrationtest/src/test/resources/asset/scenario12/PDS_INTTEST_PRODUCT_WEBSCAN.zip b/sechub-integrationtest/src/test/resources/asset/scenario12/PDS_INTTEST_PRODUCT_WEBSCAN.zip
new file mode 100644
index 0000000000..1c1ce661bf
Binary files /dev/null and b/sechub-integrationtest/src/test/resources/asset/scenario12/PDS_INTTEST_PRODUCT_WEBSCAN.zip differ
diff --git a/sechub-integrationtest/src/test/resources/sechub-integrationtest-webscanconfig-all-options.json b/sechub-integrationtest/src/test/resources/sechub-integrationtest-webscanconfig-all-options.json
index 6951487d61..894924e54f 100644
--- a/sechub-integrationtest/src/test/resources/sechub-integrationtest-webscanconfig-all-options.json
+++ b/sechub-integrationtest/src/test/resources/sechub-integrationtest-webscanconfig-all-options.json
@@ -25,8 +25,8 @@
},
"webScan" : {
"url" : "https://demo.example.org/myapp",
- "includes": [ "/portal/admin", "/abc.html", "/hidden", "/customer/<*>" ],
- "excludes": [ "/public/media", "/contact.html", "/static", "<*>/admin/<*>" ],
+ "includes" : [ "/portal/admin", "/abc.html", "/hidden", "/customer/<*>" ],
+ "excludes" : [ "/public/media", "/contact.html", "/static", "<*>/admin/<*>" ],
"maxScanDuration" : {
"duration" : 35,
"unit" : "minutes"
@@ -39,7 +39,7 @@
"password" : "secret-password",
"use" : [ "client-cert-api-file-reference" ]
},
- "headers" : [{
+ "headers" : [ {
"name" : "Authorization",
"use" : [ "header-file-ref-for-big-token" ]
}, {
@@ -50,9 +50,13 @@
}, {
"name" : "Key",
"use" : [ "another-header-file-ref-for-big-token" ]
- }],
+ } ],
"login" : {
"url" : "https://demo.example.org/myapp/login",
+ "templateData" : {
+ "username" : "testuser",
+ "password" : "testpwd"
+ },
"basic" : {
"realm" : "realm0",
"user" : "user0",
diff --git a/sechub-openapi-java/src/main/resources/openapi.yaml b/sechub-openapi-java/src/main/resources/openapi.yaml
index 0f1ca5927d..89428aa834 100644
--- a/sechub-openapi-java/src/main/resources/openapi.yaml
+++ b/sechub-openapi-java/src/main/resources/openapi.yaml
@@ -150,6 +150,11 @@ components:
items:
type: string
type: array
+ templates:
+ description: A list of all assigned templates
+ items:
+ type: string
+ type: array
example:
owner: owner
metaData:
@@ -163,6 +168,9 @@ components:
users:
- user1
- user2
+ templates:
+ - template1
+ - template2
ProjectWhitelist:
title: ProjectWhitelist
@@ -673,6 +681,8 @@ components:
format: int32
hashAlgorithm:
$ref: '#/components/schemas/TOTPHashAlgorithm'
+ encodingType:
+ $ref: '#/components/schemas/EncodingType'
required:
- seed
@@ -684,6 +694,16 @@ components:
- HmacSHA512
description: Representing the TOTP hash algorithms.
default: HmacSHA1
+
+ EncodingType:
+ enum:
+ - AUTODETECT
+ - HEX
+ - BASE32
+ - BASE64
+ - PLAIN
+ description: Representing the encoding of the TOTP seed.
+ default: AUTODETECT
WebLoginConfiguration:
title: WebLoginConfiguration
@@ -2005,16 +2025,81 @@ components:
$ref: '#/components/schemas/SecHubJobInfoForUser'
projectId:
type: string
-
-
+
+ TemplateType:
+ title: TemplateType
+ type: string
+ enum:
+ - WEBSCAN_LOGIN
+
+ TemplateVariableValidation:
+ title: TemplateVariableValidation
+ type: object
+ properties:
+ minLength:
+ type: integer
+ format: int32
+ maxLength:
+ type: integer
+ format: int32
+ regularExpression:
+ type: string
+
+ TemplateVariable:
+ title: TemplateVariable
+ type: object
+ properties:
+ name:
+ type: string
+ optional:
+ type: boolean
+ validation:
+ $ref: '#/components/schemas/TemplateVariableValidation'
+
+ TemplateDefinition:
+ title: TemplateDefinition
+ type: object
+ properties:
+ type:
+ $ref: '#/components/schemas/TemplateType'
+ assets:
+ type: array
+ items:
+ type: string
+ variables:
+ type: array
+ items:
+ $ref: '#/components/schemas/TemplateVariable'
+
+ AssetDetailData:
+ title: AssetDetailData
+ type: object
+ properties:
+ assetId:
+ type: string
+ files:
+ type: array
+ items:
+ type: object
+ $ref: '#/components/schemas/AssetFileData'
+
+ AssetFileData:
+ title: AssetFileData
+ type: object
+ properties:
+ fileName:
+ type: string
+ checksum:
+ type: string
+
security:
- basicAuth: [ ]
paths:
- #############
+ ############
## System ##
- #############
+ ############
/api/anonymous/check/alive:
get:
@@ -2535,7 +2620,60 @@ paths:
description: "Not acceptable"
tags:
- Project Administration
-
+ /api/admin/project/{projectId}/template/{templateId}:
+ put:
+ summary: Admin assigns template to project
+ description: An administrator assigns a template to a project
+ operationId: adminAssignTemplateToProject
+ parameters:
+ - name: projectId
+ description: The id for project
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: templateId
+ description: The id of the template to assign to project
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ description: "Ok"
+ "404":
+ description: "Not found"
+ "406":
+ description: "Not acceptable"
+ tags:
+ - Project Administration
+ delete:
+ summary: Admin unassigns template from project
+ description: An administrator unassigns a template from a project
+ operationId: adminUnassignTemplateFromProject
+ parameters:
+ - name: projectId
+ description: The id for project
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: templateId
+ description: The id of the template to assign to project
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ description: "Ok"
+ "404":
+ description: "Not found"
+ "406":
+ description: "Not acceptable"
+ tags:
+ - Project Administration
+
###################
## User Profile ##
###################
@@ -3777,4 +3915,256 @@ paths:
description: "Not acceptable"
x-content-type: application/json
tags:
- - Other
\ No newline at end of file
+ - Other
+
+ /api/admin/template/{templateId}:
+ put:
+ summary: Admin creates or updates a template
+ description: An administrator wants to create a new template or to update a template definition
+ operationId: adminCreateOrUpdateTemplate
+ parameters:
+ - name: templateId
+ description: The template id
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ description: "Ok"
+ "404":
+ description: "Not found"
+ "406":
+ description: "Not acceptable"
+ tags:
+ - Configuration
+
+ delete:
+ summary: Admin deletes a template
+ description: An administrator wants to delete an existing template
+ operationId: adminDeleteTemplate
+ parameters:
+ - name: templateId
+ description: The template id
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ description: "Ok"
+ "404":
+ description: "Not found"
+ tags:
+ - Configuration
+
+ get:
+ summary: Admin fetches template
+ description: An administrator wants to fetch the template definition by template id
+ operationId: adminFetchTemplate
+ parameters:
+ - name: templateId
+ description: The template id
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ description: "Ok"
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/TemplateDefinition'
+ "404":
+ description: "Not found"
+ tags:
+ - Configuration
+
+ /api/admin/templates:
+ get:
+ summary: Admin fetches template ids
+ description: An administrator wants to fetch a list containing all available template identifiers
+ operationId: adminFetchTemplateIds
+ parameters:
+ - name: templateId
+ description: The template id
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ description: "Ok"
+ content:
+ application/json;charset=UTF-8:
+ schema:
+ type: array
+ items:
+ type: string
+ "404":
+ description: "Not found"
+ tags:
+ - Configuration
+
+ /api/admin/asset/ids:
+ get:
+ summary: Admin fetches asset ids
+ description: An administrator fetches all available asset ids.
+ operationId: adminFetchAssetIds
+ responses:
+ "200":
+ description: "Ok"
+ content:
+ application/json;charset=UTF-8:
+ schema:
+ type: array
+ items:
+ type: string
+ tags:
+ - Configuration
+
+ /api/admin/asset/{assetId}/details:
+ get:
+ summary: Admin fetches asset details
+ description: "An administrator fetches details about an asset. For example: the result will contain names but also checksum of files."
+ operationId: adminFetchAssetDetails
+ parameters:
+ - name: assetId
+ description: The asset identifier
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ description: "Ok"
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/AssetDetailData'
+
+ "404":
+ description: "Not found"
+ tags:
+ - Configuration
+
+ /api/admin/asset/{assetId}:
+ delete:
+ summary: Admin deletes asset comletely
+ description: An administrator deletes an asset completely.
+ operationId: adminDeletesAssetCompletely
+ parameters:
+ - name: assetId
+ description: TThe asset identifier for the asset which shall be deleted completely
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ description: "Ok"
+
+ "404":
+ description: "Not found"
+ tags:
+ - Configuration
+
+ /api/admin/asset/{assetId}/file:
+ post:
+ summary: Admin uploads an asset file
+ description: "An administrator uploads a file for an asset. If the file already exits, it will be overriden."
+ operationId: adminUploadsAssetFile
+ parameters:
+ - name: assetId
+ description: The id of the asset to which the uploaded file belongs to
+ in: path
+ required: true
+ schema:
+ type: string
+ requestBody:
+ required: true
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ properties:
+ file:
+ type: string
+ format: binary
+ description: The asset file to upload
+ checkSum:
+ type: string
+ description: A sha256 checksum for file upload validation
+ required:
+ - file
+ - checkSum
+ encoding:
+ file:
+ contentType: multipart/form-data
+ responses:
+ "200":
+ description: "Ok"
+ "406":
+ description: "Not acceptable"
+ x-content-type: multipart/form-data
+ tags:
+ - Configuration
+
+ /api/admin/asset/{assetId}/file/{fileName}:
+ get:
+ summary: Admin downloads an asset file
+ description: An administrator downloads a file fom an asset.
+ operationId: adminDownloadsAssetFile
+ parameters:
+ - name: assetId
+ description: The asset identifier of the asset containing the file.
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: fileName
+ description: The name of the file inside the asset which shall be downloaded.
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+ description: "Ok"
+
+ "404":
+ description: "Not found"
+ tags:
+ - Configuration
+
+ delete:
+ summary: Admin deletes an asset file
+ description: An administrator deletes a file fom an asset.
+ operationId: adminDeletesAssetFile
+ parameters:
+ - name: assetId
+ description: The asset identifier for the asset in which the file shall be deleted.
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: fileName
+ description: The name of the file to delete inside the asset.
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ description: "Ok"
+
+ "404":
+ description: "Not found"
+ tags:
+ - Configuration
\ No newline at end of file
diff --git a/sechub-pds-core/src/main/java/com/mercedesbenz/sechub/pds/job/PDSJobConfigurationSupport.java b/sechub-pds-core/src/main/java/com/mercedesbenz/sechub/pds/job/PDSJobConfigurationSupport.java
index 8482876303..414831af46 100644
--- a/sechub-pds-core/src/main/java/com/mercedesbenz/sechub/pds/job/PDSJobConfigurationSupport.java
+++ b/sechub-pds-core/src/main/java/com/mercedesbenz/sechub/pds/job/PDSJobConfigurationSupport.java
@@ -13,12 +13,14 @@
import org.slf4j.LoggerFactory;
import com.mercedesbenz.sechub.commons.core.util.SimpleStringUtils;
+import com.mercedesbenz.sechub.commons.model.JSONConverter;
import com.mercedesbenz.sechub.commons.model.SecHubConfigurationModel;
import com.mercedesbenz.sechub.commons.model.SecHubDataConfigurationType;
import com.mercedesbenz.sechub.commons.model.SecHubDataConfigurationTypeListParser;
import com.mercedesbenz.sechub.commons.model.SecHubScanConfiguration;
import com.mercedesbenz.sechub.commons.pds.PDSDefaultParameterKeyConstants;
import com.mercedesbenz.sechub.commons.pds.PDSDefaultParameterValueConstants;
+import com.mercedesbenz.sechub.commons.pds.data.PDSTemplateMetaData;
import com.mercedesbenz.sechub.pds.execution.PDSExecutionParameterEntry;
public class PDSJobConfigurationSupport {
@@ -200,4 +202,13 @@ public int getJobStorageReadResiliencRetryWaitSeconds(int defaultValue) {
return result;
}
+ public List getTemplateMetaData() {
+
+ String json = getStringParameterOrNull(PARAM_KEY_PDS_CONFIG_TEMPLATE_META_DATA_LIST);
+ if (json == null || json.isBlank()) {
+ return Collections.emptyList();
+ }
+ return JSONConverter.get().fromJSONtoListOf(PDSTemplateMetaData.class, json);
+ }
+
}
diff --git a/sechub-pds-core/src/main/java/com/mercedesbenz/sechub/pds/usecase/PDSUseCaseIdentifier.java b/sechub-pds-core/src/main/java/com/mercedesbenz/sechub/pds/usecase/PDSUseCaseIdentifier.java
index 70de74439b..f59264d4dc 100644
--- a/sechub-pds-core/src/main/java/com/mercedesbenz/sechub/pds/usecase/PDSUseCaseIdentifier.java
+++ b/sechub-pds-core/src/main/java/com/mercedesbenz/sechub/pds/usecase/PDSUseCaseIdentifier.java
@@ -47,6 +47,8 @@ public enum PDSUseCaseIdentifier {
UC_SYSTEM_SIGTERM_HANDLING(19, false),
+ UC_SYSTEM_JOB_EXECUTION(20, false),
+
;
/* +---------------------------------------------------------------------+ */
diff --git a/sechub-pds-core/src/main/java/com/mercedesbenz/sechub/pds/usecase/UseCaseSystemExecutesJob.java b/sechub-pds-core/src/main/java/com/mercedesbenz/sechub/pds/usecase/UseCaseSystemExecutesJob.java
new file mode 100644
index 0000000000..d3be4feb73
--- /dev/null
+++ b/sechub-pds-core/src/main/java/com/mercedesbenz/sechub/pds/usecase/UseCaseSystemExecutesJob.java
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.pds.usecase;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/* @formatter:off */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@PDSUseCaseDefinition(
+ id=PDSUseCaseIdentifier.UC_SYSTEM_JOB_EXECUTION,
+ group=PDSUseCaseGroup.JOB_EXECUTION,
+ title="System executes job",
+ description="The PDS does execute a PDS job.")
+public @interface UseCaseSystemExecutesJob {
+ PDSStep value();
+}
diff --git a/sechub-pds-solutions/checkmarx/helm/pds-checkmarx/Chart.yaml b/sechub-pds-solutions/checkmarx/helm/pds-checkmarx/Chart.yaml
index 6aad19547e..1c7ddb568a 100644
--- a/sechub-pds-solutions/checkmarx/helm/pds-checkmarx/Chart.yaml
+++ b/sechub-pds-solutions/checkmarx/helm/pds-checkmarx/Chart.yaml
@@ -9,4 +9,4 @@ type: application
# This is the chart version.
# This version number should be incremented each time you make changes to the chart and its templates.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 1.4.0
+version: 1.5.0
diff --git a/sechub-pds-solutions/checkmarx/helm/pds-checkmarx/templates/deployment.yaml b/sechub-pds-solutions/checkmarx/helm/pds-checkmarx/templates/deployment.yaml
index 8d0270b380..b0153e29b3 100644
--- a/sechub-pds-solutions/checkmarx/helm/pds-checkmarx/templates/deployment.yaml
+++ b/sechub-pds-solutions/checkmarx/helm/pds-checkmarx/templates/deployment.yaml
@@ -124,6 +124,8 @@ spec:
value: "{{ .Values.storage.sharedVolume.upload.dir }}"
{{- end}}
ports:
+ - name: pds-health-port
+ containerPort: 10251
- name: pds-https-port
containerPort: 8444
startupProbe:
@@ -131,9 +133,19 @@ spec:
scheme: HTTPS
path: /api/anonymous/check/alive
port: pds-https-port
+ initialDelaySeconds: 5
periodSeconds: 1
- failureThreshold: 300
- # probe every 1s x 300 = 5 mins before restart of container
+ # probe every 1s x 600 = 10 mins before restart of container (some PDS download huge files before startup)
+ failureThreshold: 600
+ successThreshold: 1
+ timeoutSeconds: 1
+ readinessProbe:
+ httpGet:
+ scheme: HTTP
+ path: /actuator/health/readiness
+ port: pds-health-port
+ periodSeconds: 2
+ failureThreshold: 2
successThreshold: 1
timeoutSeconds: 1
livenessProbe:
@@ -144,7 +156,7 @@ spec:
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
- timeoutSeconds: 3
+ timeoutSeconds: 5
volumeMounts:
- mountPath: "/workspace"
name: pds-workspace
diff --git a/sechub-pds-solutions/findsecuritybugs/helm/pds-findsecuritybugs/Chart.yaml b/sechub-pds-solutions/findsecuritybugs/helm/pds-findsecuritybugs/Chart.yaml
index cfbc141c95..f4c3486a26 100644
--- a/sechub-pds-solutions/findsecuritybugs/helm/pds-findsecuritybugs/Chart.yaml
+++ b/sechub-pds-solutions/findsecuritybugs/helm/pds-findsecuritybugs/Chart.yaml
@@ -9,4 +9,4 @@ type: application
# This is the chart version.
# This version number should be incremented each time you make changes to the chart and its templates.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 1.4.0
+version: 1.5.0
diff --git a/sechub-pds-solutions/findsecuritybugs/helm/pds-findsecuritybugs/templates/deployment.yaml b/sechub-pds-solutions/findsecuritybugs/helm/pds-findsecuritybugs/templates/deployment.yaml
index 951448fd1a..e8db3fe28a 100644
--- a/sechub-pds-solutions/findsecuritybugs/helm/pds-findsecuritybugs/templates/deployment.yaml
+++ b/sechub-pds-solutions/findsecuritybugs/helm/pds-findsecuritybugs/templates/deployment.yaml
@@ -120,6 +120,8 @@ spec:
value: "{{ .Values.storage.sharedVolume.upload.dir }}"
{{- end}}
ports:
+ - name: pds-health-port
+ containerPort: 10251
- name: pds-https-port
containerPort: 8444
startupProbe:
@@ -127,9 +129,19 @@ spec:
scheme: HTTPS
path: /api/anonymous/check/alive
port: pds-https-port
+ initialDelaySeconds: 5
periodSeconds: 1
- failureThreshold: 300
- # probe every 1s x 300 = 5 mins before restart of container
+ # probe every 1s x 600 = 10 mins before restart of container (some PDS download huge files before startup)
+ failureThreshold: 600
+ successThreshold: 1
+ timeoutSeconds: 1
+ readinessProbe:
+ httpGet:
+ scheme: HTTP
+ path: /actuator/health/readiness
+ port: pds-health-port
+ periodSeconds: 2
+ failureThreshold: 2
successThreshold: 1
timeoutSeconds: 1
livenessProbe:
@@ -140,7 +152,7 @@ spec:
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
- timeoutSeconds: 3
+ timeoutSeconds: 5
volumeMounts:
- mountPath: "/workspace"
name: pds-fsb-workspace
diff --git a/sechub-pds-solutions/gitleaks/helm/pds-gitleaks/Chart.yaml b/sechub-pds-solutions/gitleaks/helm/pds-gitleaks/Chart.yaml
index 3694c52fac..f7919a2ec6 100644
--- a/sechub-pds-solutions/gitleaks/helm/pds-gitleaks/Chart.yaml
+++ b/sechub-pds-solutions/gitleaks/helm/pds-gitleaks/Chart.yaml
@@ -9,4 +9,4 @@ type: application
# This is the chart version.
# This version number should be incremented each time you make changes to the chart and its templates.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 1.4.0
+version: 1.5.0
diff --git a/sechub-pds-solutions/gitleaks/helm/pds-gitleaks/templates/deployment.yaml b/sechub-pds-solutions/gitleaks/helm/pds-gitleaks/templates/deployment.yaml
index 5170250dbf..79b00453a2 100644
--- a/sechub-pds-solutions/gitleaks/helm/pds-gitleaks/templates/deployment.yaml
+++ b/sechub-pds-solutions/gitleaks/helm/pds-gitleaks/templates/deployment.yaml
@@ -136,6 +136,8 @@ spec:
value: "{{ .Values.storage.sharedVolume.upload.dir }}"
{{- end}}
ports:
+ - name: pds-health-port
+ containerPort: 10251
- name: pds-https-port
containerPort: 8444
startupProbe:
@@ -143,9 +145,19 @@ spec:
scheme: HTTPS
path: /api/anonymous/check/alive
port: pds-https-port
+ initialDelaySeconds: 5
periodSeconds: 1
- failureThreshold: 300
- # probe every 1s x 300 = 5 mins before restart of container
+ # probe every 1s x 600 = 10 mins before restart of container (some PDS download huge files before startup)
+ failureThreshold: 600
+ successThreshold: 1
+ timeoutSeconds: 1
+ readinessProbe:
+ httpGet:
+ scheme: HTTP
+ path: /actuator/health/readiness
+ port: pds-health-port
+ periodSeconds: 2
+ failureThreshold: 2
successThreshold: 1
timeoutSeconds: 1
livenessProbe:
@@ -156,7 +168,7 @@ spec:
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
- timeoutSeconds: 3
+ timeoutSeconds: 5
{{- if .Values.image.imagePullSecrets }}
imagePullSecrets:
{{ .Values.image.imagePullSecrets | indent 8 | trim }}
diff --git a/sechub-pds-solutions/gosec/helm/pds-gosec/Chart.yaml b/sechub-pds-solutions/gosec/helm/pds-gosec/Chart.yaml
index c8a70ba4e2..bbd921f00a 100644
--- a/sechub-pds-solutions/gosec/helm/pds-gosec/Chart.yaml
+++ b/sechub-pds-solutions/gosec/helm/pds-gosec/Chart.yaml
@@ -9,4 +9,4 @@ type: application
# This is the chart version.
# This version number should be incremented each time you make changes to the chart and its templates.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 1.4.0
+version: 1.5.0
diff --git a/sechub-pds-solutions/gosec/helm/pds-gosec/templates/deployment.yaml b/sechub-pds-solutions/gosec/helm/pds-gosec/templates/deployment.yaml
index 8d0270b380..b0153e29b3 100644
--- a/sechub-pds-solutions/gosec/helm/pds-gosec/templates/deployment.yaml
+++ b/sechub-pds-solutions/gosec/helm/pds-gosec/templates/deployment.yaml
@@ -124,6 +124,8 @@ spec:
value: "{{ .Values.storage.sharedVolume.upload.dir }}"
{{- end}}
ports:
+ - name: pds-health-port
+ containerPort: 10251
- name: pds-https-port
containerPort: 8444
startupProbe:
@@ -131,9 +133,19 @@ spec:
scheme: HTTPS
path: /api/anonymous/check/alive
port: pds-https-port
+ initialDelaySeconds: 5
periodSeconds: 1
- failureThreshold: 300
- # probe every 1s x 300 = 5 mins before restart of container
+ # probe every 1s x 600 = 10 mins before restart of container (some PDS download huge files before startup)
+ failureThreshold: 600
+ successThreshold: 1
+ timeoutSeconds: 1
+ readinessProbe:
+ httpGet:
+ scheme: HTTP
+ path: /actuator/health/readiness
+ port: pds-health-port
+ periodSeconds: 2
+ failureThreshold: 2
successThreshold: 1
timeoutSeconds: 1
livenessProbe:
@@ -144,7 +156,7 @@ spec:
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
- timeoutSeconds: 3
+ timeoutSeconds: 5
volumeMounts:
- mountPath: "/workspace"
name: pds-workspace
diff --git a/sechub-pds-solutions/iac/helm/pds-iac/Chart.yaml b/sechub-pds-solutions/iac/helm/pds-iac/Chart.yaml
index 7be03c4e83..d443958aa1 100644
--- a/sechub-pds-solutions/iac/helm/pds-iac/Chart.yaml
+++ b/sechub-pds-solutions/iac/helm/pds-iac/Chart.yaml
@@ -9,4 +9,4 @@ type: application
# This is the chart version.
# This version number should be incremented each time you make changes to the chart and its templates.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 1.4.0
+version: 1.5.0
diff --git a/sechub-pds-solutions/iac/helm/pds-iac/templates/deployment.yaml b/sechub-pds-solutions/iac/helm/pds-iac/templates/deployment.yaml
index 16b91bf72c..7f1d0a353f 100644
--- a/sechub-pds-solutions/iac/helm/pds-iac/templates/deployment.yaml
+++ b/sechub-pds-solutions/iac/helm/pds-iac/templates/deployment.yaml
@@ -124,6 +124,8 @@ spec:
value: "{{ .Values.storage.sharedVolume.upload.dir }}"
{{- end}}
ports:
+ - name: pds-health-port
+ containerPort: 10251
- name: pds-https-port
containerPort: 8444
startupProbe:
@@ -131,9 +133,19 @@ spec:
scheme: HTTPS
path: /api/anonymous/check/alive
port: pds-https-port
+ initialDelaySeconds: 5
periodSeconds: 1
- failureThreshold: 300
- # probe every 1s x 300 = 5 mins before restart of container
+ # probe every 1s x 600 = 10 mins before restart of container (some PDS download huge files before startup)
+ failureThreshold: 600
+ successThreshold: 1
+ timeoutSeconds: 1
+ readinessProbe:
+ httpGet:
+ scheme: HTTP
+ path: /actuator/health/readiness
+ port: pds-health-port
+ periodSeconds: 2
+ failureThreshold: 2
successThreshold: 1
timeoutSeconds: 1
livenessProbe:
@@ -144,4 +156,4 @@ spec:
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
- timeoutSeconds: 3
+ timeoutSeconds: 5
diff --git a/sechub-pds-solutions/loc/helm/pds-loc/Chart.yaml b/sechub-pds-solutions/loc/helm/pds-loc/Chart.yaml
index ed91b6e04f..ddf86c9c61 100644
--- a/sechub-pds-solutions/loc/helm/pds-loc/Chart.yaml
+++ b/sechub-pds-solutions/loc/helm/pds-loc/Chart.yaml
@@ -9,4 +9,4 @@ type: application
# This is the chart version.
# This version number should be incremented each time you make changes to the chart and its templates.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 1.3.0
+version: 1.4.0
diff --git a/sechub-pds-solutions/loc/helm/pds-loc/templates/deployment.yaml b/sechub-pds-solutions/loc/helm/pds-loc/templates/deployment.yaml
index 5c3396071d..9d29d500e5 100644
--- a/sechub-pds-solutions/loc/helm/pds-loc/templates/deployment.yaml
+++ b/sechub-pds-solutions/loc/helm/pds-loc/templates/deployment.yaml
@@ -124,6 +124,8 @@ spec:
value: "{{ .Values.storage.sharedVolume.upload.dir }}"
{{- end}}
ports:
+ - name: pds-health-port
+ containerPort: 10251
- name: pds-https-port
containerPort: 8444
startupProbe:
@@ -131,9 +133,19 @@ spec:
scheme: HTTPS
path: /api/anonymous/check/alive
port: pds-https-port
+ initialDelaySeconds: 5
periodSeconds: 1
- failureThreshold: 300
- # probe every 1s x 300 = 5 mins before restart of container
+ # probe every 1s x 600 = 10 mins before restart of container (some PDS download huge files before startup)
+ failureThreshold: 600
+ successThreshold: 1
+ timeoutSeconds: 1
+ readinessProbe:
+ httpGet:
+ scheme: HTTP
+ path: /actuator/health/readiness
+ port: pds-health-port
+ periodSeconds: 2
+ failureThreshold: 2
successThreshold: 1
timeoutSeconds: 1
livenessProbe:
@@ -144,7 +156,7 @@ spec:
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
- timeoutSeconds: 3
+ timeoutSeconds: 5
volumeMounts:
- mountPath: "/workspace"
name: pds-workspace
diff --git a/sechub-pds-solutions/multi/helm/pds-multi/Chart.yaml b/sechub-pds-solutions/multi/helm/pds-multi/Chart.yaml
index fdff571b9f..0777ce8585 100644
--- a/sechub-pds-solutions/multi/helm/pds-multi/Chart.yaml
+++ b/sechub-pds-solutions/multi/helm/pds-multi/Chart.yaml
@@ -9,4 +9,4 @@ type: application
# This is the chart version.
# This version number should be incremented each time you make changes to the chart and its templates.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 1.4.0
+version: 1.5.0
diff --git a/sechub-pds-solutions/multi/helm/pds-multi/templates/deployment.yaml b/sechub-pds-solutions/multi/helm/pds-multi/templates/deployment.yaml
index 74d77f580b..587895f63d 100644
--- a/sechub-pds-solutions/multi/helm/pds-multi/templates/deployment.yaml
+++ b/sechub-pds-solutions/multi/helm/pds-multi/templates/deployment.yaml
@@ -124,6 +124,8 @@ spec:
value: "{{ .Values.storage.sharedVolume.upload.dir }}"
{{- end}}
ports:
+ - name: pds-health-port
+ containerPort: 10251
- name: pds-https-port
containerPort: 8444
startupProbe:
@@ -131,9 +133,19 @@ spec:
scheme: HTTPS
path: /api/anonymous/check/alive
port: pds-https-port
+ initialDelaySeconds: 5
periodSeconds: 1
- failureThreshold: 300
- # probe every 1s x 300 = 5 mins before restart of container
+ # probe every 1s x 600 = 10 mins before restart of container (some PDS download huge files before startup)
+ failureThreshold: 600
+ successThreshold: 1
+ timeoutSeconds: 1
+ readinessProbe:
+ httpGet:
+ scheme: HTTP
+ path: /actuator/health/readiness
+ port: pds-health-port
+ periodSeconds: 2
+ failureThreshold: 2
successThreshold: 1
timeoutSeconds: 1
livenessProbe:
@@ -144,7 +156,7 @@ spec:
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
- timeoutSeconds: 3
+ timeoutSeconds: 5
volumeMounts:
- mountPath: "/workspace"
name: pds-workspace
diff --git a/sechub-pds-solutions/owaspzap/docker/Owasp-Zap-Debian.dockerfile b/sechub-pds-solutions/owaspzap/docker/Owasp-Zap-Debian.dockerfile
index 3f360047fe..dc60a49cf6 100644
--- a/sechub-pds-solutions/owaspzap/docker/Owasp-Zap-Debian.dockerfile
+++ b/sechub-pds-solutions/owaspzap/docker/Owasp-Zap-Debian.dockerfile
@@ -62,9 +62,6 @@ RUN cd "$TOOL_FOLDER" && \
sha256sum --check sechub-pds-wrapperowaspzap-$OWASPZAP_WRAPPER_VERSION.jar.sha256sum && \
ln -s sechub-pds-wrapperowaspzap-$OWASPZAP_WRAPPER_VERSION.jar wrapperowaspzap.jar
-# Copy default full ruleset file
-COPY owasp-zap-full-ruleset-all-release-status.json ${TOOL_FOLDER}/owasp-zap-full-ruleset-all-release-status.json
-
# Copy zap addon download urls into container
COPY zap-addons.txt "$TOOL_FOLDER/zap-addons.txt"
diff --git a/sechub-pds-solutions/owaspzap/docker/owasp-zap-full-ruleset-all-release-status.json b/sechub-pds-solutions/owaspzap/docker/owasp-zap-full-ruleset-all-release-status.json
deleted file mode 100644
index d08f86716d..0000000000
--- a/sechub-pds-solutions/owaspzap/docker/owasp-zap-full-ruleset-all-release-status.json
+++ /dev/null
@@ -1,1152 +0,0 @@
-{
- "timestamp" : "2024-10-07 07:29:08.282732",
- "origin" : "https://www.zaproxy.org/docs/alerts/",
- "rules" : {
- "Directory-Browsing-0" : {
- "id" : "0",
- "name" : "Directory Browsing",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/0"
- },
- "Private-IP-Disclosure-2" : {
- "id" : "2",
- "name" : "Private IP Disclosure",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/2"
- },
- "Session-ID-in-URL-Rewrite-3" : {
- "id" : "3",
- "name" : "Session ID in URL Rewrite",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/3"
- },
- "Path-Traversal-6" : {
- "id" : "6",
- "name" : "Path Traversal",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/6"
- },
- "Remote-File-Inclusion-7" : {
- "id" : "7",
- "name" : "Remote File Inclusion",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/7"
- },
- "Source-Code-Disclosure-Git-41" : {
- "id" : "41",
- "name" : "Source Code Disclosure - Git",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/41"
- },
- "Source-Code-Disclosure-SVN-42" : {
- "id" : "42",
- "name" : "Source Code Disclosure - SVN",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/42"
- },
- "Source-Code-Disclosure-File-Inclusion-43" : {
- "id" : "43",
- "name" : "Source Code Disclosure - File Inclusion",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/43"
- },
- "Vulnerable-JS-Library-10003" : {
- "id" : "10003",
- "name" : "Vulnerable JS Library",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10003"
- },
- "Tech-Detection-Passive-Scanner-10004" : {
- "id" : "10004",
- "name" : "Tech Detection Passive Scanner",
- "type" : "tool",
- "link" : "https://www.zaproxy.org/docs/alerts/10004"
- },
- "In-Page-Banner-Information-Leak-10009" : {
- "id" : "10009",
- "name" : "In Page Banner Information Leak",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10009"
- },
- "Cookie-No-HttpOnly-Flag-10010" : {
- "id" : "10010",
- "name" : "Cookie No HttpOnly Flag",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10010"
- },
- "Cookie-Without-Secure-Flag-10011" : {
- "id" : "10011",
- "name" : "Cookie Without Secure Flag",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10011"
- },
- "Re-examine-Cache-control-Directives-10015" : {
- "id" : "10015",
- "name" : "Re-examine Cache-control Directives",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10015"
- },
- "Cross-Domain-JavaScript-Source-File-Inclusion-10017" : {
- "id" : "10017",
- "name" : "Cross-Domain JavaScript Source File Inclusion",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10017"
- },
- "Content-Type-Header-Missing-10019" : {
- "id" : "10019",
- "name" : "Content-Type Header Missing",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10019"
- },
- "Anti-clickjacking-Header-10020" : {
- "id" : "10020",
- "name" : "Anti-clickjacking Header",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10020"
- },
- "X-Content-Type-Options-Header-Missing-10021" : {
- "id" : "10021",
- "name" : "X-Content-Type-Options Header Missing",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10021"
- },
- "Information-Disclosure-Debug-Error-Messages-10023" : {
- "id" : "10023",
- "name" : "Information Disclosure - Debug Error Messages",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10023"
- },
- "Information-Disclosure-Sensitive-Information-in-URL-10024" : {
- "id" : "10024",
- "name" : "Information Disclosure - Sensitive Information in URL",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10024"
- },
- "Information-Disclosure-Sensitive-Information-in-HTTP-Referrer-Header-10025" : {
- "id" : "10025",
- "name" : "Information Disclosure - Sensitive Information in HTTP Referrer Header",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10025"
- },
- "HTTP-Parameter-Override-10026" : {
- "id" : "10026",
- "name" : "HTTP Parameter Override",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10026"
- },
- "Information-Disclosure-Suspicious-Comments-10027" : {
- "id" : "10027",
- "name" : "Information Disclosure - Suspicious Comments",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10027"
- },
- "Open-Redirect-10028" : {
- "id" : "10028",
- "name" : "Open Redirect",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10028"
- },
- "Cookie-Poisoning-10029" : {
- "id" : "10029",
- "name" : "Cookie Poisoning",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10029"
- },
- "User-Controllable-Charset-10030" : {
- "id" : "10030",
- "name" : "User Controllable Charset",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10030"
- },
- "User-Controllable-HTML-Element-Attribute-(Potential-XSS)-10031" : {
- "id" : "10031",
- "name" : "User Controllable HTML Element Attribute (Potential XSS)",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10031"
- },
- "Viewstate-10032" : {
- "id" : "10032",
- "name" : "Viewstate",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10032"
- },
- "Directory-Browsing-10033" : {
- "id" : "10033",
- "name" : "Directory Browsing",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10033"
- },
- "Heartbleed-OpenSSL-Vulnerability-(Indicative)-10034" : {
- "id" : "10034",
- "name" : "Heartbleed OpenSSL Vulnerability (Indicative)",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10034"
- },
- "Strict-Transport-Security-Header-10035" : {
- "id" : "10035",
- "name" : "Strict-Transport-Security Header",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10035"
- },
- "HTTP-Server-Response-Header-10036" : {
- "id" : "10036",
- "name" : "HTTP Server Response Header",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10036"
- },
- "Server-Leaks-Information-via-\"X-Powered-By\"-HTTP-Response-Header-Field(s)-10037" : {
- "id" : "10037",
- "name" : "Server Leaks Information via \"X-Powered-By\" HTTP Response Header Field(s)",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10037"
- },
- "Content-Security-Policy-(CSP)-Header-Not-Set-10038" : {
- "id" : "10038",
- "name" : "Content Security Policy (CSP) Header Not Set",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10038"
- },
- "X-Backend-Server-Header-Information-Leak-10039" : {
- "id" : "10039",
- "name" : "X-Backend-Server Header Information Leak",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10039"
- },
- "Secure-Pages-Include-Mixed-Content-10040" : {
- "id" : "10040",
- "name" : "Secure Pages Include Mixed Content",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10040"
- },
- "HTTP-to-HTTPS-Insecure-Transition-in-Form-Post-10041" : {
- "id" : "10041",
- "name" : "HTTP to HTTPS Insecure Transition in Form Post",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10041"
- },
- "HTTPS-to-HTTP-Insecure-Transition-in-Form-Post-10042" : {
- "id" : "10042",
- "name" : "HTTPS to HTTP Insecure Transition in Form Post",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10042"
- },
- "User-Controllable-JavaScript-Event-(XSS)-10043" : {
- "id" : "10043",
- "name" : "User Controllable JavaScript Event (XSS)",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10043"
- },
- "Big-Redirect-Detected-(Potential-Sensitive-Information-Leak)-10044" : {
- "id" : "10044",
- "name" : "Big Redirect Detected (Potential Sensitive Information Leak)",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10044"
- },
- "Source-Code-Disclosure-/WEB-INF-Folder-10045" : {
- "id" : "10045",
- "name" : "Source Code Disclosure - /WEB-INF Folder",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/10045"
- },
- "HTTPS-Content-Available-via-HTTP-10047" : {
- "id" : "10047",
- "name" : "HTTPS Content Available via HTTP",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/10047"
- },
- "Remote-Code-Execution-Shell-Shock-10048" : {
- "id" : "10048",
- "name" : "Remote Code Execution - Shell Shock",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/10048"
- },
- "Content-Cacheability-10049" : {
- "id" : "10049",
- "name" : "Content Cacheability",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10049"
- },
- "Retrieved-from-Cache-10050" : {
- "id" : "10050",
- "name" : "Retrieved from Cache",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10050"
- },
- "Relative-Path-Confusion-10051" : {
- "id" : "10051",
- "name" : "Relative Path Confusion",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/10051"
- },
- "X-ChromeLogger-Data-(XCOLD)-Header-Information-Leak-10052" : {
- "id" : "10052",
- "name" : "X-ChromeLogger-Data (XCOLD) Header Information Leak",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10052"
- },
- "Cookie-without-SameSite-Attribute-10054" : {
- "id" : "10054",
- "name" : "Cookie without SameSite Attribute",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10054"
- },
- "CSP-10055" : {
- "id" : "10055",
- "name" : "CSP",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10055"
- },
- "X-Debug-Token-Information-Leak-10056" : {
- "id" : "10056",
- "name" : "X-Debug-Token Information Leak",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10056"
- },
- "Username-Hash-Found-10057" : {
- "id" : "10057",
- "name" : "Username Hash Found",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10057"
- },
- "GET-for-POST-10058" : {
- "id" : "10058",
- "name" : "GET for POST",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/10058"
- },
- "X-AspNet-Version-Response-Header-10061" : {
- "id" : "10061",
- "name" : "X-AspNet-Version Response Header",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10061"
- },
- "PII-Disclosure-10062" : {
- "id" : "10062",
- "name" : "PII Disclosure",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10062"
- },
- "Permissions-Policy-Header-Not-Set-10063" : {
- "id" : "10063",
- "name" : "Permissions Policy Header Not Set",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10063"
- },
- "Use-of-SAML-10070" : {
- "id" : "10070",
- "name" : "Use of SAML",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10070"
- },
- "Base64-Disclosure-10094" : {
- "id" : "10094",
- "name" : "Base64 Disclosure",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10094"
- },
- "Backup-File-Disclosure-10095" : {
- "id" : "10095",
- "name" : "Backup File Disclosure",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/10095"
- },
- "Timestamp-Disclosure-Unix-10096" : {
- "id" : "10096",
- "name" : "Timestamp Disclosure - Unix",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10096"
- },
- "Hash-Disclosure-MD4-/-MD5-10097" : {
- "id" : "10097",
- "name" : "Hash Disclosure - MD4 / MD5",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10097"
- },
- "Cross-Domain-Misconfiguration-10098" : {
- "id" : "10098",
- "name" : "Cross-Domain Misconfiguration",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10098"
- },
- "Source-Code-Disclosure-PHP-10099" : {
- "id" : "10099",
- "name" : "Source Code Disclosure - PHP",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10099"
- },
- "Access-Control-Issue-Improper-Authentication-10101" : {
- "id" : "10101",
- "name" : "Access Control Issue - Improper Authentication",
- "type" : "tool",
- "link" : "https://www.zaproxy.org/docs/alerts/10101"
- },
- "Access-Control-Issue-Improper-Authorization-10102" : {
- "id" : "10102",
- "name" : "Access Control Issue - Improper Authorization",
- "type" : "tool",
- "link" : "https://www.zaproxy.org/docs/alerts/10102"
- },
- "Image-Exposes-Location-or-Privacy-Data-10103" : {
- "id" : "10103",
- "name" : "Image Exposes Location or Privacy Data",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10103"
- },
- "User-Agent-Fuzzer-10104" : {
- "id" : "10104",
- "name" : "User Agent Fuzzer",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/10104"
- },
- "Weak-Authentication-Method-10105" : {
- "id" : "10105",
- "name" : "Weak Authentication Method",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10105"
- },
- "HTTP-Only-Site-10106" : {
- "id" : "10106",
- "name" : "HTTP Only Site",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/10106"
- },
- "Httpoxy-Proxy-Header-Misuse-10107" : {
- "id" : "10107",
- "name" : "Httpoxy - Proxy Header Misuse",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/10107"
- },
- "Reverse-Tabnabbing-10108" : {
- "id" : "10108",
- "name" : "Reverse Tabnabbing",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10108"
- },
- "Modern-Web-Application-10109" : {
- "id" : "10109",
- "name" : "Modern Web Application",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10109"
- },
- "Dangerous-JS-Functions-10110" : {
- "id" : "10110",
- "name" : "Dangerous JS Functions",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10110"
- },
- "Authentication-Request-Identified-10111" : {
- "id" : "10111",
- "name" : "Authentication Request Identified",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10111"
- },
- "Session-Management-Response-Identified-10112" : {
- "id" : "10112",
- "name" : "Session Management Response Identified",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10112"
- },
- "Verification-Request-Identified-10113" : {
- "id" : "10113",
- "name" : "Verification Request Identified",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10113"
- },
- "Script-Served-From-Malicious-Domain-(polyfill)-10115" : {
- "id" : "10115",
- "name" : "Script Served From Malicious Domain (polyfill)",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10115"
- },
- "Absence-of-Anti-CSRF-Tokens-10202" : {
- "id" : "10202",
- "name" : "Absence of Anti-CSRF Tokens",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/10202"
- },
- "Anti-CSRF-Tokens-Check-20012" : {
- "id" : "20012",
- "name" : "Anti-CSRF Tokens Check",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/20012"
- },
- "HTTP-Parameter-Pollution-20014" : {
- "id" : "20014",
- "name" : "HTTP Parameter Pollution",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/20014"
- },
- "Heartbleed-OpenSSL-Vulnerability-20015" : {
- "id" : "20015",
- "name" : "Heartbleed OpenSSL Vulnerability",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/20015"
- },
- "Cross-Domain-Misconfiguration-20016" : {
- "id" : "20016",
- "name" : "Cross-Domain Misconfiguration",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/20016"
- },
- "Source-Code-Disclosure-CVE-2012-1823-20017" : {
- "id" : "20017",
- "name" : "Source Code Disclosure - CVE-2012-1823",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/20017"
- },
- "Remote-Code-Execution-CVE-2012-1823-20018" : {
- "id" : "20018",
- "name" : "Remote Code Execution - CVE-2012-1823",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/20018"
- },
- "External-Redirect-20019" : {
- "id" : "20019",
- "name" : "External Redirect",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/20019"
- },
- "Buffer-Overflow-30001" : {
- "id" : "30001",
- "name" : "Buffer Overflow",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/30001"
- },
- "Format-String-Error-30002" : {
- "id" : "30002",
- "name" : "Format String Error",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/30002"
- },
- "Integer-Overflow-Error-30003" : {
- "id" : "30003",
- "name" : "Integer Overflow Error",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/30003"
- },
- "CRLF-Injection-40003" : {
- "id" : "40003",
- "name" : "CRLF Injection",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40003"
- },
- "Parameter-Tampering-40008" : {
- "id" : "40008",
- "name" : "Parameter Tampering",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40008"
- },
- "Server-Side-Include-40009" : {
- "id" : "40009",
- "name" : "Server Side Include",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40009"
- },
- "Cross-Site-Scripting-(Reflected)-40012" : {
- "id" : "40012",
- "name" : "Cross Site Scripting (Reflected)",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40012"
- },
- "Session-Fixation-40013" : {
- "id" : "40013",
- "name" : "Session Fixation",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40013"
- },
- "Cross-Site-Scripting-(Persistent)-40014" : {
- "id" : "40014",
- "name" : "Cross Site Scripting (Persistent)",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40014"
- },
- "LDAP-Injection-40015" : {
- "id" : "40015",
- "name" : "LDAP Injection",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40015"
- },
- "Cross-Site-Scripting-(Persistent)-Prime-40016" : {
- "id" : "40016",
- "name" : "Cross Site Scripting (Persistent) - Prime",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40016"
- },
- "Cross-Site-Scripting-(Persistent)-Spider-40017" : {
- "id" : "40017",
- "name" : "Cross Site Scripting (Persistent) - Spider",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40017"
- },
- "SQL-Injection-40018" : {
- "id" : "40018",
- "name" : "SQL Injection",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40018"
- },
- "SQL-Injection-MySQL-40019" : {
- "id" : "40019",
- "name" : "SQL Injection - MySQL",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40019"
- },
- "SQL-Injection-Hypersonic-SQL-40020" : {
- "id" : "40020",
- "name" : "SQL Injection - Hypersonic SQL",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40020"
- },
- "SQL-Injection-Oracle-40021" : {
- "id" : "40021",
- "name" : "SQL Injection - Oracle",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40021"
- },
- "SQL-Injection-PostgreSQL-40022" : {
- "id" : "40022",
- "name" : "SQL Injection - PostgreSQL",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40022"
- },
- "Possible-Username-Enumeration-40023" : {
- "id" : "40023",
- "name" : "Possible Username Enumeration",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40023"
- },
- "SQL-Injection-SQLite-40024" : {
- "id" : "40024",
- "name" : "SQL Injection - SQLite",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40024"
- },
- "Proxy-Disclosure-40025" : {
- "id" : "40025",
- "name" : "Proxy Disclosure",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40025"
- },
- "Cross-Site-Scripting-(DOM-Based)-40026" : {
- "id" : "40026",
- "name" : "Cross Site Scripting (DOM Based)",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40026"
- },
- "SQL-Injection-MsSQL-40027" : {
- "id" : "40027",
- "name" : "SQL Injection - MsSQL",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40027"
- },
- "ELMAH-Information-Leak-40028" : {
- "id" : "40028",
- "name" : "ELMAH Information Leak",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40028"
- },
- "Trace.axd-Information-Leak-40029" : {
- "id" : "40029",
- "name" : "Trace.axd Information Leak",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40029"
- },
- "Out-of-Band-XSS-40031" : {
- "id" : "40031",
- "name" : "Out of Band XSS",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40031"
- },
- ".htaccess-Information-Leak-40032" : {
- "id" : "40032",
- "name" : ".htaccess Information Leak",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40032"
- },
- "NoSQL-Injection-MongoDB-40033" : {
- "id" : "40033",
- "name" : "NoSQL Injection - MongoDB",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40033"
- },
- ".env-Information-Leak-40034" : {
- "id" : "40034",
- "name" : ".env Information Leak",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40034"
- },
- "Hidden-File-Found-40035" : {
- "id" : "40035",
- "name" : "Hidden File Found",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40035"
- },
- "JWT-Scan-Rule-40036" : {
- "id" : "40036",
- "name" : "JWT Scan Rule",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40036"
- },
- "Bypassing-403-40038" : {
- "id" : "40038",
- "name" : "Bypassing 403",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40038"
- },
- "Web-Cache-Deception-40039" : {
- "id" : "40039",
- "name" : "Web Cache Deception",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40039"
- },
- "CORS-Header-40040" : {
- "id" : "40040",
- "name" : "CORS Header",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40040"
- },
- "File-Upload-40041" : {
- "id" : "40041",
- "name" : "File Upload",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40041"
- },
- "Spring-Actuator-Information-Leak-40042" : {
- "id" : "40042",
- "name" : "Spring Actuator Information Leak",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40042"
- },
- "Log4Shell-40043" : {
- "id" : "40043",
- "name" : "Log4Shell",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40043"
- },
- "Exponential-Entity-Expansion-(Billion-Laughs-Attack)-40044" : {
- "id" : "40044",
- "name" : "Exponential Entity Expansion (Billion Laughs Attack)",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40044"
- },
- "Spring4Shell-40045" : {
- "id" : "40045",
- "name" : "Spring4Shell",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40045"
- },
- "Server-Side-Request-Forgery-40046" : {
- "id" : "40046",
- "name" : "Server Side Request Forgery",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40046"
- },
- "Text4shell-(CVE-2022-42889)-40047" : {
- "id" : "40047",
- "name" : "Text4shell (CVE-2022-42889)",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/40047"
- },
- "ExtensionGraphQl-50007" : {
- "id" : "50007",
- "name" : "ExtensionGraphQl",
- "type" : "tool",
- "link" : "https://www.zaproxy.org/docs/alerts/50007"
- },
- "Insecure-JSF-ViewState-90001" : {
- "id" : "90001",
- "name" : "Insecure JSF ViewState",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/90001"
- },
- "Java-Serialization-Object-90002" : {
- "id" : "90002",
- "name" : "Java Serialization Object",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/90002"
- },
- "Sub-Resource-Integrity-Attribute-Missing-90003" : {
- "id" : "90003",
- "name" : "Sub Resource Integrity Attribute Missing",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/90003"
- },
- "Insufficient-Site-Isolation-Against-Spectre-Vulnerability-90004" : {
- "id" : "90004",
- "name" : "Insufficient Site Isolation Against Spectre Vulnerability",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/90004"
- },
- "Fetch-Metadata-Request-Headers-90005" : {
- "id" : "90005",
- "name" : "Fetch Metadata Request Headers",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/90005"
- },
- "Charset-Mismatch-90011" : {
- "id" : "90011",
- "name" : "Charset Mismatch",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/90011"
- },
- "XSLT-Injection-90017" : {
- "id" : "90017",
- "name" : "XSLT Injection",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/90017"
- },
- "Advanced-SQL-Injection-90018" : {
- "id" : "90018",
- "name" : "Advanced SQL Injection",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/90018"
- },
- "Server-Side-Code-Injection-90019" : {
- "id" : "90019",
- "name" : "Server Side Code Injection",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/90019"
- },
- "Remote-OS-Command-Injection-90020" : {
- "id" : "90020",
- "name" : "Remote OS Command Injection",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/90020"
- },
- "XPath-Injection-90021" : {
- "id" : "90021",
- "name" : "XPath Injection",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/90021"
- },
- "Application-Error-Disclosure-90022" : {
- "id" : "90022",
- "name" : "Application Error Disclosure",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/90022"
- },
- "XML-External-Entity-Attack-90023" : {
- "id" : "90023",
- "name" : "XML External Entity Attack",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/90023"
- },
- "Generic-Padding-Oracle-90024" : {
- "id" : "90024",
- "name" : "Generic Padding Oracle",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/90024"
- },
- "Expression-Language-Injection-90025" : {
- "id" : "90025",
- "name" : "Expression Language Injection",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/90025"
- },
- "SOAP-Action-Spoofing-90026" : {
- "id" : "90026",
- "name" : "SOAP Action Spoofing",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/90026"
- },
- "Cookie-Slack-Detector-90027" : {
- "id" : "90027",
- "name" : "Cookie Slack Detector",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/90027"
- },
- "Insecure-HTTP-Method-90028" : {
- "id" : "90028",
- "name" : "Insecure HTTP Method",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/90028"
- },
- "SOAP-XML-Injection-90029" : {
- "id" : "90029",
- "name" : "SOAP XML Injection",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/90029"
- },
- "WSDL-File-Detection-90030" : {
- "id" : "90030",
- "name" : "WSDL File Detection",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/90030"
- },
- "Loosely-Scoped-Cookie-90033" : {
- "id" : "90033",
- "name" : "Loosely Scoped Cookie",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/90033"
- },
- "Cloud-Metadata-Potentially-Exposed-90034" : {
- "id" : "90034",
- "name" : "Cloud Metadata Potentially Exposed",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/90034"
- },
- "Server-Side-Template-Injection-90035" : {
- "id" : "90035",
- "name" : "Server Side Template Injection",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/90035"
- },
- "Server-Side-Template-Injection-(Blind)-90036" : {
- "id" : "90036",
- "name" : "Server Side Template Injection (Blind)",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/90036"
- },
- "NoSQL-Injection-MongoDB-(Time-Based)-90039" : {
- "id" : "90039",
- "name" : "NoSQL Injection - MongoDB (Time Based)",
- "type" : "active",
- "link" : "https://www.zaproxy.org/docs/alerts/90039"
- },
- "Server-is-running-on-Clacks-GNU-Terry-Pratchett-100002" : {
- "id" : "100002",
- "name" : "Server is running on Clacks - GNU Terry Pratchett",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100002"
- },
- "Cookie-Set-Without-HttpOnly-Flag-100003" : {
- "id" : "100003",
- "name" : "Cookie Set Without HttpOnly Flag",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100003"
- },
- "Content-Security-Policy-Violations-Reporting-Enabled-100004" : {
- "id" : "100004",
- "name" : "Content Security Policy Violations Reporting Enabled",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100004"
- },
- "SameSite-Cookie-Attribute-Protection-Used-100005" : {
- "id" : "100005",
- "name" : "SameSite Cookie Attribute Protection Used",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100005"
- },
- "Information-Disclosure-IP-Exposed-via-F5-BIG-IP-Persistence-Cookie-100006" : {
- "id" : "100006",
- "name" : "Information Disclosure - IP Exposed via F5 BIG-IP Persistence Cookie",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100006"
- },
- "Information-Disclosure-Base64-encoded-String-100007" : {
- "id" : "100007",
- "name" : "Information Disclosure - Base64-encoded String",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100007"
- },
- "Information-Disclosure-Credit-Card-Number-100008" : {
- "id" : "100008",
- "name" : "Information Disclosure - Credit Card Number",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100008"
- },
- "Information-Disclosure-Email-Addresses-100009" : {
- "id" : "100009",
- "name" : "Information Disclosure - Email Addresses",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100009"
- },
- "Information-Disclosure-Hash-100010" : {
- "id" : "100010",
- "name" : "Information Disclosure - Hash",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100010"
- },
- "Information-Disclosure-HTML-Comments-100011" : {
- "id" : "100011",
- "name" : "Information Disclosure - HTML Comments",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100011"
- },
- "Information-Disclosure-IBAN-Numbers-100012" : {
- "id" : "100012",
- "name" : "Information Disclosure - IBAN Numbers",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100012"
- },
- "Information-Disclosure-Private-IP-Address-100013" : {
- "id" : "100013",
- "name" : "Information Disclosure - Private IP Address",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100013"
- },
- "Reflected-HTTP-GET-Parameter(s)-100014" : {
- "id" : "100014",
- "name" : "Reflected HTTP GET Parameter(s)",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100014"
- },
- "HUNT-Methodology-100015" : {
- "id" : "100015",
- "name" : "HUNT Methodology",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100015"
- },
- "Missing-Security-Headers-100016" : {
- "id" : "100016",
- "name" : "Missing Security Headers",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100016"
- },
- "Non-Static-Site-Detected-100017" : {
- "id" : "100017",
- "name" : "Non Static Site Detected",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100017"
- },
- "Relative-Path-Overwrite-100018" : {
- "id" : "100018",
- "name" : "Relative Path Overwrite",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100018"
- },
- "Information-Disclosure-Server-Header-100019" : {
- "id" : "100019",
- "name" : "Information Disclosure - Server Header",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100019"
- },
- "Information-Disclosure-SQL-Error-100020" : {
- "id" : "100020",
- "name" : "Information Disclosure - SQL Error",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100020"
- },
- "Telerik-UI-for-ASP.NET-AJAX-Cryptographic-Weakness-(CVE-2017-9248)-100021" : {
- "id" : "100021",
- "name" : "Telerik UI for ASP.NET AJAX Cryptographic Weakness (CVE-2017-9248)",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100021"
- },
- "Upload-Form-Discovered-100022" : {
- "id" : "100022",
- "name" : "Upload Form Discovered",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100022"
- },
- "Information-Disclosure-X-Powered-By-Header-100023" : {
- "id" : "100023",
- "name" : "Information Disclosure - X-Powered-By Header",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100023"
- },
- "Cross-Site-WebSocket-Hijacking-100025" : {
- "id" : "100025",
- "name" : "Cross-Site WebSocket Hijacking",
- "type" : "script active",
- "link" : "https://www.zaproxy.org/docs/alerts/100025"
- },
- "JWT-None-Exploit-100026" : {
- "id" : "100026",
- "name" : "JWT None Exploit",
- "type" : "script active",
- "link" : "https://www.zaproxy.org/docs/alerts/100026"
- },
- "File-Content-Disclosure-(CVE-2019-5418)-100029" : {
- "id" : "100029",
- "name" : "File Content Disclosure (CVE-2019-5418)",
- "type" : "script active",
- "link" : "https://www.zaproxy.org/docs/alerts/100029"
- },
- "Backup-File-Detected-100030" : {
- "id" : "100030",
- "name" : "Backup File Detected",
- "type" : "script active",
- "link" : "https://www.zaproxy.org/docs/alerts/100030"
- },
- "Information-Disclosure-Google-API-Key-100034" : {
- "id" : "100034",
- "name" : "Information Disclosure - Google API Key",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100034"
- },
- "Information-Disclosure-Java-Stack-Trace-100035" : {
- "id" : "100035",
- "name" : "Information Disclosure - Java Stack Trace",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100035"
- },
- "Information-Disclosure-Amazon-S3-Bucket-URL-100036" : {
- "id" : "100036",
- "name" : "Information Disclosure - Amazon S3 Bucket URL",
- "type" : "script passive",
- "link" : "https://www.zaproxy.org/docs/alerts/100036"
- },
- "Application-Error-Disclosure-via-WebSockets-110001" : {
- "id" : "110001",
- "name" : "Application Error Disclosure via WebSockets",
- "type" : "websocket passive",
- "link" : "https://www.zaproxy.org/docs/alerts/110001"
- },
- "Base64-Disclosure-in-WebSocket-message-110002" : {
- "id" : "110002",
- "name" : "Base64 Disclosure in WebSocket message",
- "type" : "websocket passive",
- "link" : "https://www.zaproxy.org/docs/alerts/110002"
- },
- "Information-Disclosure-Debug-Error-Messages-via-WebSocket-110003" : {
- "id" : "110003",
- "name" : "Information Disclosure - Debug Error Messages via WebSocket",
- "type" : "websocket passive",
- "link" : "https://www.zaproxy.org/docs/alerts/110003"
- },
- "Email-address-found-in-WebSocket-message-110004" : {
- "id" : "110004",
- "name" : "Email address found in WebSocket message",
- "type" : "websocket passive",
- "link" : "https://www.zaproxy.org/docs/alerts/110004"
- },
- "Personally-Identifiable-Information-via-WebSocket-110005" : {
- "id" : "110005",
- "name" : "Personally Identifiable Information via WebSocket",
- "type" : "websocket passive",
- "link" : "https://www.zaproxy.org/docs/alerts/110005"
- },
- "Private-IP-Disclosure-via-WebSocket-110006" : {
- "id" : "110006",
- "name" : "Private IP Disclosure via WebSocket",
- "type" : "websocket passive",
- "link" : "https://www.zaproxy.org/docs/alerts/110006"
- },
- "Username-Hash-Found-in-WebSocket-message-110007" : {
- "id" : "110007",
- "name" : "Username Hash Found in WebSocket message",
- "type" : "websocket passive",
- "link" : "https://www.zaproxy.org/docs/alerts/110007"
- },
- "Information-Disclosure-Suspicious-Comments-in-XML-via-WebSocket-110008" : {
- "id" : "110008",
- "name" : "Information Disclosure - Suspicious Comments in XML via WebSocket",
- "type" : "websocket passive",
- "link" : "https://www.zaproxy.org/docs/alerts/110008"
- },
- "Full-Path-Disclosure-110009" : {
- "id" : "110009",
- "name" : "Full Path Disclosure",
- "type" : "passive",
- "link" : "https://www.zaproxy.org/docs/alerts/110009"
- },
- "Information-Disclosure-Information-in-Browser-Storage-120000" : {
- "id" : "120000",
- "name" : "Information Disclosure - Information in Browser Storage",
- "type" : "client passive",
- "link" : "https://www.zaproxy.org/docs/alerts/120000"
- },
- "Information-Disclosure-Sensitive-Information-in-Browser-Storage-120001" : {
- "id" : "120001",
- "name" : "Information Disclosure - Sensitive Information in Browser Storage",
- "type" : "client passive",
- "link" : "https://www.zaproxy.org/docs/alerts/120001"
- },
- "Information-Disclosure-JWT-in-Browser-Storage-120002" : {
- "id" : "120002",
- "name" : "Information Disclosure - JWT in Browser Storage",
- "type" : "client passive",
- "link" : "https://www.zaproxy.org/docs/alerts/120002"
- }
- }
-}
\ No newline at end of file
diff --git a/sechub-pds-solutions/owaspzap/docker/scripts/owasp-zap.sh b/sechub-pds-solutions/owaspzap/docker/scripts/owasp-zap.sh
index 2168042335..fb3ae04865 100755
--- a/sechub-pds-solutions/owaspzap/docker/scripts/owasp-zap.sh
+++ b/sechub-pds-solutions/owaspzap/docker/scripts/owasp-zap.sh
@@ -125,14 +125,7 @@ echo ""
echo "Start scanning"
echo ""
-if [ ! -z "$PDS_SCAN_CONFIGURATION" ] ; then
- sechub_scan_configuration="$PDS_JOB_WORKSPACE_LOCATION/sechubScanConfiguration.json"
- echo "Using configuration file: $sechub_scan_configuration"
- echo "$PDS_SCAN_CONFIGURATION" > "$sechub_scan_configuration"
- zap_options="$zap_options --sechubConfigfile $sechub_scan_configuration"
-fi
-
-java -jar $options "$TOOL_FOLDER/wrapperowaspzap.jar" $zap_options --zapHost "$ZAP_HOST" --zapPort "$ZAP_PORT" --zapApiKey "$ZAP_API_KEY" --jobUUID "$SECHUB_JOB_UUID" --targetURL "$PDS_SCAN_TARGET_URL" --report "$PDS_JOB_RESULT_FILE" --fullRulesetfile "$TOOL_FOLDER/owasp-zap-full-ruleset-all-release-status.json"
+java -jar $options "$TOOL_FOLDER/wrapperowaspzap.jar" $zap_options --zapHost "$ZAP_HOST" --zapPort "$ZAP_PORT" --zapApiKey "$ZAP_API_KEY" --jobUUID "$SECHUB_JOB_UUID" --targetURL "$PDS_SCAN_TARGET_URL" --report "$PDS_JOB_RESULT_FILE"
# Shutdown OWASP-ZAP and cleanup after the scan
echo "Shutdown OWASP-ZAP after scan"
diff --git a/sechub-pds-solutions/owaspzap/helm/pds-owaspzap/Chart.yaml b/sechub-pds-solutions/owaspzap/helm/pds-owaspzap/Chart.yaml
index a9d8e66502..a42eaafc8f 100644
--- a/sechub-pds-solutions/owaspzap/helm/pds-owaspzap/Chart.yaml
+++ b/sechub-pds-solutions/owaspzap/helm/pds-owaspzap/Chart.yaml
@@ -9,4 +9,4 @@ type: application
# This is the chart version.
# This version number should be incremented each time you make changes to the chart and its templates.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 1.5.0
+version: 1.6.0
diff --git a/sechub-pds-solutions/owaspzap/helm/pds-owaspzap/templates/deployment.yaml b/sechub-pds-solutions/owaspzap/helm/pds-owaspzap/templates/deployment.yaml
index 5a5e2840e7..514696b6e0 100644
--- a/sechub-pds-solutions/owaspzap/helm/pds-owaspzap/templates/deployment.yaml
+++ b/sechub-pds-solutions/owaspzap/helm/pds-owaspzap/templates/deployment.yaml
@@ -125,6 +125,8 @@ spec:
value: "{{ .Values.storage.sharedVolume.upload.dir }}"
{{- end}}
ports:
+ - name: pds-health-port
+ containerPort: 10251
- name: pds-https-port
containerPort: 8444
startupProbe:
@@ -132,9 +134,19 @@ spec:
scheme: HTTPS
path: /api/anonymous/check/alive
port: pds-https-port
+ initialDelaySeconds: 5
periodSeconds: 1
- failureThreshold: 300
- # probe every 1s x 300 = 5 mins before restart of container
+ # probe every 1s x 600 = 10 mins before restart of container (some PDS download huge files before startup)
+ failureThreshold: 600
+ successThreshold: 1
+ timeoutSeconds: 1
+ readinessProbe:
+ httpGet:
+ scheme: HTTP
+ path: /actuator/health/readiness
+ port: pds-health-port
+ periodSeconds: 2
+ failureThreshold: 2
successThreshold: 1
timeoutSeconds: 1
livenessProbe:
@@ -145,7 +157,7 @@ spec:
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
- timeoutSeconds: 3
+ timeoutSeconds: 5
volumeMounts:
{{- if .Values.pds.volumes.pdsWorkspace.enabled }}
- mountPath: "/workspace"
diff --git a/sechub-pds-solutions/pmd/helm/pds-pmd/Chart.yaml b/sechub-pds-solutions/pmd/helm/pds-pmd/Chart.yaml
index 61249dea99..5cba443a75 100644
--- a/sechub-pds-solutions/pmd/helm/pds-pmd/Chart.yaml
+++ b/sechub-pds-solutions/pmd/helm/pds-pmd/Chart.yaml
@@ -9,4 +9,4 @@ type: application
# This is the chart version.
# This version number should be incremented each time you make changes to the chart and its templates.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 1.4.0
+version: 1.5.0
diff --git a/sechub-pds-solutions/pmd/helm/pds-pmd/templates/deployment.yaml b/sechub-pds-solutions/pmd/helm/pds-pmd/templates/deployment.yaml
index 74d77f580b..587895f63d 100644
--- a/sechub-pds-solutions/pmd/helm/pds-pmd/templates/deployment.yaml
+++ b/sechub-pds-solutions/pmd/helm/pds-pmd/templates/deployment.yaml
@@ -124,6 +124,8 @@ spec:
value: "{{ .Values.storage.sharedVolume.upload.dir }}"
{{- end}}
ports:
+ - name: pds-health-port
+ containerPort: 10251
- name: pds-https-port
containerPort: 8444
startupProbe:
@@ -131,9 +133,19 @@ spec:
scheme: HTTPS
path: /api/anonymous/check/alive
port: pds-https-port
+ initialDelaySeconds: 5
periodSeconds: 1
- failureThreshold: 300
- # probe every 1s x 300 = 5 mins before restart of container
+ # probe every 1s x 600 = 10 mins before restart of container (some PDS download huge files before startup)
+ failureThreshold: 600
+ successThreshold: 1
+ timeoutSeconds: 1
+ readinessProbe:
+ httpGet:
+ scheme: HTTP
+ path: /actuator/health/readiness
+ port: pds-health-port
+ periodSeconds: 2
+ failureThreshold: 2
successThreshold: 1
timeoutSeconds: 1
livenessProbe:
@@ -144,7 +156,7 @@ spec:
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
- timeoutSeconds: 3
+ timeoutSeconds: 5
volumeMounts:
- mountPath: "/workspace"
name: pds-workspace
diff --git a/sechub-pds-solutions/prepare/helm/pds-prepare/Chart.yaml b/sechub-pds-solutions/prepare/helm/pds-prepare/Chart.yaml
index 9524db9f7b..3a978f803a 100644
--- a/sechub-pds-solutions/prepare/helm/pds-prepare/Chart.yaml
+++ b/sechub-pds-solutions/prepare/helm/pds-prepare/Chart.yaml
@@ -9,4 +9,4 @@ type: application
# This is the chart version.
# This version number should be incremented each time you make changes to the chart and its templates.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 1.4.0
+version: 1.5.0
diff --git a/sechub-pds-solutions/prepare/helm/pds-prepare/templates/deployment.yaml b/sechub-pds-solutions/prepare/helm/pds-prepare/templates/deployment.yaml
index 7d3e8956f1..850d4e7e84 100644
--- a/sechub-pds-solutions/prepare/helm/pds-prepare/templates/deployment.yaml
+++ b/sechub-pds-solutions/prepare/helm/pds-prepare/templates/deployment.yaml
@@ -129,6 +129,8 @@ spec:
- name: PDS_NO_PROXY
value: {{ .Values.proxy.noProxy }}
ports:
+ - name: pds-health-port
+ containerPort: 10251
- name: pds-https-port
containerPort: 8444
startupProbe:
@@ -136,9 +138,19 @@ spec:
scheme: HTTPS
path: /api/anonymous/check/alive
port: pds-https-port
+ initialDelaySeconds: 5
periodSeconds: 1
- failureThreshold: 300
- # probe every 1s x 300 = 5 mins before restart of container
+ # probe every 1s x 600 = 10 mins before restart of container (some PDS download huge files before startup)
+ failureThreshold: 600
+ successThreshold: 1
+ timeoutSeconds: 1
+ readinessProbe:
+ httpGet:
+ scheme: HTTP
+ path: /actuator/health/readiness
+ port: pds-health-port
+ periodSeconds: 2
+ failureThreshold: 2
successThreshold: 1
timeoutSeconds: 1
livenessProbe:
@@ -149,7 +161,7 @@ spec:
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
- timeoutSeconds: 3
+ timeoutSeconds: 5
volumeMounts:
- mountPath: "/workspace"
name: pds-workspace
diff --git a/sechub-pds-solutions/scancode/helm/pds-scancode/Chart.yaml b/sechub-pds-solutions/scancode/helm/pds-scancode/Chart.yaml
index b8ec812e50..cd1797fc7f 100644
--- a/sechub-pds-solutions/scancode/helm/pds-scancode/Chart.yaml
+++ b/sechub-pds-solutions/scancode/helm/pds-scancode/Chart.yaml
@@ -9,4 +9,4 @@ type: application
# This is the chart version.
# This version number should be incremented each time you make changes to the chart and its templates.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 1.4.0
+version: 1.5.0
diff --git a/sechub-pds-solutions/scancode/helm/pds-scancode/templates/deployment.yaml b/sechub-pds-solutions/scancode/helm/pds-scancode/templates/deployment.yaml
index 50280d8fd6..f3e66c12b6 100644
--- a/sechub-pds-solutions/scancode/helm/pds-scancode/templates/deployment.yaml
+++ b/sechub-pds-solutions/scancode/helm/pds-scancode/templates/deployment.yaml
@@ -124,6 +124,8 @@ spec:
value: "{{ .Values.storage.sharedVolume.upload.dir }}"
{{- end}}
ports:
+ - name: pds-health-port
+ containerPort: 10251
- name: pds-https-port
containerPort: 8444
startupProbe:
@@ -131,9 +133,19 @@ spec:
scheme: HTTPS
path: /api/anonymous/check/alive
port: pds-https-port
+ initialDelaySeconds: 5
periodSeconds: 1
- failureThreshold: 300
- # probe every 1s x 300 = 5 mins before restart of container
+ # probe every 1s x 600 = 10 mins before restart of container (some PDS download huge files before startup)
+ failureThreshold: 600
+ successThreshold: 1
+ timeoutSeconds: 1
+ readinessProbe:
+ httpGet:
+ scheme: HTTP
+ path: /actuator/health/readiness
+ port: pds-health-port
+ periodSeconds: 2
+ failureThreshold: 2
successThreshold: 1
timeoutSeconds: 1
livenessProbe:
@@ -144,7 +156,7 @@ spec:
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
- timeoutSeconds: 3
+ timeoutSeconds: 5
volumeMounts:
- mountPath: "/workspace"
name: pds-workspace
diff --git a/sechub-pds-solutions/xray/helm/pds-xray/Chart.yaml b/sechub-pds-solutions/xray/helm/pds-xray/Chart.yaml
index d230dd3449..6ce70cb0c1 100644
--- a/sechub-pds-solutions/xray/helm/pds-xray/Chart.yaml
+++ b/sechub-pds-solutions/xray/helm/pds-xray/Chart.yaml
@@ -9,4 +9,4 @@ type: application
# This is the chart version.
# This version number should be incremented each time you make changes to the chart and its templates.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 1.3.0
+version: 1.4.0
diff --git a/sechub-pds-solutions/xray/helm/pds-xray/templates/deployment.yaml b/sechub-pds-solutions/xray/helm/pds-xray/templates/deployment.yaml
index 41ee7c0b79..6fde67ba1c 100644
--- a/sechub-pds-solutions/xray/helm/pds-xray/templates/deployment.yaml
+++ b/sechub-pds-solutions/xray/helm/pds-xray/templates/deployment.yaml
@@ -133,6 +133,8 @@ spec:
- name: XRAY_PASSWORD
value: {{ .Values.xrayWrapper.artifactory.password }}
ports:
+ - name: pds-health-port
+ containerPort: 10251
- name: pds-https-port
containerPort: 8444
startupProbe:
@@ -140,9 +142,19 @@ spec:
scheme: HTTPS
path: /api/anonymous/check/alive
port: pds-https-port
+ initialDelaySeconds: 5
periodSeconds: 1
- failureThreshold: 300
- # probe every 1s x 300 = 5 mins before restart of container
+ # probe every 1s x 600 = 10 mins before restart of container (some PDS download huge files before startup)
+ failureThreshold: 600
+ successThreshold: 1
+ timeoutSeconds: 1
+ readinessProbe:
+ httpGet:
+ scheme: HTTP
+ path: /actuator/health/readiness
+ port: pds-health-port
+ periodSeconds: 2
+ failureThreshold: 2
successThreshold: 1
timeoutSeconds: 1
livenessProbe:
@@ -153,7 +165,7 @@ spec:
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
- timeoutSeconds: 3
+ timeoutSeconds: 5
volumeMounts:
- mountPath: "/workspace"
name: pds-workspace
diff --git a/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/batch/PDSBatchTriggerService.java b/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/batch/PDSBatchTriggerService.java
index 5fa5765a36..6485696c34 100644
--- a/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/batch/PDSBatchTriggerService.java
+++ b/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/batch/PDSBatchTriggerService.java
@@ -16,6 +16,8 @@
import com.mercedesbenz.sechub.pds.execution.PDSExecutionService;
import com.mercedesbenz.sechub.pds.job.PDSJobRepository;
import com.mercedesbenz.sechub.pds.job.PDSJobTransactionService;
+import com.mercedesbenz.sechub.pds.usecase.PDSStep;
+import com.mercedesbenz.sechub.pds.usecase.UseCaseSystemExecutesJob;
import jakarta.annotation.PostConstruct;
@@ -59,6 +61,7 @@ protected void postConstruct() {
// default 10 seconds delay and 5 seconds initial
@Scheduled(initialDelayString = "${pds.config.trigger.nextjob.initialdelay:" + DEFAULT_INITIAL_DELAY_MILLIS
+ "}", fixedDelayString = "${pds.config.trigger.nextjob.delay:" + DEFAULT_FIXED_DELAY_MILLIS + "}")
+ @UseCaseSystemExecutesJob(@PDSStep(number = 1, name = "PDS trigger service fills queue", description = "Trigger service adds jobs to queue (when queue not full)"))
public void triggerExecutionOfNextJob() {
if (!schedulingEnabled) {
LOG.trace("Trigger execution of next job canceled, because scheduling disabled.");
@@ -86,8 +89,7 @@ public void triggerExecutionOfNextJob() {
} catch (ObjectOptimisticLockingFailureException e) {
/*
* This can happen when PDS instances are started at same time, so the check for
- * next jobs can lead to race condiitons - and optmistic locks will occurre
- * here.
+ * next jobs can lead to race conditions - and optimistic locks will occur here.
*
* To avoid this to happen again, we wait a random time here. So next call on
* this machine should normally not collide again.
diff --git a/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/execution/PDSExecutionCallable.java b/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/execution/PDSExecutionCallable.java
index 6dfb1085f2..61bac36ec3 100644
--- a/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/execution/PDSExecutionCallable.java
+++ b/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/execution/PDSExecutionCallable.java
@@ -39,6 +39,7 @@
import com.mercedesbenz.sechub.pds.usecase.UseCaseAdminFetchesJobErrorStream;
import com.mercedesbenz.sechub.pds.usecase.UseCaseAdminFetchesJobMetaData;
import com.mercedesbenz.sechub.pds.usecase.UseCaseAdminFetchesJobOutputStream;
+import com.mercedesbenz.sechub.pds.usecase.UseCaseSystemExecutesJob;
import com.mercedesbenz.sechub.pds.usecase.UseCaseSystemHandlesJobCancelRequests;
import com.mercedesbenz.sechub.pds.util.PDSResilientRetryExecutor;
import com.mercedesbenz.sechub.pds.util.PDSResilientRetryExecutor.ExceptionThrower;
@@ -91,6 +92,7 @@ public void throwException(String message, Exception cause) throws IllegalStateE
}
@Override
+ @UseCaseSystemExecutesJob(@PDSStep(number = 3, name = "PDS execution call", description = "Central point of PDS job execution."))
public PDSExecutionResult call() throws Exception {
LOG.info("Prepare execution of PDS job: {}", pdsJobUUID);
PDSExecutionResult result = new PDSExecutionResult();
diff --git a/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/execution/PDSExecutionEnvironmentService.java b/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/execution/PDSExecutionEnvironmentService.java
index 27fdcaa7eb..3896d68bcc 100644
--- a/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/execution/PDSExecutionEnvironmentService.java
+++ b/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/execution/PDSExecutionEnvironmentService.java
@@ -105,6 +105,10 @@ private void addPdsJobRelatedVariables(UUID pdsJobUUID, Map map)
map.put(PDS_JOB_SOURCECODE_ZIP_FILE, locationData.getSourceCodeZipFileLocation());
map.put(PDS_JOB_BINARIES_TAR_FILE, locationData.getBinariesTarFileLocation());
+ String extractedAssetsLocation = locationData.getExtractedAssetsLocation();
+
+ map.put(PDS_JOB_EXTRACTED_ASSETS_FOLDER, extractedAssetsLocation);
+
String extractedSourcesLocation = locationData.getExtractedSourcesLocation();
map.put(PDS_JOB_SOURCECODE_UNZIPPED_FOLDER, extractedSourcesLocation);
diff --git a/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/execution/PDSExecutionService.java b/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/execution/PDSExecutionService.java
index 71131c1981..28f57eb91e 100644
--- a/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/execution/PDSExecutionService.java
+++ b/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/execution/PDSExecutionService.java
@@ -37,6 +37,7 @@
import com.mercedesbenz.sechub.pds.job.PDSWorkspaceService;
import com.mercedesbenz.sechub.pds.usecase.PDSStep;
import com.mercedesbenz.sechub.pds.usecase.UseCaseAdminFetchesMonitoringStatus;
+import com.mercedesbenz.sechub.pds.usecase.UseCaseSystemExecutesJob;
import com.mercedesbenz.sechub.pds.usecase.UseCaseSystemHandlesJobCancelRequests;
import com.mercedesbenz.sechub.pds.usecase.UseCaseSystemSigTermHandling;
@@ -205,6 +206,7 @@ public boolean isQueueFull() {
}
@Async
+ @UseCaseSystemExecutesJob(@PDSStep(number = 2, name = "Add Job to queue", description = "PDS job is added to queue"))
public void addToExecutionQueueAsynchron(UUID jobUUID) {
Future> former = null;
synchronized (jobsInQueue) {
diff --git a/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/job/PDSWorkspaceService.java b/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/job/PDSWorkspaceService.java
index 329354db30..6fc14bbdfd 100644
--- a/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/job/PDSWorkspaceService.java
+++ b/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/job/PDSWorkspaceService.java
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT
package com.mercedesbenz.sechub.pds.job;
-import static com.mercedesbenz.sechub.commons.core.CommonConstants.FILENAME_BINARIES_TAR;
-import static com.mercedesbenz.sechub.commons.core.CommonConstants.FILENAME_SOURCECODE_ZIP;
+import static com.mercedesbenz.sechub.commons.core.CommonConstants.*;
+import static java.util.Objects.*;
import java.io.File;
import java.io.FileFilter;
@@ -15,6 +15,7 @@
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.time.Duration;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -34,10 +35,14 @@
import com.mercedesbenz.sechub.commons.archive.ArchiveSupport.ArchiveType;
import com.mercedesbenz.sechub.commons.archive.FileSize;
import com.mercedesbenz.sechub.commons.archive.SecHubFileStructureDataProvider;
+import com.mercedesbenz.sechub.commons.core.security.CheckSumSupport;
import com.mercedesbenz.sechub.commons.model.JSONConverter;
import com.mercedesbenz.sechub.commons.model.JSONConverterException;
import com.mercedesbenz.sechub.commons.model.ScanType;
import com.mercedesbenz.sechub.commons.model.SecHubConfigurationModel;
+import com.mercedesbenz.sechub.commons.model.template.TemplateType;
+import com.mercedesbenz.sechub.commons.pds.data.PDSTemplateMetaData;
+import com.mercedesbenz.sechub.commons.pds.data.PDSTemplateMetaData.PDSAssetData;
import com.mercedesbenz.sechub.commons.pds.execution.ExecutionEventData;
import com.mercedesbenz.sechub.commons.pds.execution.ExecutionEventDetailIdentifier;
import com.mercedesbenz.sechub.commons.pds.execution.ExecutionEventType;
@@ -47,18 +52,24 @@
import com.mercedesbenz.sechub.pds.config.PDSServerConfigurationService;
import com.mercedesbenz.sechub.pds.storage.PDSMultiStorageService;
import com.mercedesbenz.sechub.pds.storage.PDSStorageInfoCollector;
+import com.mercedesbenz.sechub.pds.usecase.PDSStep;
+import com.mercedesbenz.sechub.pds.usecase.UseCaseSystemExecutesJob;
import com.mercedesbenz.sechub.pds.util.PDSArchiveSupportProvider;
import com.mercedesbenz.sechub.pds.util.PDSResilientRetryExecutor;
import com.mercedesbenz.sechub.pds.util.PDSResilientRetryExecutor.ExceptionThrower;
+import com.mercedesbenz.sechub.storage.core.AssetStorage;
import com.mercedesbenz.sechub.storage.core.JobStorage;
+import com.mercedesbenz.sechub.storage.core.Storage;
@Service
public class PDSWorkspaceService {
public static final String UPLOAD = "upload";
+ public static final String ASSETS = "assets";
public static final String EXTRACTED = "extracted";
public static final String EXTRACTED_SOURCES = EXTRACTED + "/sources";
public static final String EXTRACTED_BINARIES = EXTRACTED + "/binaries";
+ public static final String EXTRACTED_ASSETS = EXTRACTED + "/assets";
public static final String OUTPUT = "output";
public static final String MESSAGES = "messages";
@@ -120,6 +131,9 @@ public class PDSWorkspaceService {
@Autowired
PDSWorkspacePreparationResultCalculator preparationResultCalculator;
+ @Autowired
+ CheckSumSupport checksumSupport;
+
private static final ArchiveFilter TAR_FILE_FILTER = new TarFileFilter();
private static final ArchiveFilter ZIP_FILE_FILTER = new SourcecodeZipFileFilter();
@@ -151,6 +165,7 @@ public void throwException(String message, Exception cause) throws IOException {
* @return {@link PDSWorkspacePreparationResult}, never null
* @throws IOException
*/
+ @UseCaseSystemExecutesJob(@PDSStep(number = 4, name = "PDS workspace preparation", description = "PDS job workspace is prepared here: Directories are created, files are downloaded and extracted etc."))
public PDSWorkspacePreparationResult prepare(UUID pdsJobUUID, PDSJobConfiguration config, String metaData) throws IOException {
PDSJobConfigurationSupport configurationSupport = new PDSJobConfigurationSupport(config);
@@ -162,6 +177,8 @@ public PDSWorkspacePreparationResult prepare(UUID pdsJobUUID, PDSJobConfiguratio
writeMetaData(pdsJobUUID, metaData);
+ importAndExtractFilesFromAssetStorage(pdsJobUUID, config, configurationSupport, preparationContext);
+
importWantedFilesFromJobStorage(pdsJobUUID, config, configurationSupport, preparationContext);
extractZipFileUploadsWhenConfigured(pdsJobUUID, config, preparationContext);
@@ -188,7 +205,7 @@ private void importWantedFilesFromJobStorage(UUID pdsJobUUID, PDSJobConfiguratio
PDSResilientRetryExecutor resilientStorageReadExecutor = createResilientReadExecutor(preparationContext);
File jobFolder = getUploadFolder(pdsJobUUID);
- JobStorage storage = fetchStorage(pdsJobUUID, config);
+ JobStorage storage = fetchJobStorage(pdsJobUUID, config);
Set names = resilientStorageReadExecutor.execute(() -> storage.listNames(), "List storage names for job: " + pdsJobUUID.toString());
@@ -209,6 +226,79 @@ private void importWantedFilesFromJobStorage(UUID pdsJobUUID, PDSJobConfiguratio
} finally {
storage.close();
}
+
+ }
+
+ private void importAndExtractFilesFromAssetStorage(UUID pdsJobUUID, PDSJobConfiguration config, PDSJobConfigurationSupport configurationSupport,
+ PDSWorkspacePreparationContext preparationContext) throws IOException {
+
+ List templateMetaDataList = configurationSupport.getTemplateMetaData();
+ if (templateMetaDataList.isEmpty()) {
+ LOG.debug("No template meta data available - will skipp asset data import");
+ return;
+ }
+
+ PDSResilientRetryExecutor resilientStorageReadExecutor = createResilientReadExecutor(preparationContext);
+
+ for (PDSTemplateMetaData metaData : templateMetaDataList) {
+ TemplateType templateType = requireNonNull(metaData.getTemplateType(), "Template type may not be null");
+ PDSAssetData assetData = requireNonNull(metaData.getAssetData(), "Asset data may not be null");
+ String fileName = requireNonBlank(assetData.getFileName(), "Filename must be defined");
+ String assetId = requireNonBlank(assetData.getAssetId(), "Asset id must be defined");
+ String checksumFromSecHub = requireNonBlank(assetData.getChecksum(), "Checksum must be defined.");
+
+ File assetDownloadFile = getAssetFileFromUpload(pdsJobUUID, assetId, fileName);
+ String assetFilePath = assetId + "/" + fileName;
+
+ resilientStorageReadExecutor.execute(() -> {
+
+ try (AssetStorage storage = storageService.createAssetStorage(assetId)) {
+ InputStream storageStream = storage.fetch(fileName);
+ readAndCopyInputStreamToFileSystem(pdsJobUUID, fileName, assetDownloadFile, storageStream);
+
+ String checksumAfterDownloadFromStorage = checksumSupport.createSha256Checksum(assetDownloadFile.toPath());
+
+ if (!checksumAfterDownloadFromStorage.equals(checksumFromSecHub)) {
+ throw new IOException("Checksum not as expected for asset file:" + assetFilePath + "\nSecHub checksum:" + checksumFromSecHub
+ + ", Download checksum:" + checksumAfterDownloadFromStorage);
+ }
+ }
+ }, "Read and copy asset file: " + assetFilePath + " for job: " + pdsJobUUID);
+
+ File extractionFolder = getAssetExtractionFolder(pdsJobUUID, templateType);
+ if (fileName.toLowerCase().endsWith(".zip")) {
+ LOG.info("Start extraction of asset file '{}'", fileName);
+ /* extract ZIP file */
+ ArchiveExtractionConstraints archiveExtractionConstraints = createExtractionContraints();
+ archiveSupportProvider.getArchiveSupport().extractFileAsIsToFolder(ArchiveType.ZIP, assetDownloadFile, extractionFolder,
+ archiveExtractionConstraints);
+ } else {
+ /*
+ * This case will only happen in PDS solution development:
+ *
+ * At development time, it can happen that there is a need to directly upload
+ * and change asset files in storage to make things easier/faster turn around
+ * times.
+ *
+ * But SecHub will always use "$pdsProductIdentifier.zip" as the asset file name
+ * for the product (if there exists one in storage) and send this inside
+ * parameters!
+ */
+ LOG.warn(
+ "Asset file name '{}' does not end with '.zip' - will just copy the file to extraction folder. This may ONLY happen at PDS solution development time, but not in production by SecHub!",
+ fileName);
+ FileUtils.copyFile(assetDownloadFile, new File(extractionFolder, fileName));
+ }
+
+ }
+
+ }
+
+ private static final String requireNonBlank(String target, String message) {
+ if (requireNonNull(target, message).isBlank()) {
+ throw new IllegalStateException(message);
+ }
+ return target;
}
private PDSResilientRetryExecutor createResilientReadExecutor(PDSWorkspacePreparationContext preparationContext) {
@@ -220,30 +310,34 @@ private PDSResilientRetryExecutor createResilientReadExecutor(PDSWo
return resilientExecutor;
}
- private void readAndCopyStorageToFileSystem(UUID jobUUID, File jobFolder, JobStorage storage, String name) throws IOException {
+ private void readAndCopyStorageToFileSystem(UUID jobUUID, File jobFolder, Storage storage, String fileName) throws IOException {
- File uploadFile = new File(jobFolder, name);
+ File uploadFile = new File(jobFolder, fileName);
- try (InputStream fetchedInputStream = storage.fetch(name)) {
+ try (InputStream fetchedInputStream = storage.fetch(fileName)) {
- try {
+ readAndCopyInputStreamToFileSystem(jobUUID, fileName, uploadFile, fetchedInputStream);
+ }
- FileUtils.copyInputStreamToFile(fetchedInputStream, uploadFile);
+ }
- LOG.debug("Imported '{}' for job {} from storage to {}", name, jobUUID, uploadFile.getAbsolutePath());
+ private void readAndCopyInputStreamToFileSystem(UUID jobUUID, String fileName, File uploadFile, InputStream fetchedInputStream) throws IOException {
+ try {
- } catch (IOException e) {
+ FileUtils.copyInputStreamToFile(fetchedInputStream, uploadFile);
- LOG.error("Was not able to copy stream of uploaded file: {} for job {}, reason: ", name, jobUUID, e.getMessage());
+ LOG.debug("Imported '{}' for job {} from storage to {}", fileName, jobUUID, uploadFile.getAbsolutePath());
- if (uploadFile.exists()) {
- boolean deleteSuccessful = uploadFile.delete();
- LOG.info("Uploaded file existed. Deleted successfully: {}", deleteSuccessful);
- }
- throw e;
+ } catch (IOException e) {
+
+ LOG.error("Was not able to copy stream of uploaded file: {} for job {}, reason: ", fileName, jobUUID, e.getMessage());
+
+ if (uploadFile.exists()) {
+ boolean deleteSuccessful = uploadFile.delete();
+ LOG.info("Uploaded file existed. Deleted successfully: {}", deleteSuccessful);
}
+ throw e;
}
-
}
void extractZipFileUploadsWhenConfigured(UUID jobUUID, PDSJobConfiguration config, PDSWorkspacePreparationContext preparationContext) throws IOException {
@@ -288,7 +382,7 @@ private boolean isWantedStorageContent(String name, PDSJobConfigurationSupport c
return false;
}
- private JobStorage fetchStorage(UUID pdsJobUUID, PDSJobConfiguration config) {
+ private JobStorage fetchJobStorage(UUID pdsJobUUID, PDSJobConfiguration config) {
UUID pdsOrSecHubJobUUID;
String storagePath;
@@ -325,6 +419,41 @@ public File getUploadFolder(UUID pdsJobUUID) {
return file;
}
+ /**
+ * Resolves assets folder - if not existing it will be created
+ *
+ * @param pdsJobUUID
+ * @return assets folder
+ */
+ private Path getAssetUploadFolder(UUID pdsJobUUID) {
+ File file = new File(getUploadFolder(pdsJobUUID), ASSETS);
+ file.mkdirs();
+ return file.toPath();
+ }
+
+ /**
+ * Resolves asset extraction folder - if not existing it will be created
+ *
+ * @param pdsJobUUID
+ * @param templateType
+ * @return asset extraction folder
+ */
+ public File getAssetExtractionFolder(UUID pdsJobUUID, TemplateType templateType) {
+ Path parent = getUploadFolder(pdsJobUUID).toPath();
+ Path assetsExtractedPath = parent.resolve(EXTRACTED_ASSETS);
+ Path templateTypeExtractedPath = assetsExtractedPath.resolve(templateType.getId());
+ File file = templateTypeExtractedPath.toFile();
+ file.mkdirs();
+ return file;
+ }
+
+ private File getAssetFileFromUpload(UUID pdsJobUUID, String assetId, String fileName) {
+ Path parent = getAssetUploadFolder(pdsJobUUID);
+ Path assetIdDownloadPath = parent.resolve(assetId);
+ assetIdDownloadPath.toFile().mkdirs();
+ return assetIdDownloadPath.resolve(fileName).toFile();
+ }
+
private Path getWorkspaceFolderPath(UUID jobUUID) {
File workspaceFolder = getWorkspaceFolder(jobUUID);
Path workspaceFolderPath = workspaceFolder.toPath();
@@ -418,8 +547,7 @@ private boolean extractArchives(UUID pdsJobUUID, boolean deleteOriginFiles, SecH
}
ArchiveSupport archiveSupport = archiveSupportProvider.getArchiveSupport();
- ArchiveExtractionConstraints archiveExtractionConstraints = new ArchiveExtractionConstraints(archiveExtractionMaxFileSizeUncompressed,
- archiveExtractionMaxEntries, archiveExtractionMaxDirectoryDepth, archiveExtractionTimeout);
+ ArchiveExtractionConstraints archiveExtractionConstraints = createExtractionContraints();
for (File archiveFile : archiveFiles) {
try (FileInputStream archiveFileInputStream = new FileInputStream(archiveFile)) {
@@ -443,6 +571,12 @@ private boolean extractArchives(UUID pdsJobUUID, boolean deleteOriginFiles, SecH
return true;
}
+ private ArchiveExtractionConstraints createExtractionContraints() {
+ ArchiveExtractionConstraints archiveExtractionConstraints = new ArchiveExtractionConstraints(archiveExtractionMaxFileSizeUncompressed,
+ archiveExtractionMaxEntries, archiveExtractionMaxDirectoryDepth, archiveExtractionTimeout);
+ return archiveExtractionConstraints;
+ }
+
public void cleanup(UUID jobUUID, PDSJobConfiguration config) throws IOException {
FileUtils.deleteDirectory(getWorkspaceFolder(jobUUID));
LOG.info("Removed workspace folder for job {}", jobUUID);
@@ -453,7 +587,7 @@ public void cleanup(UUID jobUUID, PDSJobConfiguration config) throws IOException
LOG.info("Removed NOT storage for PDS job {} because sechub storage and will be handled by sechub job {}", jobUUID, config.getSechubJobUUID());
} else {
- JobStorage storage = fetchStorage(jobUUID, config);
+ JobStorage storage = fetchJobStorage(jobUUID, config);
storage.deleteAll();
LOG.info("Removed storage for job {}", jobUUID);
storage.close();
@@ -549,6 +683,7 @@ public WorkspaceLocationData createLocationData(UUID jobUUID) {
locationData.extractedSourcesLocation = createExtractedSourcesLocation(workspaceFolderPath).toString();
locationData.extractedBinariesLocation = createExtractedBinariesLocation(workspaceFolderPath).toString();
+ locationData.extractedAssetsLocation = createExtractedAssetsLocation(workspaceFolderPath).toString();
locationData.sourceCodeZipFileLocation = createSourceCodeZipFileLocation(workspaceFolderPath).toString();
locationData.binariesTarFileLocation = createBinariesTarFileLocation(workspaceFolderPath).toString();
@@ -679,6 +814,10 @@ private Path createExtractedSourcesLocation(Path workspaceFolderPath) {
return createWorkspacePathAndEnsureParentDirectories(workspaceFolderPath, UPLOAD + "/" + EXTRACTED_SOURCES);
}
+ private Path createExtractedAssetsLocation(Path workspaceFolderPath) {
+ return createWorkspacePathAndEnsureParentDirectories(workspaceFolderPath, UPLOAD + "/" + EXTRACTED_ASSETS);
+ }
+
private Path createWorkspacePathAndEnsureDirectory(Path workspaceLocation, String subPath) {
Path path = createWorkspacePathAndEnsureParentDirectories(workspaceLocation, subPath);
if (!Files.exists(path)) {
diff --git a/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/job/WorkspaceLocationData.java b/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/job/WorkspaceLocationData.java
index 6ee704ba75..36a386f261 100644
--- a/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/job/WorkspaceLocationData.java
+++ b/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/job/WorkspaceLocationData.java
@@ -11,6 +11,7 @@ public class WorkspaceLocationData {
String userMessagesLocation;
String metaDataFileLocation;
String eventsLocation;
+ String extractedAssetsLocation;
public String getWorkspaceLocation() {
return workspaceLocation;
@@ -47,4 +48,8 @@ public String getMetaDataFileLocation() {
public String getEventsLocation() {
return eventsLocation;
}
+
+ public String getExtractedAssetsLocation() {
+ return extractedAssetsLocation;
+ }
}
diff --git a/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/storage/PDSMultiStorageService.java b/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/storage/PDSMultiStorageService.java
index 9ed628b700..efdbd681ef 100644
--- a/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/storage/PDSMultiStorageService.java
+++ b/sechub-pds/src/main/java/com/mercedesbenz/sechub/pds/storage/PDSMultiStorageService.java
@@ -9,6 +9,8 @@
import org.springframework.stereotype.Service;
import com.mercedesbenz.sechub.pds.config.PDSServerConfigurationService;
+import com.mercedesbenz.sechub.storage.core.AssetStorage;
+import com.mercedesbenz.sechub.storage.core.AssetStorageFactory;
import com.mercedesbenz.sechub.storage.core.JobStorage;
import com.mercedesbenz.sechub.storage.core.JobStorageFactory;
import com.mercedesbenz.sechub.storage.core.S3Setup;
@@ -34,22 +36,30 @@ public class PDSMultiStorageService implements StorageService {
private static final Logger LOG = LoggerFactory.getLogger(PDSMultiStorageService.class);
private JobStorageFactory jobStorageFactory;
+ private AssetStorageFactory assetStorageFactory;
@Autowired
public PDSMultiStorageService(SharedVolumeSetup sharedVolumeSetup, S3Setup s3Setup) {
if (s3Setup.isAvailable()) {
- jobStorageFactory = new AwsS3JobStorageFactory(s3Setup);
+ AwsS3JobStorageFactory awsJobFactory = new AwsS3JobStorageFactory(s3Setup);
+
+ jobStorageFactory = awsJobFactory;
+ assetStorageFactory = awsJobFactory;
} else if (sharedVolumeSetup.isAvailable()) {
- jobStorageFactory = new SharedVolumeJobStorageFactory(sharedVolumeSetup);
+ SharedVolumeJobStorageFactory sharedVolumeStorageFactory = new SharedVolumeJobStorageFactory(sharedVolumeSetup);
+
+ jobStorageFactory = sharedVolumeStorageFactory;
+ assetStorageFactory = sharedVolumeStorageFactory;
}
- if (jobStorageFactory == null) {
+ if (jobStorageFactory == null || assetStorageFactory == null) {
throw new IllegalStateException("Did not found any available storage setup! At least one must be set!");
}
LOG.info("Created storage factory: {}", jobStorageFactory.getClass().getSimpleName());
+ LOG.info("Created asset storage factory: {}", assetStorageFactory.getClass().getSimpleName());
}
@@ -64,4 +74,9 @@ public JobStorage createJobStorageForPath(String storagePath, UUID jobUUID) {
return jobStorage;
}
+ @Override
+ public AssetStorage createAssetStorage(String assetId) {
+ return assetStorageFactory.createAssetStorage(assetId);
+ }
+
}
\ No newline at end of file
diff --git a/sechub-pds/src/test/java/com/mercedesbenz/sechub/pds/job/PDSWorkspaceServiceTest.java b/sechub-pds/src/test/java/com/mercedesbenz/sechub/pds/job/PDSWorkspaceServiceTest.java
index d7791fd554..220567d65f 100644
--- a/sechub-pds/src/test/java/com/mercedesbenz/sechub/pds/job/PDSWorkspaceServiceTest.java
+++ b/sechub-pds/src/test/java/com/mercedesbenz/sechub/pds/job/PDSWorkspaceServiceTest.java
@@ -3,27 +3,37 @@
import static com.mercedesbenz.sechub.test.TestConstants.*;
import static java.io.File.*;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.Collections;
+import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import com.mercedesbenz.sechub.commons.core.security.CheckSumSupport;
+import com.mercedesbenz.sechub.commons.model.JSONConverter;
+import com.mercedesbenz.sechub.commons.model.template.TemplateType;
import com.mercedesbenz.sechub.commons.pds.PDSDefaultParameterKeyConstants;
+import com.mercedesbenz.sechub.commons.pds.data.PDSTemplateMetaData;
+import com.mercedesbenz.sechub.commons.pds.data.PDSTemplateMetaData.PDSAssetData;
import com.mercedesbenz.sechub.pds.commons.core.config.PDSProductSetup;
import com.mercedesbenz.sechub.pds.config.PDSServerConfigurationService;
import com.mercedesbenz.sechub.pds.execution.PDSExecutionParameterEntry;
import com.mercedesbenz.sechub.pds.storage.PDSMultiStorageService;
import com.mercedesbenz.sechub.pds.storage.PDSStorageInfoCollector;
+import com.mercedesbenz.sechub.storage.core.AssetStorage;
import com.mercedesbenz.sechub.storage.core.JobStorage;
import com.mercedesbenz.sechub.test.TestFileReader;
import com.mercedesbenz.sechub.test.TestUtil;
@@ -41,6 +51,7 @@ class PDSWorkspaceServiceTest {
private PDSWorkspacePreparationResultCalculator preparationResultCalculator;
private PDSWorkspacePreparationContext preparationContext;
private UUID jobUUID;
+ private CheckSumSupport checksumSupport;
@BeforeAll
static void beforeAll() throws IOException {
@@ -51,14 +62,15 @@ static void beforeAll() throws IOException {
void beforeEach() {
jobUUID = UUID.randomUUID();
- storageService = mock(PDSMultiStorageService.class);
- storage = mock(JobStorage.class);
- storageInfoCollector = mock(PDSStorageInfoCollector.class);
- preparationContextFactory = mock(PDSWorkspacePreparationContextFactory.class);
- serverConfigService = mock(PDSServerConfigurationService.class);
- preparationResultCalculator = mock(PDSWorkspacePreparationResultCalculator.class);
+ storageService = mock();
+ storage = mock();
+ storageInfoCollector = mock();
+ preparationContextFactory = mock();
+ serverConfigService = mock();
+ preparationResultCalculator = mock();
+ checksumSupport = mock();
- preparationContext = mock(PDSWorkspacePreparationContext.class);
+ preparationContext = mock();
when(preparationContextFactory.createPreparationContext(any())).thenReturn(preparationContext);
PDSProductSetup setup = new PDSProductSetup();
@@ -73,6 +85,7 @@ void beforeEach() {
serviceToTest.preparationContextFactory = preparationContextFactory;
serviceToTest.serverConfigService = serverConfigService;
serviceToTest.preparationResultCalculator = preparationResultCalculator;
+ serviceToTest.checksumSupport = checksumSupport;
config = new PDSJobConfiguration();
@@ -145,7 +158,7 @@ void prepare_returns_result_from_calculator() throws Exception {
PDSWorkspacePreparationResult result = serviceToTest.prepare(jobUUID, config, null);
/* test */
- assertSame(expected, result);
+ assertThat(expected).isSameAs(result);
}
@Test
@@ -156,7 +169,7 @@ void when_job_has_no_metadata_no_metadata_file_is_created_in_workspace() throws
/* test */
File metaDataFile = serviceToTest.getMetaDataFile(jobUUID);
- assertFalse(metaDataFile.exists());
+ assertThat(metaDataFile.exists()).isFalse();
}
@@ -168,8 +181,8 @@ void when_job_has_metadata_a_metadata_file_is_created_in_workspace_containing_co
/* test */
File metaDataFile = serviceToTest.getMetaDataFile(jobUUID);
- assertTrue(metaDataFile.exists());
- assertEquals("this is my metadata", TestFileReader.readTextFromFile(metaDataFile));
+ assertThat(metaDataFile.exists()).isTrue();
+ assertThat(TestFileReader.readTextFromFile(metaDataFile)).isEqualTo("this is my metadata");
}
@Test
@@ -182,14 +195,15 @@ void createLocationData_contains_expected_pathes_when_using_temp_directory_as_up
String expectedWorspaceLocation = workspaceRootFolderPath + separatorChar + jobUUID;
/* @formatter:off */
- assertEquals(expectedWorspaceLocation,result.getWorkspaceLocation());
- assertEquals(expectedWorspaceLocation+separatorChar+"output"+separatorChar+"result.txt",result.getResultFileLocation());
- assertEquals(expectedWorspaceLocation+separatorChar+"output"+separatorChar+"messages",result.getUserMessagesLocation());
- assertEquals(expectedWorspaceLocation+separatorChar+"metadata.txt",result.getMetaDataFileLocation());
- assertEquals(expectedWorspaceLocation+separatorChar+"upload"+separatorChar+SOURCECODE_ZIP,result.getSourceCodeZipFileLocation());
- assertEquals(expectedWorspaceLocation+separatorChar+"upload"+separatorChar+"extracted"+separatorChar+"sources",result.getExtractedSourcesLocation());
- assertEquals(expectedWorspaceLocation+separatorChar+"upload"+separatorChar+"extracted"+separatorChar+"binaries",result.getExtractedBinariesLocation());
- assertEquals(expectedWorspaceLocation+separatorChar+"events",result.getEventsLocation());
+ assertThat(result.getWorkspaceLocation()).isEqualTo(expectedWorspaceLocation);
+ assertThat(result.getResultFileLocation()).isEqualTo(expectedWorspaceLocation+separatorChar+"output"+separatorChar+"result.txt");
+
+ assertThat(result.getUserMessagesLocation()).isEqualTo(expectedWorspaceLocation+separatorChar+"output"+separatorChar+"messages");
+ assertThat(result.getMetaDataFileLocation()).isEqualTo(expectedWorspaceLocation+separatorChar+"metadata.txt");
+ assertThat(result.getSourceCodeZipFileLocation()).isEqualTo(expectedWorspaceLocation+separatorChar+"upload"+separatorChar+SOURCECODE_ZIP);
+ assertThat(result.getExtractedSourcesLocation()).isEqualTo(expectedWorspaceLocation+separatorChar+"upload"+separatorChar+"extracted"+separatorChar+"sources");
+ assertThat(result.getExtractedBinariesLocation()).isEqualTo(expectedWorspaceLocation+separatorChar+"upload"+separatorChar+"extracted"+separatorChar+"binaries");
+ assertThat(result.getEventsLocation()).isEqualTo(expectedWorspaceLocation+separatorChar+"events");
/* @formatter:on */
}
@@ -209,6 +223,69 @@ void when_configuration_tells_to_use_sechubstorage_sechub_storage_path_and_sechu
verify(storageService).createJobStorageForPath("xyz/abc/project1", config.getSechubJobUUID());
}
+ @Test
+ void prepare_downloads_asset_and_stores_file_locally_when_parameter_contains_pds_template_metadata_no_checksum_failure() throws Exception {
+ /* prepare */
+ PDSTemplateMetaData metaData = new PDSTemplateMetaData();
+ metaData.setTemplateId("template1");
+ metaData.setTemplateType(TemplateType.WEBSCAN_LOGIN);
+ PDSAssetData assetData = new PDSAssetData();
+ assetData.setAssetId("asset1");
+ assetData.setChecksum("checksum1");
+ assetData.setFileName("file1.txt");
+ metaData.setAssetData(assetData);
+
+ String json = JSONConverter.get().toJSON(metaData, false);
+ config.getParameters().add(createEntry(PDSDefaultParameterKeyConstants.PARAM_KEY_PDS_CONFIG_TEMPLATE_META_DATA_LIST, json));
+
+ AssetStorage assetStorage = mock();
+ when(storageService.createAssetStorage("asset1")).thenReturn(assetStorage);
+ when(assetStorage.fetch("file1.txt")).thenReturn(new ByteArrayInputStream("testdata".getBytes()));
+ when(checksumSupport.createSha256Checksum(any(Path.class))).thenReturn("checksum1");
+
+ /* execute */
+ serviceToTest.prepare(jobUUID, config, null);
+
+ /* test */
+ ArgumentCaptor pathCaptor = ArgumentCaptor.captor();
+ verify(storageService).createAssetStorage("asset1");
+ verify(checksumSupport).createSha256Checksum(pathCaptor.capture());
+ Path path = pathCaptor.getValue();
+
+ assertThat(path.getFileName().toString()).isEqualTo("file1.txt");
+
+ // check file is created
+ assertThat(Files.exists(path)).isTrue();
+ List lines = Files.readAllLines(path);
+ assertThat(lines).contains("testdata").hasSize(1);
+ }
+
+ @Test
+ void prepare_downloads_asset_and_stores_file_locally_when_parameter_contains_pds_template_metadata_checksum_failure() throws Exception {
+ /* prepare */
+ PDSTemplateMetaData metaData = new PDSTemplateMetaData();
+ metaData.setTemplateId("template1");
+ metaData.setTemplateType(TemplateType.WEBSCAN_LOGIN);
+ PDSAssetData assetData = new PDSAssetData();
+ assetData.setAssetId("asset1");
+ assetData.setChecksum("checksum1");
+ assetData.setFileName("file1.txt");
+ metaData.setAssetData(assetData);
+
+ String json = JSONConverter.get().toJSON(metaData, false);
+ config.getParameters().add(createEntry(PDSDefaultParameterKeyConstants.PARAM_KEY_PDS_CONFIG_TEMPLATE_META_DATA_LIST, json));
+
+ AssetStorage assetStorage = mock();
+ when(storageService.createAssetStorage("asset1")).thenReturn(assetStorage);
+ when(assetStorage.fetch("file1.txt")).thenReturn(new ByteArrayInputStream("testdata".getBytes()));
+ when(checksumSupport.createSha256Checksum(any(Path.class))).thenReturn("checksum-other-means-failure");
+
+ /* execute + test */
+ assertThatThrownBy(() -> serviceToTest.prepare(jobUUID, config, null)).cause().isInstanceOf(IOException.class)
+ .hasMessageStartingWith("Checksum not as expected");
+
+ }
+
private PDSExecutionParameterEntry createEntry(String key, String value) {
PDSExecutionParameterEntry entry = new PDSExecutionParameterEntry();
entry.setKey(key);
diff --git a/sechub-scan-product-checkmarx/src/main/java/com/mercedesbenz/sechub/domain/scan/product/checkmarx/CheckmarxExecutorConfigSuppport.java b/sechub-scan-product-checkmarx/src/main/java/com/mercedesbenz/sechub/domain/scan/product/checkmarx/CheckmarxExecutorConfigSuppport.java
index 26cf7301f8..066af81fc3 100644
--- a/sechub-scan-product-checkmarx/src/main/java/com/mercedesbenz/sechub/domain/scan/product/checkmarx/CheckmarxExecutorConfigSuppport.java
+++ b/sechub-scan-product-checkmarx/src/main/java/com/mercedesbenz/sechub/domain/scan/product/checkmarx/CheckmarxExecutorConfigSuppport.java
@@ -9,6 +9,7 @@
import com.mercedesbenz.sechub.commons.core.environment.SystemEnvironmentVariableSupport;
import com.mercedesbenz.sechub.commons.core.util.SimpleStringUtils;
import com.mercedesbenz.sechub.commons.model.SecHubRuntimeException;
+import com.mercedesbenz.sechub.domain.scan.product.ProductExecutorContext;
import com.mercedesbenz.sechub.domain.scan.product.config.ProductExecutorConfig;
import com.mercedesbenz.sechub.sharedkernel.error.NotAcceptableException;
import com.mercedesbenz.sechub.sharedkernel.mapping.MappingIdentifier;
@@ -27,14 +28,14 @@ public class CheckmarxExecutorConfigSuppport extends DefaultExecutorConfigSuppor
* @return support
* @throws NotAcceptableException when configuration is not valid
*/
- public static CheckmarxExecutorConfigSuppport createSupportAndAssertConfigValid(ProductExecutorConfig config,
+ public static CheckmarxExecutorConfigSuppport createSupportAndAssertConfigValid(ProductExecutorContext context,
SystemEnvironmentVariableSupport variableSupport) {
- return new CheckmarxExecutorConfigSuppport(config, variableSupport, new CheckmarxProductExecutorMinimumConfigValidation());
+ return new CheckmarxExecutorConfigSuppport(context, variableSupport, new CheckmarxProductExecutorMinimumConfigValidation());
}
- private CheckmarxExecutorConfigSuppport(ProductExecutorConfig config, SystemEnvironmentVariableSupport variableSupport,
+ private CheckmarxExecutorConfigSuppport(ProductExecutorContext context, SystemEnvironmentVariableSupport variableSupport,
Validation validation) {
- super(config, variableSupport, validation);
+ super(context, variableSupport, validation);
}
public boolean isAlwaysFullScanEnabled() {
diff --git a/sechub-scan-product-checkmarx/src/main/java/com/mercedesbenz/sechub/domain/scan/product/checkmarx/CheckmarxProductExecutor.java b/sechub-scan-product-checkmarx/src/main/java/com/mercedesbenz/sechub/domain/scan/product/checkmarx/CheckmarxProductExecutor.java
index b17cc43bc0..d1c640f636 100644
--- a/sechub-scan-product-checkmarx/src/main/java/com/mercedesbenz/sechub/domain/scan/product/checkmarx/CheckmarxProductExecutor.java
+++ b/sechub-scan-product-checkmarx/src/main/java/com/mercedesbenz/sechub/domain/scan/product/checkmarx/CheckmarxProductExecutor.java
@@ -109,8 +109,8 @@ protected List executeByAdapter(ProductExecutorData data) throws
JobStorage storage = storageService.createJobStorageForProject(projectId, jobUUID);
- CheckmarxExecutorConfigSuppport configSupport = CheckmarxExecutorConfigSuppport
- .createSupportAndAssertConfigValid(data.getProductExecutorContext().getExecutorConfig(), systemEnvironmentVariableSupport);
+ CheckmarxExecutorConfigSuppport configSupport = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(data.getProductExecutorContext(),
+ systemEnvironmentVariableSupport);
AdapterMetaDataCallback metaDataCallback = data.getProductExecutorContext().getCallback();
diff --git a/sechub-scan-product-checkmarx/src/test/java/com/mercedesbenz/sechub/domain/scan/product/checkmarx/CheckmarxExecutorConfigSuppportTest.java b/sechub-scan-product-checkmarx/src/test/java/com/mercedesbenz/sechub/domain/scan/product/checkmarx/CheckmarxExecutorConfigSuppportTest.java
index 7fee8f00c9..01b4215396 100644
--- a/sechub-scan-product-checkmarx/src/test/java/com/mercedesbenz/sechub/domain/scan/product/checkmarx/CheckmarxExecutorConfigSuppportTest.java
+++ b/sechub-scan-product-checkmarx/src/test/java/com/mercedesbenz/sechub/domain/scan/product/checkmarx/CheckmarxExecutorConfigSuppportTest.java
@@ -12,6 +12,7 @@
import com.mercedesbenz.sechub.adapter.checkmarx.CheckmarxConstants;
import com.mercedesbenz.sechub.commons.core.environment.SystemEnvironmentVariableSupport;
+import com.mercedesbenz.sechub.domain.scan.product.ProductExecutorContext;
import com.mercedesbenz.sechub.domain.scan.product.config.ProductExecutorConfig;
import com.mercedesbenz.sechub.domain.scan.product.config.ProductExecutorConfigSetup;
import com.mercedesbenz.sechub.domain.scan.product.config.ProductExecutorConfigSetupJobParameter;
@@ -23,12 +24,14 @@ public class CheckmarxExecutorConfigSuppportTest {
private ProductExecutorConfigSetup setup;
private List jobParameters;
private SystemEnvironmentVariableSupport systemEnvironmentVariableSupport;
+ private ProductExecutorContext context;
@Before
public void before() throws Exception {
-
+ context = mock();
systemEnvironmentVariableSupport = mock(SystemEnvironmentVariableSupport.class);
config = mock(ProductExecutorConfig.class);
+ when(context.getExecutorConfig()).thenReturn(config);
setup = mock(ProductExecutorConfigSetup.class);
jobParameters = new ArrayList<>();
@@ -40,7 +43,7 @@ public void before() throws Exception {
@Test
public void client_secret_returns_default_when_not_configured() {
/* prepare */
- supportToTest = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(config, systemEnvironmentVariableSupport);
+ supportToTest = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(context, systemEnvironmentVariableSupport);
assertEquals(CheckmarxConstants.DEFAULT_CLIENT_SECRET, supportToTest.getClientSecret());
}
@@ -49,7 +52,7 @@ public void client_secret_returns_default_when_not_configured() {
public void client_secret_returns_configured_value_when_parameter_available() {
/* prepare */
jobParameters.add(new ProductExecutorConfigSetupJobParameter(CheckmarxExecutorConfigParameterKeys.CHECKMARX_CLIENT_SECRET, "new.secret"));
- supportToTest = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(config, systemEnvironmentVariableSupport);
+ supportToTest = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(context, systemEnvironmentVariableSupport);
/* execute +test */
assertEquals("new.secret", supportToTest.getClientSecret());
@@ -59,7 +62,7 @@ public void client_secret_returns_configured_value_when_parameter_available() {
public void client_secret_returns_an_empty_string_default_is_returned() {
/* prepare */
jobParameters.add(new ProductExecutorConfigSetupJobParameter(CheckmarxExecutorConfigParameterKeys.CHECKMARX_CLIENT_SECRET, ""));
- supportToTest = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(config, systemEnvironmentVariableSupport);
+ supportToTest = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(context, systemEnvironmentVariableSupport);
/* execute +test */
assertEquals(CheckmarxConstants.DEFAULT_CLIENT_SECRET, supportToTest.getClientSecret());
@@ -68,7 +71,7 @@ public void client_secret_returns_an_empty_string_default_is_returned() {
@Test
public void engine_configuration_name_returns_default_when_not_configured() {
/* prepare */
- supportToTest = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(config, systemEnvironmentVariableSupport);
+ supportToTest = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(context, systemEnvironmentVariableSupport);
assertEquals(CheckmarxConstants.DEFAULT_CHECKMARX_ENGINECONFIGURATION_MULTILANGANGE_SCAN_NAME, supportToTest.getEngineConfigurationName());
}
@@ -77,7 +80,7 @@ public void engine_configuration_name_returns_default_when_not_configured() {
public void engine_configuration_name_returns_configured_value_when_parameter_available() {
/* prepare */
jobParameters.add(new ProductExecutorConfigSetupJobParameter(CheckmarxExecutorConfigParameterKeys.CHECKMARX_ENGINE_CONFIGURATIONNAME, "test.engine"));
- supportToTest = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(config, systemEnvironmentVariableSupport);
+ supportToTest = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(context, systemEnvironmentVariableSupport);
/* execute +test */
assertEquals("test.engine", supportToTest.getEngineConfigurationName());
@@ -87,7 +90,7 @@ public void engine_configuration_name_returns_configured_value_when_parameter_av
public void engine_configuration_name_returns_an_empty_string_default_is_returned() {
/* prepare */
jobParameters.add(new ProductExecutorConfigSetupJobParameter(CheckmarxExecutorConfigParameterKeys.CHECKMARX_ENGINE_CONFIGURATIONNAME, ""));
- supportToTest = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(config, systemEnvironmentVariableSupport);
+ supportToTest = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(context, systemEnvironmentVariableSupport);
/* execute +test */
assertEquals(CheckmarxConstants.DEFAULT_CHECKMARX_ENGINECONFIGURATION_MULTILANGANGE_SCAN_NAME, supportToTest.getEngineConfigurationName());
@@ -97,7 +100,7 @@ public void engine_configuration_name_returns_an_empty_string_default_is_returne
public void always_fullscan_enabled_true() {
/* prepare */
jobParameters.add(new ProductExecutorConfigSetupJobParameter(CheckmarxExecutorConfigParameterKeys.CHECKMARX_FULLSCAN_ALWAYS, "true"));
- supportToTest = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(config, systemEnvironmentVariableSupport);
+ supportToTest = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(context, systemEnvironmentVariableSupport);
/* test */
assertEquals(true, supportToTest.isAlwaysFullScanEnabled());
@@ -107,7 +110,7 @@ public void always_fullscan_enabled_true() {
public void always_fullscan_enabled_false() {
/* prepare */
jobParameters.add(new ProductExecutorConfigSetupJobParameter(CheckmarxExecutorConfigParameterKeys.CHECKMARX_FULLSCAN_ALWAYS, "false"));
- supportToTest = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(config, systemEnvironmentVariableSupport);
+ supportToTest = CheckmarxExecutorConfigSuppport.createSupportAndAssertConfigValid(context, systemEnvironmentVariableSupport);
/* test */
assertEquals(false, supportToTest.isAlwaysFullScanEnabled());
diff --git a/sechub-scan-product-checkmarx/src/test/java/com/mercedesbenz/sechub/domain/scan/product/checkmarx/CheckmarxProductExecutorMockTest.java b/sechub-scan-product-checkmarx/src/test/java/com/mercedesbenz/sechub/domain/scan/product/checkmarx/CheckmarxProductExecutorMockTest.java
index 3b81bf864a..a06127bc77 100644
--- a/sechub-scan-product-checkmarx/src/test/java/com/mercedesbenz/sechub/domain/scan/product/checkmarx/CheckmarxProductExecutorMockTest.java
+++ b/sechub-scan-product-checkmarx/src/test/java/com/mercedesbenz/sechub/domain/scan/product/checkmarx/CheckmarxProductExecutorMockTest.java
@@ -18,12 +18,13 @@
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.client.RestTemplate;
import com.amazonaws.util.StringInputStream;
import com.mercedesbenz.sechub.adapter.AdapterException;
@@ -49,11 +50,10 @@
import com.mercedesbenz.sechub.domain.scan.product.config.ProductExecutorConfigSetupJobParameter;
import com.mercedesbenz.sechub.domain.scan.resolve.NetworkTargetResolver;
import com.mercedesbenz.sechub.sharedkernel.ProductIdentifier;
-import com.mercedesbenz.sechub.sharedkernel.Profiles;
import com.mercedesbenz.sechub.sharedkernel.configuration.SecHubConfiguration;
import com.mercedesbenz.sechub.sharedkernel.mapping.MappingIdentifier;
import com.mercedesbenz.sechub.sharedkernel.metadata.DefaultMetaDataInspector;
-import com.mercedesbenz.sechub.sharedkernel.security.AbstractSecHubAPISecurityConfiguration;
+import com.mercedesbenz.sechub.sharedkernel.security.SecHubSecurityConfiguration;
import com.mercedesbenz.sechub.sharedkernel.storage.SecHubStorageService;
import com.mercedesbenz.sechub.storage.core.JobStorage;
@@ -61,6 +61,7 @@
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = { CheckmarxProductExecutor.class, CheckmarxResilienceConsultant.class,
CheckmarxProductExecutorMockTest.SimpleTestConfiguration.class, DefaultMetaDataInspector.class })
+@Import(SecHubSecurityConfiguration.class)
public class CheckmarxProductExecutorMockTest {
private static final String PROJECT_EXAMPLE = "projectIdxyz";
@@ -215,10 +216,12 @@ private SecHubExecutionContext createExecutionContextForPseudoCodeScan() {
}
@TestConfiguration
- @Profile(Profiles.TEST)
- @EnableAutoConfiguration
- public static class SimpleTestConfiguration extends AbstractSecHubAPISecurityConfiguration {
+ static class SimpleTestConfiguration {
+ @Bean
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
}
}
diff --git a/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/AbstractPDSProductExecutor.java b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/AbstractPDSProductExecutor.java
index d12908f390..56041bb67a 100644
--- a/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/AbstractPDSProductExecutor.java
+++ b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/AbstractPDSProductExecutor.java
@@ -75,8 +75,7 @@ protected final List executeByAdapter(ProductExecutorData data) t
LOG.debug("Trigger PDS adapter execution for scan type: {} by: {}", getScanType(), getClass().getSimpleName());
ProductExecutorContext executorContext = data.getProductExecutorContext();
- PDSExecutorConfigSupport configSupport = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(executorContext.getExecutorConfig(),
- serviceCollection);
+ PDSExecutorConfigSupport configSupport = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(executorContext, serviceCollection);
SecHubExecutionContext context = data.getSechubExecutionContext();
diff --git a/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSAdapterConfigurationStrategy.java b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSAdapterConfigurationStrategy.java
index 7693ce64bd..ed5dbf7d78 100644
--- a/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSAdapterConfigurationStrategy.java
+++ b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSAdapterConfigurationStrategy.java
@@ -13,6 +13,7 @@
import com.mercedesbenz.sechub.adapter.AdapterConfigurationStrategy;
import com.mercedesbenz.sechub.adapter.pds.PDSAdapterConfigurator;
import com.mercedesbenz.sechub.adapter.pds.PDSAdapterConfiguratorProvider;
+import com.mercedesbenz.sechub.commons.core.ConfigurationFailureException;
import com.mercedesbenz.sechub.commons.model.ScanType;
import com.mercedesbenz.sechub.commons.model.SecHubRuntimeException;
import com.mercedesbenz.sechub.domain.scan.DefaultAdapterConfigurationStrategy;
@@ -136,7 +137,7 @@ private PDSAdapterConfigurationStrategy(PDSAdapterConfigurationStrategyConfig co
}
@Override
- public void configure(B configBuilder) {
+ public void configure(B configBuilder) throws ConfigurationFailureException {
PDSAdapterConfigurator pdsConfigurator = null;
if (configBuilder instanceof PDSAdapterConfiguratorProvider) {
PDSAdapterConfiguratorProvider provider = (PDSAdapterConfiguratorProvider) configBuilder;
@@ -153,13 +154,11 @@ public void configure(
handlePdsParts(pdsConfigurator);
}
- private void handlePdsParts(PDSAdapterConfigurator pdsConfigurable) {
+ private void handlePdsParts(PDSAdapterConfigurator pdsConfigurable) throws ConfigurationFailureException {
PDSExecutorConfigSupport configSupport = strategyConfig.configSupport;
SecHubExecutionContext context = strategyConfig.productExecutorData.getSechubExecutionContext();
- Map jobParametersToSend = configSupport.createJobParametersToSendToPDS(context.getConfiguration());
- pdsConfigurable.setJobParameters(jobParametersToSend);
pdsConfigurable.setReusingSecHubStorage(configSupport.isReusingSecHubStorage());
pdsConfigurable.setScanType(strategyConfig.scanType);
pdsConfigurable.setPdsProductIdentifier(configSupport.getPDSProductIdentifier());
@@ -170,7 +169,6 @@ private void handlePdsParts(PDSAdapterConfigurator pdsConfigurable) {
pdsConfigurable.setBinaryTarFileInputStreamOrNull(strategyConfig.binariesTarFileInputStreamOrNull);
pdsConfigurable.setSourceCodeZipFileRequired(strategyConfig.contentProvider.isSourceRequired());
pdsConfigurable.setBinaryTarFileRequired(strategyConfig.contentProvider.isBinaryRequired());
-
pdsConfigurable.setResilienceMaxRetries(configSupport.getPDSAdapterResilienceMaxRetries());
pdsConfigurable.setResilienceTimeToWaitBeforeRetryInMilliseconds(configSupport.getPDSAdapterResilienceRetryWaitInMilliseconds());
@@ -179,6 +177,14 @@ private void handlePdsParts(PDSAdapterConfigurator pdsConfigurable) {
handleBinariesChecksum(pdsConfigurable);
handleBinariesFileSize(pdsConfigurable);
+
+ handleJobParameters(pdsConfigurable, configSupport, context);
+ }
+
+ private void handleJobParameters(PDSAdapterConfigurator pdsConfigurable, PDSExecutorConfigSupport configSupport, SecHubExecutionContext context)
+ throws ConfigurationFailureException {
+ Map jobParametersToSend = configSupport.createJobParametersToSendToPDS(context);
+ pdsConfigurable.setJobParameters(jobParametersToSend);
}
private void handleSourceCodeChecksum(PDSAdapterConfigurator pdsConfigurable) {
@@ -237,7 +243,7 @@ private void handleBinariesFileSize(PDSAdapterConfigurator pdsConfigurable) {
}
}
- private void handleCommonParts(B configBuilder) {
+ private void handleCommonParts(B configBuilder) throws ConfigurationFailureException {
/* standard configuration */
/* @formatter:off */
diff --git a/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSExecutorConfigSupport.java b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSExecutorConfigSupport.java
index a6dc958cc0..d119bbd8e4 100644
--- a/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSExecutorConfigSupport.java
+++ b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSExecutorConfigSupport.java
@@ -14,19 +14,27 @@
import org.slf4j.LoggerFactory;
import com.mercedesbenz.sechub.adapter.DefaultExecutorConfigSupport;
+import com.mercedesbenz.sechub.commons.core.ConfigurationFailureException;
import com.mercedesbenz.sechub.commons.core.environment.SystemEnvironmentVariableSupport;
import com.mercedesbenz.sechub.commons.core.util.SecHubStorageUtil;
import com.mercedesbenz.sechub.commons.core.util.SimpleStringUtils;
+import com.mercedesbenz.sechub.commons.model.JSONConverter;
+import com.mercedesbenz.sechub.commons.model.ScanType;
import com.mercedesbenz.sechub.commons.model.SecHubDataConfigurationType;
import com.mercedesbenz.sechub.commons.model.SecHubDataConfigurationTypeListParser;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition;
import com.mercedesbenz.sechub.commons.pds.PDSConfigDataKeyProvider;
import com.mercedesbenz.sechub.commons.pds.PDSDefaultParameterKeyConstants;
import com.mercedesbenz.sechub.commons.pds.PDSKey;
import com.mercedesbenz.sechub.commons.pds.PDSKeyProvider;
+import com.mercedesbenz.sechub.commons.pds.data.PDSTemplateMetaData;
import com.mercedesbenz.sechub.domain.scan.NetworkTargetProductServerDataProvider;
import com.mercedesbenz.sechub.domain.scan.NetworkTargetType;
+import com.mercedesbenz.sechub.domain.scan.SecHubExecutionContext;
import com.mercedesbenz.sechub.domain.scan.config.ScanMapping;
+import com.mercedesbenz.sechub.domain.scan.product.ProductExecutorContext;
import com.mercedesbenz.sechub.domain.scan.product.config.ProductExecutorConfig;
+import com.mercedesbenz.sechub.sharedkernel.ProductIdentifier;
import com.mercedesbenz.sechub.sharedkernel.configuration.SecHubConfiguration;
import com.mercedesbenz.sechub.sharedkernel.error.NotAcceptableException;
import com.mercedesbenz.sechub.sharedkernel.validation.Validation;
@@ -42,6 +50,8 @@ public class PDSExecutorConfigSupport extends DefaultExecutorConfigSupport imple
private PDSExecutorConfigSuppportServiceCollection serviceCollection;
private SecHubDataConfigurationTypeListParser parser = new SecHubDataConfigurationTypeListParser();
+ PDSTemplateMetaDataService templateMetaDataTransformer = new PDSTemplateMetaDataService();
+
static {
List> allParameterProviders = new ArrayList<>();
allParameterProviders.addAll(Arrays.asList(SecHubProductExecutionPDSKeyProvider.values()));
@@ -64,29 +74,63 @@ public static List> getUnmodifiableListOfParame
* @return support
* @throws NotAcceptableException when configuration is not valid
*/
- public static PDSExecutorConfigSupport createSupportAndAssertConfigValid(ProductExecutorConfig config,
+ public static PDSExecutorConfigSupport createSupportAndAssertConfigValid(ProductExecutorContext context,
PDSExecutorConfigSuppportServiceCollection serviceCollection) {
- return new PDSExecutorConfigSupport(config, serviceCollection, new PDSProductExecutorMinimumConfigValidation());
+ PDSExecutorConfigSupport result = new PDSExecutorConfigSupport(context, serviceCollection, new PDSProductExecutorMinimumConfigValidation());
+ return result;
}
- private PDSExecutorConfigSupport(ProductExecutorConfig config, PDSExecutorConfigSuppportServiceCollection serviceCollection,
+ private PDSExecutorConfigSupport(ProductExecutorContext context, PDSExecutorConfigSuppportServiceCollection serviceCollection,
Validation validation) {
- super(config, serviceCollection.getSystemEnvironmentVariableSupport(), validation);
+ super(context, serviceCollection.getSystemEnvironmentVariableSupport(), validation);
this.serviceCollection = serviceCollection;
}
- public Map createJobParametersToSendToPDS(SecHubConfiguration secHubConfiguration) {
+ public Map createJobParametersToSendToPDS(SecHubExecutionContext context) throws ConfigurationFailureException {
+ if (context == null) {
+ throw new IllegalArgumentException("context may not be null!");
+ }
+ SecHubConfiguration configuration = context.getConfiguration();
+ if (configuration == null) {
+ throw new IllegalStateException("configuration may not be null inside context at this moment!");
+ }
+ ProductIdentifier productIdentifier = config.getProductIdentifier();
+ if (productIdentifier == null) {
+ throw new IllegalStateException("productIdentifier may not be null inside config at this moment!");
+ }
+ ScanType scanType = productIdentifier.getType();
+ if (scanType == null) {
+ throw new IllegalStateException("scanType may not be null inside productIdentifier:" + productIdentifier);
+ }
Map parametersToSend = createParametersToSendByProviders(keyProvidersForSendingParametersToPDS);
handleEnvironmentVariablesInJobParameters(parametersToSend);
/* handle remaining parts without environment variable conversion */
- handleSecHubStorageIfNecessary(secHubConfiguration, parametersToSend);
+ handleSecHubStorageIfNecessary(configuration, parametersToSend);
addMappingsAsJobParameter(parametersToSend);
+ addPdsTemplateMetaDataList(scanType, context, parametersToSend);
return parametersToSend;
}
+ private void addPdsTemplateMetaDataList(ScanType scanType, SecHubExecutionContext context, Map parametersToSend)
+ throws ConfigurationFailureException {
+ List templateDefinitions = context.getTemplateDefinitions();
+ if (templateDefinitions == null) {
+ throw new IllegalStateException("template definitions may not be null inside context at this moment!");
+ }
+ PDSTemplateMetaDataService templateMetaDataService = serviceCollection.getTemplateMetaDataService();
+
+ List pdsTemplateMetaDataList = templateMetaDataService.createTemplateMetaData(templateDefinitions, getPDSProductIdentifier(),
+ scanType, context.getConfiguration());
+
+ templateMetaDataService.ensureTemplateAssetFilesAreAvailableInStorage(pdsTemplateMetaDataList);
+
+ String pdsTemplateMetaDataListAsJson = JSONConverter.get().toJSON(pdsTemplateMetaDataList, false);
+ parametersToSend.put(PDSDefaultParameterKeyConstants.PARAM_KEY_PDS_CONFIG_TEMPLATE_META_DATA_LIST, pdsTemplateMetaDataListAsJson);
+ }
+
private void handleSecHubStorageIfNecessary(SecHubConfiguration secHubConfiguration, Map parametersToSend) {
if (isReusingSecHubStorage()) {
String projectId = secHubConfiguration.getProjectId();
@@ -310,4 +354,5 @@ public boolean isGivenStorageSupportedByPDSProduct(PDSStorageContentProvider con
public String getDataTypesSupportedByPDSAsString() {
return getParameter(PDSConfigDataKeyProvider.PDS_CONFIG_SUPPORTED_DATATYPES);
}
+
}
diff --git a/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSExecutorConfigSuppportServiceCollection.java b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSExecutorConfigSuppportServiceCollection.java
index 5bd6854a7b..b36d5113a8 100644
--- a/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSExecutorConfigSuppportServiceCollection.java
+++ b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSExecutorConfigSuppportServiceCollection.java
@@ -16,6 +16,9 @@ public class PDSExecutorConfigSuppportServiceCollection {
@Autowired
ScanMappingRepository scanMappingRepository;
+ @Autowired
+ PDSTemplateMetaDataService templateMetaDataService;
+
public SystemEnvironmentVariableSupport getSystemEnvironmentVariableSupport() {
return systemEnvironment;
}
@@ -23,4 +26,8 @@ public SystemEnvironmentVariableSupport getSystemEnvironmentVariableSupport() {
public ScanMappingRepository getScanMappingRepository() {
return scanMappingRepository;
}
+
+ public PDSTemplateMetaDataService getTemplateMetaDataService() {
+ return templateMetaDataService;
+ }
}
diff --git a/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSInfraScanProductExecutor.java b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSInfraScanProductExecutor.java
index cb1192bd72..a20c32b386 100644
--- a/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSInfraScanProductExecutor.java
+++ b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSInfraScanProductExecutor.java
@@ -112,8 +112,7 @@ protected void customize(ProductExecutorData data) {
data.setNetworkLocationProvider(new InfraScanNetworkLocationProvider(secHubConfiguration));
ProductExecutorContext executorContext = data.getProductExecutorContext();
- PDSExecutorConfigSupport configSupport = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(executorContext.getExecutorConfig(),
- serviceCollection);
+ PDSExecutorConfigSupport configSupport = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(executorContext, serviceCollection);
data.setNetworkTargetDataProvider(configSupport);
}
diff --git a/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSTemplateMetaDataService.java b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSTemplateMetaDataService.java
new file mode 100644
index 0000000000..b0acdd59e3
--- /dev/null
+++ b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSTemplateMetaDataService.java
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.scan.product.pds;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.mercedesbenz.sechub.commons.core.ConfigurationFailureException;
+import com.mercedesbenz.sechub.commons.model.ScanType;
+import com.mercedesbenz.sechub.commons.model.SecHubConfigurationModel;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition;
+import com.mercedesbenz.sechub.commons.pds.data.PDSTemplateMetaData;
+import com.mercedesbenz.sechub.commons.pds.data.PDSTemplateMetaData.PDSAssetData;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetDetailData;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetFileData;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetService;
+import com.mercedesbenz.sechub.sharedkernel.error.NotFoundException;
+
+@Service
+public class PDSTemplateMetaDataService {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PDSTemplateMetaDataService.class);
+
+ @Autowired
+ AssetService assetService;
+
+ @Autowired
+ RelevantScanTemplateDefinitionFilter filter;
+
+ public List createTemplateMetaData(List templateDefinitions, String pdsProductId, ScanType scanType,
+ SecHubConfigurationModel configuration) throws ConfigurationFailureException {
+
+ List result = new ArrayList<>();
+
+ List filteredDefinitions = filter.filter(templateDefinitions, scanType, configuration);
+ if (filteredDefinitions.isEmpty()) {
+ LOG.debug("Given {} template definitions, after filtering: {}", templateDefinitions.size(), filteredDefinitions.size());
+ }
+
+ resolveAssetFileDataAndAddToResult(result, pdsProductId, filteredDefinitions);
+
+ return result;
+ }
+
+ /**
+ * Ensures that asset files in given PDS template meta data is available in
+ * storage.
+ *
+ * @param metaDataList list containing template meta data
+ * @throws ConfigurationFailureException
+ */
+ public void ensureTemplateAssetFilesAreAvailableInStorage(List metaDataList) throws ConfigurationFailureException {
+ for (PDSTemplateMetaData metaData : metaDataList) {
+
+ PDSAssetData assetData = metaData.getAssetData();
+
+ String assetId = assetData.getAssetId();
+ String fileName = assetData.getFileName();
+
+ assetService.ensureAssetFileInStorageAvailableAndHasSameChecksumAsInDatabase(fileName, assetId);
+
+ }
+ }
+
+ private void resolveAssetFileDataAndAddToResult(List result, String pdsProductId, List filteredDefinitions)
+ throws ConfigurationFailureException {
+ if (filteredDefinitions.isEmpty()) {
+ return;
+ }
+
+ String filename = pdsProductId + ".zip";
+
+ for (TemplateDefinition definition : filteredDefinitions) {
+ String assetId = definition.getAssetId();
+ AssetDetailData details = null;
+ try {
+ details = assetService.fetchAssetDetails(assetId);
+ } catch (NotFoundException e) {
+ /* asset does not exist */
+ throw new ConfigurationFailureException("The asset " + assetId + " does not exist! Cannot transform", e);
+ }
+
+ List files = details.getFiles();
+ boolean fileForTemplateFound = false;
+
+ for (AssetFileData fileData : files) {
+ if (!filename.equals(fileData.getFileName())) {
+ continue;
+ }
+ /* found */
+ PDSAssetData assetData = new PDSAssetData();
+ assetData.setAssetId(assetId);
+ assetData.setFileName(fileData.getFileName());
+ assetData.setChecksum(fileData.getChecksum());
+
+ PDSTemplateMetaData metaData = new PDSTemplateMetaData();
+ metaData.setTemplateId(definition.getId());
+ metaData.setTemplateType(definition.getType());
+ metaData.setAssetData(assetData);
+
+ result.add(metaData);
+
+ fileForTemplateFound = true;
+ break;
+ }
+
+ if (!fileForTemplateFound) {
+ throw new ConfigurationFailureException("The asset " + assetId + " does not contain file '" + filename + "'");
+ }
+ }
+ }
+}
diff --git a/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSWebScanProductExecutor.java b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSWebScanProductExecutor.java
index b11947eac7..4e4478da99 100644
--- a/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSWebScanProductExecutor.java
+++ b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSWebScanProductExecutor.java
@@ -110,8 +110,7 @@ protected void customize(ProductExecutorData data) {
data.setNetworkLocationProvider(new WebScanNetworkLocationProvider(secHubConfiguration));
ProductExecutorContext executorContext = data.getProductExecutorContext();
- PDSExecutorConfigSupport configSupport = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(executorContext.getExecutorConfig(),
- serviceCollection);
+ PDSExecutorConfigSupport configSupport = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(executorContext, serviceCollection);
data.setNetworkTargetDataProvider(configSupport);
diff --git a/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/RelevantScanTemplateDefinitionFilter.java b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/RelevantScanTemplateDefinitionFilter.java
new file mode 100644
index 0000000000..724f720403
--- /dev/null
+++ b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/RelevantScanTemplateDefinitionFilter.java
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.scan.product.pds;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.mercedesbenz.sechub.commons.model.ScanType;
+import com.mercedesbenz.sechub.commons.model.SecHubConfigurationModel;
+import com.mercedesbenz.sechub.commons.model.template.TemplateData;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDataResolver;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition;
+import com.mercedesbenz.sechub.commons.model.template.TemplateType;
+
+@Component
+/**
+ * Filters template definitions which are relevant for a scan
+ *
+ * @author Albert Tregnaghi
+ *
+ */
+public class RelevantScanTemplateDefinitionFilter {
+
+ @Autowired
+ TemplateDataResolver templateDataResolver;
+
+ /**
+ * Filters template definitions for only relevant definitions for given scan
+ * type and configuration. If the configuration does not contain a templateData
+ * definition, the list will always be empty. If configuration contains template
+ * data, but the scan type does not need/use them the result will also be empty
+ *
+ * @param templateDefinitions all given definitions
+ * @param scanType scan type for which relevant definitions shall be
+ * filtered
+ * @param configuration SecHub configuration which contains template data
+ * (or not)
+ * @return list of relevant template definitions for the scan, never
+ * null
+ */
+ public List filter(List templateDefinitions, ScanType scanType, SecHubConfigurationModel configuration) {
+ List result = new ArrayList<>();
+
+ for (TemplateDefinition definition : templateDefinitions) {
+
+ addDefinitionToResultIfNecessary(definition, scanType, configuration, result);
+ }
+ return result;
+ }
+
+ private void addDefinitionToResultIfNecessary(TemplateDefinition definition, ScanType scanType, SecHubConfigurationModel configuration,
+ List result) {
+ if (scanType == null) {
+ return;
+ }
+ switch (scanType) {
+ case WEB_SCAN:
+ /* for web scan we accept WEBSCAN_LOGIN templates */
+ if (TemplateType.WEBSCAN_LOGIN.equals(definition.getType())) {
+ TemplateData templateData = templateDataResolver.resolveTemplateData(TemplateType.WEBSCAN_LOGIN, configuration);
+ if (templateData != null) {
+ /* data available, so add to result */
+ result.add(definition);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ }
+
+}
diff --git a/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/SecHubProductExecutionPDSKeyProvider.java b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/SecHubProductExecutionPDSKeyProvider.java
index 395e7fd580..2120e1a489 100644
--- a/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/SecHubProductExecutionPDSKeyProvider.java
+++ b/sechub-scan-product-pds/src/main/java/com/mercedesbenz/sechub/domain/scan/product/pds/SecHubProductExecutionPDSKeyProvider.java
@@ -8,7 +8,7 @@
import com.mercedesbenz.sechub.domain.scan.NetworkTargetType;
/**
- * These providers/keys are used by sechub PDS product executors at runtime
+ * These providers/keys are used by SecHub PDS product executors at runtime
* while communicating with PDS servers.
*
* @author Albert Tregnaghi
diff --git a/sechub-scan-product-pds/src/test/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSExecutorConfigSupportTest.java b/sechub-scan-product-pds/src/test/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSExecutorConfigSupportTest.java
index 32049385b1..987d59c304 100644
--- a/sechub-scan-product-pds/src/test/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSExecutorConfigSupportTest.java
+++ b/sechub-scan-product-pds/src/test/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSExecutorConfigSupportTest.java
@@ -1,10 +1,13 @@
// SPDX-License-Identifier: MIT
package com.mercedesbenz.sechub.domain.scan.product.pds;
+import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -24,16 +27,25 @@
import com.mercedesbenz.sechub.commons.mapping.MappingData;
import com.mercedesbenz.sechub.commons.mapping.MappingEntry;
import com.mercedesbenz.sechub.commons.model.JSONConverter;
+import com.mercedesbenz.sechub.commons.model.ScanType;
import com.mercedesbenz.sechub.commons.model.SecHubDataConfigurationType;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition;
import com.mercedesbenz.sechub.commons.pds.PDSConfigDataKeyProvider;
+import com.mercedesbenz.sechub.commons.pds.PDSDefaultParameterKeyConstants;
+import com.mercedesbenz.sechub.commons.pds.data.PDSTemplateMetaData;
+import com.mercedesbenz.sechub.commons.pds.data.PDSTemplateMetaData.PDSAssetData;
import com.mercedesbenz.sechub.domain.scan.NetworkTargetType;
+import com.mercedesbenz.sechub.domain.scan.SecHubExecutionContext;
import com.mercedesbenz.sechub.domain.scan.config.ScanMapping;
import com.mercedesbenz.sechub.domain.scan.config.ScanMappingRepository;
+import com.mercedesbenz.sechub.domain.scan.product.ProductExecutorContext;
import com.mercedesbenz.sechub.domain.scan.product.config.ProductExecutorConfig;
import com.mercedesbenz.sechub.domain.scan.product.config.ProductExecutorConfigSetup;
import com.mercedesbenz.sechub.domain.scan.product.config.ProductExecutorConfigSetupCredentials;
import com.mercedesbenz.sechub.domain.scan.product.config.ProductExecutorConfigSetupJobParameter;
+import com.mercedesbenz.sechub.sharedkernel.ProductIdentifier;
import com.mercedesbenz.sechub.sharedkernel.configuration.SecHubConfiguration;
+import com.mercedesbenz.sechub.test.TestCanaryException;
public class PDSExecutorConfigSupportTest {
@@ -59,11 +71,27 @@ public class PDSExecutorConfigSupportTest {
private List jobParameters;
private ScanMappingRepository repository;
private SystemEnvironmentVariableSupport systemEnvironmentVariableSupport;
+ private ProductExecutorContext context;
+ private SecHubExecutionContext sechubExecutionContext;
+ private SecHubConfiguration sechubConfiguration;
+ private PDSTemplateMetaDataService templateMetaDataService;
@BeforeEach
public void before() throws Exception {
- config = mock(ProductExecutorConfig.class);
- executorConfigSetup = mock(ProductExecutorConfigSetup.class);
+ sechubConfiguration = mock();
+ sechubExecutionContext = mock();
+ config = mock();
+ context = mock();
+ executorConfigSetup = mock();
+ serviceCollection = mock();
+ repository = mock();
+ templateMetaDataService = mock();
+
+ when(sechubExecutionContext.getTemplateDefinitions()).thenReturn(Collections.emptyList());
+ when(sechubExecutionContext.getConfiguration()).thenReturn(sechubConfiguration);
+
+ when(config.getProductIdentifier()).thenReturn(ProductIdentifier.UNKNOWN);// the scan type unknown is used, because not really relevant
+ when(context.getExecutorConfig()).thenReturn(config);
jobParameters = new ArrayList<>();
jobParameters.add(new ProductExecutorConfigSetupJobParameter(PDSConfigDataKeyProvider.PDS_CONFIG_PRODUCTIDENTIFIER.getKey().getId(),
@@ -82,16 +110,14 @@ public void before() throws Exception {
when(executorConfigSetup.getJobParameters()).thenReturn(jobParameters);
- serviceCollection = mock(PDSExecutorConfigSuppportServiceCollection.class);
-
Answer defaultAnswerWithNoConversion = createAnswerWhichReturnsAlwaysJustTheOriginValue();
systemEnvironmentVariableSupport = mock(SystemEnvironmentVariableSupport.class, defaultAnswerWithNoConversion);
- repository = mock(ScanMappingRepository.class);
-
when(serviceCollection.getScanMappingRepository()).thenReturn(repository);
when(serviceCollection.getSystemEnvironmentVariableSupport()).thenReturn(systemEnvironmentVariableSupport);
- supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(config, serviceCollection);
+ when(serviceCollection.getTemplateMetaDataService()).thenReturn(templateMetaDataService);
+
+ supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(context, serviceCollection);
}
@EnumSource(SecHubDataConfigurationType.class)
@@ -107,7 +133,7 @@ void isGivenStorageSupportedByPDSProduct_binary_and_source_required_from_model_a
.add(new ProductExecutorConfigSetupJobParameter(PDSConfigDataKeyProvider.PDS_CONFIG_SUPPORTED_DATATYPES.getKey().getId(), type.toString()));
// create support again (necessary to have new job parameters included)
- supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(config, serviceCollection);
+ supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(context, serviceCollection);
/* execute */
boolean result = supportToTest.isGivenStorageSupportedByPDSProduct(contentProvider);
@@ -143,7 +169,7 @@ void isGivenStorageSupportedByPDSProduct_no_binary_in_model_required_but_product
SecHubDataConfigurationType.BINARY.toString()));
// create support again (necessary to have new job parameters included)
- supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(config, serviceCollection);
+ supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(context, serviceCollection);
/* execute */
boolean result = supportToTest.isGivenStorageSupportedByPDSProduct(contentProvider);
@@ -164,7 +190,7 @@ void isGivenStorageSupportedByPDSProduct_only_source_in_model_required_but_produ
jobParameters.add(new ProductExecutorConfigSetupJobParameter(PDSConfigDataKeyProvider.PDS_CONFIG_SUPPORTED_DATATYPES.getKey().getId(), typesAsString));
// create support again (necessary to have new job parameters included)
- supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(config, serviceCollection);
+ supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(context, serviceCollection);
/* execute */
boolean result = supportToTest.isGivenStorageSupportedByPDSProduct(contentProvider);
@@ -185,7 +211,7 @@ void isGivenStorageSupportedByPDSProduct_only_binary_in_model_required_but_produ
jobParameters.add(new ProductExecutorConfigSetupJobParameter(PDSConfigDataKeyProvider.PDS_CONFIG_SUPPORTED_DATATYPES.getKey().getId(), typesAsString));
// create support again (necessary to have new job parameters included)
- supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(config, serviceCollection);
+ supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(context, serviceCollection);
/* execute */
boolean result = supportToTest.isGivenStorageSupportedByPDSProduct(contentProvider);
@@ -206,7 +232,7 @@ void isGivenStorageSupportedByPDSProduct_no_source_in_model_required_but_product
SecHubDataConfigurationType.SOURCE.toString()));
// create support again (necessary to have new job parameters included)
- supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(config, serviceCollection);
+ supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(context, serviceCollection);
/* execute */
boolean result = supportToTest.isGivenStorageSupportedByPDSProduct(contentProvider);
@@ -227,7 +253,7 @@ void isGivenStorageSupportedByPDSProduct_no_source_or_binary_in_model_required_b
SecHubDataConfigurationType.NONE.toString()));
// create support again (necessary to have new job parameters included)
- supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(config, serviceCollection);
+ supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(context, serviceCollection);
/* execute */
boolean result = supportToTest.isGivenStorageSupportedByPDSProduct(contentProvider);
@@ -249,7 +275,7 @@ void isGivenStorageSupportedByPDSProduct_no_source_or_binary_in_model_required_b
.add(new ProductExecutorConfigSetupJobParameter(PDSConfigDataKeyProvider.PDS_CONFIG_SUPPORTED_DATATYPES.getKey().getId(), type.toString()));
// create support again (necessary to have new job parameters included)
- supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(config, serviceCollection);
+ supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(context, serviceCollection);
/* execute */
boolean result = supportToTest.isGivenStorageSupportedByPDSProduct(contentProvider);
@@ -272,7 +298,7 @@ void isGivenStorageSupportedByPDSProduct_no_source_or_binary_in_model_but_wrong_
jobParameters.add(new ProductExecutorConfigSetupJobParameter(PDSConfigDataKeyProvider.PDS_CONFIG_SUPPORTED_DATATYPES.getKey().getId(), typesAsString));
// create support again (necessary to have new job parameters included)
- supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(config, serviceCollection);
+ supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(context, serviceCollection);
/* execute */
boolean result = supportToTest.isGivenStorageSupportedByPDSProduct(contentProvider);
@@ -297,7 +323,25 @@ void isTargetTypeForbidden_returns_false_for_target_type_requested_is_intranet_w
}
@Test
- void createJobParametersToSendToPDS_environmentVariablesEntriesAreReplacedWithTheirContent() {
+ void createJobParametersToSendToPDS_null_list_of_template_meta_data_throws_illegal_argument_exception() {
+ assertThatThrownBy(() -> supportToTest.createJobParametersToSendToPDS(null)).isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ void createJobParametersToSendToPDS_empty_template_list_is_accepted() throws Exception {
+ /* prepare */
+ when(sechubExecutionContext.getTemplateDefinitions()).thenReturn(Collections.emptyList());
+
+ /* execute */
+ Map result = supportToTest.createJobParametersToSendToPDS(sechubExecutionContext);
+
+ /* test */
+ String data = result.get(PDSDefaultParameterKeyConstants.PARAM_KEY_PDS_CONFIG_TEMPLATE_META_DATA_LIST);
+ assertThat(data).isEqualTo("[]");
+ }
+
+ @Test
+ void createJobParametersToSendToPDS_environmentVariablesEntriesAreReplacedWithTheirContent() throws Exception {
/* prepare */
String parameterKey1 = "test.key1";
String parameterKey2 = "test.key2";
@@ -308,15 +352,13 @@ void createJobParametersToSendToPDS_environmentVariablesEntriesAreReplacedWithTh
jobParameters.add(new ProductExecutorConfigSetupJobParameter(parameterKey3, "just-a-key-not-converted"));
// create support again (necessary to have new job parameters included)
- supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(config, serviceCollection);
+ supportToTest = PDSExecutorConfigSupport.createSupportAndAssertConfigValid(context, serviceCollection);
when(systemEnvironmentVariableSupport.getValueOrVariableContent("env:A_TESTVARIABLE")).thenReturn("resolved-value");
when(systemEnvironmentVariableSupport.getValueOrVariableContent("env:A_NOT_EXISTING_VARIABLE")).thenReturn(null);
- SecHubConfiguration sechubConfiguration = mock(SecHubConfiguration.class);
-
/* execute */
- Map parameterMap = supportToTest.createJobParametersToSendToPDS(sechubConfiguration);
+ Map parameterMap = supportToTest.createJobParametersToSendToPDS(sechubExecutionContext);
/* test */
assertEquals("resolved-value", parameterMap.get(parameterKey1));
@@ -326,15 +368,13 @@ void createJobParametersToSendToPDS_environmentVariablesEntriesAreReplacedWithTh
}
@Test
- void createJobParametersToSendToPDS_mapping_is_resolved() {
+ void createJobParametersToSendToPDS_mapping_is_resolved() throws Exception {
/* prepare */
mockSecHubMappingInDatabase();
mockSecHubMappingId2InDatabase();
- SecHubConfiguration sechubConfiguration = new SecHubConfiguration();
-
/* execute */
- Map parameters = supportToTest.createJobParametersToSendToPDS(sechubConfiguration);
+ Map parameters = supportToTest.createJobParametersToSendToPDS(sechubExecutionContext);
/* test 1 */
String p1 = parameters.get(SECHUB_MAPPING_ID_1);
@@ -385,6 +425,58 @@ void createJobParametersToSendToPDS_mapping_is_resolved() {
verify(systemEnvironmentVariableSupport, times(1)).getValueOrVariableContent(any());
}
+ @Test
+ void createJobParametersToSendToPDS_pds_template_meta_data_is_created_by_result_from_template_metadata_service() throws Exception {
+ /* prepare */
+ List templateDefinitions = mock();
+ when(sechubExecutionContext.getTemplateDefinitions()).thenReturn(templateDefinitions);
+
+ List templateMetaDataServiceResult = createTemplateMetaDataServiceExampleResult();
+
+ when(templateMetaDataService.createTemplateMetaData(eq(templateDefinitions), anyString(), any(ScanType.class), eq(sechubConfiguration)))
+ .thenReturn(templateMetaDataServiceResult);
+ /* execute */
+ Map parameterMap = supportToTest.createJobParametersToSendToPDS(sechubExecutionContext);
+
+ /* test */
+ String expectedJson = JSONConverter.get().toJSON(templateMetaDataServiceResult, false);
+ String jsonFromConfigSupport = parameterMap.get(PDSDefaultParameterKeyConstants.PARAM_KEY_PDS_CONFIG_TEMPLATE_META_DATA_LIST);
+
+ assertThat(jsonFromConfigSupport).isNotNull().isEqualTo(expectedJson).hasSizeGreaterThan(10);
+
+ }
+
+ @Test
+ void createJobParametersToSendToPDS_templateDataService_is_called_twice_in_correct_order_and_arguments() throws Exception {
+ /* prepare */
+ List templateDefinitions = mock();
+ when(sechubExecutionContext.getTemplateDefinitions()).thenReturn(templateDefinitions);
+
+ List templateMetaDataServiceResult = createTemplateMetaDataServiceExampleResult();
+
+ when(templateMetaDataService.createTemplateMetaData(eq(templateDefinitions), anyString(), any(ScanType.class), eq(sechubConfiguration)))
+ .thenReturn(templateMetaDataServiceResult);
+ // next line does ensure that it is called with the former result. It also
+ // breaks further processing
+ doThrow(new TestCanaryException()).when(templateMetaDataService).ensureTemplateAssetFilesAreAvailableInStorage(templateMetaDataServiceResult);
+
+ /* execute + test */
+ assertThatThrownBy(() -> supportToTest.createJobParametersToSendToPDS(sechubExecutionContext)).isInstanceOf(TestCanaryException.class);
+
+ }
+
+ private List createTemplateMetaDataServiceExampleResult() {
+ List templateMetaDataServiceResult = new ArrayList<>();
+ PDSTemplateMetaData pdsTemplateMetaData1 = new PDSTemplateMetaData();
+ PDSAssetData assetData1 = new PDSAssetData();
+ assetData1.setAssetId("asset-id-1");
+ assetData1.setChecksum("checksum1");
+ assetData1.setFileName("file1");
+ pdsTemplateMetaData1.setAssetData(assetData1);
+ templateMetaDataServiceResult.add(pdsTemplateMetaData1);
+ return templateMetaDataServiceResult;
+ }
+
private void mockSecHubMappingId2InDatabase() {
String mappingId = SECHUB_MAPPING_ID_2;
diff --git a/sechub-scan-product-pds/src/test/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSTemplateMetaDataServiceTest.java b/sechub-scan-product-pds/src/test/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSTemplateMetaDataServiceTest.java
new file mode 100644
index 0000000000..1d5d9ad1d1
--- /dev/null
+++ b/sechub-scan-product-pds/src/test/java/com/mercedesbenz/sechub/domain/scan/product/pds/PDSTemplateMetaDataServiceTest.java
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.scan.product.pds;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import com.mercedesbenz.sechub.commons.core.ConfigurationFailureException;
+import com.mercedesbenz.sechub.commons.model.ScanType;
+import com.mercedesbenz.sechub.commons.model.SecHubConfigurationModel;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition.TemplateVariable;
+import com.mercedesbenz.sechub.commons.model.template.TemplateType;
+import com.mercedesbenz.sechub.commons.pds.data.PDSTemplateMetaData;
+import com.mercedesbenz.sechub.commons.pds.data.PDSTemplateMetaData.PDSAssetData;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetDetailData;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetFileData;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetService;
+import com.mercedesbenz.sechub.sharedkernel.error.NotFoundException;
+
+class PDSTemplateMetaDataServiceTest {
+ private PDSTemplateMetaDataService serviceToTest;
+ private AssetService assetService;
+ private RelevantScanTemplateDefinitionFilter filter;
+
+ @BeforeEach
+ void beforeEach() {
+
+ filter = mock();
+ assetService = mock();
+
+ serviceToTest = new PDSTemplateMetaDataService();
+ serviceToTest.filter = filter;
+ serviceToTest.assetService = assetService;
+ }
+
+ @Test
+ void createTemplateMetaData_returns_pds_template_meta_data_when_asset_is_available() throws Exception {
+
+ /* prepare */
+ ScanType exampleScanType = ScanType.WEB_SCAN;
+ TemplateType exampleTemplateType = TemplateType.WEBSCAN_LOGIN;
+ String exampleTemplateId1 = "template_id_1";
+ String exampleChecksum1 = "checksum1";
+ String exampleAssetId1 = "asset1";
+
+ SecHubConfigurationModel configuration = mock();
+
+ // prepare template data
+ List givenDefinitions = mock();
+ List filteredDefinitions = new ArrayList<>();
+ TemplateVariable variable1 = new TemplateVariable();
+ variable1.setName("var1");
+
+ TemplateDefinition templateDefinition1 = new TemplateDefinition();
+ templateDefinition1.setAssetId(exampleAssetId1);
+ templateDefinition1.setId(exampleTemplateId1);
+ templateDefinition1.setType(exampleTemplateType);
+
+ templateDefinition1.getVariables().add(variable1);
+ filteredDefinitions.add(templateDefinition1);
+
+ when(filter.filter(givenDefinitions, exampleScanType, configuration)).thenReturn(filteredDefinitions);
+
+ // prepare asset data
+ AssetFileData assetFile1a = new AssetFileData();
+ assetFile1a.setChecksum("other");
+ assetFile1a.setFileName("other_product_id.zip");
+ AssetDetailData assetDetailData1 = mock();
+
+ AssetFileData assetFile1b = new AssetFileData();
+ assetFile1b.setChecksum(exampleChecksum1);
+ assetFile1b.setFileName("test_product_id.zip");
+
+ List assetfiles = new ArrayList<>();
+ assetfiles.add(assetFile1a);
+ assetfiles.add(assetFile1b);
+
+ when(assetDetailData1.getFiles()).thenReturn(assetfiles);
+ when(assetService.fetchAssetDetails(exampleAssetId1)).thenReturn(assetDetailData1);
+
+ /* execute */
+ List result = serviceToTest.createTemplateMetaData(givenDefinitions, "test_product_id", exampleScanType, configuration);
+
+ /* test */
+ PDSTemplateMetaData expectedTemplateMetaData1 = new PDSTemplateMetaData();
+ expectedTemplateMetaData1.setTemplateId(exampleTemplateId1);
+ expectedTemplateMetaData1.setTemplateType(exampleTemplateType);
+
+ PDSAssetData assetData1 = new PDSAssetData();
+ assetData1.setAssetId(exampleAssetId1);
+ assetData1.setChecksum(exampleChecksum1);
+ assetData1.setFileName("test_product_id.zip");
+
+ expectedTemplateMetaData1.setAssetData(assetData1);
+
+ assertThat(result).isNotNull().contains(expectedTemplateMetaData1).hasSize(1);
+ }
+
+ @Test
+ void createTemplateMetaData_returns_empty_pds_template_meta_data_when_definition_list_is_empty() throws Exception {
+
+ /* prepare */
+ ScanType exampleScanType = ScanType.WEB_SCAN;
+
+ SecHubConfigurationModel configuration = mock();
+
+ // prepare template data
+ List givenDefinitions = mock();
+ List filteredDefinitions = new ArrayList<>();
+
+ when(filter.filter(givenDefinitions, exampleScanType, configuration)).thenReturn(filteredDefinitions);
+
+ /* execute */
+ List result = serviceToTest.createTemplateMetaData(givenDefinitions, "test_product_id", exampleScanType, configuration);
+
+ /* test */
+ assertThat(result).isNotNull().isEmpty();
+ }
+
+ @Test
+ void createTemplateMetaData_throws_exception_when_asset_not_found() throws Exception {
+
+ /* prepare */
+ ScanType exampleScanType = ScanType.WEB_SCAN;
+ TemplateType exampleTemplateType = TemplateType.WEBSCAN_LOGIN;
+ String exampleTemplateId1 = "template_id_1";
+ String exampleAssetId1 = "asset1";
+
+ SecHubConfigurationModel configuration = mock();
+
+ // prepare template data
+ List givenDefinitions = mock();
+ List filteredDefinitions = new ArrayList<>();
+ TemplateVariable variable1 = new TemplateVariable();
+ variable1.setName("var1");
+
+ TemplateDefinition templateDefinition1 = new TemplateDefinition();
+ templateDefinition1.setAssetId(exampleAssetId1);
+ templateDefinition1.setId(exampleTemplateId1);
+ templateDefinition1.setType(exampleTemplateType);
+
+ templateDefinition1.getVariables().add(variable1);
+ filteredDefinitions.add(templateDefinition1);
+
+ when(filter.filter(givenDefinitions, exampleScanType, configuration)).thenReturn(filteredDefinitions);
+
+ when(assetService.fetchAssetDetails(exampleAssetId1)).thenThrow(NotFoundException.class);
+
+ /* execute + test */
+ assertThatThrownBy(() -> serviceToTest.createTemplateMetaData(givenDefinitions, "test_product_id", exampleScanType, configuration))
+ .isInstanceOf(ConfigurationFailureException.class).hasMessageContaining(exampleAssetId1 + " does not exist");
+ }
+
+ @Test
+ void createTemplateMetaData_throws_exception_when_asset_product_file_not_found() throws Exception {
+
+ /* prepare */
+ ScanType exampleScanType = ScanType.WEB_SCAN;
+ TemplateType exampleTemplateType = TemplateType.WEBSCAN_LOGIN;
+ String exampleTemplateId1 = "template_id_1";
+ String exampleAssetId1 = "asset1";
+
+ SecHubConfigurationModel configuration = mock();
+
+ // prepare template data
+ List givenDefinitions = mock();
+ List filteredDefinitions = new ArrayList<>();
+ TemplateVariable variable1 = new TemplateVariable();
+ variable1.setName("var1");
+
+ TemplateDefinition templateDefinition1 = new TemplateDefinition();
+ templateDefinition1.setAssetId(exampleAssetId1);
+ templateDefinition1.setId(exampleTemplateId1);
+ templateDefinition1.setType(exampleTemplateType);
+
+ templateDefinition1.getVariables().add(variable1);
+ filteredDefinitions.add(templateDefinition1);
+
+ when(filter.filter(givenDefinitions, exampleScanType, configuration)).thenReturn(filteredDefinitions);
+ // prepare asset data
+ AssetFileData assetFile1a = new AssetFileData();
+ assetFile1a.setChecksum("other");
+ assetFile1a.setFileName("other_product_id.zip");
+ AssetDetailData assetDetailData1 = mock();
+
+ List assetfiles = new ArrayList<>();
+ assetfiles.add(assetFile1a);
+
+ when(assetDetailData1.getFiles()).thenReturn(assetfiles);
+ when(assetService.fetchAssetDetails(exampleAssetId1)).thenReturn(assetDetailData1);
+
+ /* execute + test */
+ assertThatThrownBy(() -> serviceToTest.createTemplateMetaData(givenDefinitions, "test_product_id", exampleScanType, configuration))
+ .isInstanceOf(ConfigurationFailureException.class).hasMessageContaining("does not contain file 'test_product_id.zip'");
+ ;
+ }
+
+ @Test
+ void ensureTemplateAssetFilesAreAvailableInStorage_calls_asste_service_for_each_metadata_assetfile() throws Exception {
+
+ /* prepare */
+ PDSAssetData assetData1 = new PDSAssetData();
+ assetData1.setAssetId("asset1");
+ assetData1.setFileName("file1.txt");
+
+ PDSTemplateMetaData templateMetaData1 = new PDSTemplateMetaData();
+ templateMetaData1.setAssetData(assetData1);
+
+ PDSAssetData assetData2 = new PDSAssetData();
+ assetData2.setAssetId("asset2");
+ assetData2.setFileName("file2.txt");
+
+ PDSTemplateMetaData templateMetaData2 = new PDSTemplateMetaData();
+ templateMetaData2.setAssetData(assetData2);
+
+ List metaDataList = new ArrayList<>();
+ metaDataList.add(templateMetaData1);
+ metaDataList.add(templateMetaData2);
+
+ /* execute */
+ serviceToTest.ensureTemplateAssetFilesAreAvailableInStorage(metaDataList);
+
+ /* test */
+ verify(assetService).ensureAssetFileInStorageAvailableAndHasSameChecksumAsInDatabase("file1.txt", "asset1");
+ verify(assetService).ensureAssetFileInStorageAvailableAndHasSameChecksumAsInDatabase("file2.txt", "asset2");
+ }
+
+}
diff --git a/sechub-scan-product-pds/src/test/java/com/mercedesbenz/sechub/domain/scan/product/pds/RelevantScanTemplateDefinitionFilterTest.java b/sechub-scan-product-pds/src/test/java/com/mercedesbenz/sechub/domain/scan/product/pds/RelevantScanTemplateDefinitionFilterTest.java
new file mode 100644
index 0000000000..365d9ab202
--- /dev/null
+++ b/sechub-scan-product-pds/src/test/java/com/mercedesbenz/sechub/domain/scan/product/pds/RelevantScanTemplateDefinitionFilterTest.java
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.scan.product.pds;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.EnumSource.Mode;
+import org.junit.jupiter.params.provider.NullSource;
+
+import com.mercedesbenz.sechub.commons.model.ScanType;
+import com.mercedesbenz.sechub.commons.model.SecHubConfigurationModel;
+import com.mercedesbenz.sechub.commons.model.template.TemplateData;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDataResolver;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition;
+import com.mercedesbenz.sechub.commons.model.template.TemplateType;
+
+class RelevantScanTemplateDefinitionFilterTest {
+ private RelevantScanTemplateDefinitionFilter filterToTest;
+ private SecHubConfigurationModel configuration;
+ private TemplateDataResolver templateDataResolver;
+ private TemplateDefinition defininition1WebScanLogin;
+ private TemplateDefinition defininition2NoTemplateType;
+ private List templateDefinitions;
+
+ @BeforeEach
+ void beforeEach() {
+ templateDataResolver = mock();
+ configuration = mock();
+
+ filterToTest = new RelevantScanTemplateDefinitionFilter();
+ filterToTest.templateDataResolver = templateDataResolver;
+
+ defininition1WebScanLogin = new TemplateDefinition();
+ defininition1WebScanLogin.setType(TemplateType.WEBSCAN_LOGIN);
+
+ defininition2NoTemplateType = new TemplateDefinition();
+ defininition2NoTemplateType.setType(null);
+
+ templateDefinitions = new ArrayList<>(2);
+ templateDefinitions.add(defininition1WebScanLogin);
+ templateDefinitions.add(defininition2NoTemplateType);
+
+ }
+
+ @Test
+ void webscan_login_definition_inside_result_when_resolver_finds_template_data_and_scan_type_is_web_scan() {
+ /* prepare */
+ TemplateData templateData = mock();
+
+ when(templateDataResolver.resolveTemplateData(TemplateType.WEBSCAN_LOGIN, configuration)).thenReturn(templateData);
+
+ /* execute */
+ List result = filterToTest.filter(templateDefinitions, ScanType.WEB_SCAN, configuration);
+
+ /* test */
+ assertThat(result).contains(defininition1WebScanLogin).doesNotContain(defininition2NoTemplateType).hasSize(1);
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = ScanType.class, mode = Mode.EXCLUDE, names = "WEB_SCAN")
+ @NullSource
+ void webscan_login_definition_not_inside_result_when_resolver_finds_template_data_but_scan_type_is_not_web_scan(ScanType scanType) {
+ /* prepare */
+ TemplateData templateData = mock();
+
+ when(templateDataResolver.resolveTemplateData(TemplateType.WEBSCAN_LOGIN, configuration)).thenReturn(templateData);
+
+ /* execute */
+ List result = filterToTest.filter(templateDefinitions, scanType, configuration);
+
+ /* test */
+ assertThat(result).isEmpty();
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = ScanType.class)
+ @NullSource
+ void no_login_definition_inside_result_when_resolver_does_not_find_template_data(ScanType scanType) {
+ /* prepare */
+ when(templateDataResolver.resolveTemplateData(TemplateType.WEBSCAN_LOGIN, configuration)).thenReturn(null);
+
+ /* execute */
+ List result = filterToTest.filter(templateDefinitions, scanType, configuration);
+
+ /* test */
+ assertThat(result).isEmpty();
+ }
+
+}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/adapter/DefaultExecutorConfigSupport.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/adapter/DefaultExecutorConfigSupport.java
index 63abcbeae6..ec6eac4871 100644
--- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/adapter/DefaultExecutorConfigSupport.java
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/adapter/DefaultExecutorConfigSupport.java
@@ -14,6 +14,7 @@
import com.mercedesbenz.sechub.commons.mapping.NamePatternIdProvider;
import com.mercedesbenz.sechub.commons.mapping.NamePatternIdProviderFactory;
import com.mercedesbenz.sechub.commons.model.SecHubRuntimeException;
+import com.mercedesbenz.sechub.domain.scan.product.ProductExecutorContext;
import com.mercedesbenz.sechub.domain.scan.product.config.ProductExecutorConfig;
import com.mercedesbenz.sechub.domain.scan.product.config.ProductExecutorConfigSetupCredentials;
import com.mercedesbenz.sechub.domain.scan.product.config.ProductExecutorConfigSetupJobParameter;
@@ -41,12 +42,13 @@ public class DefaultExecutorConfigSupport {
NamePatternIdProviderFactory providerFactory;
- public DefaultExecutorConfigSupport(ProductExecutorConfig config, SystemEnvironmentVariableSupport variableSupport,
+ public DefaultExecutorConfigSupport(ProductExecutorContext context, SystemEnvironmentVariableSupport variableSupport,
Validation validation) {
- notNull(config, "config may not be null!");
+ notNull(context, "context may not be null!");
+ notNull(context.getExecutorConfig(), "executor config may not be null!");
notNull(variableSupport, "variableSupport may not be null!");
- this.config = config;
+ this.config = context.getExecutorConfig();
this.variableSupport = variableSupport;
providerFactory = new NamePatternIdProviderFactory();
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/DefaultAdapterConfigurationStrategy.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/DefaultAdapterConfigurationStrategy.java
index 128cd891bf..3f97199e3f 100644
--- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/DefaultAdapterConfigurationStrategy.java
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/DefaultAdapterConfigurationStrategy.java
@@ -5,6 +5,7 @@
import com.mercedesbenz.sechub.adapter.AdapterConfigBuilder;
import com.mercedesbenz.sechub.adapter.AdapterConfigurationStrategy;
import com.mercedesbenz.sechub.adapter.DefaultExecutorConfigSupport;
+import com.mercedesbenz.sechub.commons.core.ConfigurationFailureException;
import com.mercedesbenz.sechub.commons.model.ScanType;
import com.mercedesbenz.sechub.domain.scan.product.ProductExecutorData;
@@ -46,7 +47,7 @@ public DefaultAdapterConfigurationStrategy(ProductExecutorData data, DefaultExec
}
@Override
- public void configure(B configBuilder) {
+ public void configure(B configBuilder) throws ConfigurationFailureException {
/* @formatter:off */
SecHubExecutionContext context = data.getSechubExecutionContext();
String projectId = context.getConfiguration().getProjectId();
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/IntegrationTestScanRestController.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/IntegrationTestScanRestController.java
index 2568951e77..d949f38dd4 100644
--- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/IntegrationTestScanRestController.java
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/IntegrationTestScanRestController.java
@@ -37,6 +37,8 @@
import com.mercedesbenz.sechub.domain.scan.product.config.ProductExecutionProfileRepository;
import com.mercedesbenz.sechub.domain.scan.product.config.ProductExecutorConfigInfo;
import com.mercedesbenz.sechub.domain.scan.product.config.WithoutProductExecutorConfigInfo;
+import com.mercedesbenz.sechub.domain.scan.project.ScanProjectConfig;
+import com.mercedesbenz.sechub.domain.scan.project.ScanProjectConfigRepository;
import com.mercedesbenz.sechub.domain.scan.report.ScanReportCountService;
import com.mercedesbenz.sechub.sharedkernel.ProductIdentifier;
import com.mercedesbenz.sechub.sharedkernel.Profiles;
@@ -92,6 +94,9 @@ public class IntegrationTestScanRestController {
@Autowired
private ScanConfigService scanConfigService;
+ @Autowired
+ private ScanProjectConfigRepository scanProjectConfigRepository;
+
@RequestMapping(path = APIConstants.API_ANONYMOUS + "integrationtest/autocleanup/inspection/scan/days", method = RequestMethod.GET, produces = {
MediaType.APPLICATION_JSON_VALUE })
public long fetchScheduleAutoCleanupConfiguredDays() {
@@ -262,4 +267,9 @@ public List getPDSJobUUIDSForSecHubJOob(@PathVariable("jobUUID") UUID sech
return list;
}
+ @RequestMapping(path = APIConstants.API_ANONYMOUS + "integrationtest/project-scanconfig/{projectId}", method = RequestMethod.GET)
+ public List fetchScanConfigValue(@PathVariable("projectId") String projectId) {
+ return scanProjectConfigRepository.findAllForProject(projectId);
+ }
+
}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/ProjectDataDeleteService.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/ProjectDataDeleteService.java
index 2580f42855..fc4bb67041 100644
--- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/ProjectDataDeleteService.java
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/ProjectDataDeleteService.java
@@ -59,6 +59,10 @@ public void deleteAllDataForProject(String projectId) {
productResultRepository.deleteAllResultsForProject(projectId);
scanReportRepository.deleteAllReportsForProject(projectId);
scanLogRepository.deleteAllLogDataForProject(projectId);
+ /*
+ * next line deletes any project related configuration - this includes template
+ * assignment
+ */
scanProjectConfigRepository.deleteAllConfigurationsForProject(projectId);
profileRepository.deleteAllProfileRelationsToProject(projectId);
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/ScanJobExecutionRunnable.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/ScanJobExecutionRunnable.java
index c102279096..8595244d30 100644
--- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/ScanJobExecutionRunnable.java
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/ScanJobExecutionRunnable.java
@@ -14,6 +14,7 @@
import com.mercedesbenz.sechub.domain.scan.product.WebScanProductExecutionService;
import com.mercedesbenz.sechub.sharedkernel.LogConstants;
import com.mercedesbenz.sechub.sharedkernel.Step;
+import com.mercedesbenz.sechub.sharedkernel.usecases.job.UseCaseSchedulerStartsJob;
import com.mercedesbenz.sechub.sharedkernel.usecases.other.UseCaseSystemSuspendsJobsWhenSigTermReceived;
/**
@@ -36,6 +37,13 @@ public ScanJobRunnableData getRunnableData() {
}
@Override
+ @UseCaseSchedulerStartsJob(@Step(number = 3, name = "Runnable calls execution services",
+
+ description = """
+ The job execution runnable creates the execution context and calls dedicated execution services
+ (preparation, analytics, product execution, storage and reporting) synchronously for the job.
+ It is also responsible for cancelation and supsension of jobs.
+ """))
public void run() {
/* runs in own thread so we set job uuid to MDC here ! */
try {
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/ScanMessageHandler.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/ScanMessageHandler.java
index e2cbade215..0f7143a77c 100644
--- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/ScanMessageHandler.java
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/ScanMessageHandler.java
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT
package com.mercedesbenz.sechub.domain.scan;
+import java.util.Set;
import java.util.UUID;
import org.slf4j.Logger;
@@ -16,6 +17,7 @@
import com.mercedesbenz.sechub.domain.scan.config.UpdateScanMappingConfigurationService;
import com.mercedesbenz.sechub.domain.scan.product.ProductResultService;
import com.mercedesbenz.sechub.domain.scan.project.ScanProjectConfigAccessLevelService;
+import com.mercedesbenz.sechub.domain.scan.template.TemplateService;
import com.mercedesbenz.sechub.sharedkernel.Step;
import com.mercedesbenz.sechub.sharedkernel.mapping.MappingIdentifier;
import com.mercedesbenz.sechub.sharedkernel.mapping.MappingIdentifier.MappingType;
@@ -33,6 +35,8 @@
import com.mercedesbenz.sechub.sharedkernel.messaging.SynchronMessageHandler;
import com.mercedesbenz.sechub.sharedkernel.messaging.UserMessage;
import com.mercedesbenz.sechub.sharedkernel.project.ProjectAccessLevel;
+import com.mercedesbenz.sechub.sharedkernel.template.SecHubProjectTemplateData;
+import com.mercedesbenz.sechub.sharedkernel.template.SecHubProjectToTemplate;
import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdmiUpdatesMappingConfiguration;
import com.mercedesbenz.sechub.sharedkernel.usecases.admin.project.UseCaseAdminChangesProjectAccessLevel;
@@ -68,6 +72,9 @@ public class ScanMessageHandler implements AsynchronMessageHandler, SynchronMess
@Autowired
ScanConfigService configService;
+ @Autowired
+ TemplateService templateService;
+
@Override
public void receiveAsyncMessage(DomainMessage request) {
MessageID messageId = request.getMessageId();
@@ -114,12 +121,47 @@ public DomainMessageSynchronousResult receiveSynchronMessage(DomainMessage reque
case REQUEST_PURGE_JOB_RESULTS:
return handleJobRestartHardRequested(request);
-
+ case REQUEST_ASSIGN_TEMPLATE_TO_PROJECT:
+ return handleAssignTemplateToProjectRequest(request);
+ case REQUEST_UNASSIGN_TEMPLATE_FROM_PROJECT:
+ return handleUnassignTemplateFromProjectRequest(request);
default:
throw new IllegalStateException("unhandled message id:" + messageId);
}
}
+ @IsRecevingSyncMessage(MessageID.REQUEST_ASSIGN_TEMPLATE_TO_PROJECT)
+ private DomainMessageSynchronousResult handleAssignTemplateToProjectRequest(DomainMessage request) {
+ SecHubProjectToTemplate projectToTemplate = request.get(MessageDataKeys.PROJECT_TO_TEMPLATE);
+ String templateId = projectToTemplate.getTemplateId();
+ String projectId = projectToTemplate.getProjectId();
+
+ try {
+ templateService.assignTemplateToProject(templateId, projectId);
+ Set templateIds = templateService.fetchAssignedTemplateIdsForProject(projectId);
+ return templateAssignmentDone(projectId, templateIds);
+ } catch (Exception e) {
+ return templateAssignmentFailed(e);
+ }
+
+ }
+
+ @IsRecevingSyncMessage(MessageID.REQUEST_UNASSIGN_TEMPLATE_FROM_PROJECT)
+ private DomainMessageSynchronousResult handleUnassignTemplateFromProjectRequest(DomainMessage request) {
+ SecHubProjectToTemplate projectToTemplate = request.get(MessageDataKeys.PROJECT_TO_TEMPLATE);
+ String templateId = projectToTemplate.getTemplateId();
+ String projectId = projectToTemplate.getProjectId();
+
+ try {
+ templateService.unassignTemplateFromProject(templateId, projectId);
+ Set templateIds = templateService.fetchAssignedTemplateIdsForProject(projectId);
+ return templateUnassignmentDone(projectId, templateIds);
+ } catch (Exception e) {
+ return templateUnassignmentFailed(e);
+ }
+
+ }
+
@IsRecevingSyncMessage(MessageID.REQUEST_PURGE_JOB_RESULTS)
private DomainMessageSynchronousResult handleJobRestartHardRequested(DomainMessage request) {
UUID jobUUID = request.get(MessageDataKeys.SECHUB_JOB_UUID);
@@ -147,6 +189,40 @@ private DomainMessageSynchronousResult purgeDone(UUID jobUUID) {
return result;
}
+ @IsSendingSyncMessageAnswer(value = MessageID.RESULT_ASSIGN_TEMPLATE_TO_PROJECT, answeringTo = MessageID.REQUEST_ASSIGN_TEMPLATE_TO_PROJECT, branchName = "success")
+ private DomainMessageSynchronousResult templateAssignmentDone(String projectId, Set assignedTemplates) {
+ DomainMessageSynchronousResult result = new DomainMessageSynchronousResult(MessageID.RESULT_ASSIGN_TEMPLATE_TO_PROJECT);
+ SecHubProjectTemplateData templates = new SecHubProjectTemplateData();
+ templates.setProjectId(projectId);
+ templates.getTemplateIds().addAll(assignedTemplates);
+
+ result.set(MessageDataKeys.PROJECT_TEMPLATES, templates);
+ return result;
+ }
+
+ @IsSendingSyncMessageAnswer(value = MessageID.RESULT_ASSIGN_TEMPLATE_TO_PROJECT, answeringTo = MessageID.REQUEST_ASSIGN_TEMPLATE_TO_PROJECT, branchName = "failed")
+ private DomainMessageSynchronousResult templateAssignmentFailed(Exception failure) {
+ DomainMessageSynchronousResult result = new DomainMessageSynchronousResult(MessageID.RESULT_ASSIGN_TEMPLATE_TO_PROJECT, failure);
+ return result;
+ }
+
+ @IsSendingSyncMessageAnswer(value = MessageID.RESULT_UNASSIGN_TEMPLATE_FROM_PROJECT, answeringTo = MessageID.REQUEST_UNASSIGN_TEMPLATE_FROM_PROJECT, branchName = "success")
+ private DomainMessageSynchronousResult templateUnassignmentDone(String projectId, Set assignedTemplates) {
+ DomainMessageSynchronousResult result = new DomainMessageSynchronousResult(MessageID.RESULT_UNASSIGN_TEMPLATE_FROM_PROJECT);
+ SecHubProjectTemplateData templates = new SecHubProjectTemplateData();
+ templates.setProjectId(projectId);
+ templates.getTemplateIds().addAll(assignedTemplates);
+
+ result.set(MessageDataKeys.PROJECT_TEMPLATES, templates);
+ return result;
+ }
+
+ @IsSendingSyncMessageAnswer(value = MessageID.RESULT_UNASSIGN_TEMPLATE_FROM_PROJECT, answeringTo = MessageID.REQUEST_UNASSIGN_TEMPLATE_FROM_PROJECT, branchName = "failed")
+ private DomainMessageSynchronousResult templateUnassignmentFailed(Exception failure) {
+ DomainMessageSynchronousResult result = new DomainMessageSynchronousResult(MessageID.RESULT_UNASSIGN_TEMPLATE_FROM_PROJECT, failure);
+ return result;
+ }
+
@IsReceivingAsyncMessage(MessageID.MAPPING_CONFIGURATION_CHANGED)
@UseCaseAdmiUpdatesMappingConfiguration(@Step(number = 3, name = "Event handler", description = "Receives mapping configuration change event"))
private void handleMappingConfigurationChanged(DomainMessage request) {
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/ScanService.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/ScanService.java
index 6ad21e23b4..98f2563fef 100644
--- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/ScanService.java
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/ScanService.java
@@ -26,6 +26,7 @@
import com.mercedesbenz.sechub.domain.scan.report.CreateScanReportService;
import com.mercedesbenz.sechub.domain.scan.report.ScanReport;
import com.mercedesbenz.sechub.domain.scan.report.ScanReportException;
+import com.mercedesbenz.sechub.domain.scan.template.TemplateService;
import com.mercedesbenz.sechub.sharedkernel.MustBeDocumented;
import com.mercedesbenz.sechub.sharedkernel.ProgressStateFetcher;
import com.mercedesbenz.sechub.sharedkernel.Step;
@@ -80,6 +81,9 @@ public class ScanService implements SynchronMessageHandler {
@Autowired
ScanProgressStateFetcherFactory monitorFactory;
+ @Autowired
+ TemplateService templateService;
+
@MustBeDocumented("Define delay in milliseconds, for before next job cancellation check will be executed.")
@Value("${sechub.config.check.canceljob.delay:" + DEFAULT_CHECK_CANCELJOB_DELAY_MILLIS + "}")
private int millisecondsToWaitBeforeCancelCheck = DEFAULT_CHECK_CANCELJOB_DELAY_MILLIS;
@@ -195,7 +199,7 @@ private void cleanupStorage(SecHubExecutionContext context) {
}
- private SecHubExecutionContext createExecutionContext(DomainMessage message) throws JSONConverterException {
+ SecHubExecutionContext createExecutionContext(DomainMessage message) throws JSONConverterException {
UUID executionUUID = message.get(SECHUB_EXECUTION_UUID);
UUID sechubJobUUID = message.get(SECHUB_JOB_UUID);
@@ -206,7 +210,6 @@ private SecHubExecutionContext createExecutionContext(DomainMessage message) thr
throw new IllegalStateException("SecHubConfiguration not found in message - so cannot execute!");
}
SecHubExecutionContext executionContext = new SecHubExecutionContext(sechubJobUUID, configuration, executedBy, executionUUID);
-
buildOptions(executionContext);
return executionContext;
@@ -224,6 +227,10 @@ private void buildOptions(SecHubExecutionContext executionContext) {
ScanProjectMockDataConfiguration mockDataConfig = ScanProjectMockDataConfiguration.fromString(data);
executionContext.putData(ScanKey.PROJECT_MOCKDATA_CONFIGURATION, mockDataConfig);
}
+
+ /* append template definitions */
+ executionContext.getTemplateDefinitions().addAll(templateService.fetchAllTemplateDefinitionsForProject(projectId));
+
}
@Override
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/SecHubExecutionContext.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/SecHubExecutionContext.java
index 43f2b7621c..f93e6969d2 100644
--- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/SecHubExecutionContext.java
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/SecHubExecutionContext.java
@@ -2,13 +2,16 @@
package com.mercedesbenz.sechub.domain.scan;
import java.time.LocalDateTime;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition;
import com.mercedesbenz.sechub.domain.scan.product.ProductExecutor;
import com.mercedesbenz.sechub.domain.scan.product.ProductExecutorData;
import com.mercedesbenz.sechub.sharedkernel.TypedKey;
@@ -50,6 +53,8 @@ public class SecHubExecutionContext {
private boolean suspended;
+ private List templateDefinitions = new ArrayList<>();
+
public SecHubExecutionContext(UUID sechubJobUUID, SecHubConfiguration configuration, String executedBy, UUID executionUUID) {
this(sechubJobUUID, configuration, executedBy, executionUUID, null);
}
@@ -191,4 +196,8 @@ public boolean isSuspended() {
return suspended;
}
+ public List getTemplateDefinitions() {
+ return templateDefinitions;
+ }
+
}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetDetailData.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetDetailData.java
new file mode 100644
index 0000000000..79942f30f8
--- /dev/null
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetDetailData.java
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.scan.asset;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.mercedesbenz.sechub.commons.model.JSONable;
+
+@JsonIgnoreProperties(ignoreUnknown = true) // we do ignore to avoid problems from wrong configured values!
+public class AssetDetailData implements JSONable {
+
+ private String assetId;
+
+ private List files = new ArrayList<>();
+
+ public void setAssetId(String assetid) {
+ this.assetId = assetid;
+ }
+
+ public String getAssetId() {
+ return assetId;
+ }
+
+ public List getFiles() {
+ return files;
+ }
+
+ @Override
+ public Class getJSONTargetClass() {
+ return AssetDetailData.class;
+ }
+}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetFile.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetFile.java
new file mode 100644
index 0000000000..9d912b89e6
--- /dev/null
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetFile.java
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.scan.asset;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
+import jakarta.persistence.EmbeddedId;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import jakarta.persistence.Version;
+
+/**
+ * Represents a template
+ *
+ * @author Albert Tregnaghi
+ *
+ */
+@Entity
+@Table(name = AssetFile.TABLE_NAME)
+public class AssetFile {
+
+ /* +-----------------------------------------------------------------------+ */
+ /* +............................ SQL ......................................+ */
+ /* +-----------------------------------------------------------------------+ */
+ public static final String TABLE_NAME = "SCAN_ASSET_FILE";
+
+ public static final String COLUMN_ASSET_ID = "ASSET_ID";
+ public static final String COLUMN_FILE_NAME = "FILE_NAME";
+ public static final String COLUMN_DATA = "DATA";
+ public static final String COLUMN_CHECKSUM = "CHECKSUM";
+
+ /* +-----------------------------------------------------------------------+ */
+ /* +............................ JPQL .....................................+ */
+ /* +-----------------------------------------------------------------------+ */
+ public static final String CLASS_NAME = "AssetFile";
+
+ @EmbeddedId
+ AssetFileCompositeKey key;
+
+ @Column(name = COLUMN_CHECKSUM)
+ String checksum;
+
+ @Column(name = COLUMN_DATA)
+ byte[] data;
+
+ @Version
+ @Column(name = "VERSION")
+ @JsonIgnore
+ Integer version;
+
+ AssetFile() {
+ // jpa only
+ }
+
+ public AssetFile(AssetFileCompositeKey key) {
+ this.key = key;
+ }
+
+ public AssetFileCompositeKey getKey() {
+ return key;
+ }
+
+ public void setChecksum(String definition) {
+ this.checksum = definition;
+ }
+
+ public String getChecksum() {
+ return checksum;
+ }
+
+ public void setData(byte[] data) {
+ this.data = data;
+ }
+
+ public byte[] getData() {
+ return data;
+ }
+
+ /**
+ * Asset id and file name or only strings. To avoid confusion in constructor
+ * usage, this builder was introduced.
+ *
+ * @author Albert Tregnaghi
+ *
+ */
+ public static class AssetFileCompositeKeyBuilder {
+
+ private String assetId;
+ private String fileName;
+
+ public AssetFileCompositeKey build() {
+
+ if (assetId == null) {
+ throw new IllegalStateException("asset id not defined!");
+ }
+ if (fileName == null) {
+ throw new IllegalStateException("file name not defined!");
+ }
+
+ AssetFileCompositeKey key = new AssetFileCompositeKey();
+ key.setAssetId(assetId);
+ key.setFileName(fileName);
+
+ return key;
+ }
+
+ public AssetFileCompositeKeyBuilder assetId(String assetId) {
+ this.assetId = assetId;
+ return this;
+ }
+
+ public AssetFileCompositeKeyBuilder fileName(String fileName) {
+ this.fileName = fileName;
+ return this;
+ }
+ }
+
+ @Embeddable
+ public static class AssetFileCompositeKey implements Serializable {
+
+ public static AssetFileCompositeKeyBuilder builder() {
+ return new AssetFileCompositeKeyBuilder();
+ }
+
+ private static final long serialVersionUID = 8753389792382752253L;
+
+ @Column(name = COLUMN_ASSET_ID, nullable = false)
+ private String assetId;
+
+ @Column(name = COLUMN_FILE_NAME, nullable = false)
+ private String fileName;
+
+ AssetFileCompositeKey() {
+ // jpa only
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public void setFileName(String userId) {
+ this.fileName = userId;
+ }
+
+ public String getAssetId() {
+ return assetId;
+ }
+
+ public void setAssetId(String projectId) {
+ this.assetId = projectId;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((assetId == null) ? 0 : assetId.hashCode());
+ result = prime * result + ((fileName == null) ? 0 : fileName.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ AssetFileCompositeKey other = (AssetFileCompositeKey) obj;
+ if (assetId == null) {
+ if (other.assetId != null)
+ return false;
+ } else if (!assetId.equals(other.assetId))
+ return false;
+ if (fileName == null) {
+ if (other.fileName != null)
+ return false;
+ } else if (!fileName.equals(other.fileName))
+ return false;
+ return true;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(key);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ AssetFile other = (AssetFile) obj;
+ return Objects.equals(key, other.key);
+ }
+
+ @Override
+ public String toString() {
+ return "AssetFile [" + (key != null ? "key=" + key + ", " : "") + (checksum != null ? "checksum=" + checksum + ", " : "")
+ + (version != null ? "version=" + version : "") + "]";
+ }
+}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetFileData.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetFileData.java
new file mode 100644
index 0000000000..6e18327e57
--- /dev/null
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetFileData.java
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.scan.asset;
+
+import java.util.Objects;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+@JsonIgnoreProperties(ignoreUnknown = true) // we do ignore to avoid problems from wrong configured values!
+public class AssetFileData {
+
+ private String fileName;
+
+ private String checksum;
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ public String getChecksum() {
+ return checksum;
+ }
+
+ public void setChecksum(String checksum) {
+ this.checksum = checksum;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(checksum, fileName);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ AssetFileData other = (AssetFileData) obj;
+ return Objects.equals(checksum, other.checksum) && Objects.equals(fileName, other.fileName);
+ }
+
+ @Override
+ public String toString() {
+ return "AssetFileInformation [" + (checksum != null ? "checksum=" + checksum + ", " : "") + (fileName != null ? "fileName=" + fileName : "") + "]";
+ }
+
+}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetFileRepository.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetFileRepository.java
new file mode 100644
index 0000000000..b6394bac30
--- /dev/null
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetFileRepository.java
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.scan.asset;
+
+import static com.mercedesbenz.sechub.domain.scan.asset.AssetFile.*;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import com.mercedesbenz.sechub.domain.scan.asset.AssetFile.AssetFileCompositeKey;
+
+public interface AssetFileRepository extends JpaRepository {
+
+ @Query(value = "SELECT DISTINCT " + COLUMN_ASSET_ID + " FROM " + TABLE_NAME, nativeQuery = true)
+ List fetchAllAssetIds();
+
+ @Query(value = "SELECT * from " + TABLE_NAME + " WHERE " + COLUMN_ASSET_ID + "=:assetId", nativeQuery = true)
+ List fetchAllAssetFilesWithAssetId(@Param("assetId") String assetId);
+
+ @Modifying
+ @Query(value = "DELETE from " + TABLE_NAME + " WHERE " + COLUMN_ASSET_ID + "=:assetId", nativeQuery = true)
+ void deleteAssetFilesHavingAssetId(@Param("assetId") String assetId);
+
+}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetRestController.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetRestController.java
new file mode 100644
index 0000000000..c4f0b1f83d
--- /dev/null
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetRestController.java
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.scan.asset;
+
+import static com.mercedesbenz.sechub.commons.core.CommonConstants.*;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.mercedesbenz.sechub.sharedkernel.Profiles;
+import com.mercedesbenz.sechub.sharedkernel.Step;
+import com.mercedesbenz.sechub.sharedkernel.logging.AuditLogService;
+import com.mercedesbenz.sechub.sharedkernel.logging.LogSanitizer;
+import com.mercedesbenz.sechub.sharedkernel.security.APIConstants;
+import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminDeletesAssetCompletely;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminDeletesOneFileFromAsset;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminDownloadsAssetFile;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminFetchesAssetDetails;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminFetchesAssetIds;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminUploadsAssetFile;
+
+import jakarta.annotation.security.RolesAllowed;
+import jakarta.servlet.http.HttpServletResponse;
+
+@RestController
+@EnableAutoConfiguration
+@RequestMapping(APIConstants.API_ADMINISTRATION)
+@RolesAllowed({ RoleConstants.ROLE_SUPERADMIN })
+@Profile(Profiles.ADMIN_ACCESS)
+public class AssetRestController {
+
+ @Autowired
+ AssetService assetService;
+
+ @Autowired
+ AuditLogService auditLogService;
+
+ @Autowired
+ LogSanitizer logSanitizer;
+
+ /* @formatter:off */
+ @UseCaseAdminUploadsAssetFile(@Step(number = 1, next = 2, name = "REST API call to upload a file for an asset", needsRestDoc = true))
+ @RequestMapping(path = "/asset/{assetId}/file", method = RequestMethod.POST)
+ @ResponseStatus(HttpStatus.OK)
+ /* @formatter:on */
+ public void uploadAssetFile(@PathVariable("assetId") String assetId, @RequestPart(MULTIPART_FILE) MultipartFile file,
+ @RequestParam(MULTIPART_CHECKSUM) String checkSum) {
+
+ auditLogService.log("starts upload of file:{} for asset: {}", logSanitizer.sanitize(file.getOriginalFilename(), 100),
+ logSanitizer.sanitize(assetId, 40));
+
+ assetService.uploadAssetFile(assetId, file, checkSum);
+
+ }
+
+ /* @formatter:off */
+ @UseCaseAdminDownloadsAssetFile(@Step(number = 1, next = 2, name = "REST API call to download a file for an asset", needsRestDoc = true))
+ @RequestMapping(path = "/asset/{assetId}/file/{fileName}", method = RequestMethod.GET, produces = {MediaType.APPLICATION_OCTET_STREAM_VALUE, MediaType.ALL_VALUE})
+ @ResponseStatus(HttpStatus.OK)
+ /* @formatter:on */
+ public void downloadAssetFile(@PathVariable("assetId") String assetId, @PathVariable("fileName") String fileName, HttpServletResponse response)
+ throws IOException {
+
+ auditLogService.log("starts download of file:{} for asset: {}", logSanitizer.sanitize(fileName, 100), logSanitizer.sanitize(assetId, 40));
+
+ assetService.downloadAssetFile(assetId, fileName, response.getOutputStream());
+
+ }
+
+ /* @formatter:off */
+ @UseCaseAdminFetchesAssetIds(@Step(number = 1, next = 2, name = "REST API call to fetch all availbale asset ids", needsRestDoc = true))
+ @RequestMapping(path = "/asset/ids", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE})
+ @ResponseStatus(HttpStatus.OK)
+ /* @formatter:on */
+ public List fetchAllAssetIds() {
+ return assetService.fetchAllAssetIds();
+ }
+
+ /* @formatter:off */
+ @UseCaseAdminFetchesAssetDetails(@Step(number = 1, next = 2, name = "REST API call to fetch details about asset", needsRestDoc = true))
+ @RequestMapping(path = "/asset/{assetId}/details", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE})
+ @ResponseStatus(HttpStatus.OK)
+ /* @formatter:on */
+ public AssetDetailData fetchAssetDetails(@PathVariable("assetId") String assetId) {
+ return assetService.fetchAssetDetails(assetId);
+ }
+
+ /* @formatter:off */
+ @UseCaseAdminDeletesOneFileFromAsset(@Step(number = 1, next = 2, name = "REST API call to delete an asset file", needsRestDoc = true))
+ @RequestMapping(path = "/asset/{assetId}/file/{fileName}", method = RequestMethod.DELETE)
+ @ResponseStatus(HttpStatus.OK)
+ /* @formatter:on */
+ public void deleteAssetFile(@PathVariable("assetId") String assetId, @PathVariable("fileName") String fileName) throws IOException {
+ assetService.deleteAssetFile(assetId, fileName);
+ }
+
+ /* @formatter:off */
+ @UseCaseAdminDeletesAssetCompletely(@Step(number = 1, next = 2, name = "REST API call to delete complete asset", needsRestDoc = true))
+ @RequestMapping(path = "/asset/{assetId}", method = RequestMethod.DELETE)
+ @ResponseStatus(HttpStatus.OK)
+ /* @formatter:on */
+ public void deleteAsset(@PathVariable("assetId") String assetId) throws IOException {
+ assetService.deleteAsset(assetId);
+ }
+}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetService.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetService.java
new file mode 100644
index 0000000000..28965552cb
--- /dev/null
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/asset/AssetService.java
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.scan.asset;
+
+import static com.mercedesbenz.sechub.commons.core.CommonConstants.*;
+import static com.mercedesbenz.sechub.sharedkernel.util.Assert.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Optional;
+import java.util.Scanner;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.amazonaws.util.StringInputStream;
+import com.mercedesbenz.sechub.commons.core.ConfigurationFailureException;
+import com.mercedesbenz.sechub.commons.core.security.CheckSumSupport;
+import com.mercedesbenz.sechub.commons.model.SecHubRuntimeException;
+import com.mercedesbenz.sechub.domain.scan.asset.AssetFile.AssetFileCompositeKey;
+import com.mercedesbenz.sechub.sharedkernel.Step;
+import com.mercedesbenz.sechub.sharedkernel.error.BadRequestException;
+import com.mercedesbenz.sechub.sharedkernel.error.NotAcceptableException;
+import com.mercedesbenz.sechub.sharedkernel.error.NotFoundException;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminDeletesAssetCompletely;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminDeletesOneFileFromAsset;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminDownloadsAssetFile;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminFetchesAssetDetails;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminFetchesAssetIds;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminUploadsAssetFile;
+import com.mercedesbenz.sechub.sharedkernel.validation.UserInputAssertion;
+import com.mercedesbenz.sechub.storage.core.AssetStorage;
+import com.mercedesbenz.sechub.storage.core.StorageService;
+
+import jakarta.servlet.ServletOutputStream;
+
+@Service
+public class AssetService {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AssetService.class);
+
+ private final AssetFileRepository repository;
+
+ private final UserInputAssertion inputAssertion;
+
+ private final CheckSumSupport checkSumSupport;
+
+ private final StorageService storageService;
+
+ AssetService(AssetFileRepository repository, UserInputAssertion inputAssertion, CheckSumSupport checkSumSupport, StorageService storageService) {
+ this.repository = repository;
+ this.inputAssertion = inputAssertion;
+ this.checkSumSupport = checkSumSupport;
+ this.storageService = storageService;
+ }
+
+ @UseCaseAdminDeletesAssetCompletely(@Step(number = 2, name = "Services deletes all asset parts"))
+ @Transactional
+ public void deleteAsset(String assetId) throws IOException {
+ inputAssertion.assertIsValidAssetId(assetId);
+
+ repository.deleteAssetFilesHavingAssetId(assetId);
+ storageService.createAssetStorage(assetId).deleteAll();
+ }
+
+ @UseCaseAdminDeletesOneFileFromAsset(@Step(number = 2, name = "Services deletes file from asset"))
+ public void deleteAssetFile(String assetId, String fileName) throws IOException {
+ inputAssertion.assertIsValidAssetId(assetId);
+ inputAssertion.assertIsValidAssetFileName(fileName);
+
+ repository.deleteById(AssetFileCompositeKey.builder().assetId(assetId).fileName(fileName).build());
+ storageService.createAssetStorage(assetId).delete(fileName);
+ }
+
+ @UseCaseAdminDownloadsAssetFile(@Step(number = 2, name = "Service downloads asset file from database"))
+ public void downloadAssetFile(String assetId, String fileName, ServletOutputStream outputStream) throws IOException {
+ inputAssertion.assertIsValidAssetId(assetId);
+ inputAssertion.assertIsValidAssetFileName(fileName);
+
+ notNull(outputStream, "output stream may not be null!");
+
+ AssetFile assetFile = assertAssetFileFromDatabase(assetId, fileName);
+ outputStream.write(assetFile.getData());
+
+ }
+
+ /**
+ * Ensures file for asset exists in database and and also in storage (with same
+ * checksum). If the file is not inside database a {@link NotFoundException}
+ * will be thrown. If the file is not available in storage, or the checksum in
+ * storage is different than the checksum from database, the file will stored
+ * again in storage (with data from database)
+ *
+ * @param fileName file name
+ * @param assetId asset identifier
+ * @throws ConfigurationFailureException if there are configuration problems
+ * @throws NotFoundException when the asset or the file is not found
+ * in database
+ *
+ */
+ public void ensureAssetFileInStorageAvailableAndHasSameChecksumAsInDatabase(String fileName, String assetId) throws ConfigurationFailureException {
+
+ try (AssetStorage assetStorage = storageService.createAssetStorage(assetId)) {
+ AssetFile assetFile = assertAssetFileFromDatabase(assetId, fileName);
+ String checksumFromDatabase = assetFile.getChecksum();
+
+ if (assetStorage.isExisting(fileName)) {
+ String checksumFileName = fileName + DOT_CHECKSUM;
+ if (assetStorage.isExisting(checksumFileName)) {
+
+ String checksumFromStorage = null;
+ try (InputStream inputStream = assetStorage.fetch(checksumFileName); Scanner scanner = new Scanner(inputStream)) {
+ checksumFromStorage = scanner.hasNext() ? scanner.next() : "";
+ }
+ if (checksumFromStorage.equals(checksumFromDatabase)) {
+ LOG.debug("Checksum for file '{}' in asset '{}' is '{}' in database and storage. Can be kept, no recration necessary", fileName,
+ assetId, checksumFromStorage, checksumFromDatabase);
+ return;
+ }
+ LOG.warn(
+ "Checksum for file '{}' in asset '{}' was '{}' instead of expected value from database '{}'. Will recreated file and checksum in storage.",
+ fileName, assetId, checksumFromStorage, checksumFromDatabase);
+ } else {
+ LOG.warn("Asset storage for file '{}' in asset '{}' did exist, but checksum did not exist. Will recreated file and checksum in storage.",
+ fileName, assetId);
+ }
+ } else {
+ LOG.info("Asset storage for file '{}' in asset '{}' does not exist and must be created.", fileName, assetId);
+ }
+ storeStream(fileName, checksumFromDatabase, assetStorage, assetFile.getData().length, new ByteArrayInputStream(assetFile.getData()));
+
+ } catch (NotFoundException | IOException e) {
+ throw new ConfigurationFailureException("Was not able to ensure file " + fileName + " in asset " + assetId, e);
+ }
+
+ }
+
+ @UseCaseAdminFetchesAssetIds(@Step(number = 2, name = "Service fetches all asset ids from database"))
+ public List fetchAllAssetIds() {
+ return repository.fetchAllAssetIds();
+ }
+
+ /**
+ * Fetches asset details (from database)
+ *
+ * @param assetId asset identifier
+ * @return detail data
+ * @throws NotFoundException when no asset exists for given identifier
+ */
+ @UseCaseAdminFetchesAssetDetails(@Step(number = 2, name = "Service fetches asset details for given asset id"))
+ public AssetDetailData fetchAssetDetails(String assetId) {
+ inputAssertion.assertIsValidAssetId(assetId);
+
+ List assetFiles = repository.fetchAllAssetFilesWithAssetId(assetId);
+ if (assetFiles.isEmpty()) {
+ throw new NotFoundException("No asset data available for asset id:" + assetId);
+ }
+
+ AssetDetailData data = new AssetDetailData();
+ data.setAssetId(assetId);
+ for (AssetFile assetFile : assetFiles) {
+ AssetFileData information = new AssetFileData();
+ information.setFileName(assetFile.getKey().getFileName());
+ information.setChecksum(assetFile.getChecksum());
+ data.getFiles().add(information);
+ }
+
+ return data;
+ }
+
+ @UseCaseAdminUploadsAssetFile(@Step(number = 2, name = "Service tries to upload file for asset", description = "Uploaded file will be stored in database and in storage"))
+ public void uploadAssetFile(String assetId, MultipartFile multipartFile, String checkSum) {
+ inputAssertion.assertIsValidAssetId(assetId);
+
+ inputAssertion.assertIsValidSha256Checksum(checkSum);
+
+ String fileName = assertAssetFile(multipartFile);
+
+ handleChecksumValidation(fileName, multipartFile, checkSum, assetId);
+
+ try {
+ /* now store */
+ byte[] bytes = multipartFile.getBytes();
+ persistFileAndChecksumInDatabase(fileName, bytes, checkSum, assetId);
+
+ ensureAssetFileInStorageAvailableAndHasSameChecksumAsInDatabase(fileName, assetId);
+
+ LOG.info("Successfully uploaded file '{}' for asset '{}'", fileName, assetId);
+
+ } catch (IOException e) {
+ throw new SecHubRuntimeException("Was not able to upload file '" + fileName + "' for asset '" + assetId + "'", e);
+ } catch (ConfigurationFailureException e) {
+ throw new IllegalStateException("A configuration failure should not happen at this point!", e);
+ }
+ }
+
+ private String assertAssetFile(MultipartFile file) {
+ notNull(file, "file may not be null!");
+ String fileName = file.getOriginalFilename();
+
+ inputAssertion.assertIsValidAssetFileName(fileName);
+
+ long fileSize = file.getSize();
+
+ if (fileSize <= 0) {
+ throw new BadRequestException("Uploaded asset file may not be empty!");
+ }
+ return fileName;
+ }
+
+ private AssetFile assertAssetFileFromDatabase(String assetId, String fileName) {
+ AssetFileCompositeKey key = AssetFileCompositeKey.builder().assetId(assetId).fileName(fileName).build();
+ Optional result = repository.findById(key);
+ if (result.isEmpty()) {
+ throw new NotFoundException("For asset:" + assetId + " no file with name:" + fileName + " exists!");
+ }
+ AssetFile assetFile = result.get();
+ return assetFile;
+ }
+
+ private void assertCheckSumCorrect(String checkSum, InputStream inputStream) {
+ if (!checkSumSupport.hasCorrectSha256Checksum(checkSum, inputStream)) {
+ LOG.error("Uploaded file has incorrect sha256 checksum! Something must have happened during the upload.");
+ throw new NotAcceptableException("Sourcecode checksum check failed");
+ }
+ }
+
+ private String createFileNameForChecksum(String fileName) {
+ return fileName + DOT_CHECKSUM;
+ }
+
+ private void handleChecksumValidation(String fileName, MultipartFile file, String checkSum, String assetid) {
+ try (InputStream inputStream = file.getInputStream()) {
+ /* validate */
+ assertCheckSumCorrect(checkSum, inputStream);
+
+ } catch (IOException e) {
+ LOG.error("Was not able to validate uploaded file checksum for file '{}' in asset '{}'", fileName, assetid, e);
+ throw new SecHubRuntimeException("Was not able to validate uploaded asset checksum");
+ }
+ }
+
+ private void persistFileAndChecksumInDatabase(String fileName, byte[] bytes, String checkSum, String assetId) throws IOException {
+ /* delete if exists */
+ AssetFileCompositeKey key = AssetFileCompositeKey.builder().assetId(assetId).fileName(fileName).build();
+ repository.deleteById(key);
+
+ AssetFile assetFile = new AssetFile(key);
+ assetFile.setChecksum(checkSum);
+ assetFile.setData(bytes);
+
+ repository.save(assetFile);
+ }
+
+ private void storeStream(String fileName, String checkSum, AssetStorage assetStorage, long fileSize, InputStream inputStream) throws IOException {
+ assetStorage.store(fileName, inputStream, fileSize);
+
+ long checksumSizeInBytes = checkSum.getBytes().length;
+ assetStorage.store(createFileNameForChecksum(fileName), new StringInputStream(checkSum), checksumSizeInBytes);
+ }
+
+}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/product/ProductExecutorContextFactory.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/product/ProductExecutorContextFactory.java
index 2ff0b373ad..76e0f72049 100644
--- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/product/ProductExecutorContextFactory.java
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/product/ProductExecutorContextFactory.java
@@ -15,12 +15,12 @@ public class ProductExecutorContextFactory {
@Autowired
ProductResultTransactionService transactionService;
- public ProductExecutorContext create(List formerResults, SecHubExecutionContext executionContext, ProductExecutor productExecutor,
+ public ProductExecutorContext create(List formerResults, SecHubExecutionContext sechubExecutionContext, ProductExecutor productExecutor,
ProductExecutorConfig config) {
ProductExecutorContext productExecutorContext = new ProductExecutorContext(config, formerResults);
- ProductExecutorCallbackImpl callback = new ProductExecutorCallbackImpl(executionContext, productExecutorContext, transactionService);
+ ProductExecutorCallbackImpl callback = new ProductExecutorCallbackImpl(sechubExecutionContext, productExecutorContext, transactionService);
productExecutorContext.callback = callback;
productExecutorContext.useFirstFormerResult();
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/ScanProjectConfig.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/ScanProjectConfig.java
index b27366dc5f..ea46fe873e 100644
--- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/ScanProjectConfig.java
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/ScanProjectConfig.java
@@ -40,6 +40,11 @@ public class ScanProjectConfig {
/* +-----------------------------------------------------------------------+ */
public static final String CLASS_NAME = ScanProjectConfig.class.getSimpleName();
+ public static final String PROPERTY_KEY = "key";
+
+ public static final String QUERY_FIND_ALL_CONFIGURATIONS_FOR_PROJECT = "SELECT c FROM ScanProjectConfig c where c." + PROPERTY_KEY + "."
+ + ScanProjectConfigCompositeKey.PROPERTY_PROJECT_ID + " =:projectId";
+
@EmbeddedId
ScanProjectConfigCompositeKey key;
@@ -98,6 +103,8 @@ public static class ScanProjectConfigCompositeKey implements Serializable {
private static final long serialVersionUID = 8753389792382752253L;
+ public static final String PROPERTY_PROJECT_ID = "projectId";
+
@Column(name = COLUMN_PROJECT_ID, nullable = false)
private String projectId;
@@ -169,6 +176,12 @@ public boolean equals(Object obj) {
return false;
return true;
}
+
+ @Override
+ public String toString() {
+ return "ScanProjectConfigCompositeKey [" + (projectId != null ? "projectId=" + projectId + ", " : "")
+ + (configId != null ? "configId=" + configId : "") + "]";
+ }
}
@Override
@@ -197,4 +210,9 @@ public boolean equals(Object obj) {
return true;
}
+ @Override
+ public String toString() {
+ return "ScanProjectConfig [" + (key != null ? "key=" + key + ", " : "") + (data != null ? "data=" + data : "") + "]";
+ }
+
}
\ No newline at end of file
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/ScanProjectConfigID.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/ScanProjectConfigID.java
index 60a80ea7ac..e968f1bf20 100644
--- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/ScanProjectConfigID.java
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/ScanProjectConfigID.java
@@ -4,6 +4,7 @@
import static com.mercedesbenz.sechub.sharedkernel.util.Assert.*;
import com.mercedesbenz.sechub.commons.core.MustBeKeptStable;
+import com.mercedesbenz.sechub.commons.model.template.TemplateType;
@MustBeKeptStable("You can rename enums, but do not change id parts, because used inside DB!")
public enum ScanProjectConfigID {
@@ -26,13 +27,15 @@ public enum ScanProjectConfigID {
*/
PROJECT_ACCESS_LEVEL("project_access_level"),
+ TEMPLATE_WEBSCAN_LOGIN("template_" + TemplateType.WEBSCAN_LOGIN.name().toLowerCase()),
+
;
private String id;
ScanProjectConfigID(String id) {
notNull(id, "config id may not be null!");
- maxLength(id, 20); // because in DB we got only 3x20 defined, so max is 20
+ maxLength(id, 60); // in DB we got 3x20 defined, but we have only ascii chars allowed, so 60 is max
this.id = id;
}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/ScanProjectConfigRepository.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/ScanProjectConfigRepository.java
index 526bfd31b5..2b1deb1f57 100644
--- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/ScanProjectConfigRepository.java
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/ScanProjectConfigRepository.java
@@ -3,9 +3,13 @@
import static com.mercedesbenz.sechub.domain.scan.project.ScanProjectConfig.*;
+import java.util.List;
+import java.util.Set;
+
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
import com.mercedesbenz.sechub.domain.scan.project.ScanProjectConfig.ScanProjectConfigCompositeKey;
@@ -15,4 +19,11 @@ public interface ScanProjectConfigRepository extends JpaRepository configIds, String value);
+
+ @Query(value = ScanProjectConfig.QUERY_FIND_ALL_CONFIGURATIONS_FOR_PROJECT)
+ List findAllForProject(@Param("projectId") String projectId);
+
}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/ScanProjectConfigService.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/ScanProjectConfigService.java
index c15d69cb5d..4a785785f2 100644
--- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/ScanProjectConfigService.java
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/ScanProjectConfigService.java
@@ -2,9 +2,11 @@
package com.mercedesbenz.sechub.domain.scan.project;
import java.util.Optional;
+import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
import com.mercedesbenz.sechub.domain.scan.ScanAssertService;
import com.mercedesbenz.sechub.domain.scan.project.ScanProjectConfig.ScanProjectConfigCompositeKey;
@@ -74,10 +76,21 @@ public ScanProjectConfig get(String projectId, ScanProjectConfigID configId, boo
}
/**
- * Set configuration for project (means will persist given change to config)
+ * Removes project configuration entry
*
- * @param projectId
- * @param configId
+ * @param projectId project identifier
+ * @param configId configuration identifier
+ */
+ public void unset(String projectId, ScanProjectConfigID configId) {
+ set(projectId, configId, null);
+ }
+
+ /**
+ * Set configuration for project (means will persist given change to
+ * configuration)
+ *
+ * @param projectId project identifier
+ * @param configId configuration identifier
* @param data when null
existing entry will be deleted on
* database
*/
@@ -107,4 +120,9 @@ public void set(String projectId, ScanProjectConfigID configId, String data) {
}
+ @Transactional
+ public void deleteAllConfigurationsOfGivenConfigIdsAndValue(Set configIds, String value) {
+ repository.deleteAllConfigurationsOfGivenConfigIdsAndValue(configIds, value);
+ }
+
}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/Template.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/Template.java
new file mode 100644
index 0000000000..c885e3c20a
--- /dev/null
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/Template.java
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.scan.template;
+
+import java.util.Objects;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import jakarta.persistence.Version;
+
+/**
+ * Represents a template
+ *
+ * @author Albert Tregnaghi
+ *
+ */
+@Entity
+@Table(name = Template.TABLE_NAME)
+public class Template {
+
+ /* +-----------------------------------------------------------------------+ */
+ /* +............................ SQL ......................................+ */
+ /* +-----------------------------------------------------------------------+ */
+ public static final String TABLE_NAME = "SCAN_TEMPLATE";
+
+ public static final String COLUMN_TEMPLATE_ID = "TEMPLATE_ID";
+ public static final String COLUMN_DEFINITION = "TEMPLATE_DEFINITION";
+
+ /* +-----------------------------------------------------------------------+ */
+ /* +............................ JPQL .....................................+ */
+ /* +-----------------------------------------------------------------------+ */
+ public static final String CLASS_NAME = "Template";
+
+ public static final String PROPERTY_ID = "id";
+ public static final String PROPERTY_DEFINITION = "definition";
+
+ public static final String QUERY_All_TEMPLATE_IDS = "select t.id from #{#entityName} t";
+
+ @Id
+ @Column(name = COLUMN_TEMPLATE_ID, updatable = false, nullable = false)
+ String id;
+
+ @Column(name = COLUMN_DEFINITION)
+ String definition;
+
+ @Version
+ @Column(name = "VERSION")
+ @JsonIgnore
+ Integer version;
+
+ Template() {
+ // jpa only
+ }
+
+ public Template(String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setDefinition(String definition) {
+ this.definition = definition;
+ }
+
+ public String getDefinition() {
+ return definition;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Template other = (Template) obj;
+ return Objects.equals(id, other.id);
+ }
+
+ @Override
+ public String toString() {
+ return "Template [" + (id != null ? "id=" + id + ", " : "") + (definition != null ? "definition=" + definition : "") + "]";
+ }
+
+}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/TemplateProjectAssignment.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/TemplateProjectAssignment.java
new file mode 100644
index 0000000000..32f5551374
--- /dev/null
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/TemplateProjectAssignment.java
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.scan.template;
+
+import java.util.Objects;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import jakarta.persistence.Version;
+
+/**
+ * Represents a template
+ *
+ * @author Albert Tregnaghi
+ *
+ */
+@Entity
+@Table(name = TemplateProjectAssignment.TABLE_NAME)
+public class TemplateProjectAssignment {
+
+ /* +-----------------------------------------------------------------------+ */
+ /* +............................ SQL ......................................+ */
+ /* +-----------------------------------------------------------------------+ */
+ public static final String TABLE_NAME = "SCAN_TEMPLATE";
+
+ public static final String COLUMN_TEMPLATE_ID = "TEMPLATE_ID";
+ public static final String COLUMN_DEFINITION = "TEMPLATE_DEFINITION";
+
+ /* +-----------------------------------------------------------------------+ */
+ /* +............................ JPQL .....................................+ */
+ /* +-----------------------------------------------------------------------+ */
+ public static final String CLASS_NAME = "Template";
+
+ public static final String PROPERTY_ID = "id";
+ public static final String PROPERTY_DEFINITION = "definition";
+
+ public static final String QUERY_All_TEMPLATE_IDS = "select t.id from #{#entityName} t";
+
+ @Id
+ @Column(name = COLUMN_TEMPLATE_ID, updatable = false, nullable = false)
+ String id;
+
+ @Column(name = COLUMN_DEFINITION)
+ String definition;
+
+ @Version
+ @Column(name = "VERSION")
+ @JsonIgnore
+ Integer version;
+
+ TemplateProjectAssignment() {
+ // jpa only
+ }
+
+ public TemplateProjectAssignment(String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setDefinition(String definition) {
+ this.definition = definition;
+ }
+
+ public String getDefinition() {
+ return definition;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ TemplateProjectAssignment other = (TemplateProjectAssignment) obj;
+ return Objects.equals(id, other.id);
+ }
+
+ @Override
+ public String toString() {
+ return "Template [" + (id != null ? "id=" + id + ", " : "") + (definition != null ? "definition=" + definition : "") + "]";
+ }
+
+}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/TemplateRepository.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/TemplateRepository.java
new file mode 100644
index 0000000000..3f5ceeaf88
--- /dev/null
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/TemplateRepository.java
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.scan.template;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+
+public interface TemplateRepository extends JpaRepository {
+
+ @Query(Template.QUERY_All_TEMPLATE_IDS)
+ List findAllTemplateIds();
+}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/TemplateRestController.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/TemplateRestController.java
new file mode 100644
index 0000000000..573bf7defd
--- /dev/null
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/TemplateRestController.java
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.scan.template;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition;
+import com.mercedesbenz.sechub.sharedkernel.Profiles;
+import com.mercedesbenz.sechub.sharedkernel.Step;
+import com.mercedesbenz.sechub.sharedkernel.logging.AuditLogService;
+import com.mercedesbenz.sechub.sharedkernel.logging.LogSanitizer;
+import com.mercedesbenz.sechub.sharedkernel.security.APIConstants;
+import com.mercedesbenz.sechub.sharedkernel.security.RoleConstants;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminCreatesOrUpdatesTemplate;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminDeletesTemplate;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminFetchesAllTemplateIds;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminFetchesTemplate;
+
+import jakarta.annotation.security.RolesAllowed;
+
+@RestController
+@EnableAutoConfiguration
+@RequestMapping(APIConstants.API_ADMINISTRATION)
+@RolesAllowed({ RoleConstants.ROLE_SUPERADMIN })
+@Profile(Profiles.ADMIN_ACCESS)
+public class TemplateRestController {
+
+ @Autowired
+ TemplateService templateService;
+
+ @Autowired
+ AuditLogService auditLogService;
+
+ @Autowired
+ LogSanitizer logSanitizer;
+
+ @UseCaseAdminCreatesOrUpdatesTemplate(@Step(number = 1, next = 2, name = "REST API call to create or update template", needsRestDoc = true))
+ @RequestMapping(path = "/template/{templateId}", method = RequestMethod.PUT)
+ @ResponseStatus(HttpStatus.OK)
+ public void createOrUpdate(@RequestBody TemplateDefinition templateDefinition, @PathVariable("templateId") String templateId) {
+
+ auditLogService.log("starts create/update of template: {}", logSanitizer.sanitize(templateId, -1));
+
+ templateService.createOrUpdateTemplate(templateId, templateDefinition);
+
+ }
+
+ @UseCaseAdminDeletesTemplate(@Step(number = 1, next = 2, name = "REST API call to delete a template", needsRestDoc = true))
+ @RequestMapping(path = "/template/{templateId}", method = RequestMethod.DELETE)
+ @ResponseStatus(HttpStatus.OK)
+ public void delete(@PathVariable("templateId") String templateId) {
+
+ auditLogService.log("starts delete of template: {}", logSanitizer.sanitize(templateId, -1));
+
+ templateService.deleteTemplate(templateId);
+
+ }
+
+ @UseCaseAdminFetchesTemplate(@Step(number = 1, next = 2, name = "REST API call to fetch template", needsRestDoc = true))
+ @RequestMapping(path = "/template/{templateId}", method = RequestMethod.GET)
+ @ResponseStatus(HttpStatus.OK)
+ public TemplateDefinition fetchTemplate(@PathVariable("templateId") String templateId) {
+
+ auditLogService.log("fetches template definition for template: {}", logSanitizer.sanitize(templateId, -1));
+
+ return templateService.fetchTemplateDefinition(templateId);
+
+ }
+
+ @UseCaseAdminFetchesAllTemplateIds(@Step(number = 1, next = 2, name = "REST API call to fetch template list", needsRestDoc = true))
+ @RequestMapping(path = "/templates", method = RequestMethod.GET)
+ @ResponseStatus(HttpStatus.OK)
+ public List fetchAllTemplateIds() {
+ return templateService.fetchAllTemplateIds();
+
+ }
+}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/TemplateService.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/TemplateService.java
new file mode 100644
index 0000000000..ad66b38d37
--- /dev/null
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/TemplateService.java
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.scan.template;
+
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import com.mercedesbenz.sechub.commons.model.template.TemplateDefinition;
+import com.mercedesbenz.sechub.commons.model.template.TemplateType;
+import com.mercedesbenz.sechub.domain.scan.project.ScanProjectConfig;
+import com.mercedesbenz.sechub.domain.scan.project.ScanProjectConfigID;
+import com.mercedesbenz.sechub.domain.scan.project.ScanProjectConfigService;
+import com.mercedesbenz.sechub.sharedkernel.Step;
+import com.mercedesbenz.sechub.sharedkernel.error.NotFoundException;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminAssignsTemplateToProject;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminCreatesOrUpdatesTemplate;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminDeletesTemplate;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminFetchesAllTemplateIds;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminFetchesTemplate;
+import com.mercedesbenz.sechub.sharedkernel.usecases.admin.config.UseCaseAdminUnassignsTemplateFromProject;
+import com.mercedesbenz.sechub.sharedkernel.validation.UserInputAssertion;
+
+@Service
+public class TemplateService {
+
+ private static final Logger LOG = LoggerFactory.getLogger(TemplateService.class);
+
+ private final TemplateRepository repository;
+
+ private final ScanProjectConfigService configService;
+
+ private final TemplateTypeScanConfigIdResolver resolver;
+
+ private final UserInputAssertion inputAssertion;
+
+ TemplateService(TemplateRepository repository, ScanProjectConfigService configService, UserInputAssertion inputAssertion,
+ TemplateTypeScanConfigIdResolver resolver) {
+ this.repository = repository;
+ this.configService = configService;
+ this.resolver = resolver;
+ this.inputAssertion = inputAssertion;
+ }
+
+ @UseCaseAdminCreatesOrUpdatesTemplate(@Step(number = 2, name = "Service creates or updates template"))
+ public void createOrUpdateTemplate(String templateId, TemplateDefinition newTemplateDefinition) {
+ if (templateId == null) {
+ throw new IllegalArgumentException("Template id may not be null!");
+ }
+ if (newTemplateDefinition == null) {
+ throw new IllegalArgumentException("Template definition may not be null!");
+ }
+
+ // first of all we always set template id, so we have never a clash here
+ // even when somebody copied an existing template definition.
+ newTemplateDefinition.setId(templateId);
+
+ Optional templateOpt = repository.findById(templateId);
+ Template template = null;
+
+ if (templateOpt.isEmpty()) {
+ // we did not find an existing template, so we create one
+ template = new Template(templateId);
+ } else {
+ template = templateOpt.get();
+
+ TemplateDefinition existingDefinition = TemplateDefinition.from(template.getDefinition());
+ TemplateType originType = existingDefinition.getType();
+
+ if (!Objects.equals(originType, newTemplateDefinition.getType())) {
+ // we reuse the existing type - so the type keeps immutable
+ // which is important because it may not change when we
+ // have templates assigned to projects (we only allow one template per type
+ // for a project!)
+ LOG.warn("Update will not set type '{}' to template '{}' because type is immutable. Will keep origin type '{}'",
+ newTemplateDefinition.getType(), templateId, originType);
+
+ newTemplateDefinition.setType(originType);
+ }
+ }
+ template.setDefinition(newTemplateDefinition.toFormattedJSON());
+
+ repository.save(template);
+
+ LOG.info("Template {} has been updated/created", templateId);
+ }
+
+ @UseCaseAdminDeletesTemplate(@Step(number = 2, name = "Service removes all assignments and deletes template completely"))
+ public void deleteTemplate(String templateId) {
+ if (templateId == null) {
+ throw new IllegalArgumentException("Template id may not be null!");
+ }
+ Set allTemplateConfigIds = resolver.resolveAllPossibleConfigIds();
+ configService.deleteAllConfigurationsOfGivenConfigIdsAndValue(allTemplateConfigIds, templateId);
+
+ repository.deleteById(templateId);
+ }
+
+ /**
+ * Fetches template definition by given template id
+ *
+ * @param templateId template identifier
+ * @return definition, never null
+ *
+ * @throws NotFoundException if no template exists for given identifier
+ */
+ @UseCaseAdminFetchesTemplate(@Step(number = 2, name = "Service fetches template"))
+ public TemplateDefinition fetchTemplateDefinition(String templateId) {
+ if (templateId == null) {
+ throw new IllegalArgumentException("Template id may not be null!");
+ }
+ Optional templateOpt = repository.findById(templateId);
+ if (templateOpt.isEmpty()) {
+ throw new NotFoundException("Template does not exist!");
+ }
+ String json = templateOpt.get().getDefinition();
+
+ return TemplateDefinition.from(json);
+ }
+
+ @UseCaseAdminFetchesAllTemplateIds(@Step(number = 2, name = "Services fetches all template ids"))
+ public List fetchAllTemplateIds() {
+ return repository.findAllTemplateIds();
+ }
+
+ @UseCaseAdminAssignsTemplateToProject(@Step(number = 3, name = "service assigns project to template in scan domain"))
+ public void assignTemplateToProject(String templateId, String projectId) {
+ inputAssertion.assertIsValidTemplateId(templateId);
+ inputAssertion.assertIsValidProjectId(projectId);
+
+ LOG.debug("try to assign template '{}' to project '{}'", templateId, projectId);
+
+ TemplateDefinition template = fetchTemplateDefinition(templateId);
+ ScanProjectConfigID key = resolver.resolve(template.getType());
+
+ configService.set(projectId, key, templateId);
+
+ LOG.info("assigned template '{}' to project '{}'", templateId, projectId);
+ }
+
+ @UseCaseAdminUnassignsTemplateFromProject(@Step(number = 3, name = "service unassigns project from template in scan domain"))
+ public void unassignTemplateFromProject(String templateId, String projectId) {
+ inputAssertion.assertIsValidTemplateId(templateId);
+ inputAssertion.assertIsValidProjectId(projectId);
+
+ LOG.debug("try to unassign template '{}' from project '{}'", templateId, projectId);
+
+ TemplateDefinition template = fetchTemplateDefinition(templateId);
+ ScanProjectConfigID key = resolver.resolve(template.getType());
+
+ ScanProjectConfig config = configService.get(projectId, key);
+ String value = config.getData();
+
+ if (!templateId.equals(value)) {
+ LOG.warn("Cannot unassign template {} from project {} , because for '{}' the (other) template '{}' is set!", templateId, projectId, key.getId(),
+ value);
+ return;
+ }
+
+ configService.unset(projectId, key);
+
+ LOG.info("unassigned template '{}' from project '{}'", templateId, projectId);
+ }
+
+ /**
+ * Fetches all template identifiers for given project
+ *
+ * @param projectId project identifier
+ * @return set with identifiers, never null
+ */
+ public Set fetchAssignedTemplateIdsForProject(String projectId) {
+ Set result = new LinkedHashSet<>();
+ for (TemplateType type : TemplateType.values()) {
+ ScanProjectConfigID configId = resolver.resolve(type);
+ ScanProjectConfig config = configService.get(projectId, configId, false);
+ if (config == null) {
+ continue;
+ }
+ String templateId = config.getData();
+ result.add(templateId);
+ }
+ return result;
+ }
+
+ /**
+ * Fetches all template definitions for given project
+ *
+ * @param projectId project identifier
+ * @return list of template definitions, never null
+ */
+ public List fetchAllTemplateDefinitionsForProject(String projectId) {
+ List result = new ArrayList<>();
+ Set templateIds = fetchAssignedTemplateIdsForProject(projectId);
+ for (String templateId : templateIds) {
+ TemplateDefinition templateDefinition = fetchTemplateDefinition(templateId);
+ result.add(templateDefinition);
+ }
+ return result;
+ }
+
+}
diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/TemplateTypeScanConfigIdResolver.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/TemplateTypeScanConfigIdResolver.java
new file mode 100644
index 0000000000..c19c765299
--- /dev/null
+++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/template/TemplateTypeScanConfigIdResolver.java
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.domain.scan.template;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.springframework.stereotype.Component;
+
+import com.mercedesbenz.sechub.commons.model.template.TemplateType;
+import com.mercedesbenz.sechub.domain.scan.project.ScanProjectConfigID;
+
+@Component
+public class TemplateTypeScanConfigIdResolver {
+
+ private static Map map = new LinkedHashMap<>();
+ private static final Set unmodifiableSet;
+
+ static {
+ register();
+
+ Set