diff --git a/.github/workflows/push-trigger.yml b/.github/workflows/push-trigger.yml
index d8aeed6..6467373 100644
--- a/.github/workflows/push-trigger.yml
+++ b/.github/workflows/push-trigger.yml
@@ -44,7 +44,7 @@ jobs:
secrets:
OSSRH_USER: ${{ secrets.OSSRH_USER }}
OSSRH_SECRET: ${{ secrets.OSSRH_SECRET }}
- OSSRH_URL: ${{ secrets.RELEASE_URL }}
+ OSSRH_URL: ${{ secrets.OSSRH_SNAPSHOT_URL }}
OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }}
GPG_SECRET: ${{ secrets.GPG_SECRET }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
@@ -52,7 +52,7 @@ jobs:
sonar_analysis_sunbird-rc-esignet-integration-impl:
needs: build-maven-sunbird-rc-esignet-integration-impl
if: "${{ github.event_name != 'pull_request' }}"
- uses: mosip/kattu/.github/workflows/maven-sonar-analysis.yml@master-java21
+ uses: mosip/kattu/.github/workflows/maven-sonar-analysis.yml@master
with:
SERVICE_LOCATION: ./sunbird-rc-esignet-integration-impl
secrets:
@@ -85,7 +85,7 @@ jobs:
secrets:
OSSRH_USER: ${{ secrets.OSSRH_USER }}
OSSRH_SECRET: ${{ secrets.OSSRH_SECRET }}
- OSSRH_URL: ${{ secrets.RELEASE_URL }}
+ OSSRH_URL: ${{ secrets.OSSRH_SNAPSHOT_URL }}
OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }}
GPG_SECRET: ${{ secrets.GPG_SECRET }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
@@ -126,7 +126,7 @@ jobs:
secrets:
OSSRH_USER: ${{ secrets.OSSRH_USER }}
OSSRH_SECRET: ${{ secrets.OSSRH_SECRET }}
- OSSRH_URL: ${{ secrets.RELEASE_URL }}
+ OSSRH_URL: ${{ secrets.OSSRH_SNAPSHOT_URL }}
OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }}
GPG_SECRET: ${{ secrets.GPG_SECRET }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
@@ -145,7 +145,7 @@ jobs:
OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }}
GPG_SECRET: ${{ secrets.GPG_SECRET }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
-
+
build-maven-mock-certify-plugin:
uses: mosip/kattu/.github/workflows/maven-build.yml@master-java21
with:
@@ -167,7 +167,7 @@ jobs:
secrets:
OSSRH_USER: ${{ secrets.OSSRH_USER }}
OSSRH_SECRET: ${{ secrets.OSSRH_SECRET }}
- OSSRH_URL: ${{ secrets.RELEASE_URL }}
+ OSSRH_URL: ${{ secrets.OSSRH_SNAPSHOT_URL }}
OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }}
GPG_SECRET: ${{ secrets.GPG_SECRET }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
@@ -186,3 +186,44 @@ jobs:
OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }}
GPG_SECRET: ${{ secrets.GPG_SECRET }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
+
+ build-maven-postgres-dataprovider-plugin:
+ uses: mosip/kattu/.github/workflows/maven-build.yml@master-java21
+ with:
+ SERVICE_LOCATION: postgres-dataprovider-plugin
+ BUILD_ARTIFACT: postgres-dataprovider-plugin
+ secrets:
+ OSSRH_USER: ${{ secrets.OSSRH_USER }}
+ OSSRH_SECRET: ${{ secrets.OSSRH_SECRET }}
+ OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }}
+ GPG_SECRET: ${{ secrets.GPG_SECRET }}
+ SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
+
+ publish_to_nexus_postgres-dataprovider-plugin:
+ if: "${{ !contains(github.ref, 'master') && github.event_name != 'pull_request' && github.event_name != 'release' && github.event_name != 'prerelease' && github.event_name != 'publish' }}"
+ needs: build-maven-postgres-dataprovider-plugin
+ uses: mosip/kattu/.github/workflows/maven-publish-to-nexus.yml@master-java21
+ with:
+ SERVICE_LOCATION: ./postgres-dataprovider-plugin
+ secrets:
+ OSSRH_USER: ${{ secrets.OSSRH_USER }}
+ OSSRH_SECRET: ${{ secrets.OSSRH_SECRET }}
+ OSSRH_URL: ${{ secrets.OSSRH_SNAPSHOT_URL }}
+ OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }}
+ GPG_SECRET: ${{ secrets.GPG_SECRET }}
+ SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
+
+ sonar_analysis_postgres-dataprovider-plugin:
+ needs: build-maven-postgres-dataprovider-plugin
+ if: "${{ github.event_name != 'pull_request' }}"
+ uses: mosip/kattu/.github/workflows/maven-sonar-analysis.yml@master-java21
+ with:
+ SERVICE_LOCATION: ./postgres-dataprovider-plugin
+ secrets:
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+ ORG_KEY: ${{ secrets.ORG_KEY }}
+ OSSRH_USER: ${{ secrets.OSSRH_USER }}
+ OSSRH_SECRET: ${{ secrets.OSSRH_SECRET }}
+ OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }}
+ GPG_SECRET: ${{ secrets.GPG_SECRET }}
+ SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
diff --git a/.gitignore b/.gitignore
index f271844..9a6b505 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,3 +32,4 @@ target/
.setting/
.mvn/
.project/
+*.DS_Store
diff --git a/mock-certify-plugin/pom.xml b/mock-certify-plugin/pom.xml
index 828b75f..fc6e540 100644
--- a/mock-certify-plugin/pom.xml
+++ b/mock-certify-plugin/pom.xml
@@ -10,7 +10,7 @@
io.mosip.certify
mock-certify-plugin
- 0.2.1-SNAPSHOT
+ 0.3.0-SNAPSHOT
jar
mock-certify-integration-impl
@@ -54,22 +54,20 @@
3.6.3
1.3.0-beta.1
-
+
org.projectlombok
lombok
1.18.30
provided
-
io.mosip.certify
certify-core
- 0.9.0
+ 0.10.0-SNAPSHOT
provided
-
io.mosip.esignet
esignet-core
@@ -131,6 +129,43 @@
slf4j-api
2.0.12
+
+ org.jetbrains.kotlinx
+ kotlinx-datetime-jvm
+ 0.6.0
+
+
+ com.android.identity
+ identity-credential
+ 20231002
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.10.1
+
+
+ co.nstant.in
+ cbor
+ 0.9
+
+
+ net.javacrumbs.json-unit
+ json-unit-assertj
+ 3.4.1
+ test
+
+
+ org.apache.commons
+ commons-csv
+ 1.9.0
+
+
+ commons-codec
+ commons-codec
+
+
+
@@ -156,6 +191,10 @@
danubetech-maven-public
https://repo.danubetech.com/repository/maven-public/
+
+ google
+ https://maven.google.com/
+
diff --git a/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MDocMockVCIssuancePlugin.java b/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MDocMockVCIssuancePlugin.java
new file mode 100644
index 0000000..d5542f9
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MDocMockVCIssuancePlugin.java
@@ -0,0 +1,156 @@
+package io.mosip.certify.mock.integration.service;
+
+import foundation.identity.jsonld.JsonLDObject;
+import io.mosip.certify.api.dto.VCRequestDto;
+import io.mosip.certify.api.dto.VCResult;
+import io.mosip.certify.api.exception.VCIExchangeException;
+import io.mosip.certify.api.spi.VCIssuancePlugin;
+import io.mosip.certify.api.util.ErrorConstants;
+import io.mosip.certify.constants.VCFormats;
+import io.mosip.certify.core.exception.CertifyException;
+import io.mosip.certify.mock.integration.mocks.MdocGenerator;
+import io.mosip.esignet.core.dto.OIDCTransaction;
+import io.mosip.kernel.core.keymanager.spi.KeyStore;
+import io.mosip.kernel.keymanagerservice.constant.KeymanagerConstant;
+import io.mosip.kernel.keymanagerservice.entity.KeyAlias;
+import io.mosip.kernel.keymanagerservice.helper.KeymanagerDBHelper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cache.CacheManager;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.Cipher;
+import java.security.Key;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.*;
+
+@ConditionalOnProperty(value = "mosip.certify.integration.vci-plugin", havingValue = "MDocMockVCIssuancePlugin")
+@Component
+@Slf4j
+public class MDocMockVCIssuancePlugin implements VCIssuancePlugin {
+ private static final String AES_CIPHER_FAILED = "aes_cipher_failed";
+ private static final String NO_UNIQUE_ALIAS = "no_unique_alias";
+ private static final String USERINFO_CACHE = "userinfo";
+
+ @Autowired
+ private CacheManager cacheManager;
+
+ @Autowired
+ private KeyStore keyStore;
+
+ @Autowired
+ private KeymanagerDBHelper dbHelper;
+
+ @Value("${mosip.certify.cache.security.secretkey.reference-id}")
+ private String cacheSecretKeyRefId;
+
+ @Value("${mosip.certify.cache.security.algorithm-name}")
+ private String aesECBTransformation;
+
+ @Value("${mosip.certify.cache.secure.individual-id}")
+ private boolean secureIndividualId;
+
+ @Value("${mosip.certify.cache.store.individual-id}")
+ private boolean storeIndividualId;
+
+ @Value("${mosip.certify.mock.vciplugin.mdoc.issuer-key-cert:empty}")
+ private String issuerKeyAndCertificate = null;
+
+ private static final String ACCESS_TOKEN_HASH = "accessTokenHash";
+
+ public static final String CERTIFY_SERVICE_APP_ID = "CERTIFY_SERVICE";
+
+ @Override
+ public VCResult getVerifiableCredentialWithLinkedDataProof(VCRequestDto vcRequestDto, String holderId, Map identityDetails) throws VCIExchangeException {
+ log.error("not implemented the format {}", vcRequestDto);
+ throw new VCIExchangeException(ErrorConstants.NOT_IMPLEMENTED);
+ }
+
+ @Override
+ public VCResult getVerifiableCredential(VCRequestDto vcRequestDto, String holderId, Map identityDetails) throws VCIExchangeException {
+ String accessTokenHash = identityDetails.get(ACCESS_TOKEN_HASH).toString();
+ String documentNumber;
+ try {
+ documentNumber = getIndividualId(getUserInfoTransaction(accessTokenHash));
+ } catch (Exception e) {
+ log.error("Error getting documentNumber", e);
+ throw new VCIExchangeException(ErrorConstants.VCI_EXCHANGE_FAILED);
+ }
+
+ if(vcRequestDto.getFormat().equals(VCFormats.MSO_MDOC)){
+ VCResult vcResult = new VCResult<>();
+ String mdocVc;
+ try {
+ mdocVc = new MdocGenerator().generate(mockDataForMsoMdoc(documentNumber),holderId, issuerKeyAndCertificate);
+ } catch (Exception e) {
+ log.error("Exception on mdoc creation", e);
+ throw new VCIExchangeException(ErrorConstants.VCI_EXCHANGE_FAILED);
+ }
+ vcResult.setCredential(mdocVc);
+ vcResult.setFormat(VCFormats.MSO_MDOC);
+ return vcResult;
+ }
+ log.error("not implemented the format {}", vcRequestDto);
+ throw new VCIExchangeException(ErrorConstants.NOT_IMPLEMENTED);
+ }
+
+ private Map mockDataForMsoMdoc(String documentNumber) {
+ Map data = new HashMap<>();
+ log.info("Setting up the data for mDoc");
+ data.put("family_name","Agatha");
+ data.put("given_name","Joseph");
+ data.put("birth_date", "1994-11-06");
+ data.put("issuing_country", "IN");
+ data.put("document_number", documentNumber);
+ data.put("driving_privileges",new HashMap<>(){{
+ put("vehicle_category_code","A");
+ }});
+ return data;
+ }
+
+ /**
+ * TODO: This function getIndividualId is duplicated with Other VCIPlugin class and can be moved to commons
+ */
+ protected String getIndividualId(OIDCTransaction transaction) {
+ if(!storeIndividualId)
+ return null;
+ return secureIndividualId ? decryptIndividualId(transaction.getIndividualId()) : transaction.getIndividualId();
+ }
+
+ private String decryptIndividualId(String encryptedIndividualId) {
+ try {
+ Cipher cipher = Cipher.getInstance(aesECBTransformation);
+ byte[] decodedBytes = Base64.getUrlDecoder().decode(encryptedIndividualId);
+ cipher.init(Cipher.DECRYPT_MODE, getSecretKeyFromHSM());
+ return new String(cipher.doFinal(decodedBytes, 0, decodedBytes.length));
+ } catch(Exception e) {
+ log.error("Error Cipher Operations of provided secret data.", e);
+ throw new CertifyException(AES_CIPHER_FAILED);
+ }
+ }
+
+ private OIDCTransaction getUserInfoTransaction(String accessTokenHash) {
+ return cacheManager.getCache(USERINFO_CACHE).get(accessTokenHash, OIDCTransaction.class);
+ }
+
+ private Key getSecretKeyFromHSM() {
+ String keyAlias = getKeyAlias(CERTIFY_SERVICE_APP_ID, cacheSecretKeyRefId);
+ if (Objects.nonNull(keyAlias)) {
+ return keyStore.getSymmetricKey(keyAlias);
+ }
+ throw new CertifyException(NO_UNIQUE_ALIAS);
+ }
+
+ private String getKeyAlias(String keyAppId, String keyRefId) {
+ Map> keyAliasMap = dbHelper.getKeyAliases(keyAppId, keyRefId, LocalDateTime.now(ZoneOffset.UTC));
+ List currentKeyAliases = keyAliasMap.get(KeymanagerConstant.CURRENTKEYALIAS);
+ if (!currentKeyAliases.isEmpty() && currentKeyAliases.size() == 1) {
+ return currentKeyAliases.getFirst().getAlias();
+ }
+ log.error("CurrentKeyAlias is not unique. KeyAlias count: {}", currentKeyAliases.size());
+ throw new CertifyException(NO_UNIQUE_ALIAS);
+ }
+}
diff --git a/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MockCSVDataProviderPlugin.java b/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MockCSVDataProviderPlugin.java
new file mode 100644
index 0000000..1d1506b
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MockCSVDataProviderPlugin.java
@@ -0,0 +1,91 @@
+package io.mosip.certify.mock.integration.service;
+
+
+import io.mosip.certify.api.exception.DataProviderExchangeException;
+import io.mosip.certify.api.spi.DataProviderPlugin;
+import io.mosip.certify.util.CSVReader;
+import jakarta.annotation.PostConstruct;
+import lombok.extern.slf4j.Slf4j;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.http.HttpMethod;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ResourceUtils;
+import org.springframework.util.StreamUtils;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+
+@ConditionalOnProperty(value = "mosip.certify.integration.data-provider-plugin", havingValue = "MockCSVDataProviderPlugin")
+@Component
+@Slf4j
+public class MockCSVDataProviderPlugin implements DataProviderPlugin {
+ @Value("${mosip.certify.mock.vciplugin.id-uri:https://example.com/}")
+ private String id;
+ @Autowired
+ private CSVReader csvReader;
+ @Value("${mosip.certify.mock.data-provider.csv-registry-uri}")
+ private String csvRegistryURI;
+ @Value("${mosip.certify.mock.data-provider.csv.identifier-column}")
+ private String identifierColumn;
+ @Value("#{'${mosip.certify.mock.data-provider.csv.data-columns}'.split(',')}")
+ private Set dataColumns;
+ @Autowired
+ private RestTemplate restTemplate;
+
+ /**
+ * initialize sets up a CSV data for this DataProviderPlugin on start of application
+ * @return
+ */
+ @PostConstruct
+ public File initialize() throws IOException, JSONException {
+ File filePath;
+ if (csvRegistryURI.startsWith("http")) {
+ // download the file to a path: usecase(docker, spring cloud config)
+ filePath = restTemplate.execute(csvRegistryURI, HttpMethod.GET, null, resp -> {
+ File ret = File.createTempFile("download", "tmp");
+ StreamUtils.copy(resp.getBody(), new FileOutputStream(ret));
+ return ret;
+ });
+ } else if (csvRegistryURI.startsWith("classpath:")) {
+ try {
+ // usecase(local setup)
+ filePath = ResourceUtils.getFile(csvRegistryURI);
+ } catch (IOException e) {
+ throw new FileNotFoundException("File not found in: " + csvRegistryURI);
+ }
+ } else {
+ // usecase(local setup)
+ filePath = new File(csvRegistryURI);
+ if (!filePath.isFile()) {
+ // TODO: make sure it crashes the application
+ throw new FileNotFoundException("File not found: " + csvRegistryURI);
+ }
+ }
+ csvReader.readCSV(filePath, identifierColumn, dataColumns);
+ return filePath;
+ }
+
+ @Override
+ public JSONObject fetchData(Map identityDetails) throws DataProviderExchangeException {
+ try {
+ String individualId = (String) identityDetails.get("sub");
+ if (individualId != null) {
+ JSONObject jsonRes = csvReader.getJsonObjectByIdentifier(individualId);
+ return jsonRes;
+ }
+ } catch (Exception e) {
+ log.error("Failed to fetch json data for from data provider plugin", e);
+ throw new DataProviderExchangeException("ERROR_FETCHING_IDENTITY_DATA");
+ }
+ throw new DataProviderExchangeException("No Data Found");
+ }
+}
diff --git a/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MockVCIssuancePlugin.java b/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MockVCIssuancePlugin.java
index 22b7aef..e35ed4b 100644
--- a/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MockVCIssuancePlugin.java
+++ b/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MockVCIssuancePlugin.java
@@ -24,8 +24,9 @@
import io.mosip.certify.api.exception.VCIExchangeException;
import io.mosip.certify.api.spi.VCIssuancePlugin;
import io.mosip.certify.api.util.ErrorConstants;
-import io.mosip.certify.core.dto.ParsedAccessToken;
+import io.mosip.certify.constants.VCFormats;
import io.mosip.certify.core.exception.CertifyException;
+import io.mosip.certify.util.UUIDGenerator;
import io.mosip.esignet.core.dto.OIDCTransaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -93,7 +94,7 @@ public class MockVCIssuancePlugin implements VCIssuancePlugin {
@Value("#{${mosip.certify.mock.vciplugin.vc-credential-contexts:{'https://www.w3.org/2018/credentials/v1','https://schema.org/'}}}")
private List vcCredentialContexts;
- private static final String ACCESS_TOKEN_HASH = "accessTokenHash";
+ private static final String ACCESS_TOKEN_HASH = "accessTokenHash";
public static final String UTC_DATETIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
@@ -107,7 +108,7 @@ public VCResult getVerifiableCredentialWithLinkedDataProof(VCReque
VCResult vcResult = new VCResult<>();
vcJsonLdObject = buildJsonLDWithLDProof(identityDetails.get(ACCESS_TOKEN_HASH).toString());
vcResult.setCredential(vcJsonLdObject);
- vcResult.setFormat("ldp_vc");
+ vcResult.setFormat(VCFormats.LDP_VC);
return vcResult;
} catch (Exception e) {
log.error("Failed to build mock VC", e);
@@ -125,10 +126,12 @@ private JsonLDObject buildJsonLDWithLDProof(String accessTokenHash)
log.error("Unable to get KYC exchange data from MOCK", e);
}
+ String uuid = new UUIDGenerator().generate();
+
Map verCredJsonObject = new HashMap<>();
verCredJsonObject.put("@context", vcCredentialContexts);
verCredJsonObject.put("type", Arrays.asList("VerifiableCredential", "MockVerifiableCredential"));
- verCredJsonObject.put("id", "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5");
+ verCredJsonObject.put("id", uuid);
verCredJsonObject.put("issuer", "did:example:123456789");
verCredJsonObject.put("issuanceDate", getUTCDateTime());
verCredJsonObject.put("credentialSubject", formattedMap);
@@ -175,7 +178,6 @@ private Map getIndividualData(OIDCTransaction transaction){
ret.put("vcVer", "VC-V1");
ret.put("id", getIdentityUrl+"/"+individualId);
ret.put("UIN", individualId);
- ret.put("name", res.get("name"));
ret.put("fullName", res.get("fullName"));
ret.put("gender", res.get("gender"));
ret.put("dateOfBirth", res.get("dateOfBirth"));
@@ -203,7 +205,7 @@ private String decryptIndividualId(String encryptedIndividualId) {
Cipher cipher = Cipher.getInstance(aesECBTransformation);
byte[] decodedBytes = Base64.getUrlDecoder().decode(encryptedIndividualId);
cipher.init(Cipher.DECRYPT_MODE, getSecretKeyFromHSM());
- return new String(cipher.doFinal(decodedBytes, 0, decodedBytes.length));
+ return new String(cipher.doFinal(decodedBytes, 0, decodedBytes.length));
} catch(Exception e) {
log.error("Error Cipher Operations of provided secret data.", e);
throw new CertifyException(AES_CIPHER_FAILED);
@@ -238,7 +240,21 @@ public VCResult getVerifiableCredential(VCRequestDto vcRequestDto, Strin
throw new VCIExchangeException(ErrorConstants.NOT_IMPLEMENTED);
}
+ private Map mockDataForMsoMdoc(String documentNumber) {
+ Map data = new HashMap<>();
+ log.info("Setting up the data for mDoc");
+ data.put("family_name","Agatha");
+ data.put("given_name","Joseph");
+ data.put("birth_date", "1994-11-06");
+ data.put("issuing_country", "IN");
+ data.put("document_number", documentNumber);
+ data.put("driving_privileges",new HashMap<>(){{
+ put("vehicle_category_code","A");
+ }});
+ return data;
+ }
+
public OIDCTransaction getUserInfoTransaction(String accessTokenHash) {
- return cacheManager.getCache(USERINFO_CACHE).get(accessTokenHash, OIDCTransaction.class);
+ return cacheManager.getCache(USERINFO_CACHE).get(accessTokenHash, OIDCTransaction.class);
}
-}
+}
\ No newline at end of file
diff --git a/mock-certify-plugin/src/main/java/io/mosip/certify/constants/VCFormats.java b/mock-certify-plugin/src/main/java/io/mosip/certify/constants/VCFormats.java
new file mode 100644
index 0000000..86e518b
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io/mosip/certify/constants/VCFormats.java
@@ -0,0 +1,11 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+package io.mosip.certify.constants;
+
+public class VCFormats {
+ public static final String MSO_MDOC = "mso_mdoc";
+ public static final String LDP_VC = "ldp_vc";
+}
diff --git a/mock-certify-plugin/src/main/java/io/mosip/certify/mock/integration/mocks/MdocGenerator.java b/mock-certify-plugin/src/main/java/io/mosip/certify/mock/integration/mocks/MdocGenerator.java
new file mode 100644
index 0000000..3e28593
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io/mosip/certify/mock/integration/mocks/MdocGenerator.java
@@ -0,0 +1,150 @@
+package io.mosip.certify.mock.integration.mocks;
+
+import co.nstant.in.cbor.CborBuilder;
+import co.nstant.in.cbor.CborEncoder;
+import co.nstant.in.cbor.CborException;
+import co.nstant.in.cbor.model.DataItem;
+import com.android.identity.credential.NameSpacedData;
+import com.android.identity.internal.Util;
+import com.android.identity.mdoc.mso.MobileSecurityObjectGenerator;
+import com.android.identity.mdoc.util.MdocUtil;
+import com.android.identity.util.Timestamp;
+import io.mosip.certify.util.*;
+
+import java.io.ByteArrayOutputStream;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+
+public class MdocGenerator {
+
+ public static final String NAMESPACE = "org.iso.18013.5.1";
+ public static final String DOCTYPE = NAMESPACE + ".mDL";
+ public static final String DIGEST_ALGORITHM = "SHA-256";
+ public static final String ECDSA_ALGORITHM = "SHA256withECDSA";
+ public static final long SEED = 42L;
+ public static final DateTimeFormatter FULL_DATE_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE;
+
+ /**
+ * @param data - content of the mdoc
+ * @param holderId - documentNumber of the mDL
+ * @param issuerKeyAndCertificate - Document signet related details
+ * @return
+ * @throws Exception
+ *
+ * As of now, only issuer certificate (DS) is available and its used to sign the mdoc. But as per spec,
+ * DS certificate is signed by the issuing authority’s root CA certificate basically IA creates a certificate chain keeping the
+ * root = root CA certificate
+ * leaf = DS certificate
+ * And only the DS certificate is attached to the credential.
+ * Root certificate is not available as of now and is a limitation.
+ */
+ public String generate(Map data, String holderId, String issuerKeyAndCertificate) throws Exception {
+ KeyPairAndCertificateExtractor keyPairAndCertificateExtractor = new KeyPairAndCertificateExtractor();
+ KeyPairAndCertificate issuerDetails = keyPairAndCertificateExtractor.extract(issuerKeyAndCertificate);
+
+ if (issuerDetails.keyPair() == null) {
+ throw new RuntimeException("Unable to load Crypto details");
+ }
+
+ JwkToKeyConverter jwkToKeyConverter = new JwkToKeyConverter();
+ PublicKey devicePublicKey = jwkToKeyConverter.convertToPublicKey(holderId.replace("did:jwk:", ""));
+ KeyPair issuerKeypair = issuerDetails.keyPair();
+
+ LocalDate issueDate = LocalDate.now();
+ String formattedIssueDate = issueDate.format(FULL_DATE_FORMATTER);
+ LocalDate expiryDate = issueDate.plusYears(5);
+ String formattedExpiryDate = expiryDate.format(FULL_DATE_FORMATTER);
+
+ NameSpacedData.Builder nameSpacedDataBuilder = new NameSpacedData.Builder();
+ nameSpacedDataBuilder.putEntryString(NAMESPACE, "issue_date", formattedIssueDate);
+ nameSpacedDataBuilder.putEntryString(NAMESPACE, "expiry_date", formattedExpiryDate);
+
+ Map drivingPrivileges = (Map) data.get("driving_privileges");
+ drivingPrivileges.put("issue_date", formattedIssueDate);
+ drivingPrivileges.put("expiry_date", formattedExpiryDate);
+
+ data.keySet().forEach(key -> nameSpacedDataBuilder.putEntryString(NAMESPACE, key, Objects.toString(data.get(key),"")));
+
+ NameSpacedData nameSpacedData = nameSpacedDataBuilder.build();
+ Map> generatedIssuerNameSpaces = MdocUtil.generateIssuerNameSpaces(nameSpacedData, new Random(SEED), 16);
+ Map calculateDigestsForNameSpace = MdocUtil.calculateDigestsForNameSpace(NAMESPACE, generatedIssuerNameSpaces, DIGEST_ALGORITHM);
+
+ MobileSecurityObjectGenerator mobileSecurityObjectGenerator = new MobileSecurityObjectGenerator(DIGEST_ALGORITHM, DOCTYPE, devicePublicKey);
+ mobileSecurityObjectGenerator.addDigestIdsForNamespace(NAMESPACE, calculateDigestsForNameSpace);
+
+ Timestamp currentTimestamp = Timestamp.now();
+ Timestamp validUntil = Timestamp.ofEpochMilli(addYearsToDate(currentTimestamp.toEpochMilli(), 2));
+ mobileSecurityObjectGenerator.setValidityInfo(currentTimestamp, currentTimestamp, validUntil, null);
+
+ byte[] mso = mobileSecurityObjectGenerator.generate();
+
+ DataItem coseSign1Sign = Util.coseSign1Sign(
+ issuerKeypair.getPrivate(),
+ ECDSA_ALGORITHM,
+ Util.cborEncode(Util.cborBuildTaggedByteString(mso)),
+ null,
+ Collections.singletonList(issuerDetails.certificate())
+ );
+
+ return construct(generatedIssuerNameSpaces, coseSign1Sign);
+ }
+
+ private String construct(Map> nameSpaces, DataItem issuerAuth) throws CborException {
+ MDoc mDoc = new MDoc(DOCTYPE, new IssuerSigned(nameSpaces, issuerAuth));
+ byte[] cbor = mDoc.toCBOR();
+ return Base64.getUrlEncoder().encodeToString(cbor);
+ }
+
+ private long addYearsToDate(long dateInEpochMillis, int years) {
+ Instant instant = Instant.ofEpochMilli(dateInEpochMillis);
+ Instant futureInstant = instant.plus(years * 365L, ChronoUnit.DAYS);
+ return futureInstant.toEpochMilli();
+ }
+}
+
+
+class MDoc {
+ private final String docType;
+ private final IssuerSigned issuerSigned;
+
+ public MDoc(String docType, IssuerSigned issuerSigned) {
+ this.docType = docType;
+ this.issuerSigned = issuerSigned;
+ }
+
+ public byte[] toCBOR() throws CborException {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ CborEncoder cborEncoder = new CborEncoder(byteArrayOutputStream);
+ cborEncoder.encode(
+ new CborBuilder().addMap()
+ .put("docType", docType)
+ .put(CBORConverter.toDataItem("issuerSigned"), CBORConverter.toDataItem(issuerSigned.toMap()))
+ .end()
+ .build()
+ );
+ return byteArrayOutputStream.toByteArray();
+ }
+}
+
+class IssuerSigned {
+ private final Map> nameSpaces;
+ private final DataItem issuerAuth;
+
+ public IssuerSigned(Map> nameSpaces, DataItem issuerAuth) {
+ this.nameSpaces = nameSpaces;
+ this.issuerAuth = issuerAuth;
+ }
+
+ public Map toMap() {
+ Map map = new HashMap<>();
+ map.put("nameSpaces", CBORConverter.toDataItem(nameSpaces));
+ map.put("issuerAuth", issuerAuth);
+ return map;
+ }
+}
+
diff --git a/mock-certify-plugin/src/main/java/io/mosip/certify/util/CBORConverter.java b/mock-certify-plugin/src/main/java/io/mosip/certify/util/CBORConverter.java
new file mode 100644
index 0000000..418c50c
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io/mosip/certify/util/CBORConverter.java
@@ -0,0 +1,65 @@
+package io.mosip.certify.util;
+
+import co.nstant.in.cbor.CborDecoder;
+import co.nstant.in.cbor.model.Array;
+import co.nstant.in.cbor.model.DataItem;
+import co.nstant.in.cbor.model.Map;
+import co.nstant.in.cbor.model.SimpleValue;
+import co.nstant.in.cbor.model.UnicodeString;
+import co.nstant.in.cbor.model.UnsignedInteger;
+
+import java.util.List;
+
+public class CBORConverter {
+
+ public static DataItem toDataItem(Object value) {
+ if (value instanceof DataItem) {
+ return (DataItem) value;
+ } else if (value instanceof String) {
+ return new UnicodeString((String) value);
+ } else if (value instanceof Integer) {
+ return new UnsignedInteger(((Integer) value).longValue());
+ } else if (value instanceof Long) {
+ return new UnsignedInteger((Long) value);
+ } else if (value instanceof Boolean) {
+ return ((Boolean) value) ? SimpleValue.TRUE : SimpleValue.FALSE;
+ } else if (value instanceof java.util.Map, ?>) {
+ Map cborMap = new Map();
+ java.util.Map, ?> map = (java.util.Map, ?>) value;
+ for (java.util.Map.Entry, ?> entry : map.entrySet()) {
+ cborMap.put(new UnicodeString((String) entry.getKey()), toDataItem(entry.getValue()));
+ }
+ return cborMap;
+ } else if (value instanceof List>) {
+ Array cborArray = new Array();
+ List> list = (List>) value;
+ for (Object item : list) {
+ cborArray.add(toDataItem(item));
+ }
+ return cborArray;
+ } else if (value instanceof Object[]) {
+ Array cborArray = new Array();
+ Object[] array = (Object[]) value;
+ for (Object item : array) {
+ cborArray.add(toDataItem(item));
+ }
+ return cborArray;
+ } else if (value instanceof byte[]) {
+ try {
+ List dataItems = CborDecoder.decode((byte[]) value);
+ if (!dataItems.isEmpty()) {
+ return dataItems.get(0);
+ }
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Failed to decode ByteArray", e);
+ }
+ } else {
+ throw new IllegalArgumentException("Unsupported value: " + value + " " + value.getClass().getSimpleName());
+ }
+
+ return null;
+ }
+}
+
+
+
diff --git a/mock-certify-plugin/src/main/java/io/mosip/certify/util/CSVReader.java b/mock-certify-plugin/src/main/java/io/mosip/certify/util/CSVReader.java
new file mode 100644
index 0000000..b161f2f
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io/mosip/certify/util/CSVReader.java
@@ -0,0 +1,63 @@
+package io.mosip.certify.util;
+
+import io.mosip.certify.api.exception.DataProviderExchangeException;
+import jakarta.annotation.PostConstruct;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVParser;
+import org.apache.commons.csv.CSVRecord;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.*;
+
+@Component
+@Slf4j
+public class CSVReader {
+ private Map dataMap = new HashMap<>();
+
+ public void readCSV(File filePath, String identifierColumn, Set dataColumns) throws IOException, JSONException {
+ try (FileReader reader = new FileReader(filePath);
+ CSVParser csvParser = new CSVParser(reader, CSVFormat.DEFAULT.withFirstRecordAsHeader())) {
+ // Get header names
+ List headers = csvParser.getHeaderNames();
+ // Validate that identifier column exists
+ if (!headers.contains(identifierColumn)) {
+ throw new IllegalArgumentException("Identifier column " + identifierColumn + " not found in CSV");
+ }
+
+ // Process each record
+ for (CSVRecord record : csvParser) {
+ String identifier = record.get(identifierColumn);
+ JSONObject jsonObject = new JSONObject();
+ // Store only the configured fields
+ for (String header : headers) {
+ if (dataColumns.contains(header) || header.equals(identifierColumn)) {
+ jsonObject.put(header, record.get(header));
+ }
+ }
+
+ // Add to dataMap
+ dataMap.put(identifier, jsonObject);
+ }
+ } catch (IOException e) {
+ log.error("Error finding csv file path", e);
+ throw new IOException("Unable to find the CSV file.");
+ }
+ }
+
+ public JSONObject getJsonObjectByIdentifier(String identifier) throws DataProviderExchangeException, JSONException {
+ JSONObject record = dataMap.get(identifier);
+ if(record == null) {
+ log.error("No identifier found.");
+ throw new DataProviderExchangeException("No record found in csv with the provided identifier");
+ }
+
+ return record;
+ }
+}
diff --git a/mock-certify-plugin/src/main/java/io/mosip/certify/util/JwkToKeyConverter.java b/mock-certify-plugin/src/main/java/io/mosip/certify/util/JwkToKeyConverter.java
new file mode 100644
index 0000000..d7e8a33
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io/mosip/certify/util/JwkToKeyConverter.java
@@ -0,0 +1,28 @@
+package io.mosip.certify.util;
+
+import com.nimbusds.jose.jwk.ECKey;
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.RSAKey;
+
+import java.nio.charset.StandardCharsets;
+import java.security.PublicKey;
+import java.util.Base64;
+
+
+public class
+JwkToKeyConverter {
+
+ public PublicKey convertToPublicKey(String encodedData) throws Exception {
+ String jwkJsonString = new String(Base64.getUrlDecoder().decode(encodedData), StandardCharsets.UTF_8);
+ JWK jwk = JWK.parse(jwkJsonString);
+
+ if (jwk instanceof RSAKey) {
+ return ((RSAKey) jwk).toPublicKey();
+ } else if (jwk instanceof ECKey) {
+ return ((ECKey) jwk).toPublicKey();
+ }
+
+ throw new IllegalArgumentException("Unsupported key type");
+ }
+
+}
diff --git a/mock-certify-plugin/src/main/java/io/mosip/certify/util/KeyPairAndCertificate.java b/mock-certify-plugin/src/main/java/io/mosip/certify/util/KeyPairAndCertificate.java
new file mode 100644
index 0000000..c95e10f
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io/mosip/certify/util/KeyPairAndCertificate.java
@@ -0,0 +1,7 @@
+package io.mosip.certify.util;
+
+import java.security.KeyPair;
+import java.security.cert.X509Certificate;
+
+public record KeyPairAndCertificate(KeyPair keyPair, X509Certificate certificate) {
+}
diff --git a/mock-certify-plugin/src/main/java/io/mosip/certify/util/KeyPairAndCertificateExtractor.java b/mock-certify-plugin/src/main/java/io/mosip/certify/util/KeyPairAndCertificateExtractor.java
new file mode 100644
index 0000000..398b625
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io/mosip/certify/util/KeyPairAndCertificateExtractor.java
@@ -0,0 +1,49 @@
+package io.mosip.certify.util;
+
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.ByteArrayInputStream;
+import java.security.*;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+
+@Slf4j
+public class KeyPairAndCertificateExtractor {
+ public KeyPairAndCertificate extract(String keyCert) {
+ String[] splitKeyCert = keyCert.split("\\|\\|");
+ try {
+ X509Certificate certificate = convertStringToX509Certificate((splitKeyCert[1]));
+ return (new KeyPairAndCertificate(getKeyPair(splitKeyCert[0], certificate), certificate));
+ } catch (Exception e) {
+ log.error("Failed to extract key certificate", e);
+ }
+
+ return null;
+ }
+
+ private X509Certificate convertStringToX509Certificate(String certString) throws CertificateException {
+ byte[] certBytes = Base64.getDecoder().decode(certString);
+
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(certBytes));
+ }
+
+ private KeyPair getKeyPair(String base64PrivateKey, X509Certificate certificate) throws NoSuchAlgorithmException, InvalidKeySpecException {
+ byte[] privateKeyBytes = Base64.getDecoder().decode(base64PrivateKey);
+
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
+ KeyFactory keyFactory = KeyFactory.getInstance("EC");
+ PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
+
+ PublicKey publicKey = certificate.getPublicKey();
+
+ return new KeyPair(publicKey, privateKey);
+ }
+}
+
+
diff --git a/mock-certify-plugin/src/main/java/io/mosip/certify/util/UUIDGenerator.java b/mock-certify-plugin/src/main/java/io/mosip/certify/util/UUIDGenerator.java
new file mode 100644
index 0000000..f95ec22
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io/mosip/certify/util/UUIDGenerator.java
@@ -0,0 +1,9 @@
+package io.mosip.certify.util;
+
+import java.util.UUID;
+
+public class UUIDGenerator {
+ public String generate() {
+ return "urn:uuid:" + UUID.randomUUID();
+ }
+}
diff --git a/mock-certify-plugin/src/test/java/io/mosip/certify/mock/integration/service/MockCSVDataProviderPluginTest.java b/mock-certify-plugin/src/test/java/io/mosip/certify/mock/integration/service/MockCSVDataProviderPluginTest.java
new file mode 100644
index 0000000..eb1d20b
--- /dev/null
+++ b/mock-certify-plugin/src/test/java/io/mosip/certify/mock/integration/service/MockCSVDataProviderPluginTest.java
@@ -0,0 +1,67 @@
+package io.mosip.certify.mock.integration.service;
+
+import io.mosip.certify.api.exception.DataProviderExchangeException;
+import io.mosip.certify.util.CSVReader;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+@RunWith(MockitoJUnitRunner.class)
+public class MockCSVDataProviderPluginTest {
+ @Mock
+ CSVReader csvReader;
+
+ @InjectMocks
+ MockCSVDataProviderPlugin mockCSVDataProviderPlugin = new MockCSVDataProviderPlugin();
+
+ @Before
+ public void setup() throws JSONException, DataProviderExchangeException {
+ String dataColumnFields = "name,age,phone";
+ Set dataColumns = new HashSet<>(Arrays.asList(dataColumnFields.split(",")));
+ ReflectionTestUtils.setField(mockCSVDataProviderPlugin, "identifierColumn", "individualId");
+ ReflectionTestUtils.setField(mockCSVDataProviderPlugin, "dataColumns", dataColumns);
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("individualId", "1234567");
+ jsonObject.put("name", "John Doe");
+ jsonObject.put("age", "40");
+ jsonObject.put("phone", "98765");
+
+ Mockito.when(csvReader.getJsonObjectByIdentifier("1234567")).thenReturn(jsonObject);
+ }
+
+ @Test
+ public void fetchJsonDataWithValidIndividualId_thenPass() throws DataProviderExchangeException, JSONException {
+ JSONObject jsonObject = mockCSVDataProviderPlugin.fetchData(Map.of("sub", "1234567", "client_id", "CLIENT_ID"));
+ Assert.assertNotNull(jsonObject);
+ Assert.assertNotNull(jsonObject.get("name"));
+ Assert.assertNotNull(jsonObject.get("phone"));
+ Assert.assertNotNull(jsonObject.get("age"));
+ Assert.assertNotNull(jsonObject.get("individualId"));
+ Assert.assertEquals("John Doe", jsonObject.get("name"));
+ Assert.assertEquals("98765", jsonObject.get("phone"));
+ Assert.assertEquals("40", jsonObject.get("age"));
+ Assert.assertEquals("1234567", jsonObject.get("individualId"));
+ }
+
+ @Test
+ public void fetchJsonDataWithInValidIndividualId_thenFail() {
+ try {
+ mockCSVDataProviderPlugin.fetchData(Map.of("sub", "12345678", "client_id", "CLIENT_ID"));
+ } catch (DataProviderExchangeException e) {
+ Assert.assertEquals("ERROR_FETCHING_IDENTITY_DATA", e.getMessage());
+ }
+ }
+}
diff --git a/mock-certify-plugin/src/test/java/io/mosip/certify/mock/integration/service/MockVCIssuancePluginTest.java b/mock-certify-plugin/src/test/java/io/mosip/certify/mock/integration/service/MockVCIssuancePluginTest.java
index b4c91fe..0614e33 100644
--- a/mock-certify-plugin/src/test/java/io/mosip/certify/mock/integration/service/MockVCIssuancePluginTest.java
+++ b/mock-certify-plugin/src/test/java/io/mosip/certify/mock/integration/service/MockVCIssuancePluginTest.java
@@ -4,6 +4,7 @@
import io.mosip.certify.api.dto.VCRequestDto;
import io.mosip.certify.api.dto.VCResult;
import io.mosip.certify.api.exception.VCIExchangeException;
+import io.mosip.certify.core.dto.ParsedAccessToken;
import io.mosip.esignet.core.dto.OIDCTransaction;
import io.mosip.kernel.signature.dto.JWTSignatureResponseDto;
import io.mosip.kernel.signature.service.SignatureService;
diff --git a/mock-certify-plugin/src/test/java/io/mosip/certify/util/CSVReaderTest.java b/mock-certify-plugin/src/test/java/io/mosip/certify/util/CSVReaderTest.java
new file mode 100644
index 0000000..9a872ab
--- /dev/null
+++ b/mock-certify-plugin/src/test/java/io/mosip/certify/util/CSVReaderTest.java
@@ -0,0 +1,85 @@
+package io.mosip.certify.util;
+
+import io.mosip.certify.api.exception.DataProviderExchangeException;
+import lombok.SneakyThrows;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+@RunWith(MockitoJUnitRunner.class)
+public class CSVReaderTest {
+ @InjectMocks
+ CSVReader csvReader;
+
+ Set dataColumns;
+ Map dataMap = new HashMap<>();
+
+ @Before
+ public void setup() {
+ String dataColumnFields = "name,age,phone";
+ dataColumns = new HashSet<>(Arrays.asList(dataColumnFields.split(",")));
+ ReflectionTestUtils.setField(csvReader, "dataMap", dataMap);
+ }
+
+ @Test
+ public void readCSVDataFromValidFile_thenPass() throws IOException, JSONException {
+ File f = new File("src/test/resources/test.csv");
+ csvReader.readCSV(f, "individualId", dataColumns);
+ Assert.assertNotNull(dataMap);
+ Assert.assertNotNull(dataMap.get("1234567"));
+ JSONObject jsonObject = dataMap.get("1234567");
+ Assert.assertNotNull(jsonObject);
+ Assert.assertEquals("John Doe", jsonObject.get("name"));
+ Assert.assertEquals("9876543210", jsonObject.get("phone"));
+ Assert.assertEquals("40", jsonObject.get("age"));
+ }
+
+ @Test
+ public void readCSVDataFromInvalidFile_thenFail() throws JSONException {
+ try {
+ File f = new File("test.csv");
+ csvReader.readCSV(f, "individualId", dataColumns);
+ } catch (IOException e) {
+ Assert.assertEquals("Unable to find the CSV file.", e.getMessage());
+ }
+ }
+
+ @SneakyThrows
+ @Test
+ public void getJsonObjectByValidIdentifier_thenPass() {
+ Map data = new HashMap<>();
+ JSONObject jsonObject = new JSONObject(Map.of("phone", "9876543210", "name", "John Doe", "individualId", "1234567", "age", "40"));
+ data.put("1234567", jsonObject);
+ ReflectionTestUtils.setField(csvReader, "dataMap", data);
+
+ JSONObject jsonObjectResult = csvReader.getJsonObjectByIdentifier("1234567");
+ Assert.assertNotNull(jsonObject);
+ Assert.assertEquals("1234567", jsonObjectResult.get("individualId"));
+ Assert.assertEquals("John Doe", jsonObjectResult.get("name"));
+ Assert.assertEquals("40", jsonObjectResult.get("age"));
+ Assert.assertEquals("9876543210", jsonObjectResult.get("phone"));
+ }
+
+ @Test
+ public void getJsonObjectByInvalidIdentifier_thenFail() throws JSONException {
+ Map data = new HashMap<>();
+ JSONObject jsonObject = new JSONObject(Map.of("phone", "9876543210", "name", "John Doe", "individualId", "1234567", "age", "40"));
+ data.put("1234567", jsonObject);
+ ReflectionTestUtils.setField(csvReader, "dataMap", data);
+ try {
+ csvReader.getJsonObjectByIdentifier("12345678");
+ } catch (DataProviderExchangeException e) {
+ Assert.assertEquals("No record found in csv with the provided identifier", e.getMessage());
+ }
+ }
+}
diff --git a/mock-certify-plugin/src/test/java/io/mosip/certify/util/UUIDGeneratorTest.java b/mock-certify-plugin/src/test/java/io/mosip/certify/util/UUIDGeneratorTest.java
new file mode 100644
index 0000000..1257e12
--- /dev/null
+++ b/mock-certify-plugin/src/test/java/io/mosip/certify/util/UUIDGeneratorTest.java
@@ -0,0 +1,14 @@
+package io.mosip.certify.util;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class UUIDGeneratorTest {
+ @Test
+ void shouldReturnUUIDInRequiredFormatWhenGenerated() {
+ String generatedUUID = new UUIDGenerator().generate();
+
+ assertTrue(generatedUUID.matches("^urn:uuid:[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$"));
+ }
+}
diff --git a/mock-certify-plugin/src/test/resources/test.csv b/mock-certify-plugin/src/test/resources/test.csv
new file mode 100644
index 0000000..933fad3
--- /dev/null
+++ b/mock-certify-plugin/src/test/resources/test.csv
@@ -0,0 +1,6 @@
+individualId,name,phone,age
+1234567,John Doe,9876543210,40
+9876543,Mary Smith,8765432109,45
+2345678,Raj Patel,7654321098,28
+8765432,Sarah Johnson,6543210987,35
+
diff --git a/mosip-identity-certify-plugin/pom.xml b/mosip-identity-certify-plugin/pom.xml
index 633e53d..765b527 100644
--- a/mosip-identity-certify-plugin/pom.xml
+++ b/mosip-identity-certify-plugin/pom.xml
@@ -8,7 +8,7 @@
4.0.0
io.mosip.certify
mosip-identity-certify-plugin
- 0.2.1-SNAPSHOT
+ 0.3.0-SNAPSHOT
jar
mosipid-certify-integration-impl
@@ -98,13 +98,13 @@
io.mosip.certify
certify-core
- 0.9.0
+ 0.10.0-SNAPSHOT
provided
io.mosip.certify
certify-integration-api
- 0.9.0
+ 0.10.0-SNAPSHOT
provided
@@ -145,6 +145,7 @@
io.mosip.esignet
esignet-core
+
1.4.1
diff --git a/postgres-dataprovider-plugin/pom.xml b/postgres-dataprovider-plugin/pom.xml
new file mode 100644
index 0000000..2586c15
--- /dev/null
+++ b/postgres-dataprovider-plugin/pom.xml
@@ -0,0 +1,313 @@
+
+ 4.0.0
+
+ io.mosip.certify
+ postgres-dataprovider-plugin
+ 0.3.0-SNAPSHOT
+ jar
+
+ postgres-dataprovider-plugin
+ Data provider plugin implementation through postgres db that is used to showcase the integration with certify
+ https://github.com/mosip/digital-credential-plugins
+
+
+
+ MPL 2.0
+ https://www.mozilla.org/en-US/MPL/2.0/
+
+
+
+ scm:git:git://github.com/mosip/digital-credential-plugins.git
+ scm:git:ssh://github.com:mosip/digital-credential-plugins.git
+ https://github.com/mosip/digital-credential-plugins
+ HEAD
+
+
+
+ MOSIP
+ mosip.emailnotifier@gmail.com
+ io.mosip
+ https://www.mosip.io
+
+
+
+
+ UTF-8
+ 21
+ 3.7.1
+ 21
+ 21
+ 3.10.1
+ 3.2.5
+ 1.5
+ 2.2.1
+ 6.1.0
+ 3.0.1
+ 0.8.11
+ 3.6.3
+
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.30
+ provided
+
+
+ io.mosip.certify
+ certify-core
+ 0.10.0-SNAPSHOT
+ provided
+
+
+ org.mockito
+ mockito-core
+ 5.11.0
+ test
+
+
+ org.springframework
+ spring-test
+ 6.1.4
+
+
+ org.slf4j
+ slf4j-api
+ 2.0.12
+
+
+ junit
+ junit
+ 4.13.1
+ test
+
+
+ jakarta.persistence
+ jakarta.persistence-api
+ 3.1.0
+
+
+
+
+
+ ossrh
+ CentralRepository
+ https://oss.sonatype.org/content/repositories/snapshots
+ default
+
+ true
+
+
+
+ central
+ MavenCentral
+ default
+ https://repo1.maven.org/maven2
+
+ false
+
+
+
+
+
+
+ ossrh
+ https://oss.sonatype.org/content/repositories/snapshots
+
+
+ ossrh
+ https://oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ ${maven-assembly-plugin.version}
+
+
+ jar-with-dependencies
+
+ false
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${maven-javadoc-plugin.version}
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+ none
+ src/main/java
+
+
+
+ maven-deploy-plugin
+ 3.1.2
+
+
+ default-deploy
+ deploy
+
+ deploy
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.14
+ true
+
+
+ default-deploy
+ deploy
+
+ deploy
+
+
+
+
+ ossrh
+ https://oss.sonatype.org/
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ true
+ 2.2.1
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.5
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+ --pinentry-mode
+ loopback
+
+
+
+
+
+
+ pl.project13.maven
+ git-commit-id-plugin
+ 3.0.1
+
+
+ get-the-git-infos
+
+ revision
+
+ validate
+
+
+
+ true
+ ${project.build.outputDirectory}/git.properties
+
+ ^git.build.(time|version)$
+ ^git.commit.id.(abbrev|full)$
+
+ full
+ ${project.basedir}/.git
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+ true
+ true
+
+ ${argLine} --add-opens
+ java.xml/jdk.xml.internal=ALL-UNNAMED
+ --illegal-access=permit
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${maven.jacoco.version}
+
+
+
+ prepare-agent
+
+
+
+ report
+ prepare-package
+
+ report
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+ 3.1.0
+
+
+ make-jar-executable
+ package
+
+ run
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/postgres-dataprovider-plugin/src/main/java/io/mosip/certify/postgresdataprovider/integration/repository/DataProviderRepository.java b/postgres-dataprovider-plugin/src/main/java/io/mosip/certify/postgresdataprovider/integration/repository/DataProviderRepository.java
new file mode 100644
index 0000000..29daaf3
--- /dev/null
+++ b/postgres-dataprovider-plugin/src/main/java/io/mosip/certify/postgresdataprovider/integration/repository/DataProviderRepository.java
@@ -0,0 +1,8 @@
+package io.mosip.certify.postgresdataprovider.integration.repository;
+
+
+import java.util.Map;
+
+public interface DataProviderRepository {
+ Map fetchQueryResult(String id, String queryString);
+}
\ No newline at end of file
diff --git a/postgres-dataprovider-plugin/src/main/java/io/mosip/certify/postgresdataprovider/integration/repository/DataProviderRepositoryImpl.java b/postgres-dataprovider-plugin/src/main/java/io/mosip/certify/postgresdataprovider/integration/repository/DataProviderRepositoryImpl.java
new file mode 100644
index 0000000..4a6dcc4
--- /dev/null
+++ b/postgres-dataprovider-plugin/src/main/java/io/mosip/certify/postgresdataprovider/integration/repository/DataProviderRepositoryImpl.java
@@ -0,0 +1,37 @@
+package io.mosip.certify.postgresdataprovider.integration.repository;
+
+import jakarta.persistence.*;
+import org.springframework.stereotype.Repository;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+@Repository(value = "dataProviderRepository")
+public class DataProviderRepositoryImpl implements DataProviderRepository {
+ @PersistenceContext
+ private EntityManager entityManager;
+
+ @Override
+ public Map fetchQueryResult(String id, String queryString) {
+ Query query = entityManager.createNativeQuery(queryString, Tuple.class);
+ query.setParameter("id", id);
+ List list = query.getResultList();
+ List