Skip to content

Commit

Permalink
feat: caller BPN security added and test case modification for the same
Browse files Browse the repository at this point in the history
  • Loading branch information
nitin-vavdiya committed Jun 1, 2023
1 parent f6e1cc3 commit b375317
Show file tree
Hide file tree
Showing 20 changed files with 608 additions and 154 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.requestMatchers(new AntPathRequestMatcher(RestURI.CREDENTIALS_ISSUER_MEMBERSHIP, POST.name())).hasAnyRole(ApplicationConstant.ROLE_UPDATE_WALLETS, ApplicationConstant.ROLE_UPDATE_WALLET) //issue Membership Credential
.requestMatchers(new AntPathRequestMatcher(RestURI.CREDENTIALS_ISSUER_DISMANTLER, POST.name())).hasAnyRole(ApplicationConstant.ROLE_UPDATE_WALLETS, ApplicationConstant.ROLE_UPDATE_WALLET) //issue dismantler Credential
.requestMatchers(new AntPathRequestMatcher(RestURI.API_CREDENTIALS_ISSUER_FRAMEWORK, POST.name())).hasAnyRole(ApplicationConstant.ROLE_UPDATE_WALLETS, ApplicationConstant.ROLE_UPDATE_WALLET) //issue dismantler Credential

.requestMatchers(new AntPathRequestMatcher(RestURI.API_PRESENTATIONS, POST.name())).hasAnyRole(ApplicationConstant.ROLE_UPDATE_WALLETS, ApplicationConstant.ROLE_UPDATE_WALLET) //issue dismantler Credential
.requestMatchers(new AntPathRequestMatcher("/error")).permitAll()
.and().oauth2ResourceServer()
.jwt()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* *******************************************************************************
* Copyright (c) 2021,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.managedidentitywallets.controller;

import org.eclipse.tractusx.managedidentitywallets.exception.ForbiddenException;
import org.eclipse.tractusx.managedidentitywallets.utils.Validate;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;

import java.security.Principal;

