diff --git a/.config/spotbugs-excludes.xml b/.config/spotbugs-excludes.xml
index e38b12df62..ff2379382f 100644
--- a/.config/spotbugs-excludes.xml
+++ b/.config/spotbugs-excludes.xml
@@ -25,13 +25,13 @@
-
+
-
+
diff --git a/charts/irs-helm/templates/configmap-spring-app-config.yaml b/charts/irs-helm/templates/configmap-spring-app-config.yaml
index c3961c4b2a..7032da7f92 100644
--- a/charts/irs-helm/templates/configmap-spring-app-config.yaml
+++ b/charts/irs-helm/templates/configmap-spring-app-config.yaml
@@ -34,6 +34,11 @@ data:
irs:
apiUrl: {{ tpl (.Values.irsUrl | default "http://localhost") . | quote }}
+ security:
+ api:
+ keys:
+ admin: {{ tpl (.Values.apiKeyAdmin | default "") . | quote }}
+ regular: {{ tpl (.Values.apiKeyRegular | default "") . | quote }}
blobstore:
endpoint: {{ tpl (.Values.minioUrl | default "") . | quote }}
@@ -62,9 +67,6 @@ data:
token-uri: {{ tpl (.Values.oauth2.clientTokenUri | default "http://localhost") . | quote }}
portal:
token-uri: {{ tpl (.Values.oauth2.clientTokenUri | default "http://localhost") . | quote }}
- resourceserver:
- jwt:
- jwk-set-uri: {{ tpl (.Values.oauth2.jwkSetUri | default "http://localhost") . | quote }}
digitalTwinRegistry:
descriptorEndpoint: {{ tpl (.Values.digitalTwinRegistry.descriptorEndpoint | default "") . | quote }}
@@ -145,13 +147,6 @@ data:
{{- end }}
{{- end }}
- apiAllowedBpn: {{ tpl (.Values.bpn | default "") . | quote }}
-
- oauth:
- resourceClaim: {{ tpl (.Values.oauth.resourceClaim | default "resource_access") . | quote }}
- irsNamespace: {{ tpl (.Values.oauth.irsNamespace | default "") . | quote }}
- roles: {{ tpl (.Values.oauth.roles | default "roles") . | quote }}
-
{{- if .Values.config.content }}
{{- tpl (toYaml .Values.config.content) . | nindent 4 }}
{{- end }}
diff --git a/charts/irs-helm/values.yaml b/charts/irs-helm/values.yaml
index 32f565aff3..3e066f347d 100644
--- a/charts/irs-helm/values.yaml
+++ b/charts/irs-helm/values.yaml
@@ -105,7 +105,8 @@ readinessProbe:
# IRS Configuration #
#####################
irsUrl: # "https://"
-bpn: # BPN for this IRS instance; only users with this BPN are allowed to access the API
+apiKeyAdmin: # Api key to access API with admin role
+apiKeyRegular: # Api key to access API with regular/view role
ingress:
enabled: false
@@ -153,7 +154,6 @@ oauth2:
clientId: #
clientSecret: #
clientTokenUri: #
- jwkSetUri: #
portal:
oauth2:
clientId: #
@@ -217,11 +217,6 @@ ess:
policydefinitionsPath: /management/v2/policydefinitions # EDC management API "policydefinitions" path - used for notification policy definition creation
contractdefinitionsPath: /management/v2/contractdefinitions # EDC management API "contractdefinitions" path - used for notification contract definitions creation
-oauth:
- resourceClaim: "resource_access" # Name of the JWT claim for roles
- irsNamespace: "Cl20-CX-IRS" # Namespace for the IRS roles
- roles: "roles" # Name of the list of roles within the IRS namespace
-
config:
# If true, the config provided below will completely replace the configmap.
# In this case, you need to provide all required config values defined above yourself!
diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/IrsApplication.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/IrsApplication.java
index 9d4bbbf566..81c0e0fa27 100644
--- a/irs-api/src/main/java/org/eclipse/tractusx/irs/IrsApplication.java
+++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/IrsApplication.java
@@ -26,6 +26,8 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
+import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
import org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.cache.annotation.EnableCaching;
@@ -37,7 +39,7 @@
/**
* Application entry point.
*/
-@SpringBootApplication(exclude = WebSocketServletAutoConfiguration.class)
+@SpringBootApplication(exclude = { SecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class, WebSocketServletAutoConfiguration.class })
@EnableScheduling
@EnableCaching
@EnableAsync
diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/DependenciesHealthMetricsExportConfiguration.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/DependenciesHealthMetricsExportConfiguration.java
index fee6f78ca5..9016948bec 100644
--- a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/DependenciesHealthMetricsExportConfiguration.java
+++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/DependenciesHealthMetricsExportConfiguration.java
@@ -73,7 +73,7 @@ private void registerOverallHealthMetric(final MeterRegistry registry,
log.debug("Registering metric '{}'", metricDescriptor.name);
final ToDoubleFunction statusProvider = //
- healthIndicator -> HealthStatusHelper.healthStatustoNumeric(overallStatus(healthIndicator));
+ healthIndicator -> HealthStatusHelper.healthStatusToNumeric(overallStatus(healthIndicator));
Gauge.builder(metricDescriptor.name, dependenciesHealthIndicator, statusProvider)
.description(metricDescriptor.description())
@@ -103,7 +103,7 @@ private void registerIrsDependencyHealthMetric(final MeterRegistry registry,
log.debug("Registering metric '{}' tag '{}'", metricDescriptor.name, dependencyName);
final ToDoubleFunction statusProvider = //
- healthIndicator -> HealthStatusHelper.healthStatustoNumeric(
+ healthIndicator -> HealthStatusHelper.healthStatusToNumeric(
getIrsDependencyStatus(healthIndicator, dependencyName));
Gauge.builder(metricDescriptor.name, dependenciesHealthIndicator, statusProvider)
diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/HealthMetricsExportConfiguration.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/HealthMetricsExportConfiguration.java
index 4f3f323fcf..d56559f3e1 100644
--- a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/HealthMetricsExportConfiguration.java
+++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/HealthMetricsExportConfiguration.java
@@ -55,7 +55,7 @@ private void registerIrsHealthMetrics(final MeterRegistry registry, final Health
log.debug("Registering metric '{}'", metricName);
final ToDoubleFunction super HealthEndpoint> statusProvider = //
- healthEndpoint -> HealthStatusHelper.healthStatustoNumeric(getIrsStatus(healthEndpoint));
+ healthEndpoint -> HealthStatusHelper.healthStatusToNumeric(getIrsStatus(healthEndpoint));
Gauge.builder(metricName, irsHealthEndpoint, statusProvider)
.description("The IRS health status.")
diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/HealthStatusHelper.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/HealthStatusHelper.java
index c531a0e4ca..305bccd738 100644
--- a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/HealthStatusHelper.java
+++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/HealthStatusHelper.java
@@ -45,7 +45,7 @@ public class HealthStatusHelper {
* @param status the health status
* @return the numeric representation of the health status
*/
- public static int healthStatustoNumeric(final Status status) {
+ public static int healthStatusToNumeric(final Status status) {
// see Spring documentation - map health indicators to metrics:
// https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/
diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/converter/IrsTokenParser.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/converter/IrsTokenParser.java
deleted file mode 100644
index 198e767930..0000000000
--- a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/converter/IrsTokenParser.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2021,2022,2023
- * 2022: ZF Friedrichshafen AG
- * 2022: ISTOS GmbH
- * 2022,2023: Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
- * 2022,2023: BOSCH AG
- * Copyright (c) 2021,2022,2023 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Apache License, Version 2.0 which is available at
- * https://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- ********************************************************************************/
-package org.eclipse.tractusx.irs.configuration.converter;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import com.nimbusds.jose.shaded.gson.internal.LinkedTreeMap;
-import lombok.AllArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.security.oauth2.jwt.Jwt;
-
-/**
- * Parsing JWT - retrieving resource_access claim with IRS roles.
- */
-@Slf4j
-@AllArgsConstructor
-public class IrsTokenParser {
-
- private String resourceAccessClaim;
- private String irsResourceAccess;
- private String roles;
-
- /**
- * Parsing JWT - retrieving resource_access claim with IRS roles.
- *
- * @param jwt source
- * @return set of roles from token
- */
- public Set extractIrsRolesFromToken(final Jwt jwt) {
- return Optional.ofNullable(jwt.getClaim(resourceAccessClaim))
- .map(LinkedTreeMap.class::cast)
- .map(accesses -> accesses.get(irsResourceAccess))
- .map(LinkedTreeMap.class::cast)
- .map(irsAccesses -> irsAccesses.get(roles))
- .map(irsRoles -> ((List) irsRoles).stream()
- .map(SimpleGrantedAuthority::new)
- .collect(Collectors.toSet()))
- .orElse(Collections.emptySet());
- }
-}
diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/converter/JwtAuthenticationConverter.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/converter/JwtAuthenticationConverter.java
deleted file mode 100644
index 81f4b71ced..0000000000
--- a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/converter/JwtAuthenticationConverter.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2021,2022,2023
- * 2022: ZF Friedrichshafen AG
- * 2022: ISTOS GmbH
- * 2022,2023: Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
- * 2022,2023: BOSCH AG
- * Copyright (c) 2021,2022,2023 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Apache License, Version 2.0 which is available at
- * https://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- ********************************************************************************/
-package org.eclipse.tractusx.irs.configuration.converter;
-
-import java.util.Collection;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import lombok.AllArgsConstructor;
-import org.jetbrains.annotations.NotNull;
-import org.springframework.core.convert.converter.Converter;
-import org.springframework.security.authentication.AbstractAuthenticationToken;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.oauth2.jwt.Jwt;
-import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
-import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
-
-/**
- * JWT Converter
- */
-@AllArgsConstructor
-public class JwtAuthenticationConverter implements Converter {
-
- private JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter;
- private IrsTokenParser irsTokenParser;
-
- @Override
- public AbstractAuthenticationToken convert(final @NotNull Jwt source) {
- final Collection grantedAuthorities = jwtGrantedAuthoritiesConverter.convert(source);
-
- final Collection authorities = Stream.concat(
- grantedAuthorities != null ? grantedAuthorities.stream() : Stream.empty(),
- irsTokenParser.extractIrsRolesFromToken(source).stream()).collect(Collectors.toSet());
-
- return new JwtAuthenticationToken(source, authorities);
- }
-}
-
diff --git a/irs-common/src/main/java/org/eclipse/tractusx/irs/common/auth/AuthorizationService.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/ApiKeyAuthentication.java
similarity index 56%
rename from irs-common/src/main/java/org/eclipse/tractusx/irs/common/auth/AuthorizationService.java
rename to irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/ApiKeyAuthentication.java
index f695499d15..7584d033c8 100644
--- a/irs-common/src/main/java/org/eclipse/tractusx/irs/common/auth/AuthorizationService.java
+++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/ApiKeyAuthentication.java
@@ -21,33 +21,30 @@
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/
-package org.eclipse.tractusx.irs.common.auth;
+package org.eclipse.tractusx.irs.configuration.security;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
/**
- * BPN contains in JWT Token matches BPN under which IRS product is registered.
+ * Api key authentication representation
*/
-@Service
-public class AuthorizationService {
+public class ApiKeyAuthentication extends AbstractAuthenticationToken {
- private final SecurityHelperService securityHelperService;
- private final String apiAllowedBpn;
+ private final ApiKeyAuthority apiKeyAuthority;
- public AuthorizationService(@Value("${apiAllowedBpn:}") final String apiAllowedBpn) {
- this.securityHelperService = new SecurityHelperService();
- this.apiAllowedBpn = apiAllowedBpn;
+ public ApiKeyAuthentication(final ApiKeyAuthority apiKeyAuthority) {
+ super(apiKeyAuthority.authorities());
+ this.apiKeyAuthority = apiKeyAuthority;
+ setAuthenticated(true);
}
- public boolean verifyBpn() {
- if (StringUtils.isBlank(apiAllowedBpn)) {
- return false;
- }
-
- final String bpnFromToken = securityHelperService.getBpnClaim();
- return apiAllowedBpn.equals(bpnFromToken);
+ @Override
+ public Object getCredentials() {
+ return apiKeyAuthority.apiKey();
}
+ @Override
+ public Object getPrincipal() {
+ return apiKeyAuthority.apiKey();
+ }
}
diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/ApiKeyAuthenticationFilter.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/ApiKeyAuthenticationFilter.java
new file mode 100644
index 0000000000..484254e393
--- /dev/null
+++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/ApiKeyAuthenticationFilter.java
@@ -0,0 +1,82 @@
+/********************************************************************************
+ * Copyright (c) 2021,2022,2023
+ * 2022: ZF Friedrichshafen AG
+ * 2022: ISTOS GmbH
+ * 2022,2023: Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ * 2022,2023: BOSCH AG
+ * Copyright (c) 2021,2022,2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+package org.eclipse.tractusx.irs.configuration.security;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import org.eclipse.tractusx.irs.dtos.ErrorResponse;
+import org.eclipse.tractusx.irs.util.JsonUtil;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+/**
+ * Filter to execute authentication based on X-API-KEY header
+ */
+@RequiredArgsConstructor
+class ApiKeyAuthenticationFilter extends OncePerRequestFilter {
+
+ private final AuthenticationService authenticationService;
+ private final JsonUtil jsonUtil;
+
+ @Override
+ protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
+ final FilterChain filterChain) throws ServletException, IOException {
+ try {
+ final Authentication authentication = authenticationService.getAuthentication(request);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ } catch (final BadCredentialsException exception) {
+ unauthorizedResponse(response, exception);
+ }
+
+ filterChain.doFilter(request, response);
+ }
+
+ private void unauthorizedResponse(final HttpServletResponse servletResponse, final Exception exception) throws IOException {
+ servletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ servletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
+ servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.name());
+
+ final ErrorResponse errorResponse = ErrorResponse.builder()
+ .withStatusCode(HttpStatus.UNAUTHORIZED)
+ .withError(exception.getMessage())
+ .build();
+
+
+ try (PrintWriter writer = servletResponse.getWriter()) {
+ writer.print(jsonUtil.asString(errorResponse));
+ writer.flush();
+ }
+ }
+}
diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/ApiKeyAuthority.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/ApiKeyAuthority.java
new file mode 100644
index 0000000000..6b61ab6047
--- /dev/null
+++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/ApiKeyAuthority.java
@@ -0,0 +1,41 @@
+/********************************************************************************
+ * Copyright (c) 2021,2022,2023
+ * 2022: ZF Friedrichshafen AG
+ * 2022: ISTOS GmbH
+ * 2022,2023: Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ * 2022,2023: BOSCH AG
+ * Copyright (c) 2021,2022,2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+package org.eclipse.tractusx.irs.configuration.security;
+
+import java.util.List;
+
+import org.springframework.security.core.GrantedAuthority;
+
+/**
+ * @param apiKey
+ * @param authorities
+ */
+public record ApiKeyAuthority(String apiKey, List authorities) {
+
+ @SuppressWarnings("PMD.ShortMethodName")
+ public static ApiKeyAuthority of(final String apiKey, final List authorities) {
+ return new ApiKeyAuthority(apiKey, authorities);
+ }
+
+}
diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/ApiKeysConfiguration.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/ApiKeysConfiguration.java
new file mode 100644
index 0000000000..c8be770750
--- /dev/null
+++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/ApiKeysConfiguration.java
@@ -0,0 +1,64 @@
+/********************************************************************************
+ * Copyright (c) 2021,2022,2023
+ * 2022: ZF Friedrichshafen AG
+ * 2022: ISTOS GmbH
+ * 2022,2023: Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ * 2022,2023: BOSCH AG
+ * Copyright (c) 2021,2022,2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+package org.eclipse.tractusx.irs.configuration.security;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import lombok.Setter;
+import org.eclipse.tractusx.irs.common.auth.IrsRoles;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.annotation.Validated;
+
+/**
+ * Configuration class for api keys
+ */
+@Component
+@ConfigurationProperties(prefix = "irs.security.api.keys")
+@Validated
+@Setter
+class ApiKeysConfiguration {
+
+ private static final int MIN_API_KEY_SIZE = 20;
+
+ @NotBlank
+ @Size(min = MIN_API_KEY_SIZE)
+ private String admin;
+
+ @NotBlank
+ @Size(min = MIN_API_KEY_SIZE)
+ private String regular;
+
+ /* package */ ApiKeyAuthority authorityOf(final String apiKey) {
+ if (apiKey.equals(admin)) {
+ return ApiKeyAuthority.of(apiKey, AuthorityUtils.createAuthorityList(IrsRoles.ADMIN_IRS));
+ } else if (apiKey.equals(regular)) {
+ return ApiKeyAuthority.of(regular, AuthorityUtils.createAuthorityList(IrsRoles.VIEW_IRS));
+ }
+
+ throw new BadCredentialsException("Wrong ApiKey");
+ }
+}
diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/AuthenticationService.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/AuthenticationService.java
new file mode 100644
index 0000000000..aafebcfc39
--- /dev/null
+++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/AuthenticationService.java
@@ -0,0 +1,52 @@
+/********************************************************************************
+ * Copyright (c) 2021,2022,2023
+ * 2022: ZF Friedrichshafen AG
+ * 2022: ISTOS GmbH
+ * 2022,2023: Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ * 2022,2023: BOSCH AG
+ * Copyright (c) 2021,2022,2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+package org.eclipse.tractusx.irs.configuration.security;
+
+import com.apicatalog.jsonld.StringUtils;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Service;
+
+/**
+ * Check X-API-KEY header against api keys from config
+ */
+@RequiredArgsConstructor
+@Service
+public class AuthenticationService {
+
+ private static final String AUTH_TOKEN_HEADER_NAME = "X-API-KEY";
+
+ private final ApiKeysConfiguration apiKeysConfiguration;
+
+ public Authentication getAuthentication(final HttpServletRequest request) {
+ final String apiKeyHeader = request.getHeader(AUTH_TOKEN_HEADER_NAME);
+ if (StringUtils.isBlank(apiKeyHeader)) {
+ throw new BadCredentialsException("Wrong ApiKey");
+ }
+
+ return new ApiKeyAuthentication(apiKeysConfiguration.authorityOf(apiKeyHeader));
+ }
+}
diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/SecurityConfiguration.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/SecurityConfiguration.java
similarity index 58%
rename from irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/SecurityConfiguration.java
rename to irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/SecurityConfiguration.java
index d7a46ab4bd..432fce53fa 100644
--- a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/SecurityConfiguration.java
+++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/security/SecurityConfiguration.java
@@ -21,17 +21,23 @@
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/
-package org.eclipse.tractusx.irs.configuration;
+package org.eclipse.tractusx.irs.configuration.security;
+import static java.util.Arrays.stream;
import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
+import java.io.IOException;
import java.time.Duration;
import java.util.List;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
import org.eclipse.tractusx.irs.common.ApiConstants;
-import org.eclipse.tractusx.irs.configuration.converter.IrsTokenParser;
-import org.eclipse.tractusx.irs.configuration.converter.JwtAuthenticationConverter;
-import org.springframework.beans.factory.annotation.Value;
+import org.eclipse.tractusx.irs.util.JsonUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
@@ -40,16 +46,18 @@
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
-import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter;
import org.springframework.security.web.header.writers.PermissionsPolicyHeaderWriter;
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
+import org.springframework.util.AntPathMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.OncePerRequestFilter;
/**
* Security config bean
@@ -57,21 +65,22 @@
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
+@SuppressWarnings({ "PMD.ExcessiveImports" })
public class SecurityConfiguration {
- private static final String[] WHITELIST = {
- "/actuator/health",
- "/actuator/health/readiness",
- "/actuator/health/liveness",
- "/actuator/prometheus",
- "/api/swagger-ui/**",
- "/api/api-docs",
- "/api/api-docs.yaml",
- "/api/api-docs/swagger-config",
- "/" + ApiConstants.API_PREFIX_INTERNAL + "/endpoint-data-reference",
- "/ess/mock/notification/receive",
- "/ess/notification/receive",
- "/ess/notification/receive-recursive"
+ private static final String[] WHITELIST = { "/actuator/health",
+ "/actuator/health/readiness",
+ "/actuator/health/liveness",
+ "/actuator/prometheus",
+ "/api/swagger-ui/**",
+ "/api/api-docs",
+ "/api/api-docs.yaml",
+ "/api/api-docs/swagger-config",
+ "/favicon.ico",
+ "/" + ApiConstants.API_PREFIX_INTERNAL + "/endpoint-data-reference",
+ "/ess/mock/notification/receive",
+ "/ess/notification/receive",
+ "/ess/notification/receive-recursive"
};
private static final long HSTS_MAX_AGE_DAYS = 365;
private static final String ONLY_SELF_SCRIPT_SRC = "script-src 'self'";
@@ -80,7 +89,7 @@ public class SecurityConfiguration {
@SuppressWarnings("PMD.SignatureDeclareThrowsException")
@Bean
/* package */ SecurityFilterChain securityFilterChain(final HttpSecurity httpSecurity,
- final JwtAuthenticationConverter jwtAuthenticationConverter) throws Exception {
+ final AuthenticationService authenticationService) throws Exception {
httpSecurity.httpBasic(AbstractHttpConfigurer::disable);
httpSecurity.formLogin(AbstractHttpConfigurer::disable);
httpSecurity.csrf(AbstractHttpConfigurer::disable);
@@ -88,39 +97,53 @@ public class SecurityConfiguration {
httpSecurity.cors(Customizer.withDefaults());
httpSecurity.headers(headers -> headers.httpStrictTransportSecurity(
- httpStrictTransportSecurity ->
- httpStrictTransportSecurity.maxAgeInSeconds(Duration.ofDays(HSTS_MAX_AGE_DAYS).toSeconds())
- .includeSubDomains(true)
- .preload(true)
- .requestMatcher(AnyRequestMatcher.INSTANCE)));
+ httpStrictTransportSecurity -> httpStrictTransportSecurity.maxAgeInSeconds(
+ Duration.ofDays(HSTS_MAX_AGE_DAYS).toSeconds()).includeSubDomains(true).preload(true).requestMatcher(AnyRequestMatcher.INSTANCE)));
- httpSecurity.headers(headers -> headers.xssProtection(xXssConfig ->
- xXssConfig.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK)));
+ httpSecurity.headers(headers -> headers.xssProtection(xXssConfig -> xXssConfig.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK)));
httpSecurity.headers(headers -> headers.addHeaderWriter(new ContentSecurityPolicyHeaderWriter(ONLY_SELF_SCRIPT_SRC)));
httpSecurity.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin));
- httpSecurity.headers(headers -> headers.addHeaderWriter(new ReferrerPolicyHeaderWriter(
- ReferrerPolicyHeaderWriter.ReferrerPolicy.SAME_ORIGIN)));
+ httpSecurity.headers(headers -> headers.addHeaderWriter(new ReferrerPolicyHeaderWriter(ReferrerPolicyHeaderWriter.ReferrerPolicy.SAME_ORIGIN)));
httpSecurity.headers(headers -> headers.addHeaderWriter(new PermissionsPolicyHeaderWriter(PERMISSION_POLICY)));
- httpSecurity.sessionManagement(sessionManagement -> sessionManagement
- .sessionCreationPolicy(STATELESS));
+ httpSecurity.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(STATELESS));
- httpSecurity.authorizeHttpRequests(auth -> auth
- .requestMatchers(WHITELIST)
- .permitAll()
- .requestMatchers("/**")
- .authenticated());
+ httpSecurity.authorizeHttpRequests(auth -> auth.requestMatchers(WHITELIST).permitAll().requestMatchers("/**").authenticated());
- httpSecurity.oauth2ResourceServer(oauth2ResourceServer ->
- oauth2ResourceServer.jwt(jwt ->
- jwt.jwtAuthenticationConverter(jwtAuthenticationConverter)))
- .oauth2Client(Customizer.withDefaults());
+ httpSecurity.addFilterBefore(new IgnoreWhitelistedPathFilter(new ApiKeyAuthenticationFilter(authenticationService, new JsonUtil())), UsernamePasswordAuthenticationFilter.class);
+
+ httpSecurity.exceptionHandling(AbstractHttpConfigurer::disable);
return httpSecurity.build();
}
+ /**
+ * Dont execute delegate filter on whitelisted paths
+ */
+ @RequiredArgsConstructor
+ private static final class IgnoreWhitelistedPathFilter extends OncePerRequestFilter {
+
+ private final Filter delegate;
+ private final AntPathMatcher pathMatcher = new AntPathMatcher();
+
+ @Override
+ protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) throws
+ ServletException, IOException {
+ if (isNotWhitelisted(request)) {
+ delegate.doFilter(request, response, filterChain);
+ } else {
+ filterChain.doFilter(request, response);
+ }
+ }
+
+ private boolean isNotWhitelisted(final HttpServletRequest request) {
+ return stream(WHITELIST).noneMatch(path -> pathMatcher.match(path, request.getRequestURI()));
+ }
+ }
+
+
@Bean
/* package */ CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
@@ -134,16 +157,4 @@ public class SecurityConfiguration {
source.registerCorsConfiguration("/**", configuration);
return source;
}
-
- @Bean
- /* package */ IrsTokenParser irsTokenParser(@Value("${oauth.resourceClaim}") final String resourceAccessClaim,
- @Value("${oauth.irsNamespace}") final String irsResourceAccess,
- @Value("${oauth.roles}") final String roles) {
- return new IrsTokenParser(resourceAccessClaim, irsResourceAccess, roles);
- }
-
- @Bean
- /* package */ JwtAuthenticationConverter jwtAuthenticationConverter(final IrsTokenParser irsTokenParser) {
- return new JwtAuthenticationConverter(new JwtGrantedAuthoritiesConverter(), irsTokenParser);
- }
}
diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/BatchController.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/BatchController.java
index f638ac0bcb..d8c7f4db7d 100644
--- a/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/BatchController.java
+++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/BatchController.java
@@ -49,7 +49,6 @@
import org.eclipse.tractusx.irs.component.RegisterBatchOrder;
import org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder;
import org.eclipse.tractusx.irs.dtos.ErrorResponse;
-import org.eclipse.tractusx.irs.common.auth.AuthorizationService;
import org.eclipse.tractusx.irs.services.CreationBatchService;
import org.eclipse.tractusx.irs.services.QueryBatchService;
import org.eclipse.tractusx.irs.services.timeouts.CancelBatchProcessingService;
@@ -79,7 +78,6 @@ public class BatchController {
private final CreationBatchService creationBatchService;
private final QueryBatchService queryBatchService;
private final CancelBatchProcessingService cancelBatchProcessingService;
- private final AuthorizationService authorizationService;
@Operation(operationId = "registerOrder",
summary = "Registers an IRS order with an array of {globalAssetIds}. "
@@ -116,7 +114,7 @@ public class BatchController {
})
@PostMapping("/orders")
@ResponseStatus(HttpStatus.CREATED)
- @PreAuthorize("@authorizationService.verifyBpn() && hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
+ @PreAuthorize("hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
public BatchOrderCreated registerBatchOrder(final @Valid @RequestBody RegisterBatchOrder request) {
final UUID batchOrderId = creationBatchService.create(request);
return BatchOrderCreated.builder().id(batchOrderId).build();
@@ -155,7 +153,7 @@ public BatchOrderCreated registerBatchOrder(final @Valid @RequestBody RegisterBa
})
@PostMapping("/ess/orders")
@ResponseStatus(HttpStatus.CREATED)
- @PreAuthorize("@authorizationService.verifyBpn() && hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
+ @PreAuthorize("hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
public BatchOrderCreated registerESSInvestigationOrder(final @Valid @RequestBody RegisterBpnInvestigationBatchOrder request) {
final UUID batchOrderId = creationBatchService.create(request);
return BatchOrderCreated.builder().id(batchOrderId).build();
@@ -199,7 +197,7 @@ public BatchOrderCreated registerESSInvestigationOrder(final @Valid @RequestBody
}),
})
@GetMapping("/orders/{orderId}")
- @PreAuthorize("@authorizationService.verifyBpn() && hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
+ @PreAuthorize("hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
public BatchOrderResponse getBatchOrder(
@Parameter(description = "Id of the order.", schema = @Schema(implementation = UUID.class), name = "orderId",
example = "6c311d29-5753-46d4-b32c-19b918ea93b0") @Size(min = IrsAppConstants.JOB_ID_SIZE,
@@ -245,7 +243,7 @@ public BatchOrderResponse getBatchOrder(
}),
})
@GetMapping("/orders/{orderId}/batches/{batchId}")
- @PreAuthorize("@authorizationService.verifyBpn() && hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
+ @PreAuthorize("hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
public BatchResponse getBatch(
@Parameter(description = "Id of the order.", schema = @Schema(implementation = UUID.class), name = "orderId",
example = "6c311d29-5753-46d4-b32c-19b918ea93b0") @Size(min = IrsAppConstants.JOB_ID_SIZE,
@@ -294,7 +292,7 @@ public BatchResponse getBatch(
}),
})
@PutMapping("/orders/{orderId}")
- @PreAuthorize("@authorizationService.verifyBpn() && hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
+ @PreAuthorize("hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
public BatchOrderResponse cancelBatchOrder(
@Parameter(description = "Id of the order.", schema = @Schema(implementation = UUID.class), name = "orderId",
example = "6c311d29-5753-46d4-b32c-19b918ea93b0") @Size(min = IrsAppConstants.JOB_ID_SIZE,
diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/IrsController.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/IrsController.java
index a3fb4bea4c..2324ee45e5 100644
--- a/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/IrsController.java
+++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/IrsController.java
@@ -57,7 +57,6 @@
import org.eclipse.tractusx.irs.connector.job.IrsTimer;
import org.eclipse.tractusx.irs.dtos.ErrorResponse;
import org.eclipse.tractusx.irs.semanticshub.AspectModels;
-import org.eclipse.tractusx.irs.common.auth.AuthorizationService;
import org.eclipse.tractusx.irs.services.IrsItemGraphQueryService;
import org.eclipse.tractusx.irs.services.SemanticHubService;
import org.eclipse.tractusx.irs.services.validation.SchemaNotFoundException;
@@ -92,7 +91,6 @@ public class IrsController {
private final IrsItemGraphQueryService itemJobService;
private final SemanticHubService semanticHubService;
- private final AuthorizationService authorizationService;
@Operation(operationId = "registerJobForGlobalAssetId",
summary = "Register an IRS job to retrieve an item graph for given {globalAssetId}.",
@@ -128,7 +126,7 @@ public class IrsController {
@IrsTimer("registerjob")
@PostMapping("/jobs")
@ResponseStatus(HttpStatus.CREATED)
- @PreAuthorize("@authorizationService.verifyBpn() && hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
+ @PreAuthorize("hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
public JobHandle registerJobForGlobalAssetId(final @Valid @RequestBody RegisterJob request) {
return itemJobService.registerItemJob(request);
}
@@ -179,7 +177,7 @@ public JobHandle registerJobForGlobalAssetId(final @Valid @RequestBody RegisterJ
})
@IrsTimer("getjob")
@GetMapping("/jobs/{id}")
- @PreAuthorize("@authorizationService.verifyBpn() && hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
+ @PreAuthorize("hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
public ResponseEntity getJobById(
@Parameter(description = "Id of the job.", schema = @Schema(implementation = UUID.class), name = "id",
example = "6c311d29-5753-46d4-b32c-19b918ea93b0") @Size(min = IrsAppConstants.JOB_ID_SIZE,
@@ -232,7 +230,7 @@ public ResponseEntity getJobById(
})
@IrsTimer("canceljob")
@PutMapping("/jobs/{id}")
- @PreAuthorize("@authorizationService.verifyBpn() && hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
+ @PreAuthorize("hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
public Job cancelJobByJobId(
@Parameter(description = "Id of the job.", schema = @Schema(implementation = UUID.class), name = "id",
example = "6c311d29-5753-46d4-b32c-19b918ea93b0") @Size(min = IrsAppConstants.JOB_ID_SIZE,
@@ -274,7 +272,7 @@ public Job cancelJobByJobId(
@IrsTimer("getjobbystate")
@GetMapping("/jobs")
@PageableAsQueryParam
- @PreAuthorize("@authorizationService.verifyBpn() && hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
+ @PreAuthorize("hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
public PageResult getJobsByState(
@Valid @ParameterObject @Parameter(description = "Requested job states.", in = QUERY,
explode = Explode.FALSE, array = @ArraySchema(schema = @Schema(implementation = JobState.class), maxItems = Integer.MAX_VALUE))
@@ -308,7 +306,7 @@ public PageResult getJobsByState(
}),
})
@GetMapping("/aspectmodels")
- @PreAuthorize("@authorizationService.verifyBpn() && hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
+ @PreAuthorize("hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
public AspectModels getAllAvailableAspectModels() throws SchemaNotFoundException {
return semanticHubService.getAllAspectModels();
}
diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/ess/controller/EssController.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/ess/controller/EssController.java
index 2ba3374983..04bb98e83e 100644
--- a/irs-api/src/main/java/org/eclipse/tractusx/irs/ess/controller/EssController.java
+++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/ess/controller/EssController.java
@@ -41,7 +41,6 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.tractusx.irs.ess.service.EssService;
-import org.eclipse.tractusx.irs.common.auth.AuthorizationService;
import org.eclipse.tractusx.irs.common.auth.IrsRoles;
import org.eclipse.tractusx.irs.component.JobHandle;
import org.eclipse.tractusx.irs.component.Jobs;
@@ -73,7 +72,6 @@
class EssController {
private final EssService essService;
- private final AuthorizationService authorizationService;
@Operation(operationId = "registerBPNInvestigation",
summary = "Registers an IRS job to start an investigation if a given bpn is contained in a part chain of a given globalAssetId.",
@@ -108,7 +106,7 @@ class EssController {
})
@PostMapping("/bpn/investigations")
@ResponseStatus(HttpStatus.CREATED)
- @PreAuthorize("@authorizationService.verifyBpn() && hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
+ @PreAuthorize("hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
public JobHandle registerBPNInvestigation(final @Valid @RequestBody RegisterBpnInvestigationJob request) {
return essService.startIrsJob(request);
}
@@ -151,7 +149,7 @@ public JobHandle registerBPNInvestigation(final @Valid @RequestBody RegisterBpnI
}),
})
@GetMapping("/bpn/investigations/{id}")
- @PreAuthorize("@authorizationService.verifyBpn() && hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
+ @PreAuthorize("hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')")
public Jobs getBPNInvestigation(
@Parameter(description = "Id of the job.", schema = @Schema(implementation = UUID.class), name = "id",
example = "6c311d29-5753-46d4-b32c-19b918ea93b0") @Valid @PathVariable final UUID id) {
diff --git a/irs-api/src/main/resources/application.yml b/irs-api/src/main/resources/application.yml
index 52f8cd87b1..4132ba837f 100644
--- a/irs-api/src/main/resources/application.yml
+++ b/irs-api/src/main/resources/application.yml
@@ -22,9 +22,7 @@ spring:
token-uri: ${OAUTH2_CLIENT_TOKEN_URI:https://default} # OAuth2 endpoint to request tokens using the client credentials
portal:
token-uri: ${PORTAL_OAUTH2_CLIENT_TOKEN_URI:https://default} # OAuth2 endpoint to request tokens using the client credentials
- resourceserver:
- jwt:
- jwk-set-uri: ${OAUTH2_JWK_SET_URI:https://default} # OAuth2 endpoint to request the JWK set
+
management: # Spring management API config, see https://spring.io/guides/gs/centralized-configuration/
endpoints:
@@ -96,6 +94,11 @@ irs: # Application config
completed: P7D # ISO 8601 Duration
cron:
expression: "*/10 * * * * ?" # Determines how often the number of stored jobs is updated in the metrics API.
+ security:
+ api:
+ keys:
+ admin: ${API_KEY_ADMIN}
+ regular: ${API_KEY_REGULAR}
blobstore:
endpoint: "${MINIO_URL}" # S3 compatible API endpoint (e.g. Minio)
@@ -238,10 +241,3 @@ ess:
mockEdcResult: { } # Mocked BPN Investigation results
mockRecursiveEdcAsset: # Mocked BPN Recursive Investigation results
-apiAllowedBpn: ${API_ALLOWED_BPN:BPNL00000001CRHK} # BPN value that is allowed to access IRS API
-
-# OAuth2 JWT token parse config. This configures the structure IRS expects when parsing the IRS role of an access token.
-oauth:
- resourceClaim: "resource_access" # Name of the JWT claim for roles
- irsNamespace: "Cl20-CX-IRS" # Namespace for the IRS roles
- roles: "roles" # Name of the list of roles within the IRS namespace
diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/ControllerTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/ControllerTest.java
new file mode 100644
index 0000000000..ae20b160c0
--- /dev/null
+++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/ControllerTest.java
@@ -0,0 +1,48 @@
+/********************************************************************************
+ * Copyright (c) 2021,2022,2023
+ * 2022: ZF Friedrichshafen AG
+ * 2022: ISTOS GmbH
+ * 2022,2023: Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ * 2022,2023: BOSCH AG
+ * Copyright (c) 2021,2022,2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+package org.eclipse.tractusx.irs;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import jakarta.servlet.http.HttpServletRequest;
+import org.eclipse.tractusx.irs.configuration.security.ApiKeyAuthentication;
+import org.eclipse.tractusx.irs.configuration.security.ApiKeyAuthority;
+import org.eclipse.tractusx.irs.configuration.security.AuthenticationService;
+import org.eclipse.tractusx.irs.util.JsonUtil;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.security.core.authority.AuthorityUtils;
+
+public abstract class ControllerTest {
+
+ @MockBean
+ protected AuthenticationService authenticationService;
+
+ protected void authenticateWith(String... roles) {
+ when(authenticationService.getAuthentication(any(HttpServletRequest.class)))
+ .thenReturn(new ApiKeyAuthentication(
+ ApiKeyAuthority.of("123", AuthorityUtils.createAuthorityList(roles))));
+ }
+
+}
diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/configuration/HealthStatusHelperTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/configuration/HealthStatusHelperTest.java
index 2aab79b058..531a42e8f6 100644
--- a/irs-api/src/test/java/org/eclipse/tractusx/irs/configuration/HealthStatusHelperTest.java
+++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/configuration/HealthStatusHelperTest.java
@@ -43,6 +43,6 @@ public static List healthStatusToNumeric() {
@ParameterizedTest
@MethodSource
void healthStatusToNumeric(final Status status, final int numericStatus) {
- assertThat(HealthStatusHelper.healthStatustoNumeric(status)).isEqualTo(numericStatus);
+ assertThat(HealthStatusHelper.healthStatusToNumeric(status)).isEqualTo(numericStatus);
}
}
diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/configuration/converter/JwtAuthenticationConverterTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/configuration/converter/JwtAuthenticationConverterTest.java
deleted file mode 100644
index b79822fce1..0000000000
--- a/irs-api/src/test/java/org/eclipse/tractusx/irs/configuration/converter/JwtAuthenticationConverterTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2021,2022,2023
- * 2022: ZF Friedrichshafen AG
- * 2022: ISTOS GmbH
- * 2022,2023: Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
- * 2022,2023: BOSCH AG
- * Copyright (c) 2021,2022,2023 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Apache License, Version 2.0 which is available at
- * https://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- ********************************************************************************/
-package org.eclipse.tractusx.irs.configuration.converter;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.springframework.security.oauth2.jwt.JwtClaimNames.SUB;
-
-import java.time.Instant;
-import java.util.List;
-import java.util.Map;
-
-import com.nimbusds.jose.shaded.gson.internal.LinkedTreeMap;
-import org.eclipse.tractusx.irs.common.auth.IrsRoles;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.springframework.security.authentication.AbstractAuthenticationToken;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.security.oauth2.jwt.Jwt;
-import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
-
-class JwtAuthenticationConverterTest {
-
- private JwtAuthenticationConverter jwtAuthenticationConverter;
-
- @BeforeEach
- void setUp() {
- final String resourceAccessClaim = "resource_access";
- final String irsResourceAccess = "Cl20-CX-IRS";
- final String roles = "roles";
- jwtAuthenticationConverter = new JwtAuthenticationConverter(new JwtGrantedAuthoritiesConverter(),
- new IrsTokenParser(resourceAccessClaim, irsResourceAccess, roles));
- }
-
- @Test
- void shouldParseJwtTokenAndFindViewIrsRole() {
- // given
- final Map irsResourceAccess = new LinkedTreeMap<>();
- final Map irsRoles = new LinkedTreeMap<>();
- irsRoles.put("roles", List.of("view_irs"));
- irsResourceAccess.put("Cl20-CX-IRS", irsRoles);
- final Jwt jwt = jwt(irsResourceAccess);
-
- // when
- final AbstractAuthenticationToken authenticationToken = jwtAuthenticationConverter.convert(jwt);
-
- // then
- assertThat(authenticationToken).isNotNull();
- assertThat(authenticationToken.getAuthorities()).isNotNull();
- assertThat(authenticationToken.getAuthorities()).contains(new SimpleGrantedAuthority(IrsRoles.VIEW_IRS));
- }
-
- @Test
- void shouldParseJwtTokenAndNotFindIrsRolesWhenWrongKey() {
- // given
- final Map irsResourceAccess = new LinkedTreeMap<>();
- final Map irsRoles = new LinkedTreeMap<>();
- irsRoles.put("roles", List.of());
- irsResourceAccess.put("Cl20-CX-IRS-WRONG-KEY", irsRoles);
- final Jwt jwt = jwt(irsResourceAccess);
-
- // when
- final AbstractAuthenticationToken authenticationToken = jwtAuthenticationConverter.convert(jwt);
-
- // then
- assertThat(authenticationToken).isNotNull();
- assertThat(authenticationToken.getAuthorities()).isNotNull();
- assertThat(authenticationToken.getAuthorities()).isEmpty();
- }
-
- @Test
- void shouldParseJwtTokenAndNotFindIrsRolesWhenWrongRolesKey() {
- // given
- final Map irsResourceAccess = new LinkedTreeMap<>();
- final Map irsRoles = new LinkedTreeMap<>();
- irsRoles.put("rolesWrong", List.of("view_irs"));
- irsResourceAccess.put("Cl20-CX-IRS-WRONG-KEY", irsRoles);
- final Jwt jwt = jwt(irsResourceAccess);
-
- // when
- final AbstractAuthenticationToken authenticationToken = jwtAuthenticationConverter.convert(jwt);
-
- // then
- assertThat(authenticationToken).isNotNull();
- assertThat(authenticationToken.getAuthorities()).isNotNull();
- assertThat(authenticationToken.getAuthorities()).isEmpty();
- }
-
- Jwt jwt(final Map irsResourceAccess) {
- final Map claims = new LinkedTreeMap<>();
- claims.putAll(Map.of("resource_access", irsResourceAccess, SUB, "sub", "clientId", "clientId"));
-
- return new Jwt("token", Instant.now(), Instant.now().plusSeconds(30), Map.of("alg", "none"), claims);
- }
-}
\ No newline at end of file
diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/configuration/security/ApiKeyAuthenticationFilterTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/configuration/security/ApiKeyAuthenticationFilterTest.java
new file mode 100644
index 0000000000..57ee7e5223
--- /dev/null
+++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/configuration/security/ApiKeyAuthenticationFilterTest.java
@@ -0,0 +1,30 @@
+/********************************************************************************
+ * Copyright (c) 2021,2022,2023
+ * 2022: ZF Friedrichshafen AG
+ * 2022: ISTOS GmbH
+ * 2022,2023: Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ * 2022,2023: BOSCH AG
+ * Copyright (c) 2021,2022,2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+package org.eclipse.tractusx.irs.configuration.security;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ApiKeyAuthenticationFilterTest {
+
+}
\ No newline at end of file
diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/BatchControllerTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/BatchControllerTest.java
index 2e1b5cef34..5f4af60059 100644
--- a/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/BatchControllerTest.java
+++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/BatchControllerTest.java
@@ -23,6 +23,8 @@
********************************************************************************/
package org.eclipse.tractusx.irs.controllers;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.eclipse.tractusx.irs.util.TestMother.registerBatchOrder;
import static org.eclipse.tractusx.irs.util.TestMother.registerBpnInvestigationBatchOrder;
import static org.hamcrest.Matchers.containsString;
@@ -36,12 +38,12 @@
import java.util.UUID;
import com.fasterxml.jackson.databind.ObjectMapper;
+import org.eclipse.tractusx.irs.ControllerTest;
import org.eclipse.tractusx.irs.common.auth.IrsRoles;
import org.eclipse.tractusx.irs.component.BatchOrderResponse;
import org.eclipse.tractusx.irs.component.BatchResponse;
import org.eclipse.tractusx.irs.component.RegisterBatchOrder;
-import org.eclipse.tractusx.irs.configuration.SecurityConfiguration;
-import org.eclipse.tractusx.irs.common.auth.AuthorizationService;
+import org.eclipse.tractusx.irs.configuration.security.SecurityConfiguration;
import org.eclipse.tractusx.irs.services.CreationBatchService;
import org.eclipse.tractusx.irs.services.QueryBatchService;
import org.eclipse.tractusx.irs.services.timeouts.CancelBatchProcessingService;
@@ -51,12 +53,12 @@
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
-import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.security.access.AccessDeniedException;
import org.springframework.test.web.servlet.MockMvc;
@WebMvcTest(BatchController.class)
@Import(SecurityConfiguration.class)
-class BatchControllerTest {
+class BatchControllerTest extends ControllerTest {
@Autowired
private MockMvc mockMvc;
@@ -70,21 +72,17 @@ class BatchControllerTest {
@MockBean
private CancelBatchProcessingService cancelBatchProcessingService;
- @MockBean(name = "authorizationService")
- private AuthorizationService authorizationService;
-
@Test
- void shouldReturnUnauthorizedWhenAuthenticationIsMissing() throws Exception {
- this.mockMvc.perform(post("/irs/orders").contentType(MediaType.APPLICATION_JSON)
- .content(new ObjectMapper().writeValueAsString(
- registerBatchOrder("urn:uuid:4132cd2b-cbe7-4881-a6b4-39fdc31cca2b"))))
- .andExpect(status().isUnauthorized());
+ void shouldReturnUnauthorizedWhenAuthenticationIsMissing() {
+ assertThatThrownBy(() -> this.mockMvc.perform(post("/irs/orders").contentType(MediaType.APPLICATION_JSON)
+ .content(new ObjectMapper().writeValueAsString(
+ registerBatchOrder("urn:uuid:4132cd2b-cbe7-4881-a6b4-39fdc31cca2b"))))
+ ).isInstanceOf(AccessDeniedException.class);
}
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldReturnBadRequestWhenGlobalAssetIdWithWrongFormat() throws Exception {
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
+ authenticateWith(IrsRoles.VIEW_IRS);
this.mockMvc.perform(post("/irs/orders").contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(
@@ -93,9 +91,8 @@ void shouldReturnBadRequestWhenGlobalAssetIdWithWrongFormat() throws Exception {
}
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldReturnBadRequestWhenBatchSizeNotMod10Compliant() throws Exception {
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
+ authenticateWith(IrsRoles.VIEW_IRS);
final RegisterBatchOrder registerBatchOrder = registerBatchOrder("MALFORMED_GLOBAL_ASSET");
registerBatchOrder.setBatchSize(33);
@@ -105,9 +102,8 @@ void shouldReturnBadRequestWhenBatchSizeNotMod10Compliant() throws Exception {
}
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldRegisterRegularJobBatchOrder() throws Exception {
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
+ authenticateWith(IrsRoles.VIEW_IRS);
this.mockMvc.perform(post("/irs/orders").contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(
@@ -116,9 +112,8 @@ void shouldRegisterRegularJobBatchOrder() throws Exception {
}
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldRegisterEssJobBatchOrder() throws Exception {
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
+ authenticateWith(IrsRoles.VIEW_IRS);
this.mockMvc.perform(post("/irs/ess/orders").contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(
@@ -127,9 +122,8 @@ void shouldRegisterEssJobBatchOrder() throws Exception {
}
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldReturnBatchOrder() throws Exception {
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
+ authenticateWith(IrsRoles.VIEW_IRS);
final UUID orderId = UUID.randomUUID();
when(queryBatchService.findOrderById(orderId)).thenReturn(BatchOrderResponse.builder().orderId(orderId).build());
@@ -140,11 +134,11 @@ void shouldReturnBatchOrder() throws Exception {
}
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldReturnBatch() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
final UUID orderId = UUID.randomUUID();
final UUID batchId = UUID.randomUUID();
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
when(queryBatchService.findBatchById(orderId, batchId)).thenReturn(
BatchResponse.builder().batchId(batchId).orderId(orderId).build());
@@ -155,9 +149,8 @@ void shouldReturnBatch() throws Exception {
}
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldCancelBatchOrder() throws Exception {
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
+ authenticateWith(IrsRoles.VIEW_IRS);
final UUID orderId = UUID.randomUUID();
when(queryBatchService.findOrderById(orderId)).thenReturn(BatchOrderResponse.builder().orderId(orderId).build());
diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/IrsControllerTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/IrsControllerTest.java
index d266e5ad84..d175fdf001 100644
--- a/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/IrsControllerTest.java
+++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/IrsControllerTest.java
@@ -24,6 +24,8 @@
package org.eclipse.tractusx.irs.controllers;
import static java.lang.String.format;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.eclipse.tractusx.irs.util.TestMother.registerBatchOrder;
import static org.eclipse.tractusx.irs.util.TestMother.registerJob;
import static org.eclipse.tractusx.irs.util.TestMother.registerJobWithDepthAndAspect;
import static org.eclipse.tractusx.irs.util.TestMother.registerJobWithUrl;
@@ -49,6 +51,9 @@
import java.util.stream.Stream;
import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.servlet.http.HttpServletRequest;
+import org.eclipse.tractusx.irs.ControllerTest;
+import org.eclipse.tractusx.irs.common.auth.IrsRoles;
import org.eclipse.tractusx.irs.component.Job;
import org.eclipse.tractusx.irs.component.JobHandle;
import org.eclipse.tractusx.irs.component.JobStatusResult;
@@ -57,10 +62,9 @@
import org.eclipse.tractusx.irs.component.RegisterJob;
import org.eclipse.tractusx.irs.component.enums.Direction;
import org.eclipse.tractusx.irs.component.enums.JobState;
-import org.eclipse.tractusx.irs.configuration.SecurityConfiguration;
+import org.eclipse.tractusx.irs.configuration.security.SecurityConfiguration;
import org.eclipse.tractusx.irs.semanticshub.AspectModel;
import org.eclipse.tractusx.irs.semanticshub.AspectModels;
-import org.eclipse.tractusx.irs.common.auth.AuthorizationService;
import org.eclipse.tractusx.irs.services.IrsItemGraphQueryService;
import org.eclipse.tractusx.irs.services.SemanticHubService;
import org.junit.jupiter.api.Test;
@@ -73,13 +77,14 @@
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
-import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.server.ResponseStatusException;
@WebMvcTest(IrsController.class)
@Import(SecurityConfiguration.class)
-class IrsControllerTest {
+class IrsControllerTest extends ControllerTest {
private final UUID jobId = UUID.randomUUID();
@@ -92,8 +97,6 @@ class IrsControllerTest {
private IrsItemGraphQueryService service;
@MockBean
private SemanticHubService semanticHubService;
- @MockBean(name = "authorizationService")
- private AuthorizationService authorizationService;
private static Stream corruptedJobs() {
return Stream.of(registerJobWithDepthAndAspect(110, null),
@@ -103,11 +106,12 @@ private static Stream corruptedJobs() {
}
@Test
- @WithMockUser(authorities = "view_irs")
void initiateJobForGlobalAssetId() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
final UUID returnedJob = UUID.randomUUID();
when(service.registerItemJob(any())).thenReturn(JobHandle.builder().id(returnedJob).build());
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
+
this.mockMvc.perform(post("/irs/jobs").contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(
@@ -117,26 +121,19 @@ void initiateJobForGlobalAssetId() throws Exception {
}
@Test
- void shouldReturnUnauthorizedStatusWhenAuthenticationIsMissing() throws Exception {
- this.mockMvc.perform(post("/irs/jobs").contentType(MediaType.APPLICATION_JSON)
- .content(new ObjectMapper().writeValueAsString(
- registerJobWithoutDepthAndAspect())))
- .andExpect(status().isUnauthorized());
+ void shouldReturnUnauthorizedStatusWhenAuthenticationIsMissing() {
+ when(authenticationService.getAuthentication(any(HttpServletRequest.class)))
+ .thenThrow(new BadCredentialsException("Wrong ApiKey"));
+
+ assertThatThrownBy(() -> this.mockMvc.perform(post("/irs/jobs").contentType(MediaType.APPLICATION_JSON)
+ .content(new ObjectMapper().writeValueAsString(
+ registerJobWithoutDepthAndAspect())))
+ ).isInstanceOf(AccessDeniedException.class);
}
@Test
- @WithMockUser(authorities = "view_irs_wrong_authority")
void shouldReturnForbiddenStatusWhenRequiredAuthorityIsMissing() throws Exception {
- this.mockMvc.perform(post("/irs/jobs").contentType(MediaType.APPLICATION_JSON)
- .content(new ObjectMapper().writeValueAsString(
- registerJobWithoutDepthAndAspect())))
- .andExpect(status().isForbidden());
- }
-
- @Test
- @WithMockUser(authorities = "view_irs")
- void shouldReturnForbiddenStatusWhenWrongBpnInJwtToken() throws Exception {
- when(authorizationService.verifyBpn()).thenReturn(Boolean.FALSE);
+ authenticateWith("view_irs_wrong_authority");
this.mockMvc.perform(post("/irs/jobs").contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(
@@ -146,16 +143,18 @@ void shouldReturnForbiddenStatusWhenWrongBpnInJwtToken() throws Exception {
@ParameterizedTest
@MethodSource("corruptedJobs")
- @WithMockUser(authorities = "view_irs")
void shouldReturnBadRequestWhenRegisterJobBodyNotValid(final RegisterJob registerJob) throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
this.mockMvc.perform(post("/irs/jobs").contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(registerJob)))
.andExpect(status().isBadRequest());
}
@Test
- @WithMockUser(authorities = "view_irs")
void shouldReturnBadRequestWhenRegisterJobHasWrongCallbackUrl() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
this.mockMvc.perform(post("/irs/jobs").contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(
registerJobWithUrl("hhh://example.com"))))
@@ -163,11 +162,11 @@ void shouldReturnBadRequestWhenRegisterJobHasWrongCallbackUrl() throws Exception
}
@Test
- @WithMockUser(authorities = "view_irs")
void shouldAcceptCorrectCallbackUrl() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
final UUID returnedJob = UUID.randomUUID();
when(service.registerItemJob(any())).thenReturn(JobHandle.builder().id(returnedJob).build());
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
this.mockMvc.perform(post("/irs/jobs").contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(
@@ -176,8 +175,9 @@ void shouldAcceptCorrectCallbackUrl() throws Exception {
}
@Test
- @WithMockUser(authorities = "view_irs")
void getJobsByState() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
final JobStatusResult returnedJob = JobStatusResult.builder()
.id(UUID.randomUUID())
.state(JobState.COMPLETED)
@@ -187,7 +187,6 @@ void getJobsByState() throws Exception {
final String returnJobAsString = objectMapper.writeValueAsString(returnedJob);
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
when(service.getJobsByState(any(), any())).thenReturn(
new PageResult(new PagedListHolder<>(List.of(returnedJob))));
@@ -205,20 +204,19 @@ void getJobsByState() throws Exception {
}
@Test
- @WithMockUser(authorities = "view_irs")
void cancelJobById() throws Exception {
- final Job canceledJob = Job.builder().id(jobId).state(JobState.CANCELED).build();
+ authenticateWith(IrsRoles.VIEW_IRS);
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
+ final Job canceledJob = Job.builder().id(jobId).state(JobState.CANCELED).build();
when(this.service.cancelJobById(jobId)).thenReturn(canceledJob);
this.mockMvc.perform(put("/irs/jobs/" + jobId)).andExpect(status().isOk());
}
@Test
- @WithMockUser(authorities = "view_irs")
void cancelJobById_throwEntityNotFoundException() throws Exception {
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
+ authenticateWith(IrsRoles.VIEW_IRS);
+
given(this.service.cancelJobById(jobId)).willThrow(
new ResponseStatusException(HttpStatus.NOT_FOUND, "No job exists with id " + jobId));
@@ -228,18 +226,19 @@ void cancelJobById_throwEntityNotFoundException() throws Exception {
}
@Test
- @WithMockUser(authorities = "view_irs")
void getJobWithMalformedIdShouldReturnBadRequest() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
final String jobIdMalformed = UUID.randomUUID() + "MALFORMED";
this.mockMvc.perform(get("/irs/jobs/" + jobIdMalformed)).andExpect(status().isBadRequest());
}
@Test
- @WithMockUser(authorities = "view_irs")
void shouldReturnBadRequestWhenRegisterJobWithMalformedAspectJson() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
when(service.registerItemJob(any())).thenThrow(IllegalArgumentException.class);
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
final String requestBody = "{ \"aspects\": [ \"MALFORMED\" ], \"globalAssetId\": \"urn:uuid:8a61c8db-561e-4db0-84ec-a693fc5ffdf6\" }";
this.mockMvc.perform(post("/irs/jobs").contentType(MediaType.APPLICATION_JSON).content(requestBody))
@@ -247,9 +246,9 @@ void shouldReturnBadRequestWhenRegisterJobWithMalformedAspectJson() throws Excep
}
@Test
- @WithMockUser(authorities = "view_irs")
void shouldReturnBadRequestWhenCancelingAlreadyCompletedJob() throws Exception {
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
+ authenticateWith(IrsRoles.VIEW_IRS);
+
given(this.service.cancelJobById(jobId)).willThrow(new IllegalStateException(
format("Cannot transition from state %s to %s", JobState.COMPLETED, JobState.CANCELED)));
@@ -259,8 +258,9 @@ void shouldReturnBadRequestWhenCancelingAlreadyCompletedJob() throws Exception {
}
@Test
- @WithMockUser(authorities = "view_irs")
void shouldReturnAspectModels() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
final AspectModel assemblyPartRelationship = AspectModel.builder()
.name("AssemblyPartRelationship")
.urn("urn:bamm:io.catenax.assembly_part_relationship:1.1.1#AssemblyPartRelationship")
@@ -274,7 +274,6 @@ void shouldReturnAspectModels() throws Exception {
.models(List.of(assemblyPartRelationship))
.build();
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
given(this.semanticHubService.getAllAspectModels()).willReturn(aspectModels);
final String aspectModelResponseAsString = objectMapper.writeValueAsString(aspectModels);
@@ -284,8 +283,9 @@ void shouldReturnAspectModels() throws Exception {
}
@Test
- @WithMockUser(authorities = "view_irs_wrong_authority")
void shouldReturnForbiddenStatusForAspectModelsWhenRequiredAuthorityIsMissing() throws Exception {
+ authenticateWith("view_irs_wrong_authority");
+
this.mockMvc.perform(get("/irs/aspectmodels").contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(
registerJobWithoutDepthAndAspect())))
@@ -293,13 +293,13 @@ void shouldReturnForbiddenStatusForAspectModelsWhenRequiredAuthorityIsMissing()
}
@Test
- @WithMockUser(authorities = "view_irs")
void shouldReturnPartialWhenJobCompleted() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
final Jobs runningJob = Jobs.builder().job(Job.builder().id(jobId).state(JobState.RUNNING).build()).build();
final boolean shouldIncludePartial = Boolean.TRUE;
final boolean shouldNotIncludePartial = Boolean.FALSE;
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
when(this.service.getJobForJobId(eq(jobId), anyBoolean())).thenReturn(runningJob);
this.mockMvc.perform(get("/irs/jobs/" + jobId).queryParam("returnUncompletedJob", String.valueOf(shouldIncludePartial))).andExpect(status().isPartialContent());
@@ -307,13 +307,13 @@ void shouldReturnPartialWhenJobCompleted() throws Exception {
}
@Test
- @WithMockUser(authorities = "view_irs")
void shouldReturnOkWhenJobCompleted() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
final Jobs completedJob = Jobs.builder().job(Job.builder().id(jobId).state(JobState.COMPLETED).build()).build();
final boolean shouldIncludePartial = Boolean.TRUE;
final boolean shouldNotIncludePartial = Boolean.FALSE;
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
when(this.service.getJobForJobId(eq(jobId), anyBoolean())).thenReturn(completedJob);
this.mockMvc.perform(get("/irs/jobs/" + jobId).queryParam("returnUncompletedJob", String.valueOf(shouldIncludePartial))).andExpect(status().isOk());
diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/IrsExceptionHandlerTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/IrsExceptionHandlerTest.java
index 59f061716c..8287dc8d6f 100644
--- a/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/IrsExceptionHandlerTest.java
+++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/IrsExceptionHandlerTest.java
@@ -32,9 +32,9 @@
import static org.springframework.web.client.HttpServerErrorException.InternalServerError;
import com.fasterxml.jackson.databind.ObjectMapper;
+import org.eclipse.tractusx.irs.ControllerTest;
import org.eclipse.tractusx.irs.common.auth.IrsRoles;
-import org.eclipse.tractusx.irs.configuration.SecurityConfiguration;
-import org.eclipse.tractusx.irs.common.auth.AuthorizationService;
+import org.eclipse.tractusx.irs.configuration.security.SecurityConfiguration;
import org.eclipse.tractusx.irs.services.IrsItemGraphQueryService;
import org.eclipse.tractusx.irs.services.SemanticHubService;
import org.junit.jupiter.api.Test;
@@ -44,29 +44,26 @@
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
-import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
@WebMvcTest(IrsController.class)
@Import(SecurityConfiguration.class)
-class IrsExceptionHandlerTest {
+class IrsExceptionHandlerTest extends ControllerTest {
@MockBean
private IrsItemGraphQueryService service;
@MockBean
private SemanticHubService semanticHubService;
- @MockBean(name = "authorizationService")
- private AuthorizationService authorizationService;
@Autowired
private MockMvc mockMvc;
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void handleAll() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
when(service.registerItemJob(any())).thenThrow(InternalServerError.class);
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
this.mockMvc.perform(post("/irs/jobs").contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(
@@ -75,10 +72,10 @@ void handleAll() throws Exception {
}
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldReturn500WhenGetSemanticModelsFails() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
when(semanticHubService.getAllAspectModels()).thenThrow(InternalServerError.class);
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
this.mockMvc.perform(get("/irs/aspectmodels").contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(
@@ -87,10 +84,10 @@ void shouldReturn500WhenGetSemanticModelsFails() throws Exception {
}
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldReturn400WhenProvidingBadInput() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
when(semanticHubService.getAllAspectModels()).thenThrow(IllegalArgumentException.class);
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
this.mockMvc.perform(get("/irs/aspectmodels").contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(
@@ -99,10 +96,10 @@ void shouldReturn400WhenProvidingBadInput() throws Exception {
}
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldReturn400WhenCatchingIllegalStateException() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
when(semanticHubService.getAllAspectModels()).thenThrow(IllegalStateException.class);
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
this.mockMvc.perform(get("/irs/aspectmodels").contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(
@@ -111,10 +108,10 @@ void shouldReturn400WhenCatchingIllegalStateException() throws Exception {
}
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldReturn400WhenCatchingMethodArgumentTypeMismatchException() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
when(semanticHubService.getAllAspectModels()).thenThrow(MethodArgumentTypeMismatchException.class);
- when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE);
this.mockMvc.perform(get("/irs/aspectmodels").contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(
@@ -123,8 +120,9 @@ void shouldReturn400WhenCatchingMethodArgumentTypeMismatchException() throws Exc
}
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldReturn403WhenRightsAreMissing() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
when(semanticHubService.getAllAspectModels()).thenThrow(AccessDeniedException.class);
this.mockMvc.perform(get("/irs/aspectmodels").contentType(MediaType.APPLICATION_JSON)
diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/ess/controller/EssControllerTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/ess/controller/EssControllerTest.java
index fd34ee0585..192f6600f5 100644
--- a/irs-api/src/test/java/org/eclipse/tractusx/irs/ess/controller/EssControllerTest.java
+++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/ess/controller/EssControllerTest.java
@@ -25,7 +25,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
-import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -34,23 +33,25 @@
import java.util.UUID;
import com.fasterxml.jackson.databind.ObjectMapper;
-import org.eclipse.tractusx.irs.ess.service.EssService;
-import org.eclipse.tractusx.irs.common.auth.AuthorizationService;
+import org.eclipse.tractusx.irs.ControllerTest;
import org.eclipse.tractusx.irs.common.auth.IrsRoles;
import org.eclipse.tractusx.irs.component.JobHandle;
import org.eclipse.tractusx.irs.component.Jobs;
import org.eclipse.tractusx.irs.component.PartChainIdentificationKey;
import org.eclipse.tractusx.irs.component.RegisterBpnInvestigationJob;
+import org.eclipse.tractusx.irs.configuration.security.SecurityConfiguration;
+import org.eclipse.tractusx.irs.ess.service.EssService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
-import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
@WebMvcTest(EssController.class)
-class EssControllerTest {
+@Import(SecurityConfiguration.class)
+class EssControllerTest extends ControllerTest {
@Autowired
private MockMvc mockMvc;
@@ -58,28 +59,26 @@ class EssControllerTest {
@MockBean
private EssService essService;
- @MockBean(name = "authorizationService")
- private AuthorizationService authorizationService;
-
private final String path = "/ess/bpn/investigations";
private final String globalAssetId = "urn:uuid:d3c0bf85-d44f-47c5-990d-fec8a36065c6";
private final String bpn = "BPNS000000000DDD";
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldRegisterBpnInvestigationForValidRequest() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
when(essService.startIrsJob(any(RegisterBpnInvestigationJob.class))).thenReturn(
JobHandle.builder().id(UUID.randomUUID()).build());
- this.mockMvc.perform(post(path).with(csrf())
- .contentType(MediaType.APPLICATION_JSON)
+ this.mockMvc.perform(post(path).contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(
reqBody(globalAssetId, List.of(bpn))))).andExpect(status().isCreated());
}
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldGetJob() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
+
final String jobId = UUID.randomUUID().toString();
when(essService.getIrsJob(jobId)).thenReturn(Jobs.builder().build());
@@ -87,20 +86,20 @@ void shouldGetJob() throws Exception {
}
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldReturnBadRequestForWrongGlobalAssetId() throws Exception {
- this.mockMvc.perform(post(path).with(csrf())
- .contentType(MediaType.APPLICATION_JSON)
+ authenticateWith(IrsRoles.VIEW_IRS);
+
+ this.mockMvc.perform(post(path).contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(
reqBody("wrongGlobalAssetId", List.of(bpn)))))
.andExpect(status().isBadRequest());
}
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldReturnBadRequestForWrongBpn() throws Exception {
- this.mockMvc.perform(post(path).with(csrf())
- .contentType(MediaType.APPLICATION_JSON)
+ authenticateWith(IrsRoles.VIEW_IRS);
+
+ this.mockMvc.perform(post(path).contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(
reqBody(globalAssetId, List.of(bpn, "WRONG_BPN")))))
.andExpect(status().isBadRequest());
diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/ess/controller/EssRecursiveControllerTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/ess/controller/EssRecursiveControllerTest.java
index fa74452107..7d08cde90f 100644
--- a/irs-api/src/test/java/org/eclipse/tractusx/irs/ess/controller/EssRecursiveControllerTest.java
+++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/ess/controller/EssRecursiveControllerTest.java
@@ -23,28 +23,30 @@
********************************************************************************/
package org.eclipse.tractusx.irs.ess.controller;
-import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.List;
import com.fasterxml.jackson.databind.ObjectMapper;
-import org.eclipse.tractusx.irs.ess.service.EssRecursiveService;
+import org.eclipse.tractusx.irs.ControllerTest;
import org.eclipse.tractusx.irs.common.auth.IrsRoles;
+import org.eclipse.tractusx.irs.configuration.security.SecurityConfiguration;
import org.eclipse.tractusx.irs.edc.client.model.notification.EdcNotification;
import org.eclipse.tractusx.irs.edc.client.model.notification.EdcNotificationHeader;
import org.eclipse.tractusx.irs.edc.client.model.notification.InvestigationNotificationContent;
+import org.eclipse.tractusx.irs.ess.service.EssRecursiveService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
-import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
@WebMvcTest(EssRecursiveController.class)
-class EssRecursiveControllerTest {
+@Import(SecurityConfiguration.class)
+class EssRecursiveControllerTest extends ControllerTest {
private final String path = "/ess/notification/receive-recursive";
@@ -55,11 +57,10 @@ class EssRecursiveControllerTest {
private EssRecursiveService essRecursiveService;
@Test
- @WithMockUser(authorities = IrsRoles.VIEW_IRS)
void shouldHandleRecursiveBpnInvestigationByNotification() throws Exception {
+ authenticateWith(IrsRoles.VIEW_IRS);
- this.mockMvc.perform(post(path).with(csrf())
- .contentType(MediaType.APPLICATION_JSON)
+ this.mockMvc.perform(post(path).contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(prepareNotification())))
.andExpect(status().isCreated());
}
diff --git a/irs-common/src/test/java/org/eclipse/tractusx/irs/common/auth/AuthorizationServiceTest.java b/irs-common/src/test/java/org/eclipse/tractusx/irs/common/auth/AuthorizationServiceTest.java
deleted file mode 100644
index 1cc89800f7..0000000000
--- a/irs-common/src/test/java/org/eclipse/tractusx/irs/common/auth/AuthorizationServiceTest.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2021,2022,2023
- * 2022: ZF Friedrichshafen AG
- * 2022: ISTOS GmbH
- * 2022,2023: Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
- * 2022,2023: BOSCH AG
- * Copyright (c) 2021,2022,2023 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Apache License, Version 2.0 which is available at
- * https://www.apache.org/licenses/LICENSE-2.0.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- ********************************************************************************/
-package org.eclipse.tractusx.irs.common.auth;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.springframework.security.oauth2.jwt.JwtClaimNames.SUB;
-
-import java.time.Instant;
-import java.util.Map;
-
-import org.junit.jupiter.api.Test;
-import org.mockito.Mockito;
-import org.springframework.security.core.context.SecurityContext;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.oauth2.jwt.Jwt;
-import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
-
-class AuthorizationServiceTest {
-
- @Test
- void shouldReturnTrueWhenTokenBpnIsEqualToAllowedBpn() {
- // given
- final String BPN = "BPNL00000001CRHK";
- final Map claims = Map.of(SUB, "sub", "clientId", "clientId", "bpn", BPN);
- thereIsJwtAuthenticationWithClaims(claims);
- final AuthorizationService authorizationService = new AuthorizationService(BPN);
-
- // when
- final Boolean isBpnAllowed = authorizationService.verifyBpn();
-
- // then
- assertThat(isBpnAllowed).isEqualTo(Boolean.TRUE);
- }
-
- @Test
- void shouldReturnFalseWhenTokenBpnIsDifferentThanAllowedBpn() {
- // given
- final String claimBPN = "BPNL00000003CRHK";
- final String configurationBPN = "BPNL00000003CML1";
- final Map claims = Map.of(SUB, "sub", "clientId", "clientId", "bpn", claimBPN);
- thereIsJwtAuthenticationWithClaims(claims);
- final AuthorizationService authorizationService = new AuthorizationService(configurationBPN);
-
- // when
- final Boolean isBpnAllowed = authorizationService.verifyBpn();
-
- // then
- assertThat(isBpnAllowed).isEqualTo(Boolean.FALSE);
- }
-
- @Test
- void shouldReturnFalseWhenNotAllowedBpnConfigured() {
- // given
- final String emptyConfigurationBPN = "";
- final AuthorizationService authorizationService = new AuthorizationService(emptyConfigurationBPN);
-
- // when
- final Boolean isBpnAllowed = authorizationService.verifyBpn();
-
- // then
- assertThat(isBpnAllowed).isEqualTo(Boolean.FALSE);
- }
-
- @Test
- void shouldReturnFalseTokenBpnIsMissing() {
- // given
- final String configurationBPN = "BPNL00000003CML1";
- final Map claims = Map.of(SUB, "sub", "clientId", "clientId");
- thereIsJwtAuthenticationWithClaims(claims);
- final AuthorizationService authorizationService = new AuthorizationService(configurationBPN);
-
- // when
- final Boolean isBpnAllowed = authorizationService.verifyBpn();
-
- // then
- assertThat(isBpnAllowed).isEqualTo(Boolean.FALSE);
- }
-
- private void thereIsJwtAuthenticationWithClaims(final Map claims) {
- final JwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(jwt(claims));
- SecurityContext securityContext = mock(SecurityContext.class);
- Mockito.when(securityContext.getAuthentication()).thenReturn(jwtAuthenticationToken);
- SecurityContextHolder.setContext(securityContext);
- }
-
- Jwt jwt(final Map claims) {
- return new Jwt("token", Instant.now(), Instant.now().plusSeconds(30), Map.of("alg", "none"), claims);
- }
-
-}
diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java
index efad6f94d8..ad18b47f6a 100644
--- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java
+++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java
@@ -99,7 +99,7 @@ public class PolicyStoreController {
})
@PostMapping("/policies")
@ResponseStatus(HttpStatus.CREATED)
- @PreAuthorize("@authorizationService.verifyBpn() && hasAuthority('" + IrsRoles.ADMIN_IRS + "')")
+ @PreAuthorize("hasAuthority('" + IrsRoles.ADMIN_IRS + "')")
public void registerAllowedPolicy(final @Valid @RequestBody CreatePolicyRequest request) {
service.registerPolicy(request);
}
@@ -128,7 +128,7 @@ public void registerAllowedPolicy(final @Valid @RequestBody CreatePolicyRequest
})
@GetMapping("/policies")
@ResponseStatus(HttpStatus.OK)
- @PreAuthorize("@authorizationService.verifyBpn() && hasAuthority('" + IrsRoles.ADMIN_IRS + "')")
+ @PreAuthorize("hasAuthority('" + IrsRoles.ADMIN_IRS + "')")
public List getPolicies() {
return service.getStoredPolicies();
}
@@ -160,7 +160,7 @@ public List getPolicies() {
})
@DeleteMapping("/policies/{policyId}")
@ResponseStatus(HttpStatus.OK)
- @PreAuthorize("@authorizationService.verifyBpn() && hasAuthority('" + IrsRoles.ADMIN_IRS + "')")
+ @PreAuthorize("hasAuthority('" + IrsRoles.ADMIN_IRS + "')")
public void deleteAllowedPolicy(@PathVariable("policyId") final String policyId) {
service.deletePolicy(policyId);
}
@@ -191,7 +191,7 @@ public void deleteAllowedPolicy(@PathVariable("policyId") final String policyId)
})
@PutMapping("/policies/{policyId}")
@ResponseStatus(HttpStatus.OK)
- @PreAuthorize("@authorizationService.verifyBpn() && hasAuthority('" + IrsRoles.ADMIN_IRS + "')")
+ @PreAuthorize("hasAuthority('" + IrsRoles.ADMIN_IRS + "')")
public void updateAllowedPolicy(@PathVariable("policyId") final String policyId,
final @Valid @RequestBody UpdatePolicyRequest request) {
service.updatePolicy(policyId, request);