diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..2424c1ef --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# other directories +target/ +.idea/ +*.p12 +*.iml +.settings/ +.setting/ +.mvn/ +.project/ +helm/*/Chart.lock +helm/*/charts/ \ No newline at end of file diff --git a/README.md b/README.md index 7b57e287..cef3c7b0 100644 --- a/README.md +++ b/README.md @@ -45,8 +45,8 @@ Execute installation script * Set the hostname of the endpoints correctly as per your docker setup * Now generate a DID, create a credential schema and create an issuance registry * take note of `$.schema[0].author` and `$.schema[0].id` from the create credential schema request -6. Add the jar file of Digital Credential Stack(DCS) plugin implementation in [loader_path](docker-compose-esignet/loader_path). The JAR can be built [from source](https://github.com/mosip/digital-credential-plugins/) or [downloaded directly](https://mvnrepository.com/artifact/io.mosip.esignet.sunbirdrc/sunbird-rc-esignet-integration-impl). -7. Modify the properties of the Esignet service located in the [esignet-default.properties](docker-compose-esignet/config/esignet-default.properties) file: +6. Add the jar file of Digital Credential Stack(DCS) plugin implementation in [loader_path](docker-compose/docker-compose-esignet/loader_path). The JAR can be built [from source](https://github.com/mosip/digital-credential-plugins/) or [downloaded directly](https://mvnrepository.com/artifact/io.mosip.esignet.sunbirdrc/sunbird-rc-esignet-integration-impl). +7. Modify the properties of the Esignet service located in the [esignet-default.properties](docker-compose/docker-compose-esignet/config/esignet-default.properties) file: - Include Issuer ID and credential schema ID for the following properties: `mosip.esignet.vciplugin.sunbird-rc.credential-type.{credential type}.static-value-map.issuerId`, `mosip.esignet.vciplugin.sunbird-rc.credential-type.{credential-type}.cred-schema-id`. - The `$.schema[0].author` DID goes to the config ending in issuerId and `$.schema[0].id` DID goes to the config ending in `cred-schema-id`. 8. Once the Esignet properties are configured, proceed to select Esignet from the options provided for eSignet. @@ -60,11 +60,11 @@ Execute installation script ## Properties for custom use case -- Sample schemas for Insurance registry are provided [here](docker-compose-sunbird/schemas), change it according to use case. +- Sample schemas for Insurance registry are provided [here](docker-compose/docker-compose-sunbird/schemas), change it according to use case. - Change these properties for different use case `mosip.esignet.authenticator.sunbird-rc.auth-factor.kba.field-details`,`mosip.esignet.authenticator.sunbird-rc.auth-factor.kba.individual-id-field` - Add the Sunbird registry URL for these properties: `mosip.esignet.vciplugin.sunbird-rc.issue-credential-url`,`mosip.esignet.authenticator.sunbird-rc.auth-factor.kba.registry-search-url`. - Specify the list of supported credential types using the property: `mosip.esignet.vciplugin.sunbird-rc.supported-credential-types`. -- For each supported credential type change the below properties. Sample properties are provided in the [default properties](docker-compose-esignet/config/esignet-default.properties) file. +- For each supported credential type change the below properties. Sample properties are provided in the [default properties](docker-compose/docker-compose-esignet/config/esignet-default.properties) file. * Issuer id `mosip.esignet.vciplugin.sunbird-rc.credential-type.{credential type}.static-value-map.issuerId` * Credential schema id `mosip.esignet.vciplugin.sunbird-rc.credential-type.{credential type}.cred-schema-id` * Registry Url `mosip.esignet.vciplugin.sunbird-rc.credential-type.{credential type}.registry-get-url` diff --git a/certify-core/pom.xml b/certify-core/pom.xml new file mode 100644 index 00000000..4af86876 --- /dev/null +++ b/certify-core/pom.xml @@ -0,0 +1,39 @@ + + + + 4.0.0 + + + io.mosip.certify + certify-parent + 0.0.1-SNAPSHOT + + + io.mosip.certify + certify-core + certify-core + ${project.parent.version} + Certify Core Library + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${springdoc-openapi-webmvc-ui-version} + + + commons-validator + commons-validator + ${commons.validator.version} + + + io.mosip.certify + certify-integration-api + ${project.version} + + + \ No newline at end of file diff --git a/certify-core/src/main/java/io/mosip/certify/core/config/LocalAuthenticationEntryPoint.java b/certify-core/src/main/java/io/mosip/certify/core/config/LocalAuthenticationEntryPoint.java new file mode 100644 index 00000000..ccd18c34 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/config/LocalAuthenticationEntryPoint.java @@ -0,0 +1,29 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.config; + +import java.io.IOException; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerExceptionResolver; + +@Component +public class LocalAuthenticationEntryPoint implements AuthenticationEntryPoint { + @Autowired + private HandlerExceptionResolver handlerExceptionResolver; + + public LocalAuthenticationEntryPoint() { + } + + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + this.handlerExceptionResolver.resolveException(request, response, (Object)null, authException); + } +} 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 new file mode 100644 index 00000000..9508918c --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/constants/Constants.java @@ -0,0 +1,16 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.constants; + +public class Constants { + + public static final String UTC_DATETIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + public static final String SPACE = " "; + + public static final String C_NONCE = "c_nonce"; + public static final String C_NONCE_EXPIRES_IN = "c_nonce_expires_in"; + public static final String CLIENT_ID = "client_id"; +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/constants/ErrorConstants.java b/certify-core/src/main/java/io/mosip/certify/core/constants/ErrorConstants.java new file mode 100644 index 00000000..19cf956d --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/constants/ErrorConstants.java @@ -0,0 +1,25 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.constants; + +public class ErrorConstants { + + public static final String INVALID_REQUEST="invalid_request"; + public static final String INVALID_SCOPE="invalid_scope"; + public static final String INVALID_AUTH_TOKEN="invalid_token"; + public static final String INVALID_ALGORITHM = "invalid_algorithm"; + public static final String UNKNOWN_ERROR = "unknown_error"; + public static final String UNSUPPORTED_VC_FORMAT = "unsupported_credential_format"; + public static final String INVALID_VC_FORMAT = "invalid_vc_format"; + public static final String INVALID_PROOF = "invalid_proof"; + public static final String UNSUPPORTED_PROOF_TYPE = "unsupported_proof_type"; + public static final String UNSUPPORTED_VC_TYPE = "unsupported_credential_type"; + public static final String VC_ISSUANCE_FAILED = "vc_issuance_failed"; + public static final String PROOF_HEADER_INVALID_TYP = "proof_header_invalid_typ"; + public static final String PROOF_HEADER_INVALID_ALG = "proof_header_invalid_alg"; + public static final String PROOF_HEADER_INVALID_KEY = "proof_header_invalid_key"; + public static final String PROOF_HEADER_AMBIGUOUS_KEY = "proof_header_ambiguous_key"; +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialDefinition.java b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialDefinition.java new file mode 100644 index 00000000..f90c3ae9 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialDefinition.java @@ -0,0 +1,29 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.dto; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.mosip.certify.core.constants.ErrorConstants; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import java.util.List; +import java.util.Map; + +@Data +public class CredentialDefinition { + + @JsonProperty("@context") + private List<@NotBlank(message = ErrorConstants.INVALID_REQUEST) String> context; + + @NotEmpty(message = ErrorConstants.INVALID_REQUEST) + private List<@NotBlank(message = ErrorConstants.INVALID_REQUEST) String> type; + + private Map credentialSubject; + +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialMetadata.java b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialMetadata.java new file mode 100644 index 00000000..eb0df9f2 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialMetadata.java @@ -0,0 +1,23 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +import java.util.List; + +@JsonIgnoreProperties(ignoreUnknown = true) +@Data +public class CredentialMetadata { + + private String id; + private String format; + private String scope; + private List proof_types_supported; + private List types; + +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialProof.java b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialProof.java new file mode 100644 index 00000000..7bbb2a27 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialProof.java @@ -0,0 +1,31 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.dto; + +import io.mosip.certify.core.constants.ErrorConstants; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; + +@Data +public class CredentialProof { + + /** + * The proof object MUST contain a proof_type claim of type JSON string denoting the concrete proof type. + */ + @NotBlank(message = ErrorConstants.UNSUPPORTED_PROOF_TYPE) + private String proof_type; + + /** + * When proof_type is jwt, a proof object MUST include a jwt claim + */ + private String jwt; + + /** + * When proof_type is cwt, a proof object MUST include a cwt claim + */ + private String cwt; +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialRequest.java b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialRequest.java new file mode 100644 index 00000000..73986e97 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialRequest.java @@ -0,0 +1,45 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.dto; + +import io.mosip.certify.core.constants.ErrorConstants; +import jakarta.validation.Valid; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +@Data +public class CredentialRequest { + + /** + * REQUIRED. Format of the Credential to be issued. + */ + @NotBlank(message = ErrorConstants.INVALID_VC_FORMAT) + private String format; + + /** + * OPTIONAL. + * JSON object containing proof of possession of the key material the issued Credential shall be bound to. + */ + @Valid + @NotNull(message = ErrorConstants.INVALID_PROOF) + private CredentialProof proof; + + /** + * "format": jwt_vc_json | jwt_vc_json-ld | ldp_vc + * REQUIRED + * JSON object containing (and isolating) the detailed description of the credential type. + * This object MUST be processed using full JSON-LD processing. + * It consists of the following sub claims: + * @context: REQUIRED. JSON array + * types: REQUIRED. JSON array. This claim contains the type values the Wallet shall request + * in the subsequent Credential Request. + */ + @Valid + @NotNull(message = ErrorConstants.INVALID_REQUEST) + private CredentialDefinition credential_definition; +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialResponse.java b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialResponse.java new file mode 100644 index 00000000..da7015ec --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/dto/CredentialResponse.java @@ -0,0 +1,44 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +@Data +public class CredentialResponse { + + /** + * JSON string denoting the format of the issued Credential. + */ + private String format; + + /** + * Contains issued Credential. MUST be present when acceptance_token is not returned. + * MAY be a JSON string or a JSON object, depending on the Credential format. + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + private T credential; + + /** + * A JSON string containing a security token subsequently used to obtain a Credential. + * MUST be present when credential is not returned + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + private String acceptance_token; + + /** + * JSON string containing a nonce to be used to create a proof of possession of key material + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + private String c_nonce; + + /** + * JSON integer denoting the lifetime in seconds of the c_nonce + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + private Integer c_nonce_expires_in; +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/dto/Error.java b/certify-core/src/main/java/io/mosip/certify/core/dto/Error.java new file mode 100644 index 00000000..c1e62da6 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/dto/Error.java @@ -0,0 +1,20 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Error { + + private String errorCode; + private String errorMessage; + +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/dto/ParsedAccessToken.java b/certify-core/src/main/java/io/mosip/certify/core/dto/ParsedAccessToken.java new file mode 100644 index 00000000..4e2fe764 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/dto/ParsedAccessToken.java @@ -0,0 +1,20 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.dto; + +import lombok.Data; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Data +@Component +public class ParsedAccessToken { + + private Map claims; + private String accessTokenHash; + private boolean isActive; +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/dto/ResponseWrapper.java b/certify-core/src/main/java/io/mosip/certify/core/dto/ResponseWrapper.java new file mode 100644 index 00000000..c7d00739 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/dto/ResponseWrapper.java @@ -0,0 +1,19 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.dto; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +@Data +public class ResponseWrapper { + + private String responseTime; + private T response; + private List errors = new ArrayList<>(); +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/dto/VCError.java b/certify-core/src/main/java/io/mosip/certify/core/dto/VCError.java new file mode 100644 index 00000000..b67e7a36 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/dto/VCError.java @@ -0,0 +1,28 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +@Data +public class VCError { + + private String error; + private String error_description; + + /** + * JSON string containing a nonce to be used to create a proof of possession of key material when requesting a Credential + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + private String c_nonce; + + /** + * JSON integer denoting the lifetime in seconds of the c_nonce. + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + private Integer c_nonce_expires_in; +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/dto/VCIssuanceTransaction.java b/certify-core/src/main/java/io/mosip/certify/core/dto/VCIssuanceTransaction.java new file mode 100644 index 00000000..b1ae7830 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/dto/VCIssuanceTransaction.java @@ -0,0 +1,21 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.dto; + + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class VCIssuanceTransaction implements Serializable { + + private String cNonce; + private long cNonceIssuedEpoch; + private int cNonceExpireSeconds; + + +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/exception/CertifyException.java b/certify-core/src/main/java/io/mosip/certify/core/exception/CertifyException.java new file mode 100644 index 00000000..642a7159 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/exception/CertifyException.java @@ -0,0 +1,27 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.exception; + +import io.mosip.certify.core.constants.ErrorConstants; + +public class CertifyException extends RuntimeException { + + private String errorCode; + + public CertifyException() { + super(ErrorConstants.UNKNOWN_ERROR); + this.errorCode = ErrorConstants.UNKNOWN_ERROR; + } + + public CertifyException(String errorCode) { + super(errorCode); + this.errorCode = errorCode; + } + + public String getErrorCode() { + return errorCode; + } +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/exception/InvalidRequestException.java b/certify-core/src/main/java/io/mosip/certify/core/exception/InvalidRequestException.java new file mode 100644 index 00000000..287bbfca --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/exception/InvalidRequestException.java @@ -0,0 +1,21 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.exception; + +public class InvalidRequestException extends CertifyException { + + private String errorCode; + + public InvalidRequestException(String errorCode) { + super(errorCode); + this.errorCode = errorCode; + } + + public String getErrorCode() { + return errorCode; + } + +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/exception/NotAuthenticatedException.java b/certify-core/src/main/java/io/mosip/certify/core/exception/NotAuthenticatedException.java new file mode 100644 index 00000000..1735bc03 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/exception/NotAuthenticatedException.java @@ -0,0 +1,19 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.exception; + +import io.mosip.certify.core.constants.ErrorConstants; + +public class NotAuthenticatedException extends CertifyException { + + public NotAuthenticatedException() { + super(ErrorConstants.INVALID_AUTH_TOKEN); + } + + public NotAuthenticatedException(String errorCode) { + super(errorCode); + } +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/spi/VCIssuanceService.java b/certify-core/src/main/java/io/mosip/certify/core/spi/VCIssuanceService.java new file mode 100644 index 00000000..63aedaeb --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/spi/VCIssuanceService.java @@ -0,0 +1,23 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.spi; + +import io.mosip.certify.core.dto.CredentialRequest; +import io.mosip.certify.core.dto.CredentialResponse; + +import java.util.Map; + +public interface VCIssuanceService { + + /** + * + * @param credentialRequest + * @return + */ + CredentialResponse getCredential(CredentialRequest credentialRequest); + + Map getCredentialIssuerMetadata(String version); +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/util/AuditHelper.java b/certify-core/src/main/java/io/mosip/certify/core/util/AuditHelper.java new file mode 100644 index 00000000..97b25160 --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/util/AuditHelper.java @@ -0,0 +1,18 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.util; + +import io.mosip.certify.api.dto.AuditDTO; + +public class AuditHelper { + + public static AuditDTO buildAuditDto(String transactionId, String idType) { + AuditDTO auditDTO = new AuditDTO(); + auditDTO.setTransactionId(transactionId); + auditDTO.setIdType(idType); + return auditDTO; + } +} diff --git a/certify-core/src/main/java/io/mosip/certify/core/util/CommonUtil.java b/certify-core/src/main/java/io/mosip/certify/core/util/CommonUtil.java new file mode 100644 index 00000000..5d663fdd --- /dev/null +++ b/certify-core/src/main/java/io/mosip/certify/core/util/CommonUtil.java @@ -0,0 +1,85 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.core.util; + +import com.nimbusds.jose.util.ByteUtils; +import io.mosip.certify.core.constants.Constants; +import io.mosip.certify.core.constants.ErrorConstants; +import io.mosip.certify.core.exception.CertifyException; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.validator.routines.UrlValidator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; + +import static org.apache.commons.validator.routines.UrlValidator.ALLOW_ALL_SCHEMES; +import static org.apache.commons.validator.routines.UrlValidator.ALLOW_LOCAL_URLS; + +@Slf4j +public class CommonUtil { + + private static final Logger logger = LoggerFactory.getLogger(CommonUtil.class); + public static final String ALGO_SHA_256 = "SHA-256"; + public static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + private static Base64.Encoder urlSafeEncoder; + private static PathMatcher pathMatcher; + private static UrlValidator urlValidator; + + static { + urlSafeEncoder = Base64.getUrlEncoder().withoutPadding(); + pathMatcher = new AntPathMatcher(); + urlValidator = new UrlValidator(ALLOW_ALL_SCHEMES+ALLOW_LOCAL_URLS); + } + + /** + * Output format : 2022-12-01T03:22:46.720Z + * @return Formatted datetime + */ + public static String getUTCDateTime() { + return ZonedDateTime + .now(ZoneOffset.UTC) + .format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)); + } + + /** + * if the alg is RS256, hash the access_token value with SHA-256, then take the left-most 128 bits and base64url + * encode them. The at_hash value is a case-sensitive string. + * @param accessToken + * @return + * @throws CertifyException + */ + public static String generateOIDCAtHash(String accessToken) throws CertifyException { + try { + MessageDigest digest = MessageDigest.getInstance(ALGO_SHA_256); + byte[] hash = digest.digest(accessToken.getBytes(StandardCharsets.UTF_8)); + //taking only 16 bytes (=128 bits) + byte[] leftMost128Bits = ByteUtils.subArray(hash, 0, 16); + return urlSafeEncoder.encodeToString(leftMost128Bits); + } catch (NoSuchAlgorithmException ex) { + log.error("Access token hashing failed with alg:{}", ALGO_SHA_256, ex); + throw new CertifyException(ErrorConstants.INVALID_ALGORITHM); + } + } + + public static String generateRandomAlphaNumeric(int length) { + StringBuilder builder = new StringBuilder(); + for(int i=0; i + + 4.0.0 + + io.mosip.certify + certify-parent + 0.0.1-SNAPSHOT + + + io.mosip.certify + certify-integration-api + ${project.parent.version} + certify-integration-api + Certify Integration Library + + + + decentralized-identity + jsonld-common-java + ${jsonld.common.java.version} + + + + + \ No newline at end of file diff --git a/certify-integration-api/src/main/java/io/mosip/certify/api/dto/AuditDTO.java b/certify-integration-api/src/main/java/io/mosip/certify/api/dto/AuditDTO.java new file mode 100644 index 00000000..a1209835 --- /dev/null +++ b/certify-integration-api/src/main/java/io/mosip/certify/api/dto/AuditDTO.java @@ -0,0 +1,17 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.api.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class AuditDTO { + + String transactionId; + String idType; +} diff --git a/certify-integration-api/src/main/java/io/mosip/certify/api/dto/VCRequestDto.java b/certify-integration-api/src/main/java/io/mosip/certify/api/dto/VCRequestDto.java new file mode 100644 index 00000000..ae9b11b5 --- /dev/null +++ b/certify-integration-api/src/main/java/io/mosip/certify/api/dto/VCRequestDto.java @@ -0,0 +1,20 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.api.dto; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class VCRequestDto { + + private List context; //holds @context values + private List type; + private String format; + private Map credentialSubject; +} diff --git a/certify-integration-api/src/main/java/io/mosip/certify/api/dto/VCResult.java b/certify-integration-api/src/main/java/io/mosip/certify/api/dto/VCResult.java new file mode 100644 index 00000000..d9e435b8 --- /dev/null +++ b/certify-integration-api/src/main/java/io/mosip/certify/api/dto/VCResult.java @@ -0,0 +1,23 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.api.dto; + +import lombok.Data; + +@Data +public class VCResult { + + /** + * Format of credential + * Eg: ldp_vc + */ + private String format; + + /** + * + */ + private T credential; +} diff --git a/certify-integration-api/src/main/java/io/mosip/certify/api/exception/VCIExchangeException.java b/certify-integration-api/src/main/java/io/mosip/certify/api/exception/VCIExchangeException.java new file mode 100644 index 00000000..8088efb2 --- /dev/null +++ b/certify-integration-api/src/main/java/io/mosip/certify/api/exception/VCIExchangeException.java @@ -0,0 +1,31 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.api.exception; + +import io.mosip.certify.api.util.ErrorConstants; + +public class VCIExchangeException extends Exception { + private String errorCode; + + public VCIExchangeException() { + super(ErrorConstants.VCI_EXCHANGE_FAILED); + this.errorCode = ErrorConstants.VCI_EXCHANGE_FAILED; + } + + public VCIExchangeException(String errorCode) { + super(errorCode); + this.errorCode = errorCode; + } + + public VCIExchangeException(String errorCode, String errorMessage) { + super(errorCode + " -> " + errorMessage); + this.errorCode = errorCode; + } + + public String getErrorCode() { + return errorCode; + } +} diff --git a/certify-integration-api/src/main/java/io/mosip/certify/api/spi/AuditPlugin.java b/certify-integration-api/src/main/java/io/mosip/certify/api/spi/AuditPlugin.java new file mode 100644 index 00000000..bce2a276 --- /dev/null +++ b/certify-integration-api/src/main/java/io/mosip/certify/api/spi/AuditPlugin.java @@ -0,0 +1,34 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.api.spi; + +import io.mosip.certify.api.dto.AuditDTO; +import io.mosip.certify.api.util.Action; +import io.mosip.certify.api.util.ActionStatus; + +public interface AuditPlugin { + + /** + + Plugin method to audit all the actions in certify service. + + + + @param action Action to audit @{@link Action} + + @param actionStatus Action status to audit @{@link ActionStatus} + + @param audit @{@link AuditDTO} during this action + + @param t Any error / exception occurred during this action, null if no errors / exception found. + */ + void logAudit(Action action, ActionStatus status, AuditDTO audit, Throwable t); + + /** + + Plugin method to audit all the actions in certify service. + + + + @param username Session username for audit + + @param action Action to audit @{@link Action} + + @param actionStatus Action status to audit @{@link ActionStatus} + + @param audit @{@link AuditDTO} during this action + + @param t Any error / exception occurred during this action, null if no errors / exception found. + */ + void logAudit(String username, Action action, ActionStatus status, AuditDTO audit, Throwable t); +} diff --git a/certify-integration-api/src/main/java/io/mosip/certify/api/spi/VCIssuancePlugin.java b/certify-integration-api/src/main/java/io/mosip/certify/api/spi/VCIssuancePlugin.java new file mode 100644 index 00000000..49360a03 --- /dev/null +++ b/certify-integration-api/src/main/java/io/mosip/certify/api/spi/VCIssuancePlugin.java @@ -0,0 +1,36 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.api.spi; + +import foundation.identity.jsonld.JsonLDObject; +import io.mosip.certify.api.dto.VCRequestDto; +import io.mosip.certify.api.dto.VCResult; +import io.mosip.certify.api.exception.VCIExchangeException; + +import java.util.Map; + +public interface VCIssuancePlugin { + + /** + * Applicable for formats : ldp_vc + * @param vcRequestDto + * @param holderId Holders key material as either DID / KID. This should be used for cryptographic binding of the VC + * @param identityDetails Parsed access-token or introspect endpoint response if token is opaque. + * @return + */ + VCResult getVerifiableCredentialWithLinkedDataProof(VCRequestDto vcRequestDto, String holderId, + Map identityDetails) throws VCIExchangeException; + + /** + * Applicable for formats : jwt_vc_json, jwt_vc_json-ld, mso_doc + * @param vcRequestDto + * @param holderId + * @param identityDetails + * @return + */ + VCResult getVerifiableCredential(VCRequestDto vcRequestDto, String holderId, + Map identityDetails) throws VCIExchangeException; +} diff --git a/certify-integration-api/src/main/java/io/mosip/certify/api/util/Action.java b/certify-integration-api/src/main/java/io/mosip/certify/api/util/Action.java new file mode 100644 index 00000000..e431186f --- /dev/null +++ b/certify-integration-api/src/main/java/io/mosip/certify/api/util/Action.java @@ -0,0 +1,20 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.api.util; + +public enum Action { + VC_ISSUANCE("vci-service"); + + String module; + + Action(String module) { + this.module = module; + } + + public String getModule() { + return this.module; + } +} diff --git a/certify-integration-api/src/main/java/io/mosip/certify/api/util/ActionStatus.java b/certify-integration-api/src/main/java/io/mosip/certify/api/util/ActionStatus.java new file mode 100644 index 00000000..1f884fee --- /dev/null +++ b/certify-integration-api/src/main/java/io/mosip/certify/api/util/ActionStatus.java @@ -0,0 +1,12 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.api.util; + +public enum ActionStatus { + + SUCCESS, + ERROR +} diff --git a/certify-integration-api/src/main/java/io/mosip/certify/api/util/ErrorConstants.java b/certify-integration-api/src/main/java/io/mosip/certify/api/util/ErrorConstants.java new file mode 100644 index 00000000..9e1538db --- /dev/null +++ b/certify-integration-api/src/main/java/io/mosip/certify/api/util/ErrorConstants.java @@ -0,0 +1,12 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.api.util; + +public class ErrorConstants { + + public static final String NOT_IMPLEMENTED = "not_implemented"; + public static final String VCI_EXCHANGE_FAILED = "vci_exchange_failed"; +} diff --git a/certify-service/pom.xml b/certify-service/pom.xml new file mode 100644 index 00000000..727f0a90 --- /dev/null +++ b/certify-service/pom.xml @@ -0,0 +1,63 @@ + + + + 4.0.0 + + io.mosip.certify + certify-parent + 0.0.1-SNAPSHOT + + + io.mosip.certify + certify-service + 0.0.1-SNAPSHOT + certify-service + certify vci service + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-web + + + io.mosip.certify + certify-core + ${project.version} + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + true + ZIP + + + + + build-info + repackage + + + + + + + \ No newline at end of file diff --git a/certify-service/src/main/java/io/mosip/certify/CertifyServiceApplication.java b/certify-service/src/main/java/io/mosip/certify/CertifyServiceApplication.java new file mode 100644 index 00000000..da5a74b6 --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/CertifyServiceApplication.java @@ -0,0 +1,22 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package io.mosip.certify; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.scheduling.annotation.EnableAsync; + +@EnableAsync +@EnableCaching +@SpringBootApplication(scanBasePackages = "io.mosip.certify,"+ + "${mosip.certify.integration.scan-base-package}") +public class CertifyServiceApplication { + public static void main(String[] args) { + SpringApplication.run(CertifyServiceApplication.class, args); + } +} \ No newline at end of file diff --git a/certify-service/src/main/java/io/mosip/certify/advice/ExceptionHandlerAdvice.java b/certify-service/src/main/java/io/mosip/certify/advice/ExceptionHandlerAdvice.java new file mode 100644 index 00000000..6a460e2f --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/advice/ExceptionHandlerAdvice.java @@ -0,0 +1,202 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.advice; + +import io.mosip.certify.core.dto.Error; +import io.mosip.certify.core.dto.ResponseWrapper; +import io.mosip.certify.core.dto.VCError; +import io.mosip.certify.core.exception.CertifyException; +import io.mosip.certify.core.exception.InvalidRequestException; +import io.mosip.certify.core.exception.NotAuthenticatedException; +import io.mosip.certify.core.util.CommonUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.TypeMismatchException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.context.NoSuchMessageException; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.validation.FieldError; +import org.springframework.web.HttpMediaTypeNotAcceptableException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingRequestHeaderException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import java.io.IOException; +import java.util.*; + +import static io.mosip.certify.core.constants.ErrorConstants.*; + +@Slf4j +@ControllerAdvice +public class ExceptionHandlerAdvice extends ResponseEntityExceptionHandler implements AccessDeniedHandler { + + @Autowired + MessageSource messageSource; + + @Override + protected ResponseEntity handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, + HttpStatusCode status, WebRequest request) { + return handleExceptions(ex, request); + } + + @Override + protected ResponseEntity handleHttpMediaTypeNotAcceptable( + HttpMediaTypeNotAcceptableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) { + return handleExceptions(ex, request); + } + + @Override + protected ResponseEntity handleMissingServletRequestParameter( + MissingServletRequestParameterException ex, + HttpHeaders headers, + HttpStatusCode status, + WebRequest request) { + return handleExceptions(ex, request); + } + + @Override + protected ResponseEntity handleMethodArgumentNotValid( + MethodArgumentNotValidException ex, + HttpHeaders headers, + HttpStatusCode status, + WebRequest request) { + return handleExceptions(ex, request); + } + + protected ResponseEntity handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers, + HttpStatus status, WebRequest request) { + return handleExceptions(ex, request); + } + + @ExceptionHandler(value = { Exception.class, RuntimeException.class, MissingRequestHeaderException.class }) + public ResponseEntity handleExceptions(Exception ex, WebRequest request) { + log.error("Unhandled exception encountered in handler advice", ex); + String pathInfo = ((ServletWebRequest)request).getRequest().getPathInfo(); + + if(pathInfo != null && pathInfo.startsWith("/issuance/")) { + return handleVCIControllerExceptions(ex); + } + + return handleInternalControllerException(ex); + } + + + private ResponseEntity handleInternalControllerException(Exception ex) { + if(ex instanceof MethodArgumentNotValidException) { + List errors = new ArrayList<>(); + for (FieldError error : ((MethodArgumentNotValidException) ex).getBindingResult().getFieldErrors()) { + errors.add(new Error(error.getDefaultMessage(), error.getField() + ": " + error.getDefaultMessage())); + } + return new ResponseEntity(getResponseWrapper(errors), HttpStatus.OK); + } + if(ex instanceof javax.validation.ConstraintViolationException) { + List errors = new ArrayList<>(); + Set> violations = ((javax.validation.ConstraintViolationException) ex).getConstraintViolations(); + for(javax.validation.ConstraintViolation cv : violations) { + errors.add(new Error(INVALID_REQUEST,cv.getPropertyPath().toString() + ": " + cv.getMessage())); + } + return new ResponseEntity(getResponseWrapper(errors), HttpStatus.OK); + } + if(ex instanceof MissingServletRequestParameterException) { + return new ResponseEntity(getResponseWrapper(INVALID_REQUEST, ex.getMessage()), + HttpStatus.OK); + } + if(ex instanceof HttpMediaTypeNotAcceptableException) { + return new ResponseEntity(getResponseWrapper(INVALID_REQUEST, ex.getMessage()), + HttpStatus.OK); + } + if(ex instanceof CertifyException) { + String errorCode = ((CertifyException) ex).getErrorCode(); + return new ResponseEntity(getResponseWrapper(errorCode, getMessage(errorCode)), HttpStatus.OK); + } + if(ex instanceof AuthenticationCredentialsNotFoundException) { + return new ResponseEntity(getResponseWrapper(HttpStatus.UNAUTHORIZED.name(), + HttpStatus.UNAUTHORIZED.getReasonPhrase()), HttpStatus.UNAUTHORIZED); + } + if(ex instanceof AccessDeniedException) { + return new ResponseEntity(getResponseWrapper(HttpStatus.FORBIDDEN.name(), + HttpStatus.FORBIDDEN.getReasonPhrase()), HttpStatus.FORBIDDEN); + } + return new ResponseEntity(getResponseWrapper(UNKNOWN_ERROR, ex.getMessage()), HttpStatus.OK); + } + + public ResponseEntity handleVCIControllerExceptions(Exception ex) { + if(ex instanceof MethodArgumentNotValidException) { + FieldError fieldError = ((MethodArgumentNotValidException) ex).getBindingResult().getFieldError(); + String message = fieldError != null ? fieldError.getDefaultMessage() : ex.getMessage(); + return new ResponseEntity(getVCErrorDto(message, message), HttpStatus.BAD_REQUEST); + } + if(ex instanceof javax.validation.ConstraintViolationException) { + Set> violations = ((ConstraintViolationException) ex).getConstraintViolations(); + String message = !violations.isEmpty() ? violations.stream().findFirst().get().getMessage() : ex.getMessage(); + return new ResponseEntity(getVCErrorDto(message, message), HttpStatus.BAD_REQUEST); + } + if(ex instanceof NotAuthenticatedException) { + String errorCode = ((CertifyException) ex).getErrorCode(); + return new ResponseEntity(getVCErrorDto(errorCode, getMessage(errorCode)), HttpStatus.UNAUTHORIZED); + } + if(ex instanceof InvalidRequestException | ex instanceof CertifyException) { + String errorCode = ((CertifyException) ex).getErrorCode(); + return new ResponseEntity(getVCErrorDto(errorCode, getMessage(errorCode)), HttpStatus.BAD_REQUEST); + } + log.error("Unhandled exception encountered in handler advice", ex); + return new ResponseEntity(getVCErrorDto(UNKNOWN_ERROR, ex.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR); + } + + private ResponseWrapper getResponseWrapper(String errorCode, String errorMessage) { + Error error = new Error(); + error.setErrorCode(errorCode); + error.setErrorMessage(errorMessage); + return getResponseWrapper(Arrays.asList(error)); + } + + private ResponseWrapper getResponseWrapper(List errors) { + ResponseWrapper responseWrapper = new ResponseWrapper<>(); + responseWrapper.setResponseTime(CommonUtil.getUTCDateTime()); + responseWrapper.setErrors(errors); + return responseWrapper; + } + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, + AccessDeniedException accessDeniedException) throws IOException, ServletException { + handleExceptions(accessDeniedException, (WebRequest) request); + } + + private String getMessage(String errorCode) { + try { + messageSource.getMessage(errorCode, null, errorCode, Locale.getDefault()); + } catch (NoSuchMessageException ex) { + log.error("Message not found in the i18n bundle", ex); + } + return errorCode; + } + + private VCError getVCErrorDto(String errorCode, String description) { + VCError errorRespDto = new VCError(); + errorRespDto.setError(errorCode); + errorRespDto.setError_description(description); + return errorRespDto; + } +} diff --git a/certify-service/src/main/java/io/mosip/certify/config/AppConfig.java b/certify-service/src/main/java/io/mosip/certify/config/AppConfig.java new file mode 100644 index 00000000..b6c890f5 --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/config/AppConfig.java @@ -0,0 +1,56 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.module.afterburner.AfterburnerModule; +import lombok.extern.slf4j.Slf4j; +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +@Configuration +@Slf4j +public class AppConfig { + + + @Value("${mosip.certify.default.httpclient.connections.max.per.host:20}") + private int defaultMaxConnectionPerRoute; + + @Value("${mosip.certify.default.httpclient.connections.max:100}") + private int defaultTotalMaxConnection; + + + @Bean + public ObjectMapper objectMapper() { + return JsonMapper.builder() + .addModule(new AfterburnerModule()) + .addModule(new JavaTimeModule()) + .build(); + } + + @Bean + public RestTemplate restTemplate() { + HttpClientBuilder httpClientBuilder = HttpClients.custom() + .setConnectionManager(PoolingHttpClientConnectionManagerBuilder.create() + .setMaxConnPerRoute(defaultMaxConnectionPerRoute) + .setMaxConnTotal(defaultTotalMaxConnection) + .build()) + .disableCookieManagement(); + HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); + requestFactory.setHttpClient(httpClientBuilder.build()); + return new RestTemplate(requestFactory); + } + +} diff --git a/certify-service/src/main/java/io/mosip/certify/config/SecurityConfig.java b/certify-service/src/main/java/io/mosip/certify/config/SecurityConfig.java new file mode 100644 index 00000000..fb5e101f --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/config/SecurityConfig.java @@ -0,0 +1,83 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.config; + +import io.mosip.certify.core.config.LocalAuthenticationEntryPoint; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.csrf.CookieCsrfTokenRepository; +import org.springframework.security.web.csrf.CsrfTokenRepository; + + +import java.util.List; +import java.util.Map; + +@Slf4j +@Configuration +@EnableWebSecurity +@EnableMethodSecurity +@Profile(value = {"!test"}) +public class SecurityConfig { + + @Autowired + private LocalAuthenticationEntryPoint localAuthenticationEntryPoint; + + @Value("${server.servlet.path}") + private String servletPath; + + @Value("#{${mosip.certify.security.auth.post-urls}}") + private Map> securePostUrls; + + @Value("#{${mosip.certify.security.auth.put-urls}}") + private Map> securePutUrls; + + @Value("#{${mosip.certify.security.auth.get-urls}}") + private Map> secureGetUrls; + + @Value("${mosip.certify.authn.jwk-set-uri}") + private String jwkSetUri; + + @Value("${mosip.certify.security.ignore-auth-urls}") + private String[] ignoreAuthUrls; + + @Value("${mosip.certify.security.ignore-csrf-urls}") + private String[] ignoreCsrfCheckUrls; + + @Bean + public SecurityFilterChain web(HttpSecurity http) throws Exception { + + http.csrf(httpEntry -> httpEntry.ignoringRequestMatchers(ignoreCsrfCheckUrls) + .csrfTokenRepository(this.getCsrfTokenRepository())); + + http.authorizeHttpRequests(authorizeRequests -> authorizeRequests + .requestMatchers(ignoreAuthUrls).permitAll() + .anyRequest().authenticated() + ).oauth2ResourceServer(oauth2 -> oauth2 + .jwt(jwt -> jwt + .jwkSetUri(jwkSetUri) + ) + ); + http.exceptionHandling(exceptionConfigurer -> exceptionConfigurer.authenticationEntryPoint(localAuthenticationEntryPoint)); + http.sessionManagement(sessionConfigurer -> sessionConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + return http.build(); + } + + private CsrfTokenRepository getCsrfTokenRepository() { + CookieCsrfTokenRepository cookieCsrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse(); + cookieCsrfTokenRepository.setCookiePath("/"); + return cookieCsrfTokenRepository; + } + +} diff --git a/certify-service/src/main/java/io/mosip/certify/controller/VCIssuanceController.java b/certify-service/src/main/java/io/mosip/certify/controller/VCIssuanceController.java new file mode 100644 index 00000000..f7d6b32e --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/controller/VCIssuanceController.java @@ -0,0 +1,68 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.controller; + +import io.mosip.certify.core.dto.CredentialRequest; +import io.mosip.certify.core.dto.CredentialResponse; +import io.mosip.certify.core.dto.VCError; +import io.mosip.certify.core.exception.CertifyException; +import io.mosip.certify.core.spi.VCIssuanceService; +import io.mosip.certify.exception.InvalidNonceException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import jakarta.validation.Valid; +import java.util.Locale; +import java.util.Map; + +@Slf4j +@RestController +@RequestMapping("/issuance") +public class VCIssuanceController { + + @Autowired + private VCIssuanceService vcIssuanceService; + + @Autowired + MessageSource messageSource; + + /** + * 1. The credential Endpoint MUST accept Access Tokens + * @param credentialRequest VC credential request + * @return Credential Response w.r.t requested format + * @throws CertifyException + */ + @PostMapping(value = "/credential",produces = "application/json") + public CredentialResponse getCredential(@Valid @RequestBody CredentialRequest credentialRequest) throws CertifyException { + return vcIssuanceService.getCredential(credentialRequest); + } + + /** + * Open endpoint to provide VC issuer's metadata + * @return + */ + @GetMapping(value = "/.well-known/openid-credential-issuer",produces = "application/json") + public Map getMetadata( + @RequestParam(name = "version", required = false, defaultValue = "latest") String version) { + return vcIssuanceService.getCredentialIssuerMetadata(version); + } + + + @ResponseBody + @ExceptionHandler(InvalidNonceException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public VCError invalidNonceExceptionHandler(InvalidNonceException ex) { + VCError vcError = new VCError(); + vcError.setError(ex.getErrorCode()); + vcError.setError_description(messageSource.getMessage(ex.getErrorCode(), null, ex.getErrorCode(), Locale.getDefault())); + vcError.setC_nonce(ex.getClientNonce()); + vcError.setC_nonce_expires_in(ex.getClientNonceExpireSeconds()); + return vcError; + } +} diff --git a/certify-service/src/main/java/io/mosip/certify/exception/InvalidNonceException.java b/certify-service/src/main/java/io/mosip/certify/exception/InvalidNonceException.java new file mode 100644 index 00000000..e9302b02 --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/exception/InvalidNonceException.java @@ -0,0 +1,30 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.exception; + +import io.mosip.certify.core.constants.ErrorConstants; +import io.mosip.certify.core.exception.CertifyException; + +public class InvalidNonceException extends CertifyException { + + private String clientNonce; + + private int clientNonceExpireSeconds; + + public InvalidNonceException(String cNonce, int cNonceExpireSeconds) { + super(ErrorConstants.INVALID_PROOF); + this.clientNonce = cNonce; + this.clientNonceExpireSeconds = cNonceExpireSeconds; + } + + public String getClientNonce() { + return this.clientNonce; + } + + public int getClientNonceExpireSeconds() { + return this.clientNonceExpireSeconds; + } +} diff --git a/certify-service/src/main/java/io/mosip/certify/filter/AccessTokenValidationFilter.java b/certify-service/src/main/java/io/mosip/certify/filter/AccessTokenValidationFilter.java new file mode 100644 index 00000000..d1813611 --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/filter/AccessTokenValidationFilter.java @@ -0,0 +1,107 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.filter; + +import io.mosip.certify.core.constants.Constants; +import io.mosip.certify.core.dto.ParsedAccessToken; +import io.mosip.certify.core.util.CommonUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator; +import org.springframework.security.oauth2.jwt.*; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.time.Clock; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; + + +@Slf4j +@Component +public class AccessTokenValidationFilter extends OncePerRequestFilter { + + @Value("${mosip.certify.authn.issuer-uri}") + private String issuerUri; + + @Value("${mosip.certify.authn.jwk-set-uri}") + private String jwkSetUri; + + @Value("#{${mosip.certify.authn.allowed-audiences}}") + private List allowedAudiences; + + @Value("#{${mosip.certify.authn.filter-urls}}") + private List urlPatterns; + + @Autowired + private ParsedAccessToken parsedAccessToken; + + private NimbusJwtDecoder nimbusJwtDecoder; + + + private boolean isJwt(String token) { + return token.split("\\.").length == 3; + } + + private NimbusJwtDecoder getNimbusJwtDecoder() { + if(nimbusJwtDecoder == null) { + nimbusJwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build(); + nimbusJwtDecoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>( + new JwtTimestampValidator(), + new JwtIssuerValidator(issuerUri), + new JwtClaimValidator>(JwtClaimNames.AUD, allowedAudiences::containsAll), + new JwtClaimValidator(JwtClaimNames.SUB, Objects::nonNull), + new JwtClaimValidator(Constants.CLIENT_ID, Objects::nonNull), + new JwtClaimValidator(JwtClaimNames.IAT, + iat -> iat != null && iat.isBefore(Instant.now(Clock.systemUTC()))), + new JwtClaimValidator(JwtClaimNames.EXP, + exp -> exp != null && exp.isAfter(Instant.now(Clock.systemUTC()))))); + } + return nimbusJwtDecoder; + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + final String path = request.getRequestURI(); + return !urlPatterns.contains(path); + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String authorizationHeader = request.getHeader("Authorization"); + + if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { + String token = authorizationHeader.substring(7); + //validate access token no matter if its JWT or Opaque + if(isJwt(token)) { + try { + //Verifies signature and claim predicates, If invalid throws exception + Jwt jwt = getNimbusJwtDecoder().decode(token); + parsedAccessToken.setClaims(new HashMap<>()); + parsedAccessToken.getClaims().putAll(jwt.getClaims()); + parsedAccessToken.setAccessTokenHash(CommonUtil.generateOIDCAtHash(token)); + parsedAccessToken.setActive(true); + filterChain.doFilter(request, response); + return; + + } catch (Exception e) { + log.error("Access token validation failed", e); + } + } + } + + log.error("No Bearer / Opaque token provided, continue with the request chain"); + parsedAccessToken.setActive(false); + filterChain.doFilter(request, response); + } +} diff --git a/certify-service/src/main/java/io/mosip/certify/proof/JwtProofValidator.java b/certify-service/src/main/java/io/mosip/certify/proof/JwtProofValidator.java new file mode 100644 index 00000000..bc6cef74 --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/proof/JwtProofValidator.java @@ -0,0 +1,172 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.proof; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JOSEObjectType; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.jwk.source.ImmutableJWKSet; +import com.nimbusds.jose.proc.BadJOSEException; +import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier; +import com.nimbusds.jose.proc.JWSKeySelector; +import com.nimbusds.jose.proc.JWSVerificationKeySelector; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.JWTParser; +import com.nimbusds.jwt.SignedJWT; +import com.nimbusds.jwt.proc.ConfigurableJWTProcessor; +import com.nimbusds.jwt.proc.DefaultJWTClaimsVerifier; +import com.nimbusds.jwt.proc.DefaultJWTProcessor; +import io.mosip.certify.core.constants.ErrorConstants; +import io.mosip.certify.core.dto.CredentialProof; +import io.mosip.certify.core.exception.InvalidRequestException; +import lombok.extern.slf4j.Slf4j; +import org.json.JSONException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.nio.charset.StandardCharsets; +import java.text.ParseException; +import java.util.*; + +@Slf4j +@Component +public class JwtProofValidator implements ProofValidator { + + private static final String HEADER_TYP = "openid4vci-proof+jwt"; + private static final String DID_JWK_PREFIX = "did:jwk:"; + + @Value("#{${mosip.certify.supported.jwt-proof-alg}}") + private List supportedAlgorithms; + + @Value("${mosip.certify.identifier}") + private String credentialIdentifier; + + @Override + public String getProofType() { + return "jwt"; + } + + private static final Set allowedSignatureAlgorithms; + + private static Set REQUIRED_CLAIMS; + + static { + allowedSignatureAlgorithms = new HashSet<>(); + allowedSignatureAlgorithms.addAll(List.of(JWSAlgorithm.Family.SIGNATURE.toArray(new JWSAlgorithm[0]))); + + REQUIRED_CLAIMS = new HashSet<>(); + REQUIRED_CLAIMS.add("aud"); + REQUIRED_CLAIMS.add("exp"); + REQUIRED_CLAIMS.add("iss"); + REQUIRED_CLAIMS.add("iat"); + } + + @Override + public boolean validate(String clientId, String cNonce, CredentialProof credentialProof) { + if(credentialProof.getJwt() == null || credentialProof.getJwt().isBlank()) { + log.error("Found invalid jwt in the credential proof"); + return false; + } + + try { + SignedJWT jwt = (SignedJWT) JWTParser.parse(credentialProof.getJwt()); + validateHeaderClaims(jwt.getHeader()); + + JWK jwk = getKeyFromHeader(jwt.getHeader()); + if(jwk.isPrivate()) { + log.error("Provided key material contains private key! Rejecting proof."); + throw new InvalidRequestException(ErrorConstants.PROOF_HEADER_INVALID_KEY); + } + + DefaultJWTClaimsVerifier claimsSetVerifier = new DefaultJWTClaimsVerifier(new JWTClaimsSet.Builder() + .audience(credentialIdentifier) + .issuer(clientId) + .claim("nonce", cNonce) + .build(), REQUIRED_CLAIMS); + claimsSetVerifier.setMaxClockSkew(0); + + JWSKeySelector keySelector = new JWSVerificationKeySelector(allowedSignatureAlgorithms, + new ImmutableJWKSet(new JWKSet(jwk))); + ConfigurableJWTProcessor jwtProcessor = new DefaultJWTProcessor(); + jwtProcessor.setJWSKeySelector(keySelector); + jwtProcessor.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier(new JOSEObjectType(HEADER_TYP))); + jwtProcessor.setJWTClaimsSetVerifier(claimsSetVerifier); + jwtProcessor.process(credentialProof.getJwt(), null); + return true; + } catch (InvalidRequestException e) { + log.error("Invalid proof : {}", e.getErrorCode()); + } catch (ParseException e) { + log.error("Failed to parse jwt in the credential proof", e); + } catch (BadJOSEException | JOSEException e) { + log.error("JWT proof verification failed", e); + } + return false; + } + + @Override + public String getKeyMaterial(CredentialProof credentialProof) { + try { + SignedJWT jwt = (SignedJWT) JWTParser.parse(credentialProof.getJwt()); + JWK jwk = getKeyFromHeader(jwt.getHeader()); + byte[] keyBytes = jwk.toJSONString().getBytes(StandardCharsets.UTF_8); + return DID_JWK_PREFIX.concat(Base64.getUrlEncoder().encodeToString(keyBytes)); + } catch (ParseException e) { + log.error("Failed to parse jwt in the credential proof", e); + } + throw new InvalidRequestException(ErrorConstants.PROOF_HEADER_INVALID_KEY); + } + + private void validateHeaderClaims(JWSHeader jwsHeader) { + if(Objects.isNull(jwsHeader.getType()) || !HEADER_TYP.equals(jwsHeader.getType().getType())) + throw new InvalidRequestException(ErrorConstants.PROOF_HEADER_INVALID_TYP); + + if(Objects.isNull(jwsHeader.getAlgorithm()) || !supportedAlgorithms.contains(jwsHeader.getAlgorithm().getName())) + throw new InvalidRequestException(ErrorConstants.PROOF_HEADER_INVALID_ALG); + + if(Objects.isNull(jwsHeader.getKeyID()) && Objects.isNull(jwsHeader.getJWK())) + throw new InvalidRequestException(ErrorConstants.PROOF_HEADER_INVALID_KEY); + + //both cannot be present, either one of them is only allowed + if(Objects.nonNull(jwsHeader.getKeyID()) && Objects.nonNull(jwsHeader.getJWK())) + throw new InvalidRequestException(ErrorConstants.PROOF_HEADER_AMBIGUOUS_KEY); + + //TODO x5c and trust_chain validation + } + + private JWK getKeyFromHeader(JWSHeader jwsHeader) { + if(Objects.nonNull(jwsHeader.getJWK())) + return jwsHeader.getJWK(); + + return resolveDID(jwsHeader.getKeyID()); + } + + /** + * Currently only handles did:jwk, Need to handle other methods + * @param keyId + * @return + */ + private JWK resolveDID(String did) { + if(did.startsWith(DID_JWK_PREFIX)) { + try { + //Ignoring fragment part as did:jwk only contains single key, the DID URL fragment identifier is always + //a fixed #0 value. If the JWK contains a kid value it is not used as the reference, #0 is the only valid value. + did = did.split("#")[0]; + byte[] jwkBytes = Base64.getUrlDecoder().decode(did.substring(DID_JWK_PREFIX.length())); + org.json.JSONObject jsonKey = new org.json.JSONObject(new String(jwkBytes)); + jsonKey.put("kid", did); + return JWK.parse(jsonKey.toString()); + } catch (IllegalArgumentException e) { + log.error("Invalid base64 encoded ID : {}", did, e); + } catch (ParseException | JSONException e) { + log.error("Invalid jwk : {}", did, e); + } + } + throw new InvalidRequestException(ErrorConstants.PROOF_HEADER_INVALID_KEY); + } +} diff --git a/certify-service/src/main/java/io/mosip/certify/proof/ProofValidator.java b/certify-service/src/main/java/io/mosip/certify/proof/ProofValidator.java new file mode 100644 index 00000000..0062542a --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/proof/ProofValidator.java @@ -0,0 +1,33 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.proof; + +import io.mosip.certify.core.dto.CredentialProof; + +public interface ProofValidator { + + /** + * Proof type supported by the implementation class. + * @return Returns the supported proof type + */ + String getProofType(); + + /** + * Validates the input proof. + * @param clientId Client ID as in the bearer access token + * @param cNonce valid client nonce as generated by the server(authorization/VCI) + * @param credentialProof proof from the credential request. + * @return true if proof passes all the validation else false + */ + boolean validate(String clientId, String cNonce, CredentialProof credentialProof); + + /** + * Extracts the holders public key for VC cryptographic binding from the proof header + * @param credentialProof proof from the credential request. + * @return public key as did:jwk equivalent + */ + String getKeyMaterial(CredentialProof credentialProof); +} diff --git a/certify-service/src/main/java/io/mosip/certify/proof/ProofValidatorFactory.java b/certify-service/src/main/java/io/mosip/certify/proof/ProofValidatorFactory.java new file mode 100644 index 00000000..6caff60d --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/proof/ProofValidatorFactory.java @@ -0,0 +1,33 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.proof; + +import io.mosip.certify.core.constants.ErrorConstants; +import io.mosip.certify.core.exception.CertifyException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Optional; + +@Component +public class ProofValidatorFactory { + + @Autowired + private List proofValidators; + + public ProofValidator getProofValidator(String proofType) { + Optional result = proofValidators.stream() + .filter(v -> v.getProofType().equals(proofType)) + .findFirst(); + + if(result.isPresent()) + return result.get(); + + throw new CertifyException(ErrorConstants.UNSUPPORTED_PROOF_TYPE); + } + +} diff --git a/certify-service/src/main/java/io/mosip/certify/services/VCICacheService.java b/certify-service/src/main/java/io/mosip/certify/services/VCICacheService.java new file mode 100644 index 00000000..225919eb --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/services/VCICacheService.java @@ -0,0 +1,33 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.services; + +import io.mosip.certify.core.dto.VCIssuanceTransaction; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.CachePut; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class VCICacheService { + + @Autowired + private CacheManager cacheManager; + + private static final String VCISSUANCE_CACHE = "vcissuance"; + + @CachePut(value = VCISSUANCE_CACHE, key = "#accessTokenHash") + public VCIssuanceTransaction setVCITransaction(String accessTokenHash, VCIssuanceTransaction vcIssuanceTransaction) { + return vcIssuanceTransaction; + } + + public VCIssuanceTransaction getVCITransaction(String accessTokenHash) { + return cacheManager.getCache(VCISSUANCE_CACHE).get(accessTokenHash, VCIssuanceTransaction.class); //NOSONAR getCache() will not be returning null here. + } +} + diff --git a/certify-service/src/main/java/io/mosip/certify/services/VCIssuanceServiceImpl.java b/certify-service/src/main/java/io/mosip/certify/services/VCIssuanceServiceImpl.java new file mode 100644 index 00000000..f34503a6 --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/services/VCIssuanceServiceImpl.java @@ -0,0 +1,243 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.services; + +import foundation.identity.jsonld.JsonLDObject; + +import io.mosip.certify.api.dto.VCRequestDto; +import io.mosip.certify.api.dto.VCResult; +import io.mosip.certify.api.exception.VCIExchangeException; +import io.mosip.certify.api.spi.AuditPlugin; +import io.mosip.certify.api.spi.VCIssuancePlugin; +import io.mosip.certify.api.util.Action; +import io.mosip.certify.api.util.ActionStatus; +import io.mosip.certify.core.dto.CredentialMetadata; +import io.mosip.certify.core.dto.CredentialRequest; +import io.mosip.certify.core.dto.CredentialResponse; +import io.mosip.certify.core.dto.ParsedAccessToken; +import io.mosip.certify.core.dto.VCIssuanceTransaction; +import io.mosip.certify.core.constants.Constants; +import io.mosip.certify.core.constants.ErrorConstants; +import io.mosip.certify.core.exception.CertifyException; +import io.mosip.certify.core.exception.InvalidRequestException; +import io.mosip.certify.core.exception.NotAuthenticatedException; +import io.mosip.certify.core.spi.VCIssuanceService; +import io.mosip.certify.core.util.AuditHelper; +import io.mosip.certify.core.util.SecurityHelperService; +import io.mosip.certify.exception.InvalidNonceException; +import io.mosip.certify.proof.ProofValidator; +import io.mosip.certify.proof.ProofValidatorFactory; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.oauth2.jwt.JwtClaimNames; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@Slf4j +@Service +public class VCIssuanceServiceImpl implements VCIssuanceService { + + private static final String TYPE_VERIFIABLE_CREDENTIAL = "VerifiableCredential"; + + @Value("#{${mosip.certify.key-values}}") + private LinkedHashMap> issuerMetadata; + + @Value("${mosip.certify.cnonce-expire-seconds:300}") + private int cNonceExpireSeconds; + + @Autowired + private ParsedAccessToken parsedAccessToken; + + @Autowired + private VCIssuancePlugin vcIssuancePlugin; + + @Autowired + private ProofValidatorFactory proofValidatorFactory; + + @Autowired + private VCICacheService vciCacheService; + + @Autowired + private SecurityHelperService securityHelperService; + + @Autowired + private AuditPlugin auditWrapper; + + private LinkedHashMap supportedCredentials; + + + @Override + public CredentialResponse getCredential(CredentialRequest credentialRequest) { + if(!parsedAccessToken.isActive()) + throw new NotAuthenticatedException(); + + String scopeClaim = (String) parsedAccessToken.getClaims().getOrDefault("scope", ""); + CredentialMetadata credentialMetadata = null; + for(String scope : scopeClaim.split(Constants.SPACE)) { + Optional result = getScopeCredentialMapping(scope); + if(result.isPresent()) { + credentialMetadata = result.get(); //considering only first credential scope + break; + } + } + + if(credentialMetadata == null) { + log.error("No credential mapping found for the provided scope {}", scopeClaim); + throw new CertifyException(ErrorConstants.INVALID_SCOPE); + } + + ProofValidator proofValidator = proofValidatorFactory.getProofValidator(credentialRequest.getProof().getProof_type()); + if(!proofValidator.validate((String)parsedAccessToken.getClaims().get(Constants.CLIENT_ID), getValidClientNonce(), + credentialRequest.getProof())) { + throw new CertifyException(ErrorConstants.INVALID_PROOF); + } + + //Get VC from configured plugin implementation + VCResult vcResult = getVerifiableCredential(credentialRequest, credentialMetadata, + proofValidator.getKeyMaterial(credentialRequest.getProof())); + + auditWrapper.logAudit(Action.VC_ISSUANCE, ActionStatus.SUCCESS, + AuditHelper.buildAuditDto(parsedAccessToken.getAccessTokenHash(), "accessTokenHash"), null); + return getCredentialResponse(credentialRequest.getFormat(), vcResult); + } + + @Override + public Map getCredentialIssuerMetadata(String version) { + if(issuerMetadata.containsKey(version)) + return issuerMetadata.get(version); + return issuerMetadata.get("latest"); + } + + private VCResult getVerifiableCredential(CredentialRequest credentialRequest, CredentialMetadata credentialMetadata, + String holderId) { + parsedAccessToken.getClaims().put("accessTokenHash", parsedAccessToken.getAccessTokenHash()); + VCRequestDto vcRequestDto = new VCRequestDto(); + vcRequestDto.setFormat(credentialRequest.getFormat()); + vcRequestDto.setContext(credentialRequest.getCredential_definition().getContext()); + vcRequestDto.setType(credentialRequest.getCredential_definition().getType()); + vcRequestDto.setCredentialSubject(credentialRequest.getCredential_definition().getCredentialSubject()); + + VCResult vcResult = null; + try { + switch (credentialRequest.getFormat()) { + case "ldp_vc" : + validateLdpVcFormatRequest(credentialRequest, credentialMetadata); + vcResult = vcIssuancePlugin.getVerifiableCredentialWithLinkedDataProof(vcRequestDto, holderId, + parsedAccessToken.getClaims()); + break; + + // jwt_vc_json & jwt_vc_json-ld cases are merged + case "jwt_vc_json-ld" : + case "jwt_vc_json" : + vcResult = vcIssuancePlugin.getVerifiableCredential(vcRequestDto, holderId, + parsedAccessToken.getClaims()); + break; + default: + throw new CertifyException(ErrorConstants.UNSUPPORTED_VC_FORMAT); + } + } catch (VCIExchangeException e) { + throw new CertifyException(e.getErrorCode()); + } + + if(vcResult != null && vcResult.getCredential() != null) + return vcResult; + + log.error("Failed to generate VC : {}", vcResult); + auditWrapper.logAudit(Action.VC_ISSUANCE, ActionStatus.ERROR, + AuditHelper.buildAuditDto(parsedAccessToken.getAccessTokenHash(), "accessTokenHash"), null); + throw new CertifyException(ErrorConstants.VC_ISSUANCE_FAILED); + } + + private CredentialResponse getCredentialResponse(String format, VCResult vcResult) { + switch (format) { + case "ldp_vc": + CredentialResponse ldpVcResponse = new CredentialResponse<>(); + ldpVcResponse.setCredential((JsonLDObject)vcResult.getCredential()); + ldpVcResponse.setFormat(vcResult.getFormat()); + return ldpVcResponse; + + case "jwt_vc_json-ld": + case "jwt_vc_json": + CredentialResponse jsonResponse = new CredentialResponse<>(); + jsonResponse.setCredential((String)vcResult.getCredential()); + jsonResponse.setFormat(vcResult.getFormat()); + return jsonResponse; + } + throw new CertifyException(ErrorConstants.UNSUPPORTED_VC_FORMAT); + } + + private Optional getScopeCredentialMapping(String scope) { + LinkedHashMap vciMetadata = issuerMetadata.get("latest"); + if(supportedCredentials == null) { + supportedCredentials = (LinkedHashMap) vciMetadata.get("credentials_supported"); + } + + Optional> result = supportedCredentials.entrySet().stream() + .filter(cm -> ((LinkedHashMap)cm.getValue()).get("scope").equals(scope)).findFirst(); + + if(result.isPresent()) { + LinkedHashMap metadata = (LinkedHashMap)result.get().getValue(); + CredentialMetadata credentialMetadata = new CredentialMetadata(); + credentialMetadata.setFormat((String) metadata.get("format")); + credentialMetadata.setProof_types_supported((List) metadata.get("proof_types_supported")); + credentialMetadata.setScope((String) metadata.get("scope")); + credentialMetadata.setId(result.get().getKey()); + + LinkedHashMap credentialDefinition = (LinkedHashMap) metadata.get("credential_definition"); + credentialMetadata.setTypes((List) credentialDefinition.get("type")); + return Optional.of(credentialMetadata); + } + return Optional.empty(); + } + + private void validateLdpVcFormatRequest(CredentialRequest credentialRequest, + CredentialMetadata credentialMetadata) { + if(!credentialRequest.getCredential_definition().getType().containsAll(credentialMetadata.getTypes())) + throw new InvalidRequestException(ErrorConstants.UNSUPPORTED_VC_TYPE); + + //TODO need to validate Credential_definition as JsonLD document, if invalid throw exception + } + + private String getValidClientNonce() { + VCIssuanceTransaction transaction = vciCacheService.getVCITransaction(parsedAccessToken.getAccessTokenHash()); + //If the transaction is null, it means that VCI service never created cNonce, its authorization server issued cNonce + String cNonce = (transaction == null) ? + (String) parsedAccessToken.getClaims().get(Constants.C_NONCE) : + transaction.getCNonce(); + Object nonceExpireSeconds = parsedAccessToken.getClaims().getOrDefault(Constants.C_NONCE_EXPIRES_IN, 0); + int cNonceExpire = (transaction == null) ? + nonceExpireSeconds instanceof Long ? (int)(long)nonceExpireSeconds : (int)nonceExpireSeconds : + transaction.getCNonceExpireSeconds(); + long issuedEpoch = (transaction == null) ? + ((Instant) parsedAccessToken.getClaims().getOrDefault(JwtClaimNames.IAT, Instant.MIN)).getEpochSecond(): + transaction.getCNonceIssuedEpoch(); + + if( cNonce == null || + cNonceExpire <= 0 || + (issuedEpoch+cNonceExpire) < LocalDateTime.now(ZoneOffset.UTC).toEpochSecond(ZoneOffset.UTC) ) { + log.error("Client Nonce not found / expired in the access token, generate new cNonce"); + transaction = createVCITransaction(); + throw new InvalidNonceException(transaction.getCNonce(), transaction.getCNonceExpireSeconds()); + } + return cNonce; + } + + private VCIssuanceTransaction createVCITransaction() { + VCIssuanceTransaction transaction = new VCIssuanceTransaction(); + transaction.setCNonce(securityHelperService.generateSecureRandomString(20)); + transaction.setCNonceIssuedEpoch(LocalDateTime.now(ZoneOffset.UTC).toEpochSecond(ZoneOffset.UTC)); + transaction.setCNonceExpireSeconds(cNonceExpireSeconds); + return vciCacheService.setVCITransaction(parsedAccessToken.getAccessTokenHash(), transaction); + } +} \ No newline at end of file diff --git a/certify-service/src/main/resources/application-local.properties b/certify-service/src/main/resources/application-local.properties new file mode 100644 index 00000000..e781c047 --- /dev/null +++ b/certify-service/src/main/resources/application-local.properties @@ -0,0 +1,184 @@ + +## -------------------------------------- Authentication & Authorization ----------------------------------------------- + +mosip.certify.security.auth.post-urls={} +mosip.certify.security.auth.put-urls={} +mosip.certify.security.auth.get-urls={} + +mosip.certify.security.ignore-csrf-urls=**/actuator/**,/favicon.ico,**/error,\ + **/swagger-ui/**,**/v3/api-docs/**,\ + **/issuance/** + +mosip.certify.security.ignore-auth-urls=**/actuator/**,**/error,**/swagger-ui/**,\ + **/v3/api-docs/**, **/issuance/** + + +## ------------------------------------------ Discovery openid-configuration ------------------------------------------- +mosipbox.public.url=http://localhost:8090 +mosip.certify.discovery.issuer-id=${mosipbox.public.url}${server.servlet.path} + +##--------------change this later--------------------------------- +mosip.certify.supported.jwt-proof-alg={'RS256','PS256'} + +## ---------------------------------------------- VCI ------------------------------------------------------------------ +##----- These are properties for any oauth resource server providing jwk------------### +mosip.certify.identifier=http://localhost:8088 +mosip.certify.authn.filter-urls={ '${server.servlet.path}/issuance/credential' } +mosip.certify.authn.issuer-uri=http://localhost:8088/v1/esignet +mosip.certify.authn.jwk-set-uri=http://localhost:8088/v1/esignet/oauth/.well-known/jwks.json +mosip.certify.authn.allowed-audiences={ '${mosipbox.public.url}${server.servlet.path}/issuance/credential', 'http://localhost:8088/v1/esignet/vci/credential' } + +mosip.certify.key-values={\ + 'v11' : {\ + 'credential_issuer': '${mosipbox.public.url}', \ + 'credential_endpoint': '${mosipbox.public.url}${server.servlet.path}/issuance/credential', \ + 'display': {{'name': 'Insurance', 'locale': 'en'}},\ + 'credentials_supported': {{\ + 'format': 'ldp_vc',\ + 'id': 'InsuranceCredential', \ + 'scope' : 'sunbird_rc_insurance_vc_ldp',\ + 'cryptographic_binding_methods_supported': {'did:jwk'},\ + 'cryptographic_suites_supported': {'Ed25519Signature2020'},\ + 'proof_types_supported': {'jwt'},\ + 'credential_definition': {\ + 'type': {'VerifiableCredential','InsuranceCredential'},\ + 'credentialSubject': {\ + 'fullName': {'display': {{'name': 'Name','locale': 'en'}}}, \ + 'mobile': {'display': {{'name': 'Phone Number','locale': 'en'}}},\ + 'dob': {'display': {{'name': 'Date of Birth','locale': 'en'}}},\ + 'gender': {'display': {{'name': 'Gender','locale': 'en'}}},\ + 'benefits': {'display': {{'name': 'Benefits','locale': 'en'}}},\ + 'email': {'display': {{'name': 'Email Id','locale': 'en'}}},\ + 'policyIssuedOn': {'display': {{'name': 'Policy Issued On','locale': 'en'}}},\ + 'policyExpiresOn': {'display': {{'name': 'Policy Expires On','locale': 'en'}}},\ + 'policyName': {'display': {{'name': 'Policy Name','locale': 'en'}}},\ + 'policyNumber': {'display': {{'name': 'Policy Number','locale': 'en'}}}\ + }},\ + 'display': {{'name': 'Sunbird RC Insurance Verifiable Credential', \ + 'locale': 'en', \ + 'logo': {'url': 'https://sunbird.org/images/sunbird-logo-new.png', 'alt_text': 'a square logo of a Sunbird'},\ + 'background_color': '#FDFAF9',\ + 'text_color': '#7C4616'}},\ + 'order' : {'fullName','policyName','policyExpiresOn','policyIssuedOn','policyNumber','mobile','dob','gender','benefits','email'}\ + },\ + {\ + 'format': 'ldp_vc',\ + 'id': 'LifeInsuranceCredential', \ + 'scope' : 'life_insurance_vc_ldp',\ + 'cryptographic_binding_methods_supported': {'did:jwk'},\ + 'cryptographic_suites_supported': {'Ed25519Signature2020'},\ + 'proof_types_supported': {'jwt'},\ + 'credential_definition': {\ + 'type': {'VerifiableCredential'},\ + 'credentialSubject': {\ + 'fullName': {'display': {{'name': 'Name','locale': 'en'}}}, \ + 'mobile': {'display': {{'name': 'Phone Number','locale': 'en'}}},\ + 'dob': {'display': {{'name': 'Date of Birth','locale': 'en'}}},\ + 'gender': {'display': {{'name': 'Gender','locale': 'en'}}},\ + 'benefits': {'display': {{'name': 'Benefits','locale': 'en'}}},\ + 'email': {'display': {{'name': 'Email Id','locale': 'en'}}},\ + 'policyIssuedOn': {'display': {{'name': 'Policy Issued On','locale': 'en'}}},\ + 'policyExpiresOn': {'display': {{'name': 'Policy Expires On','locale': 'en'}}},\ + 'policyName': {'display': {{'name': 'Policy Name','locale': 'en'}}},\ + 'policyNumber': {'display': {{'name': 'Policy Number','locale': 'en'}}}\ + }},\ + 'display': {{'name': 'Life Insurance Verifiable Credential', \ + 'locale': 'en', \ + 'logo': {'url': 'https://sunbird.org/images/sunbird-logo-new.png','alt_text': 'a square logo of a Sunbird'},\ + 'background_color': '#FDFAF9',\ + 'text_color': '#7C4616'}},\ + 'order' : {'fullName','policyName','policyExpiresOn','policyIssuedOn','policyNumber','mobile','dob','gender','benefits','email'}\ + }}\ + },\ + 'latest' : {\ + 'credential_issuer': '${mosipbox.public.url}', \ + 'credential_endpoint': '${mosipbox.public.url}${server.servlet.path}/issuance/credential', \ + 'display': {{'name': 'Insurance', 'locale': 'en'}},\ + 'credentials_supported' : { \ + "InsuranceCredential" : {\ + 'format': 'ldp_vc',\ + 'scope' : 'sunbird_rc_insurance_vc_ldp',\ + 'cryptographic_binding_methods_supported': {'did:jwk'},\ + 'cryptographic_suites_supported': {'Ed25519Signature2020'},\ + 'proof_types_supported': {'jwt'},\ + 'credential_definition': {\ + 'type': {'VerifiableCredential','InsuranceCredential'},\ + 'credentialSubject': {\ + 'fullName': {'display': {{'name': 'Name','locale': 'en'}}}, \ + 'mobile': {'display': {{'name': 'Phone Number','locale': 'en'}}},\ + 'dob': {'display': {{'name': 'Date of Birth','locale': 'en'}}},\ + 'gender': {'display': {{'name': 'Gender','locale': 'en'}}},\ + 'benefits': {'display': {{'name': 'Benefits','locale': 'en'}}},\ + 'email': {'display': {{'name': 'Email Id','locale': 'en'}}},\ + 'policyIssuedOn': {'display': {{'name': 'Policy Issued On','locale': 'en'}}},\ + 'policyExpiresOn': {'display': {{'name': 'Policy Expires On','locale': 'en'}}},\ + 'policyName': {'display': {{'name': 'Policy Name','locale': 'en'}}},\ + 'policyNumber': {'display': {{'name': 'Policy Number','locale': 'en'}}}\ + }},\ + 'display': {{'name': 'Sunbird RC Insurance Verifiable Credential', \ + 'locale': 'en', \ + 'logo': {'url': 'https://sunbird.org/images/sunbird-logo-new.png','alt_text': 'a square logo of a Sunbird'},\ + 'background_color': '#FDFAF9',\ + 'text_color': '#7C4616'}},\ + 'order' : {'fullName','policyName','policyExpiresOn','policyIssuedOn','policyNumber','mobile','dob','gender','benefits','email'}\ + },\ + "LifeInsuranceCredential":{\ + 'format': 'ldp_vc',\ + 'scope' : 'life_insurance_vc_ldp',\ + 'cryptographic_binding_methods_supported': {'did:jwk'},\ + 'cryptographic_suites_supported': {'Ed25519Signature2020'},\ + 'proof_types_supported': {'jwt'},\ + 'credential_definition': {\ + 'type': {'VerifiableCredential'},\ + 'credentialSubject': {\ + 'fullName': {'display': {{'name': 'Name','locale': 'en'}}}, \ + 'mobile': {'display': {{'name': 'Phone Number','locale': 'en'}}},\ + 'dob': {'display': {{'name': 'Date of Birth','locale': 'en'}}},\ + 'gender': {'display': {{'name': 'Gender','locale': 'en'}}},\ + 'benefits': {'display': {{'name': 'Benefits','locale': 'en'}}},\ + 'email': {'display': {{'name': 'Email Id','locale': 'en'}}},\ + 'policyIssuedOn': {'display': {{'name': 'Policy Issued On','locale': 'en'}}},\ + 'policyExpiresOn': {'display': {{'name': 'Policy Expires On','locale': 'en'}}},\ + 'policyName': {'display': {{'name': 'Policy Name','locale': 'en'}}},\ + 'policyNumber': {'display': {{'name': 'Policy Number','locale': 'en'}}}\ + }},\ + 'display': {{'name': 'Life Insurance Verifiable Credential', \ + 'locale': 'en', \ + 'logo': {'url': 'https://sunbird.org/images/sunbird-logo-new.png','alt_text': 'a square logo of a Sunbird'},\ + 'background_color': '#FDFAF9',\ + 'text_color': '#7C4616'}},\ + 'order' : {'fullName','policyName','policyExpiresOn','policyIssuedOn','policyNumber','mobile','dob','gender','benefits','email'}\ + }}\ + }\ +} + +## ------------------------------------------- Integrations ------------------------------------------------------------ +mosip.certify.integration.scan-base-package=io.mosip.certify.sunbirdrc.integration +mosip.certify.integration.vci-plugin=SunbirdRCVCIssuancePlugin +mosip.certify.integration.audit-plugin=LoggerAuditService + +##-----------------------------VCI related demo configuration---------------------------------------------## + +mosip.certify.vciplugin.sunbird-rc.issue-credential-url=http://localhost:8000/credential/credentials/issue +mosip.certify.vciplugin.sunbird-rc.supported-credential-types=HealthInsuranceCredential,LifeInsuranceCredential,InsuranceCredential +mosip.certify.vciplugin.sunbird-rc.credential-type.HealthInsuranceCredential.static-value-map.issuerId=did:web:challabeehyv.github.io:DID-Resolve:65d90545-878a-4807-9eba-1f56446e7926 +mosip.certify.vciplugin.sunbird-rc.credential-type.HealthInsuranceCredential.template-url=https://raw.githubusercontent.com/challabeehyv/mimoto-config/main/InsuranceConfig.json +mosip.certify.vciplugin.sunbird-rc.credential-type.HealthInsuranceCredential.registry-get-url=http://localhost:8000/registry/api/v1/Insurance/ +mosip.certify.vciplugin.sunbird-rc.credential-type.HealthInsuranceCredential.cred-schema-id=did:schema:77ea2b1b-f0aa-4214-acb5-2f3b93bc7ee7 +mosip.certify.vciplugin.sunbird-rc.credential-type.HealthInsuranceCredential.cred-schema-version=1.0.0 +mosip.certify.vciplugin.sunbird-rc.credential-type.HealthInsuranceCredential.registry-search-url=http://localhost:8000/registry/api/v1/Insurance/search + +mosip.certify.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.static-value-map.issuerId=did:web:challabeehyv.github.io:DID-Resolve:65d90545-878a-4807-9eba-1f56446e7926 +mosip.certify.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.template-url=https://raw.githubusercontent.com/challabeehyv/mimoto-config/main/InsuranceConfig.json +mosip.certify.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.registry-get-url=http://localhost:8000/registry/api/v1/Insurance/ +mosip.certify.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.cred-schema-id=did:schema:77ea2b1b-f0aa-4214-acb5-2f3b93bc7ee7 +mosip.certify.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.cred-schema-version=1.0.0 +mosip.certify.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.registry-search-url=http://localhost:8000/registry/api/v1/Insurance/search + + +mosip.certify.vciplugin.sunbird-rc.credential-type.InsuranceCredential.static-value-map.issuerId=did:web:challabeehyv.github.io:DID-Resolve:65d90545-878a-4807-9eba-1f56446e7926 +mosip.certify.vciplugin.sunbird-rc.credential-type.InsuranceCredential.template-url=https://raw.githubusercontent.com/challabeehyv/mimoto-config/main/InsuranceConfig.json +mosip.certify.vciplugin.sunbird-rc.credential-type.InsuranceCredential.registry-get-url=http://localhost:8000/registry/api/v1/Insurance/ +mosip.certify.vciplugin.sunbird-rc.credential-type.InsuranceCredential.cred-schema-id=did:schema:77ea2b1b-f0aa-4214-acb5-2f3b93bc7ee7 +mosip.certify.vciplugin.sunbird-rc.credential-type.InsuranceCredential.cred-schema-version=1.0.0 +mosip.certify.vciplugin.sunbird-rc.credential-type.InsuranceCredential.registry-search-url=http://localhost:8000/registry/api/v1/Insurance/search \ No newline at end of file diff --git a/certify-service/src/main/resources/bootstrap.properties b/certify-service/src/main/resources/bootstrap.properties new file mode 100644 index 00000000..db9a555f --- /dev/null +++ b/certify-service/src/main/resources/bootstrap.properties @@ -0,0 +1,31 @@ +# 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/. + +## Application Name +spring.application.name=certify +spring.cloud.config.uri=http://localhost:8888 +spring.profiles.active=local + +server.port=8090 +server.servlet.path=/v1/certify + +openapi.info.title=Certify Service +openapi.info.description=Rest Endpoints for operations related to certify +openapi.info.version=1.0 +openapi.info.license.name=Mosip +openapi.info.license.url=https://docs.mosip.io/platform/license +mosipbox.public.url=http://localhost:${server.port} +openapi.service.server.url=${mosipbox.public.url}${server.servlet.path} +openapi.service.server.description=Certify Service +springdoc.swagger-ui.disable-swagger-default-url=true +spring.mvc.servlet.path=${server.servlet.path} + +#logging.level.org.springframework.web=DEBUG +#logging.level.org.springframework.security=DEBUG + +spring.messages.basename=messages +spring.messages.encoding=UTF-8 + +spring.main.allow-bean-definition-overriding=true +spring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER \ No newline at end of file diff --git a/certify-service/src/test/java/io/mosip/certify/CertifyApplicationTests.java b/certify-service/src/test/java/io/mosip/certify/CertifyApplicationTests.java new file mode 100644 index 00000000..f26d5221 --- /dev/null +++ b/certify-service/src/test/java/io/mosip/certify/CertifyApplicationTests.java @@ -0,0 +1,16 @@ +package io.mosip.certify; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +public class CertifyApplicationTests { + + @Test + public void test() { + CertifyServiceApplication.main(new String[] {}); + Assertions.assertNotNull(CertifyServiceApplication.class); + } + +} \ No newline at end of file diff --git a/certify-service/src/test/java/io/mosip/certify/TestAuditPluginImpl.java b/certify-service/src/test/java/io/mosip/certify/TestAuditPluginImpl.java new file mode 100644 index 00000000..1fa6e75e --- /dev/null +++ b/certify-service/src/test/java/io/mosip/certify/TestAuditPluginImpl.java @@ -0,0 +1,27 @@ +package io.mosip.certify; + + +import io.mosip.certify.api.dto.AuditDTO; +import io.mosip.certify.api.spi.AuditPlugin; +import io.mosip.certify.api.util.Action; +import io.mosip.certify.api.util.ActionStatus; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + + +@ConditionalOnProperty(value = "mosip.certify.integration.audit-plugin", havingValue = "TestAuditPlugin") +@Component +@Slf4j +public class TestAuditPluginImpl implements AuditPlugin { + + @Override + public void logAudit(Action action, ActionStatus status, AuditDTO audit, Throwable t) { + //do nothing + } + + @Override + public void logAudit(String username, Action action, ActionStatus status, AuditDTO audit, Throwable t) { + //do nothing + } +} \ No newline at end of file diff --git a/certify-service/src/test/java/io/mosip/certify/TestVCIPluginImpl.java b/certify-service/src/test/java/io/mosip/certify/TestVCIPluginImpl.java new file mode 100644 index 00000000..9a4b1be7 --- /dev/null +++ b/certify-service/src/test/java/io/mosip/certify/TestVCIPluginImpl.java @@ -0,0 +1,28 @@ +package io.mosip.certify; + +import foundation.identity.jsonld.JsonLDObject; +import io.mosip.certify.api.dto.VCRequestDto; +import io.mosip.certify.api.dto.VCResult; +import io.mosip.certify.api.spi.VCIssuancePlugin; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@ConditionalOnProperty(value = "mosip.certify.integration.vci-plugin", havingValue = "TestVCIPluginImpl") +@Component +@Slf4j +public class TestVCIPluginImpl implements VCIssuancePlugin { + + @Override + public VCResult getVerifiableCredentialWithLinkedDataProof(VCRequestDto vcRequestDto, String holderId, + Map identityDetails) { + return new VCResult<>(); + } + + @Override + public VCResult getVerifiableCredential(VCRequestDto vcRequestDto, String holderId, Map identityDetails) { + return new VCResult<>(); + } +} \ No newline at end of file diff --git a/certify-service/src/test/java/io/mosip/certify/controller/VCIssuanceControllerTest.java b/certify-service/src/test/java/io/mosip/certify/controller/VCIssuanceControllerTest.java new file mode 100644 index 00000000..994caf2f --- /dev/null +++ b/certify-service/src/test/java/io/mosip/certify/controller/VCIssuanceControllerTest.java @@ -0,0 +1,195 @@ +package io.mosip.certify.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import foundation.identity.jsonld.JsonLDObject; +import io.mosip.certify.api.spi.AuditPlugin; +import io.mosip.certify.core.constants.ErrorConstants; +import io.mosip.certify.core.dto.*; +import io.mosip.certify.core.spi.VCIssuanceService; +import io.mosip.certify.exception.InvalidNonceException; +import io.mosip.certify.services.VCICacheService; +import org.junit.Test; +import org.junit.runner.RunWith; +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.Map; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@RunWith(SpringRunner.class) +@WebMvcTest(value = VCIssuanceController.class) +public class VCIssuanceControllerTest { + + ObjectMapper objectMapper = new ObjectMapper(); + + @Autowired + MockMvc mockMvc; + + @MockBean + AuditPlugin auditWrapper; + + + @MockBean + ParsedAccessToken parsedAccessToken; + + @MockBean + VCIssuanceService vcIssuanceService; + + @MockBean + VCICacheService vciCacheService; + + @Test + public void getIssuerMetadata_withValidDetails_thenPass() throws Exception { + Map issuerMetadata = new HashMap<>(); + issuerMetadata.put("credential_issuer", "https://localhost:9090"); + issuerMetadata.put("credential_endpoint", "https://localhost:9090/v1/certify/issuance/credential"); + issuerMetadata.put("credentials_supported", Arrays.asList()); + + Mockito.when(vcIssuanceService.getCredentialIssuerMetadata(Mockito.anyString())).thenReturn(issuerMetadata); + + mockMvc.perform(get("/issuance/.well-known/openid-credential-issuer")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.credential_issuer").exists()) + .andExpect(jsonPath("$.credential_issuer").exists()) + .andExpect(jsonPath("$.credentials_supported").exists()) + .andExpect(header().string("Content-Type", "application/json")); + + Mockito.verify(vcIssuanceService).getCredentialIssuerMetadata("latest"); + } + + @Test + public void getIssuerMetadata_withQueryParam_thenPass() throws Exception { + Map issuerMetadata = new HashMap<>(); + issuerMetadata.put("credential_issuer", "https://localhost:9090"); + issuerMetadata.put("credential_endpoint", "https://localhost:9090/v1/certify/issuance/credential"); + issuerMetadata.put("credentials_supported", Arrays.asList()); + + Mockito.when(vcIssuanceService.getCredentialIssuerMetadata(Mockito.anyString())).thenReturn(issuerMetadata); + + mockMvc.perform(get("/issuance/.well-known/openid-credential-issuer?version=v11")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.credential_issuer").exists()) + .andExpect(jsonPath("$.credential_issuer").exists()) + .andExpect(jsonPath("$.credentials_supported").exists()) + .andExpect(header().string("Content-Type", "application/json")); + + Mockito.verify(vcIssuanceService).getCredentialIssuerMetadata("v11"); + } + + @Test + public void getVerifiableCredential_withValidDetails_thenPass() throws Exception { + CredentialDefinition credentialDefinition = new CredentialDefinition(); + credentialDefinition.setType(Arrays.asList("VerifiableCredential", "SampleVerifiableCredential_ldp")); + credentialDefinition.setContext(Arrays.asList("https://www.w3.org/2018/credentials/v1")); + CredentialProof credentialProof = new CredentialProof(); + credentialProof.setProof_type("jwt"); + credentialProof.setJwt("dummy_jwt_proof"); + CredentialRequest credentialRequest = new CredentialRequest(); + credentialRequest.setFormat("ldp_vc"); + credentialRequest.setProof(credentialProof); + credentialRequest.setCredential_definition(credentialDefinition); + + CredentialResponse credentialResponse = new CredentialResponse(); + credentialResponse.setFormat("ldp_vc"); + credentialResponse.setCredential(new JsonLDObject()); + Mockito.when(vcIssuanceService.getCredential(credentialRequest)).thenReturn(credentialResponse); + + mockMvc.perform(post("/issuance/credential") + .content(objectMapper.writeValueAsBytes(credentialRequest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.format").exists()) + .andExpect(jsonPath("$.credential").exists()); + } + + @Test + public void getVerifiableCredential_withInvalidFormat_thenFail() throws Exception { + CredentialRequest credentialRequest = new CredentialRequest(); + credentialRequest.setFormat(null); + CredentialProof credentialProof = new CredentialProof(); + credentialProof.setProof_type("jwt"); + credentialRequest.setProof(credentialProof); + CredentialDefinition credentialDefinition = new CredentialDefinition(); + credentialDefinition.setType(Arrays.asList("VerifiableCredential", "SampleVerifiableCredential_ldp")); + credentialRequest.setCredential_definition(credentialDefinition); + + mockMvc.perform(post("/issuance/credential") + .content(objectMapper.writeValueAsBytes(credentialRequest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.error").value(ErrorConstants.INVALID_VC_FORMAT)); + + credentialRequest.setFormat(" "); + mockMvc.perform(post("/issuance/credential") + .content(objectMapper.writeValueAsBytes(credentialRequest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.error").value(ErrorConstants.INVALID_VC_FORMAT)); + } + + @Test + public void getVerifiableCredential_withInvalidProof_thenFail() throws Exception { + CredentialRequest credentialRequest = new CredentialRequest(); + credentialRequest.setFormat("jwt_vc_json"); + CredentialDefinition credentialDefinition = new CredentialDefinition(); + credentialDefinition.setType(Arrays.asList("VerifiableCredential", "SampleVerifiableCredential_ldp")); + credentialRequest.setCredential_definition(credentialDefinition); + + credentialRequest.setProof(null); + mockMvc.perform(post("/issuance/credential") + .content(objectMapper.writeValueAsBytes(credentialRequest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.error").value(ErrorConstants.INVALID_PROOF)); + + CredentialProof credentialProof = new CredentialProof(); + credentialRequest.setProof(credentialProof); + mockMvc.perform(post("/issuance/credential") + .content(objectMapper.writeValueAsBytes(credentialRequest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.error").value(ErrorConstants.UNSUPPORTED_PROOF_TYPE)); + + + credentialProof.setProof_type(" "); + credentialRequest.setProof(credentialProof); + mockMvc.perform(post("/issuance/credential") + .content(objectMapper.writeValueAsBytes(credentialRequest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.error").value(ErrorConstants.UNSUPPORTED_PROOF_TYPE)); + } + + @Test + public void getVerifiableCredential_withInvalidNonceException_thenFail() throws Exception { + CredentialDefinition credentialDefinition = new CredentialDefinition(); + credentialDefinition.setType(Arrays.asList("VerifiableCredential", "SampleVerifiableCredential_ldp")); + credentialDefinition.setContext(Arrays.asList("https://www.w3.org/2018/credentials/v1")); + CredentialProof credentialProof = new CredentialProof(); + credentialProof.setProof_type("jwt"); + credentialProof.setJwt("dummy_jwt_proof"); + CredentialRequest credentialRequest = new CredentialRequest(); + credentialRequest.setFormat("ldp_vc"); + credentialRequest.setProof(credentialProof); + credentialRequest.setCredential_definition(credentialDefinition); + + InvalidNonceException exception = new InvalidNonceException("test-new-nonce", 400); + Mockito.when(vcIssuanceService.getCredential(credentialRequest)).thenThrow(exception); + + mockMvc.perform(post("/issuance/credential") + .content(objectMapper.writeValueAsBytes(credentialRequest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.error").value(exception.getErrorCode())) + .andExpect(jsonPath("$.c_nonce_expires_in").value(exception.getClientNonceExpireSeconds())) + .andExpect(jsonPath("$.c_nonce").value(exception.getClientNonce())); + } +} \ No newline at end of file diff --git a/certify-service/src/test/resources/application-test.properties b/certify-service/src/test/resources/application-test.properties new file mode 100644 index 00000000..0ba9783c --- /dev/null +++ b/certify-service/src/test/resources/application-test.properties @@ -0,0 +1,82 @@ +# 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/. + +## ------------------------------------------- Integrations ------------------------------------------------------------ + +mosip.certify.integration.scan-base-package=io.mosip.certify +mosip.certify.integration.audit-plugin=TestAuditPlugin +mosip.certify.integration.vci-plugin=TestVCIPluginImpl + +## ------------------------------------------ Discovery openid-configuration ------------------------------------------- +mosipbox.public.url=http://localhost:8090 +mosip.certify.discovery.issuer-id=${mosipbox.public.url}${server.servlet.path} + +##--------------------------------------------------------------------------------------------------------------------- +spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration + +## ---------------------------------------------- VCI ------------------------------------------------------------------ + + +mosip.certify.identifier=${mosipbox.public.url}${server.servlet.path} +mosip.certify.authn.filter-urls={ '${server.servlet.path}/issuance/credential' } +mosip.certify.authn.issuer-uri=${mosipbox.public.url}${server.servlet.path} +mosip.certify.authn.jwk-set-uri=${mosipbox.public.url}${server.servlet.path}/oauth/.well-known/jwks.json +mosip.certify.authn.allowed-audiences={ '${mosipbox.public.url}${server.servlet.path}/issuance/credential' } + +mosip.certify.supported.jwt-proof-alg={'RS256'} + +mosip.certify.key-values={\ + "v11" : { \ + 'credential_issuer': '${mosipbox.public.url}', \ + 'credential_endpoint': '${mosipbox.public.url}${server.servlet.path}/issuance/credential', \ + 'credentials_supported': {{\ + 'format': 'ldp_vc',\ + 'id': 'SampleVerifiableCredential_ldp', \ + 'scope' : 'sample_vc_ldp',\ + 'cryptographic_binding_methods_supported': {'did:jwk'},\ + 'cryptographic_suites_supported': {'RsaSignature2018'},\ + 'proof_types_supported': {'jwt'},\ + 'credential_definition': {\ + 'type': {'VerifiableCredential','MockVerifiableCredential'},\ + 'credentialSubject': {\ + 'name': { 'display': {{'name': 'Given Name', 'locale': 'en' }}}, \ + 'age': { 'display': {{ 'name': 'Age', 'locale': 'en'}}}\ + }\ + },\ + 'display': {{'name': 'Sample Verifiable Credential by e-Signet', \ + 'locale': 'en', \ + 'logo': {'url': '${mosipbox.public.url}/logo.png',\ + 'alt_text': 'a square logo of a MOSIP'},\ + 'background_color': '#12107c',\ + 'text_color': '#FFFFFF'}}\ + }},\ + 'display': {{'name': 'e-Signet', 'locale': 'en'}}\ + },\ + "latest" : { \ + 'credential_issuer': '${mosipbox.public.url}', \ + 'credential_endpoint': '${mosipbox.public.url}${server.servlet.path}/issuance/credential', \ + 'display': {{'name': 'e-Signet', 'locale': 'en'}},\ + 'credentials_supported' : { \ + "SampleVerifiableCredential_ldp" : {\ + 'format': 'ldp_vc',\ + 'scope' : 'sample_vc_ldp',\ + 'cryptographic_binding_methods_supported': {'did:jwk'},\ + 'cryptographic_suites_supported': {'RsaSignature2018'},\ + 'proof_types_supported': {'jwt'},\ + 'credential_definition': {\ + 'type': {'VerifiableCredential','SampleVerifiableCredential'},\ + 'credentialSubject': {\ + 'name': { 'display': {{'name': 'Given Name', 'locale': 'en' }}}, \ + 'age': { 'display': {{ 'name': 'Age', 'locale': 'en'}}}\ + }},\ + 'display': {{'name': 'Sample Verifiable Credential by e-Signet', \ + 'locale': 'en', \ + 'logo': {'url': '${mosipbox.public.url}/logo.png',\ + 'alt_text': 'a square logo of a MOSIP'},\ + 'background_color': '#12107c',\ + 'text_color': '#FFFFFF'}}\ + }\ + }\ + }\ + } diff --git a/certify-service/src/test/resources/bootstrap.properties b/certify-service/src/test/resources/bootstrap.properties new file mode 100644 index 00000000..46892be0 --- /dev/null +++ b/certify-service/src/test/resources/bootstrap.properties @@ -0,0 +1,22 @@ +# 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/. + +## Application Name +spring.application.name=certify +spring.cloud.config.uri=http://localhost:8888 +spring.profiles.active=test + +server.port=8090 +server.servlet.path=/v1/certify + +openapi.info.title=Certify +openapi.info.description=Rest Endpoints for operations related to Certify service +openapi.info.version=1.0 +openapi.info.license.name=Mosip +openapi.info.license.url=https://docs.mosip.io/platform/license +mosipbox.public.url=http://localhost:${server.port} +openapi.service.server.url=${mosipbox.public.url}${server.servlet.path} +openapi.service.server.description=Certify Service +springdoc.swagger-ui.disable-swagger-default-url=true +spring.mvc.servlet.path=${server.servlet.path} \ No newline at end of file diff --git a/destroy.sh b/docker-compose/destroy.sh similarity index 100% rename from destroy.sh rename to docker-compose/destroy.sh diff --git a/docker-compose-esignet/README.md b/docker-compose/docker-compose-esignet/README.md similarity index 100% rename from docker-compose-esignet/README.md rename to docker-compose/docker-compose-esignet/README.md diff --git a/docker-compose-esignet/config/esignet-default.properties b/docker-compose/docker-compose-esignet/config/esignet-default.properties similarity index 98% rename from docker-compose-esignet/config/esignet-default.properties rename to docker-compose/docker-compose-esignet/config/esignet-default.properties index 0a52e020..e624f629 100644 --- a/docker-compose-esignet/config/esignet-default.properties +++ b/docker-compose/docker-compose-esignet/config/esignet-default.properties @@ -48,7 +48,7 @@ spring.messages.encoding=UTF-8 spring.main.allow-bean-definition-overriding=true ## ------------------------------------------------- e-Signet ---------------------------------------------------------- -mosip.esignet.amr-acr-mapping-file-url=https://raw.githubusercontent.com/mosip/mosip-config/collab/amr-acr-mapping.json +mosip.esignet.amr-acr-mapping-file-url=https://raw.githubusercontent.com/mosip/mosip-config/collab1/amr-acr-mapping.json mosip.esignet.auth-txn-id-length=10 mosip.esignet.supported-id-regex=\\S* mosip.esignet.id-token-expire-seconds=3600 @@ -134,25 +134,25 @@ mosip.esignet.authenticator.sunbird-rc.kba.entity-id-field=osid mosip.esignet.vciplugin.sunbird-rc.issue-credential-url=http://nginx:80/credential/credentials/issue mosip.esignet.vciplugin.sunbird-rc.supported-credential-types=HealthInsuranceCredential,LifeInsuranceCredential,InsuranceCredential -mosip.esignet.vciplugin.sunbird-rc.credential-type.HealthInsuranceCredential.static-value-map.issuerId=did:upai:9d67541a-9af1-4510-a004-23c6d955c3ee +mosip.esignet.vciplugin.sunbird-rc.credential-type.HealthInsuranceCredential.static-value-map.issuerId=did:web:challabeehyv.github.io:DID-Resolve:65d90545-878a-4807-9eba-1f56446e7926 mosip.esignet.vciplugin.sunbird-rc.credential-type.HealthInsuranceCredential.template-url=https://raw.githubusercontent.com/challabeehyv/mimoto-config/main/InsuranceConfig.json mosip.esignet.vciplugin.sunbird-rc.credential-type.HealthInsuranceCredential.registry-get-url=http://nginx:80/registry/api/v1/Insurance/ -mosip.esignet.vciplugin.sunbird-rc.credential-type.HealthInsuranceCredential.cred-schema-id=did:schema:188126c2-5657-48d4-a347-4e832d5567dd +mosip.esignet.vciplugin.sunbird-rc.credential-type.HealthInsuranceCredential.cred-schema-id=did:schema:77ea2b1b-f0aa-4214-acb5-2f3b93bc7ee7 mosip.esignet.vciplugin.sunbird-rc.credential-type.HealthInsuranceCredential.cred-schema-version=1.0.0 mosip.esignet.vciplugin.sunbird-rc.credential-type.HealthInsuranceCredential.registry-search-url=http://nginx:80/registry/api/v1/Insurance/search -mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.static-value-map.issuerId=did:upai:9d67541a-9af1-4510-a004-23c6d955c3ee +mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.static-value-map.issuerId=did:web:challabeehyv.github.io:DID-Resolve:65d90545-878a-4807-9eba-1f56446e7926 mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.template-url=https://raw.githubusercontent.com/challabeehyv/mimoto-config/main/InsuranceConfig.json mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.registry-get-url=http://nginx:80/registry/api/v1/Insurance/ -mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.cred-schema-id=did:schema:188126c2-5657-48d4-a347-4e832d5567dd +mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.cred-schema-id=did:schema:77ea2b1b-f0aa-4214-acb5-2f3b93bc7ee7 mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.cred-schema-version=1.0.0 mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.registry-search-url=http://nginx:80/registry/api/v1/Insurance/search -mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.static-value-map.issuerId=did:upai:9d67541a-9af1-4510-a004-23c6d955c3ee +mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.static-value-map.issuerId=did:web:challabeehyv.github.io:DID-Resolve:65d90545-878a-4807-9eba-1f56446e7926 mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.template-url=https://raw.githubusercontent.com/challabeehyv/mimoto-config/main/InsuranceConfig.json mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.registry-get-url=http://nginx:80/registry/api/v1/Insurance/ -mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.cred-schema-id=did:schema:188126c2-5657-48d4-a347-4e832d5567dd +mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.cred-schema-id=did:schema:77ea2b1b-f0aa-4214-acb5-2f3b93bc7ee7 mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.cred-schema-version=1.0.0 mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.registry-search-url=http://nginx:80/registry/api/v1/Insurance/search @@ -423,7 +423,7 @@ mosip.esignet.vci.authn.allowed-audiences={ '${mosip.esignet.domain.url}${server ##change this to your value mosip.esignet.cnonce-expire-seconds=1000 -mosip.esignet.vci.supported.jwt-proof-alg={'RS256','PS256'} +mosip.esignet.vci.supported.jwt-proof-alg={'RS256','PS256','ES256'} mosip.esignet.vci.key-values={\ 'v11' : {\ diff --git a/docker-compose-esignet/docker-compose.yml b/docker-compose/docker-compose-esignet/docker-compose.yml similarity index 100% rename from docker-compose-esignet/docker-compose.yml rename to docker-compose/docker-compose-esignet/docker-compose.yml diff --git a/docker-compose-esignet/init.sql b/docker-compose/docker-compose-esignet/init.sql similarity index 100% rename from docker-compose-esignet/init.sql rename to docker-compose/docker-compose-esignet/init.sql diff --git a/docker-compose-esignet/loader_path/mock-esignet-integration-impl-0.9.2-SNAPSHOT.jar b/docker-compose/docker-compose-esignet/loader_path/mock-esignet-integration-impl-0.9.2-SNAPSHOT.jar similarity index 100% rename from docker-compose-esignet/loader_path/mock-esignet-integration-impl-0.9.2-SNAPSHOT.jar rename to docker-compose/docker-compose-esignet/loader_path/mock-esignet-integration-impl-0.9.2-SNAPSHOT.jar diff --git a/docker-compose-esignet/nginx.conf b/docker-compose/docker-compose-esignet/nginx.conf similarity index 100% rename from docker-compose-esignet/nginx.conf rename to docker-compose/docker-compose-esignet/nginx.conf diff --git a/docker-compose-sunbird/.env b/docker-compose/docker-compose-sunbird/.env similarity index 87% rename from docker-compose-sunbird/.env rename to docker-compose/docker-compose-sunbird/.env index 6b246596..b163b818 100644 --- a/docker-compose-sunbird/.env +++ b/docker-compose/docker-compose-sunbird/.env @@ -10,9 +10,10 @@ VAULT_BASE_URL=http://vault:8200/v1 VAULT_ROOT_PATH=http://vault:8200/v1/kv VAULT_TIMEOUT=5000 VAULT_PROXY=false -SIGNING_ALGORITHM=Ed25519 +SIGNING_ALGORITHM=Ed25519Signature2020 JWKS_URI= ENABLE_AUTH=false +WEB_DID_BASE_URL=https://challabeehyv.github.io/DID-Resolve # schema service IDENTITY_BASE_URL=http://identity:3332 diff --git a/docker-compose-sunbird/.gitignore b/docker-compose/docker-compose-sunbird/.gitignore similarity index 78% rename from docker-compose-sunbird/.gitignore rename to docker-compose/docker-compose-sunbird/.gitignore index cc0fe230..d8acd276 100644 --- a/docker-compose-sunbird/.gitignore +++ b/docker-compose/docker-compose-sunbird/.gitignore @@ -1,4 +1,4 @@ -*data +data keys.txt data diff --git a/docker-compose-sunbird/docker-compose.yml b/docker-compose/docker-compose-sunbird/docker-compose.yml similarity index 98% rename from docker-compose-sunbird/docker-compose.yml rename to docker-compose/docker-compose-sunbird/docker-compose.yml index 2cb16e80..fbef434a 100644 --- a/docker-compose-sunbird/docker-compose.yml +++ b/docker-compose/docker-compose-sunbird/docker-compose.yml @@ -28,7 +28,7 @@ services: networks: - network identity: - image: ghcr.io/sunbird-rc/sunbird-rc-identity-service:v2.0.0-beta2 + image: ghcr.io/sunbird-rc/sunbird-rc-identity-service:v2.0.0-rc3 ports: - "3332:3332" depends_on: @@ -47,6 +47,7 @@ services: - SIGNING_ALGORITHM=${SIGNING_ALGORITHM} - JWKS_URI=${JWKS_URI} - ENABLE_AUTH=${ENABLE_AUTH} + - WEB_DID_BASE_URL=${WEB_DID_BASE_URL} healthcheck: test: [ "CMD-SHELL", "curl -f http://localhost:3332/health || exit 1" ] @@ -56,7 +57,7 @@ services: networks: - network schema: - image: ghcr.io/sunbird-rc/sunbird-rc-credential-schema:v2.0.0-beta1 + image: ghcr.io/sunbird-rc/sunbird-rc-credential-schema:v2.0.0-rc3 ports: - "3333:3333" depends_on: @@ -78,7 +79,7 @@ services: networks: - network credential: - image: ghcr.io/sunbird-rc/sunbird-rc-credentials-service:v2.0.0-beta2 + image: ghcr.io/sunbird-rc/sunbird-rc-credentials-service:v2.0.0-rc3 ports: - "3000:3000" depends_on: @@ -95,6 +96,7 @@ services: - CREDENTIAL_SERVICE_BASE_URL=${CREDENTIAL_SERVICE_BASE_URL} - JWKS_URI=${JWKS_URI} - ENABLE_AUTH=${ENABLE_AUTH} + - QR_TYPE=W3C_VC healthcheck: test: [ "CMD-SHELL", "curl -f http://localhost:3000/health || exit 1" ] diff --git a/docker-compose-sunbird/imports/nginx/nginx.conf b/docker-compose/docker-compose-sunbird/imports/nginx/nginx.conf similarity index 100% rename from docker-compose-sunbird/imports/nginx/nginx.conf rename to docker-compose/docker-compose-sunbird/imports/nginx/nginx.conf diff --git a/docker-compose-sunbird/schemas/credentials/Insurance.json b/docker-compose/docker-compose-sunbird/schemas/credentials/Insurance.json similarity index 100% rename from docker-compose-sunbird/schemas/credentials/Insurance.json rename to docker-compose/docker-compose-sunbird/schemas/credentials/Insurance.json diff --git a/docker-compose-sunbird/schemas/registry/Insurance.json b/docker-compose/docker-compose-sunbird/schemas/registry/Insurance.json similarity index 100% rename from docker-compose-sunbird/schemas/registry/Insurance.json rename to docker-compose/docker-compose-sunbird/schemas/registry/Insurance.json diff --git a/docker-compose-sunbird/setup_vault.sh b/docker-compose/docker-compose-sunbird/setup_vault.sh similarity index 100% rename from docker-compose-sunbird/setup_vault.sh rename to docker-compose/docker-compose-sunbird/setup_vault.sh diff --git a/docker-compose-sunbird/vault.json b/docker-compose/docker-compose-sunbird/vault.json similarity index 100% rename from docker-compose-sunbird/vault.json rename to docker-compose/docker-compose-sunbird/vault.json diff --git a/install.sh b/docker-compose/install.sh similarity index 100% rename from install.sh rename to docker-compose/install.sh diff --git a/mvnw b/mvnw new file mode 100644 index 00000000..66df2854 --- /dev/null +++ b/mvnw @@ -0,0 +1,308 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.2.0 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "$(uname)" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin ; then + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "\"$javaExecutable\"")" + fi + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$(cd "$wdir/.." || exit 1; pwd) + fi + # end of workaround + done + printf '%s' "$(cd "$basedir" || exit 1; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; + esac + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget > /dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 00000000..95ba6f54 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,205 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.2.0 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..dc8abb67 --- /dev/null +++ b/pom.xml @@ -0,0 +1,438 @@ + + + + + + org.springframework.boot + spring-boot-starter-parent + 3.2.3 + + + + 4.0.0 + io.mosip.certify + certify-parent + 0.0.1-SNAPSHOT + pom + certify + Parent project for MOSIP certify + https://github.com/mosip/inji-certify + + + + MPL 2.0 + https://www.mozilla.org/en-US/MPL/2.0/ + + + + + scm:git:git://github.com/mosip/inji-certify.git + scm:git:ssh://github.com:mosip/inji-certify.git + https://github.com/mosip/inji-certify + HEAD + + + + + Mosip + mosip.emailnotifier@gmail.com + io.mosip + https://github.com/mosip/inji-certify + + + + + + ossrh + CentralRepository + https://oss.sonatype.org/content/repositories/snapshots + default + + true + + + + central + MavenCentral + default + https://repo1.maven.org/maven2 + + false + + + + danubetech-maven-public + https://repo.danubetech.com/repository/maven-public/ + + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + UTF-8 + + + 21 + 21 + 3.8.1 + 3.3.0 + 3.3.2 + 3.2.5 + 0.8.12 + 3.7.0.1746 + 3.6.3 + 4.4.3 + + 3.2.3 + + 21 + 0.6.5 + 1.2.0.1 + 0.5.0 + 2.5.0 + 1.7 + + + + certify-service + certify-core + certify-integration-api + + + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + org.bitbucket.b_c + jose4j + ${jose4j.version} + + + io.mosip.kernel + kernel-core + ${kernel.core.version} + + + com.vaadin.external.google + android-json + 0.0.20131108.vaadin1 + + + org.junit.vintage + junit-vintage-engine + + + + + + + org.springframework.cloud + spring-cloud-dependencies + 2023.0.0 + pom + import + + + org.mockito + mockito-bom + + + + + + + + + sonar + + . + src/main/java/**,src/main/resources/** + ${sonar.coverage.exclusions} + https://sonarcloud.io + + + false + + + + + org.sonarsource.scanner.maven + sonar-maven-plugin + ${maven.sonar.plugin.version} + + + verify + + sonar + + + + + + + + + + openapi-doc-generate-profile + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + true + ZIP + + + + pre-integration-test + + start + + + + src/test/resources + + + openapi-profile + + + --server.port=8090 + --server.servlet.path=/app/generic + + + + + + post-integration-test + + stop + + + + + build-info + repackage + + + + + + org.springdoc + springdoc-openapi-maven-plugin + 0.2 + + + integration-test + + generate + + + + + http://localhost:8090/app/generic/v3/api-docs + openapi.json + ${project.build.directory} + false + + + + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.plugin.version} + + ${skipTests} + false + + ${argLine} --add-opens java.xml/jdk.xml.internal=ALL-UNNAMED --illegal-access=permit + + + + + org.jacoco + jacoco-maven-plugin + ${maven.jacoco.version} + + + + prepare-agent + + + + report + prepare-package + + report + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven.jar.plugin.version} + + + + true + true + + + ${project.name} + ${project.version} + + + + + + org.apache.maven.plugins + maven-war-plugin + ${maven.war.plugin.version} + + + + true + true + + + ${project.name} + ${project.version} + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven.javadoc.version} + + + attach-javadocs + + jar + + + + + none + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://oss.sonatype.org/ + false + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + --pinentry-mode + loopback + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.version} + + true + ${maven.compiler.source} + ${maven.compiler.target} + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + + pl.project13.maven + git-commit-id-plugin + 3.0.1 + + + get-the-git-infos + + revision + + validate + + + + true + ${project.build.outputDirectory}/git.properties + + ^git.build.(time|version)$ + ^git.commit.id.(abbrev|full)$ + + full + ${project.basedir}/.git + json + + + + + + +