public class BaseController {

public String getBPNFromToken(Principal principal) {
Object principal1 = ((JwtAuthenticationToken) principal).getPrincipal();
Jwt jwt = (Jwt) principal1;

Validate.isFalse(jwt.getClaims().containsKey("BPN")).launch(new ForbiddenException("Invalid token, BPN not found"));

return jwt.getClaims().get("BPN").toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

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

/**
Expand All @@ -44,27 +45,30 @@
@RestController
@RequiredArgsConstructor
@Tag(name = "VerifiableCredentials")
public class CredentialController {
public class CredentialController extends BaseController {

private final CredentialService service;


/**
* Gets credentials.
*
* @param holderIdentifier the holder identifier
* @param id the id
* @param issuerIdentifier the issuer identifier
* @param type the type
* @param sortColumn the sort column
* @param sortTpe the sort tpe
* @param principal the principal
* @return the credentials
*/
@Operation(description = "Permission: **view_wallets** OR **view_wallet** (The BPN of holderIdentifier must equal BPN of caller)\n\n Search verifiable credentials with filter criteria", summary = "Query Verifiable Credentials")
@GetMapping(path = RestURI.CREDENTIALS, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<VerifiableCredential>> getCredentials(@RequestParam(required = false) String holderIdentifier, @RequestParam(required = false) String id,
public ResponseEntity<List<VerifiableCredential>> getCredentials(@RequestParam(required = false) String id,
@RequestParam(required = false) String issuerIdentifier,
@RequestParam(required = false) List<String> type,
@RequestParam(required = false, defaultValue = "createdAt") String sortColumn,
@RequestParam(required = false, defaultValue = "desc") String sortTpe) {
return ResponseEntity.status(HttpStatus.OK).body(service.getCredentials(holderIdentifier, id, issuerIdentifier, type, sortColumn, sortTpe));
@RequestParam(required = false, defaultValue = "desc") String sortTpe, Principal principal) {
return ResponseEntity.status(HttpStatus.OK).body(service.getCredentials(id, issuerIdentifier, type, sortColumn, sortTpe, getBPNFromToken(principal)));
}

/**
Expand All @@ -75,31 +79,33 @@ public ResponseEntity<List<VerifiableCredential>> getCredentials(@RequestParam(r
*/
@Operation(summary = "Issue a Membership Verifiable Credential with base wallet issuer", description = "Permission: **update_wallets** OR **update_wallet** (The BPN of base wallet must equal BPN of caller)\n\n Issue a verifiable credential by base wallet")
@PostMapping(path = RestURI.CREDENTIALS_ISSUER_MEMBERSHIP, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<VerifiableCredential> issueMembershipCredential(@Valid @RequestBody IssueMembershipCredentialRequest issueMembershipCredentialRequest) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.issueMembershipCredential(issueMembershipCredentialRequest));
public ResponseEntity<VerifiableCredential> issueMembershipCredential(@Valid @RequestBody IssueMembershipCredentialRequest issueMembershipCredentialRequest, Principal principal) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.issueMembershipCredential(issueMembershipCredentialRequest, getBPNFromToken(principal)));
}

/**
* Issue dismantler credential response entity.
*
* @param request the request
* @param request the request
* @param principal the principal
* @return the response entity
*/
@Operation(summary = "Issue a Dismantler Verifiable Credential with base wallet issuer", description = "Permission: **update_wallets** OR **update_wallet** (The BPN of base wallet must equal BPN of caller)\n\n Issue a verifiable credential by base wallet")
@PostMapping(path = RestURI.CREDENTIALS_ISSUER_DISMANTLER, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<VerifiableCredential> issueDismantlerCredential(@Valid @RequestBody IssueDismantlerCredentialRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.issueDismantlerCredential(request));
public ResponseEntity<VerifiableCredential> issueDismantlerCredential(@Valid @RequestBody IssueDismantlerCredentialRequest request, Principal principal) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.issueDismantlerCredential(request, getBPNFromToken(principal)));
}

/**
* Issue framework credential response entity.
*
* @param request the request
* @param request the request
* @param principal the principal
* @return the response entity
*/
@Operation(summary = "Issue a Use Case Verifiable Credential with base wallet issuer", description = "Permission: **update_wallets** OR **update_wallet** (The BPN of base wallet must equal BPN of caller)\n\n Issue a verifiable credential by base wallet")
@PostMapping(path = RestURI.API_CREDENTIALS_ISSUER_FRAMEWORK, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<VerifiableCredential> issueFrameworkCredential(@Valid @RequestBody IssueFrameworkCredentialRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.issueFrameworkCredential(request));
public ResponseEntity<VerifiableCredential> issueFrameworkCredential(@Valid @RequestBody IssueFrameworkCredentialRequest request, Principal principal) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.issueFrameworkCredential(request, getBPNFromToken(principal)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
@RequiredArgsConstructor
@Tag(name = "VerifiablePresentations")
public class PresentationController {
public class PresentationController extends BaseController {

private final PresentationService presentationService;

Expand Down Expand Up @@ -81,8 +82,8 @@ public class PresentationController {
"""))
})
public ResponseEntity<Map<String, Object>> createPresentation(@RequestBody Map<String, Object> data,
@RequestParam(name = "asJwt", required = false, defaultValue = "false") boolean asJwt
@RequestParam(name = "asJwt", required = false, defaultValue = "false") boolean asJwt, Principal principal
) {
return ResponseEntity.status(HttpStatus.CREATED).body(presentationService.createPresentation(data, asJwt, "smartSense"));
return ResponseEntity.status(HttpStatus.CREATED).body(presentationService.createPresentation(data, asJwt, "smartSense", getBPNFromToken(principal)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

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

/**
Expand All @@ -45,7 +46,7 @@
@RestController
@RequiredArgsConstructor
@Tag(name = "Wallets")
public class WalletController {
public class WalletController extends BaseController {

private final WalletService service;

Expand Down Expand Up @@ -105,8 +106,8 @@ public ResponseEntity<Wallet> createWallet(@Valid @RequestBody CreateWalletReque
}
"""))
})
public ResponseEntity<Map<String, String>> storeCredential(@RequestBody Map<String, Object> data, @PathVariable(name = "identifier") String identifier) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.storeCredential(data, identifier));
public ResponseEntity<Map<String, String>> storeCredential(@RequestBody Map<String, Object> data, @PathVariable(name = "identifier") String identifier, Principal principal) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.storeCredential(data, identifier, getBPNFromToken(principal)));
}

