Skip to content

Commit

Permalink
feat: remove BaseController, change Principal to Authenticationand un…
Browse files Browse the repository at this point in the history
…it test cases added
  • Loading branch information
thackerronak committed Jul 23, 2024
1 parent 87e8a5d commit 15425be
Show file tree
Hide file tree
Showing 12 changed files with 412 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* *******************************************************************************
* 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 jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Setter;
import org.eclipse.tractusx.managedidentitywallets.constant.StringPool;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.server.resource.BearerTokenError;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.util.StringUtils;

import java.util.LinkedHashMap;
import java.util.Map;

/**
* The type Custom authentication entry point.
*/
@Setter
public final class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

private String realmName;

@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) {
HttpStatus status = HttpStatus.UNAUTHORIZED;
Map<String, String> parameters = new LinkedHashMap<>();

if (this.realmName != null) {
parameters.put("realm", this.realmName);
}

if (authException instanceof OAuth2AuthenticationException) {
OAuth2Error error = ((OAuth2AuthenticationException) authException).getError();
parameters.put("error", error.getErrorCode());
if (StringUtils.hasText(error.getDescription())) {
parameters.put("error_description", error.getDescription());
}

if (StringUtils.hasText(error.getUri())) {
parameters.put("error_uri", error.getUri());
}

if (error instanceof BearerTokenError bearerTokenError) {
if (StringUtils.hasText(bearerTokenError.getScope())) {
parameters.put("scope", bearerTokenError.getScope());
}

status = ((BearerTokenError) error).getHttpStatus();
}
}

if (authException.getMessage().contains(StringPool.BPN_NOT_FOUND)) {
status = HttpStatus.FORBIDDEN;
}

String wwwAuthenticate = computeWWWAuthenticateHeaderValue(parameters);
response.addHeader(HttpHeaders.WWW_AUTHENTICATE, wwwAuthenticate);
response.setStatus(status.value());
}

