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 b0d5e9a4..a6de5f2e 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 @@ -29,4 +29,6 @@ public class Constants { public static final String CERTIFY_VC_SIGN_EC_R1 = "CERTIFY_VC_SIGN_EC_R1"; public static final String EC_SECP256K1_SIGN = "EC_SECP256K1_SIGN"; public static final String EC_SECP256R1_SIGN = "EC_SECP256R1_SIGN"; + public static final String ACTIVE = "active"; + public static final String INACTIVE = "inactive"; } diff --git a/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialConfigResponse.java b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialConfigResponse.java new file mode 100644 index 00000000..edee3740 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialConfigResponse.java @@ -0,0 +1,14 @@ +package io.mosip.certify.core.dto; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Data +public class CredentialConfigResponse { + + @NotEmpty + private String id; + + @NotEmpty + private String status; +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialConfigurationDTO.java b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialConfigurationDTO.java new file mode 100644 index 00000000..185e1c02 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialConfigurationDTO.java @@ -0,0 +1,69 @@ +package io.mosip.certify.core.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.mosip.certify.core.constants.ErrorConstants; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.annotations.Comment; + +import java.util.List; +import java.util.Map; + +@Data +public class CredentialConfigurationDTO { + + private String vcTemplate; + + @NotNull(message = ErrorConstants.INVALID_REQUEST) + private List context; + + @NotNull(message = ErrorConstants.INVALID_REQUEST) + private List credentialType; + + @NotNull(message = ErrorConstants.INVALID_REQUEST) + private String credentialFormat; + + private String didUrl; + + private String keyManagerAppId; + + private String keyManagerRefId; + + private String signatureAlgo; //Can be called as Proof algorithm + + private String sdClaim; + + @Valid + @NotNull(message = ErrorConstants.INVALID_REQUEST) + private List> display; + + private List order; + + @NotNull(message = ErrorConstants.INVALID_REQUEST) + private String scope; + + @NotNull(message = ErrorConstants.INVALID_REQUEST) + @JsonProperty("cryptographic_binding_methods_supported") + private List cryptographicBindingMethodsSupported; + + @NotNull + @JsonProperty("credential_signing_alg_values_supported") + private List credentialSigningAlgValuesSupported; + + @NotNull(message = ErrorConstants.INVALID_REQUEST) + @JsonProperty("proof_types_supported") + private Map proofTypesSupported; + + @JsonProperty("credentialSubject") + private Map credentialSubject; + + @JsonProperty("claims") + private Map claims; + + @JsonProperty("doctype") + private String docType; + + private List> pluginConfigurations; +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialConfigurationSupportedDTO.java b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialConfigurationSupportedDTO.java new file mode 100644 index 00000000..58638b36 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialConfigurationSupportedDTO.java @@ -0,0 +1,36 @@ +package io.mosip.certify.core.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class CredentialConfigurationSupportedDTO { + + private String format; + + @JsonProperty("doctype") + private String docType; + + private String scope; + + @JsonProperty("cryptographic_binding_methods_supported") + private List cryptographicBindingMethodsSupported; + + @JsonProperty("credential_signing_alg_values_supported") + private List credentialSigningAlgValuesSupported; + + @JsonProperty("proof_types_supported") + private Map proofTypesSupported; + + @JsonProperty("credential_definition") + private CredentialDefinition credentialDefinition; + + private Map claims; + + private List> display; + + private List order; +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialIssuerMetadataDTO.java b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialIssuerMetadataDTO.java new file mode 100644 index 00000000..f4726de1 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialIssuerMetadataDTO.java @@ -0,0 +1,25 @@ +package io.mosip.certify.core.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class CredentialIssuerMetadataDTO { + + @JsonProperty("credential_issuer") + private String credentialIssuer; + + @JsonProperty("authorization_servers") + private List authorizationServers; + + @JsonProperty("credential_endpoint") + private String credentialEndpoint; + + private List> display; + + @JsonProperty("credential_configurations_supported") + private Map credentialConfigurationSupportedDTO; +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/spi/CredentialConfigurationService.java b/certify-core/src/main/java/io/mosip/certify/core/spi/CredentialConfigurationService.java new file mode 100644 index 00000000..586c250a --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/spi/CredentialConfigurationService.java @@ -0,0 +1,19 @@ +package io.mosip.certify.core.spi; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.mosip.certify.core.dto.CredentialConfigResponse; +import io.mosip.certify.core.dto.CredentialConfigurationDTO; +import io.mosip.certify.core.dto.CredentialIssuerMetadataDTO; + +public interface CredentialConfigurationService { + + CredentialConfigResponse addCredentialConfiguration(CredentialConfigurationDTO credentialConfigurationDTO) throws JsonProcessingException; + + CredentialConfigurationDTO getCredentialConfigurationById(String id) throws JsonProcessingException; + + CredentialConfigResponse updateCredentialConfiguration(String id, CredentialConfigurationDTO credentialConfigurationDTO) throws JsonProcessingException; + + String deleteCredentialConfigurationById(String id); + + CredentialIssuerMetadataDTO fetchCredentialIssuerMetadata(String version); +} diff --git a/certify-service/pom.xml b/certify-service/pom.xml index 70bfcc18..d4dffc4a 100644 --- a/certify-service/pom.xml +++ b/certify-service/pom.xml @@ -124,6 +124,16 @@ sd-jwt 1.5 + + org.mapstruct + mapstruct + 1.5.5.Final + + + io.hypersistence + hypersistence-utils-hibernate-60 + 3.9.0 + @@ -145,6 +155,30 @@ + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + + + org.mapstruct + mapstruct-processor + 1.5.5.Final + + + org.projectlombok + lombok + 1.18.30 + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + + + + diff --git a/certify-service/src/main/java/io/mosip/certify/controller/CredentialConfigController.java b/certify-service/src/main/java/io/mosip/certify/controller/CredentialConfigController.java new file mode 100644 index 00000000..8ca4fdf8 --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/controller/CredentialConfigController.java @@ -0,0 +1,57 @@ +package io.mosip.certify.controller; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.mosip.certify.core.dto.CredentialConfigResponse; +import io.mosip.certify.core.dto.CredentialConfigurationDTO; +import io.mosip.certify.core.dto.CredentialIssuerMetadataDTO; +import io.mosip.certify.core.spi.CredentialConfigurationService; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@Slf4j +@RestController +@RequestMapping("/credentials") +public class CredentialConfigController { + + @Autowired + private CredentialConfigurationService credentialConfigurationService; + + @PostMapping(value = "/configurations", produces = "application/json") + public ResponseEntity addCredentialConfiguration(@Valid @RequestBody CredentialConfigurationDTO credentialConfigurationRequest) throws JsonProcessingException { + + CredentialConfigResponse credentialConfigResponse = credentialConfigurationService.addCredentialConfiguration(credentialConfigurationRequest); + return new ResponseEntity<>(credentialConfigResponse, HttpStatus.CREATED); + } + + @GetMapping(value = "/configurations/{configurationId}", produces = "application/json") + public ResponseEntity getCredentialConfigurationById(@PathVariable String configurationId) throws JsonProcessingException { + + CredentialConfigurationDTO credentialConfigurationDTO = credentialConfigurationService.getCredentialConfigurationById(configurationId); + return new ResponseEntity<>(credentialConfigurationDTO, HttpStatus.OK); + } + + @PutMapping(value = "/configurations/{configurationId}", produces = "application/json") + public ResponseEntity updateCredentialConfiguration(@PathVariable String configurationId, + @Valid @RequestBody CredentialConfigurationDTO credentialConfigurationRequest) throws JsonProcessingException { + + CredentialConfigResponse credentialConfigResponse = credentialConfigurationService.updateCredentialConfiguration(configurationId, credentialConfigurationRequest); + return new ResponseEntity<>(credentialConfigResponse, HttpStatus.OK); + } + + @DeleteMapping(value = "/configurations/{configurationId}", produces = "application/json") + public ResponseEntity deleteCredentialConfigurationById(@PathVariable String configurationId) { + + String response = credentialConfigurationService.deleteCredentialConfigurationById(configurationId); + return new ResponseEntity<>(response, HttpStatus.OK); + } + + @GetMapping(value = "/.well-known/openid-credential-issuer", produces = "application/json") + public CredentialIssuerMetadataDTO getCredentialIssuerMetadata( + @RequestParam(name = "version", required = false, defaultValue = "latest") String version) { + return credentialConfigurationService.fetchCredentialIssuerMetadata(version); + } +} diff --git a/certify-service/src/main/java/io/mosip/certify/entity/CredentialConfig.java b/certify-service/src/main/java/io/mosip/certify/entity/CredentialConfig.java new file mode 100644 index 00000000..c0c359bd --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/entity/CredentialConfig.java @@ -0,0 +1,111 @@ +package io.mosip.certify.entity; + + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +import io.hypersistence.utils.hibernate.type.json.JsonBinaryType; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.annotations.Comment; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.annotations.Type; +import org.hibernate.type.SqlTypes; + +@Data +@Entity +@NoArgsConstructor +@Table(name = "credential_config") +@IdClass(TemplateId.class) +public class CredentialConfig { + + private String configId; + + private String status; + + private String vcTemplate; + + @Id + private String context; + + @Id + private String credentialType; + + @Id + private String credentialFormat; + + @Comment("URL for the public key. Should point to the exact key. Supports DID document or public key") + private String didUrl; + + @Comment("AppId of the keymanager") + private String keyManagerAppId; + + @Comment("RefId of the keymanager") + private String keyManagerRefId; + + @Comment("This for VC signature or proof algorithm") + private String signatureAlgo; //Can be called as Proof algorithm + + @Comment("This is a comma seperated list for selective disclosure.") + private String sdClaim; + + @NotNull(message = "Invalid request") + @Type(JsonBinaryType.class) + @JdbcTypeCode(SqlTypes.JSON) + @Column(name = "display", columnDefinition = "jsonb") + private List> display; + + @Column(name = "display_order", columnDefinition = "TEXT[]") + private List order; + + @NotNull(message = "Invalid request") + private String scope; + + @NotNull(message = "Invalid request") + @Column(name = "cryptographic_binding_methods_supported", columnDefinition = "TEXT[]") + private List cryptographicBindingMethodsSupported; + + @NotNull(message = "Invalid request") + @Column(name = "credential_signing_alg_values_supported", columnDefinition = "TEXT[]") + private List credentialSigningAlgValuesSupported; + + @NotNull(message = "Invalid request") + @Type(JsonBinaryType.class) + @JdbcTypeCode(SqlTypes.JSON) + @Column(name = "proof_types_supported", columnDefinition = "jsonb") + private Map proofTypesSupported; + + @Column(name = "doctype") + private String docType; + + @Type(JsonBinaryType.class) + @JdbcTypeCode(SqlTypes.JSON) + @Column(name = "credential_subject", columnDefinition = "jsonb") + private Map credentialSubject; + + @Type(JsonBinaryType.class) + @JdbcTypeCode(SqlTypes.JSON) + @Column(name = "claims", columnDefinition = "jsonb") + private Map claims; + + @Type(JsonBinaryType.class) + @JdbcTypeCode(SqlTypes.JSON) + @Column(name = "plugin_configurations", columnDefinition = "jsonb") + private List> pluginConfigurations; + + @NotNull + @Column(name = "cr_dtimes") + private LocalDateTime createdTimes; + + @Column(name = "upd_dtimes") + private LocalDateTime updatedTimes; + +} diff --git a/certify-service/src/main/java/io/mosip/certify/entity/CredentialTemplate.java b/certify-service/src/main/java/io/mosip/certify/entity/CredentialTemplate.java deleted file mode 100644 index 4b61cb63..00000000 --- a/certify-service/src/main/java/io/mosip/certify/entity/CredentialTemplate.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.mosip.certify.entity; - - -import java.time.LocalDateTime; - -import org.hibernate.annotations.Comment; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; -import jakarta.persistence.Id; -import jakarta.persistence.IdClass; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Entity -@NoArgsConstructor -@Table(name = "credential_template") -@IdClass(TemplateId.class) -public class CredentialTemplate { - @NotBlank(message = "Template is mandatory") - @Getter - @Setter - private String template; - @Id - @Getter - @Setter - private String context; - @Id - @Getter - @Setter - private String credentialType; - @Id - @Getter - @Setter - private String credentialFormat; - @Getter - @Setter - @Comment("URL for the public key. Should point to the exact key. Supports DID document or public key") - private String didUrl; - @Getter - @Setter - @Comment("AppId of the keymanager") - private String keyManagerAppId; - @Getter - @Setter - @Comment("RefId of the keymanager") - private String keyManagerRefId; - @Getter - @Setter - @Comment("This for VC signature or proof algorithm") - private String signatureAlgo; //Can be called as Proof algorithm - @Getter - @Setter - @Comment("This is a comma seperated list for selective disclosure.") - private String sdClaim; - - @NotBlank - @Column(name = "cr_dtimes") - private LocalDateTime createdTimes; - - @Column(name = "upd_dtimes") - private LocalDateTime updatedTimes; - -} diff --git a/certify-service/src/main/java/io/mosip/certify/mapper/CredentialConfigMapper.java b/certify-service/src/main/java/io/mosip/certify/mapper/CredentialConfigMapper.java new file mode 100644 index 00000000..aae4a08f --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/mapper/CredentialConfigMapper.java @@ -0,0 +1,55 @@ +package io.mosip.certify.mapper; + +import io.mosip.certify.core.dto.CredentialConfigurationDTO; +import io.mosip.certify.entity.CredentialConfig; +import org.mapstruct.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface CredentialConfigMapper { + @Mapping(target = "configId", ignore = true) + @Mapping(target = "status", ignore = true) + @Mapping(target = "createdTimes", expression = "java(java.time.LocalDateTime.now())") + @Mapping(target = "updatedTimes", ignore = true) + @Mapping(target = "context", source = "context", qualifiedByName = "listToCommaSeparatedString") + @Mapping(target = "credentialType", source = "credentialType", qualifiedByName = "listToCommaSeparatedString") + CredentialConfig toEntity(CredentialConfigurationDTO dto); + + // Convert Entity to DTO + @Mapping(target = "context", source = "context", qualifiedByName = "commaSeparatedStringToList") + @Mapping(target = "credentialType", source = "credentialType", qualifiedByName = "commaSeparatedStringToList") + CredentialConfigurationDTO toDto(CredentialConfig entity); + + // Update existing entity with DTO data + @Mapping(target = "configId", ignore = true) + @Mapping(target = "status", ignore = true) + @Mapping(target = "createdTimes", ignore = true) + @Mapping(target = "updatedTimes", expression = "java(java.time.LocalDateTime.now())") + @Mapping(target = "context", source = "context", qualifiedByName = "listToCommaSeparatedString") + @Mapping(target = "credentialType", source = "credentialType", qualifiedByName = "listToCommaSeparatedString") + void updateEntityFromDto(CredentialConfigurationDTO dto, @MappingTarget CredentialConfig entity); + + @Named("listToCommaSeparatedString") + default String listToCommaSeparatedString(List list) { + if (list == null || list.isEmpty()) { + return null; + } + return list.stream() + .sorted() + .collect(Collectors.joining(",")); + } + + @Named("commaSeparatedStringToList") + default List commaSeparatedStringToList(String str) { + if (str == null || str.isEmpty()) { + return new ArrayList<>(); + } + return Arrays.stream(str.split(",")) + .map(String::trim) + .collect(Collectors.toList()); + } +} diff --git a/certify-service/src/main/java/io/mosip/certify/repository/CredentialConfigRepository.java b/certify-service/src/main/java/io/mosip/certify/repository/CredentialConfigRepository.java new file mode 100644 index 00000000..84c96140 --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/repository/CredentialConfigRepository.java @@ -0,0 +1,17 @@ +package io.mosip.certify.repository; + +import io.mosip.certify.entity.CredentialConfig; +import io.mosip.certify.entity.TemplateId; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface CredentialConfigRepository extends JpaRepository { + Optional findByCredentialTypeAndContext(String credentialType, String context); + // NOTE: best practice? .save() + Optional findByConfigId(String configId); + void deleteByConfigId(String configId); +} + diff --git a/certify-service/src/main/java/io/mosip/certify/repository/CredentialTemplateRepository.java b/certify-service/src/main/java/io/mosip/certify/repository/CredentialTemplateRepository.java deleted file mode 100644 index d290641a..00000000 --- a/certify-service/src/main/java/io/mosip/certify/repository/CredentialTemplateRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.mosip.certify.repository; - -import io.mosip.certify.entity.CredentialTemplate; -import io.mosip.certify.entity.TemplateId; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -@Repository -public interface CredentialTemplateRepository extends JpaRepository { - Optional findByCredentialTypeAndContext(String credentialType, String context); - // NOTE: best practice? .save() -} - 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 index 36e624a5..d24aab61 100644 --- a/certify-service/src/main/java/io/mosip/certify/services/CertifyIssuanceServiceImpl.java +++ b/certify-service/src/main/java/io/mosip/certify/services/CertifyIssuanceServiceImpl.java @@ -382,7 +382,7 @@ private VCResult getVerifiableCredential(CredentialRequest credentialRequest, throw new CertifyException(ErrorConstants.UNSUPPORTED_VC_FORMAT); } } - + private CredentialResponse getCredentialResponse(String format, VCResult vcResult) { switch (format) { case "ldp_vc" -> { diff --git a/certify-service/src/main/java/io/mosip/certify/services/CredentialConfigurationServiceImpl.java b/certify-service/src/main/java/io/mosip/certify/services/CredentialConfigurationServiceImpl.java new file mode 100644 index 00000000..e4c1eb14 --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/services/CredentialConfigurationServiceImpl.java @@ -0,0 +1,163 @@ +package io.mosip.certify.services; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.mosip.certify.core.constants.Constants; +import io.mosip.certify.core.dto.*; +import io.mosip.certify.core.exception.CertifyException; +import io.mosip.certify.core.spi.CredentialConfigurationService; +import io.mosip.certify.entity.CredentialConfig; +import io.mosip.certify.mapper.CredentialConfigMapper; +import io.mosip.certify.repository.CredentialConfigRepository; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.transaction.Transactional; +import java.util.*; + +@Slf4j +@Component +@Transactional +public class CredentialConfigurationServiceImpl implements CredentialConfigurationService { + + @Autowired + private CredentialConfigRepository credentialConfigRepository; + + @Autowired + private CredentialConfigMapper credentialConfigMapper; + + @Value("${mosip.certify.identifier}") + private String credentialIssuer; + + @Value("#{'${mosip.certify.authorization.url}'.split(',')}") + private List authServers; + + @Value("${server.servlet.path}") + private String servletPath; + + @Value("${mosip.certify.plugin-mode}") + private String pluginMode; + + @Value("#{${mosip.certify.credential-config.issuer.display}}") + private List> issuerDisplay; + + @Override + public CredentialConfigResponse addCredentialConfiguration(CredentialConfigurationDTO credentialConfigurationDTO) throws JsonProcessingException { + CredentialConfig credentialConfig = credentialConfigMapper.toEntity(credentialConfigurationDTO); + credentialConfig.setConfigId(UUID.randomUUID().toString()); + credentialConfig.setStatus(Constants.ACTIVE); + + if(pluginMode.equals("DataProvider") && credentialConfig.getVcTemplate() == null) { + throw new CertifyException("Credential Template is mandatory for this `DataProvider` plugin issuer."); + } + + if(credentialConfigurationDTO.getCredentialSubject() == null && + (credentialConfigurationDTO.getClaims() == null || credentialConfigurationDTO.getDocType() == null)) { + + throw new CertifyException("Please provide a value for at least one of credentialSubject or both doctype and claims"); + } + + credentialConfigRepository.save(credentialConfig); + + CredentialConfigResponse credentialConfigResponse = new CredentialConfigResponse(); + credentialConfigResponse.setId(credentialConfig.getConfigId()); + credentialConfigResponse.setStatus(credentialConfig.getStatus()); + + return credentialConfigResponse; + } + + @Override + public CredentialConfigurationDTO getCredentialConfigurationById(String id) throws JsonProcessingException { + Optional optional = credentialConfigRepository.findByConfigId(id); + + if(optional.isEmpty()) { + throw new CertifyException("Configuration not found with the provided id: " + id); + } + + CredentialConfig credentialConfig = optional.get(); + if(!credentialConfig.getStatus().equals(Constants.ACTIVE)) { + throw new CertifyException("Configuration not active."); + } + + CredentialConfigurationDTO credentialConfigurationDTO = credentialConfigMapper.toDto(credentialConfig); + + return credentialConfigurationDTO; + } + + @Override + public CredentialConfigResponse updateCredentialConfiguration(String id, CredentialConfigurationDTO credentialConfigurationDTO) throws JsonProcessingException { + Optional optional = credentialConfigRepository.findByConfigId(id); + + if(optional.isEmpty()) { + throw new CertifyException("Configuration not found with the provided id: " + id); + } + + CredentialConfig credentialConfig = optional.get(); + credentialConfigMapper.updateEntityFromDto(credentialConfigurationDTO, credentialConfig); + credentialConfigRepository.save(credentialConfig); + + CredentialConfigResponse credentialConfigResponse = new CredentialConfigResponse(); + credentialConfigResponse.setId(credentialConfig.getConfigId()); + credentialConfigResponse.setStatus(credentialConfig.getStatus()); + + return credentialConfigResponse; + } + + @Override + public String deleteCredentialConfigurationById(String id) { + Optional optional = credentialConfigRepository.findByConfigId(id); + + if(optional.isEmpty()) { + throw new CertifyException("Configuration not found with the provided id: " + id); + } + + credentialConfigRepository.delete(optional.get()); + return "Configuration deleted with id: " + id; + } + + @Override + public CredentialIssuerMetadataDTO fetchCredentialIssuerMetadata(String version) { + CredentialIssuerMetadataDTO credentialIssuerMetadata = new CredentialIssuerMetadataDTO(); + credentialIssuerMetadata.setCredentialIssuer(credentialIssuer); + credentialIssuerMetadata.setAuthorizationServers(authServers); + String credentialEndpoint = credentialIssuer + servletPath + "/issuance" + (!version.equals("latest") ? "/" +version : "") + "/credential" ; + credentialIssuerMetadata.setCredentialEndpoint(credentialEndpoint); + credentialIssuerMetadata.setDisplay(issuerDisplay); + List credentialConfigList = credentialConfigRepository.findAll(); + Map credentialConfigurationSupportedMap = new HashMap<>(); + credentialConfigList.stream() + .forEach(credentialConfig -> { + CredentialConfigurationSupportedDTO credentialConfigurationSupported = new CredentialConfigurationSupportedDTO(); + CredentialConfigurationDTO credentialConfigurationDTO = credentialConfigMapper.toDto(credentialConfig); + credentialConfigurationSupported.setFormat(credentialConfigurationDTO.getCredentialFormat()); + credentialConfigurationSupported.setScope(credentialConfigurationDTO.getScope()); + credentialConfigurationSupported.setCryptographicBindingMethodsSupported(credentialConfigurationDTO.getCryptographicBindingMethodsSupported()); + credentialConfigurationSupported.setCredentialSigningAlgValuesSupported(credentialConfigurationDTO.getCredentialSigningAlgValuesSupported()); + credentialConfigurationSupported.setProofTypesSupported(credentialConfigurationDTO.getProofTypesSupported()); + + credentialConfigurationSupported.setDisplay(credentialConfigurationDTO.getDisplay()); + credentialConfigurationSupported.setOrder(credentialConfigurationDTO.getOrder()); + + if(credentialConfig.getCredentialSubject() != null) { + CredentialDefinition credentialDefinition = new CredentialDefinition(); + credentialDefinition.setType(credentialConfigurationDTO.getCredentialType()); + credentialDefinition.setContext(credentialConfigurationDTO.getContext()); + credentialDefinition.setCredentialSubject(credentialConfig.getCredentialSubject()); + credentialConfigurationSupported.setCredentialDefinition(credentialDefinition); + } else { + credentialConfigurationSupported.setClaims(credentialConfig.getClaims()); + credentialConfigurationSupported.setDocType(credentialConfig.getDocType()); + } + + String credentialType = credentialConfigurationDTO.getCredentialType().stream() + .filter(type -> !type.equals("VerifiableCredential")) + .findFirst() + .orElse(null); + credentialConfigurationSupportedMap.put(credentialType, credentialConfigurationSupported); + }); + + credentialIssuerMetadata.setCredentialConfigurationSupportedDTO(credentialConfigurationSupportedMap); + return credentialIssuerMetadata; + } +} diff --git a/certify-service/src/main/java/io/mosip/certify/vcformatters/VelocityTemplatingEngineImpl.java b/certify-service/src/main/java/io/mosip/certify/vcformatters/VelocityTemplatingEngineImpl.java index 56d98c29..98357bab 100644 --- a/certify-service/src/main/java/io/mosip/certify/vcformatters/VelocityTemplatingEngineImpl.java +++ b/certify-service/src/main/java/io/mosip/certify/vcformatters/VelocityTemplatingEngineImpl.java @@ -36,12 +36,11 @@ import org.json.JSONArray; import org.json.JSONObject; -import io.micrometer.tracing.SamplerFunction; import io.mosip.certify.core.constants.ErrorConstants; import io.mosip.certify.core.exception.CertifyException; import io.mosip.certify.core.exception.RenderingTemplateException; -import io.mosip.certify.entity.CredentialTemplate; -import io.mosip.certify.repository.CredentialTemplateRepository; +import io.mosip.certify.entity.CredentialConfig; +import io.mosip.certify.repository.CredentialConfigRepository; import io.mosip.certify.core.constants.Constants; import io.mosip.certify.core.constants.VCDM2Constants; import io.mosip.certify.core.constants.VCDMConstants; @@ -53,8 +52,6 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import io.mosip.certify.credential.Credential; - @Slf4j @Service @@ -63,7 +60,7 @@ public class VelocityTemplatingEngineImpl implements VCFormatter { public static final String DELIMITER = ":"; Map> templateCache; @Autowired - CredentialTemplateRepository credentialTemplateRepository; + CredentialConfigRepository credentialConfigRepository; @Autowired RenderingTemplateService renderingTemplateService; @@ -79,7 +76,7 @@ public void initialize() { // 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<>(); - credentialTemplateRepository.findAll().stream().forEach((template -> { + credentialConfigRepository.findAll().stream().forEach((template -> { Map templateMap = new HashMap<>(); ObjectMapper oMapper = new ObjectMapper(); templateMap = oMapper.convertValue(template , Map.class); @@ -164,7 +161,7 @@ public String format(JSONObject valueMap, Map templateSettings) // TODO: Isn't template name becoming too complex with VC_CONTEXTS & CREDENTIAL_TYPES both? String templateName = templateSettings.get(TEMPLATE_NAME).toString(); String issuer = templateSettings.get(ISSUER_URI).toString(); - String template = templateCache.get(templateName).get("template"); + String template = templateCache.get(templateName).get("vcTemplate"); if (template == null) { log.error("Template {} not found", templateName); @@ -245,9 +242,9 @@ public String getTemplate(String key) { } String credentialType = key.split(DELIMITER)[0]; String context = key.split(DELIMITER, 2)[1]; - CredentialTemplate template = credentialTemplateRepository.findByCredentialTypeAndContext(credentialType, context).orElse(null); + CredentialConfig template = credentialConfigRepository.findByCredentialTypeAndContext(credentialType, context).orElse(null); if (template != null) { - return template.getTemplate(); + return template.getVcTemplate(); } else return null; } @@ -266,7 +263,7 @@ public String format(Map templateInput) { // TODO: Isn't template name becoming too complex with VC_CONTEXTS & CREDENTIAL_TYPES both? String templateName = templateInput.get(TEMPLATE_NAME).toString(); String issuer = templateInput.get(ISSUER_URI).toString(); - String t = templateCache.get(templateName).get("template"); + String t = templateCache.get(templateName).get("vcTemplate"); StringWriter writer = new StringWriter(); // 1. Prepare map // TODO: Eventually, the credentialSubject from the plugin will be templated as-is diff --git a/certify-service/src/main/resources/application-local.properties b/certify-service/src/main/resources/application-local.properties index 53c86526..9b852ec1 100644 --- a/certify-service/src/main/resources/application-local.properties +++ b/certify-service/src/main/resources/application-local.properties @@ -10,8 +10,7 @@ mosip.certify.security.ignore-csrf-urls=**/actuator/**,/favicon.ico,**/error,\ **/issuance/**,**/system-info/** mosip.certify.security.ignore-auth-urls=/actuator/**,**/error,**/swagger-ui/**,\ - **/v3/api-docs/**, **/issuance/**,/system-info/**,/public/**,/** - **/v3/api-docs/**, **/issuance/**,/system-info/**,/rendering-template/** + **/v3/api-docs/**, **/issuance/**,/system-info/**,/rendering-template/**,/credentials/** ## ------------------------------------------ Discovery openid-configuration ------------------------------------------- @@ -30,7 +29,12 @@ mosip.certify.supported.jwt-proof-alg={'RS256','PS256','ES256'} mosip.certify.identifier=http://localhost:8090 mosip.certify.authn.filter-urls={ '${server.servlet.path}/issuance/credential', '${server.servlet.path}/issuance/vd12/credential', '${server.servlet.path}/issuance/vd11/credential'} #mosip.certify.authn.filter-urls={} -m +mosip.certify.credential-config.issuer.display={\ + {\ + 'name': 'Farmer Issuer',\ + 'locale': 'en'\ + }\ +} 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' } @@ -354,4 +358,4 @@ spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=none spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true -spring.datasource.schema=./db_scripts/mosip_certify/ddl/certify-ca_cert_store.sql,./db_scripts/mosip_certify/ddl/certify-credential_template.sql,./db_scripts/mosip_certify/ddl/certify-key_alias.sql,./db_scripts/mosip_certify/ddl/certify-key_policy_def.sql,./db_scripts/mosip_certify/ddl/certify-key_store.sql,./db_scripts/mosip_certify/ddl/certify-rendering_template.sql,./db_scripts/mosip_certify/dml/ \ No newline at end of file +spring.datasource.schema=./db_scripts/mosip_certify/ddl/certify-ca_cert_store.sql,./db_scripts/mosip_certify/ddl/certify-credential_template.sql,./db_scripts/mosip_certify/ddl/certify-key_alias.sql,./db_scripts/mosip_certify/ddl/certify-key_policy_def.sql,./db_scripts/mosip_certify/ddl/certify-key_store.sql,./db_scripts/mosip_certify/ddl/certify-rendering_template.sql,./db_scripts/mosip_certify/ddl/certify-credential_config.sql,./db_scripts/mosip_certify/dml/ \ No newline at end of file diff --git a/certify-service/src/test/java/io/mosip/certify/controller/CredentialConfigControllerTest.java b/certify-service/src/test/java/io/mosip/certify/controller/CredentialConfigControllerTest.java new file mode 100644 index 00000000..3ace3d24 --- /dev/null +++ b/certify-service/src/test/java/io/mosip/certify/controller/CredentialConfigControllerTest.java @@ -0,0 +1,156 @@ +package io.mosip.certify.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.mosip.certify.core.dto.*; +import io.mosip.certify.core.spi.CredentialConfigurationService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.mockito.ArgumentMatchers.eq; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@RunWith(SpringRunner.class) +@WebMvcTest(value = CredentialConfigController.class) +public class CredentialConfigControllerTest { + ObjectMapper objectMapper = new ObjectMapper(); + + @Autowired + MockMvc mockMvc; + + @MockBean + ParsedAccessToken parsedAccessToken; + + @MockBean + CredentialConfigurationService credentialConfigurationService; + + @Mock + private CredentialConfigurationDTO credentialConfigurationDTO; + + @Before + public void setup() { + credentialConfigurationDTO = new CredentialConfigurationDTO(); + credentialConfigurationDTO.setVcTemplate("test_template"); + credentialConfigurationDTO.setContext(List.of("https://www.w3.org/2018/credentials/v1")); + credentialConfigurationDTO.setCredentialType(Arrays.asList("VerifiableCredential", "TestVerifiableCredential")); + credentialConfigurationDTO.setCredentialFormat("ldp_vc"); + credentialConfigurationDTO.setDidUrl("did:web:test.github.io:test-env:test-folder"); + credentialConfigurationDTO.setDisplay(List.of()); + credentialConfigurationDTO.setOrder(Arrays.asList("test1", "test2", "test3", "test4")); + credentialConfigurationDTO.setScope("test_vc_ldp"); + credentialConfigurationDTO.setCryptographicBindingMethodsSupported(List.of("did:jwk")); + credentialConfigurationDTO.setCredentialSigningAlgValuesSupported(List.of("Ed25519Signature2020")); + Map jwtValues = Map.of("proof_signing_alg_values_supported", Arrays.asList("RS256", "ES256")); + credentialConfigurationDTO.setProofTypesSupported(Map.of("jwt", jwtValues)); + Map pluginConfigMap = new HashMap<>(); + pluginConfigMap.put("mosip.certify.mock.data-provider.test-one", "valueOne"); + pluginConfigMap.put("mosip.certify.mock.data-provider.test-two", "valueTwo"); + pluginConfigMap.put("mosip.certify.mock.data-provider.test-three", "valueThree"); + credentialConfigurationDTO.setPluginConfigurations(List.of(pluginConfigMap)); + credentialConfigurationDTO.setCredentialSubject(Map.of("name", "Full Name")); + } + + @Test + public void addNewCredentialConfiguration_Success() throws Exception { + CredentialConfigResponse credentialConfigResponse = new CredentialConfigResponse(); + credentialConfigResponse.setId("farmer-credential-config-001"); + credentialConfigResponse.setStatus("active"); + Mockito.when(credentialConfigurationService.addCredentialConfiguration(credentialConfigurationDTO)).thenReturn(credentialConfigResponse); + + mockMvc.perform(post("/credentials/configurations") + .content(objectMapper.writeValueAsBytes(credentialConfigurationDTO)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.id").exists()) + .andExpect(jsonPath("$.status").exists()); + } + + @Test + public void getCredentialConfigurationById_Success() throws Exception { + + Mockito.when(credentialConfigurationService.getCredentialConfigurationById(Mockito.anyString())).thenReturn(credentialConfigurationDTO); + + mockMvc.perform(get("/credentials/configurations/1")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.vcTemplate").exists()) + .andExpect(jsonPath("$.context").exists()) + .andExpect(jsonPath("$.credentialType").exists()) + .andExpect(jsonPath("$.didUrl").exists()) + .andExpect(jsonPath("$.scope").exists()) + .andExpect(jsonPath("$.cryptographic_binding_methods_supported").exists()) + .andExpect(jsonPath("$.credential_signing_alg_values_supported").exists()) + .andExpect(jsonPath("$.proof_types_supported").exists()); + } + + @Test + public void updateExistingCredentialConfiguration_Success() throws Exception { + CredentialConfigResponse credentialConfigResponse = new CredentialConfigResponse(); + credentialConfigResponse.setId("farmer-credential-config-001"); + credentialConfigResponse.setStatus("active"); + Mockito.when(credentialConfigurationService.updateCredentialConfiguration(Mockito.anyString(), eq(credentialConfigurationDTO))).thenReturn(credentialConfigResponse); + + mockMvc.perform(put("/credentials/configurations/1") + .content(objectMapper.writeValueAsBytes(credentialConfigurationDTO)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").exists()) + .andExpect(jsonPath("$.status").exists()); + } + + @Test + public void deleteExistingCredentialConfiguration_Success() throws Exception { + String response = "Configuration deleted with id: 1"; + Mockito.when(credentialConfigurationService.deleteCredentialConfigurationById(Mockito.anyString())).thenReturn(response); + + mockMvc.perform(delete("/credentials/configurations/1")) + .andExpect(status().isOk()) + .andExpect(content().string(response)); + } + + @Test + public void getIssuerMetadata_noQueryParams_thenPass() throws Exception { + CredentialIssuerMetadataDTO credentialIssuerMetadata = new CredentialIssuerMetadataDTO(); + credentialIssuerMetadata.setCredentialIssuer("https://localhost:9090"); + credentialIssuerMetadata.setAuthorizationServers(List.of("https://example.com/auth")); + credentialIssuerMetadata.setCredentialEndpoint("https://localhost:9090/v1/certify/issuance/credential"); + Map display = new HashMap<>(); + display.put("name", "Test Credential Issuer"); + display.put("locale", "en"); + credentialIssuerMetadata.setDisplay(List.of(display)); + + CredentialConfigurationSupportedDTO credentialConfigurationSupported = new CredentialConfigurationSupportedDTO(); + credentialConfigurationSupported.setFormat("ldp_vc"); + credentialConfigurationSupported.setScope("test_vc_ldp"); + credentialConfigurationSupported.setCryptographicBindingMethodsSupported(List.of("did:jwk")); + credentialConfigurationSupported.setCredentialSigningAlgValuesSupported(List.of("Ed25519Signature2020")); + Map jwtValues = Map.of("proof_signing_alg_values_supported", Arrays.asList("RS256", "ES256")); + credentialConfigurationSupported.setProofTypesSupported(jwtValues); + credentialConfigurationSupported.setDisplay(List.of()); + credentialConfigurationSupported.setOrder(Arrays.asList("test1", "test2", "test3", "test4")); + credentialIssuerMetadata.setCredentialConfigurationSupportedDTO(Map.of("TestCredential_ldp", credentialConfigurationSupported)); + + Mockito.when(credentialConfigurationService.fetchCredentialIssuerMetadata(Mockito.anyString())).thenReturn(credentialIssuerMetadata); + + mockMvc.perform(get("/credentials/.well-known/openid-credential-issuer")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.credential_issuer").exists()) + .andExpect(jsonPath("$.credential_configurations_supported").exists()) + .andExpect(header().string("Content-Type", "application/json")); + + Mockito.verify(credentialConfigurationService).fetchCredentialIssuerMetadata("latest"); + } +} diff --git a/certify-service/src/test/java/io/mosip/certify/services/CredentialConfigurationServiceImplTest.java b/certify-service/src/test/java/io/mosip/certify/services/CredentialConfigurationServiceImplTest.java new file mode 100644 index 00000000..544672c7 --- /dev/null +++ b/certify-service/src/test/java/io/mosip/certify/services/CredentialConfigurationServiceImplTest.java @@ -0,0 +1,169 @@ +package io.mosip.certify.services; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.mosip.certify.core.dto.CredentialConfigResponse; +import io.mosip.certify.core.dto.CredentialConfigurationDTO; +import io.mosip.certify.core.exception.CertifyException; +import io.mosip.certify.entity.CredentialConfig; +import io.mosip.certify.mapper.CredentialConfigMapper; +import io.mosip.certify.repository.CredentialConfigRepository; +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.MockitoAnnotations; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.*; + +import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class CredentialConfigurationServiceImplTest { + + @Mock + private CredentialConfigRepository credentialConfigRepository; + + @Mock + private CredentialConfigMapper credentialConfigMapper; + + @InjectMocks + private CredentialConfigurationServiceImpl credentialConfigurationService; + + @Mock + private CredentialConfigurationDTO credentialConfigurationDTO; + + @Mock + private CredentialConfig credentialConfig; + + @Before + public void setup() { + MockitoAnnotations.openMocks(this); + credentialConfig = new CredentialConfig(); + String id = UUID.randomUUID().toString(); + credentialConfig.setConfigId(id); + credentialConfig.setStatus("active"); + credentialConfig.setVcTemplate("test_template"); + credentialConfig.setContext("https://www.w3.org/2018/credentials/v1"); + credentialConfig.setCredentialType("VerifiableCredential,TestVerifiableCredential"); + credentialConfig.setCredentialFormat("ldp_vc"); + credentialConfig.setDidUrl("did:web:test.github.io:test-env:test-folder"); + credentialConfig.setOrder(Arrays.asList("test1", "test2", "test3", "test4")); + credentialConfig.setScope("test_vc_ldp"); + credentialConfig.setCryptographicBindingMethodsSupported(List.of("did:jwk")); + credentialConfig.setCredentialSigningAlgValuesSupported(List.of("Ed25519Signature2020")); + credentialConfig.setCredentialSubject(Map.of("name", "Full Name")); + credentialConfig.setKeyManagerAppId("TEST2019"); + + credentialConfigurationDTO = new CredentialConfigurationDTO(); + credentialConfigurationDTO.setDisplay(List.of()); + credentialConfigurationDTO.setVcTemplate("test_template"); + credentialConfigurationDTO.setCredentialFormat("test_vc"); + credentialConfigurationDTO.setContext(List.of("https://www.w3.org/2018/credentials/v1")); + credentialConfigurationDTO.setCredentialType(Arrays.asList("VerifiableCredential", "TestVerifiableCredential")); + credentialConfigurationDTO.setCredentialSubject(Map.of("name", "Full Name")); + + ReflectionTestUtils.setField(credentialConfigurationService, "credentialIssuer", "http://example.com"); + ReflectionTestUtils.setField(credentialConfigurationService, "authServers", List.of("http://auth.com")); + ReflectionTestUtils.setField(credentialConfigurationService, "servletPath", "v1/test"); + ReflectionTestUtils.setField(credentialConfigurationService, "pluginMode", "DataProvider"); + ReflectionTestUtils.setField(credentialConfigurationService, "issuerDisplay", List.of(Map.of())); + } + + @Test + public void addNewCredentialConfig_Success() throws JsonProcessingException { + when(credentialConfigMapper.toEntity(any(CredentialConfigurationDTO.class))).thenReturn(credentialConfig); + when(credentialConfigRepository.save(any(CredentialConfig.class))).thenReturn(credentialConfig); + + CredentialConfigResponse credentialConfigResponse = credentialConfigurationService.addCredentialConfiguration(credentialConfigurationDTO); + + Assert.assertNotNull(credentialConfigResponse); + Assert.assertNotNull(credentialConfigResponse.getId()); + Assert.assertNotNull(credentialConfigResponse.getStatus()); + Assert.assertEquals("active", credentialConfigResponse.getStatus()); + } + + @Test + public void getCredentialConfigById_Success() throws JsonProcessingException { + Optional optional = Optional.of(credentialConfig); + when(credentialConfigRepository.findByConfigId(anyString())).thenReturn(optional); + when(credentialConfigMapper.toDto(any(CredentialConfig.class))).thenReturn(credentialConfigurationDTO); + CredentialConfigurationDTO credentialConfigurationDTOResponse = credentialConfigurationService.getCredentialConfigurationById("test"); + + Assert.assertNotNull(credentialConfigurationDTOResponse); + Assert.assertNotNull(credentialConfigurationDTOResponse.getCredentialType()); + Assert.assertNotNull(credentialConfigurationDTOResponse.getCredentialFormat()); + Assert.assertNotNull(credentialConfigurationDTOResponse.getContext()); + Assert.assertNotNull(credentialConfigurationDTOResponse.getVcTemplate()); + Assert.assertEquals("test_template", credentialConfigurationDTOResponse.getVcTemplate()); + Assert.assertEquals(List.of("https://www.w3.org/2018/credentials/v1"), credentialConfigurationDTOResponse.getContext()); + Assert.assertEquals(Arrays.asList("VerifiableCredential", "TestVerifiableCredential"), credentialConfigurationDTOResponse.getCredentialType()); + Assert.assertEquals("test_vc", credentialConfigurationDTOResponse.getCredentialFormat()); + } + + @Test + public void getCredentialConfigurationById_ConfigNotFound() { + when(credentialConfigRepository.findByConfigId("12345678")) + .thenReturn(Optional.empty()); + + CertifyException exception = assertThrows(CertifyException.class, () -> + credentialConfigurationService.getCredentialConfigurationById("12345678")); + + assertEquals("Configuration not found with the provided id: " + "12345678", exception.getMessage()); + } + + @Test + public void updateExistingCredentialConfig_Success() throws JsonProcessingException { + Optional optional = Optional.of(credentialConfig); + when(credentialConfigRepository.findByConfigId(anyString())).thenReturn(optional); + doNothing().when(credentialConfigMapper).updateEntityFromDto(any(CredentialConfigurationDTO.class), any(CredentialConfig.class)); + + CredentialConfigResponse credentialConfigResponse = credentialConfigurationService.updateCredentialConfiguration("12345678", credentialConfigurationDTO); + + Assert.assertNotNull(credentialConfigResponse); + Assert.assertNotNull(credentialConfigResponse.getId()); + Assert.assertNotNull(credentialConfigResponse.getStatus()); + Assert.assertEquals("active", credentialConfigResponse.getStatus()); + } + + @Test + public void updateExistingCredentialConfiguration_ConfigNotFound() { + when(credentialConfigRepository.findByConfigId(anyString())) + .thenReturn(Optional.empty()); + + CertifyException exception = assertThrows(CertifyException.class, () -> + credentialConfigurationService.updateCredentialConfiguration("12345678", new CredentialConfigurationDTO())); + + assertEquals("Configuration not found with the provided id: " + "12345678", exception.getMessage()); + } + + @Test + public void deleteCredentialConfig_Success() throws JsonProcessingException { + Optional optional = Optional.of(credentialConfig); + when(credentialConfigRepository.findByConfigId(anyString())).thenReturn(optional); + doNothing().when(credentialConfigRepository).delete(any(CredentialConfig.class)); + + String result = credentialConfigurationService.deleteCredentialConfigurationById("12345678"); + + Assert.assertNotNull(result); + assertEquals("Configuration deleted with id: 12345678", result); + } + + @Test + public void deleteCredentialConfiguration_ConfigNotFound() { + when(credentialConfigRepository.findByConfigId(anyString())) + .thenReturn(Optional.empty()); + + CertifyException exception = assertThrows(CertifyException.class, () -> + credentialConfigurationService.deleteCredentialConfigurationById("12345678")); + + assertEquals("Configuration not found with the provided id: 12345678", exception.getMessage()); + } +} diff --git a/certify-service/src/test/java/io/mosip/certify/vcformatters/VelocityTemplatingEngineImplTest.java b/certify-service/src/test/java/io/mosip/certify/vcformatters/VelocityTemplatingEngineImplTest.java index 3ca26582..2d60a668 100644 --- a/certify-service/src/test/java/io/mosip/certify/vcformatters/VelocityTemplatingEngineImplTest.java +++ b/certify-service/src/test/java/io/mosip/certify/vcformatters/VelocityTemplatingEngineImplTest.java @@ -21,10 +21,8 @@ import org.springframework.web.client.RestTemplate; import io.mosip.certify.core.exception.CertifyException; -import io.mosip.certify.entity.CredentialTemplate; -import io.mosip.certify.repository.CredentialTemplateRepository; -import org.json.JSONException; -import org.json.JSONObject; +import io.mosip.certify.entity.CredentialConfig; +import io.mosip.certify.repository.CredentialConfigRepository; import junit.framework.TestCase; @@ -39,12 +37,12 @@ public class VelocityTemplatingEngineImplTest extends TestCase { @InjectMocks private VelocityTemplatingEngineImpl formatter; @Mock - CredentialTemplateRepository credentialTemplateRepository; + CredentialConfigRepository credentialConfigRepository; - private CredentialTemplate vc1; - private CredentialTemplate vc2; - private CredentialTemplate vc3; - private CredentialTemplate vc4; + private CredentialConfig vc1; + private CredentialConfig vc2; + private CredentialConfig vc3; + private CredentialConfig vc4; @SneakyThrows @Before @@ -134,7 +132,7 @@ public void setUp() { "https://vharsh.github.io/DID/mock-context.json,https://www.w3.org/2018/credentials/v1" ); //when(templateRepository.findByCredentialTypeAndContext("MockVerifiableCredential,VerifiableCredential", "https://schema.org,https://www.w3.org/2018/credentials/v1")).thenReturn(Optional.of(vc1)); - when(credentialTemplateRepository.findByCredentialTypeAndContext("MockVerifiableCredential,VerifiableCredential", "https://example.org/Person.json,https://www.w3.org/ns/credentials/v2")).thenReturn(Optional.of(vc2)); + when(credentialConfigRepository.findByCredentialTypeAndContext("MockVerifiableCredential,VerifiableCredential", "https://example.org/Person.json,https://www.w3.org/ns/credentials/v2")).thenReturn(Optional.of(vc2)); //when(templateRepository.findByCredentialTypeAndContext("MockVerifiableCredential,VerifiableCredential", "https://vharsh.github.io/DID/mock-context.json,https://www.w3.org/2018/credentials/v1")).thenReturn(Optional.of(vc3)); formatter.initialize(); // engine = new VelocityEngine(); @@ -146,9 +144,9 @@ public void setUp() { // engine.init(); } - private CredentialTemplate initTemplate(String template, String type, String context, String format, String didUrl, String keyManagerAppId, String keyManagerRefId, String signatureAlgo, String sdClaim) { - CredentialTemplate t = new CredentialTemplate(); - t.setTemplate(template); + private CredentialConfig initTemplate(String template, String type, String context, String format, String didUrl, String keyManagerAppId, String keyManagerRefId, String signatureAlgo, String sdClaim) { + CredentialConfig t = new CredentialConfig(); + t.setVcTemplate(template); t.setCredentialType(type); t.setContext(context); t.setCredentialFormat(format); @@ -161,9 +159,9 @@ private CredentialTemplate initTemplate(String template, String type, String con } - private CredentialTemplate initTemplate(String template, String type, String context) { - CredentialTemplate t = new CredentialTemplate(); - t.setTemplate(template); + private CredentialConfig initTemplate(String template, String type, String context) { + CredentialConfig t = new CredentialConfig(); + t.setVcTemplate(template); t.setCredentialType(type); t.setContext(context); return t; @@ -252,7 +250,7 @@ public void getTemplateNameWithValidKey_thenPass() { String key = "MockVerifiableCredential,VerifiableCredential:https://example.org/Person.json,https://www.w3.org/ns/credentials/v2"; String template = formatter.getTemplate(key); Assert.assertNotNull(template); - Assert.assertEquals(vc2.getTemplate(), template); + Assert.assertEquals(vc2.getVcTemplate(), template); } @Test diff --git a/certify-service/src/test/resources/application-test.properties b/certify-service/src/test/resources/application-test.properties index ceb09493..98cf0727 100644 --- a/certify-service/src/test/resources/application-test.properties +++ b/certify-service/src/test/resources/application-test.properties @@ -14,6 +14,7 @@ mosip.certify.data-provider-plugin.issuer.vc-sign-algo=Ed25519Signature2018 ## ------------------------------------------ Discovery openid-configuration ------------------------------------------- mosipbox.public.url=http://localhost:8090 +mosip.certify.authorization.url=http://localhost:8088 mosip.certify.discovery.issuer-id=${mosipbox.public.url}${server.servlet.path} mosip.certify.data-provider-plugin.issuer-public-key-uri=http://localhost/pub.key.json mosip.certify.data-provider-plugin.issuer-uri=http://localhost/pub.key.json @@ -30,6 +31,12 @@ 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.credential-config.issuer.display={\ + {\ + 'name': 'Test Issuer',\ + 'locale': 'en'\ + }\ +} mosip.certify.supported.jwt-proof-alg={'RS256'} mosip.certify.key-values={\ diff --git a/certify-service/src/test/resources/schema.sql b/certify-service/src/test/resources/schema.sql index 0015a614..692fe885 100644 --- a/certify-service/src/test/resources/schema.sql +++ b/certify-service/src/test/resources/schema.sql @@ -77,17 +77,30 @@ CREATE TABLE IF NOT EXISTS rendering_template ( CONSTRAINT pk_rendertmp_id PRIMARY KEY (id) ); -CREATE TABLE IF NOT EXISTS credential_template( - context character varying(1024) NOT NULL, - credential_type character varying(512) NOT NULL, - template VARCHAR NOT NULL, - credential_format character varying(1024), - did_url VARCHAR, - key_manager_app_id character varying(36) NOT NULL, - key_manager_ref_id character varying(128), - signature_algo character(2048), - sd_claim VARCHAR, - cr_dtimes TIMESTAMP DEFAULT NOW() NOT NULL, +-- Changed all `JSONB` and `TEXT[]` types to VARCHAR to make it work with H2 database +CREATE TABLE IF NOT EXISTS credential_config ( + config_id VARCHAR(255), + status VARCHAR(255), + vc_template VARCHAR(1000), + doctype VARCHAR(255), + context VARCHAR(255) NOT NULL, + credential_type VARCHAR(255) NOT NULL, + credential_format VARCHAR(255) NOT NULL, + did_url VARCHAR(255) NOT NULL, + key_manager_app_id VARCHAR(36) NOT NULL, + key_manager_ref_id VARCHAR(128), + signature_algo VARCHAR(255), + sd_claim VARCHAR(255), + display VARCHAR NOT NULL, + display_order VARCHAR NOT NULL, + scope VARCHAR(255) NOT NULL, + cryptographic_binding_methods_supported VARCHAR NOT NULL, + credential_signing_alg_values_supported VARCHAR NOT NULL, + proof_types_supported VARCHAR NOT NULL, + credential_subject VARCHAR, + claims VARCHAR, + plugin_configurations VARCHAR, + cr_dtimes TIMESTAMP NOT NULL, upd_dtimes TIMESTAMP, - CONSTRAINT pk_template PRIMARY KEY (context, credential_type, credential_format) + CONSTRAINT pk_config_id PRIMARY KEY (context, credential_type, credential_format) ); diff --git a/db_scripts/mosip_certify/combined.sql b/db_scripts/mosip_certify/combined.sql index e68dd988..b194dedf 100644 --- a/db_scripts/mosip_certify/combined.sql +++ b/db_scripts/mosip_certify/combined.sql @@ -22,21 +22,6 @@ CREATE TABLE ca_cert_store( CONSTRAINT cert_thumbprint_unique UNIQUE (cert_thumbprint,partner_domain) ); -Drop table if exists credential_template; -CREATE TABLE IF NOT EXISTS credential_template( - context character varying(1024) NOT NULL, - credential_type character varying(512) NOT NULL, - template VARCHAR NOT NULL, - credential_format character varying(1024), - did_url VARCHAR, - key_manager_app_id character varying(36) NOT NULL, - key_manager_ref_id character varying(128), - signature_algo character(2048), - sd_claim VARCHAR, - cr_dtimes timestamp NOT NULL default now(), - upd_dtimes timestamp, - CONSTRAINT pk_template PRIMARY KEY (context, credential_type, credential_format) -); Drop table if exists key_alias; CREATE TABLE key_alias( id character varying(36) NOT NULL, @@ -94,6 +79,33 @@ CREATE TABLE rendering_template ( upd_dtimes timestamp, CONSTRAINT pk_rendertmp_id PRIMARY KEY (id) ); +DROP TABLE IF EXISTS credential_config CASCADE CONSTRAINTS; +CREATE TABLE credential_config ( + config_id VARCHAR(255), + status VARCHAR(255), + vc_template VARCHAR, + doctype VARCHAR, + context VARCHAR NOT NULL, + credential_type VARCHAR NOT NULL, + credential_format VARCHAR(255) NOT NULL, + did_url VARCHAR NOT NULL, + key_manager_app_id VARCHAR(36) NOT NULL, + key_manager_ref_id VARCHAR(128), + signature_algo VARCHAR(36), + sd_claim VARCHAR, + display JSONB NOT NULL, + display_order TEXT[] NOT NULL, + scope VARCHAR(255) NOT NULL, + cryptographic_binding_methods_supported TEXT[] NOT NULL, + credential_signing_alg_values_supported TEXT[] NOT NULL, + proof_types_supported JSONB NOT NULL, + credential_subject JSONB, + claims JSONB, + plugin_configurations JSONB, + cr_dtimes TIMESTAMP NOT NULL, + upd_dtimes TIMESTAMP, + CONSTRAINT pk_config_id PRIMARY KEY (context, credential_type, credential_format) +); INSERT INTO key_policy_def (app_id, key_validity_duration, pre_expire_days, access_allowed, is_active, cr_by, cr_dtimes) SELECT app_id, key_validity_duration, pre_expire_days, access_allowed, is_active, cr_by, CURRENT_TIMESTAMP FROM CSVREAD('./db_scripts/mosip_certify/dml/certify-key_policy_def.csv'); \ No newline at end of file diff --git a/db_scripts/mosip_certify/ddl.sql b/db_scripts/mosip_certify/ddl.sql index 6506c911..d98162c3 100644 --- a/db_scripts/mosip_certify/ddl.sql +++ b/db_scripts/mosip_certify/ddl.sql @@ -5,6 +5,5 @@ \ir ddl/certify-key_store.sql \ir ddl/certify-ca_cert_store.sql \ir ddl/certify-rendering_template.sql -\ir ddl/certify-credential_template.sql - +\ir ddl/certify-credential_config.sql diff --git a/db_scripts/mosip_certify/ddl/certify-credential_config.sql b/db_scripts/mosip_certify/ddl/certify-credential_config.sql new file mode 100644 index 00000000..6cbe200e --- /dev/null +++ b/db_scripts/mosip_certify/ddl/certify-credential_config.sql @@ -0,0 +1,65 @@ +-- 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/. +-- ------------------------------------------------------------------------------------------------- +-- Database Name: inji_certify +-- Table Name : credential_config +-- Purpose : Credential Configuration Table +-- +-- +-- Modified Date Modified By Comments / Remarks +-- ------------------------------------------------------------------------------------------ +-- ------------------------------------------------------------------------------------------ + +CREATE TABLE credential_config ( + config_id VARCHAR(255), + status VARCHAR(255), + vc_template VARCHAR, + doctype VARCHAR, + context VARCHAR NOT NULL, + credential_type VARCHAR NOT NULL, + credential_format VARCHAR(255) NOT NULL, + did_url VARCHAR NOT NULL, + key_manager_app_id VARCHAR(36) NOT NULL, + key_manager_ref_id VARCHAR(128), + signature_algo VARCHAR(36), + sd_claim VARCHAR, + display JSONB NOT NULL, + display_order TEXT[] NOT NULL, + scope VARCHAR(255) NOT NULL, + cryptographic_binding_methods_supported TEXT[] NOT NULL, + credential_signing_alg_values_supported TEXT[] NOT NULL, + proof_types_supported JSONB NOT NULL, + credential_subject JSONB, + claims JSONB, + plugin_configurations JSONB, + cr_dtimes TIMESTAMP NOT NULL, + upd_dtimes TIMESTAMP, + CONSTRAINT pk_config_id PRIMARY KEY (context, credential_type, credential_format) +); + +COMMENT ON TABLE credential_config IS 'Credential Config: Contains details of credential configuration.'; + +COMMENT ON COLUMN credential_config.id IS 'Credential Config ID: Unique id assigned to save and identify configuration.'; +COMMENT ON COLUMN credential_config.status IS 'Credential Config Status: Status of the credential configuration.'; +COMMENT ON COLUMN credential_config.vc_template IS 'VC Template: Template used for the verifiable credential.'; +COMMENT ON COLUMN credential_config.doctype IS 'Doc Type: Doc Type specifically for Mdoc VC.'; +COMMENT ON COLUMN credential_config.context IS 'Context: Array of context URIs for the credential.'; +COMMENT ON COLUMN credential_config.credential_type IS 'Credential Type: Array of credential types supported.'; +COMMENT ON COLUMN credential_config.credential_format IS 'Credential Format: Format of the credential (e.g., JWT, JSON-LD).'; +COMMENT ON COLUMN credential_config.did_url IS 'DID URL: Decentralized Identifier URL for the issuer.'; +COMMENT ON COLUMN credential_config.key_manager_app_id IS 'Key Manager App Id: AppId of the keymanager'; +COMMENT ON COLUMN credential_config.key_manager_ref_id IS 'Key Manager Reference Id: RefId of the keymanager'; +COMMENT ON COLUMN credential_config.signature_algo IS 'Signature Algorithm: This is for VC signature or proof algorithm'; +COMMENT ON COLUMN credential_config.sd_claim IS 'SD Claim: This is a comma separated list for selective disclosure'; +COMMENT ON COLUMN credential_config.display IS 'Display: Credential Display object'; +COMMENT ON COLUMN credential_config.display_order IS 'Display Order: Array defining the order of display elements.'; +COMMENT ON COLUMN credential_config.scope IS 'Scope: Authorization scope for the credential.'; +COMMENT ON COLUMN credential_config.cryptographic_binding_methods_supported IS 'Cryptographic Binding Methods: Array of supported binding methods.'; +COMMENT ON COLUMN credential_config.credential_signing_alg_values_supported IS 'Credential Signing Algorithms: Array of supported signing algorithms.'; +COMMENT ON COLUMN credential_config.proof_types_supported IS 'Proof Types: JSON object containing supported proof types and their configurations.'; +COMMENT ON COLUMN credential_config.credential_subject IS 'Credential Subject: JSON object containing subject attributes schema.'; +COMMENT ON COLUMN credential_config.claims IS 'Claims: JSON object containing subject attributes schema specifically for Mdoc VC.'; +COMMENT ON COLUMN credential_config.plugin_configurations IS 'Plugin Configurations: Array of JSON objects for plugin configurations.'; +COMMENT ON COLUMN credential_config.cr_dtimes IS 'Created DateTime: Date and time when the config was inserted in table.'; +COMMENT ON COLUMN credential_config.upd_dtimes IS 'Updated DateTime: Date and time when the config was last updated in table.'; diff --git a/db_scripts/mosip_certify/ddl/certify-credential_template.sql b/db_scripts/mosip_certify/ddl/certify-credential_template.sql deleted file mode 100644 index 748989c0..00000000 --- a/db_scripts/mosip_certify/ddl/certify-credential_template.sql +++ /dev/null @@ -1,42 +0,0 @@ --- 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/. --- ------------------------------------------------------------------------------------------------- --- Database Name: inji_certify --- Table Name : credential_template --- Purpose : Template Data table --- --- --- Modified Date Modified By Comments / Remarks --- ------------------------------------------------------------------------------------------ --- 6/1/2025 Sasi Enhance to support multiple formats --- ------------------------------------------------------------------------------------------ - -CREATE TABLE IF NOT EXISTS credential_template( - context character varying(1024) NOT NULL, - credential_type character varying(512) NOT NULL, - template VARCHAR NOT NULL, - credential_format character varying(1024), - did_url VARCHAR, - key_manager_app_id character varying(36) NOT NULL, - key_manager_ref_id character varying(128), - signature_algo character(2048), - sd_claim VARCHAR, - cr_dtimes timestamp NOT NULL default now(), - upd_dtimes timestamp, - CONSTRAINT pk_template PRIMARY KEY (context, credential_type, credential_format) -); - -COMMENT ON TABLE credential_template IS 'Template Data: Contains velocity template for VC'; - -COMMENT ON COLUMN credential_template.context IS 'VC Context: Context URL list items separated by comma(,)'; -COMMENT ON COLUMN credential_template.credential_type IS 'Credential Type: Credential type list items separated by comma(,)'; -COMMENT ON COLUMN credential_template.template IS 'Template Content: Velocity Template to generate the VC'; -COMMENT ON COLUMN credential_template.cr_dtimes IS 'Date when the template was inserted in table.'; -COMMENT ON COLUMN credential_template.upd_dtimes IS 'Date when the template was last updated in table.'; -COMMENT ON COLUMN credential_template.credential_format IS ''; -COMMENT ON COLUMN credential_template.did_url IS 'URL for the public key. Should point to the exact key. Supports DID document or public key'; -COMMENT ON COLUMN credential_template.key_manager_app_id IS 'AppId of the keymanager'; -COMMENT ON COLUMN credential_template.key_manager_ref_id IS 'RefId of the keymanager'; -COMMENT ON COLUMN credential_template.signature_algo IS 'This for VC signature or proof algorithm'; -COMMENT ON COLUMN credential_template.sd_claim IS 'This is a comma seperated list for selective disclosure'; diff --git a/db_scripts/mosip_certify/ddl/combined.sql b/db_scripts/mosip_certify/ddl/combined.sql index cc103490..9bafa511 100644 --- a/db_scripts/mosip_certify/ddl/combined.sql +++ b/db_scripts/mosip_certify/ddl/combined.sql @@ -47,28 +47,40 @@ CREATE TABLE IF NOT EXISTS key_store( del_dtimes timestamp, CONSTRAINT pk_keystr_id PRIMARY KEY (id) ); -DROP TABLE IF EXISTS svg_template CASCADE CONSTRAINTS; -CREATE TABLE IF NOT EXISTS svg_template ( +DROP TABLE IF EXISTS rendering_template CASCADE CONSTRAINTS; +CREATE TABLE IF NOT EXISTS rendering_template ( id UUID NOT NULL, template VARCHAR NOT NULL, cr_dtimes timestamp NOT NULL, upd_dtimes timestamp, CONSTRAINT pk_svgtmp_id PRIMARY KEY (id) ); -DROP TABLE IF EXISTS template_data CASCADE CONSTRAINTS; -CREATE TABLE IF NOT EXISTS template_data( - context character varying(1024) NOT NULL, - credential_type character varying(512) NOT NULL, - template VARCHAR NOT NULL, - credential_format character varying(1024), - did_url VARCHAR, - key_manager_app_id character varying(36) NOT NULL, - key_manager_ref_id character varying(128), - signature_algo character(2048), - sd_claim VARCHAR, - cr_dtimes timestamp NOT NULL default now(), - upd_dtimes timestamp, - CONSTRAINT pk_template PRIMARY KEY (context, credential_type, credential_format) + +CREATE TABLE credential_config ( + config_id VARCHAR(255), + status VARCHAR(255), + vc_template VARCHAR, + doctype VARCHAR, + context VARCHAR NOT NULL, + credential_type VARCHAR NOT NULL, + credential_format VARCHAR(255) NOT NULL, + did_url VARCHAR NOT NULL, + key_manager_app_id VARCHAR(36) NOT NULL, + key_manager_ref_id VARCHAR(128), + signature_algo VARCHAR(36), + sd_claim VARCHAR, + display JSONB NOT NULL, + display_order TEXT[] NOT NULL, + scope VARCHAR(255) NOT NULL, + cryptographic_binding_methods_supported TEXT[] NOT NULL, + credential_signing_alg_values_supported TEXT[] NOT NULL, + proof_types_supported JSONB NOT NULL, + credential_subject JSONB, + claims JSONB, + plugin_configurations JSONB, + cr_dtimes TIMESTAMP NOT NULL, + upd_dtimes TIMESTAMP, + CONSTRAINT pk_config_id PRIMARY KEY (context, credential_type, credential_format) ); diff --git a/db_upgrade_script/mosip_certify/sql/0.11.0_to_0.12.0_rollback.sql b/db_upgrade_script/mosip_certify/sql/0.11.0_to_0.12.0_rollback.sql new file mode 100644 index 00000000..4f5d155b --- /dev/null +++ b/db_upgrade_script/mosip_certify/sql/0.11.0_to_0.12.0_rollback.sql @@ -0,0 +1,61 @@ +-- 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/. +-- ------------------------------------------------------------------------------------------------- +-- Database Name: inji_certify +-- Table Name : credential_config, credential_template +-- Purpose : To remove Certify v0.12.0 changes and make DB ready for Certify v0.11.0 +-- +-- Create By : Piyush Shukla +-- Created Date : March 2025 +-- +-- Modified Date Modified By Comments / Remarks +-- ------------------------------------------------------------------------------------------ +-- ------------------------------------------------------------------------------------------ + +-- Step 1: Drop the new primary key constraint +ALTER TABLE credential_config DROP CONSTRAINT pk_config_id; + +-- Step 2: Drop all the newly added columns +ALTER TABLE credential_config + DROP COLUMN config_id, + DROP COLUMN status, + DROP COLUMN doctype, + DROP COLUMN credential_format, + DROP COLUMN did_url, + DROP COLUMN key_manager_app_id, + DROP COLUMN key_manager_ref_id, + DROP COLUMN signature_algo, + DROP COLUMN sd_claim, + DROP COLUMN display, + DROP COLUMN display_order, + DROP COLUMN scope, + DROP COLUMN cryptographic_binding_methods_supported, + DROP COLUMN credential_signing_alg_values_supported, + DROP COLUMN proof_types_supported, + DROP COLUMN credential_subject, + DROP COLUMN claims, + DROP COLUMN plugin_configurations; + +-- Step 3: Rename vc_template back to template +ALTER TABLE credential_config RENAME COLUMN vc_template TO template; + +-- Step 4: Restore the column types to original specifications +ALTER TABLE credential_config + ALTER COLUMN context TYPE character varying(1024), + ALTER COLUMN credential_type TYPE character varying(512), + ALTER COLUMN template TYPE VARCHAR; + +-- Step 5: Add back the original primary key constraint +ALTER TABLE credential_config ADD CONSTRAINT pk_template PRIMARY KEY (context, credential_type); + +-- Step 6: Rename the table back to its original name +ALTER TABLE credential_config RENAME TO credential_template; + +COMMENT ON TABLE credential_template IS 'Template Data: Contains velocity template for VC'; + +COMMENT ON COLUMN credential_template.context IS 'VC Context: Context URL list items separated by comma(,)'; +COMMENT ON COLUMN credential_template.credential_type IS 'Credential Type: Credential type list items separated by comma(,)'; +COMMENT ON COLUMN credential_template.template IS 'Template Content: Velocity Template to generate the VC'; +COMMENT ON COLUMN credential_template.cr_dtimes IS 'Date when the template was inserted in table.'; +COMMENT ON COLUMN credential_template.upd_dtimes IS 'Date when the template was last updated in table.'; \ No newline at end of file diff --git a/db_upgrade_script/mosip_certify/sql/0.11.0_to_0.12.0_upgrade.sql b/db_upgrade_script/mosip_certify/sql/0.11.0_to_0.12.0_upgrade.sql new file mode 100644 index 00000000..3a92b109 --- /dev/null +++ b/db_upgrade_script/mosip_certify/sql/0.11.0_to_0.12.0_upgrade.sql @@ -0,0 +1,76 @@ +-- 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/. +-- ------------------------------------------------------------------------------------------------- +-- Database Name: inji_certify +-- Table Name : rendering_template,credential_template, ca_cert_store +-- Purpose : To upgrade Certify v0.11.0 changes and make it compatible with v0.12.0 +-- +-- Create By : Piyush Shukla +-- Created Date : January-2025 +-- +-- Modified Date Modified By Comments / Remarks +-- ------------------------------------------------------------------------------------------ +-- ------------------------------------------------------------------------------------------ + +-- Step 1: Rename the table +ALTER TABLE credential_template RENAME TO credential_config; + +-- Step 2: Add new columns +ALTER TABLE credential_config + ADD COLUMN config_id VARCHAR(255), + ADD COLUMN status VARCHAR(255), + ADD COLUMN doctype VARCHAR, + ADD COLUMN credential_format VARCHAR(255) NOT NULL DEFAULT 'default_format', -- Adding a default value for NOT NULL constraint + ADD COLUMN did_url VARCHAR NOT NULL DEFAULT '', -- Adding a default value for NOT NULL constraint + ADD COLUMN key_manager_app_id VARCHAR(36) NOT NULL DEFAULT '', -- Adding a default value for NOT NULL constraint + ADD COLUMN key_manager_ref_id VARCHAR(128), + ADD COLUMN signature_algo VARCHAR(36), + ADD COLUMN sd_claim VARCHAR, + ADD COLUMN display JSONB NOT NULL DEFAULT '{}'::jsonb, -- Adding a default value for NOT NULL constraint + ADD COLUMN display_order TEXT[] NOT NULL DEFAULT '{}', -- Adding a default value for NOT NULL constraint + ADD COLUMN scope VARCHAR(255) NOT NULL DEFAULT '', -- Adding a default value for NOT NULL constraint + ADD COLUMN cryptographic_binding_methods_supported TEXT[] NOT NULL DEFAULT '{}', -- Adding a default value for NOT NULL constraint + ADD COLUMN credential_signing_alg_values_supported TEXT[] NOT NULL DEFAULT '{}', -- Adding a default value for NOT NULL constraint + ADD COLUMN proof_types_supported JSONB NOT NULL DEFAULT '{}'::jsonb, -- Adding a default value for NOT NULL constraint + ADD COLUMN credential_subject JSONB, + ADD COLUMN claims JSONB, + ADD COLUMN plugin_configurations JSONB; + +-- Step 3: Rename the template column to match the new schema +ALTER TABLE credential_config RENAME COLUMN template TO vc_template; + +-- Step 4: Alter column sizes to match the new schema +ALTER TABLE credential_config + ALTER COLUMN context TYPE VARCHAR, + ALTER COLUMN credential_type TYPE VARCHAR; + +-- Step 5: Update the primary key constraint +ALTER TABLE credential_config DROP CONSTRAINT pk_template; +ALTER TABLE credential_config ADD CONSTRAINT pk_config_id PRIMARY KEY (context, credential_type, credential_format); + +COMMENT ON TABLE credential_config IS 'Credential Config: Contains details of credential configuration.'; + +COMMENT ON COLUMN credential_config.id IS 'Credential Config ID: Unique id assigned to save and identify configuration.'; +COMMENT ON COLUMN credential_config.status IS 'Credential Config Status: Status of the credential configuration.'; +COMMENT ON COLUMN credential_config.vc_template IS 'VC Template: Template used for the verifiable credential.'; +COMMENT ON COLUMN credential_config.doctype IS 'Doc Type: Doc Type specifically for Mdoc VC.'; +COMMENT ON COLUMN credential_config.context IS 'Context: Array of context URIs for the credential.'; +COMMENT ON COLUMN credential_config.credential_type IS 'Credential Type: Array of credential types supported.'; +COMMENT ON COLUMN credential_config.credential_format IS 'Credential Format: Format of the credential (e.g., JWT, JSON-LD).'; +COMMENT ON COLUMN credential_config.did_url IS 'DID URL: Decentralized Identifier URL for the issuer.'; +COMMENT ON COLUMN credential_config.key_manager_app_id IS 'Key Manager App Id: AppId of the keymanager'; +COMMENT ON COLUMN credential_config.key_manager_ref_id IS 'Key Manager Reference Id: RefId of the keymanager'; +COMMENT ON COLUMN credential_config.signature_algo IS 'Signature Algorithm: This is for VC signature or proof algorithm'; +COMMENT ON COLUMN credential_config.sd_claim IS 'SD Claim: This is a comma separated list for selective disclosure'; +COMMENT ON COLUMN credential_config.display IS 'Display: Credential Display object'; +COMMENT ON COLUMN credential_config.display_order IS 'Display Order: Array defining the order of display elements.'; +COMMENT ON COLUMN credential_config.scope IS 'Scope: Authorization scope for the credential.'; +COMMENT ON COLUMN credential_config.cryptographic_binding_methods_supported IS 'Cryptographic Binding Methods: Array of supported binding methods.'; +COMMENT ON COLUMN credential_config.credential_signing_alg_values_supported IS 'Credential Signing Algorithms: Array of supported signing algorithms.'; +COMMENT ON COLUMN credential_config.proof_types_supported IS 'Proof Types: JSON object containing supported proof types and their configurations.'; +COMMENT ON COLUMN credential_config.credential_subject IS 'Credential Subject: JSON object containing subject attributes schema.'; +COMMENT ON COLUMN credential_config.claims IS 'Claims: JSON object containing subject attributes schema specifically for Mdoc VC.'; +COMMENT ON COLUMN credential_config.plugin_configurations IS 'Plugin Configurations: Array of JSON objects for plugin configurations.'; +COMMENT ON COLUMN credential_config.cr_dtimes IS 'Created DateTime: Date and time when the config was inserted in table.'; +COMMENT ON COLUMN credential_config.upd_dtimes IS 'Updated DateTime: Date and time when the config was last updated in table.'; diff --git a/docker-compose/docker-compose-injistack/certify_init.sql b/docker-compose/docker-compose-injistack/certify_init.sql index 14536e43..c632adfc 100644 --- a/docker-compose/docker-compose-injistack/certify_init.sql +++ b/docker-compose/docker-compose-injistack/certify_init.sql @@ -97,119 +97,196 @@ CREATE TABLE certify.rendering_template ( CONSTRAINT pk_svgtmp_id PRIMARY KEY (id) ); -CREATE TABLE IF NOT EXISTS certify.credential_template( - context character varying(1024) NOT NULL, - credential_type character varying(512) NOT NULL, - template VARCHAR NOT NULL, - credential_format character varying(1024), - did_url VARCHAR, - key_manager_app_id character varying(36) NOT NULL, - key_manager_ref_id character varying(128), - signature_algo character(2048), - sd_claim VARCHAR, - cr_dtimes timestamp NOT NULL default now(), - upd_dtimes timestamp, - CONSTRAINT pk_template PRIMARY KEY (context, credential_type, credential_format) +CREATE TABLE IF NOT EXISTS credential_config ( + config_id VARCHAR(255), + status VARCHAR(255), + vc_template VARCHAR, + doctype VARCHAR, + context VARCHAR NOT NULL, + credential_type VARCHAR NOT NULL, + credential_format VARCHAR(255) NOT NULL, + did_url VARCHAR NOT NULL, + key_manager_app_id VARCHAR(36) NOT NULL, + key_manager_ref_id VARCHAR(128), + signature_algo VARCHAR(36), + sd_claim VARCHAR, + display JSONB NOT NULL, + display_order TEXT[] NOT NULL, + scope VARCHAR(255) NOT NULL, + cryptographic_binding_methods_supported TEXT[] NOT NULL, + credential_signing_alg_values_supported TEXT[] NOT NULL, + proof_types_supported JSONB NOT NULL, + credential_subject JSONB, + claims JSONB, + plugin_configurations JSONB, + cr_dtimes TIMESTAMP NOT NULL, + upd_dtimes TIMESTAMP, + CONSTRAINT pk_config_id PRIMARY KEY (context, credential_type, credential_format) ); -INSERT INTO certify.credential_template (context, credential_type, template, credential_format, key_manager_app_id, key_manager_ref_id, did_url, cr_dtimes, upd_dtimes) VALUES ('https://www.w3.org/2018/credentials/v1', 'FarmerCredential,VerifiableCredential', '{ - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://piyush7034.github.io/my-files/farmer.json", - "https://w3id.org/security/suites/ed25519-2020/v1" - ], - "issuer": "${_issuer}", - "type": [ - "VerifiableCredential", - "FarmerCredential" - ], - "issuanceDate": "${validFrom}", - "expirationDate": "${validUntil}", - "credentialSubject": { - "id": "${_holderId}", - "fullName": "${fullName}", - "mobileNumber": "${mobileNumber}", - "dateOfBirth": "${dateOfBirth}", - "gender": "${gender}", - "state": "${state}", - "district": "${district}", - "villageOrTown": "${villageOrTown}", - "postalCode": "${postalCode}", - "landArea": "${landArea}", - "landOwnershipType": "${landOwnershipType}", - "primaryCropType": "${primaryCropType}", - "secondaryCropType": "${secondaryCropType}", - "face": "${face}", - "farmerID": "${farmerID}" +INSERT INTO credential_config ( + config_id, + status, + vc_template, + doctype, + context, + credential_type, + credential_format, + did_url, + key_manager_app_id, + key_manager_ref_id, + signature_algo, + sd_claim, + display, + display_order, + scope, + cryptographic_binding_methods_supported, + credential_signing_alg_values_supported, + proof_types_supported, + credential_subject, + claims, + plugin_configurations, + cr_dtimes, + upd_dtimes +) +VALUES ( + gen_random_uuid()::VARCHAR(255), -- generating a unique config_id + 'active', -- assuming an active status + '{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://piyush7034.github.io/my-files/farmer.json", + "https://w3id.org/security/suites/ed25519-2020/v1" + ], + "issuer": "${_issuer}", + "type": [ + "VerifiableCredential", + "FarmerCredential" + ], + "issuanceDate": "${validFrom}", + "expirationDate": "${validUntil}", + "credentialSubject": { + "id": "${_holderId}", + "fullName": "${fullName}", + "mobileNumber": "${mobileNumber}", + "dateOfBirth": "${dateOfBirth}", + "gender": "${gender}", + "state": "${state}", + "district": "${district}", + "villageOrTown": "${villageOrTown}", + "postalCode": "${postalCode}", + "landArea": "${landArea}", + "landOwnershipType": "${landOwnershipType}", + "primaryCropType": "${primaryCropType}", + "secondaryCropType": "${secondaryCropType}", + "face": "${face}", + "farmerID": "${farmerID}" + } } -} -', 'ldp_vc', 'CERTIFY_VC_SIGN_ED25519','ED25519_SIGN','did:web:jainhitesh9998.github.io:tempfiles:vc-local-ed25519#key-0', '2024-10-24 12:32:38.065994', NULL); + ', -- the VC template from the JSON + NULL, -- doctype from JSON + 'https://www.w3.org/2018/credentials/v1, https://piyush7034.github.io/my-files/farmer.json, https://w3id.org/security/suites/ed25519-2020/v1', -- context as comma-separated string + 'VerifiableCredential,FarmerCredential', -- credential_type as comma-separated string + 'ldp_vc', -- credential_format + 'did:web:jainhitesh9998.github.io:tempfiles:vc-local-ed25519#key-0', -- did_url + 'CERTIFY_VC_SIGN_ED25519', -- key_manager_app_id + 'ED25519_SIGN', -- key_manager_ref_id (optional) + 'did:web:vharsh.github.io:DID:harsh', -- signature_algo (optional) + NULL, -- sd_claim (optional) + '[{"name": "Farmer Verifiable Credential", "locale": "en", "logo": {"url": "https://example.com/logo.png", "alt_text": "Farmer Credential Logo"}, "background_color": "#12107c", "text_color": "#FFFFFF"}]'::JSONB, -- display + ARRAY['fullName', 'mobileNumber', 'dateOfBirth', 'gender', 'state', 'district', 'villageOrTown', 'postalCode', 'landArea', 'landOwnershipType', 'primaryCropType', 'secondaryCropType', 'farmerID'], -- display_order + 'farmer_identity_vc', -- scope + ARRAY['did:jwk'], -- cryptographic_binding_methods_supported + ARRAY['Ed25519Signature2020'], -- credential_signing_alg_values_supported + '{"jwt": {"proof_signing_alg_values_supported": ["RS256", "ES256"]}}'::JSONB, -- proof_types_supported + '{"fullName": {"display": [{"name": "Full Name", "locale": "en"}]}, "phone": {"display": [{"name": "Phone Number", "locale": "en"}]}, "dateOfBirth": {"display": [{"name": "Date of Birth", "locale": "en"}]}, "gender": {"display": [{"name": "Gender", "locale": "en"}]}}'::JSONB, -- credential_subject + NULL, -- claims (optional) + '[{"mosip.certify.mock.data-provider.csv.identifier-column": "id", "mosip.certify.mock.data-provider.csv.data-columns": "id,fullName,mobileNumber,dateOfBirth,gender,state,district,villageOrTown,postalCode,landArea,landOwnershipType,primaryCropType,secondaryCropType,face,farmerID", "mosip.certify.mock.data-provider.csv-registry-uri": "/home/mosip/config/farmer_identity_data.csv"}]'::JSONB, -- plugin_configurations + NOW(), -- cr_dtimes + NULL -- upd_dtimes (optional) +); -INSERT INTO certify.credential_template (context, credential_type, template, credential_format, key_manager_app_id, key_manager_ref_id, did_url, cr_dtimes, upd_dtimes) VALUES ('https://www.w3.org/2018/credentials/v1', 'VerifiableCredential,FarmerCredential', '{ - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://piyush7034.github.io/my-files/farmer.json", - "https://w3id.org/security/suites/ed25519-2020/v1" - ], - "issuer": "${_issuer}", - "type": [ - "VerifiableCredential", - "FarmerCredential" - ], - "issuanceDate": "${validFrom}", - "expirationDate": "${validUntil}", - "credentialSubject": { - "id": "${_holderId}", - "fullName": "${fullName}", - "mobileNumber": "${mobileNumber}", - "dateOfBirth": "${dateOfBirth}", - "gender": "${gender}", - "state": "${state}", - "district": "${district}", - "villageOrTown": "${villageOrTown}", - "postalCode": "${postalCode}", - "landArea": "${landArea}", - "landOwnershipType": "${landOwnershipType}", - "primaryCropType": "${primaryCropType}", - "secondaryCropType": "${secondaryCropType}", - "face": "${face}", - "farmerID": "${farmerID}" +INSERT INTO credential_config ( + config_id, + status, + vc_template, + doctype, + context, + credential_type, + credential_format, + did_url, + key_manager_app_id, + key_manager_ref_id, + signature_algo, + sd_claim, + display, + display_order, + scope, + cryptographic_binding_methods_supported, + credential_signing_alg_values_supported, + proof_types_supported, + credential_subject, + claims, + plugin_configurations, + cr_dtimes, + upd_dtimes +) +VALUES ( + gen_random_uuid()::VARCHAR(255), -- generating a unique config_id + 'active', -- assuming an active status + '{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://piyush7034.github.io/my-files/farmer.json", + "https://w3id.org/security/suites/ed25519-2020/v1" + ], + "issuer": "${_issuer}", + "type": [ + "VerifiableCredential", + "FarmerCredential" + ], + "issuanceDate": "${validFrom}", + "expirationDate": "${validUntil}", + "credentialSubject": { + "id": "${_holderId}", + "fullName": "${fullName}", + "mobileNumber": "${mobileNumber}", + "dateOfBirth": "${dateOfBirth}", + "gender": "${gender}", + "state": "${state}", + "district": "${district}", + "villageOrTown": "${villageOrTown}", + "postalCode": "${postalCode}", + "landArea": "${landArea}", + "landOwnershipType": "${landOwnershipType}", + "primaryCropType": "${primaryCropType}", + "secondaryCropType": "${secondaryCropType}", + "face": "${face}", + "farmerID": "${farmerID}" + } } -} -', 'ldp_vc', 'CERTIFY_VC_SIGN_ED25519','ED25519_SIGN','did:web:jainhitesh9998.github.io:tempfiles:vc-local-ed25519#key-0', '2024-10-24 12:32:38.065994', NULL); - -INSERT INTO certify.credential_template(context, credential_type, template, credential_format, key_manager_app_id, key_manager_ref_id, did_url, cr_dtimes, upd_dtimes) VALUES ('https://www.w3.org/ns/credentials/v2', 'FarmerCredential,VerifiableCredential', '{ - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://piyush7034.github.io/my-files/farmer.json", - "https://w3id.org/security/suites/ed25519-2020/v1" - ], - "issuer": "${_issuer}", - "type": [ - "VerifiableCredential", - "FarmerCredential" - ], - "validFrom": "${validFrom}", - "validUntil": "${validUntil}", - "credentialSubject": { - "id": "${_holderId}", - "fullName": "${fullName}", - "mobileNumber": "${mobileNumber}", - "dateOfBirth": "${dateOfBirth}", - "gender": "${gender}", - "state": "${state}", - "district": "${district}", - "villageOrTown": "${villageOrTown}", - "postalCode": "${postalCode}", - "landArea": "${landArea}", - "landOwnershipType": "${landOwnershipType}", - "primaryCropType": "${primaryCropType}", - "secondaryCropType": "${secondaryCropType}", - "face": "${face}", - "farmerID": "${farmerID}" - } -}', 'ldp_vc', 'CERTIFY_MOCK_ED25519','ED25519_SIGN', 'did:web:vharsh.github.io:DID:harsh', '2024-10-24 12:32:38.065994', NULL); - + ', -- the VC template from the JSON + NULL, -- doctype from JSON + 'https://www.w3.org/ns/credentials/v2, https://piyush7034.github.io/my-files/farmer.json, https://w3id.org/security/suites/ed25519-2020/v1', -- context as comma-separated string + 'VerifiableCredential,FarmerCredential', -- credential_type as comma-separated string + 'ldp_vc', -- credential_format + 'did:web:jainhitesh9998.github.io:tempfiles:vc-local-ed25519#key-0', -- did_url + 'CERTIFY_VC_SIGN_ED25519', -- key_manager_app_id + 'ED25519_SIGN', -- key_manager_ref_id (optional) + 'did:web:vharsh.github.io:DID:harsh', -- signature_algo (optional) + NULL, -- sd_claim (optional) + '[{"name": "Farmer Verifiable Credential", "locale": "en", "logo": {"url": "https://example.com/logo.png", "alt_text": "Farmer Credential Logo"}, "background_color": "#12107c", "text_color": "#FFFFFF"}]'::JSONB, -- display + ARRAY['fullName', 'mobileNumber', 'dateOfBirth', 'gender', 'state', 'district', 'villageOrTown', 'postalCode', 'landArea', 'landOwnershipType', 'primaryCropType', 'secondaryCropType', 'farmerID'], -- display_order + 'farmer_identity_vc', -- scope + ARRAY['did:jwk'], -- cryptographic_binding_methods_supported + ARRAY['Ed25519Signature2020'], -- credential_signing_alg_values_supported + '{"jwt": {"proof_signing_alg_values_supported": ["RS256", "ES256"]}}'::JSONB, -- proof_types_supported + '{"fullName": {"display": [{"name": "Full Name", "locale": "en"}]}, "phone": {"display": [{"name": "Phone Number", "locale": "en"}]}, "dateOfBirth": {"display": [{"name": "Date of Birth", "locale": "en"}]}, "gender": {"display": [{"name": "Gender", "locale": "en"}]}}'::JSONB, -- credential_subject + NULL, -- claims (optional) + '[{"mosip.certify.mock.data-provider.csv.identifier-column": "id", "mosip.certify.mock.data-provider.csv.data-columns": "id,fullName,mobileNumber,dateOfBirth,gender,state,district,villageOrTown,postalCode,landArea,landOwnershipType,primaryCropType,secondaryCropType,face,farmerID", "mosip.certify.mock.data-provider.csv-registry-uri": "/home/mosip/config/farmer_identity_data.csv"}]'::JSONB, -- plugin_configurations + NOW(), -- cr_dtimes + NULL -- upd_dtimes (optional) +); INSERT INTO certify.key_policy_def(APP_ID,KEY_VALIDITY_DURATION,PRE_EXPIRE_DAYS,ACCESS_ALLOWED,IS_ACTIVE,CR_BY,CR_DTIMES) VALUES('ROOT', 2920, 1125, 'NA', true, 'mosipadmin', now()); INSERT INTO certify.key_policy_def(APP_ID,KEY_VALIDITY_DURATION,PRE_EXPIRE_DAYS,ACCESS_ALLOWED,IS_ACTIVE,CR_BY,CR_DTIMES) VALUES('CERTIFY_SERVICE', 1095, 60, 'NA', true, 'mosipadmin', now());