Skip to content

Commit

Permalink
feat: Store credential API with test case, Validate test case
Browse files Browse the repository at this point in the history
  • Loading branch information
nitin-vavdiya committed May 26, 2023
1 parent bfc15fc commit 99307bf
Show file tree
Hide file tree
Showing 16 changed files with 370 additions and 217 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@

package org.eclipse.tractusx.managedidentitywallets.config;

import org.eclipse.tractusx.managedidentitywallets.exception.*;
import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException;
import org.eclipse.tractusx.managedidentitywallets.exception.DuplicateWalletProblem;
import org.eclipse.tractusx.managedidentitywallets.exception.ForbiddenException;
import org.eclipse.tractusx.managedidentitywallets.exception.WalletNotFoundProblem;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.web.bind.annotation.ExceptionHandler;
Expand Down Expand Up @@ -68,20 +71,6 @@ ProblemDetail handleDuplicateWalletProblem(DuplicateWalletProblem e) {
return problemDetail;
}

/**
* Handle did document not found problem problem detail.
*
* @param e the e
* @return the problem detail
*/
@ExceptionHandler(DidDocumentsNotFoundProblem.class)
ProblemDetail handleDidDocumentNotFoundProblem(DidDocumentsNotFoundProblem e) {
ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, e.getMessage());
problemDetail.setTitle(e.getMessage());
problemDetail.setProperty(TIMESTAMP, System.currentTimeMillis());
return problemDetail;
}

/**
* Handle forbidden exception problem detail.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@
* The type Miw settings.
*/
@ConfigurationProperties(prefix = "miw")
public record MIWSettings(String host, String encryptionKey) {
public record MIWSettings(String host, String encryptionKey, String authorityWalletBpn, String authorityWalletName) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.requestMatchers(new AntPathRequestMatcher(RestURI.DID_DOCUMENTS, GET.name())).permitAll() //Get did document
.requestMatchers(new AntPathRequestMatcher(RestURI.WALLETS, POST.name())).hasRole(ApplicationConstant.ROLE_ADD_WALLETS) //Create wallet
.requestMatchers(new AntPathRequestMatcher(RestURI.WALLETS, GET.name())).hasAnyRole(ApplicationConstant.ROLE_VIEW_WALLETS) //Get all wallet
.requestMatchers(new AntPathRequestMatcher(RestURI.WALLETS_BY_BPN, GET.name())).hasAnyRole(ApplicationConstant.ROLE_VIEW_WALLET, ApplicationConstant.ROLE_VIEW_WALLETS) //get wallet by BPN
.requestMatchers(new AntPathRequestMatcher(RestURI.WALLETS_BY_BPN_CREDENTIALS, POST.name())).hasAnyRole(ApplicationConstant.ROLE_VIEW_WALLET, ApplicationConstant.ROLE_VIEW_WALLETS) //Store credential
.requestMatchers(new AntPathRequestMatcher(RestURI.API_WALLETS_IDENTIFIER, GET.name())).hasAnyRole(ApplicationConstant.ROLE_VIEW_WALLET, ApplicationConstant.ROLE_VIEW_WALLETS) //get wallet by BPN
.requestMatchers(new AntPathRequestMatcher(RestURI.API_WALLETS_IDENTIFIER_CREDENTIALS, POST.name())).hasAnyRole(ApplicationConstant.ROLE_VIEW_WALLET, ApplicationConstant.ROLE_VIEW_WALLETS) //Store credential
.requestMatchers(new AntPathRequestMatcher(RestURI.CREDENTIALS, GET.name())).hasAnyRole(ApplicationConstant.ROLE_VIEW_WALLET, ApplicationConstant.ROLE_VIEW_WALLETS) //get credentials
.requestMatchers(new AntPathRequestMatcher("/error")).permitAll()
.and().oauth2ResourceServer()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,9 @@ private ApplicationConstant() {
public static final String ROLE_UPDATE_WALLET = "update_wallet";


public static final String DID = "did";

public static final String BPN = "bpn";


}
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,17 @@ private RestURI() {
* The constant WALLETS.
*/
public static final String WALLETS = "/api/wallets";

/**
* The constant DID_DOCUMENTS.
*/
public static final String DID_DOCUMENTS = "/api/didDocuments/{identifier}";
/**
* The constant WALLETS_BY_BPN.
*/
public static final String WALLETS_BY_BPN = "/api/wallets/{bpn}";
public static final String API_WALLETS_IDENTIFIER = "/api/wallets/{identifier}";

public static final String WALLETS_BY_BPN_CREDENTIALS = "/api/wallets/{bpn}/credentials";
public static final String API_WALLETS_IDENTIFIER_CREDENTIALS = "/api/wallets/{identifier}/credentials";
public static final String CREDENTIALS = "/api/credentials";

}
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,21 @@ public ResponseEntity<Wallet> createWallet(@Valid @RequestBody CreateWalletReque
* @return the response entity
*/
@Operation(summary = "Store Verifiable Credential", description = "Permission: **update_wallets** OR **update_wallet** (The BPN of wallet to extract credentials from must equal BPN of caller) \n\n Store a verifiable credential in the wallet of the given identifier")
@PostMapping(path = RestURI.WALLETS_BY_BPN_CREDENTIALS, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Map<String, String>> storeCredential(@RequestBody Map<String, Object> data, @PathVariable(name = "bpn") String bpn) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.storeCredential(data, bpn));
@PostMapping(path = RestURI.API_WALLETS_IDENTIFIER_CREDENTIALS, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
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));
}

