Skip to content

Commit

Permalink
feat(impl):[#259] new authentication through api key impl
Browse files Browse the repository at this point in the history
  • Loading branch information
ds-ext-kmassalski committed Dec 15, 2023
1 parent ea00cda commit f60a102
Show file tree
Hide file tree
Showing 30 changed files with 540 additions and 594 deletions.
4 changes: 2 additions & 2 deletions .config/spotbugs-excludes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@
</Match>
<Match>
<!-- CSRF protection turned off on purpose. -->
<Class name="org.eclipse.tractusx.irs.configuration.SecurityConfiguration"/>
<Class name="org.eclipse.tractusx.irs.configuration.security.SecurityConfiguration"/>
<Method name="securityFilterChain"/>
<Bug pattern="SPRING_CSRF_PROTECTION_DISABLED"/>
</Match>
<Match>
<!-- We want to explicitly throw Exception in this method. -->
<Class name="org.eclipse.tractusx.irs.configuration.SecurityConfiguration"/>
<Class name="org.eclipse.tractusx.irs.configuration.security.SecurityConfiguration"/>
<Method name="securityFilterChain"/>
<Bug pattern="THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION"/>
</Match>
Expand Down
15 changes: 5 additions & 10 deletions charts/irs-helm/templates/configmap-spring-app-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down Expand Up @@ -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 }}
Expand Down Expand Up @@ -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 }}
9 changes: 2 additions & 7 deletions charts/irs-helm/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ readinessProbe:
# IRS Configuration #
#####################
irsUrl: # "https://<irs-url>"
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

Expand Down Expand Up @@ -153,7 +154,6 @@ oauth2:
clientId: # <oauth2-client-id>
clientSecret: # <oauth2-client-secret>
clientTokenUri: # <oauth2-token-uri>
jwkSetUri: # <oauth2-jwkset-uri>
portal:
oauth2:
clientId: # <portal-client-id>
Expand Down Expand Up @@ -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!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -37,7 +39,7 @@
/**
* Application entry point.
*/
@SpringBootApplication(exclude = WebSocketServletAutoConfiguration.class)
@SpringBootApplication(exclude = { SecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class, WebSocketServletAutoConfiguration.class })
@EnableScheduling
@EnableCaching
@EnableAsync
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ private void registerOverallHealthMetric(final MeterRegistry registry,
log.debug("Registering metric '{}'", metricDescriptor.name);

final ToDoubleFunction<DependenciesHealthIndicator> statusProvider = //
healthIndicator -> HealthStatusHelper.healthStatustoNumeric(overallStatus(healthIndicator));
healthIndicator -> HealthStatusHelper.healthStatusToNumeric(overallStatus(healthIndicator));

Gauge.builder(metricDescriptor.name, dependenciesHealthIndicator, statusProvider)
.description(metricDescriptor.description())
Expand Down Expand Up @@ -103,7 +103,7 @@ private void registerIrsDependencyHealthMetric(final MeterRegistry registry,
log.debug("Registering metric '{}' tag '{}'", metricDescriptor.name, dependencyName);

final ToDoubleFunction<DependenciesHealthIndicator> statusProvider = //
healthIndicator -> HealthStatusHelper.healthStatustoNumeric(
healthIndicator -> HealthStatusHelper.healthStatusToNumeric(
getIrsDependencyStatus(healthIndicator, dependencyName));

Gauge.builder(metricDescriptor.name, dependenciesHealthIndicator, statusProvider)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
Loading

0 comments on commit f60a102

Please sign in to comment.