- * To enable OAuth2 authentication in JWT mode, you must set the following
- * property {@code sechub.security.oauth2.jwt.enabled=true} in the application
- * properties. For opaque token mode, set
- * {@code sechub.security.oauth2.opaque-token.enabled=true}. Note: This
- * configuration requires exactly one of the two modes to be enabled.
- *
- *
- *
- * Subclasses must implement the {@link #isOAuth2Enabled()} method to indicate
- * whether OAuth2 is enabled and the {@link #authorizeHttpRequests()} method to
- * configure API access permissions.
- *
- *
* @see org.springframework.security.config.annotation.web.builders.HttpSecurity
* @see org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer
* @see org.springframework.security.oauth2.jwt.JwtDecoder
@@ -57,70 +58,114 @@
*
* @author hamidonos
*/
+@EnableConfigurationProperties(SecurityProperties.class)
public abstract class AbstractSecurityConfiguration {
- static final String ACCESS_TOKEN = "access_token";
- static final String LOGIN_PROPERTIES_PREFIX = "sechub.security.login";
+ static final String ACCESS_TOKEN = "access_token";
static final String SERVER_OAUTH2_PROPERTIES_PREFIX = "sechub.security.server.oauth2";;
static final String MODE = "mode";
+ private static final String SCOPE = "openid";
+ private static final String SUBJECT = "sub";
+ /* @formatter:off */
+ private static final Set DEFAULT_PUBLIC_PATHS = Set.of(
+ "/css/**",
+ "/js/**",
+ "/images/**",
+ "/login/oauth2/**",
+ "/oauth2/**",
+ "/favicon.ico",
+ "/sechub-logo.svg"
+ );
+ /* @formatter:on */
+
/* @formatter:off */
@Bean
- SecurityFilterChain filterChain(HttpSecurity httpSecurity,
- @Autowired(required = false) UserDetailsService userDetailsService,
- RestTemplate restTemplate,
- @Autowired(required = false) OAuth2JwtProperties OAuth2JwtProperties,
- @Autowired(required = false) OAuth2OpaqueTokenProperties oAuth2OpaqueTokenProperties,
- @Autowired(required = false) JwtDecoder jwtDecoder) throws Exception {
-
- httpSecurity.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ @Order(1)
+ SecurityFilterChain securityFilterChainResourceServer(HttpSecurity httpSecurity,
+ SecurityProperties securityProperties,
+ @Autowired(required = false) UserDetailsService userDetailsService,
+ RestTemplate restTemplate,
+ @Autowired(required = false) AES256Encryption aes256Encryption,
+ @Autowired(required = false) JwtDecoder jwtDecoder) throws Exception {
+ SecurityProperties.Login login = securityProperties.getLogin();
+
+ if (login != null && login.isEnabled()) {
+ Set publicPaths = new HashSet<>(DEFAULT_PUBLIC_PATHS);
+ publicPaths.add(login.getLoginPage());
+
+ RequestMatcher publicPathsMatcher = new OrRequestMatcher(
+ publicPaths.stream()
+ .map(AntPathRequestMatcher::new)
+ .toArray(AntPathRequestMatcher[]::new));
+
+ RequestMatcher protectedPathsMatcher = new NegatedRequestMatcher(publicPathsMatcher);
+
+ httpSecurity.securityMatcher(protectedPathsMatcher);
+ }
+
+ httpSecurity
+ .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authorizeHttpRequests())
.csrf(AbstractHttpConfigurer::disable) // CSRF protection disabled. The CookieServerCsrfTokenRepository does
// not work since Spring Boot 3
.httpBasic(Customizer.withDefaults()).headers((headers) -> headers
.contentSecurityPolicy((csp) -> csp.policyDirectives("default-src 'none'; style-src 'unsafe-inline'")));
- if (isOAuth2Enabled()) {
+ SecurityProperties.Server server = securityProperties.getServer();
+
+ if (server != null && server.isOAuth2ModeEnabled()) {
if (userDetailsService == null) {
throw new NoSuchBeanDefinitionException(UserDetailsService.class);
}
- if ((OAuth2JwtProperties == null && oAuth2OpaqueTokenProperties == null) || (OAuth2JwtProperties != null && oAuth2OpaqueTokenProperties != null)) {
- String exMsg = "Either JWT or opaque token mode must be enabled by setting the '%s.%s' property to either '%s' or '%s'".formatted(
+ SecurityProperties.Server.OAuth2 oAuth2 = server.getOAuth2();
+
+ if (oAuth2.isJwtModeEnabled() == oAuth2.isOpaqueTokenModeEnabled()) {
+ String exMsg = "Either 'jwt' or opaque token mode must be enabled by setting the '%s.%s' property to either '%s' or '%s'".formatted(
SERVER_OAUTH2_PROPERTIES_PREFIX,
MODE,
- OAuth2JwtPropertiesConfiguration.MODE,
- OAuth2OpaqueTokenPropertiesConfiguration.MODE
+ SecurityProperties.Server.OAuth2.OAUTH2_JWT_MODE,
+ SecurityProperties.Server.OAuth2.OAUTH2_OPAQUE_TOKEN_MODE
);
throw new BeanInstantiationException(SecurityFilterChain.class, exMsg);
}
- if (OAuth2JwtProperties != null) {
+ if (aes256Encryption == null) {
+ throw new NoSuchBeanDefinitionException(AES256Encryption.class);
+ }
+
+ BearerTokenResolver bearerTokenResolver = new DynamicBearerTokenResolver(aes256Encryption);
+
+ if (oAuth2.isJwtModeEnabled()) {
if (jwtDecoder == null) {
throw new NoSuchBeanDefinitionException(JwtDecoder.class);
}
- BearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver();
AuthenticationProvider authenticationProvider = new OAuth2JwtAuthenticationProvider(userDetailsService, jwtDecoder);
httpSecurity
.oauth2ResourceServer(oauth2ResourceServer -> oauth2ResourceServer
- .jwt(jwt -> jwt.decoder(jwtDecoder))
- .bearerTokenResolver(bearerTokenResolver))
+ .jwt(jwt -> jwt.decoder(jwtDecoder))
+ .bearerTokenResolver(bearerTokenResolver)
+ )
.authenticationProvider(authenticationProvider);
}
- if (oAuth2OpaqueTokenProperties != null) {
+ if (oAuth2.isOpaqueTokenModeEnabled()) {
+ SecurityProperties.Server.OAuth2.OpaqueToken opaqueToken = oAuth2.getOpaqueToken();
OpaqueTokenIntrospector opaqueTokenIntrospector = new OAuth2OpaqueTokenIntrospector(
restTemplate,
- oAuth2OpaqueTokenProperties.getIntrospectionUri(),
- oAuth2OpaqueTokenProperties.getClientId(),
- oAuth2OpaqueTokenProperties.getClientSecret(),
+ opaqueToken.getIntrospectionUri(),
+ opaqueToken.getClientId(),
+ opaqueToken.getClientSecret(),
userDetailsService);
httpSecurity
.oauth2ResourceServer(oauth2ResourceServer -> oauth2ResourceServer
- .opaqueToken(opaqueToken -> opaqueToken.introspector(opaqueTokenIntrospector)));
+ .opaqueToken(opaqueTokenConfigurer -> opaqueTokenConfigurer.introspector(opaqueTokenIntrospector))
+ .bearerTokenResolver(bearerTokenResolver)
+ );
}
}
/* @formatter:on */
@@ -128,7 +173,86 @@ SecurityFilterChain filterChain(HttpSecurity httpSecurity,
return httpSecurity.build();
}
- protected abstract boolean isOAuth2Enabled();
+ @Bean
+ @Conditional(LoginEnabledCondition.class)
+ ClientRegistrationRepository clientRegistrationRepository(SecurityProperties securityProperties) {
+ SecurityProperties.Login login = securityProperties.getLogin();
+ SecurityProperties.Login.OAuth2 oAuth2 = login.getOAuth2();
+
+ /* @formatter:off */
+ ClientRegistration clientRegistration = ClientRegistration
+ .withRegistrationId(oAuth2.getProvider())
+ .clientId(oAuth2.getClientId())
+ .clientSecret(oAuth2.getClientSecret())
+ .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
+ .redirectUri(oAuth2.getRedirectUri())
+ .issuerUri(oAuth2.getIssuerUri())
+ .scope(SCOPE)
+ .authorizationUri(oAuth2.getAuthorizationUri())
+ .tokenUri(oAuth2.getTokenUri())
+ .userInfoUri(oAuth2.getUserInfoUri())
+ .userNameAttributeName(SUBJECT)
+ .jwkSetUri(oAuth2.getJwkSetUri())
+ .build();
+ /* @formatter:on */
+
+ return new InMemoryClientRegistrationRepository(clientRegistration);
+ }
+
+ @Bean
+ @Conditional(LoginEnabledCondition.class)
+ @Order(2)
+ /* @formatter:off */
+ SecurityFilterChain securityFilterChainLogin(HttpSecurity httpSecurity,
+ @Autowired(required = false) SecurityProperties securityProperties,
+ @Autowired(required = false) AES256Encryption aes256Encryption,
+ @Autowired(required = false) OAuth2AuthorizedClientService oAuth2AuthorizedClientService) throws Exception {
+ SecurityProperties.Login login = securityProperties.getLogin();
+
+ Set publicPaths = new HashSet<>(DEFAULT_PUBLIC_PATHS);
+ publicPaths.add(login.getLoginPage());
+
+ httpSecurity.securityMatcher(publicPaths.toArray(new String[0]))
+ /* Disable CSRF */
+ .csrf(AbstractHttpConfigurer::disable)
+ /* Make the application stateless */
+ .sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS));
+
+ if (login.isOAuth2ModeEnabled()) {
+ SecurityProperties.Login.OAuth2 loginOAuth2 = login.getOAuth2();
+ RestTemplate restTemplate = new RestTemplate();
+ LoginOAuth2AccessTokenClient loginOAuth2AccessTokenClient = new LoginOAuth2AccessTokenClient(restTemplate);
+ if (oAuth2AuthorizedClientService == null) {
+ throw new NoSuchBeanDefinitionException(OAuth2AuthorizedClientService.class);
+ }
+ if (aes256Encryption == null) {
+ throw new NoSuchBeanDefinitionException(AES256Encryption.class);
+ }
+ AuthenticationSuccessHandler authenticationSuccessHandler = new LoginOAuth2SuccessHandler(loginOAuth2.getProvider(), oAuth2AuthorizedClientService,
+ aes256Encryption, login.getRedirectUri());
+ /* Enable OAuth2 */
+ httpSecurity.oauth2Login(oauth2 -> oauth2.loginPage(login.getLoginPage())
+ .tokenEndpoint(token -> token.accessTokenResponseClient(loginOAuth2AccessTokenClient))
+ .successHandler(authenticationSuccessHandler));
+ }
+
+ if (login.isClassicModeEnabled()) {
+ /*
+ * Enable Classic Authentication
+ *
+ * Note: This must be the last configuration in
+ * order to set the default 'loginPage' to oAuth2 because spring uses the
+ * 'loginPage' from the first authentication method configured
+ */
+ AuthenticationSuccessHandler authenticationSuccessHandler = new LoginClassicSuccessHandler(login.getRedirectUri());
+ httpSecurity.formLogin(form -> form.loginPage(login.getLoginPage()).successHandler(authenticationSuccessHandler));
+ }
+
+ /* @formatter:on */
+
+ return httpSecurity.build();
+ }
protected abstract Customizer.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequests();
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/CookieAccessTokenResolver.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/DynamicBearerTokenResolver.java
similarity index 66%
rename from sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/CookieAccessTokenResolver.java
rename to sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/DynamicBearerTokenResolver.java
index 3686d4036c..ed9f6b70d4 100644
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/CookieAccessTokenResolver.java
+++ b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/DynamicBearerTokenResolver.java
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MIT
package com.mercedesbenz.sechub.spring.security;
+import static org.springframework.http.HttpHeaders.AUTHORIZATION;
+
import java.util.Arrays;
import java.util.Base64;
@@ -12,30 +14,47 @@
import jakarta.servlet.http.HttpServletRequest;
/**
- * {@code CookieTokenResolver} implements {@link BearerTokenResolver} to provide
- * custom Bearer Token resolution. The encrypted access token is read from the
- * cookies and decrypted using {@link AES256Encryption}. Note that the access
- * token is expected in {@link Base64} encoded format.
+ * This class implements the {@link BearerTokenResolver} interface to provide
+ * custom Bearer Token resolution. The access token is read from the
+ * Authorization header first. If the access token is not found in
+ * the header, it is then read from the cookies and decrypted using
+ * {@link AES256Encryption}. Note that the access token has to be encrypted and
+ * encoded in {@link Base64} format when passed as a cookie.
*
* @see BearerTokenResolver
* @see AES256Encryption
*
* @author hamidonos
*/
-class CookieAccessTokenResolver implements BearerTokenResolver {
+class DynamicBearerTokenResolver implements BearerTokenResolver {
- private static final Logger LOG = LoggerFactory.getLogger(CookieAccessTokenResolver.class);
+ private static final Logger LOG = LoggerFactory.getLogger(DynamicBearerTokenResolver.class);
private static final String MISSING_ACCESS_TOKEN_VALUE = "missing-access-token";
private static final Base64.Decoder DECODER = Base64.getDecoder();
+ private static final String BEARER_PREFIX = "Bearer ";
private final AES256Encryption aes256Encryption;
- CookieAccessTokenResolver(AES256Encryption aes256Encryption) {
+ DynamicBearerTokenResolver(AES256Encryption aes256Encryption) {
this.aes256Encryption = aes256Encryption;
}
@Override
public String resolve(HttpServletRequest request) {
+
+ /*
+ * Try to resolve the token from the 'Authorization' header first
+ */
+
+ String authHeader = request.getHeader(AUTHORIZATION);
+ if (authHeader != null && authHeader.startsWith(BEARER_PREFIX)) {
+ return authHeader.substring(BEARER_PREFIX.length());
+ }
+
+ /*
+ * Attempt to resolve the token from the 'access_token' cookie
+ */
+
Cookie[] cookies = request.getCookies();
if (cookies == null) {
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/JwtDecoderConfiguration.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/JwtDecoderConfiguration.java
index 5e57453e15..ee675874c9 100644
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/JwtDecoderConfiguration.java
+++ b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/JwtDecoderConfiguration.java
@@ -1,26 +1,40 @@
// SPDX-License-Identifier: MIT
package com.mercedesbenz.sechub.spring.security;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import static java.util.Objects.requireNonNull;
+
import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Condition;
+import org.springframework.context.annotation.ConditionContext;
+import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
+import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
@Configuration
-@ConditionalOnProperty(value = "sechub.security.oauth2.token-type", havingValue = "JWT")
+@Conditional(JwtDecoderConfiguration.JwtModeEnabledCondition.class)
class JwtDecoderConfiguration {
@Bean
- JwtDecoder jwtDecoder(OAuth2JwtProperties oAuth2JwtProperties) {
+ JwtDecoder jwtDecoder(SecurityProperties securityProperties) {
/*
* @formatter:off
* The `NimbusJwtDecoder` is a `JwtDecoder` implementation that utilizes the Nimbus JOSE + JWT library to decode JSON Web Tokens (JWTs).
* It requires a JWK Set URI to fetch the public keys from the Identity Provider (IDP) to verify the JWT's signature.
*/
return NimbusJwtDecoder
- .withJwkSetUri(oAuth2JwtProperties.getJwkSetUri())
+ .withJwkSetUri(securityProperties.getServer().getOAuth2().getJwt().getJwkSetUri())
.build();
/* @formatter:on */
}
+
+ static class JwtModeEnabledCondition implements Condition {
+ @Override
+ public boolean matches(ConditionContext context, @SuppressWarnings("NullableProblems") AnnotatedTypeMetadata metadata) {
+ String modePrefix = SecurityProperties.Server.OAuth2.PREFIX + ".mode";
+ String property = context.getEnvironment().getProperty(modePrefix);
+ return SecurityProperties.Server.OAuth2.OAUTH2_JWT_MODE.equals(requireNonNull(property, "Property %s must not be null".formatted(modePrefix)));
+ }
+ }
}
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginClassicProperties.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginClassicProperties.java
deleted file mode 100644
index 6506321036..0000000000
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginClassicProperties.java
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-License-Identifier: MIT
-package com.mercedesbenz.sechub.spring.security;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-@ConfigurationProperties(prefix = LoginClassicProperties.PREFIX)
-public class LoginClassicProperties {
-
- static final String PREFIX = "sechub.security.login.classic";
-
-}
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginClassicPropertiesConfiguration.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginClassicPropertiesConfiguration.java
deleted file mode 100644
index 0450c3d7fa..0000000000
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginClassicPropertiesConfiguration.java
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-License-Identifier: MIT
-package com.mercedesbenz.sechub.spring.security;
-
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-@EnableConfigurationProperties(LoginClassicProperties.class)
-@ConditionalOnProperty(prefix = AbstractSecurityConfiguration.LOGIN_PROPERTIES_PREFIX, name = "enabled", havingValue = "true")
-class LoginClassicPropertiesConfiguration {
-}
\ No newline at end of file
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginClassicSuccessHandler.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginClassicSuccessHandler.java
index dd20142677..6587670259 100644
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginClassicSuccessHandler.java
+++ b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginClassicSuccessHandler.java
@@ -17,7 +17,7 @@
* successful authentication. This handler redirects the user to the specified
* redirectUri.
*
- * @see LoginSecurityConfiguration
+ * @see AbstractSecurityConfiguration
*
* @author hamidonos
*/
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginController.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginController.java
new file mode 100644
index 0000000000..40b29a8e67
--- /dev/null
+++ b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginController.java
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.spring.security;
+
+import org.springframework.context.annotation.Conditional;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+
+@Controller
+@Conditional(LoginEnabledCondition.class)
+class LoginController {
+
+ private final SecurityProperties.Login login;
+ private final boolean isOAuth2Enabled;
+ private final boolean isClassicAuthEnabled;
+
+ /* @formatter:off */
+ LoginController(RequestMappingHandlerMapping requestMappingHandlerMapping,
+ SecurityProperties securityProperties) throws NoSuchMethodException {
+ login = securityProperties.getLogin();
+ /* register the login page dynamically at runtime */
+ registerLoginMapping(requestMappingHandlerMapping, login.getLoginPage());
+ this.isOAuth2Enabled = login.isOAuth2ModeEnabled();
+ this.isClassicAuthEnabled = login.isClassicModeEnabled();
+ }
+ /* @formatter:on */
+
+ String login(Model model) {
+ model.addAttribute("isOAuth2Enabled", isOAuth2Enabled);
+ model.addAttribute("isClassicAuthEnabled", isClassicAuthEnabled);
+
+ if (login != null) {
+ String registrationId = login.getOAuth2().getProvider();
+ model.addAttribute("registrationId", registrationId);
+ }
+
+ return "login";
+ }
+
+ private void registerLoginMapping(RequestMappingHandlerMapping requestMappingHandlerMapping, String loginPage) throws NoSuchMethodException {
+ RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths(loginPage).methods(RequestMethod.GET).produces(MediaType.APPLICATION_JSON_VALUE)
+ .build();
+
+ requestMappingHandlerMapping.registerMapping(requestMappingInfo, this, getClass().getDeclaredMethod("login", Model.class));
+ }
+}
\ No newline at end of file
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginEnabledCondition.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginEnabledCondition.java
new file mode 100644
index 0000000000..3d8bcd9720
--- /dev/null
+++ b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginEnabledCondition.java
@@ -0,0 +1,17 @@
+package com.mercedesbenz.sechub.spring.security;
+
+import org.springframework.context.annotation.Condition;
+import org.springframework.context.annotation.ConditionContext;
+import org.springframework.core.type.AnnotatedTypeMetadata;
+
+public class LoginEnabledCondition implements Condition {
+
+ @Override
+ public boolean matches(ConditionContext context, @SuppressWarnings("NullableProblems") AnnotatedTypeMetadata metadata) {
+ /* @formatter:off */
+ return context
+ .getEnvironment()
+ .getProperty("%s.enabled".formatted(SecurityProperties.Login.PREFIX), boolean.class, false);
+ /* @formatter:on */
+ }
+}
\ No newline at end of file
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2Properties.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2Properties.java
deleted file mode 100644
index f5dd0feab6..0000000000
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2Properties.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// SPDX-License-Identifier: MIT
-package com.mercedesbenz.sechub.spring.security;
-
-import static java.util.Objects.requireNonNull;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.bind.ConstructorBinding;
-
-@ConfigurationProperties(prefix = LoginOAuth2Properties.PREFIX)
-public final class LoginOAuth2Properties {
-
- static final String PREFIX = "sechub.security.login.oauth2";
- private static final String ERR_MSG_FORMAT = "The property '%s.%s' must not be null";
-
- private final String clientId;
- private final String clientSecret;
- private final String provider;
- private final String redirectUri;
- private final String issuerUri;
- private final String authorizationUri;
- private final String tokenUri;
- private final String userInfoUri;
-
- /* @formatter:off */
- @ConstructorBinding
- LoginOAuth2Properties(String clientId,
- String clientSecret,
- String provider,
- String redirectUri,
- String issuerUri,
- String authorizationUri,
- String tokenUri,
- String userInfoUri) {
- this.clientId = requireNonNull(clientId, ERR_MSG_FORMAT.formatted(PREFIX, "client-id"));
- this.clientSecret = requireNonNull(clientSecret, ERR_MSG_FORMAT.formatted(PREFIX, "client-secret"));;
- this.provider = requireNonNull(provider, ERR_MSG_FORMAT.formatted(PREFIX, "provider"));
- this.redirectUri = requireNonNull(redirectUri, ERR_MSG_FORMAT.formatted(PREFIX, "redirect-uri"));
- this.issuerUri = requireNonNull(issuerUri, ERR_MSG_FORMAT.formatted(PREFIX, "issuer-uri"));
- this.authorizationUri = requireNonNull(authorizationUri, ERR_MSG_FORMAT.formatted(PREFIX, "authorization-uri"));
- this.tokenUri = requireNonNull(tokenUri, ERR_MSG_FORMAT.formatted(PREFIX, "token-uri"));
- this.userInfoUri = requireNonNull(userInfoUri, ERR_MSG_FORMAT.formatted(PREFIX, "user-info-uri"));
- }
- /* @formatter:on */
-
- public String getClientId() {
- return clientId;
- }
-
- public String getClientSecret() {
- return clientSecret;
- }
-
- public String getProvider() {
- return provider;
- }
-
- public String getRedirectUri() {
- return redirectUri;
- }
-
- public String getIssuerUri() {
- return issuerUri;
- }
-
- public String getAuthorizationUri() {
- return authorizationUri;
- }
-
- public String getTokenUri() {
- return tokenUri;
- }
-
- public String getUserInfoUri() {
- return userInfoUri;
- }
-
-}
\ No newline at end of file
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2PropertiesConfiguration.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2PropertiesConfiguration.java
deleted file mode 100644
index 6a38238df3..0000000000
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2PropertiesConfiguration.java
+++ /dev/null
@@ -1,13 +0,0 @@
-// SPDX-License-Identifier: MIT
-package com.mercedesbenz.sechub.spring.security;
-
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-@EnableConfigurationProperties(LoginOAuth2Properties.class)
-@ConditionalOnProperty(prefix = AbstractSecurityConfiguration.LOGIN_PROPERTIES_PREFIX, name = "enabled", havingValue = "true")
-class LoginOAuth2PropertiesConfiguration {
-
-}
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2SuccessHandler.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2SuccessHandler.java
index 2cd522aa57..949d58f8db 100644
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2SuccessHandler.java
+++ b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2SuccessHandler.java
@@ -35,8 +35,6 @@
* and encoded using {@link Base64}.
*
*
- * @see LoginSecurityConfiguration
- * @see LoginOAuth2Properties
* @see OAuth2AuthorizedClientService
*
* @author hamidonos
@@ -48,17 +46,17 @@ class LoginOAuth2SuccessHandler implements AuthenticationSuccessHandler {
private static final int DEFAULT_EXPIRY_SECONDS = 3600;
private static final String BASE_PATH = "/";
- private final LoginOAuth2Properties loginOAuth2Properties;
+ private final String provider;
private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
private final AES256Encryption aes256Encryption;
private final String redirectUri;
/* @formatter:off */
- public LoginOAuth2SuccessHandler(LoginOAuth2Properties loginOAuth2Properties,
+ public LoginOAuth2SuccessHandler(String provider,
OAuth2AuthorizedClientService oAuth2AuthorizedClientService,
AES256Encryption aes256Encryption,
String redirectUri) {
- this.loginOAuth2Properties = requireNonNull(loginOAuth2Properties, "Property loginOAuth2Properties must not be null");
+ this.provider = requireNonNull(provider, "Property provider must not be null");
this.oAuth2AuthorizedClientService = requireNonNull(oAuth2AuthorizedClientService, "Property oAuth2AuthorizedClientService must not be null");
this.aes256Encryption = requireNonNull(aes256Encryption, "Property aes256Encryption must not be null");
this.redirectUri = requireNonNull(redirectUri, "Property redirectUri must not be null");
@@ -81,8 +79,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
}
private OAuth2AccessToken getAccessTokenFromAuthentication(Authentication authentication) {
- OAuth2AuthorizedClient oAuth2AuthorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient(loginOAuth2Properties.getProvider(),
- authentication.getName());
+ OAuth2AuthorizedClient oAuth2AuthorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient(provider, authentication.getName());
return oAuth2AuthorizedClient.getAccessToken();
}
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginProperties.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginProperties.java
deleted file mode 100644
index 8aed494eb4..0000000000
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginProperties.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.mercedesbenz.sechub.spring.security;
-
-// SPDX-License-Identifier: MIT
-import java.util.List;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.bind.ConstructorBinding;
-
-@ConfigurationProperties(prefix = LoginProperties.PREFIX)
-public class LoginProperties {
-
- static final String PREFIX = "sechub.security.login";
- private static final String CLASSIC = "classic";
- private static final String OAUTH2 = "oauth2";
-
- private final boolean isEnabled;
- private final String redirectUri;
- private final List modes;
-
- @ConstructorBinding
- LoginProperties(boolean enabled, String redirectUri, List modes) {
- this.isEnabled = enabled;
- this.redirectUri = redirectUri;
- this.modes = modes;
- }
-
- public boolean isEnabled() {
- return isEnabled;
- }
-
- public String getRedirectUri() {
- return redirectUri;
- }
-
- public List getModes() {
- return modes;
- }
-
- public boolean isClassicModeEnabled() {
- return modes.contains(CLASSIC);
- }
-
- public boolean isOAuth2ModeEnabled() {
- return modes.contains(OAUTH2);
- }
-}
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginPropertiesConfiguration.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginPropertiesConfiguration.java
deleted file mode 100644
index de54906e56..0000000000
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/LoginPropertiesConfiguration.java
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-License-Identifier: MIT
-package com.mercedesbenz.sechub.spring.security;
-
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-@ConditionalOnProperty(prefix = LoginProperties.PREFIX, name = "enabled", havingValue = "true")
-@EnableConfigurationProperties(LoginProperties.class)
-class LoginPropertiesConfiguration {
-}
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/MissingAuthenticationEntryPointHandler.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/MissingAuthenticationEntryPointHandler.java
index 2b56317472..c7a093d377 100644
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/MissingAuthenticationEntryPointHandler.java
+++ b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/MissingAuthenticationEntryPointHandler.java
@@ -13,8 +13,8 @@
* {@code MissingAuthenticationEntryPointHandler} implements
* {@link AuthenticationEntryPoint} to provide custom behavior upon missing or
* invalid authentication. This class is used by Spring's
- * oauth2ResourceServer configuration to redirect the user to the login
- * page if the user is not authenticated.
+ * oauth2ResourceServer configuration to redirect the user to the
+ * specified location if the user is not authenticated.
*
* @author hamidonos
*/
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtAuthenticationProvider.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtAuthenticationProvider.java
index dce0aa5016..c5cc9d86f0 100644
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtAuthenticationProvider.java
+++ b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtAuthenticationProvider.java
@@ -24,10 +24,9 @@
*
*
* The {@link org.springframework.security.oauth2.jwt.JwtDecoder} is employed to
- * decode the JWT token, extracting the username by interacting with the
- * identity provider. This username is then utilized to retrieve user details
- * from the user details service. These details are subsequently used to create
- * a
+ * decode the JWT token, extracting the username by decoding the JWT. This
+ * username is then utilized to retrieve user details from the user details
+ * service. These details are subsequently used to create a
* {@link org.springframework.security.authentication.UsernamePasswordAuthenticationToken},
* which encapsulates information about the authenticated user and their roles.
*
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtProperties.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtProperties.java
deleted file mode 100644
index 7d6cd51d40..0000000000
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtProperties.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: MIT
-package com.mercedesbenz.sechub.spring.security;
-
-import static java.util.Objects.requireNonNull;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-@ConfigurationProperties(OAuth2JwtProperties.PREFIX)
-public class OAuth2JwtProperties {
-
- static final String PREFIX = "sechub.security.server.oauth2.jwt";
- private final String jwkSetUri;
-
- OAuth2JwtProperties(String jwkSetUri) {
- this.jwkSetUri = requireNonNull(jwkSetUri, "Property '%s.%s' must not be null".formatted(PREFIX, "jwk-set-uri"));
- }
-
- public String getJwkSetUri() {
- return jwkSetUri;
- }
-}
\ No newline at end of file
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtPropertiesConfiguration.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtPropertiesConfiguration.java
deleted file mode 100644
index b13144e91a..0000000000
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtPropertiesConfiguration.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.mercedesbenz.sechub.spring.security;
-
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-@EnableConfigurationProperties(OAuth2JwtProperties.class)
-@ConditionalOnProperty(prefix = AbstractSecurityConfiguration.SERVER_OAUTH2_PROPERTIES_PREFIX, name = AbstractSecurityConfiguration.MODE, havingValue = OAuth2JwtPropertiesConfiguration.MODE)
-class OAuth2JwtPropertiesConfiguration {
- static final String MODE = "JWT";
-}
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenIntrospectionResponse.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenIntrospectionResponse.java
index 4c283d82ea..792f2c6b1f 100644
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenIntrospectionResponse.java
+++ b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenIntrospectionResponse.java
@@ -9,10 +9,11 @@
import com.fasterxml.jackson.annotation.JsonProperty;
/**
- * OAuth2OpaqueTokenIntrospectionResponse represents the response from the OAuth2
- * opaque token introspection endpoint. It contains various properties related
- * to the token, such as its active status, scope, client ID, client type,
- * username, token type, expiration time, subject, audience, and group type.
+ * OAuth2OpaqueTokenIntrospectionResponse represents the response
+ * from the OAuth2 opaque token introspection endpoint. It contains various
+ * properties related to the token, such as its active status, scope, client ID,
+ * client type, username, token type, expiration time, subject, audience, and
+ * group type.
*
*
* The active property is required and indicates whether the token
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenProperties.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenProperties.java
deleted file mode 100644
index 6b17e314b2..0000000000
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenProperties.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// SPDX-License-Identifier: MIT
-package com.mercedesbenz.sechub.spring.security;
-
-import static java.util.Objects.requireNonNull;
-
-import javax.crypto.SealedObject;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-import com.mercedesbenz.sechub.commons.core.security.CryptoAccess;
-
-@ConfigurationProperties(OAuth2OpaqueTokenProperties.PREFIX)
-public class OAuth2OpaqueTokenProperties {
-
- static final String PREFIX = "sechub.security.server.oauth2.opaque-token";
- private static final String ERR_MSG_FORMAT = "Property '%s.%s' must not be null";
- private static final CryptoAccess CRYPTO_STRING = CryptoAccess.CRYPTO_STRING;
-
- private final String introspectionUri;
- private final SealedObject clientIdSealed;
- private final SealedObject clientSecretSealed;
-
- /* @formatter:off */
- OAuth2OpaqueTokenProperties(String introspectionUri,
- String clientId,
- String clientSecret) {
- this.introspectionUri = requireNonNull(introspectionUri, ERR_MSG_FORMAT.formatted(PREFIX, "introspection-uri"));
- this.clientIdSealed = CRYPTO_STRING.seal(requireNonNull(clientId, ERR_MSG_FORMAT.formatted(PREFIX, "client-id")));
- this.clientSecretSealed = CRYPTO_STRING.seal(requireNonNull(clientSecret, ERR_MSG_FORMAT.formatted(PREFIX, "client-secret")));
- }
- /* @formatter:on */
-
- public String getIntrospectionUri() {
- return introspectionUri;
- }
-
- public String getClientId() {
- return CRYPTO_STRING.unseal(clientIdSealed);
- }
-
- public String getClientSecret() {
- return CRYPTO_STRING.unseal(clientSecretSealed);
- }
-}
\ No newline at end of file
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenPropertiesConfiguration.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenPropertiesConfiguration.java
deleted file mode 100644
index e0180fe8d3..0000000000
--- a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenPropertiesConfiguration.java
+++ /dev/null
@@ -1,13 +0,0 @@
-// SPDX-License-Identifier: MIT
-package com.mercedesbenz.sechub.spring.security;
-
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-@EnableConfigurationProperties(OAuth2OpaqueTokenProperties.class)
-@ConditionalOnProperty(prefix = AbstractSecurityConfiguration.SERVER_OAUTH2_PROPERTIES_PREFIX, name = AbstractSecurityConfiguration.MODE, havingValue = OAuth2OpaqueTokenPropertiesConfiguration.MODE)
-class OAuth2OpaqueTokenPropertiesConfiguration {
- static final String MODE = "OPAQUE_TOKEN";
-}
\ No newline at end of file
diff --git a/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/security/PortAccessGuard.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/PortAccessGuard.java
similarity index 74%
rename from sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/security/PortAccessGuard.java
rename to sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/PortAccessGuard.java
index d01591513f..cf52e24fff 100644
--- a/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/security/PortAccessGuard.java
+++ b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/PortAccessGuard.java
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-package com.mercedesbenz.sechub.webserver.security;
+package com.mercedesbenz.sechub.spring.security;
import java.io.IOException;
@@ -11,8 +11,14 @@
import jakarta.servlet.http.HttpServletResponse;
/**
- * Filter which checks if the request is targeting the allowed port. If not, it
- * will return a 403 Forbidden response.
+ * Filter which checks if the request path is targeting the allowed port. If
+ * not, it will return a 403 Forbidden response.
+ *
+ *
+ * For example, if the allowed port is 8080, and the request is
+ * targeting 8081, the filter will return a
+ * 403 Forbidden response .
+ *
*
* @author hamidonos
*/
diff --git a/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/SecurityProperties.java b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/SecurityProperties.java
new file mode 100644
index 0000000000..c023f8aa88
--- /dev/null
+++ b/sechub-commons-security-spring/src/main/java/com/mercedesbenz/sechub/spring/security/SecurityProperties.java
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: MIT
+package com.mercedesbenz.sechub.spring.security;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Set;
+
+import javax.crypto.SealedObject;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.bind.ConstructorBinding;
+
+import com.mercedesbenz.sechub.commons.core.security.CryptoAccess;
+
+@ConfigurationProperties(prefix = SecurityProperties.PREFIX)
+public class SecurityProperties {
+ static final String PREFIX = "sechub.security";
+
+ private static final Logger LOG = LoggerFactory.getLogger(SecurityProperties.class);
+ private static final String ERR_MSG_FORMAT = "The property '%s.%s' must not be null";
+ private static final String OAUTH2_MODE = "oauth2";
+ private static final String CLASSIC_MODE = "classic";
+ private static final Set ALLOWED_MODES = Set.of(OAUTH2_MODE, CLASSIC_MODE);
+
+ /**
+ * Holds all the configuration properties for the server to authenticate
+ * incoming requests. Authentication can be handled either in 'oauth2' mode or
+ * 'classic' mode. Set this to null if none of these modes is needed (e.g. when
+ * testing)
+ */
+ private final Server server;
+
+ /**
+ * Configures the server to offer login functionality for users. With this
+ * configuration, the server will be able to provide authentication to users.
+ * Set this to null if the server should not offer login functionality.
+ */
+ private final Login login;
+
+ private final Encryption encryption;
+
+ @ConstructorBinding
+ public SecurityProperties(Server server, Login login, Encryption encryption) {
+ this.server = server;
+ if (server == null) {
+ LOG.warn("The property '%s.server' is not set. The server will not be able to authenticate requests".formatted(PREFIX));
+ }
+ this.login = login;
+ this.encryption = login != null && login.isEnabled() ? requireNonNull(encryption, ERR_MSG_FORMAT.formatted(PREFIX, "encryption")) : encryption;
+ }
+
+ public Server getServer() {
+ return server;
+ }
+
+ public Login getLogin() {
+ return login;
+ }
+
+ public Encryption getEncryption() {
+ return encryption;
+ }
+
+ public static class Server {
+ static final String PREFIX = "%s.server".formatted(SecurityProperties.PREFIX);
+
+ private final Set modes;
+ private final OAuth2 oAuth2;
+
+ @ConstructorBinding
+ public Server(Set modes, OAuth2 oAuth2) {
+ this.modes = requireNonNull(modes, ERR_MSG_FORMAT.formatted(PREFIX, "modes"));
+ if (this.modes.isEmpty()) {
+ throw new IllegalArgumentException("The property '%s.modes' must at least include 'oauth2' or 'classic' mode".formatted(PREFIX));
+ }
+ if (this.modes.stream().noneMatch(ALLOWED_MODES::contains)) {
+ throw new IllegalArgumentException("The property '%s.modes' allows only 'oauth2' or 'classic' mode".formatted(PREFIX));
+ }
+ this.oAuth2 = oAuth2;
+ }
+
+ public Set getModes() {
+ return modes;
+ }
+
+ public boolean isOAuth2ModeEnabled() {
+ return modes.contains(OAUTH2_MODE);
+ }
+
+ public boolean isClassicModeEnabled() {
+ return modes.contains(CLASSIC_MODE);
+ }
+
+ public OAuth2 getOAuth2() {
+ return oAuth2;
+ }
+
+ public static class OAuth2 {
+ static final String PREFIX = "%s.oauth2".formatted(Server.PREFIX);
+ public static final String OAUTH2_JWT_MODE = "jwt";
+ public static final String OAUTH2_OPAQUE_TOKEN_MODE = "opaque-token";
+ private static final Set ALLOWED_MODES = Set.of(OAUTH2_JWT_MODE, OAUTH2_OPAQUE_TOKEN_MODE);
+
+ private final String mode;
+ private final Jwt jwt;
+ private final OpaqueToken opaqueToken;
+
+ @ConstructorBinding
+ public OAuth2(String mode, Jwt jwt, OpaqueToken opaqueToken) {
+ this.mode = requireNonNull(mode, ERR_MSG_FORMAT.formatted(PREFIX, "mode"));
+ if (!ALLOWED_MODES.contains(mode)) {
+ throw new IllegalArgumentException("The property '%s.mode' allows only 'jwt' or 'opaque-token' mode".formatted(PREFIX));
+ }
+ this.jwt = OAUTH2_JWT_MODE.equals(this.mode) ? requireNonNull(jwt, ERR_MSG_FORMAT.formatted(PREFIX, "jwt")) : null;
+ this.opaqueToken = OAUTH2_OPAQUE_TOKEN_MODE.equals(this.mode) ? requireNonNull(opaqueToken, ERR_MSG_FORMAT.formatted(PREFIX, "opaque-token"))
+ : null;
+ }
+
+ public String getMode() {
+ return mode;
+ }
+
+ public boolean isJwtModeEnabled() {
+ return OAUTH2_JWT_MODE.equals(mode);
+ }
+
+ public boolean isOpaqueTokenModeEnabled() {
+ return OAUTH2_OPAQUE_TOKEN_MODE.equals(mode);
+ }
+
+ public Jwt getJwt() {
+ return jwt;
+ }
+
+ public OpaqueToken getOpaqueToken() {
+ return opaqueToken;
+ }
+
+ public static class Jwt {
+ static final String PREFIX = "%s.jwt".formatted(OAuth2.PREFIX);
+
+ private final String jwkSetUri;
+
+ @ConstructorBinding
+ public Jwt(String jwkSetUri) {
+ this.jwkSetUri = requireNonNull(jwkSetUri, ERR_MSG_FORMAT.formatted(PREFIX, "jwk-set-uri"));
+ }
+
+ public String getJwkSetUri() {
+ return jwkSetUri;
+ }
+ }
+
+ public static class OpaqueToken {
+ static final String PREFIX = "%s.opaque-token".formatted(OAuth2.PREFIX);
+
+ private final String introspectionUri;
+ private final String clientId;
+ private final String clientSecret;
+
+ @ConstructorBinding
+ public OpaqueToken(String introspectionUri, String clientId, String clientSecret) {
+ this.introspectionUri = requireNonNull(introspectionUri, ERR_MSG_FORMAT.formatted(PREFIX, "introspection-uri"));
+ this.clientId = requireNonNull(clientId, ERR_MSG_FORMAT.formatted(PREFIX, "client-id"));
+ this.clientSecret = requireNonNull(clientSecret, ERR_MSG_FORMAT.formatted(PREFIX, "client-secret"));
+ }
+
+ public String getIntrospectionUri() {
+ return introspectionUri;
+ }
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public String getClientSecret() {
+ return clientSecret;
+ }
+ }
+ }
+ }
+
+ public static class Login {
+ static final String PREFIX = "%s.login".formatted(SecurityProperties.PREFIX);
+
+ private final boolean isEnabled;
+ private final String loginPage;
+ private final String redirectUri;
+ private final Set modes;
+ private final OAuth2 oAuth2;
+
+ public Login(Boolean enabled, String loginPage, String redirectUri, Set modes, OAuth2 oAuth2) {
+ this.isEnabled = requireNonNull(enabled, ERR_MSG_FORMAT.formatted(PREFIX, "enabled"));
+ this.loginPage = requireNonNull(loginPage, ERR_MSG_FORMAT.formatted(PREFIX, "login-page"));
+ this.redirectUri = requireNonNull(redirectUri, ERR_MSG_FORMAT.formatted(PREFIX, "redirect-uri"));
+ this.modes = requireNonNull(modes, ERR_MSG_FORMAT.formatted(PREFIX, "modes"));
+ if (this.modes.isEmpty()) {
+ throw new IllegalArgumentException("The property '%s.modes' must at least include 'oauth2' or 'classic' mode".formatted(PREFIX));
+ }
+ if (this.modes.stream().noneMatch(ALLOWED_MODES::contains)) {
+ throw new IllegalArgumentException("The property '%s.modes' allows only 'oauth2' or 'classic' mode".formatted(PREFIX));
+ }
+ /*
+ * Later we will differentiate between classic and oauth2 login. For now only
+ * oauth2 login is enabled
+ */
+ this.oAuth2 = requireNonNull(oAuth2, ERR_MSG_FORMAT.formatted(PREFIX, "oauth2"));
+ }
+
+ public boolean isEnabled() {
+ return isEnabled;
+ }
+
+ public String getLoginPage() {
+ return loginPage;
+ }
+
+ public String getRedirectUri() {
+ return redirectUri;
+ }
+
+ public Set getModes() {
+ return modes;
+ }
+
+ public boolean isOAuth2ModeEnabled() {
+ return modes.contains(OAUTH2_MODE);
+ }
+
+ public boolean isClassicModeEnabled() {
+ return modes.contains(CLASSIC_MODE);
+ }
+
+ public OAuth2 getOAuth2() {
+ return oAuth2;
+ }
+
+ public static class OAuth2 {
+ static final String PREFIX = "%s.oauth2".formatted(Login.PREFIX);
+
+ private final String clientId;
+ private final String clientSecret;
+ private final String provider;
+ private final String redirectUri;
+ private final String issuerUri;
+ private final String authorizationUri;
+ private final String tokenUri;
+ private final String userInfoUri;
+ private final String jwkSetUri;
+
+ @ConstructorBinding
+ public OAuth2(String clientId, String clientSecret, String provider, String redirectUri, String issuerUri, String authorizationUri, String tokenUri,
+ String userInfoUri, String jwkSetUri) {
+ this.clientId = requireNonNull(clientId, ERR_MSG_FORMAT.formatted(PREFIX, "client-id"));
+ this.clientSecret = requireNonNull(clientSecret, ERR_MSG_FORMAT.formatted(PREFIX, "client-secret"));
+ this.provider = requireNonNull(provider, ERR_MSG_FORMAT.formatted(PREFIX, "provider"));
+ this.redirectUri = requireNonNull(redirectUri, ERR_MSG_FORMAT.formatted(PREFIX, "redirect-uri"));
+ this.issuerUri = requireNonNull(issuerUri, ERR_MSG_FORMAT.formatted(PREFIX, "issuer-uri"));
+ this.authorizationUri = requireNonNull(authorizationUri, ERR_MSG_FORMAT.formatted(PREFIX, "authorization-uri"));
+ this.tokenUri = requireNonNull(tokenUri, ERR_MSG_FORMAT.formatted(PREFIX, "token-uri"));
+ this.userInfoUri = requireNonNull(userInfoUri, ERR_MSG_FORMAT.formatted(PREFIX, "user-info-uri"));
+ this.jwkSetUri = requireNonNull(jwkSetUri, ERR_MSG_FORMAT.formatted(PREFIX, "jwk-set-uri"));
+ }
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public String getClientSecret() {
+ return clientSecret;
+ }
+
+ public String getProvider() {
+ return provider;
+ }
+
+ public String getRedirectUri() {
+ return redirectUri;
+ }
+
+ public String getIssuerUri() {
+ return issuerUri;
+ }
+
+ public String getAuthorizationUri() {
+ return authorizationUri;
+ }
+
+ public String getTokenUri() {
+ return tokenUri;
+ }
+
+ public String getUserInfoUri() {
+ return userInfoUri;
+ }
+
+ public String getJwkSetUri() {
+ return jwkSetUri;
+ }
+
+ }
+ }
+
+ public static class Encryption {
+ static final String PREFIX = "%s.encryption".formatted(SecurityProperties.PREFIX);
+ private static final int AES_256_SECRET_KEY_LENGTH = 32;
+
+ private final SealedObject secretKey;
+
+ @ConstructorBinding
+ public Encryption(String secretKey) {
+ requireNonNull(secretKey, ERR_MSG_FORMAT.formatted(PREFIX, "secret-key"));
+ if (!is256BitString(secretKey)) {
+ throw new IllegalArgumentException("The property %s.%s must be a 256-bit string".formatted(PREFIX, "secret-key"));
+ }
+ this.secretKey = CryptoAccess.CRYPTO_STRING.seal(secretKey);
+ }
+
+ public String getSecretKey() {
+ return CryptoAccess.CRYPTO_STRING.unseal(secretKey);
+ }
+
+ /*
+ * Checks if the secret key length is 32 characters (32 * 8 = 256 bits)
+ */
+ private static boolean is256BitString(String secretKey) {
+ return secretKey.length() == AES_256_SECRET_KEY_LENGTH;
+ }
+ }
+ /* @formatter:on */
+}
diff --git a/sechub-commons-security-spring/src/main/resources/static/favicon.ico b/sechub-commons-security-spring/src/main/resources/static/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..02fcae3958a8a5ad8d07e2faa2eae44b6cef275a
GIT binary patch
literal 4286
zcmcIn4LFr)8a^(ywZ*m5b@j752o;^f;T%+?{N`^Eg~phg$!uxoYOZZe#iS8R%Ct)j
zZ8H6g-zE~uio~qSU#M0T5;2;72ID8h#CPxWey1a@n5LawuKT>-dEW1N@B4k8=Y78K
z2m}iBuc0AepMukG2?VnR0)db~CV?e^*(dq~AQ%rSYHE&FVU~@Obx|e>0e#roI;)*%LPuIfulO?Shl6k(WbLam4x9(l;Z@S&|
zw)IKgL4p#Tz>Z3li3=HGKlR<>nbYWT906#BIs)W&IYfi^mcYypi
zy(u@l-#pm-it`C4EZu6t>3G>gC##uX+q<+I*W3DeUdGLEzFmgo2ZL}e?&j~)J5>qG
z5OeZhiusEc?4o>3dlMa0<2Tlv^MHM%4a+URWIcoPMna6JL``x0F>d+8NBTJlmG^;&w;xn0=QuwIDDh0@1})
zBQana4%hy~>y;L?Q_Wa{#nC$48d7fqkcJ4>gI=d&@#e+x?0o5*mgPLXv1csz{T95J
zycQnk-Qo4M?-+ij!KJ8^*C5^<15-a!&VkO2M}|9`Pdaf1e>S{EI@EtjgZ^s$m}UM;
z`LktEI*7WiXCwTVYHnd&`g&~1^2fe{Xsk?hM#7zkV>EnmLly(!kpk$RDB*-Bia0G7
zN6zN&E8vmo&YA5pYo160s}-XShF6z_1oE-pSRt!(zSjR;3D?su2+NNmO;2&^)(t)e
zq1hM5@JBSrYCx278`|M%kkB6V|J4aipJ2}5BSW~Qt%h`+v_KvMU3YO7_4x{$NSg|a
z-4^|}kv5zp{3sv8;PUp7y?>b-gRs0PH1-biw(Lj!c=zlFc>n8dWB7O9Y?sx*ncI-&
zcR=UZRY+OSpdPVcwI@gG!|tFhs|S0gK~+s{x{;63R?9sWmCO81%gut!&TBJ1d|jM|F|H&oL}Qy^UL_ldD&RsH^lEbwx&*&gPfN+_^4Etf66_ir9VS2
z=LcQ`i-9Pvj5GV(3{J6*obji|v2tGdIA`{3!8B!MbDfNPJbz(p_qhA7>6P(kJRX4O
zS#Nk9|7;`&R)rnBuhza4NUse+H=_aCQMvs7=kGj<<$lXK*CZEC%St;y&MP04rcYNO
z{!Q%sviM(r*fB2u&8IJWPfT?U4wY8${OJ#$@qUK%ToN9=%t@~eLnrAvB$s>m8Wh@#
z=ox0rIUKgP1
z>O^JdFfyCkM>rXGa#IvF6#b=m$!Eao8zoQUd3NcFd9?;fl;V2zdW&d~SNPo#4`
zWi;>TYfn#4!Q@Gk6sJy`YM~i@F;AG@FrtHLc^wvg*MWuWH({CoGFXOMg8EIHS%_=f
z(!@+GJWm--v3Jt;~REv5PQ{sQ)=dw^J6p)|$
z+Mvi=CvP+%q2>X;ZTyKx!ktIh5EhA(w_0$nZ3r2U2l?D2HTA%5<7e6Ar$TTOXFs4`
z_?+?Ym}t)v*~v#5l$wuZ>h)(@x_KUX%^k=q!+U{Y*qM|urY6`t)AH`FgSq5COBj2Z
z&)qyPzqpBbCbFxjzP&TH{2tO;dXUrnBetA9gk8DOxYO|zS@aH+_I(d>pA4Y)r@6Aqj05S0^!ipD0Mf6LL++{`(11ap@KO_xLDW5^Ut&P
zN@Q!&8X~9UO88%E|F3Fr!~U3-bkgcE|C0mfXdNYjS#)VVqDyL-F0)_3JyU!4x6R}7vR+h1{f_+xU>ZvM==$eZ
z_0fIocgp;2hl|+zjlIwI)Rgag;T;}(i@yKjZxsy}pLqRZSx@EnRn!mhKU<-f-UKl{
zSM<^vAkJ)tnEH@7qZxXswa`r@JDX?co$5^<5BF%2GDgU;{Et-x|`zgM2Y
z@|aM3l(i$W2ZYB<`UpK*!AX^5+ek1X7!rg8>5K2o#-K;glG~{7`D*V?X new AES256EncryptionProperties(null))
- .isInstanceOf(NullPointerException.class)
- .hasMessageContaining("The property 'sechub.security.encryption.secret-key' must not be null");
- /* @formatter:on */
- }
-
- @ParameterizedTest
- @ValueSource(strings = { "", "1", "est-test-test-test-test-test-31", "-test-test-test-test-test-test-33" })
- void construct_aes256encryption_properties_with_non_256_bit_long_secret_key_fails(String secretKey) {
- /* @formatter:off */
- /* execute & test */
- assertThatThrownBy(() -> new AES256EncryptionProperties(secretKey))
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessageContaining("The property sechub.security.encryption.secret-key must be a 256-bit string");
- /* @formatter:on */
- }
-
- @Configuration
- @EnableConfigurationProperties(AES256EncryptionProperties.class)
- static class TestConfig {
- }
-}
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/AES256EncryptionTest.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/AES256EncryptionTest.java
index 219725efdd..bb637ae0d5 100644
--- a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/AES256EncryptionTest.java
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/AES256EncryptionTest.java
@@ -3,6 +3,7 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
import java.security.GeneralSecurityException;
import java.util.Base64;
@@ -13,13 +14,14 @@
class AES256EncryptionTest {
private static final String VALID_AES_256_TEST_SECRET_KEY = "test-test-test-test-test-test-32";
- private static final AES256EncryptionProperties aes256EncryptionProperties = new AES256EncryptionProperties(VALID_AES_256_TEST_SECRET_KEY);
+ private static final SecurityProperties securityProperties = new SecurityProperties(mock(), mock(),
+ new SecurityProperties.Encryption(VALID_AES_256_TEST_SECRET_KEY));
private static final AES256Encryption aes256Encryption;
private static final Base64.Encoder ENCODER = Base64.getEncoder();
static {
try {
- aes256Encryption = new AES256Encryption(aes256EncryptionProperties);
+ aes256Encryption = new AES256Encryption(securityProperties);
} catch (GeneralSecurityException e) {
throw new TestAbortedException("Failed to prepare AES256EncryptionTest", e);
}
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/CookieAccessTokenResolverTest.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/DynamicBearerTokenResolverTest.java
similarity index 77%
rename from sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/CookieAccessTokenResolverTest.java
rename to sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/DynamicBearerTokenResolverTest.java
index 4ea90e6b8b..b6f019d233 100644
--- a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/CookieAccessTokenResolverTest.java
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/DynamicBearerTokenResolverTest.java
@@ -4,6 +4,8 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -18,12 +20,11 @@
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;
-import org.mockito.Mockito;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
-class CookieAccessTokenResolverTest {
+class DynamicBearerTokenResolverTest {
private static final String MISSING_ACCESS_TOKEN = "missing-access-token";
private static final String ACCESS_TOKEN_KEY = "access_token";
@@ -34,25 +35,40 @@ class CookieAccessTokenResolverTest {
private static final String BASE_PATH = "/";
private static final AES256Encryption aes256Encryption = mock();
- private static final CookieAccessTokenResolver cookieAccessTokenResolver = new CookieAccessTokenResolver(aes256Encryption);
+ private static final DynamicBearerTokenResolver DYNAMIC_BEARER_TOKEN_RESOLVER = new DynamicBearerTokenResolver(aes256Encryption);
private static final HttpServletRequest httpServletRequest = mock();
@BeforeEach
void beforeEach() {
- Mockito.reset(aes256Encryption);
+ reset(aes256Encryption, httpServletRequest);
when(aes256Encryption.decrypt(any())).thenReturn(ACCESS_TOKEN);
}
@Test
- void resolve_reads_and_decrypts_access_token_from_cookies_successfully() {
+ void resolve_reads_access_token_from_authorization_header_successfully() {
/* prepare */
+ when(httpServletRequest.getHeader("Authorization")).thenReturn("Bearer " + ACCESS_TOKEN);
+
+ /* execute */
+ String accessToken = DYNAMIC_BEARER_TOKEN_RESOLVER.resolve(httpServletRequest);
+
+ /* test */
+ assertThat(accessToken).isEqualTo(ACCESS_TOKEN);
+ verify(httpServletRequest, never()).getCookies();
+ verify(aes256Encryption, never()).decrypt(DECRYPTED_ACCESS_TOKEN_B64_DECODED);
+ }
+
+ @Test
+ void resolve_reads_and_decrypts_access_token_from_cookies_successfully_when_no_authorization_header_is_available() {
+ /* prepare */
+ when(httpServletRequest.getHeader("Authorization")).thenReturn("");
Cookie cookie = createAccessTokenCookie(ACCESS_TOKEN_KEY, ENCRYPTED_ACCESS_TOKEN_B64_ENCODED);
Cookie someOtherCookie = createAccessTokenCookie("some-other-cookie-name", "some-other-cookie-value");
Cookie[] cookies = List.of(cookie, someOtherCookie).toArray(new Cookie[0]);
when(httpServletRequest.getCookies()).thenReturn(cookies);
/* execute */
- String accessToken = cookieAccessTokenResolver.resolve(httpServletRequest);
+ String accessToken = DYNAMIC_BEARER_TOKEN_RESOLVER.resolve(httpServletRequest);
/* test */
assertThat(accessToken).isEqualTo(ACCESS_TOKEN);
@@ -60,14 +76,14 @@ void resolve_reads_and_decrypts_access_token_from_cookies_successfully() {
}
@ParameterizedTest
- @ArgumentsSource(CookieAccessTokenResolverTest.InvalidCookieListProvider.class)
+ @ArgumentsSource(DynamicBearerTokenResolverTest.InvalidCookieListProvider.class)
void resolve_returns_missing_access_token_value_when_access_token_cookie_is_not_found(List cookies) {
/* prepare */
Cookie[] array = cookies == null ? null : cookies.toArray(new Cookie[0]);
when(httpServletRequest.getCookies()).thenReturn(array);
/* execute */
- String accessToken = cookieAccessTokenResolver.resolve(httpServletRequest);
+ String accessToken = DYNAMIC_BEARER_TOKEN_RESOLVER.resolve(httpServletRequest);
/* test */
assertThat(accessToken).isEqualTo(MISSING_ACCESS_TOKEN);
@@ -82,7 +98,7 @@ void resolve_returns_missing_access_token_value_when_access_token_decoding_fails
/* execute & test */
- String accessToken = cookieAccessTokenResolver.resolve(httpServletRequest);
+ String accessToken = DYNAMIC_BEARER_TOKEN_RESOLVER.resolve(httpServletRequest);
/* test */
assertThat(accessToken).isEqualTo(MISSING_ACCESS_TOKEN);
@@ -98,7 +114,7 @@ void resolve_returns_missing_access_token_value_when_access_token_decryption_fai
/* execute & test */
- String accessToken = cookieAccessTokenResolver.resolve(httpServletRequest);
+ String accessToken = DYNAMIC_BEARER_TOKEN_RESOLVER.resolve(httpServletRequest);
/* test */
assertThat(accessToken).isEqualTo(MISSING_ACCESS_TOKEN);
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2PropertiesTest.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2PropertiesTest.java
deleted file mode 100644
index 6e96f67a08..0000000000
--- a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2PropertiesTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-// 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.assertj.core.api.Assertions;
-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.TestPropertySource;
-
-import com.mercedesbenz.sechub.testframework.spring.YamlPropertyLoaderFactory;
-
-@SpringBootTest
-@TestPropertySource(locations = "classpath:application-login-oauth2-test.yaml", factory = YamlPropertyLoaderFactory.class)
-class LoginOAuth2PropertiesTest {
-
- private static final String ERR_MSG_FORMAT = "The property 'sechub.security.login.oauth2.%s' must not be null";
-
- private final LoginOAuth2Properties properties;
-
- @Autowired
- LoginOAuth2PropertiesTest(LoginOAuth2Properties properties) {
- this.properties = properties;
- }
-
- @Test
- void construct_login_o_auth_2_properties_with_valid_properties_file_succeeds() {
- assertThat(properties.getClientId()).isEqualTo("client-id");
- assertThat(properties.getClientSecret()).isEqualTo("client-secret");
- assertThat(properties.getProvider()).isEqualTo("provider");
- assertThat(properties.getRedirectUri()).isEqualTo("redirect-uri");
- assertThat(properties.getIssuerUri()).isEqualTo("issuer-uri");
- assertThat(properties.getAuthorizationUri()).isEqualTo("authorization-uri");
- assertThat(properties.getTokenUri()).isEqualTo("token-uri");
- assertThat(properties.getUserInfoUri()).isEqualTo("user-info-uri");
- }
-
- /* @formatter:off */
- @ParameterizedTest
- @ArgumentsSource(InvalidLoginOAuth2PropertiesProvider.class)
- void construct_login_o_auth_2_properties_with_null_property_fails(String clientId,
- String clientSecret,
- String provider,
- String redirectUri,
- String issuerUri,
- String authorizationUri,
- String tokenUri,
- String userInfoUri,
- String errMsg) {
- Assertions.assertThatThrownBy(() -> new LoginOAuth2Properties(clientId, clientSecret, provider, redirectUri, issuerUri, authorizationUri, tokenUri, userInfoUri))
- .isInstanceOf(NullPointerException.class)
- .hasMessageContaining(errMsg);
- }
- /* @formatter:on */
-
- @Configuration
- @Import(LoginOAuth2PropertiesConfiguration.class)
- static class TestConfig {
- }
-
- private static class InvalidLoginOAuth2PropertiesProvider implements ArgumentsProvider {
- @Override
- public Stream extends Arguments> provideArguments(ExtensionContext extensionContext) throws Exception {
- /* @formatter:off */
- return Stream.of(
- Arguments.of(null, "client-secret", "provider", "redirect-uri", "issuer-uri", "authorization-uri", "token-uri", "user-info-uri", String.format(ERR_MSG_FORMAT, "client-id")),
- Arguments.of("client-id", null, "provider", "redirect-uri", "issuer-uri", "authorization-uri", "token-uri", "user-info-uri", String.format(ERR_MSG_FORMAT, "client-secret")),
- Arguments.of("client-id", "client-secret", null, "redirect-uri", "issuer-uri", "authorization-uri", "token-uri", "user-info-uri", String.format(ERR_MSG_FORMAT, "provider")),
- Arguments.of("client-id", "client-secret", "provider", null, "issuer-uri", "authorization-uri", "token-uri", "user-info-uri", String.format(ERR_MSG_FORMAT, "redirect-uri")),
- Arguments.of("client-id", "client-secret", "provider", "redirect-uri", null, "authorization-uri", "token-uri", "user-info-uri", String.format(ERR_MSG_FORMAT, "issuer-uri")),
- Arguments.of("client-id", "client-secret", "provider", "redirect-uri", "issuer-uri", null, "token-uri", "user-info-uri", String.format(ERR_MSG_FORMAT, "authorization-uri")),
- Arguments.of("client-id", "client-secret", "provider", "redirect-uri", "issuer-uri", "authorization-uri", null, "user-info-uri", String.format(ERR_MSG_FORMAT, "token-uri")),
- Arguments.of("client-id", "client-secret", "provider", "redirect-uri", "issuer-uri", "authorization-uri", "token-uri", null, String.format(ERR_MSG_FORMAT, "user-info-uri"))
- );
- /* @formatter:on */
- }
- }
-}
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2SuccessHandlerTest.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2SuccessHandlerTest.java
index 49a1552534..6087f4b2d2 100644
--- a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2SuccessHandlerTest.java
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/LoginOAuth2SuccessHandlerTest.java
@@ -1,6 +1,11 @@
// SPDX-License-Identifier: MIT
package com.mercedesbenz.sechub.spring.security;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
@@ -10,7 +15,6 @@
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatcher;
import org.mockito.ArgumentMatchers;
-import org.mockito.Mockito;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
@@ -33,42 +37,40 @@ class LoginOAuth2SuccessHandlerTest {
private static final String BASE_PATH = "/";
private static final String REDIRECT_URI = "https://example.org/redirect-uri";
- private static final LoginOAuth2Properties loginOAuth2Properties = Mockito.mock();
- private static final OAuth2AuthorizedClientService oAuth2AuthorizedClientService = Mockito.mock();
- private static final AES256Encryption aes256Encryption = Mockito.mock();
- private static final HttpServletRequest httpServletRequest = Mockito.mock();
- private static final HttpServletResponse httpServletResponse = Mockito.mock();
- private static final Authentication authentication = Mockito.mock();
- private static final OAuth2AuthorizedClient oauth2AuthorizedClient = Mockito.mock();
- private static final OAuth2AccessToken oAuth2AccessToken = Mockito.mock();
- private static final LoginOAuth2SuccessHandler loginOAuth2SuccessHandler = new LoginOAuth2SuccessHandler(loginOAuth2Properties,
- oAuth2AuthorizedClientService, aes256Encryption, REDIRECT_URI);
+ private static final OAuth2AuthorizedClientService oAuth2AuthorizedClientService = mock();
+ private static final AES256Encryption aes256Encryption = mock();
+ private static final HttpServletRequest httpServletRequest = mock();
+ private static final HttpServletResponse httpServletResponse = mock();
+ private static final Authentication authentication = mock();
+ private static final OAuth2AuthorizedClient oauth2AuthorizedClient = mock();
+ private static final OAuth2AccessToken oAuth2AccessToken = mock();
+ private static final LoginOAuth2SuccessHandler loginOAuth2SuccessHandler = new LoginOAuth2SuccessHandler(PROVIDER, oAuth2AuthorizedClientService,
+ aes256Encryption, REDIRECT_URI);
@BeforeEach
void beforeEach() {
- Mockito.reset(aes256Encryption, httpServletResponse);
- Mockito.when(loginOAuth2Properties.getProvider()).thenReturn(PROVIDER);
- Mockito.when(aes256Encryption.encrypt(ArgumentMatchers.anyString())).thenReturn(ENCRYPTED_ACCESS_TOKEN_BYTES);
- Mockito.when(oAuth2AuthorizedClientService.loadAuthorizedClient(PROVIDER, PRINCIPAL)).thenReturn(oauth2AuthorizedClient);
- Mockito.when(authentication.getName()).thenReturn(PRINCIPAL);
- Mockito.when(oauth2AuthorizedClient.getAccessToken()).thenReturn(oAuth2AccessToken);
- Mockito.when(oAuth2AccessToken.getTokenValue()).thenReturn(ACCESS_TOKEN);
+ reset(aes256Encryption, httpServletResponse);
+ when(aes256Encryption.encrypt(ArgumentMatchers.anyString())).thenReturn(ENCRYPTED_ACCESS_TOKEN_BYTES);
+ when(oAuth2AuthorizedClientService.loadAuthorizedClient(PROVIDER, PRINCIPAL)).thenReturn(oauth2AuthorizedClient);
+ when(authentication.getName()).thenReturn(PRINCIPAL);
+ when(oauth2AuthorizedClient.getAccessToken()).thenReturn(oAuth2AccessToken);
+ when(oAuth2AccessToken.getTokenValue()).thenReturn(ACCESS_TOKEN);
}
@Test
void on_authentication_success_sends_a_valid_redirect_containing_the_encrypted_access_token_cookie() throws IOException {
/* prepare */
Instant now = Instant.now();
- Mockito.when(oAuth2AccessToken.getIssuedAt()).thenReturn(now);
- Mockito.when(oAuth2AccessToken.getExpiresAt()).thenReturn(now.plusSeconds(60));
+ when(oAuth2AccessToken.getIssuedAt()).thenReturn(now);
+ when(oAuth2AccessToken.getExpiresAt()).thenReturn(now.plusSeconds(60));
int expirySeconds = 60;
/* execute */
loginOAuth2SuccessHandler.onAuthenticationSuccess(httpServletRequest, httpServletResponse, authentication);
/* test */
- Mockito.verify(aes256Encryption).encrypt(ACCESS_TOKEN);
- Mockito.verify(httpServletResponse).sendRedirect(REDIRECT_URI);
+ verify(aes256Encryption).encrypt(ACCESS_TOKEN);
+ verify(httpServletResponse).sendRedirect(REDIRECT_URI);
ArgumentMatcher argumentMatcher = cookie -> {
/* @formatter:off */
if (!ACCESS_TOKEN_KEY.equals(cookie.getName())) return false;
@@ -79,21 +81,21 @@ void on_authentication_success_sends_a_valid_redirect_containing_the_encrypted_a
return BASE_PATH.equals(cookie.getPath());
/* @formatter:on */
};
- Mockito.verify(httpServletResponse).addCookie(ArgumentMatchers.argThat(argumentMatcher));
+ verify(httpServletResponse).addCookie(ArgumentMatchers.argThat(argumentMatcher));
}
@Test
void on_authentication_success_assumes_default_expiry_when_expires_at_is_null() throws IOException {
/* prepare */
Instant now = Instant.now();
- Mockito.when(oAuth2AccessToken.getIssuedAt()).thenReturn(now);
- Mockito.when(oAuth2AccessToken.getExpiresAt()).thenReturn(null);
+ when(oAuth2AccessToken.getIssuedAt()).thenReturn(now);
+ when(oAuth2AccessToken.getExpiresAt()).thenReturn(null);
/* execute */
loginOAuth2SuccessHandler.onAuthenticationSuccess(httpServletRequest, httpServletResponse, authentication);
/* test */
ArgumentMatcher argumentMatcher = cookie -> cookie.getMaxAge() == DEFAULT_EXPIRY_SECONDS;
- Mockito.verify(httpServletResponse).addCookie(ArgumentMatchers.argThat(argumentMatcher));
+ verify(httpServletResponse).addCookie(ArgumentMatchers.argThat(argumentMatcher));
}
}
\ 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
index 1635c03529..203511d7f0 100644
--- 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
@@ -12,7 +12,6 @@
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;
@@ -54,7 +53,6 @@
*/
@WebMvcTest
@TestPropertySource(locations = "classpath:application-jwt-test.yml", factory = YamlPropertyLoaderFactory.class)
-@ActiveProfiles("oauth2")
class OAuth2JwtIntegrationTest {
/**
@@ -133,7 +131,7 @@ void api_user_is_accessible_as_user() throws Exception {
}
@Configuration
- @Import({ TestSecurityConfiguration.class, TestOAuth2JwtSecurityConfiguration.class, OAuth2JwtPropertiesConfiguration.class })
+ @Import({ TestSecurityConfiguration.class, TestOAuth2JwtSecurityConfiguration.class, AES256Encryption.class })
static class TestConfig {
@Bean
@@ -141,4 +139,5 @@ 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
deleted file mode 100644
index e89905dc2b..0000000000
--- a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2JwtPropertiesTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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 properties;
-
- OAuth2JwtPropertiesTest(@Autowired OAuth2JwtProperties properties) {
- this.properties = properties;
- }
-
- @Test
- void construct_properties_with_jwt_enabled_succeeds() {
- assertThat(properties.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.server.oauth2.jwt.jwk-set-uri' must not be null");
- }
- /* @formatter:on */
-
- @Configuration
- @Import(OAuth2JwtPropertiesConfiguration.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
index 56cd493985..a118ad6c0b 100644
--- 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
@@ -12,7 +12,6 @@
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;
@@ -56,7 +55,6 @@
@SuppressWarnings("JavadocReference")
@WebMvcTest
@TestPropertySource(locations = "classpath:application-opaque-token-test.yml", factory = YamlPropertyLoaderFactory.class)
-@ActiveProfiles("oauth2")
class OAuth2OpaqueTokenIntegrationTest {
/**
@@ -135,7 +133,7 @@ void api_user_is_accessible_as_user() throws Exception {
}
@Configuration
- @Import({ TestSecurityConfiguration.class, TestOAuth2OpaqueTokenSecurityConfiguration.class, OAuth2OpaqueTokenPropertiesConfiguration.class })
+ @Import({ TestSecurityConfiguration.class, TestOAuth2OpaqueTokenSecurityConfiguration.class, AES256Encryption.class })
static class TestConfig {
@Bean
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenIntrospectorTest.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenIntrospectorTest.java
index dccfe0fee9..926346af00 100644
--- a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenIntrospectorTest.java
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenIntrospectorTest.java
@@ -93,7 +93,8 @@ void introspect_with_null_response_fails() {
void introspect_with_inactive_token_fails() {
/* prepare */
OAuth2OpaqueTokenIntrospectionResponse OAuth2OpaqueTokenIntrospectionResponse = createOpaqueTokenResponse(Boolean.FALSE, null);
- when(restTemplate.postForObject(eq(INTROSPECTION_URI), any(), eq(OAuth2OpaqueTokenIntrospectionResponse.class))).thenReturn(OAuth2OpaqueTokenIntrospectionResponse);
+ when(restTemplate.postForObject(eq(INTROSPECTION_URI), any(), eq(OAuth2OpaqueTokenIntrospectionResponse.class)))
+ .thenReturn(OAuth2OpaqueTokenIntrospectionResponse);
/* execute & assert */
/* @formatter:off */
@@ -108,7 +109,8 @@ void introspect_with_valid_token_succeeds() {
/* prepare */
long expiresAt = 3600L;
OAuth2OpaqueTokenIntrospectionResponse OAuth2OpaqueTokenIntrospectionResponse = createOpaqueTokenResponse(Boolean.TRUE, expiresAt);
- when(restTemplate.postForObject(eq(INTROSPECTION_URI), any(), eq(OAuth2OpaqueTokenIntrospectionResponse.class))).thenReturn(OAuth2OpaqueTokenIntrospectionResponse);
+ when(restTemplate.postForObject(eq(INTROSPECTION_URI), any(), eq(OAuth2OpaqueTokenIntrospectionResponse.class)))
+ .thenReturn(OAuth2OpaqueTokenIntrospectionResponse);
Collection extends GrantedAuthority> authorities = Set.of(new SimpleGrantedAuthority(TestRoles.USER));
when(userDetailsService.loadUserByUsername(SUBJECT)).thenReturn(new TestUserDetails(authorities, SUBJECT));
@@ -134,7 +136,8 @@ void introspect_with_null_expires_at_constructs_principal_with_default_expires_a
/* prepare */
Instant now = Instant.now();
OAuth2OpaqueTokenIntrospectionResponse OAuth2OpaqueTokenIntrospectionResponse = createOpaqueTokenResponse(Boolean.TRUE, null);
- when(restTemplate.postForObject(eq(INTROSPECTION_URI), any(), eq(OAuth2OpaqueTokenIntrospectionResponse.class))).thenReturn(OAuth2OpaqueTokenIntrospectionResponse);
+ when(restTemplate.postForObject(eq(INTROSPECTION_URI), any(), eq(OAuth2OpaqueTokenIntrospectionResponse.class)))
+ .thenReturn(OAuth2OpaqueTokenIntrospectionResponse);
Collection extends GrantedAuthority> authorities = Set.of(new SimpleGrantedAuthority(TestRoles.USER));
when(userDetailsService.loadUserByUsername(SUBJECT)).thenReturn(new TestUserDetails(authorities, SUBJECT));
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
deleted file mode 100644
index c3d6be2503..0000000000
--- a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/OAuth2OpaqueTokenPropertiesTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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 properties;
-
- OAuth2OpaqueTokenPropertiesTest(@Autowired OAuth2OpaqueTokenProperties properties) {
- this.properties = properties;
- }
-
- @Test
- void construct_properties_with_opaque_token_enabled_succeeds() {
- assertThat(properties.getIntrospectionUri()).isEqualTo("https://example.org/introspection-uri");
- assertThat(properties.getClientId()).isEqualTo("example-client-id");
- assertThat(properties.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, OAuth2OpaqueTokenPropertiesConfiguration.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.server.oauth2.opaque-token.introspection-uri' must not be null"),
- Arguments.of("https://example.org/introspection-uri", null, "example-client-secret", "Property 'sechub.security.server.oauth2.opaque-token.client-id' must not be null"),
- Arguments.of("https://example.org/introspection-uri", "example-client-id", null, "Property 'sechub.security.server.oauth2.opaque-token.client-secret' must not be null")
- );
- /* @formatter:on */
- }
- }
-}
\ No newline at end of file
diff --git a/sechub-web-server/src/test/java/com/mercedesbenz/sechub/webserver/security/PortAccessGuardTest.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/PortAccessGuardTest.java
similarity index 97%
rename from sechub-web-server/src/test/java/com/mercedesbenz/sechub/webserver/security/PortAccessGuardTest.java
rename to sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/PortAccessGuardTest.java
index 428d52ce64..c71fd3a24f 100644
--- a/sechub-web-server/src/test/java/com/mercedesbenz/sechub/webserver/security/PortAccessGuardTest.java
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/PortAccessGuardTest.java
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-package com.mercedesbenz.sechub.webserver.security;
+package com.mercedesbenz.sechub.spring.security;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
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
index ffd68f2c7b..aa0b1371b1 100644
--- 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
@@ -27,7 +27,7 @@
*
* Note: Here we don't test the integration of OAuth2 or Basic Auth. For
* that, see {@link OAuth2JwtIntegrationTest} or
- * {@link OAuth2OpaqueTokenIntegrationTest}. T his test class is only concerned
+ * {@link OAuth2OpaqueTokenIntegrationTest}. This test class is only concerned
* with verifying if the security rules are correctly applied on an abstract
* level.
*
diff --git a/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/SecurityPropertiesTest.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/SecurityPropertiesTest.java
new file mode 100644
index 0000000000..1602d9e685
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/SecurityPropertiesTest.java
@@ -0,0 +1,431 @@
+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 static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Set;
+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.junit.jupiter.params.provider.ValueSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.TestPropertySource;
+
+import com.mercedesbenz.sechub.testframework.spring.YamlPropertyLoaderFactory;
+
+@SpringBootTest
+@TestPropertySource(locations = "classpath:application-security-properties-test.yaml", factory = YamlPropertyLoaderFactory.class)
+class SecurityPropertiesTest {
+
+ private final SecurityProperties properties;
+
+ @Autowired
+ SecurityPropertiesTest(SecurityProperties properties) {
+ this.properties = properties;
+ }
+
+ @Test
+ void construct_security_properties_with_valid_properties_file_succeeds() {
+ SecurityProperties.Server server = properties.getServer();
+ assertThat(server).isNotNull();
+ assertThat(server.isOAuth2ModeEnabled()).isTrue();
+ assertThat(server.isClassicModeEnabled()).isTrue();
+ SecurityProperties.Server.OAuth2 oAuth2 = server.getOAuth2();
+ assertThat(oAuth2).isNotNull();
+ assertThat(oAuth2.getMode()).isEqualTo("jwt");
+ assertThat(oAuth2.isJwtModeEnabled()).isTrue();
+ assertThat(oAuth2.isOpaqueTokenModeEnabled()).isFalse();
+ SecurityProperties.Server.OAuth2.Jwt jwt = oAuth2.getJwt();
+ assertThat(jwt).isNotNull();
+ assertThat(jwt.getJwkSetUri()).isEqualTo("https://example.org/jwk-set-uri");
+ SecurityProperties.Server.OAuth2.OpaqueToken opaqueToken = oAuth2.getOpaqueToken();
+ assertThat(opaqueToken).isNull();
+
+ SecurityProperties.Login login = properties.getLogin();
+ assertThat(login).isNotNull();
+ assertThat(login.isEnabled()).isTrue();
+ assertThat(login.getLoginPage()).isEqualTo("/login");
+ assertThat(login.getRedirectUri()).isEqualTo("example.org/redirect-uri");
+ assertThat(login.getModes()).containsExactly("oauth2", "classic");
+ SecurityProperties.Login.OAuth2 loginOAuth2 = login.getOAuth2();
+ assertThat(loginOAuth2).isNotNull();
+ assertThat(loginOAuth2.getClientId()).isEqualTo("example-client-id");
+ assertThat(loginOAuth2.getClientSecret()).isEqualTo("example-client-secret");
+ assertThat(loginOAuth2.getProvider()).isEqualTo("example-provider");
+ assertThat(loginOAuth2.getRedirectUri()).isEqualTo("https://example.org/redirect-uri");
+ assertThat(loginOAuth2.getIssuerUri()).isEqualTo("https://example.org/issuer-uri");
+ assertThat(loginOAuth2.getAuthorizationUri()).isEqualTo("https://example.org/authorization-uri");
+ assertThat(loginOAuth2.getTokenUri()).isEqualTo("https://example.org/token-uri");
+ assertThat(loginOAuth2.getUserInfoUri()).isEqualTo("https://example.org/user-info-uri");
+ assertThat(loginOAuth2.getJwkSetUri()).isEqualTo("https://example.org/jwk-set-uri");
+
+ SecurityProperties.Encryption encryption = properties.getEncryption();
+ assertThat(encryption).isNotNull();
+ assertThat(encryption.getSecretKey()).isEqualTo("test-test-test-test-test-test-32");
+ }
+
+ @Test
+ void construct_security_properties_with_null_server_is_ok() {
+ /* execute + test */
+ assertDoesNotThrow(() -> new SecurityProperties(null, null, null));
+ }
+
+ @Test
+ void construct_security_properties_with_null_login_is_ok() {
+ /* execute + test */
+ assertDoesNotThrow(() -> new SecurityProperties(mock(), null, null));
+ }
+
+ @Test
+ void construct_security_properties_with_login_enabled_and_null_encryption_fails() {
+ /* prepare */
+ SecurityProperties.Login loginMock = mock();
+ when(loginMock.isEnabled()).thenReturn(true);
+
+ /* execute + test */
+ /* @formatter:off */
+ assertThatThrownBy(() -> new SecurityProperties(mock(), loginMock, null))
+ .isInstanceOf(NullPointerException.class)
+ .hasMessageContaining("The property 'sechub.security.encryption' must not be null");
+ /* @formatter:on */
+ }
+
+ @Test
+ void construct_server_properties_with_valid_arguments_succeeds() {
+ /* prepare */
+ Set modes = Set.of("oauth2", "classic");
+ SecurityProperties.Server.OAuth2 oAuth2 = mock();
+
+ /* execute */
+ SecurityProperties.Server server = new SecurityProperties.Server(modes, oAuth2);
+
+ /* test */
+ assertThat(server.getModes()).isEqualTo(modes);
+ assertThat(server.isOAuth2ModeEnabled()).isTrue();
+ assertThat(server.isClassicModeEnabled()).isTrue();
+ assertThat(server.getOAuth2()).isEqualTo(oAuth2);
+ }
+
+ @Test
+ void construct_server_properties_with_null_modes_fails() {
+ /* execute + test */
+ /* @formatter:off */
+ assertThatThrownBy(() -> new SecurityProperties.Server(null, mock()))
+ .isInstanceOf(NullPointerException.class)
+ .hasMessageContaining("The property 'sechub.security.server.modes' must not be null");
+ /* @formatter:on */
+ }
+
+ @Test
+ void construct_server_properties_with_empty_modes_fails() {
+ /* prepare */
+ Set modes = Set.of();
+
+ /* execute + test */
+ /* @formatter:off */
+ assertThatThrownBy(() -> new SecurityProperties.Server(modes, mock()))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("The property 'sechub.security.server.modes' must at least include 'oauth2' or 'classic' mode");
+ /* @formatter:on */
+ }
+
+ @Test
+ void construct_server_properties_with_invalid_modes_fails() {
+ /* prepare */
+ Set modes = Set.of("invalid");
+
+ /* execute + test */
+ /* @formatter:off */
+ assertThatThrownBy(() -> new SecurityProperties.Server(modes, mock()))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("The property 'sechub.security.server.modes' allows only 'oauth2' or 'classic' mode");
+ /* @formatter:on */
+ }
+
+ @Test
+ void construct_server_oauth2_properties_with_jwt_mode_succeeds() {
+ /* prepare */
+ String mode = "jwt";
+ SecurityProperties.Server.OAuth2.Jwt jwt = mock();
+
+ /* execute */
+ SecurityProperties.Server.OAuth2 oAuth2 = new SecurityProperties.Server.OAuth2(mode, jwt, null);
+
+ /* test */
+ assertThat(oAuth2.getMode()).isEqualTo(mode);
+ assertThat(oAuth2.isJwtModeEnabled()).isTrue();
+ assertThat(oAuth2.isOpaqueTokenModeEnabled()).isFalse();
+ assertThat(oAuth2.getJwt()).isEqualTo(jwt);
+ assertThat(oAuth2.getOpaqueToken()).isNull();
+ }
+
+ @Test
+ void construct_server_oauth2_properties_with_opaque_token_mode_succeeds() {
+ /* prepare */
+ String mode = "opaque-token";
+ SecurityProperties.Server.OAuth2.OpaqueToken opaqueToken = mock();
+
+ /* execute */
+ SecurityProperties.Server.OAuth2 oAuth2 = new SecurityProperties.Server.OAuth2(mode, null, opaqueToken);
+
+ /* test */
+ assertThat(oAuth2.getMode()).isEqualTo(mode);
+ assertThat(oAuth2.isJwtModeEnabled()).isFalse();
+ assertThat(oAuth2.isOpaqueTokenModeEnabled()).isTrue();
+ assertThat(oAuth2.getJwt()).isNull();
+ assertThat(oAuth2.getOpaqueToken()).isEqualTo(opaqueToken);
+ }
+
+ @Test
+ void construct_server_oauth2_properties_with_null_mode_fails() {
+ /* execute + test */
+ /* @formatter:off */
+ assertThatThrownBy(() -> new SecurityProperties.Server.OAuth2(null, mock(), mock()))
+ .isInstanceOf(NullPointerException.class)
+ .hasMessageContaining("The property 'sechub.security.server.oauth2.mode' must not be null");
+ /* @formatter:on */
+ }
+
+ @Test
+ void construct_server_oauth2_properties_with_invalid_mode_fails() {
+ /* execute + test */
+ /* @formatter:off */
+ assertThatThrownBy(() -> new SecurityProperties.Server.OAuth2("invalid", mock(), mock()))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("The property 'sechub.security.server.oauth2.mode' allows only 'jwt' or 'opaque-token' mode");
+ /* @formatter:on */
+ }
+
+ @Test
+ void construct_jwt_properties_with_valid_arguments_succeeds() {
+ /* prepare */
+ String jwkSetUri = "https://example.org/jwk-set-uri";
+
+ /* execute */
+ SecurityProperties.Server.OAuth2.Jwt jwt = new SecurityProperties.Server.OAuth2.Jwt(jwkSetUri);
+
+ /* test */
+ assertThat(jwt.getJwkSetUri()).isEqualTo(jwkSetUri);
+ }
+
+ @Test
+ void construct_jwt_properties_with_null_jwk_set_uri_fails() {
+ /* execute + test */
+ /* @formatter:off */
+ assertThatThrownBy(() -> new SecurityProperties.Server.OAuth2.Jwt(null))
+ .isInstanceOf(NullPointerException.class)
+ .hasMessageContaining("The property 'sechub.security.server.oauth2.jwt.jwk-set-uri' must not be null");
+ /* @formatter:on */
+ }
+
+ @Test
+ void construct_opaque_token_properties_with_valid_arguments_succeeds() {
+ /* prepare */
+ String introspectionUri = "https://example.org/introspection-uri";
+ String clientId = "example-client-id";
+ String clientSecret = "example-client-secret";
+
+ /* execute */
+ SecurityProperties.Server.OAuth2.OpaqueToken opaqueToken = new SecurityProperties.Server.OAuth2.OpaqueToken(introspectionUri, clientId, clientSecret);
+
+ /* test */
+ assertThat(opaqueToken.getIntrospectionUri()).isEqualTo(introspectionUri);
+ assertThat(opaqueToken.getClientId()).isEqualTo(clientId);
+ assertThat(opaqueToken.getClientSecret()).isEqualTo(clientSecret);
+ }
+
+ @ParameterizedTest
+ @ArgumentsSource(InvalidOpaqueTokenPropertiesProvider.class)
+ /* @formatter:off */
+ void construct_opaque_token_properties_with_null_arguments_fails(String introspectionUri,
+ String clientId,
+ String clientSecret,
+ String errMsg) {
+ /* execute + test */
+ assertThatThrownBy(() -> new SecurityProperties.Server.OAuth2.OpaqueToken(introspectionUri, clientId, clientSecret))
+ .isInstanceOf(NullPointerException.class)
+ .hasMessageContaining(errMsg);
+ /* @formatter:on */
+ }
+
+ @Test
+ void construct_login_properties_with_valid_arguments_succeeds() {
+ /* prepare */
+ boolean enabled = true;
+ String loginPage = "/login";
+ String redirectUri = "example.org/redirect-uri";
+ Set modes = Set.of("oauth2", "classic");
+ SecurityProperties.Login.OAuth2 oAuth2 = mock();
+
+ /* execute */
+ SecurityProperties.Login login = new SecurityProperties.Login(enabled, loginPage, redirectUri, modes, oAuth2);
+
+ /* test */
+ assertThat(login.isEnabled()).isEqualTo(enabled);
+ assertThat(login.getLoginPage()).isEqualTo(loginPage);
+ assertThat(login.getRedirectUri()).isEqualTo(redirectUri);
+ assertThat(login.getModes()).isEqualTo(modes);
+ assertThat(login.getOAuth2()).isEqualTo(oAuth2);
+ }
+
+ @ParameterizedTest
+ @ArgumentsSource(InvalidLoginPropertiesProvider.class)
+ /* @formatter:off */
+ void construct_login_properties_with_null_property_fails(Boolean enabled,
+ String loginPage,
+ String redirectUri,
+ Set modes,
+ SecurityProperties.Login.OAuth2 oAuth2,
+ String errMsg) {
+ /* execute + test */
+ assertThatThrownBy(() -> new SecurityProperties.Login(enabled, loginPage, redirectUri, modes, oAuth2))
+ .isInstanceOf(NullPointerException.class)
+ .hasMessageContaining(errMsg);
+ /* @formatter:on */
+ }
+
+ @Test
+ void construct_login_oauth2_properties_with_valid_arguments_succeeds() {
+ /* prepare */
+ String clientId = "example-client-id";
+ String clientSecret = "example-client-secret";
+ String provider = "example-provider";
+ String redirectUri = "https://example.org/redirect-uri";
+ String issuerUri = "https://example.org/issuer-uri";
+ String authorizationUri = "https://example.org/authorization-uri";
+ String tokenUri = "https://example.org/token-uri";
+ String userInfoUri = "https://example.org/user-info-uri";
+ String jwkSetUri = "https://example.org/jwk-set-uri";
+
+ /* execute */
+ SecurityProperties.Login.OAuth2 oAuth2 = new SecurityProperties.Login.OAuth2(clientId, clientSecret, provider, redirectUri, issuerUri, authorizationUri,
+ tokenUri, userInfoUri, jwkSetUri);
+
+ /* test */
+ assertThat(oAuth2.getClientId()).isEqualTo(clientId);
+ assertThat(oAuth2.getClientSecret()).isEqualTo(clientSecret);
+ assertThat(oAuth2.getProvider()).isEqualTo(provider);
+ assertThat(oAuth2.getRedirectUri()).isEqualTo(redirectUri);
+ assertThat(oAuth2.getIssuerUri()).isEqualTo(issuerUri);
+ assertThat(oAuth2.getAuthorizationUri()).isEqualTo(authorizationUri);
+ assertThat(oAuth2.getTokenUri()).isEqualTo(tokenUri);
+ assertThat(oAuth2.getUserInfoUri()).isEqualTo(userInfoUri);
+ assertThat(oAuth2.getJwkSetUri()).isEqualTo(jwkSetUri);
+ }
+
+ @ParameterizedTest
+ @ArgumentsSource(InvalidLoginOAuth2PropertiesProvider.class)
+ /* @formatter:off */
+ void construct_login_oauth2_properties_with_null_arguments_fails(String clientId,
+ String clientSecret,
+ String provider,
+ String redirectUri,
+ String issuerUri,
+ String authorizationUri,
+ String tokenUri,
+ String userInfoUri,
+ String jwkSetUri,
+ String errMsg) {
+ /* execute + test */
+ assertThatThrownBy(() -> new SecurityProperties.Login.OAuth2(clientId, clientSecret, provider, redirectUri, issuerUri, authorizationUri, tokenUri, userInfoUri, jwkSetUri))
+ .isInstanceOf(NullPointerException.class)
+ .hasMessageContaining(errMsg);
+ /* @formatter:on */
+ }
+
+ @Test
+ void construct_encryption_properties_with_valid_arguments_succeeds() {
+ /* prepare */
+ String secretKey = "test-test-test-test-test-test-32";
+
+ /* execute */
+ SecurityProperties.Encryption encryption = new SecurityProperties.Encryption(secretKey);
+
+ /* test */
+ assertThat(encryption.getSecretKey()).isEqualTo(secretKey);
+ }
+
+ @Test
+ void construct_encryption_properties_with_null_secret_key_fails() {
+ /* execute + test */
+ /* @formatter:off */
+ assertThatThrownBy(() -> new SecurityProperties.Encryption(null))
+ .isInstanceOf(NullPointerException.class)
+ .hasMessageContaining("The property 'sechub.security.encryption.secret-key' must not be null");
+ /* @formatter:on */
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = { "", "1", "est-test-test-test-test-test-31", "-test-test-test-test-test-test-33" })
+ void construct_aes256encryption_properties_with_non_256_bit_long_secret_key_fails(String secretKey) {
+ /* @formatter:off */
+ /* execute & test */
+ assertThatThrownBy(() -> new SecurityProperties.Encryption(secretKey))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("The property sechub.security.encryption.secret-key must be a 256-bit string");
+ /* @formatter:on */
+ }
+
+ @Configuration
+ @EnableConfigurationProperties(SecurityProperties.class)
+ static class TestConfiguration {
+
+ }
+
+ private static class InvalidOpaqueTokenPropertiesProvider implements ArgumentsProvider {
+ /* @formatter:off */
+ @Override
+ public Stream extends Arguments> provideArguments(ExtensionContext context) throws Exception {
+ return Stream.of(
+ Arguments.of(null, "example-client-id", "example-client-secret", "The property 'sechub.security.server.oauth2.opaque-token.introspection-uri' must not be null"),
+ Arguments.of("https://example.org/introspection-uri", null, "example-client-secret", "The property 'sechub.security.server.oauth2.opaque-token.client-id' must not be null"),
+ Arguments.of("https://example.org/introspection-uri", "example-client-id", null, "The property 'sechub.security.server.oauth2.opaque-token.client-secret' must not be null")
+ );
+ }
+ /* @formatter:on */
+ }
+
+ private static class InvalidLoginPropertiesProvider implements ArgumentsProvider {
+ /* @formatter:off */
+ @Override
+ public Stream extends Arguments> provideArguments(ExtensionContext context) throws Exception {
+ return Stream.of(
+ Arguments.of(null, "/login", "example.org/redirect-uri", Set.of("oauth2", "classic"), mock(SecurityProperties.Login.OAuth2.class), "The property 'sechub.security.login.enabled' must not be null"),
+ Arguments.of(true, null, "example.org/redirect-uri", Set.of("oauth2", "classic"), mock(SecurityProperties.Login.OAuth2.class), "The property 'sechub.security.login.login-page' must not be null"),
+ Arguments.of(true, "/login", null, Set.of("oauth2", "classic"), mock(SecurityProperties.Login.OAuth2.class), "The property 'sechub.security.login.redirect-uri' must not be null"),
+ Arguments.of(true, "/login", "example.org/redirect-uri", null, mock(SecurityProperties.Login.OAuth2.class), "The property 'sechub.security.login.modes' must not be null"),
+ Arguments.of(true, "/login", "example.org/redirect-uri", Set.of("oauth2", "classic"), null, "The property 'sechub.security.login.oauth2' must not be null")
+ );
+ }
+ /* @formatter:on */
+ }
+
+ private static class InvalidLoginOAuth2PropertiesProvider implements ArgumentsProvider {
+ /* @formatter:off */
+ @Override
+ public Stream extends Arguments> provideArguments(ExtensionContext context) throws Exception {
+ return Stream.of(
+ Arguments.of(null, "example-client-secret", "example-provider", "https://example.org/redirect-uri", "https://example.org/issuer-uri", "https://example.org/authorization-uri", "https://example.org/token-uri", "https://example.org/user-info-uri", "https://example.org/jwk-set-uri", "The property 'sechub.security.login.oauth2.client-id' must not be null"),
+ Arguments.of("example-client-id", null, "example-provider", "https://example.org/redirect-uri", "https://example.org/issuer-uri", "https://example.org/authorization-uri", "https://example.org/token-uri", "https://example.org/user-info-uri", "https://example.org/jwk-set-uri", "The property 'sechub.security.login.oauth2.client-secret' must not be null"),
+ Arguments.of("example-client-id", "example-client-secret", null, "https://example.org/redirect-uri", "https://example.org/issuer-uri", "https://example.org/authorization-uri", "https://example.org/token-uri", "https://example.org/user-info-uri", "https://example.org/jwk-set-uri", "The property 'sechub.security.login.oauth2.provider' must not be null"),
+ Arguments.of("example-client-id", "example-client-secret", "example-provider", null, "https://example.org/issuer-uri", "https://example.org/authorization-uri", "https://example.org/token-uri", "https://example.org/user-info-uri", "https://example.org/jwk-set-uri", "The property 'sechub.security.login.oauth2.redirect-uri' must not be null"),
+ Arguments.of("example-client-id", "example-client-secret", "example-provider", "https://example.org/redirect-uri", null, "https://example.org/authorization-uri", "https://example.org/token-uri", "https://example.org/user-info-uri", "https://example.org/jwk-set-uri", "The property 'sechub.security.login.oauth2.issuer-uri' must not be null"),
+ Arguments.of("example-client-id", "example-client-secret", "example-provider", "https://example.org/redirect-uri", "https://example.org/issuer-uri", null, "https://example.org/token-uri", "https://example.org/user-info-uri", "https://example.org/jwk-set-uri", "The property 'sechub.security.login.oauth2.authorization-uri' must not be null"),
+ Arguments.of("example-client-id", "example-client-secret", "example-provider", "https://example.org/redirect-uri", "https://example.org/issuer-uri", "https://example.org/authorization-uri", null, "https://example.org/user-info-uri", "https://example.org/jwk-set-uri", "The property 'sechub.security.login.oauth2.token-uri' must not be null"),
+ Arguments.of("example-client-id", "example-client-secret", "example-provider", "https://example.org/redirect-uri", "https://example.org/issuer-uri", "https://example.org/authorization-uri", "https://example.org/token-uri", null, "https://example.org/jwk-set-uri", "The property 'sechub.security.login.oauth2.user-info-uri' must not be null"),
+ Arguments.of("example-client-id", "example-client-secret", "example-provider", "https://example.org/redirect-uri", "https://example.org/issuer-uri", "https://example.org/authorization-uri", "https://example.org/token-uri", "https://example.org/user-info-uri", null, "The property 'sechub.security.login.oauth2.jwk-set-uri' 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/TestOAuth2JwtSecurityConfiguration.java b/sechub-commons-security-spring/src/test/java/com/mercedesbenz/sechub/spring/security/TestOAuth2JwtSecurityConfiguration.java
index 06395eb7a0..2775046906 100644
--- 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
@@ -20,9 +20,9 @@
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.BadJwtException;
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
@@ -85,7 +85,7 @@ JwtDecoder jwtDecoder() {
return builder.subject(USER_ID).build();
}
- throw new JwtException("Invalid JWT token");
+ throw new BadJwtException("Invalid JWT token");
});
return jwtDecoder;
}
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
index 35be940dd6..0f4abd4c79 100644
--- 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
@@ -58,9 +58,11 @@ public class TestOAuth2OpaqueTokenSecurityConfiguration {
/* @formatter:off */
@Autowired
TestOAuth2OpaqueTokenSecurityConfiguration(RestTemplate restTemplate,
- OAuth2OpaqueTokenProperties oAuth2OpaqueTokenProperties) {
+ SecurityProperties securityProperties) {
- when(restTemplate.postForObject(eq(oAuth2OpaqueTokenProperties.getIntrospectionUri()), any(), eq(OAuth2OpaqueTokenIntrospectionResponse.class))).thenAnswer(invocation -> {
+ String introspectionUri = securityProperties.getServer().getOAuth2().getOpaqueToken().getIntrospectionUri();
+
+ when(restTemplate.postForObject(eq(introspectionUri), any(), eq(OAuth2OpaqueTokenIntrospectionResponse.class))).thenAnswer(invocation -> {
HttpEntity> request = invocation.getArgument(1);
String token = requireNonNull(request.getBody()).getFirst(TOKEN);
boolean isActive = false;
@@ -108,9 +110,9 @@ public class TestOAuth2OpaqueTokenSecurityConfiguration {
/**
* 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 OAuth2OpaqueTokenIntrospector}
- * component. Depending on the user id, the {@link TestUserDetails} object will
- * contain the corresponding authorities.
+ * is determined by the {@link OAuth2OpaqueTokenIntrospector} component.
+ * Depending on the user id, the {@link TestUserDetails} object will contain the
+ * corresponding authorities.
*/
@Bean
UserDetailsService userDetailsService() {
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
index 0b44ca475e..def27107a2 100644
--- 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
@@ -5,7 +5,6 @@
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;
@@ -35,22 +34,11 @@
@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 */
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
index 64bdc01153..2daee34edc 100644
--- 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
@@ -51,4 +51,5 @@ String errorPage() {
String actuator() {
return OK;
}
+
}
diff --git a/sechub-commons-security-spring/src/test/resources/application-aes256-encryption-test.yaml b/sechub-commons-security-spring/src/test/resources/application-aes256-encryption-test.yaml
deleted file mode 100644
index e96a515e4b..0000000000
--- a/sechub-commons-security-spring/src/test/resources/application-aes256-encryption-test.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-sechub:
- security:
- encryption:
- secret-key: test-test-test-test-test-test-32
\ No newline at end of file
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
index 2a1b6aad0b..5b1a37cf9b 100644
--- a/sechub-commons-security-spring/src/test/resources/application-jwt-test.yml
+++ b/sechub-commons-security-spring/src/test/resources/application-jwt-test.yml
@@ -3,7 +3,12 @@
sechub:
security:
server:
+ modes:
+ - oauth2
+ - classic
oauth2:
- mode: JWT
+ mode: jwt
jwt:
jwk-set-uri: https://example.org/jwk-set-uri
+ encryption:
+ secret-key: test-test-test-test-test-test-32
\ No newline at end of file
diff --git a/sechub-commons-security-spring/src/test/resources/application-login-oauth2-test.yaml b/sechub-commons-security-spring/src/test/resources/application-login-oauth2-test.yaml
deleted file mode 100644
index f5c83a2c20..0000000000
--- a/sechub-commons-security-spring/src/test/resources/application-login-oauth2-test.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-sechub:
- security:
- login:
- enabled: true
- redirect-uri: example.org/redirect-uri
- modes:
- - oauth2
- oauth2:
- client-id: client-id
- client-secret: client-secret
- provider: provider
- redirect-uri: redirect-uri
- issuer-uri: issuer-uri
- authorization-uri: authorization-uri
- token-uri: token-uri
- user-info-uri: user-info-uri
\ No newline at end of file
diff --git a/sechub-commons-security-spring/src/test/resources/application-login-test.yaml b/sechub-commons-security-spring/src/test/resources/application-login-test.yaml
new file mode 100644
index 0000000000..4ce0b7c38a
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/resources/application-login-test.yaml
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: MIT
+
+sechub:
+ security:
+ login:
+ enabled: true
+ login-page: /login
+ public-paths:
+ - /js/**
+ - /css/**
+ - /error
+ redirect-uri: example.org/redirect-uri
+ modes:
+ - oauth2
+ - classic
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
index bdf78a45b7..3c5675eea1 100644
--- 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
@@ -3,9 +3,14 @@
sechub:
security:
server:
+ modes:
+ - oauth2
+ - classic
oauth2:
- mode: OPAQUE_TOKEN
+ mode: opaque-token
opaque-token:
introspection-uri: https://example.org/introspection-uri
client-id: example-client-id
client-secret: example-client-secret
+ encryption:
+ secret-key: test-test-test-test-test-test-32
\ No newline at end of file
diff --git a/sechub-commons-security-spring/src/test/resources/application-security-properties-test.yaml b/sechub-commons-security-spring/src/test/resources/application-security-properties-test.yaml
new file mode 100644
index 0000000000..fa53a5d6ce
--- /dev/null
+++ b/sechub-commons-security-spring/src/test/resources/application-security-properties-test.yaml
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: MIT
+
+sechub:
+ security:
+ server:
+ modes:
+ - oauth2
+ - classic
+ oauth2:
+ mode: jwt
+ jwt:
+ jwk-set-uri: https://example.org/jwk-set-uri
+ login:
+ enabled: true
+ login-page: /login
+ redirect-uri: example.org/redirect-uri
+ modes:
+ - oauth2
+ - classic
+ oauth2:
+ client-id: example-client-id
+ client-secret: example-client-secret
+ provider: example-provider
+ redirect-uri: https://example.org/redirect-uri
+ issuer-uri: https://example.org/issuer-uri
+ authorization-uri: https://example.org/authorization-uri
+ token-uri: https://example.org/token-uri
+ user-info-uri: https://example.org/user-info-uri
+ jwk-set-uri: https://example.org/jwk-set-uri
+ encryption:
+ secret-key: test-test-test-test-test-test-32
\ No newline at end of file
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
index 78f1d9c9a9..d49a0ced14 100644
--- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/TestRestDocSecurityConfiguration.java
+++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/TestRestDocSecurityConfiguration.java
@@ -1,12 +1,16 @@
// SPDX-License-Identifier: MIT
package com.mercedesbenz.sechub.restdoc;
+import com.mercedesbenz.sechub.spring.security.AES256Encryption;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
+import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.client.RestTemplate;
import com.mercedesbenz.sechub.sharedkernel.security.SecHubSecurityConfiguration;
+import static org.mockito.Mockito.mock;
+
@Import(SecHubSecurityConfiguration.class)
class TestRestDocSecurityConfiguration {
@@ -14,4 +18,5 @@ class TestRestDocSecurityConfiguration {
RestTemplate restTemplate() {
return new RestTemplate();
}
+
}
diff --git a/sechub-server/.gitignore b/sechub-server/.gitignore
index bb48f0290a..d8fe1d1e31 100644
--- a/sechub-server/.gitignore
+++ b/sechub-server/.gitignore
@@ -1,2 +1,6 @@
# Dockerfile is generated by myke
-*Dockerfile
\ No newline at end of file
+*Dockerfile
+
+# Ignore local property files
+application-local.*.yaml
+application-local.*.yml
\ No newline at end of file
diff --git a/sechub-server/build.gradle b/sechub-server/build.gradle
index 0c3a26b419..4475fc517d 100644
--- a/sechub-server/build.gradle
+++ b/sechub-server/build.gradle
@@ -14,7 +14,7 @@ dependencies {
api project(':sechub-server-core')
implementation project(':sechub-adapter') // necessary because of adapter mocking factory methods
-
+
implementation(library.flyway)
implementation(library.apache_commons_fileupload2_core)
implementation(library.apache_commons_fileupload2_jakarta)
diff --git a/sechub-web-server/src/main/resources/application-local.yml b/sechub-server/src/main/resources/application-local.yml
similarity index 100%
rename from sechub-web-server/src/main/resources/application-local.yml
rename to sechub-server/src/main/resources/application-local.yml
diff --git a/sechub-server/src/main/resources/application.yml b/sechub-server/src/main/resources/application.yml
index 8c6db5c83b..b80546a848 100644
--- a/sechub-server/src/main/resources/application.yml
+++ b/sechub-server/src/main/resources/application.yml
@@ -8,7 +8,48 @@ sechub:
security:
diffiehellman:
length: 2048 # JDK uses per default 1024, we set here to 2048 which is more secure
-
+
+ # TODO: Albert: Move this into separate properties files
+
+ # This will configure the SecHub Server to function as a 'resource server'.
+ # In resource server mode the SecHub Server will only validate tokens and not issue them.
+ # Validation can be done in two modes: 'jwt' or 'opaque'.
+ # In jwt mode the SecHub Server will validate the tokens using public keys from the issuer.
+ # In opaque token mode the SecHub Server will call an introspection uri to validate the token for every request.
+ #
+ # For classic mode the server will accept basic auth credentials.
+ server:
+ modes:
+ - oauth2
+ - classic
+ oauth2:
+ mode: jwt
+ jwt:
+ jwk-set-uri: http://localhost:8080/realms/local-realm/protocol/openid-connect/certs
+
+ # Here the SecHub Server can be configured to offer a login page.
+ # With this the SecHub Server will become an authentication issuer providing the client with tokens that he can
+ # use to authenticate against the SecHub Server for subsequent requests.
+ login:
+ enabled: true
+ login-page: /login
+ redirect-uri: https://www.google.com
+ modes:
+ - oauth2
+ - classic
+ oauth2:
+ client-id: web-ui-server-local
+ client-secret: n8Ka5Wnkl9tv3rbPzeHt8KQ7CsEy7o5x
+ provider: keycloak
+ redirect-uri: https://localhost:8443/login/oauth2/code/keycloak
+ issuer-uri: http://localhost:8080/realms/local-realm
+ authorization-uri: http://localhost:8080/realms/local-realm/protocol/openid-connect/auth
+ token-uri: http://localhost:8080/realms/local-realm/protocol/openid-connect/token
+ user-info-uri: http://localhost:8080/realms/local-realm/protocol/openid-connect/userinfo
+ jwk-set-uri: http://localhost:8080/realms/local-realm/protocol/openid-connect/certs
+ encryption:
+ secret-key: aB3xYz8KpL9mQw2VcT1sNj7FuW4vEp0Z
+
spring:
profiles:
group:
@@ -58,6 +99,10 @@ spring:
max-request-size: 5MB
resolve-lazily: true
enabled: true
+ web:
+ resources:
+ static-locations: classpath:/static
+
# --------------------------------------
# - Security
# --------------------------------------
diff --git a/sechub-shared-kernel/build.gradle b/sechub-shared-kernel/build.gradle
index 7a7eb72a27..872ec9c577 100644
--- a/sechub-shared-kernel/build.gradle
+++ b/sechub-shared-kernel/build.gradle
@@ -12,7 +12,6 @@ plugins {
dependencies {
implementation project(':sechub-commons-security-spring')
-
api project(':sechub-commons-model')
api project(':sechub-commons-archive')
api project(':sechub-storage-core')
diff --git a/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/security/SecHubSecurityConfiguration.java b/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/security/SecHubSecurityConfiguration.java
index 0f771ba164..c05c372120 100644
--- a/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/security/SecHubSecurityConfiguration.java
+++ b/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/security/SecHubSecurityConfiguration.java
@@ -1,14 +1,12 @@
package com.mercedesbenz.sechub.sharedkernel.security;
import org.springframework.context.annotation.Configuration;
-import org.springframework.core.env.Environment;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
-import com.mercedesbenz.sechub.sharedkernel.Profiles;
import com.mercedesbenz.sechub.spring.security.AbstractSecurityConfiguration;
@Configuration
@@ -16,17 +14,6 @@
@EnableWebSecurity
public class SecHubSecurityConfiguration extends AbstractSecurityConfiguration {
- private final Environment environment;
-
- SecHubSecurityConfiguration(Environment environment) {
- this.environment = environment;
- }
-
- @Override
- protected boolean isOAuth2Enabled() {
- return environment.matchesProfiles(Profiles.OAUTH2);
- }
-
@Override
protected Customizer.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequests() {
/* @formatter:off */
diff --git a/sechub-web-server/.gitignore b/sechub-web-server/.gitignore
deleted file mode 100644
index 0fc15402e5..0000000000
--- a/sechub-web-server/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-application-local.*.yaml
-application-local.*.yml
\ No newline at end of file
diff --git a/sechub-web-server/README.adoc b/sechub-web-server/README.adoc
deleted file mode 100644
index d5b9abe2df..0000000000
--- a/sechub-web-server/README.adoc
+++ /dev/null
@@ -1,41 +0,0 @@
-// SPDX-License-Identifier: MIT
-= Web Server
-
-WARNING: Don't use this early Web Server in production.
-
-== Development
-
-=== Standalone with mocked SecHub server access
-Activate the Spring Boot Dev Profile by
-starting the application using the JVM argument:
-
-----
--Dspring.profiles.active=web-server_dev
-----
-
-The started application will
-
-- used mocked sechub access (with mocked data)
-- start with self signed certificate
-- Login credentials
- - User: user
- - Password: password
-- Class auto reloading is activated
-
-==== Integration test with running SecHub server
-----
--Dspring.profiles.active=web-server_integrationtest
-----
-
-The started application will
-
-- use running SecHub integration test server (port 8443)
-- use predefined integrationtest sechub admin credentials
-- trust generated inerationtest self signed certificate
-- start with self signed certificate
-- Login credentials
- - User: user
- - Password: password
-
-=== Access
-The Web Server is accessible at: https://localhost:4443/
diff --git a/sechub-web-server/README.md b/sechub-web-server/README.md
deleted file mode 100644
index 5eba3d9a8e..0000000000
--- a/sechub-web-server/README.md
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-# SecHub Web Server
-
-## Overview
-
-SecHub Web Server is a web-based user interface for managing and interacting with the SecHub application.
-
-## Profiles
-
-To start the application locally use the `web-server_local` profile.
-
-This will include the following profiles:
-
-- `ssl-cert-provided`: a default ssl certificate will be used by the Web Server server
-- `classic-auth-enabled`: enable classic login (for now with preconfigured credentials) at `/login/classic`)
-- `local`: includes any local configurations matching `application-local.${USER}.yml`
-
-If you want to provide local configurations, create a file named `application-local.${USER}.yml` in the `src/main/resources` directory.
-Make sure that the ${USER} part matches your system username.
-
-This will enable configurations suitable for local development and testing.
-
-## Running the application in OAuth2 Mode
-
-To run the application in OAuth2 mode, include the `oauth2` profile.
-
-Note: The `web-server_prod` profile includes the `oauth2` profile.
-
-Make sure that you either provide a valid `application-oauth2.yml` file in the `src/main/resources` directory or set the required environment variables.
-
-Example `application-oauth2.yml`:
-
-```yaml
-sechub:
- security:
- oauth2:
- client-id: example-client-id
- client-secret: example-client-secret
- provider: example-provider
- redirect-uri: {baseUrl}/login/oauth2/code/{provider}
- issuer-uri: https://sso.provider.example.org
- authorization-uri: https://sso.provider.example.org/as/authorization.oauth2
- token-uri: https://sso.provider.example.org/as/token.oauth2
- user-info-uri: https://sso.provider.example.org/idp/userinfo.openid
- jwk-set-uri: https://sso.provider.example.org/pf/JWKS
-```
-
-Alternatively, you can provide the following environment variables:
-
-```bash
-SECHUB_SECURITY_OAUTH2_CLIENT_ID=example-client-id
-SECHUB_SECURITY_OAUTH2_CLIENT_SECRET=example-client-secret
-SECHUB_SECURITY_OAUTH2_PROVIDER=example-provider
-SECHUB_SECURITY_OAUTH2_REDIRECT_URI={baseUrl}/login/oauth2/code/{provider}
-SECHUB_SECURITY_OAUTH2_ISSUER_URI=https://sso.provider.example.org
-SECHUB_SECURITY_OAUTH2_AUTHORIZATION_URI=https://sso.provider.example.org/as/authorization.oauth2
-SECHUB_SECURITY_OAUTH2_TOKEN_URI=https://sso.provider.example.org/as/token.oauth2
-SECHUB_SECURITY_OAUTH2_USER_INFO_URI=https://sso.provider.example.org/idp/userinfo.openid
-SECHUB_SECURITY_OAUTH2_JWK_SET_URI=https://sso.provider.example.org/pf/JWKS
-```
diff --git a/sechub-web-server/build.gradle b/sechub-web-server/build.gradle
deleted file mode 100644
index 38c34fc1eb..0000000000
--- a/sechub-web-server/build.gradle
+++ /dev/null
@@ -1,95 +0,0 @@
-// SPDX-License-Identifier: MIT
- /*============================================================================
- * Build file for subproject
- *
- * Root build file: "${rootProject.projectDir}/build.gradle"
- * ============================================================================
- */
-
-plugins {
- id 'org.springframework.boot' apply true
-}
-
-dependencies {
- implementation project(':sechub-commons-core')
- implementation project(':sechub-api-java')
- implementation library.springboot_starter_web
- implementation library.springboot_starter_security
- implementation library.springboot_starter_thymeleaf
- implementation library.logstashLogbackEncoder
- implementation library.thymeleaf_extras_springsecurity5
- implementation library.springboot_starter_oauth2_client
- implementation library.springboot_starter_oauth2_resource_server
- implementation library.springboot_starter_actuator
-
- testImplementation project(':sechub-testframework-spring')
- testImplementation library.springboot_starter_test
- testImplementation library.springframework_security_test
-
- developmentOnly library.springboot_devtoolssf
-}
-
-/* make eclipse task depend on ensured local certificate*/
-tasks.eclipse.dependsOn 'ensureLocalhostCertificate'
-
-/**
- * For integration tests, local develoment etc. we need a generated, private key
- * which is different for each developer, not accidently committed to git, also
- * valid on builds etc.
- *
- * This is done by dev-ensure_localhost_certificate.sh - for details refer to bash script
- */
-task ensureLocalhostCertificate(type: Exec) {
- group 'sechub'
- description 'Calling this task, will ensure a localhost certificate exists. This is necessary for development and integration tests'
-
- workingDir "${projectDir}"
-
- if (OSUtil.isWindows()){
- commandLine 'cmd', '/c', 'bash', "${projectDir}/dev-ensure_localhost_certificate.sh"
- }else{
- commandLine "${projectDir}/dev-ensure_localhost_certificate.sh"
- }
-}
-
-apply plugin: 'maven-publish'
-
-version = versionData.getWebServerVersion()
-
-publishing {
- publications {
- mavenJava(MavenPublication) {
-
- from components.java
-
- pom {
- name = 'SecHub Web Server'
- description = 'SecHub Web Server as a Spring Boot Jar. Ready to use.'
-
- scm {
- url = 'https://github.com/mercedes-benz/sechub'
- }
-
- licenses {
- license {
- name = 'MIT License'
- url = 'https://github.com/mercedes-benz/sechub/blob/master/LICENSE'
- }
- }
- }
- }
- }
-
- repositories {
- maven {
- url = project.hasProperty("mavenTargetRepoUrl") ? project.properties['mavenTargetRepoUrl'] : System.getProperty("user.home")+"/.m2/repository"
-
- if (project.hasProperty("mavenRepoUserName") && project.hasProperty("mavenRepoPassword")) {
- credentials(PasswordCredentials) {
- username project.properties['mavenRepoUserName']
- password project.properties['mavenRepoPassword']
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/sechub-web-server/dev-base.sh b/sechub-web-server/dev-base.sh
deleted file mode 100644
index 4f7ccb3026..0000000000
--- a/sechub-web-server/dev-base.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: MIT
-
-# For integration tests, local develoment etc. we need a generated, private key
-# which is different for each developer, not accidently committed to git, also
-# valid on builds etc.
-#
-# We support this by having a fixed target file path to a folder which is not tracked
-# by git (except the README.md there). So even generated private keys are not accidently added
-# to git...
-
-DEV_CERT_PATH="$(pwd)/src/main/resources/certificates-untracked"
-DEV_CERT_FILE="$DEV_CERT_PATH/generated-dev-localhost-keystore.p12"
-
-PSEUDO_PWD="123456"
-
-function createLocalhostCertifcate(){
-
- #
- # PRECONDITION: We assume a JDK is installed and so keytool is accessible!
- #
- # see https://stackoverflow.com/questions/13578134/how-to-automate-keystore-generation-using-the-java-keystore-tool-w-o-user-inter
- echo "Start creating localhost certificate"
- keytool -genkey -alias tomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore "$DEV_CERT_FILE" -validity 3650 -storepass $PSEUDO_PWD --dname "CN=localhost, OU=ID"
- echo "Created file $DEV_CERT_FILE"
-
-}
-
diff --git a/sechub-web-server/dev-create_localhost_certificate.sh b/sechub-web-server/dev-create_localhost_certificate.sh
deleted file mode 100755
index e8c0c2439b..0000000000
--- a/sechub-web-server/dev-create_localhost_certificate.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: MIT
-
-source dev-base.sh
-
-if [ -f "$DEV_CERT_FILE" ]; then
- echo "Remove old localhost certificate"
- rm "$DEV_CERT_FILE"
-fi
-createLocalhostCertifcate
\ No newline at end of file
diff --git a/sechub-web-server/dev-ensure_localhost_certificate.sh b/sechub-web-server/dev-ensure_localhost_certificate.sh
deleted file mode 100755
index 60c650b531..0000000000
--- a/sechub-web-server/dev-ensure_localhost_certificate.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: MIT
-
-source dev-base.sh
-
-if [ -f "$DEV_CERT_FILE" ]; then
- exit 0
-fi
-createLocalhostCertifcate
\ No newline at end of file
diff --git a/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/ApplicationProfiles.java b/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/ApplicationProfiles.java
deleted file mode 100644
index 278f8bb203..0000000000
--- a/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/ApplicationProfiles.java
+++ /dev/null
@@ -1,18 +0,0 @@
-// SPDX-License-Identifier: MIT
-package com.mercedesbenz.sechub.webserver;
-
-public final class ApplicationProfiles {
-
- public static final String BASIC_AUTH_MOCKED = "classic-auth-enabled";
- public static final String INTEGRATION_TEST_DATA = "integrationtest-data";
- public static final String LOCAL = "local";
- public static final String CLASSIC_AUTH_ENABLED = "classic-auth-enabled";
- public static final String OAUTH2_ENABLED = "oauth2";
- public static final String SSL_CERT_PROVIDED = "ssl-cert-provided";
- public static final String SSL_CERT_REQUIRED = "ssl-cert-required";
- public static final String TEST = "test";
-
- private ApplicationProfiles() {
- /* Prevent instantiation */
- }
-}
\ No newline at end of file
diff --git a/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/SecHubWebServerApplication.java b/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/SecHubWebServerApplication.java
deleted file mode 100644
index dbdcc6a5bb..0000000000
--- a/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/SecHubWebServerApplication.java
+++ /dev/null
@@ -1,13 +0,0 @@
-// SPDX-License-Identifier: MIT
-package com.mercedesbenz.sechub.webserver;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-@SpringBootApplication
-public class SecHubWebServerApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(SecHubWebServerApplication.class, args);
- }
-}
diff --git a/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/server/ManagementServerProperties.java b/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/server/ManagementServerProperties.java
deleted file mode 100644
index fe83077882..0000000000
--- a/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/server/ManagementServerProperties.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// SPDX-License-Identifier: MIT
-package com.mercedesbenz.sechub.webserver.server;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.bind.ConstructorBinding;
-
-@ConfigurationProperties(prefix = ManagementServerProperties.PREFIX)
-public final class ManagementServerProperties {
-
- static final String PREFIX = "management.server";
-
- private final int port;
-
- @ConstructorBinding
- ManagementServerProperties(int port) {
- this.port = port;
- }
-
- public int getPort() {
- return port;
- }
-}
\ No newline at end of file
diff --git a/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/server/ServerProperties.java b/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/server/ServerProperties.java
deleted file mode 100644
index 6e6a6e892f..0000000000
--- a/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/server/ServerProperties.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// SPDX-License-Identifier: MIT
-package com.mercedesbenz.sechub.webserver.server;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.bind.ConstructorBinding;
-
-@ConfigurationProperties(prefix = ServerProperties.PREFIX)
-public final class ServerProperties {
-
- static final String PREFIX = "server";
-
- private final int port;
-
- @ConstructorBinding
- ServerProperties(int port) {
- this.port = port;
- }
-
- public int getPort() {
- return port;
- }
-}
\ No newline at end of file
diff --git a/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/server/ServerPropertiesConfiguration.java b/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/server/ServerPropertiesConfiguration.java
deleted file mode 100644
index d283e7bd91..0000000000
--- a/sechub-web-server/src/main/java/com/mercedesbenz/sechub/webserver/server/ServerPropertiesConfiguration.java
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-License-Identifier: MIT
-package com.mercedesbenz.sechub.webserver.server;
-
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-@EnableConfigurationProperties({ ServerProperties.class, ManagementServerProperties.class })
-public class ServerPropertiesConfiguration {
-
-}
diff --git a/sechub-web-server/src/main/resources/application-classic-auth-enabled.yml b/sechub-web-server/src/main/resources/application-classic-auth-enabled.yml
deleted file mode 100644
index bb475be945..0000000000
--- a/sechub-web-server/src/main/resources/application-classic-auth-enabled.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-# SPDX-License-Identifier: MIT
-web-server:
- client:
- mocked: true
- sechub:
- ## Mocked sechub user (necessary for credentialinjection, we have no defaults...)
- userid: "mocked-user"
- apitoken: "mocked-apitoken"
-
-spring:
- security:
- user:
- name: mock-user
- password: mock-password
\ No newline at end of file
diff --git a/sechub-web-server/src/main/resources/application-integrationtest-data.yml b/sechub-web-server/src/main/resources/application-integrationtest-data.yml
deleted file mode 100644
index 6d3e83fa71..0000000000
--- a/sechub-web-server/src/main/resources/application-integrationtest-data.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: MIT
-web-server:
- sechub:
- ## Setup integration test sechub user (currently admin, later other role)
- userid: int-test_superadmin
- apitoken: int-test_superadmin-pwd
\ No newline at end of file
diff --git a/sechub-web-server/src/main/resources/application-ssl-cert-provided.yml b/sechub-web-server/src/main/resources/application-ssl-cert-provided.yml
deleted file mode 100644
index 45a7810b64..0000000000
--- a/sechub-web-server/src/main/resources/application-ssl-cert-provided.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-# SPDX-License-Identifier: MIT
-# This configuration is used for development and local testing. It uses a self-signed certificate provided by the build.
-
-server:
- ssl:
- key-store-type: 'PKCS12'
- # we use a keystore location which is never tracked by git.
- # see dev-create_localhost_certificate.sh and dev-ensure_localhost_certificate.sh
- key-store: 'classpath:certificates-untracked/generated-dev-localhost-keystore.p12'
- key-store-password: '123456'
- key-alias: 'tomcat'
-
-web-server:
- sechub:
- server-url: "https://localhost:8443"
- trust-all-certificates: true
diff --git a/sechub-web-server/src/main/resources/application-ssl-cert-required.yml b/sechub-web-server/src/main/resources/application-ssl-cert-required.yml
deleted file mode 100644
index f9c736423f..0000000000
--- a/sechub-web-server/src/main/resources/application-ssl-cert-required.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-# SPDX-License-Identifier: MIT
-# This configuration is used for prod and int. It requires a valid certificate to be provided through the environment variables.
-
-server:
- ssl:
- keyStoreType:
- ${SECHUB_WEB_SERVER_SSL_KEYSTORE_TYPE}
- key-store:
- ${SECHUB_WEB_SERVER_SSL_KEYSTORE_LOCATION}
- key-store-password:
- ${SECHUB_WEB_SERVER_SSL_KEYSTORE_PASSWORD}
- key-alias:
- ${SECHUB_WEB_SERVER_SSL_KEYSTORE_ALIAS}
diff --git a/sechub-web-server/src/main/resources/application.yml b/sechub-web-server/src/main/resources/application.yml
deleted file mode 100644
index c97f5b09f0..0000000000
--- a/sechub-web-server/src/main/resources/application.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-# Main settings
-server:
- port:
- 4443
- ssl:
- enabled: true # always enabled
- protocol: TLS
- enabled-protocols: TLSv1.2,TLSv1.3
-
-spring:
- messages:
- basename: "i18n/messages"
- profiles:
- group:
- web-server_prod: "ssl-cert-required,oauth2"
- web-server_int: "ssl-cert-required,oauth2,classic-auth-enabled"
- web-server_dev: "ssl-cert-provided,classic-auth-enabled"
- web-server_local: "ssl-cert-provided,classic-auth-enabled,local"
- web-server_test: "test"
- web-server_integrationtest: "ssl-cert-provided,integrationtest-data"
- web:
- resources:
- static-locations: classpath:/static
-
-# Spring Boot Actuators and Metrics
-management:
- server:
- port:
- 10250
- ssl:
- enabled: false
- endpoints:
- web:
- exposure:
- include: "prometheus,health"
- endpoint:
- metrics:
- enabled: true
- prometheus:
- enabled: true
- prometheus:
- metrics:
- export:
- enabled: true
\ No newline at end of file
diff --git a/sechub-web-server/src/main/resources/banner.txt b/sechub-web-server/src/main/resources/banner.txt
deleted file mode 100644
index c071cd43f1..0000000000
--- a/sechub-web-server/src/main/resources/banner.txt
+++ /dev/null
@@ -1,8 +0,0 @@
- ____ _ _ _ __ __ _ ____
- / ___| ___ ___| | | |_ _| |__ \ \ / /__| |__/ ___| ___ _ ____ _____ _ __
- \___ \ / _ \/ __| |_| | | | | '_ \ \ \ /\ / / _ \ '_ \___ \ / _ \ '__\ \ / / _ \ '__|
- ___) | __/ (__| _ | |_| | |_) | \ V V / __/ |_) |__) | __/ | \ V / __/ |
- |____/ \___|\___|_| |_|\__,_|_.__/ \_/\_/ \___|_.__/____/ \___|_| \_/ \___|_|
-
-:: SecHub Web Server :: ${application.formatted-version}
-:: Spring Boot :: ${spring-boot.formatted-version}
\ No newline at end of file
diff --git a/sechub-web-server/src/main/resources/certificates-untracked/.gitignore b/sechub-web-server/src/main/resources/certificates-untracked/.gitignore
deleted file mode 100644
index a8df1b6f0c..0000000000
--- a/sechub-web-server/src/main/resources/certificates-untracked/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-# accept nothing
-*
-# except the readme.md
-!README.md
-!.gitignore
diff --git a/sechub-web-server/src/main/resources/certificates-untracked/README.md b/sechub-web-server/src/main/resources/certificates-untracked/README.md
deleted file mode 100644
index 3172a4735d..0000000000
--- a/sechub-web-server/src/main/resources/certificates-untracked/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-# About p12 folder
-
-Here your p12 certificates have to be stored
-- local development
-- prod
-- ..
-
-Inside this folder GIT does ignore anything except this `README.md`.
\ No newline at end of file
diff --git a/sechub-web-server/src/main/resources/i18n/messages.properties b/sechub-web-server/src/main/resources/i18n/messages.properties
deleted file mode 100644
index 8a9f3d6b8d..0000000000
--- a/sechub-web-server/src/main/resources/i18n/messages.properties
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: MIT
-common.projects=Projects
-common.status=Status
-common.loggout=Logout
-
-lang.switch-de=German
-lang.switch-en=English
-
-newapitoken.request-new=Request new API Token
-
-scans.project-headline-prefix=Scanned
\ No newline at end of file
diff --git a/sechub-web-server/src/main/resources/i18n/messages_de.properties b/sechub-web-server/src/main/resources/i18n/messages_de.properties
deleted file mode 100644
index e91330ba21..0000000000
--- a/sechub-web-server/src/main/resources/i18n/messages_de.properties
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: MIT
-common.projects=Projekte
-common.status=Status
-common.loggout=Ausloggen
-
-lang.switch-de=Deutsch
-lang.switch-en=Englisch
-
-newapitoken.request-new=Neues API Token anfordern
-
-scans.project-headline-prefix=Scans für
\ No newline at end of file
diff --git a/sechub-web-server/src/main/resources/logback-spring.xml b/sechub-web-server/src/main/resources/logback-spring.xml
deleted file mode 100644
index 763b30380f..0000000000
--- a/sechub-web-server/src/main/resources/logback-spring.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/sechub-web-server/src/main/resources/static/css/main.css b/sechub-web-server/src/main/resources/static/css/main.css
deleted file mode 100644
index 9810deffeb..0000000000
--- a/sechub-web-server/src/main/resources/static/css/main.css
+++ /dev/null
@@ -1,166 +0,0 @@
-* {
- margin: 0;
- padding: 0;
-}
-
-:root {
- --color-dark: #100c08;
- --color-light: #FDFDFD;
- --color-grey: #696969;
-}
-
-body {
- font-family: sans-serif;
- min-height: 100vh
-}
-
-.placeholder {
- color: var(--color-grey);
-}
-
-.border-thin {
- border-width: 0.2rem;
- border-style: solid;
-}
-
-.warning-border-dark-bg {
- border-color: goldenrod;
-}
-
-.warning-light-bg {
- color: darkgoldenrod;
-}
-
-.warning-dark-bg {
- color: goldenrod;
-}
-
-.container-flex {
- display: flex;
-}
-
-.flex-column {
- flex-direction: column;
-}
-
-.flex-row {
- flex-direction: row;
-}
-
-.flex-grow {
- flex-grow: 1;
-}
-
-.flex-shrink {
- flex-shrink: 1;
-}
-
-.text-align-center {
- text-align: center;
-}
-
-.padding-thin-all {
- padding: 1em;
-}
-
-.menu {
- list-style: none;
-}
-
-.menu > li {
- display: inline;
-}
-
-.item > a {
- color: white;
-}
-
-.item > a:hover {
- color: gold;
-}
-
-.item > a:visited {
- color: white;
-}
-
-.text-color-dark {
- color: var(--color-dark);
-}
-
-.text-color-light {
- color: var(--color-light);
-}
-
-.background-dark {
- background: var(--color-dark);
-}
-
-.background-light {
- background: var(--color-light);
-}
-
-.text-color-white {
- color: white;
-}
-
-.error {
- color: darkred;
-}
-
-/* -----------------
- * Info table
- * -----------------
- */
-
-.infoTable {
- padding-bottom: 16px;
- padding-top: 10px;
- border-collapse: separate;
- border: solid #cccccc 1px;
- border-radius: 16px;
- border-spacing: 0px;
-}
-
-.infoTable th {
- padding: 8px;
- vertical-align: top;
- text-align: left;
-}
-
-.infoTable tr:nth-child(even) {
- background: #f0f0f0;
-}
-
-.infoTable tr:nth-child(odd) {
- background: #fefefe;
-}
-
-.infoTable td {
- padding:8px;
-
- font-family: monospace;
- vertical-align: top;
- text-align: left;
-}
-
-.infoTable td:nth-child(1) {
- width: 150px;
- padding-left: 10px;
-}
-
-.infoTable td:nth-child(2) {
- width: 300px;
- padding-left: 10px;
- border-left: 1px solid #cccccc;
-}
-
-/*
- * Logo
- */
-.logo {
- width: 80px;
- height: 80px;
-}
-
-
-
\ No newline at end of file
diff --git a/sechub-web-server/src/main/resources/templates/fragments/banner.html b/sechub-web-server/src/main/resources/templates/fragments/banner.html
deleted file mode 100644
index 4e813969b2..0000000000
--- a/sechub-web-server/src/main/resources/templates/fragments/banner.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-Banner
-
-
-
-
-
Early WebUI draft - do not use this in production!