/**
* Gets wallet by bpn.
*
* @param bpn the bpn
* @param identifier the identifier
* @return the wallet by bpn
*/
@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.WALLETS_BY_BPN, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Wallet> getWalletByBpn(@PathVariable(name = "bpn") String bpn) {
return ResponseEntity.status(HttpStatus.OK).body(service.getWalletByBpn(bpn));
@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));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
import lombok.*;
import org.eclipse.tractusx.managedidentitywallets.utils.StringToDidDocumentConverter;
import org.eclipse.tractusx.ssi.lib.model.did.DidDocument;
import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential;

import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.List;

/**
* The type Wallet.
Expand Down Expand Up @@ -60,4 +65,12 @@ public class Wallet extends BaseEntity {
@Column(nullable = false)
@Convert(converter = StringToDidDocumentConverter.class)
private DidDocument didDocument;


@Transient
private List<VerifiableCredential> verifiableCredentials;

public void setDid(String did) {
this.did = URLDecoder.decode(did, Charset.defaultCharset());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,16 @@
package org.eclipse.tractusx.managedidentitywallets.dao.repository;

import org.eclipse.tractusx.managedidentitywallets.dao.entity.Credential;
import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface CredentialRepository extends JpaRepository<Credential, Long> {
List<Credential> getByHolder(Long id);

@Query("select data from Credential where holder=:holder")
List<VerifiableCredential> getCredentialsByHolder(@Param("holder") Long holder);
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet;
import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository;
import org.eclipse.tractusx.managedidentitywallets.exception.DidDocumentsNotFoundProblem;
import org.eclipse.tractusx.ssi.lib.model.did.DidDocument;
import org.springframework.stereotype.Service;

Expand All @@ -36,8 +33,8 @@
@RequiredArgsConstructor
@Slf4j
public class DidDocumentService {

private final WalletRepository walletRepository;
private final WalletService walletService;

/**
* Gets did document.
Expand All @@ -46,11 +43,7 @@ public class DidDocumentService {
* @return the did document
*/
public DidDocument getDidDocument(String identifier) {
Wallet wallet = identifier.startsWith("did:") ? walletRepository.getByDid(identifier) : walletRepository.getByBpn(identifier);
if (wallet == null) {
throw new DidDocumentsNotFoundProblem("DidDocument not found for identifier " + identifier);
}
return wallet.getDidDocument();
return walletService.getWalletByIdentifier(identifier).getDidDocument();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

package org.eclipse.tractusx.managedidentitywallets.service;

import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -33,6 +34,7 @@
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings;
import org.eclipse.tractusx.managedidentitywallets.constant.ApplicationConstant;
import org.eclipse.tractusx.managedidentitywallets.dao.entity.Credential;
import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet;
import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey;
Expand All @@ -44,6 +46,7 @@
import org.eclipse.tractusx.managedidentitywallets.exception.DuplicateWalletProblem;
import org.eclipse.tractusx.managedidentitywallets.exception.ForbiddenException;
import org.eclipse.tractusx.managedidentitywallets.exception.WalletNotFoundProblem;
import org.eclipse.tractusx.managedidentitywallets.utils.CommonUtils;
import org.eclipse.tractusx.managedidentitywallets.utils.EncryptionUtils;
import org.eclipse.tractusx.managedidentitywallets.utils.Validate;
import org.eclipse.tractusx.ssi.lib.base.MultibaseFactory;
Expand Down Expand Up @@ -89,15 +92,15 @@ public class WalletService {
* @param bpn the bpn
* @return the map
*/
public Map<String, String> storeCredential(Map<String, Object> data, String bpn) {
public Map<String, String> storeCredential(Map<String, Object> data, String identifier) {
VerifiableCredential verifiableCredential = new VerifiableCredential(data);
Wallet wallet = walletRepository.getByBpn(bpn);
Validate.isNull(wallet).launch(new WalletNotFoundProblem("Can not find wallet with bpn " + bpn));
Wallet wallet = getWalletByIdentifier(identifier);
Validate.isNull(wallet).launch(new WalletNotFoundProblem("Can not find wallet with identifier " + identifier));
String did = wallet.getDid();
String holderDid = verifiableCredential.getCredentialSubject().get(0).get("id").toString();

//check ownership of credentials
Validate.isFalse(did.equals(holderDid)).launch(new ForbiddenException(String.format("The target wallet %s is not holder of provided credentials", bpn)));
Validate.isFalse(did.equals(holderDid)).launch(new ForbiddenException(String.format("The target wallet %s is not holder of provided credentials", identifier)));

//check type
Validate.isTrue(verifiableCredential.getTypes().isEmpty()).launch(new BadDataException("Invalid types provided in credentials"));
Expand All @@ -114,17 +117,36 @@ public Map<String, String> storeCredential(Map<String, Object> data, String bpn)
return Map.of("message", String.format("Credential with id %s has been successfully stored", verifiableCredential.getId()));
}


/**
* Gets wallet by identifier.
*
* @param identifier the identifier
* @param withCredentials the with credentials
* @return the wallet by identifier
*/
public Wallet getWalletByIdentifier(String identifier, boolean withCredentials) {
Wallet wallet = getWalletByIdentifier(identifier);
if (withCredentials) {
wallet.setVerifiableCredentials(credentialRepository.getCredentialsByHolder(wallet.getId()));
}
return wallet;
}

/**
* Gets wallet by bpn.
* Gets wallet by identifier.
*
* @param bpn the bpn
* @return the wallet by bpn
* @param identifier the identifier
* @return the wallet by identifier
*/
public Wallet getWalletByBpn(String bpn) {
Wallet wallet = walletRepository.getByBpn(bpn);
if (wallet == null) {
throw new WalletNotFoundProblem("Wallet not found for bpn " + bpn);
public Wallet getWalletByIdentifier(String identifier) {
Wallet wallet;
if (CommonUtils.getIdentifierType(identifier).equals(ApplicationConstant.BPN)) {
wallet = walletRepository.getByBpn(identifier);
} else {
wallet = walletRepository.getByDid(identifier);
}
Validate.isNull(wallet).launch(new WalletNotFoundProblem("Wallet not found for identifier " + identifier));
return wallet;
}

Expand All @@ -151,7 +173,7 @@ public Wallet createWallet(CreateWalletRequest request) {
Ed25519KeySet keyPair = createKeyPair();

//create did json
Did did = DidWebFactory.fromHostname(URLDecoder.decode(miwSettings.host() + ":" + request.getBpn(), Charset.defaultCharset()));
Did did = DidWebFactory.fromHostname(miwSettings.host() + ":" + request.getBpn());

//Extracting keys
Ed25519KeySet keySet = new Ed25519KeySet(keyPair.getPrivateKey(), keyPair.getPublicKey());
Expand Down Expand Up @@ -180,7 +202,7 @@ public Wallet createWallet(CreateWalletRequest request) {
.didDocument(didDocument)
.bpn(request.getBpn())
.name(request.getName())
.did(did.toString())
.did(URLDecoder.decode(did.toUri().toString(), Charset.defaultCharset()))
.algorithm("ED25519")
.build());

Expand All @@ -193,13 +215,28 @@ public Wallet createWallet(CreateWalletRequest request) {
.publicKey(encryptionUtils.encrypt(getPublicKeyString(keyPair.getPublicKey())))
.build());
log.debug("Wallet created for bpn ->{}", request.getBpn());
return wallet;
return wallet;
}

@PostConstruct
public void createAuthorityWallet() {
boolean exist = walletRepository.existsByBpn(miwSettings.authorityWalletBpn());
if (!exist) {
CreateWalletRequest request = CreateWalletRequest.builder()
.name(miwSettings.authorityWalletName())
.bpn(miwSettings.authorityWalletBpn())
.build();
createWallet(request);
log.info("Authority wallet created with bpn {}", miwSettings.authorityWalletBpn());
} else {
log.info("Authority wallet exists with bpn {}", miwSettings.authorityWalletBpn());
}
}

private void validateCreateWallet(CreateWalletRequest request){
private void validateCreateWallet(CreateWalletRequest request) {
boolean exist = walletRepository.existsByBpn(request.getBpn());
if(exist){
throw new DuplicateWalletProblem("Wallet is already exists for bpn "+request.getBpn());
if (exist) {
throw new DuplicateWalletProblem("Wallet is already exists for bpn " + request.getBpn());
}

}
Expand Down
Loading

0 comments on commit 99307bf

Please sign in to comment.