diff --git a/certify-core/pom.xml b/certify-core/pom.xml index 402bbc3e..aa024db5 100644 --- a/certify-core/pom.xml +++ b/certify-core/pom.xml @@ -53,6 +53,41 @@ org.springframework.boot spring-boot-starter-cache + + + org.apache.velocity + velocity + 1.7 + + + org.apache.velocity.tools + velocity-tools-generic + 3.1 + + + org.slf4j + slf4j-api + + + org.apache.velocity + velocity-engine-core + + + + + org.springframework.data + spring-data-jpa + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-test + test + + jakarta.persistence jakarta.persistence-api diff --git a/certify-core/src/main/java/io/mosip/certify/core/constants/Constants.java b/certify-core/src/main/java/io/mosip/certify/core/constants/Constants.java index 509f4ac4..06f6ed25 100644 --- a/certify-core/src/main/java/io/mosip/certify/core/constants/Constants.java +++ b/certify-core/src/main/java/io/mosip/certify/core/constants/Constants.java @@ -15,5 +15,7 @@ public class Constants { public static final String CLIENT_ID = "client_id"; public static final String CERTIFY_PARTNER_APP_ID = "CERTIFY_PARTNER"; public static final String CERTIFY_SERVICE_APP_ID = "CERTIFY_SERVICE"; + public static final String CERTIFY_MOCK_RSA = "CERTIFY_MOCK_RSA"; public static final String ROOT_KEY = "ROOT"; + public static final String EMPTY_REF_ID = ""; } diff --git a/certify-core/src/main/java/io/mosip/certify/core/constants/SignatureAlg.java b/certify-core/src/main/java/io/mosip/certify/core/constants/SignatureAlg.java new file mode 100644 index 00000000..b5903d77 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/constants/SignatureAlg.java @@ -0,0 +1,15 @@ +package io.mosip.certify.core.constants; + +/** + * SignatureAlg is the constants file of supported VC sign algorithms. + * TODO(later): convert this into a structure such that it enables + * consumers to choose VC sign algos with + */ +public class SignatureAlg { + // LinkedDataSignature Algorithms + public static final String RSA_SIGNATURE_SUITE = "RsaSignature2018"; + + public static final String ED25519_SIGNATURE_SUITE = "Ed25519Signature2018"; + + // RS256, PS256, ES256 --> JWSAlgorithm.RS256.getName(); +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/constants/VCDM1Constants.java b/certify-core/src/main/java/io/mosip/certify/core/constants/VCDM1Constants.java new file mode 100644 index 00000000..197423b3 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/constants/VCDM1Constants.java @@ -0,0 +1,6 @@ +package io.mosip.certify.core.constants; + +public class VCDM1Constants { + public static final String ISSUANCE_DATE = "issuanceDate"; + public static final String EXPIRATION_DATE = "expirationDate"; +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/constants/VCDM2Constants.java b/certify-core/src/main/java/io/mosip/certify/core/constants/VCDM2Constants.java new file mode 100644 index 00000000..b3c77286 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/constants/VCDM2Constants.java @@ -0,0 +1,11 @@ +package io.mosip.certify.core.constants; + +/** + * VC Data Model 2.0 Constants are some constant fields for the (draft) + * VC DataModel 2.0. + * ref: + */ +public class VCDM2Constants { + public static final String VALID_UNITL = "validUntil"; + public static final String VALID_FROM = "validFrom"; +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/constants/VCDMConstants.java b/certify-core/src/main/java/io/mosip/certify/core/constants/VCDMConstants.java new file mode 100644 index 00000000..3a100ea3 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/constants/VCDMConstants.java @@ -0,0 +1,8 @@ +package io.mosip.certify.core.constants; + +public class VCDMConstants { + + // the below assertionMethods are common b/w VC 1.1 & VC 2.0 + public static final String ASSERTION_METHOD = "assertionMethod"; + public static final String PROOF = "proof"; +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/entity/TemplateData.java b/certify-core/src/main/java/io/mosip/certify/core/entity/TemplateData.java new file mode 100644 index 00000000..10e592f1 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/entity/TemplateData.java @@ -0,0 +1,34 @@ +package io.mosip.certify.core.entity; + + +import jakarta.persistence.Entity; +import jakarta.persistence.*; +import lombok.*; +import jakarta.validation.constraints.NotBlank; +import java.time.LocalDateTime; + +@Entity +@NoArgsConstructor +@IdClass(TemplateId.class) +public class TemplateData { + @NotBlank(message = "Template is mandatory") + @Getter + @Setter + private String template; + @Id + @Getter + @Setter + private String context; + @Id + @Getter + @Setter + private String credentialType; + + @NotBlank + @Column(name = "cr_dtimes") + private LocalDateTime createdTimes; + + @Column(name = "upd_dtimes") + private LocalDateTime updatedTimes; + +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/entity/TemplateId.java b/certify-core/src/main/java/io/mosip/certify/core/entity/TemplateId.java new file mode 100644 index 00000000..ef472d04 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/entity/TemplateId.java @@ -0,0 +1,29 @@ +package io.mosip.certify.core.entity; + +import lombok.*; + +import java.io.Serializable; +import java.util.Objects; + +@NoArgsConstructor +@AllArgsConstructor +public class TemplateId implements Serializable { + @Getter + @Setter + private String context; + @Getter + @Setter + private String credentialType; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TemplateId that)) return false; + return Objects.equals(context, that.context) && Objects.equals(credentialType, that.credentialType); + } + + @Override + public int hashCode() { + return Objects.hash(context, credentialType); + } +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/repository/TemplateRepository.java b/certify-core/src/main/java/io/mosip/certify/core/repository/TemplateRepository.java new file mode 100644 index 00000000..bcef3d65 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/repository/TemplateRepository.java @@ -0,0 +1,15 @@ +package io.mosip.certify.core.repository; + +import io.mosip.certify.core.entity.TemplateData; +import io.mosip.certify.core.entity.TemplateId; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface TemplateRepository extends JpaRepository { + Optional findByCredentialTypeAndContext(String credentialType, String context); + // NOTE: best practice? .save() +} + diff --git a/certify-core/src/test/java/io/mosip/certify/core/validators/CredentialRequestValidatorFactoryTest.java b/certify-core/src/test/java/io/mosip/certify/core/validators/CredentialRequestValidatorFactoryTest.java new file mode 100644 index 00000000..c5f6e53d --- /dev/null +++ b/certify-core/src/test/java/io/mosip/certify/core/validators/CredentialRequestValidatorFactoryTest.java @@ -0,0 +1,51 @@ +package io.mosip.certify.core.validators; + +import io.mosip.certify.core.constants.VCFormats; +import io.mosip.certify.core.dto.CredentialDefinition; +import io.mosip.certify.core.dto.CredentialRequest; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; + +public class CredentialRequestValidatorFactoryTest { + + CredentialRequestValidatorFactory factory; + + @Before + public void setUp() { + factory = new CredentialRequestValidatorFactory(); + } + + @Test + public void isValid_invalidFormat() { + CredentialRequest cr = new CredentialRequest(); + cr.setFormat("fake-format"); + assertFalse(factory.isValid(cr)); + } + + @Test + public void isValid_LDP_true() { + CredentialRequest cr = new CredentialRequest(); + cr.setFormat(VCFormats.LDP_VC); + cr.setCredential_definition(new CredentialDefinition()); + assertTrue(factory.isValid(cr)); + } + + @Test + public void isValid_mDoc_true() { + CredentialRequest cr = new CredentialRequest(); + cr.setFormat(VCFormats.MSO_MDOC); + cr.setDoctype("mDoc-doctype-fake"); + cr.setClaims(Map.of("isAge", "21")); + CredentialDefinition cd = new CredentialDefinition(); + cd.setType(List.of("VerifiableCredential", "MockDrivingLicense")); + cd.setContext(List.of("https://example.context.page.sh")); + cr.setCredential_definition(new CredentialDefinition()); + assertTrue(factory.isValid(cr)); + } +} \ No newline at end of file diff --git a/certify-core/src/test/resources/SchoolTemplate.vm b/certify-core/src/test/resources/SchoolTemplate.vm new file mode 100644 index 00000000..957c9e42 --- /dev/null +++ b/certify-core/src/test/resources/SchoolTemplate.vm @@ -0,0 +1,38 @@ +#set($validFrom = $validFrom) ## Assume $validFrom might be null or undefined +#set($validUntil = $validUntil) ## Assume $validFrom might be null or undefined + +{ +"@context": [ +"https://www.w3.org/ns/credentials/v2", +"${context}" +], +"type": [ +"VerifiableCredential", +"SchoolCertificate" +], +"validFrom": "${validFrom}", +"issuer": "${issuer}", +#if($validUntil) +"validUntil": "${validUntil}", +#end +"credentialSubject": { + "foundingDate": "${dob}", + "city": "${city}", +#if($amenities) + "amenities": #if($amenities)$amenities#else#set($amenities = '"[]"') $amenities#end, +#end + "name": "${name}", + "principalName": "${principalName}", + "schoolType": "${schoolType}", + "country": "${country}", + "schoolRegistrationNumber": "${policyName}" +}, +"renderMethod": [{ +"id": "https://vharsh.github.io/DID/insurance_svg_template.svg", +"type": "SvgRenderingTemplate", +"name": "Portrait Mode", +"css3MediaQuery": "@media (orientation: portrait)", +"digestMultibase": "zQmAPdhyxzznFCwYxAp2dRerWC85Wg6wFl9G270iEu5h6JqW" +}] +} + diff --git a/certify-integration-api/src/main/java/io/mosip/certify/api/exception/DataProviderExchangeException.java b/certify-integration-api/src/main/java/io/mosip/certify/api/exception/DataProviderExchangeException.java new file mode 100644 index 00000000..7232c4f4 --- /dev/null +++ b/certify-integration-api/src/main/java/io/mosip/certify/api/exception/DataProviderExchangeException.java @@ -0,0 +1,31 @@ +package io.mosip.certify.api.exception; + +import io.mosip.certify.api.util.ErrorConstants; + +/** + * {@link DataProviderExchangeException} is thrown when the DataProvider + * plugin fails to return user data against a set of claims generated by + * an Authentication provider. + */ +public class DataProviderExchangeException extends Exception { + private String errorCode; + + public DataProviderExchangeException() { + super(ErrorConstants.VCI_DATAFETCH_FAILED); + this.errorCode = ErrorConstants.VCI_DATAFETCH_FAILED; + } + + public DataProviderExchangeException(String errorCode) { + super(errorCode); + this.errorCode = errorCode; + } + + public DataProviderExchangeException(String errorCode, String errorMessage) { + super(errorCode + " -> " + errorMessage); + this.errorCode = errorCode; + } + + public String getErrorCode() { + return errorCode; + } +} diff --git a/certify-integration-api/src/main/java/io/mosip/certify/api/spi/DataProviderPlugin.java b/certify-integration-api/src/main/java/io/mosip/certify/api/spi/DataProviderPlugin.java new file mode 100644 index 00000000..0d2e6c79 --- /dev/null +++ b/certify-integration-api/src/main/java/io/mosip/certify/api/spi/DataProviderPlugin.java @@ -0,0 +1,14 @@ +package io.mosip.certify.api.spi; + +import io.mosip.certify.api.exception.DataProviderExchangeException; + +import java.util.Map; + +/** + * DataProviderPlugin is implemented by type#2 of identity plugin + * implementors to fetch data for Certify to template into a VC + * format of choice using {@link VCFormatter}. + */ +public interface DataProviderPlugin { + Map fetchData(Map identityDetails) throws DataProviderExchangeException; +} diff --git a/certify-integration-api/src/main/java/io/mosip/certify/api/spi/VCFormatter.java b/certify-integration-api/src/main/java/io/mosip/certify/api/spi/VCFormatter.java new file mode 100644 index 00000000..b5082bea --- /dev/null +++ b/certify-integration-api/src/main/java/io/mosip/certify/api/spi/VCFormatter.java @@ -0,0 +1,13 @@ +package io.mosip.certify.api.spi; + + +import java.util.Map; +/** + * VCDataModelFormatter is a templating engine which takes @param templateInput and returns a templated VC. + * Some implementations include + * - VC 2.0 data model templating engine + */ +public interface VCFormatter { + // TODO: Should it be changed to JSONObject? + String format(Map templateInput, Map defaultSettings); +} \ No newline at end of file diff --git a/certify-integration-api/src/main/java/io/mosip/certify/api/spi/VCSigner.java b/certify-integration-api/src/main/java/io/mosip/certify/api/spi/VCSigner.java new file mode 100644 index 00000000..724ae57f --- /dev/null +++ b/certify-integration-api/src/main/java/io/mosip/certify/api/spi/VCSigner.java @@ -0,0 +1,11 @@ +package io.mosip.certify.api.spi; + +import io.mosip.certify.api.dto.VCResult; +import java.util.Map; + +/** + * VCSigner can sign any VC provided a vcHash & Signer inputs + */ +public interface VCSigner { + VCResult perform(String templatedVC, Map params); +} diff --git a/certify-integration-api/src/main/java/io/mosip/certify/api/util/ErrorConstants.java b/certify-integration-api/src/main/java/io/mosip/certify/api/util/ErrorConstants.java index 9e1538db..87ef9ffd 100644 --- a/certify-integration-api/src/main/java/io/mosip/certify/api/util/ErrorConstants.java +++ b/certify-integration-api/src/main/java/io/mosip/certify/api/util/ErrorConstants.java @@ -9,4 +9,5 @@ public class ErrorConstants { public static final String NOT_IMPLEMENTED = "not_implemented"; public static final String VCI_EXCHANGE_FAILED = "vci_exchange_failed"; + public static final String VCI_DATAFETCH_FAILED = "vci_datafetch_failed"; } diff --git a/certify-service/configure_start.sh b/certify-service/configure_start.sh index ef5e13fd..71783552 100644 --- a/certify-service/configure_start.sh +++ b/certify-service/configure_start.sh @@ -25,6 +25,9 @@ download_and_extract() { if [ "$enable_certify_artifactory" = "true" ]; then download_and_extract "${artifactory_url_env}/artifactory/libs-release-local/certify/certify-plugin.zip" "${loader_path_env}" + echo "Please patch plugin JAR now" + sleep 60 + echo "Plugin JAR patching not posssible now" fi #installs the pkcs11 libraries. @@ -67,4 +70,4 @@ if [ "$download_hsm_client" = "true" ]; then fi cd $work_dir -exec "$@" \ No newline at end of file +exec "$@" diff --git a/certify-service/pom.xml b/certify-service/pom.xml index f535c5c6..ca90cd4f 100644 --- a/certify-service/pom.xml +++ b/certify-service/pom.xml @@ -57,6 +57,12 @@ + + net.javacrumbs.json-unit + json-unit-assertj + 3.4.1 + test + info.weboftrust ld-signatures-java diff --git a/certify-service/src/main/java/io/mosip/certify/CertifyServiceApplication.java b/certify-service/src/main/java/io/mosip/certify/CertifyServiceApplication.java index 134bc317..3a7e80e4 100644 --- a/certify-service/src/main/java/io/mosip/certify/CertifyServiceApplication.java +++ b/certify-service/src/main/java/io/mosip/certify/CertifyServiceApplication.java @@ -9,11 +9,13 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.scheduling.annotation.EnableAsync; @EnableAsync @EnableCaching @SpringBootApplication(scanBasePackages = "io.mosip.certify,"+ + "io.mosip.certify.core.*," + "io.mosip.kernel.crypto," + "io.mosip.kernel.keymanager.hsm," + "io.mosip.kernel.cryptomanager," + diff --git a/certify-service/src/main/java/io/mosip/certify/config/AppConfig.java b/certify-service/src/main/java/io/mosip/certify/config/AppConfig.java index f097e5dc..265d30ae 100644 --- a/certify-service/src/main/java/io/mosip/certify/config/AppConfig.java +++ b/certify-service/src/main/java/io/mosip/certify/config/AppConfig.java @@ -30,8 +30,8 @@ import org.springframework.web.client.RestTemplate; @Configuration -@EnableJpaRepositories(basePackages = {"io.mosip.certify.core.repository", "io.mosip.kernel.keymanagerservice.repository"}) -@EntityScan(basePackages = {"io.mosip.certify.core.entity", "io.mosip.kernel.keymanagerservice.entity"}) +@EnableJpaRepositories(basePackages = {"io.mosip.kernel.keymanagerservice.repository", "io.mosip.certify.core.repository"}) +@EntityScan(basePackages = {"io.mosip.kernel.keymanagerservice.entity, io.mosip.certify.core.entity"}) @Slf4j public class AppConfig implements ApplicationRunner { @@ -84,7 +84,12 @@ public void run(ApplicationArguments args) throws Exception { // Set the reference id to empty string, as keymanager is expecting the same for initialization masterKeyRequest.setReferenceId(org.apache.commons.lang3.StringUtils.EMPTY); keymanagerService.generateMasterKey(objectType, masterKeyRequest); - + // TODO: Generate an EC & ED key via K8s Job(INJICERT-469) + KeyPairGenerateRequestDto rsaKeyRequest = new KeyPairGenerateRequestDto(); + rsaKeyRequest.setApplicationId(Constants.CERTIFY_MOCK_RSA); + rsaKeyRequest.setReferenceId(Constants.EMPTY_REF_ID); + rsaKeyRequest.setForce(false); + keymanagerService.generateMasterKey("certificate", rsaKeyRequest); if(!StringUtils.isEmpty(cacheSecretKeyRefId)) { SymmetricKeyGenerateRequestDto symmetricKeyGenerateRequestDto = new SymmetricKeyGenerateRequestDto(); symmetricKeyGenerateRequestDto.setApplicationId(Constants.CERTIFY_SERVICE_APP_ID); diff --git a/certify-service/src/main/java/io/mosip/certify/impl/LoggerAuditService.java b/certify-service/src/main/java/io/mosip/certify/plugin/impl/LoggerAuditService.java similarity index 100% rename from certify-service/src/main/java/io/mosip/certify/impl/LoggerAuditService.java rename to certify-service/src/main/java/io/mosip/certify/plugin/impl/LoggerAuditService.java diff --git a/certify-service/src/main/java/io/mosip/certify/services/CertifyIssuanceServiceImpl.java b/certify-service/src/main/java/io/mosip/certify/services/CertifyIssuanceServiceImpl.java new file mode 100644 index 00000000..aac9a097 --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/services/CertifyIssuanceServiceImpl.java @@ -0,0 +1,255 @@ +/* + * 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.services; + +import com.nimbusds.jose.JWSAlgorithm; +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.DataProviderExchangeException; +import io.mosip.certify.api.spi.*; +import io.mosip.certify.api.util.Action; +import io.mosip.certify.api.util.ActionStatus; +import io.mosip.certify.core.constants.VCFormats; +import io.mosip.certify.core.constants.SignatureAlg; +import io.mosip.certify.core.dto.CredentialMetadata; +import io.mosip.certify.core.dto.CredentialRequest; +import io.mosip.certify.core.dto.CredentialResponse; +import io.mosip.certify.core.dto.ParsedAccessToken; +import io.mosip.certify.core.dto.VCIssuanceTransaction; +import io.mosip.certify.core.constants.Constants; +import io.mosip.certify.core.constants.ErrorConstants; +import io.mosip.certify.core.exception.CertifyException; +import io.mosip.certify.core.exception.InvalidRequestException; +import io.mosip.certify.core.exception.NotAuthenticatedException; +import io.mosip.certify.core.spi.VCIssuanceService; +import io.mosip.certify.core.util.AuditHelper; +import io.mosip.certify.core.util.SecurityHelperService; +import io.mosip.certify.core.validators.CredentialRequestValidatorFactory; +import io.mosip.certify.exception.InvalidNonceException; +import io.mosip.certify.proof.ProofValidator; +import io.mosip.certify.proof.ProofValidatorFactory; +import io.mosip.certify.utils.CredentialUtils; +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.security.oauth2.jwt.JwtClaimNames; +import org.springframework.stereotype.Service; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.*; + +@Slf4j +@Service +@ConditionalOnProperty(value = "mosip.certify.issuer", havingValue = "CertifyIssuer") +public class CertifyIssuanceServiceImpl implements VCIssuanceService { + + @Value("#{${mosip.certify.key-values}}") + private LinkedHashMap> issuerMetadata; + + @Value("${mosip.certify.cnonce-expire-seconds:300}") + private int cNonceExpireSeconds; + + @Autowired + private ParsedAccessToken parsedAccessToken; + + @Autowired + private VCFormatter vcFormatter; + + @Autowired + private VCSigner vcSigner; + + @Autowired + private DataProviderPlugin dataModelService; + + @Value("${mosip.certify.issuer.pub.key}") + private String hostedKey; + + @Value("${mosip.certify.issuer.uri}") + private String issuerURI; + + @Autowired + private ProofValidatorFactory proofValidatorFactory; + + @Autowired + private VCICacheService vciCacheService; + + @Autowired + private SecurityHelperService securityHelperService; + + @Autowired + private AuditPlugin auditWrapper; + + @Override + public CredentialResponse getCredential(CredentialRequest credentialRequest) { + // 1. Credential Request validation + boolean isValidCredentialRequest = new CredentialRequestValidatorFactory().isValid(credentialRequest); + if(!isValidCredentialRequest) { + throw new InvalidRequestException(ErrorConstants.INVALID_REQUEST); + } + + if(!parsedAccessToken.isActive()) + throw new NotAuthenticatedException(); + // 2. Scope Validation + String scopeClaim = (String) parsedAccessToken.getClaims().getOrDefault("scope", ""); + CredentialMetadata credentialMetadata = null; + for(String scope : scopeClaim.split(Constants.SPACE)) { + Optional result = getScopeCredentialMapping(scope, credentialRequest.getFormat()); + if(result.isPresent()) { + credentialMetadata = result.get(); //considering only first credential scope + break; + } + } + + if(credentialMetadata == null) { + log.error("No credential mapping found for the provided scope {}", scopeClaim); + throw new CertifyException(ErrorConstants.INVALID_SCOPE); + } + + // 3. Proof Validation + ProofValidator proofValidator = proofValidatorFactory.getProofValidator(credentialRequest.getProof().getProof_type()); + if(!proofValidator.validate((String)parsedAccessToken.getClaims().get(Constants.CLIENT_ID), getValidClientNonce(), + credentialRequest.getProof())) { + throw new CertifyException(ErrorConstants.INVALID_PROOF); + } + + // 4. Get VC from configured plugin implementation + VCResult vcResult = getVerifiableCredential(credentialRequest, credentialMetadata, + proofValidator.getKeyMaterial(credentialRequest.getProof())); + + auditWrapper.logAudit(Action.VC_ISSUANCE, ActionStatus.SUCCESS, + AuditHelper.buildAuditDto(parsedAccessToken.getAccessTokenHash(), "accessTokenHash"), null); + return getCredentialResponse(credentialRequest.getFormat(), vcResult); + } + + @Override + public Map getCredentialIssuerMetadata(String version) { + if(issuerMetadata.containsKey(version)) + return issuerMetadata.get(version); + throw new InvalidRequestException(ErrorConstants.UNSUPPORTED_OPENID_VERSION); + } + + private VCResult getVerifiableCredential(CredentialRequest credentialRequest, CredentialMetadata credentialMetadata, + String holderId) { + parsedAccessToken.getClaims().put("accessTokenHash", parsedAccessToken.getAccessTokenHash()); + VCRequestDto vcRequestDto = new VCRequestDto(); + vcRequestDto.setFormat(credentialRequest.getFormat()); + + + VCResult vcResult = null; + switch (credentialRequest.getFormat()) { + case "ldp_vc" : + vcRequestDto.setContext(credentialRequest.getCredential_definition().getContext()); + vcRequestDto.setType(credentialRequest.getCredential_definition().getType()); + vcRequestDto.setCredentialSubject(credentialRequest.getCredential_definition().getCredentialSubject()); + validateLdpVcFormatRequest(credentialRequest, credentialMetadata); + try { + // TODO(multitenancy): later decide which plugin out of n plugins is the correct one + Map identityData = dataModelService.fetchData(parsedAccessToken.getClaims()); + Map templateParams = new HashMap<>(); + templateParams.put("templateName", CredentialUtils.getTemplateName(vcRequestDto)); + templateParams.put("issuerURI", issuerURI); + String templatedVC = vcFormatter.format(identityData, templateParams); + Map vcSignerParams = new HashMap<>(); + // TODO: Collate this into simpler APIs where just key-type is specified + vcSignerParams.put(KeyManagerConstants.VC_SIGN_ALGO, + SignatureAlg.RSA_SIGNATURE_SUITE); + vcSignerParams.put(KeyManagerConstants.PUBLIC_KEY_URL, hostedKey); + vcSignerParams.put(KeyManagerConstants.KEY_APP_ID, Constants.CERTIFY_MOCK_RSA); + vcSignerParams.put(KeyManagerConstants.KEY_REF_ID, Constants.EMPTY_REF_ID); + // Change it to PS256 as per --> https://w3c.github.io/vc-jws-2020/#dfn-jsonwebsignature2020 + vcSignerParams.put(KeyManagerConstants.KEYMGR_SIGN_ALGO, JWSAlgorithm.RS256.getName()); + vcResult = vcSigner.perform(templatedVC, vcSignerParams); + } catch(DataProviderExchangeException e) { + throw new CertifyException(e.getErrorCode()); + } + break; + default: + throw new CertifyException(ErrorConstants.UNSUPPORTED_VC_FORMAT); + } + + if(vcResult != null && vcResult.getCredential() != null) + return vcResult; + + log.error("Failed to generate VC : {}", vcResult); + auditWrapper.logAudit(Action.VC_ISSUANCE, ActionStatus.ERROR, + AuditHelper.buildAuditDto(parsedAccessToken.getAccessTokenHash(), "accessTokenHash"), null); + throw new CertifyException(ErrorConstants.VC_ISSUANCE_FAILED); + } + + private CredentialResponse getCredentialResponse(String format, VCResult vcResult) { + switch (format) { + case "ldp_vc": + CredentialResponse ldpVcResponse = new CredentialResponse<>(); + ldpVcResponse.setCredential((JsonLDObject)vcResult.getCredential()); + return ldpVcResponse; + } + throw new CertifyException(ErrorConstants.UNSUPPORTED_VC_FORMAT); + } + + private Optional getScopeCredentialMapping(String scope, String format) { + Map vciMetadata = getCredentialIssuerMetadata("latest"); + LinkedHashMap supportedCredentials = (LinkedHashMap) vciMetadata.get("credential_configurations_supported"); + Optional> result = supportedCredentials.entrySet().stream() + .filter(cm -> ((LinkedHashMap) cm.getValue()).get("scope").equals(scope)).findFirst(); + + if(result.isPresent()) { + LinkedHashMap metadata = (LinkedHashMap)result.get().getValue(); + CredentialMetadata credentialMetadata = new CredentialMetadata(); + credentialMetadata.setFormat((String) metadata.get("format")); + credentialMetadata.setScope((String) metadata.get("scope")); + credentialMetadata.setId(result.get().getKey()); + if(format.equals(VCFormats.LDP_VC)){ + LinkedHashMap credentialDefinition = (LinkedHashMap) metadata.get("credential_definition"); + credentialMetadata.setTypes((List) credentialDefinition.get("type")); + } + return Optional.of(credentialMetadata); + } + return Optional.empty(); + } + + private void validateLdpVcFormatRequest(CredentialRequest credentialRequest, + CredentialMetadata credentialMetadata) { + if(!credentialRequest.getCredential_definition().getType().containsAll(credentialMetadata.getTypes())) + throw new InvalidRequestException(ErrorConstants.UNSUPPORTED_VC_TYPE); + + //TODO need to validate Credential_definition as JsonLD document, if invalid throw exception + } + + private String getValidClientNonce() { + VCIssuanceTransaction transaction = vciCacheService.getVCITransaction(parsedAccessToken.getAccessTokenHash()); + //If the transaction is null, it means that VCI service never created cNonce, its authorization server issued cNonce + String cNonce = (transaction == null) ? + (String) parsedAccessToken.getClaims().get(Constants.C_NONCE) : + transaction.getCNonce(); + Object nonceExpireSeconds = parsedAccessToken.getClaims().getOrDefault(Constants.C_NONCE_EXPIRES_IN, 0); + int cNonceExpire = (transaction == null) ? + nonceExpireSeconds instanceof Long ? (int)(long)nonceExpireSeconds : (int)nonceExpireSeconds : + transaction.getCNonceExpireSeconds(); + long issuedEpoch = (transaction == null) ? + ((Instant) parsedAccessToken.getClaims().getOrDefault(JwtClaimNames.IAT, Instant.MIN)).getEpochSecond(): + transaction.getCNonceIssuedEpoch(); + + if( cNonce == null || + cNonceExpire <= 0 || + (issuedEpoch+cNonceExpire) < LocalDateTime.now(ZoneOffset.UTC).toEpochSecond(ZoneOffset.UTC) ) { + log.error("Client Nonce not found / expired in the access token, generate new cNonce"); + transaction = createVCITransaction(); + throw new InvalidNonceException(transaction.getCNonce(), transaction.getCNonceExpireSeconds()); + } + return cNonce; + } + + private VCIssuanceTransaction createVCITransaction() { + VCIssuanceTransaction transaction = new VCIssuanceTransaction(); + transaction.setCNonce(securityHelperService.generateSecureRandomString(20)); + transaction.setCNonceIssuedEpoch(LocalDateTime.now(ZoneOffset.UTC).toEpochSecond(ZoneOffset.UTC)); + transaction.setCNonceExpireSeconds(cNonceExpireSeconds); + return vciCacheService.setVCITransaction(parsedAccessToken.getAccessTokenHash(), transaction); + } +} diff --git a/certify-service/src/main/java/io/mosip/certify/services/KeyManagerConstants.java b/certify-service/src/main/java/io/mosip/certify/services/KeyManagerConstants.java new file mode 100644 index 00000000..46a83918 --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/services/KeyManagerConstants.java @@ -0,0 +1,14 @@ +/* + * 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.services; + +public class KeyManagerConstants { + public static final String VC_SIGN_ALGO = "VCsignAlgo"; + public static final String PUBLIC_KEY_URL = "publicKeyURL"; + public static final String KEY_APP_ID = "keyAppId"; + public static final String KEY_REF_ID = "keyRefId"; + public static final String KEYMGR_SIGN_ALGO = "KeyMgrSignAlgo"; +} diff --git a/certify-service/src/main/java/io/mosip/certify/services/KeymanagerLibSigner.java b/certify-service/src/main/java/io/mosip/certify/services/KeymanagerLibSigner.java new file mode 100644 index 00000000..693336bb --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/services/KeymanagerLibSigner.java @@ -0,0 +1,118 @@ +/* + * 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.services; + +import foundation.identity.jsonld.JsonLDException; +import foundation.identity.jsonld.JsonLDObject; +import info.weboftrust.ldsignatures.LdProof; +import info.weboftrust.ldsignatures.canonicalizer.URDNA2015Canonicalizer; +import io.mosip.certify.api.dto.VCResult; +import io.mosip.certify.api.spi.VCSigner; +import io.mosip.certify.core.constants.*; +import io.mosip.certify.core.exception.CertifyException; +import io.mosip.kernel.signature.dto.JWSSignatureRequestDto; +import io.mosip.kernel.signature.dto.JWTSignatureResponseDto; +import io.mosip.kernel.signature.service.SignatureService; +import lombok.extern.slf4j.Slf4j; +import org.json.JSONArray; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.net.URI; +import java.security.GeneralSecurityException; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Base64; +import java.util.Date; +import java.util.Map; + +/** + * KeymanagerLibSigner is a VCSigner which uses the Certify embedded + * keymanager to perform VC signing tasks for JSON LD VCs. + * These are the known external requirements: + * - the public key must be pre-hosted for the VC & should be available + * so long that VC should be verifiable + * - the VC should have a validFrom or issuanceDate in a specific UTC format + */ +@Slf4j +@Service +public class KeymanagerLibSigner implements VCSigner { + + @Autowired + SignatureService signatureService; + + @Override + public VCResult perform(String templatedVC, Map keyMgrInput) { + // Can the below lines be done at Templating side itself ? + VCResult VC = new VCResult<>(); + JsonLDObject j = JsonLDObject.fromJson(templatedVC); + j.setDocumentLoader(null); + // NOTE: other aspects can be configured via keyMgrInput map + String validFrom; + String signatureAlgorithm = keyMgrInput.getOrDefault(KeyManagerConstants.VC_SIGN_ALGO, + SignatureAlg.RSA_SIGNATURE_SUITE); + String publicKeyURL = keyMgrInput.get(KeyManagerConstants.PUBLIC_KEY_URL); + String keyAppId = keyMgrInput.get(KeyManagerConstants.KEY_APP_ID); + String keyRefId = keyMgrInput.get(KeyManagerConstants.KEY_REF_ID); + String keyManagerSignAlgo = keyMgrInput.get(KeyManagerConstants.KEYMGR_SIGN_ALGO); + if (j.getJsonObject().containsKey(VCDM1Constants.ISSUANCE_DATE)) { + validFrom = j.getJsonObject().get(VCDM1Constants.ISSUANCE_DATE).toString(); + } else if (j.getJsonObject().containsKey(VCDM2Constants.VALID_FROM)){ + validFrom = j.getJsonObject().get(VCDM2Constants.VALID_FROM).toString(); + } else { + validFrom = ZonedDateTime.now(ZoneOffset.UTC) + .format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)); + } + // TODO: VC Data Model spec doesn't specify a single date format or a + // timezone restriction, this will have to be supported timely. + Date createDate = Date + .from(LocalDateTime + .parse(validFrom, + DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)) + .atZone(ZoneId.systemDefault()).toInstant()); + LdProof vcLdProof = LdProof.builder().defaultContexts(false).defaultTypes(false).type(signatureAlgorithm) + .created(createDate).proofPurpose(VCDMConstants.ASSERTION_METHOD) + .verificationMethod(URI.create(publicKeyURL)) + .build(); + // 1. Canonicalize + URDNA2015Canonicalizer canonicalizer = new URDNA2015Canonicalizer(); + byte[] vcSignBytes = null; + try { + vcSignBytes = canonicalizer.canonicalize(vcLdProof, j); + } catch (IOException | GeneralSecurityException | JsonLDException e) { + log.error("Error during canonicalization", e.getMessage()); + throw new CertifyException("Error during canonicalization"); + } + + // 2. VC Sign + String vcEncodedData = Base64.getUrlEncoder().encodeToString(vcSignBytes); + JWSSignatureRequestDto payload = new JWSSignatureRequestDto(); + payload.setDataToSign(vcEncodedData); + payload.setApplicationId(keyAppId); + payload.setReferenceId(keyRefId); // alg, empty = RSA + payload.setIncludePayload(false); + payload.setIncludeCertificate(false); + payload.setIncludeCertHash(true); + payload.setValidateJson(false); + payload.setB64JWSHeaderParam(false); + payload.setCertificateUrl(""); + payload.setSignAlgorithm(keyManagerSignAlgo); // RSSignature2018 --> RS256, PS256, ES256 + // TODO: Should this be a well defined Certify Exception for better comms b/w Certify & Support team? + JWTSignatureResponseDto jwsSignedData = signatureService.jwsSign(payload); + String sign = jwsSignedData.getJwtSignedData(); + LdProof ldProofWithJWS = LdProof.builder().base(vcLdProof).defaultContexts(false) + .jws(sign).build(); + ldProofWithJWS.addToJsonLDObject(j); + VC.setCredential(j); + return VC; + // TODO: Check if this is really a VC + // MOSIP ref: https://github.com/mosip/id-authentication/blob/master/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/impl/VciServiceImpl.java#L281 + } +} diff --git a/certify-service/src/main/java/io/mosip/certify/services/VCIssuanceServiceImpl.java b/certify-service/src/main/java/io/mosip/certify/services/VCIssuanceServiceImpl.java index bda09424..e768e6dd 100644 --- a/certify-service/src/main/java/io/mosip/certify/services/VCIssuanceServiceImpl.java +++ b/certify-service/src/main/java/io/mosip/certify/services/VCIssuanceServiceImpl.java @@ -35,6 +35,7 @@ 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.security.oauth2.jwt.JwtClaimNames; import org.springframework.stereotype.Service; import java.time.Instant; @@ -47,6 +48,7 @@ @Slf4j @Service +@ConditionalOnProperty(value = "mosip.certify.issuer", havingValue = "PluginIssuer") public class VCIssuanceServiceImpl implements VCIssuanceService { private static final String TYPE_VERIFIABLE_CREDENTIAL = "VerifiableCredential"; @@ -117,9 +119,9 @@ public CredentialResponse getCredential(CredentialRequest credentialRequest) { @Override public Map getCredentialIssuerMetadata(String version) { - if(issuerMetadata.containsKey(version)) - return issuerMetadata.get(version); - throw new InvalidRequestException(ErrorConstants.UNSUPPORTED_OPENID_VERSION); + if(issuerMetadata.containsKey(version)) + return issuerMetadata.get(version); + throw new InvalidRequestException(ErrorConstants.UNSUPPORTED_OPENID_VERSION); } private VCResult getVerifiableCredential(CredentialRequest credentialRequest, CredentialMetadata credentialMetadata, @@ -213,7 +215,7 @@ private Optional getScopeCredentialMapping(String scope, St private void validateLdpVcFormatRequest(CredentialRequest credentialRequest, CredentialMetadata credentialMetadata) { if(!credentialRequest.getCredential_definition().getType().containsAll(credentialMetadata.getTypes())) - throw new InvalidRequestException(ErrorConstants.UNSUPPORTED_VC_TYPE); + throw new InvalidRequestException(ErrorConstants.UNSUPPORTED_VC_TYPE); //TODO need to validate Credential_definition as JsonLD document, if invalid throw exception } diff --git a/certify-service/src/main/java/io/mosip/certify/services/templating/VelocityTemplatingEngineImpl.java b/certify-service/src/main/java/io/mosip/certify/services/templating/VelocityTemplatingEngineImpl.java new file mode 100644 index 00000000..dca1614d --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/services/templating/VelocityTemplatingEngineImpl.java @@ -0,0 +1,98 @@ +/* + * 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.services.templating; + +import java.io.*; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import io.mosip.certify.api.spi.VCFormatter; +import io.mosip.certify.core.constants.Constants; +import io.mosip.certify.core.constants.VCDM2Constants; +import io.mosip.certify.core.repository.TemplateRepository; +import jakarta.annotation.PostConstruct; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.runtime.RuntimeConstants; +import org.apache.velocity.tools.generic.DateTool; +import org.json.JSONArray; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +public class VelocityTemplatingEngineImpl implements VCFormatter { + VelocityEngine engine; + public static final String DELIMITER = ":"; + Map templateCache; + @Autowired + TemplateRepository templateRepository; + @Value("${mosip.certify.vcformat.vc.expiry:true}") + boolean shouldHaveDates; + + @PostConstruct + public void initialize() { + engine = new VelocityEngine(); + // TODO: The DataSourceResourceLoader can be used instead if there's a + // single primary key column and the table has a last modified date. + templateCache = new HashMap<>(); + templateRepository.findAll().stream().forEach((template -> templateCache.put(String.join(DELIMITER, template.getCredentialType(), template.getContext()), template.getTemplate()))); + engine.setProperty(RuntimeConstants.INPUT_ENCODING, "UTF-8"); + engine.setProperty(RuntimeConstants.OUTPUT_ENCODING, "UTF-8"); + engine.init(); + } + + // TODO: Add a public method for updating the Velocity template cache + + /** + * performs the templating + * NOTE: the defaultSettings map should have the "templateName" key set to + * "${sort(CREDENTIALTYPE1,CREDENTIALTYPE2,CREDENTIALTYPE3...)}:${sort(VC_CONTEXT1,VC_CONTENXT2,VC_CONTEXT3...)}" + * + * @param templateInput is the input from the DataProvider plugin + * @param defaultSettings has some sensible defaults from Certify for + * internal work such as locating the appropriate template + * @return templated VC as a String + */ + @Override + public String format(Map templateInput, Map defaultSettings) { + // TODO: Isn't template name becoming too complex with VC_CONTEXTS & CREDENTIAL_TYPES both? + String templateName = defaultSettings.get("templateName").toString(); + String issuer = defaultSettings.get("issuerURI").toString(); + String t = templateCache.get(templateName); + StringWriter writer = new StringWriter(); + // 1. Prepare map + Map finalTemplate = new HashMap<>(); + for (String key : templateInput.keySet()) { + Object value = templateInput.get(key); + if (value instanceof List) { + // TODO(problem area): handle field values with unescaped JSON + // reserved literals such as " or , + // (Q) Should Object always be a JSONObject? + finalTemplate.put(key, new JSONArray((List) value)); + } else if (value.getClass().isArray()) { + finalTemplate.put(key, new JSONArray(List.of(value))); + } else { + finalTemplate.put(key, value); + } + } + // add the issuer value + finalTemplate.put("issuer", issuer); + if (shouldHaveDates && !(templateInput.containsKey(VCDM2Constants.VALID_FROM) + && templateInput.containsKey(VCDM2Constants.VALID_UNITL))) { + templateInput.put("_dateTool", new DateTool()); + String time = ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)); + // hardcoded time + String expiryTime = ZonedDateTime.now(ZoneOffset.UTC).plusYears(2).format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)); + finalTemplate.put(VCDM2Constants.VALID_FROM, time); + finalTemplate.put(VCDM2Constants.VALID_UNITL, expiryTime); + } + VelocityContext context = new VelocityContext(finalTemplate); + engine.evaluate(context, writer, /*logTag */ templateName,t.toString()); + return writer.toString(); + } +} diff --git a/certify-service/src/main/java/io/mosip/certify/utils/CredentialUtils.java b/certify-service/src/main/java/io/mosip/certify/utils/CredentialUtils.java new file mode 100644 index 00000000..6dc0392d --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/utils/CredentialUtils.java @@ -0,0 +1,29 @@ +package io.mosip.certify.utils; + +import io.mosip.certify.api.dto.VCRequestDto; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class CredentialUtils { + // returns true for VC 2.0 VCI requests + public static boolean isVC2_0Request(VCRequestDto r) { + return r.getContext().get(0).equals("https://www.w3.org/ns/credentials/v2"); + } + + /** + * get the template name for a VCRequest for VCFormatter lib + * @param vcRequestDto + * @return + */ + public static String getTemplateName(VCRequestDto vcRequestDto) { + List c = new ArrayList<>(vcRequestDto.getContext()); + List t = new ArrayList<>(vcRequestDto.getType()); + Collections.sort(c); + Collections.sort(t); + String contextKey = String.join(",", c); + String typeKey = String.join(",", t); + return String.join(":", typeKey, contextKey); + } +} diff --git a/certify-service/src/main/resources/application-local.properties b/certify-service/src/main/resources/application-local.properties index c2b5eb24..c0eca24d 100644 --- a/certify-service/src/main/resources/application-local.properties +++ b/certify-service/src/main/resources/application-local.properties @@ -15,9 +15,12 @@ mosip.certify.security.ignore-auth-urls=/actuator/**,**/error,**/swagger-ui/**,\ ## ------------------------------------------ Discovery openid-configuration ------------------------------------------- mosip.certify.domain.url=http://localhost:8090 +mosipbox.public.url=https://localhost:8090 mosip.certify.authorization.url=http://localhost:8088 mosip.certify.discovery.issuer-id=${mosip.certify.domain.url}${server.servlet.path} +mosip.certify.issuer=CertifyIssuer + ##--------------change this later--------------------------------- mosip.certify.supported.jwt-proof-alg={'RS256','PS256','ES256'} @@ -28,6 +31,7 @@ mosip.certify.authn.filter-urls={ '${server.servlet.path}/issuance/credential', mosip.certify.authn.issuer-uri=http://localhost:8088/v1/esignet mosip.certify.authn.jwk-set-uri=http://localhost:8088/v1/esignet/oauth/.well-known/jwks.json mosip.certify.authn.allowed-audiences={ '${mosip.certify.domain.url}${server.servlet.path}/issuance/credential', 'http://localhost:8088/v1/esignet/vci/credential' } +mosip.certify.dataprovider.types={'MockVerifiableCredential','StudentCredential','UniversityCredential'} mosip.certify.key-values={\ 'vd12' : {\ 'credential_issuer': '${mosip.certify.identifier}', \ @@ -42,7 +46,7 @@ mosip.certify.key-values={\ 'cryptographic_suites_supported': {'Ed25519Signature2020'},\ 'proof_types_supported': {'jwt'},\ 'credential_definition': {\ - 'type': {'VerifiableCredential','InsuranceCredential'},\ + 'type': {'VerifiableCredential','MockCredential'},\ 'credentialSubject': {\ 'fullName': {'display': {{'name': 'Name','locale': 'en'}}}, \ 'mobile': {'display': {{'name': 'Phone Number','locale': 'en'}}},\ @@ -69,7 +73,7 @@ mosip.certify.key-values={\ 'cryptographic_suites_supported': {'Ed25519Signature2020'},\ 'proof_types_supported': {'jwt'},\ 'credential_definition': {\ - 'type': {'VerifiableCredential', 'LifeInsuranceCredential'},\ + 'type': {'VerifiableCredential', 'MockCredential'},\ 'credentialSubject': {\ 'fullName': {'display': {{'name': 'Name','locale': 'en'}}}, \ 'mobile': {'display': {{'name': 'Phone Number','locale': 'en'}}},\ @@ -98,12 +102,12 @@ mosip.certify.key-values={\ 'credential_configurations_supported' : { \ 'InsuranceCredential' : {\ 'format': 'ldp_vc',\ - 'scope' : 'sunbird_rc_insurance_vc_ldp',\ + 'scope' : 'sample_vc_ldp',\ 'cryptographic_binding_methods_supported': {'did:jwk'},\ 'credential_signing_alg_values_supported': {'Ed25519Signature2020'},\ 'proof_types_supported': {'jwt': {'proof_signing_alg_values_supported': {'RS256', 'PS256'}}},\ 'credential_definition': {\ - 'type': {'VerifiableCredential','InsuranceCredential'},\ + 'type': {'VerifiableCredential','MockVerifiableCredential'},\ 'credentialSubject': {\ 'fullName': {'display': {{'name': 'Name','locale': 'en'}}}, \ 'mobile': {'display': {{'name': 'Phone Number','locale': 'en'}}},\ @@ -126,12 +130,12 @@ mosip.certify.key-values={\ },\ 'LifeInsuranceCredential':{\ 'format': 'ldp_vc',\ - 'scope' : 'life_insurance_vc_ldp',\ + 'scope' : 'sample_vc_ldp',\ 'cryptographic_binding_methods_supported': {'did:jwk'},\ 'credential_signing_alg_values_supported': {'Ed25519Signature2020'},\ 'proof_types_supported': {'jwt': {'proof_signing_alg_values_supported': {'RS256', 'ES256'}}},\ 'credential_definition': {\ - 'type': {'VerifiableCredential', 'LifeInsuranceCredential'},\ + 'type': {'VerifiableCredential', 'MockCredential'},\ 'credentialSubject': {\ 'fullName': {'display': {{'name': 'Name','locale': 'en'}}}, \ 'mobile': {'display': {{'name': 'Phone Number','locale': 'en'}}},\ @@ -192,17 +196,21 @@ mosip.certify.svg-templates=svg-template.json #mosip.certify.authenticator.ida.secret-key=client-secret-key #mosip.certify.authenticator.ida.app-id=ida +mosip.certify.vcformat.vc.expiry=true ## ------------------------------------------- Mock ID Integration properties ------------------------------------------------------------ mosip.certify.integration.scan-base-package=io.mosip.certify.mock.integration mosip.certify.integration.audit-plugin=LoggerAuditService mosip.certify.integration.vci-plugin=MockVCIssuancePlugin mosip.certify.mock.vciplugin.verification-method=${mosip.certify.authn.jwk-set-uri} -mosip.certify.mock.authenticator.get-identity-url="http://localhost:8082/v1/mock-identity-system/identity" +mosip.certify.mock.authenticator.get-identity-url=http://localhost:8082/v1/mock-identity-system/identity #TODO: get the secret for key-cert onboarded for local test mosip.certify.mock.vciplugin.issuer.key-cert="dummy-issuer-cert" mosip.certify.mock.vciplugin.ca.key-cert="dummy-ca-cert" +# details of VC issuer's public key & controller for DataProvider plugin +mosip.certify.issuer.pub.key=https://vharsh.github.io/DID/mock-rsa.json +mosip.certify.issuer.uri=https://vharsh.github.io/DID/mock-rsac.json ## ---------------------------------------- Cache configuration -------------------------------------------------------- @@ -313,7 +321,7 @@ crypto.PrependThumbprint.enable=true ##----------------------------------------- Database properties -------------------------------------------------------- mosip.certify.database.hostname=localhost -mosip.certify.database.port=5456 +mosip.certify.database.port=5432 spring.datasource.url=jdbc:postgresql://${mosip.certify.database.hostname}:${mosip.certify.database.port}/inji_certify?currentSchema=certify spring.datasource.username=postgres spring.datasource.password=postgres @@ -321,4 +329,4 @@ spring.datasource.password=postgres spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=none -spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true \ No newline at end of file +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true diff --git a/certify-service/src/test/java/io/mosip/certify/TestMockDataProviderPluginImpl.java b/certify-service/src/test/java/io/mosip/certify/TestMockDataProviderPluginImpl.java new file mode 100644 index 00000000..9aa24402 --- /dev/null +++ b/certify-service/src/test/java/io/mosip/certify/TestMockDataProviderPluginImpl.java @@ -0,0 +1,19 @@ +package io.mosip.certify; + +import io.mosip.certify.api.exception.DataProviderExchangeException; +import io.mosip.certify.api.spi.DataProviderPlugin; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@ConditionalOnProperty(value = "mosip.certify.integration.vci-plugin", havingValue = "TestVCIPluginImpl") +@Component +@Slf4j +public class TestMockDataProviderPluginImpl implements DataProviderPlugin { + @Override + public Map fetchData(Map identityDetails) throws DataProviderExchangeException { + return Map.of(); + } +} diff --git a/certify-service/src/test/java/io/mosip/certify/services/CertifyIssuanceServiceImplTest.java b/certify-service/src/test/java/io/mosip/certify/services/CertifyIssuanceServiceImplTest.java new file mode 100644 index 00000000..1098a060 --- /dev/null +++ b/certify-service/src/test/java/io/mosip/certify/services/CertifyIssuanceServiceImplTest.java @@ -0,0 +1,98 @@ +package io.mosip.certify.services; + +import io.mosip.certify.api.spi.AuditPlugin; +import io.mosip.certify.api.spi.DataProviderPlugin; +import io.mosip.certify.api.spi.VCFormatter; +import io.mosip.certify.api.spi.VCSigner; +import io.mosip.certify.core.constants.ErrorConstants; +import io.mosip.certify.core.constants.VCFormats; +import io.mosip.certify.core.dto.CredentialDefinition; +import io.mosip.certify.core.dto.CredentialRequest; +import io.mosip.certify.core.dto.ParsedAccessToken; +import io.mosip.certify.core.exception.InvalidRequestException; +import io.mosip.certify.core.exception.NotAuthenticatedException; +import io.mosip.certify.core.util.SecurityHelperService; +import io.mosip.certify.proof.ProofValidatorFactory; +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.MockitoAnnotations; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.junit.Assert.*; + +@RunWith(MockitoJUnitRunner.class) +public class CertifyIssuanceServiceImplTest { + + @Mock + private LinkedHashMap> issuerMetadata; + + @Mock + private ParsedAccessToken parsedAccessToken; + + @Mock + private VCFormatter vcFormatter; + + @Mock + private VCSigner vcSigner; + + @Mock + private DataProviderPlugin dataProviderPlugin; + + @Mock + private ProofValidatorFactory proofValidatorFactory; + + @Mock + private VCICacheService vciCacheService; + + @Mock + private SecurityHelperService securityHelperService; + + @Mock + private AuditPlugin auditWrapper; + + @InjectMocks + private CertifyIssuanceServiceImpl issuanceService; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + @Test + public void getCredentialIssuerMetadata_valid() { + Mockito.when(issuerMetadata.containsKey("latest")).thenReturn(true); + Mockito.when(issuerMetadata.get("latest")).thenReturn((new LinkedHashMap())); + Map actual = issuanceService.getCredentialIssuerMetadata("latest"); + assertNotNull(actual); + } + + @Test + public void getCredentialIssuerMetadata_invalid() { + Mockito.when(issuerMetadata.containsKey("latest")).thenReturn(false); + assertThrows(InvalidRequestException.class, () -> issuanceService.getCredentialIssuerMetadata("latest")); + assertThrows(ErrorConstants.UNSUPPORTED_OPENID_VERSION, InvalidRequestException.class, () -> issuanceService.getCredentialIssuerMetadata(null)); + } + + @Test + public void getVerifiableCredential_invalidRequest() { + CredentialRequest cr = new CredentialRequest(); + cr.setFormat("fake-format"); + assertThrows(ErrorConstants.INVALID_REQUEST, InvalidRequestException.class, + () -> issuanceService.getCredential(cr)); + } + + @Test + public void getVerifiableCredential_invalidScope() { + CredentialRequest cr = new CredentialRequest(); + cr.setFormat(VCFormats.LDP_VC); + cr.setCredential_definition(new CredentialDefinition()); + Mockito.when(parsedAccessToken.isActive()).thenReturn(false); + assertThrows(NotAuthenticatedException.class, () -> issuanceService.getCredential(cr)); + } +} \ No newline at end of file diff --git a/certify-service/src/test/java/io/mosip/certify/services/KeymanagerLibSignerTest.java b/certify-service/src/test/java/io/mosip/certify/services/KeymanagerLibSignerTest.java new file mode 100644 index 00000000..69cb5d36 --- /dev/null +++ b/certify-service/src/test/java/io/mosip/certify/services/KeymanagerLibSignerTest.java @@ -0,0 +1,98 @@ +package io.mosip.certify.services; + +import com.nimbusds.jose.JWSAlgorithm; +import foundation.identity.jsonld.JsonLDObject; +import io.mosip.certify.api.dto.VCResult; +import io.mosip.certify.core.constants.Constants; +import io.mosip.certify.core.constants.SignatureAlg; +import io.mosip.certify.core.constants.VCDMConstants; +import io.mosip.kernel.signature.dto.JWTSignatureResponseDto; +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 io.mosip.kernel.signature.service.SignatureService; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class KeymanagerLibSignerTest { + + @Mock + private SignatureService signatureService; + @InjectMocks + private KeymanagerLibSigner signer; + private static final String VC_1 = """ + { + "@context": [ + "https://www.w3.org/ns/credentials/v2" + ], + "validFrom": "2024-09-22T23:06:22.123Z", + "type": [ + "VerifiableCredential", + "MyPrototypeCredential" + ], + "credentialSubject": { + "mySubjectProperty": "mySubjectValue" + } + } + """; + private static final String VC_2 = """ + { + "@context": [ + "https://www.w3.org/ns/credentials/v2" + ], + "validFrom": "2024-09-22T23:06:22.123Z", + "type": [ + "VerifiableCredential", + "MyPrototypeCredential" + ], + "credentialSubject": { + "mySubjectProperty": "mySubjectValue" + } + }"""; + + @Before + public void setup() { + } + + @Test + public void testPerformSuccess_VC2() { + // Mock Templated VC and Key Manager Input + String VCs[] = new String[]{VC_1, VC_2}; + for (String templatedVC : VCs) { + + Map keyMgrInput = new HashMap<>(); + keyMgrInput.put(KeyManagerConstants.PUBLIC_KEY_URL, "https://example.com/sample.pub.key.json/"); + keyMgrInput.put(KeyManagerConstants.KEY_APP_ID, Constants.CERTIFY_MOCK_RSA); + keyMgrInput.put(KeyManagerConstants.KEY_REF_ID, Constants.EMPTY_REF_ID); + keyMgrInput.put(KeyManagerConstants.VC_SIGN_ALGO, SignatureAlg.RSA_SIGNATURE_SUITE); + keyMgrInput.put(KeyManagerConstants.KEYMGR_SIGN_ALGO, JWSAlgorithm.RS256.getName()); + + // Mock Signature Service Response + JWTSignatureResponseDto jwsSignedData = new JWTSignatureResponseDto(); + jwsSignedData.setJwtSignedData("mocked-jws"); + when(signatureService.jwsSign(any())).thenReturn(jwsSignedData); + // Perform the test + VCResult vcResult = signer.perform(templatedVC, keyMgrInput); + + // Assertions + Assert.assertNotNull(vcResult); + JsonLDObject credential = vcResult.getCredential(); + Assert.assertNotNull(credential); + Assert.assertNotNull(credential.getJsonObject().get(VCDMConstants.PROOF)); + Assert.assertNotNull(vcResult.getCredential().getJsonObject().containsKey("proof")); + Map proof = (Map) credential.getJsonObject().get("proof"); + Assert.assertTrue(proof.containsKey("jws")); + Assert.assertEquals("mocked-jws", proof.get("jws")); + } + } + +} \ No newline at end of file diff --git a/certify-service/src/test/java/io/mosip/certify/services/templating/VelocityTemplatingEngineImplTest.java b/certify-service/src/test/java/io/mosip/certify/services/templating/VelocityTemplatingEngineImplTest.java new file mode 100644 index 00000000..5158fde9 --- /dev/null +++ b/certify-service/src/test/java/io/mosip/certify/services/templating/VelocityTemplatingEngineImplTest.java @@ -0,0 +1,145 @@ +package io.mosip.certify.services.templating; + +import io.mosip.certify.core.entity.TemplateData; +import io.mosip.certify.core.repository.TemplateRepository; +import junit.framework.TestCase; +import lombok.SneakyThrows; +import net.javacrumbs.jsonunit.assertj.JsonAssertions; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Service; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.mockito.Mockito.when; + +@Service +@RunWith(MockitoJUnitRunner.class) +public class VelocityTemplatingEngineImplTest extends TestCase { + @InjectMocks + private VelocityTemplatingEngineImpl formatter; + @Mock + TemplateRepository templateRepository; + + @Before + public void setUp() { + List templates = new ArrayList<>(); + TemplateData vc1 = initTemplate(""" + + { + "@context": [ + "https://www.w3.org/2018/credentials/v1"] + "issuer": "${issuer}", + "type": ["VerifiableCredential", "MockVerifiableCredential"], + "issuanceDate": "${validFrom}", + "expirationDate": "${validUntil}", + "credentialSubject": { + "postalCode": ${postalCode}, + "fullName": "${fullName}", + "dateOfBirth": "${dateOfBirth}", + "province": "${province}", + "phone": "${phone}", + "addressLine1": ${addressLine1}, + "region": "${region}", + "vcVer": "${vcVer}", + "UIN": ${UIN}, + "email": "${email}", + "face": "${face}" + } + } + """, + "MockVerifiableCredential,VerifiableCredential", + "https://schema.org,https://www.w3.org/2018/credentials/v1"); + TemplateData vc2 = initTemplate(""" + { + "@context": [ + "https://www.w3.org/ns/credentials/v2"], + "issuer": "${issuer}", + "type": ["VerifiableCredential", "MockVerifiableCredential"], + "validFrom": "${validFrom}", + "validUntil": "${validUntil}", + "credentialSubject": { + "gender": "${gender}", + "postalCode": ${postalCode}, + "fullName": "${fullName}", + "dateOfBirth": "${dateOfBirth}", + "province": "${province}", + "phone": "${phone}", + "addressLine1": ${addressLine1}, + "region": "${region}", + "vcVer": "${vcVer}", + "UIN": ${UIN}, + "email": "${email}", + "face": "${face}" + } + } + """, + "MockVerifiableCredential,VerifiableCredential", + "https://example.org/Person.json,https://www.w3.org/ns/credentials/v2" + + ); + when(templateRepository.findAll()).thenReturn(List.of(vc1, vc2)); + formatter.initialize(); +// engine = new VelocityEngine(); +// engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath"); +// engine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName()); +// engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, "org.apache.velocity.runtime.log.NullLogChute"); +// engine.setProperty(RuntimeConstants.INPUT_ENCODING, "UTF-8"); +// engine.setProperty(RuntimeConstants.OUTPUT_ENCODING, "UTF-8"); +// engine.init(); + } + + private TemplateData initTemplate(String template, String type, String context) { + TemplateData t = new TemplateData(); + t.setTemplate(template); + t.setCredentialType(type); + t.setContext(context); + return t; + } + + @SneakyThrows + @Test + public void testTemplating() { + // 1. setup template + Resource cr = new ClassPathResource("MockCredential1.vm"); + assert cr.isFile(); + String t = Files.readString(cr.getFile().toPath(), StandardCharsets.UTF_8); + assert t != null; + Map ret = new HashMap<>(); + ret.put("vcVer", "VC-V1"); + // ret.put("issuer", "https://example.com/fake-issuer"); + ret.put("fullName", "Amit Developer"); + ret.put("validFrom", "01/01/2022"); + ret.put("validUntil", "02/02/2122"); + ret.put("gender", "female"); + ret.put("dateOfBirth", "01/01/2022"); + ret.put("email", "amit@fakemail.com"); + ret.put("UIN", 123456); + ret.put("phone", "1234567890"); + // both of the below work + ret.put("addressLine1", List.of("1", "Fake building", "Fake Area", "Fake City", "Near Fake Landmark")); + // ret.put("addressLine1", new String[]{"1", "Fake building", "Fake Area", "Fake City", "Near Fake Landmark"}); + ret.put("province", "Fake Area"); + ret.put("region", "FakeRegion"); + ret.put("postalCode", "123"); + ret.put("face", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII"); + Map templateMap = Map.of("templateName", "MockVerifiableCredential,VerifiableCredential:https://example.org/Person.json,https://www.w3.org/ns/credentials/v2", + "issuerURI", "https://example.com/fake-issuer"); + String actualJSON = formatter.format(ret, templateMap); + String expectedJSON = """ + {"credentialSubject":{"face":"data:image\\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX\\/\\/\\/+\\/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD\\/aNpbtEAAAAASUVORK5CYII","gender":"female","province":"Fake Area","phone":"1234567890","postalCode":123,"fullName":"Amit Developer","addressLine1":["1","Fake building","Fake Area","Fake City","Near Fake Landmark"],"dateOfBirth":"01\\/01\\/2022","vcVer":"VC-V1","UIN":123456,"region":"FakeRegion","email":"amit@fakemail.com"},"validUntil":"02\\/02\\/2122","validFrom":"01\\/01\\/2022","type":["VerifiableCredential","MockVerifiableCredential"],"@context":["https:\\/\\/www.w3.org\\/ns\\/credentials\\/v2"],"issuer":"https:\\/\\/example.com\\/fake-issuer"} + """; + JsonAssertions.assertThatJson(actualJSON).isEqualTo(expectedJSON); + } +} \ No newline at end of file diff --git a/certify-service/src/test/java/io/mosip/certify/utils/CredentialUtilsTest.java b/certify-service/src/test/java/io/mosip/certify/utils/CredentialUtilsTest.java new file mode 100644 index 00000000..e6c4c0e1 --- /dev/null +++ b/certify-service/src/test/java/io/mosip/certify/utils/CredentialUtilsTest.java @@ -0,0 +1,18 @@ +package io.mosip.certify.utils; + +import io.mosip.certify.api.dto.VCRequestDto; +import junit.framework.TestCase; +import org.junit.jupiter.params.provider.ArgumentsSource; + +import java.util.List; + +public class CredentialUtilsTest extends TestCase { + + public void testGetTemplateName() { + VCRequestDto request = new VCRequestDto(); + request.setContext(List.of("https://www.w3.org/ns/credentials/v2", "https://example.org/Person.json")); + request.setType(List.of("VerifiableCredential", "UniversityCredential")); + String expected = "UniversityCredential,VerifiableCredential:https://example.org/Person.json,https://www.w3.org/ns/credentials/v2"; + assertEquals(expected, CredentialUtils.getTemplateName(request)); + } +} \ No newline at end of file diff --git a/certify-service/src/test/resources/MockCredential1.vm b/certify-service/src/test/resources/MockCredential1.vm new file mode 100644 index 00000000..8e8811b4 --- /dev/null +++ b/certify-service/src/test/resources/MockCredential1.vm @@ -0,0 +1,23 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://vharsh.github.io/DID/mock-context.json"], + "issuer": "${issuer}", + "type": ["VerifiableCredential", "MockVerifiableCredential"], + "issuanceDate": "${validFrom}", + "expirationDate": "${validUntil}", + "credentialSubject": { + "gender": "${gender}", + "postalCode": ${postalCode}, + "fullName": "${fullName}", + "dateOfBirth": "${dateOfBirth}", + "province": "${province}", + "phone": "${phone}", + "addressLine1": ${addressLine1}, + "region": "${region}", + "vcVer": "${vcVer}", + "UIN": ${UIN}, + "email": "${email}", + "face": "${face}" + } +} diff --git a/certify-service/src/test/resources/MockCredential2.vm b/certify-service/src/test/resources/MockCredential2.vm new file mode 100644 index 00000000..7107ec8c --- /dev/null +++ b/certify-service/src/test/resources/MockCredential2.vm @@ -0,0 +1,22 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1"], + "issuer": "${issuer}", + "type": ["VerifiableCredential", "MockVerifiableCredential"], + "issuanceDate": "${validFrom}", + "expirationDate": "${validUntil}", + "credentialSubject": { + "gender": "${gender}", + "postalCode": "${postalCode}", + "fullName": "${fullName}", + "dateOfBirth": "${dateOfBirth}", + "province": "${province}", + "phone": "${phone}", + "addressLine1": ${addressLine1}, + "region": "${region}", + "vcVer": "${vcVer}", + "UIN": ${UIN}, + "email": "${email}", + "face": "${face}" + } +} diff --git a/certify-service/src/test/resources/application-test.properties b/certify-service/src/test/resources/application-test.properties index ba209a2a..ac815402 100644 --- a/certify-service/src/test/resources/application-test.properties +++ b/certify-service/src/test/resources/application-test.properties @@ -7,10 +7,13 @@ mosip.certify.integration.scan-base-package=io.mosip.certify mosip.certify.integration.audit-plugin=TestAuditPlugin mosip.certify.integration.vci-plugin=TestVCIPluginImpl +mosip.certify.issuer=PluginIssuer ## ------------------------------------------ Discovery openid-configuration ------------------------------------------- mosipbox.public.url=http://localhost:8090 mosip.certify.discovery.issuer-id=${mosipbox.public.url}${server.servlet.path} +mosip.certify.issuer.pub.key=http://localhost/pub.key.json +mosip.certify.issuer.uri=http://localhost/pub.key.json ##--------------------------------------------------------------------------------------------------------------------- spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration @@ -23,6 +26,7 @@ mosip.certify.authn.filter-urls={ '${server.servlet.path}/issuance/credential' } mosip.certify.authn.issuer-uri=${mosipbox.public.url}${server.servlet.path} mosip.certify.authn.jwk-set-uri=${mosipbox.public.url}${server.servlet.path}/oauth/.well-known/jwks.json mosip.certify.authn.allowed-audiences={ '${mosipbox.public.url}${server.servlet.path}/issuance/credential' } +mosip.certify.mock.authenticator.get-identity-url=http://localhost:8082/v1/mock-identity-system/identity mosip.certify.supported.jwt-proof-alg={'RS256'} mosip.certify.key-values={\ @@ -283,4 +287,4 @@ spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=test spring.datasource.password=test -spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true \ No newline at end of file +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true diff --git a/certify-service/src/test/resources/data.sql b/certify-service/src/test/resources/data.sql index 87d0ebbd..ee9d9314 100644 --- a/certify-service/src/test/resources/data.sql +++ b/certify-service/src/test/resources/data.sql @@ -1,2 +1,2 @@ -MERGE INTO KEY_POLICY_DEF (APP_ID,KEY_VALIDITY_DURATION,PRE_EXPIRE_DAYS,ACCESS_ALLOWED,IS_ACTIVE,CR_BY,CR_DTIMES) KEY(APP_ID) VALUES ('ROOT', 1095, 50, 'NA', true, 'mosipadmin', now()), ('CERTIFY_SERVICE', 1095, 50, 'NA', true, 'mosipadmin', now()), ('CERTIFY_PARTNER', 1095, 50, 'NA', true, 'mosipadmin', now()); +MERGE INTO KEY_POLICY_DEF (APP_ID,KEY_VALIDITY_DURATION,PRE_EXPIRE_DAYS,ACCESS_ALLOWED,IS_ACTIVE,CR_BY,CR_DTIMES) KEY(APP_ID) VALUES ('ROOT', 1095, 50, 'NA', true, 'mosipadmin', now()), ('CERTIFY_SERVICE', 1095, 50, 'NA', true, 'mosipadmin', now()), ('CERTIFY_PARTNER', 1095, 50, 'NA', true, 'mosipadmin', now()), ('CERTIFY_MOCK_RSA', 1095, 50, 'NA', true, 'mosipadmin', now()); diff --git a/certify-service/src/test/resources/schema.sql b/certify-service/src/test/resources/schema.sql index f47caa44..a3078f01 100644 --- a/certify-service/src/test/resources/schema.sql +++ b/certify-service/src/test/resources/schema.sql @@ -53,3 +53,12 @@ CREATE TABLE IF NOT EXISTS svg_template ( upd_dtimes timestamp, CONSTRAINT pk_svgtmp_id PRIMARY KEY (id) ); + +CREATE TABLE IF NOT EXISTS template_data( + context character varying(1024) NOT NULL, + credential_type character varying(512) NOT NULL, + template VARCHAR NOT NULL, + cr_dtimes TIMESTAMP DEFAULT NOW() NOT NULL, + upd_dtimes TIMESTAMP, + CONSTRAINT pk_template PRIMARY KEY (context, credential_type) +); diff --git a/db_scripts/mosip_certify/ddl/template_data.sql b/db_scripts/mosip_certify/ddl/template_data.sql new file mode 100644 index 00000000..2130e644 --- /dev/null +++ b/db_scripts/mosip_certify/ddl/template_data.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS template_data( + context character varying(1024) NOT NULL, + credential_type character varying(512) NOT NULL, + template VARCHAR NOT NULL, + cr_dtimes timestamp NOT NULL default now(), + upd_dtimes timestamp, + CONSTRAINT pk_template PRIMARY KEY (context, credential_type) +); diff --git a/db_scripts/mosip_certify/dml/certify-key_policy_def.csv b/db_scripts/mosip_certify/dml/certify-key_policy_def.csv index 1dcfce5f..79638499 100644 --- a/db_scripts/mosip_certify/dml/certify-key_policy_def.csv +++ b/db_scripts/mosip_certify/dml/certify-key_policy_def.csv @@ -2,3 +2,4 @@ app_id,key_validity_duration,pre_expire_days,access_allowed,is_active,cr_by,cr_d ROOT,2920,1125,NA,TRUE,mosipadmin,now() CERTIFY_SERVICE,1095,60,NA,TRUE,mosipadmin,now() CERTIFY_PARTNER,1095,60,NA,TRUE,mosipadmin,now() +CERTIFY_MOCK_RSA,1095,60,NA,TRUE,mosipadmin,now()