/**
Expand All @@ -118,8 +119,11 @@ public ResponseEntity<Map<String, String>> storeCredential(@RequestBody Map<Stri
*/
@Operation(summary = "Retrieve wallet by identifier", description = "Permission: **view_wallets** OR **view_wallet** (The BPN of Wallet to retrieve must equal the BPN of caller) \n\n Retrieve single wallet by identifier, with or without its credentials")
@GetMapping(path = RestURI.API_WALLETS_IDENTIFIER, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Wallet> getWalletByIdentifier(@PathVariable(name = "identifier") String identifier, @RequestParam(name = "withCredentials", defaultValue = "false") boolean withCredentials) {
return ResponseEntity.status(HttpStatus.OK).body(service.getWalletByIdentifier(identifier, withCredentials));
public ResponseEntity<Wallet> getWalletByIdentifier(@PathVariable(name = "identifier") String identifier,
@RequestParam(name = "withCredentials", defaultValue = "false") boolean withCredentials,
Principal principal) {

return ResponseEntity.status(HttpStatus.OK).body(service.getWalletByIdentifier(identifier, withCredentials, getBPNFromToken(principal)));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@
import org.eclipse.tractusx.managedidentitywallets.dto.IssueFrameworkCredentialRequest;
import org.eclipse.tractusx.managedidentitywallets.dto.IssueMembershipCredentialRequest;
import org.eclipse.tractusx.managedidentitywallets.exception.DuplicateCredentialProblem;
import org.eclipse.tractusx.managedidentitywallets.exception.ForbiddenException;
import org.eclipse.tractusx.managedidentitywallets.utils.CommonUtils;
import org.eclipse.tractusx.managedidentitywallets.utils.Validate;
import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential;
import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialType;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
Expand All @@ -61,6 +63,7 @@
@RequiredArgsConstructor
public class CredentialService extends BaseService<Credential, Long> {

public static final String BASE_WALLET_BPN_IS_NOT_MATCHING_WITH_REQUEST_BPN_FROM_TOKEN = "Base wallet BPN is not matching with request BPN(from token)";
private final CredentialRepository credentialRepository;
private final MIWSettings miwSettings;
private final WalletService walletService;
Expand All @@ -84,20 +87,20 @@ protected SpecificationUtil<Credential> getSpecificationUtil() {
/**
* Gets credentials.
*
* @param holderIdentifier the holder identifier
* @param id the id
* @param issuerIdentifier the issuer identifier
* @param type the type
* @param sortColumn the sort column
* @param sortType the sort type
* @param callerBPN the caller bpn
* @return the credentials
*/
public List<VerifiableCredential> getCredentials(String holderIdentifier, String id, String issuerIdentifier, List<String> type, String sortColumn, String sortType) {
public List<VerifiableCredential> getCredentials(String id, String issuerIdentifier, List<String> type, String sortColumn, String sortType, String callerBPN) {
FilterRequest filterRequest = new FilterRequest();
if (StringUtils.hasText(holderIdentifier)) {
Wallet holderWallet = walletService.getWalletByIdentifier(holderIdentifier);
filterRequest.appendNewCriteria("holderDid", Operator.EQUALS, holderWallet.getDid());
}


Wallet holderWallet = walletService.getWalletByIdentifier(callerBPN);
filterRequest.appendNewCriteria("holderDid", Operator.EQUALS, holderWallet.getDid());

if (StringUtils.hasText(issuerIdentifier)) {
Wallet issuerWallet = walletService.getWalletByIdentifier(issuerIdentifier);
Expand Down Expand Up @@ -126,15 +129,18 @@ public List<VerifiableCredential> getCredentials(String holderIdentifier, String
/**
* Issue framework credential verifiable credential.
*
* @param request the request
* @param request the request
* @param callerBPN the caller bpn
* @return the verifiable credential
*/
public VerifiableCredential issueFrameworkCredential(IssueFrameworkCredentialRequest request) {
public VerifiableCredential issueFrameworkCredential(IssueFrameworkCredentialRequest request, String callerBPN) {
//Fetch Holder Wallet
Wallet holderWallet = walletService.getWalletByIdentifier(request.getBpn());

Wallet baseWallet = walletService.getWalletByIdentifier(miwSettings.authorityWalletBpn());

//validate BPN access
Validate.isFalse(callerBPN.equals(baseWallet.getBpn())).launch(new ForbiddenException(BASE_WALLET_BPN_IS_NOT_MATCHING_WITH_REQUEST_BPN_FROM_TOKEN));

// get Key
byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdentifier(baseWallet.getId());
Expand All @@ -156,17 +162,21 @@ public VerifiableCredential issueFrameworkCredential(IssueFrameworkCredentialReq
/**
* Issue dismantler credential verifiable credential.
*
* @param request the request
* @param request the request
* @param callerBPN the caller bpn
* @return the verifiable credential
*/
public VerifiableCredential issueDismantlerCredential(IssueDismantlerCredentialRequest request) {
public VerifiableCredential issueDismantlerCredential(IssueDismantlerCredentialRequest request, String callerBPN) {

//Fetch Holder Wallet
Wallet holderWallet = walletService.getWalletByIdentifier(request.getBpn());

// Fetch Issuer Wallet
Wallet baseWallet = walletService.getWalletByIdentifier(miwSettings.authorityWalletBpn());

//check BPN access
Validate.isFalse(callerBPN.equals(baseWallet.getBpn())).launch(new ForbiddenException(BASE_WALLET_BPN_IS_NOT_MATCHING_WITH_REQUEST_BPN_FROM_TOKEN));

//check duplicate
isCredentialExit(holderWallet.getDid(), MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL_CX);

Expand All @@ -190,10 +200,11 @@ public VerifiableCredential issueDismantlerCredential(IssueDismantlerCredentialR
* Issue membership credential verifiable credential.
*
* @param issueMembershipCredentialRequest the issue membership credential request
* @param callerBPN the caller bpn
* @return the verifiable credential
*/
@SneakyThrows
public VerifiableCredential issueMembershipCredential(IssueMembershipCredentialRequest issueMembershipCredentialRequest) {
public VerifiableCredential issueMembershipCredential(IssueMembershipCredentialRequest issueMembershipCredentialRequest, String callerBPN) {

//Fetch Holder Wallet
Wallet holderWallet = walletService.getWalletByIdentifier(issueMembershipCredentialRequest.getBpn());
Expand All @@ -203,10 +214,14 @@ public VerifiableCredential issueMembershipCredential(IssueMembershipCredentialR

// Fetch Issuer Wallet
Wallet baseWallet = walletService.getWalletByIdentifier(miwSettings.authorityWalletBpn());

//validate BPN access
Validate.isFalse(callerBPN.equals(baseWallet.getBpn())).launch(new ForbiddenException(BASE_WALLET_BPN_IS_NOT_MATCHING_WITH_REQUEST_BPN_FROM_TOKEN));

byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdentifier(baseWallet.getId());

//VC Subject
Credential credential = CommonUtils.getCredential(Map.of("type", MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL,
Credential credential = CommonUtils.getCredential(Map.of("type", VerifiableCredentialType.MEMBERSHIP_CREDENTIAL,
"id", holderWallet.getDid(),
"holderIdentifier", holderWallet.getBpn(),
"memberOf", baseWallet.getName(),
Expand Down
Loading

0 comments on commit b375317

Please sign in to comment.