private static String computeWWWAuthenticateHeaderValue(Map<String, String> parameters) {
StringBuilder wwwAuthenticate = new StringBuilder();
wwwAuthenticate.append("Bearer");
if (!parameters.isEmpty()) {
wwwAuthenticate.append(" ");
int i = 0;
for (Map.Entry<String, String> entry : parameters.entrySet()) {
wwwAuthenticate.append(entry.getKey()).append("=\"").append(entry.getValue()).append("\"");
if (i != parameters.size() - 1) {
wwwAuthenticate.append(", ");
}
i++;
}
}
return wwwAuthenticate.toString();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@

package org.eclipse.tractusx.managedidentitywallets.config.security;

import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.tractusx.managedidentitywallets.constant.ApplicationRole;
import org.eclipse.tractusx.managedidentitywallets.constant.RestURI;
import org.eclipse.tractusx.managedidentitywallets.service.STSTokenValidationService;
import org.eclipse.tractusx.managedidentitywallets.utils.BpnValidator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
Expand All @@ -39,6 +41,12 @@
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
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;
Expand All @@ -53,13 +61,16 @@
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true)
@Configuration
@AllArgsConstructor
@RequiredArgsConstructor
public class SecurityConfig {

private final STSTokenValidationService validationService;

private final SecurityConfigProperties securityConfigProperties;

@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuerUri;

/**
* Filter chain security filter chain.
*
Expand Down Expand Up @@ -115,7 +126,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//error
.requestMatchers(new AntPathRequestMatcher("/error")).permitAll()
).oauth2ResourceServer(resourceServer -> resourceServer.jwt(jwt ->
jwt.jwtAuthenticationConverter(new CustomAuthenticationConverter(securityConfigProperties.clientId()))))
jwt.jwtAuthenticationConverter(new CustomAuthenticationConverter(securityConfigProperties.clientId())))
.authenticationEntryPoint(new CustomAuthenticationEntryPoint()))
.addFilterAfter(new PresentationIatpFilter(validationService), BasicAuthenticationFilter.class);

return http.build();
Expand All @@ -141,4 +153,17 @@ public WebSecurityCustomizer securityCustomizer() {
(ApplicationEventPublisher applicationEventPublisher) {
return new DefaultAuthenticationEventPublisher(applicationEventPublisher);
}

@Bean
JwtDecoder jwtDecoder() {
NimbusJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(issuerUri);
OAuth2TokenValidator<Jwt> bpnValidator = bpnValidator();
OAuth2TokenValidator<Jwt> withBpn = new DelegatingOAuth2TokenValidator<>(bpnValidator);
jwtDecoder.setJwtValidator(withBpn);
return jwtDecoder;
}

OAuth2TokenValidator<Jwt> bpnValidator() {
return new BpnValidator();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,5 @@ private StringPool() {
public static final String SECURITY_TOKEN_SERVICE = "SecurityTokenService";
public static final String CREDENTIAL_SERVICE = "CredentialService";
public static final String HTTPS_SCHEME = "https://";
public static final String BPN_NOT_FOUND = "BPN not found";
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
@RequiredArgsConstructor
@Tag(name = "DIDDocument")
@Slf4j
public class DidDocumentController extends BaseController {
public class DidDocumentController {
private final DidDocumentService service;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,18 @@
import org.eclipse.tractusx.managedidentitywallets.constant.StringPool;
import org.eclipse.tractusx.managedidentitywallets.dto.CredentialsResponse;
import org.eclipse.tractusx.managedidentitywallets.service.HoldersCredentialService;
import org.eclipse.tractusx.managedidentitywallets.utils.TokenParsingUtils;
import org.springframework.data.domain.PageImpl;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.security.Principal;
import java.util.List;
import java.util.Map;

Expand All @@ -58,7 +59,7 @@
@RequiredArgsConstructor
@Slf4j
@Tag(name = "Verifiable Credential - Holder")
public class HoldersCredentialController extends BaseController {
public class HoldersCredentialController {

private final HoldersCredentialService holdersCredentialService;

Expand All @@ -71,7 +72,7 @@ public class HoldersCredentialController extends BaseController {
* @param type the type
* @param sortColumn the sort column
* @param sortTpe the sort tpe
* @param principal the principal
* @param authentication the authentication
* @return the credentials
*/
@GetCredentialsApiDocs
Expand All @@ -94,8 +95,8 @@ public ResponseEntity<PageImpl<CredentialsResponse>> getCredentials(@Parameter(n
@Min(0) @Max(Integer.MAX_VALUE) @Parameter(description = "Number of records per page") @RequestParam(required = false, defaultValue = Integer.MAX_VALUE + "") int size,
@AsJwtParam @RequestParam(name = StringPool.AS_JWT, defaultValue = "false") boolean asJwt,

Principal principal) {
log.debug("Received request to get credentials. BPN: {}", getBPNFromToken(principal));
Authentication authentication) {
log.debug("Received request to get credentials. BPN: {}", TokenParsingUtils.getBPNFromToken(authentication));
final GetCredentialsCommand command;
command = GetCredentialsCommand.builder()
.credentialId(credentialId)
Expand All @@ -106,7 +107,7 @@ public ResponseEntity<PageImpl<CredentialsResponse>> getCredentials(@Parameter(n
.pageNumber(pageNumber)
.size(size)
.asJwt(asJwt)
.callerBPN(getBPNFromToken(principal))
.callerBPN(TokenParsingUtils.getBPNFromToken(authentication))
.build();
return ResponseEntity.status(HttpStatus.OK).body(holdersCredentialService.getCredentials(command));
}
Expand All @@ -115,17 +116,17 @@ public ResponseEntity<PageImpl<CredentialsResponse>> getCredentials(@Parameter(n
/**
* Issue credential response entity.
*
* @param data the data
* @param principal the principal
* @param data the data
* @param authentication the authentication
* @return the response entity
*/

@IssueCredentialApiDoc
@PostMapping(path = RestURI.CREDENTIALS, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<CredentialsResponse> issueCredential(@RequestBody Map<String, Object> data, Principal principal,
public ResponseEntity<CredentialsResponse> issueCredential(@RequestBody Map<String, Object> data, Authentication authentication,
@AsJwtParam @RequestParam(name = "asJwt", defaultValue = "false") boolean asJwt
) {
log.debug("Received request to issue credential. BPN: {}", getBPNFromToken(principal));
return ResponseEntity.status(HttpStatus.CREATED).body(holdersCredentialService.issueCredential(data, getBPNFromToken(principal), asJwt));
log.debug("Received request to issue credential. BPN: {}", TokenParsingUtils.getBPNFromToken(authentication));
return ResponseEntity.status(HttpStatus.CREATED).body(holdersCredentialService.issueCredential(data, TokenParsingUtils.getBPNFromToken(authentication), asJwt));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,18 @@
import org.eclipse.tractusx.managedidentitywallets.dto.CredentialVerificationRequest;
import org.eclipse.tractusx.managedidentitywallets.dto.CredentialsResponse;
import org.eclipse.tractusx.managedidentitywallets.service.IssuersCredentialService;
import org.eclipse.tractusx.managedidentitywallets.utils.TokenParsingUtils;
import org.springframework.data.domain.PageImpl;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.security.Principal;
import java.util.List;
import java.util.Map;

Expand All @@ -57,7 +58,7 @@
@RestController
@RequiredArgsConstructor
@Slf4j
public class IssuersCredentialController extends BaseController {
public class IssuersCredentialController {

/**
* The constant API_TAG_VERIFIABLE_CREDENTIAL_ISSUER.
Expand All @@ -81,7 +82,7 @@ public class IssuersCredentialController extends BaseController {
* @param size the size
* @param sortColumn the sort column
* @param sortTpe the sort tpe
* @param principal the principal
* @param authentication the authentication
* @return the credentials
*/
@GetCredentialsApiDocs
Expand All @@ -101,8 +102,8 @@ public ResponseEntity<PageImpl<CredentialsResponse>> getCredentials(@Parameter(n
) @RequestParam(required = false, defaultValue = "createdAt") String sortColumn,
@Parameter(name = "sortTpe", description = "Sort order", examples = { @ExampleObject(value = "desc", name = "Descending order"), @ExampleObject(value = "asc", name = "Ascending order") }) @RequestParam(required = false, defaultValue = "desc") String sortTpe,
@AsJwtParam @RequestParam(name = StringPool.AS_JWT, defaultValue = "false") boolean asJwt,
Principal principal) {
log.debug("Received request to get credentials. BPN: {}", getBPNFromToken(principal));
Authentication authentication) {
log.debug("Received request to get credentials. BPN: {}", TokenParsingUtils.getBPNFromToken(authentication));
final GetCredentialsCommand command;
command = GetCredentialsCommand.builder()
.credentialId(credentialId)
Expand All @@ -113,7 +114,7 @@ public ResponseEntity<PageImpl<CredentialsResponse>> getCredentials(@Parameter(n
.pageNumber(pageNumber)
.size(size)
.asJwt(asJwt)
.callerBPN(getBPNFromToken(principal))
.callerBPN(TokenParsingUtils.getBPNFromToken(authentication))
.build();
return ResponseEntity.status(HttpStatus.OK).body(issuersCredentialService.getCredentials(command));
}
Expand All @@ -139,14 +140,14 @@ public ResponseEntity<Map<String, Object>> credentialsValidation(@RequestBody Cr
*
* @param holderDid the holder did
* @param data the data
* @param principal the principal
* @param authentication the authentication
* @return the response entity
*/
@PostMapping(path = RestURI.ISSUERS_CREDENTIALS, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@IssueVerifiableCredentialUsingBaseWalletApiDocs
public ResponseEntity<CredentialsResponse> issueCredentialUsingBaseWallet(@Parameter(description = "Holder DID", examples = {@ExampleObject(description = "did", name = "did", value = "did:web:localhost:BPNL000000000000")}) @RequestParam(name = "holderDid") String holderDid, @RequestBody Map<String, Object> data, Principal principal,
public ResponseEntity<CredentialsResponse> issueCredentialUsingBaseWallet(@Parameter(description = "Holder DID", examples = {@ExampleObject(description = "did", name = "did", value = "did:web:localhost:BPNL000000000000")}) @RequestParam(name = "holderDid") String holderDid, @RequestBody Map<String, Object> data, Authentication authentication,
@AsJwtParam @RequestParam(name = StringPool.AS_JWT, defaultValue = "false") boolean asJwt) {
log.debug("Received request to issue verifiable credential. BPN: {}", getBPNFromToken(principal));
return ResponseEntity.status(HttpStatus.CREATED).body(issuersCredentialService.issueCredentialUsingBaseWallet(holderDid, data, asJwt, getBPNFromToken(principal)));
log.debug("Received request to issue verifiable credential. BPN: {}", TokenParsingUtils.getBPNFromToken(authentication));
return ResponseEntity.status(HttpStatus.CREATED).body(issuersCredentialService.issueCredentialUsingBaseWallet(holderDid, data, asJwt, TokenParsingUtils.getBPNFromToken(authentication)));
}
}
Loading

0 comments on commit 15425be

Please sign in to comment.