diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/PresentationIatpFilter.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/PresentationIatpFilter.java deleted file mode 100644 index cefefb8fb..000000000 --- a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/PresentationIatpFilter.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * ******************************************************************************* - * Copyright (c) 2021,2024 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.managedidentitywallets.config.security; - -import io.micrometer.common.util.StringUtils; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletResponse; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; -import org.eclipse.tractusx.managedidentitywallets.dto.ValidationResult; -import org.eclipse.tractusx.managedidentitywallets.service.STSTokenValidationService; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.web.filter.GenericFilterBean; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.COMA_SEPARATOR; - -public class PresentationIatpFilter extends GenericFilterBean { - - RequestMatcher customFilterUrl1 = new AntPathRequestMatcher(RestURI.API_PRESENTATIONS_IATP); - RequestMatcher customFilterUrl2 = new AntPathRequestMatcher(RestURI.API_PRESENTATIONS_IATP_WORKAROUND); - - STSTokenValidationService validationService; - - public PresentationIatpFilter(STSTokenValidationService validationService) { - this.validationService = validationService; - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - - HttpServletRequest httpServletRequest = (HttpServletRequest) request; - HttpServletResponse httpServletResponse = (HttpServletResponse) response; - - if (customFilterUrl1.matches(httpServletRequest) || customFilterUrl2.matches(httpServletRequest)) { - String authHeader = httpServletRequest.getHeader("Authorization"); - if (StringUtils.isEmpty(authHeader)) { - httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - } else { - ValidationResult result = validationService.validateToken(authHeader); - if (!result.isValid()) { - List errorValues = new ArrayList<>(); - result.getErrors().forEach(c -> errorValues.add(c.name())); - String content = String.join(COMA_SEPARATOR, errorValues); - - httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - httpServletResponse.setContentLength(content.length()); - httpServletResponse.getWriter().write(content); - } else { - chain.doFilter(request, response); - } - } - } else { - chain.doFilter(request, response); - } - } -} diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfig.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfig.java index 937060e10..a0a6178e2 100644 --- a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfig.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfig.java @@ -48,8 +48,11 @@ import org.springframework.security.oauth2.jwt.JwtDecoders; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.NegatedRequestMatcher; +import org.springframework.security.web.util.matcher.OrRequestMatcher; + +import java.util.List; import static org.springframework.http.HttpMethod.GET; import static org.springframework.http.HttpMethod.POST; @@ -127,8 +130,11 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .requestMatchers(new AntPathRequestMatcher("/error")).permitAll() ).oauth2ResourceServer(resourceServer -> resourceServer.jwt(jwt -> jwt.jwtAuthenticationConverter(new CustomAuthenticationConverter(securityConfigProperties.clientId()))) - .authenticationEntryPoint(new CustomAuthenticationEntryPoint())) - .addFilterAfter(new PresentationIatpFilter(validationService), BasicAuthenticationFilter.class); + .authenticationEntryPoint(new CustomAuthenticationEntryPoint())) + .securityMatcher(new NegatedRequestMatcher(new OrRequestMatcher( + List.of( + new AntPathRequestMatcher(RestURI.API_PRESENTATIONS_IATP), + new AntPathRequestMatcher(RestURI.API_PRESENTATIONS_IATP_WORKAROUND))))); return http.build(); } diff --git a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationController.java b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationController.java index 315678228..cb38bca5a 100644 --- a/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationController.java +++ b/miw/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationController.java @@ -23,6 +23,7 @@ import com.nimbusds.jwt.SignedJWT; import io.swagger.v3.oas.annotations.Parameter; +import liquibase.util.StringUtil; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -33,6 +34,7 @@ import org.eclipse.tractusx.managedidentitywallets.dto.PresentationResponseMessage; import org.eclipse.tractusx.managedidentitywallets.reader.TractusXPresentationRequestReader; import org.eclipse.tractusx.managedidentitywallets.service.PresentationService; +import org.eclipse.tractusx.managedidentitywallets.service.STSTokenValidationService; import org.eclipse.tractusx.managedidentitywallets.utils.TokenParsingUtils; import org.eclipse.tractusx.ssi.lib.model.verifiable.presentation.VerifiablePresentation; import org.springframework.http.HttpStatus; @@ -63,6 +65,8 @@ public class PresentationController { private final TractusXPresentationRequestReader presentationRequestReader; + private final STSTokenValidationService validationService; + /** * Create presentation response entity. * @@ -114,17 +118,37 @@ public ResponseEntity> validatePresentation(@RequestBody Map @PostMapping(path = { RestURI.API_PRESENTATIONS_IATP, RestURI.API_PRESENTATIONS_IATP_WORKAROUND }, produces = { MediaType.APPLICATION_JSON_VALUE }) @GetVerifiablePresentationIATPApiDocs @SneakyThrows - public ResponseEntity createPresentation(@Parameter(hidden = true) @RequestHeader(name = "Authorization") String stsToken, - @RequestParam(name = "asJwt", required = false, defaultValue = "false") boolean asJwt, - InputStream is) { + public ResponseEntity createPresentation( + /* As filters are disabled for this endpoint set required to false and handle missing token manually */ + @Parameter(hidden = true) @RequestHeader(name = "Authorization", required = false) String stsToken, + @RequestParam(name = "asJwt", required = false, defaultValue = "false") boolean asJwt, + InputStream is) { try { + if(stsToken == null){ + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } + + if (stsToken.startsWith("Bearer ")) { + stsToken = stsToken.substring("Bearer ".length()); + } + + var validationResult = validationService.validateToken(stsToken); + if (!validationResult.isValid()) { + log.atDebug().log("Unauthorized request. Errors: '%s'".formatted( + StringUtil.join(validationResult.getErrors().stream() + .map(Enum::name) + .toList(), + ", "))); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } + + // requested scopes are ignored for now final List requestedScopes = presentationRequestReader.readVerifiableCredentialScopes(is); - // requested scopes are ignored until the documentation is better refined SignedJWT accessToken = getAccessToken(stsToken); Map map = presentationService.createVpWithRequiredScopes(accessToken, asJwt); - VerifiablePresentation verifiablePresentation = new VerifiablePresentation((Map)map.get("vp")); + VerifiablePresentation verifiablePresentation = new VerifiablePresentation((Map) map.get("vp")); PresentationResponseMessage message = new PresentationResponseMessage(verifiablePresentation); return ResponseEntity.ok(message); } catch (TractusXPresentationRequestReader.InvalidPresentationQueryMessageResource e) { diff --git a/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationIatpFilterTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationIatpFilterTest.java index ed2555bdd..d85994f17 100644 --- a/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationIatpFilterTest.java +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationIatpFilterTest.java @@ -70,7 +70,7 @@ void createPresentationFailure401Test() { HttpEntity entity = new HttpEntity<>(headers); ResponseEntity> response = testTemplate.exchange( RestURI.API_PRESENTATIONS_IATP, - HttpMethod.GET, + HttpMethod.POST, entity, new ParameterizedTypeReference<>() { } @@ -94,15 +94,12 @@ void createPresentationFailure401WithErrorsTest() { ResponseEntity response = testTemplate.exchange( RestURI.API_PRESENTATIONS_IATP, - HttpMethod.GET, + HttpMethod.POST, entity, new ParameterizedTypeReference<>() { } ); - String expectedBody = TOKEN_ALREADY_EXPIRED.name() + StringPool.COMA_SEPARATOR + NONCE_MISSING.name(); - Assertions.assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode()); - Assertions.assertEquals(expectedBody, response.getBody()); } } diff --git a/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/identityminustrust/TokenRequestTest.java b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/identityminustrust/TokenRequestTest.java index 44317ee08..0bbfeeec5 100644 --- a/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/identityminustrust/TokenRequestTest.java +++ b/miw/src/test/java/org/eclipse/tractusx/managedidentitywallets/identityminustrust/TokenRequestTest.java @@ -148,7 +148,7 @@ public void testPresentationQueryWithToken() { final Map data2 = MAPPER.readValue(message2, Map.class); final HttpHeaders headers2 = new HttpHeaders(); - headers2.set(HttpHeaders.AUTHORIZATION, jwt); + headers2.set(HttpHeaders.AUTHORIZATION, "Bearer " + jwt); final HttpEntity> entity2 = new HttpEntity<>(data2, headers2); var result2 = restTemplate .postForEntity(RestURI.API_PRESENTATIONS_IATP, entity2, String.class);