diff --git a/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/OSSystemFields.java b/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/OSSystemFields.java index 6f5afd270..f81d40fa0 100644 --- a/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/OSSystemFields.java +++ b/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/OSSystemFields.java @@ -74,6 +74,36 @@ public boolean hasCredential(String signatureProvider, JsonNode node) { return node.get(property) != null && !node.get(property).asText().isEmpty(); } }, + attestation { + private String getCredentialPropertyName(String signatureProvider) { + String signatureProperty = _osAttestedData.name(); + if(Objects.equals(signatureProvider, "dev.sunbirdrc.registry.service.impl.SignatureV2ServiceImpl")) { + signatureProperty = _osAttestedData.name(); + } + return signatureProperty; + } + @Override + public void setCredential(String signatureProvider, JsonNode node, Object signedCredential) { + if(Objects.equals(signatureProvider, "dev.sunbirdrc.registry.service.impl.SignatureV2ServiceImpl")) { + JSONUtil.addField((ObjectNode) node, String.valueOf(_osAttestedData), ((ObjectNode) signedCredential).get("id").asText()); + } else { + JSONUtil.addField((ObjectNode) node, String.valueOf(_osAttestedData), signedCredential.toString()); + } + } + @Override + public void removeCredential(String signatureProvider, JsonNode node) { + ((ObjectNode) node).put(getCredentialPropertyName(signatureProvider), ""); + } + @Override + public JsonNode getCredential(String signatureProvider, JsonNode node) { + return node.get(getCredentialPropertyName(signatureProvider)); + } + @Override + public boolean hasCredential(String signatureProvider, JsonNode node) { + String property = getCredentialPropertyName(signatureProvider); + return node.get(property) != null && !node.get(property).asText().isEmpty(); + } + }, _osState, _osClaimId, _osAttestedData, _osSignedData, _osCredentialId; public void createdBy(JsonNode node, String userId){}; diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryEntityController.java b/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryEntityController.java index f9470f407..9d2b18a19 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryEntityController.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryEntityController.java @@ -24,6 +24,7 @@ import dev.sunbirdrc.registry.middleware.util.OSSystemFields; import dev.sunbirdrc.registry.service.FileStorageService; import dev.sunbirdrc.registry.service.ICertificateService; +import dev.sunbirdrc.registry.service.impl.SignatureV2ServiceImpl; import dev.sunbirdrc.registry.transform.Configuration; import dev.sunbirdrc.registry.transform.Data; import dev.sunbirdrc.registry.transform.ITransformer; @@ -432,10 +433,13 @@ private String getNotes(JsonNode requestBody) { private JsonNode getAttestationSignedData(String attestationId, JsonNode node) throws AttestationNotFoundException, JsonProcessingException { JsonNode attestationNode = getAttestationNode(attestationId, node); - if (attestationNode.get(OSSystemFields._osAttestedData.name()) == null) + if (!OSSystemFields.attestation.hasCredential(GenericConfiguration.getSignatureProvider(), attestationNode)) throw new AttestationNotFoundException(); - attestationNode = objectMapper.readTree(attestationNode.get(OSSystemFields._osAttestedData.name()).asText()); - return attestationNode; + JsonNode signed = OSSystemFields.attestation.getCredential(GenericConfiguration.getSignatureProvider(), attestationNode); + if(GenericConfiguration.getSignatureProvider().equals(SignatureV2ServiceImpl.class.getName())) { + return signed; + } + return objectMapper.readTree(signed.asText()); } @Nullable @@ -449,9 +453,6 @@ private JsonNode getAttestationNode(String attestationId, JsonNode node) throws } } assert attestationNode != null; - if (attestationNode.get(OSSystemFields._osAttestedData.name()) == null) - throw new AttestationNotFoundException(); - attestationNode = objectMapper.readTree(attestationNode.get(OSSystemFields._osAttestedData.name()).asText()); return attestationNode; } @@ -835,7 +836,7 @@ public ResponseEntity getSignedEntityByToken(@PathVariable String entity JsonNode result = registryHelper.getRequestedUserDetails(request, entityName); if (result.get(entityName).size() > 0) { Object credentialTemplate = definitionsManager.getCredentialTemplate(entityName); - Object signedCredentials = registryHelper.getSignedDoc(result.get(entityName).get(0), credentialTemplate); + Object signedCredentials = registryHelper.getSignedDoc(entityName, result.get(entityName).get(0), credentialTemplate); return new ResponseEntity<>(signedCredentials, HttpStatus.OK); } else { responseParams.setErrmsg("Entity not found"); diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/helper/RegistryHelper.java b/java/registry/src/main/java/dev/sunbirdrc/registry/helper/RegistryHelper.java index 6992fd51b..2e0ec3ef1 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/helper/RegistryHelper.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/helper/RegistryHelper.java @@ -32,6 +32,7 @@ import dev.sunbirdrc.registry.model.attestation.EntityPropertyURI; import dev.sunbirdrc.registry.model.dto.AttestationRequest; import dev.sunbirdrc.registry.service.*; +import dev.sunbirdrc.registry.service.impl.SignatureV2ServiceImpl; import dev.sunbirdrc.registry.sink.shard.Shard; import dev.sunbirdrc.registry.sink.shard.ShardManager; import dev.sunbirdrc.registry.util.*; @@ -604,10 +605,15 @@ public void updateState(PluginResponseMessage pluginResponseMessage) throws Exce if (!signatureEnabled) { throw new UnreachableException("Signature service not enabled!"); } - Object signedData = getSignedDoc(response, credentialTemplate); + String title = String.format("%s_%s", pluginResponseMessage.getSourceEntity(), pluginResponseMessage.getPolicyName()); + Object signedData = getSignedDoc(title, response, credentialTemplate); + String value = signedData.toString(); + if(GenericConfiguration.getSignatureProvider().equals(SignatureV2ServiceImpl.class.getName())) { + value = ((ObjectNode) signedData).get("id").asText(); + } metaData.put( ATTESTED_DATA, - signedData.toString() + value ); } else { metaData.put( @@ -1032,9 +1038,10 @@ private void updateAttestation(ArrayNode attestations,String propertyToUpdate) { } } - public Object getSignedDoc(JsonNode result, Object credentialTemplate) throws + public Object getSignedDoc(String title, JsonNode result, Object credentialTemplate) throws SignatureException.CreationException, SignatureException.UnreachableException { Map requestBodyMap = new HashMap<>(); + requestBodyMap.put("title", title); requestBodyMap.put("data", result); requestBodyMap.put(CREDENTIAL_TEMPLATE, credentialTemplate); return signatureHelper.sign(requestBodyMap); @@ -1049,7 +1056,7 @@ public void signDocument(String entityName, String entityId, String userId) thro if (credentialTemplate != null) { ObjectNode updatedNode = (ObjectNode) readEntity(userId, entityName, entityId, false, null, false) .get(entityName); - Object signedCredentials = getSignedDoc(updatedNode, credentialTemplate); + Object signedCredentials = getSignedDoc(entityId, updatedNode, credentialTemplate); OSSystemFields.credentials.setCredential(GenericConfiguration.getSignatureProvider(), updatedNode, signedCredentials); ObjectNode updatedNodeParent = JsonNodeFactory.instance.objectNode(); updatedNodeParent.set(entityName, updatedNode); diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/service/CredentialSchemaService.java b/java/registry/src/main/java/dev/sunbirdrc/registry/service/CredentialSchemaService.java index 06fee8924..56ae4edeb 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/service/CredentialSchemaService.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/service/CredentialSchemaService.java @@ -57,6 +57,7 @@ public class CredentialSchemaService implements HealthIndicator { private RetryRestTemplate retryRestTemplate; public JsonNode convertCredentialTemplateToSchema(String title, Object credTemplate) throws IOException { + logger.debug("Converting credential template to credential schema for {}", title); String name = "Proof of " + title + " Credential"; String schemaId = "Proof-of-" + title + "-Credential"; String templateJsonString = null; @@ -77,27 +78,37 @@ public JsonNode convertCredentialTemplateToSchema(String title, Object credTempl value.put("type", "string"); ((ObjectNode) schemaProperties).set(key, value); }); + logger.debug("Successfully converted credential template to credential schema for {}", title); return credSchema; } public void ensureCredentialSchemas() { - this.definitionsManager.getAllDefinitions() - .forEach(definition -> { + Map credTemplates = new HashMap<>(); + this.definitionsManager.getAllDefinitions().forEach(definition -> { + Object credTemplate = definition.getOsSchemaConfiguration().getCredentialTemplate(); + if(credTemplate != null) credTemplates.put(definition.getTitle(), credTemplate); + definition.getOsSchemaConfiguration().getAttestationPolicies().forEach(attestationPolicy -> { + if(attestationPolicy.getCredentialTemplate() != null) { + String name = String.format("%s_%s", definition.getTitle(), attestationPolicy.getName()); + credTemplates.put(name, attestationPolicy.getCredentialTemplate()); + } + }); + }); + credTemplates.forEach((key, value) -> { try { - Object credTemplate = definition.getOsSchemaConfiguration().getCredentialTemplate(); - if(credTemplate != null) { - this.ensureCredentialSchema( - definition.getTitle(), - credTemplate, null); - logger.info("Ensured credential schema for definition: {}", definition.getTitle()); - } + this.ensureCredentialSchema( + key, + value, null); + logger.info("Ensured credential schema for : {}", key); } catch (Exception e) { + logger.error("Exception occurred while ensuring credential Schema for {} : {}", key, ExceptionUtils.getStackTrace(e)); throw new RuntimeException(e); } }); } public void ensureCredentialSchema(String title, Object credTemplate, String status) throws Exception { + logger.debug("Ensuring credential schema for {}", title); JsonNode schema = convertCredentialTemplateToSchema(title, credTemplate); ObjectNode prevSchema = (ObjectNode) getLatestSchemaByTags(Collections.singletonList(title)); String author = DIDService.ensureDidForName(authorName, authorDidMethod); @@ -106,6 +117,7 @@ public void ensureCredentialSchema(String title, Object credTemplate, String sta ((ObjectNode) schema).set("authored", JsonNodeFactory.instance.textNode(authored)); if (prevSchema == null) { createSchema(title, schema, status); + logger.debug("Created credential schema for {}", title); } else { ObjectNode prevProps = (ObjectNode) prevSchema.get("schema").get("schema").get("properties"); ObjectNode currProps = (ObjectNode) schema.get("schema").get("properties"); @@ -118,6 +130,7 @@ public void ensureCredentialSchema(String title, Object credTemplate, String sta if(updateRequired.get()) { if(status == null) status = prevSchema.get("status").asText(); updateSchema(did, version, schema, status); + logger.debug("Updated credential schema for {}", title); } } } diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/service/CredentialService.java b/java/registry/src/main/java/dev/sunbirdrc/registry/service/CredentialService.java deleted file mode 100644 index 30881f8e2..000000000 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/service/CredentialService.java +++ /dev/null @@ -1,171 +0,0 @@ -package dev.sunbirdrc.registry.service; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.github.jknack.handlebars.Handlebars; -import com.github.jknack.handlebars.Template; -import dev.sunbirdrc.pojos.ComponentHealthInfo; -import dev.sunbirdrc.pojos.HealthIndicator; -import dev.sunbirdrc.registry.dao.NotFoundException; -import dev.sunbirdrc.registry.middleware.util.JSONUtil; -import dev.sunbirdrc.registry.service.impl.RetryRestTemplate; -import org.apache.commons.lang3.exception.ExceptionUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; - -import java.io.IOException; -import java.util.*; - -import static dev.sunbirdrc.registry.middleware.util.Constants.CONNECTION_FAILURE; - -public class CredentialService implements HealthIndicator { - private static final Logger logger = LoggerFactory.getLogger(CredentialService.class); - @Value("${signature.v2.healthCheckURL}") - private String healthCheckUrl; - @Value("${signature.v2.issueCredentialURL}") - private String issueCredentialURL; - @Value("${signature.v2.getCredentialByIdURL}") - private String getCredentialByIdURL; - @Value("${signature.v2.deleteCredentialByIdURL}") - private String deleteCredentialByIdURL; - @Value("${signature.v2.verifyCredentialURL}") - private String verifyCredentialURL; - @Value("${signature.v2.getRevocationListURL}") - private String getRevocationListURL; - - private static final String credentialMethod = "rcw"; - private static final String credentialIssuerMethod = "abc"; - - @Autowired - protected RetryRestTemplate retryRestTemplate; - @Autowired - private ObjectMapper objectMapper; - @Autowired - private CredentialSchemaService credentialSchemaService; - @Autowired - private dev.sunbirdrc.registry.service.DIDService DIDService; - - public JsonNode issueCredential(String title, Object credentialTemplate, JsonNode input) throws Exception { - // Render the credential using credential template - Handlebars hb = new Handlebars(); - String templateJsonString = null; - if(credentialTemplate instanceof LinkedHashMap || credentialTemplate instanceof JsonNode) { - templateJsonString = JSONUtil.convertObjectJsonString(credentialTemplate); - } else { - templateJsonString = (String) credentialTemplate; - } - Template template = hb.compileInline(templateJsonString); - String credString = template.apply(JSONUtil.convertJsonNodeToMap(input)); - ObjectNode credential = (ObjectNode) objectMapper.readTree(credString); - - // Fetch the credentials schema to get credential schema id and version - JsonNode credSchema = credentialSchemaService.getLatestSchemaByTags(Collections.singletonList(title)); - if (credSchema == null) throw new NotFoundException("CredentialSchema", title); - JsonNode credSchemaDid = credSchema.get("schema").get("id"); - JsonNode credSchemaVersion = credSchema.get("schema").get("version"); - - - // ensure issuer did - String issuerDid = DIDService.ensureDidForName(credential.get("issuer").asText(), credentialIssuerMethod); - credential.set("issuer", JsonNodeFactory.instance.textNode(issuerDid)); - - // Wire the create credential request payload - ObjectNode node = JsonNodeFactory.instance.objectNode(); - node.set("credential", credential); - node.set("credentialSchemaId", credSchemaDid); - node.set("credentialSchemaVersion", credSchemaVersion); - node.set("method", JsonNodeFactory.instance.textNode(credentialMethod)); - ArrayNode tags = JsonNodeFactory.instance.arrayNode(); - tags.add(title); - if(input.get("osid") != null) tags.add(input.get("osid")); - node.set("tags", tags); - - // send the request and issue credential - HttpEntity request = createPayloadFromJsonNode(node); - ResponseEntity response = retryRestTemplate.postForEntity(issueCredentialURL, request); - if (response.getStatusCode().is2xxSuccessful()) { - JsonNode result = JSONUtil.convertStringJsonNode(response.getBody()); - return result.get("credential"); - } - throw new RuntimeException("Unable to issue credential"); - } - - public JsonNode getCredentialById(String credentialId) throws IOException, NotFoundException { - ResponseEntity response = retryRestTemplate.getForEntity(getCredentialByIdURL, credentialId); - if (response.getStatusCode().is2xxSuccessful()) { - return JSONUtil.convertStringJsonNode(response.getBody()); - } - throw new NotFoundException("Credential", credentialId); - } - - public byte[] getCredentialById(String credentialId, String format, String templateId, String template) throws IOException, NotFoundException { - HttpHeaders headers = new HttpHeaders(); - headers.set("templateId", templateId); - if(template != null) headers.set("template", template.trim()); - headers.setAccept(Collections.singletonList(MediaType.valueOf(format))); - ResponseEntity response = retryRestTemplate.getForObject(getCredentialByIdURL, headers, byte[].class, credentialId); - if (response.getStatusCode().is2xxSuccessful()) { - return response.getBody(); - } - throw new RuntimeException("Unable to render the credential"); - } - - public void revokeCredential(String credentialId) throws IOException { - retryRestTemplate.deleteForEntity(deleteCredentialByIdURL, credentialId); - } - - public ArrayNode revocationList(String issuerDid, Integer page, Integer limit) throws IOException { - if(page != null && page < 1) page = 1; - if(limit != null && limit < 1) limit = 1000; - ResponseEntity response = retryRestTemplate.getForEntity(getRevocationListURL, issuerDid, page, limit); - if (response.getStatusCode().is2xxSuccessful()) { - return (ArrayNode) JSONUtil.convertStringJsonNode(response.getBody()); - } - return JsonNodeFactory.instance.arrayNode(); - } - - public JsonNode verifyCredential(String credentialId) throws IOException { - ResponseEntity response = retryRestTemplate.getForEntity(verifyCredentialURL, credentialId); - if (response.getStatusCode().is2xxSuccessful()) { - return JSONUtil.convertStringJsonNode(response.getBody()); - } - return JsonNodeFactory.instance.objectNode(); - } - - @Override - public String getServiceName() { - return "CREDENTIAL_SERVICE"; - } - - @Override - public ComponentHealthInfo getHealthInfo() { - try { - ResponseEntity response = retryRestTemplate.getForEntity(healthCheckUrl); - if (!StringUtils.isEmpty(response.getBody()) && JSONUtil.convertStringJsonNode(response.getBody()).get("status").asText().equalsIgnoreCase("UP")) { - logger.debug("{} service running!", this.getServiceName()); - return new ComponentHealthInfo(getServiceName(), true); - } else { - return new ComponentHealthInfo(getServiceName(), false); - } - } catch (Exception e) { - logger.error("Exception when checking the health of the Sunbird {} service: {}", getServiceName(), ExceptionUtils.getStackTrace(e)); - return new ComponentHealthInfo(getServiceName(), false, CONNECTION_FAILURE, e.getMessage()); - } - } - - public HttpEntity createPayloadFromJsonNode(JsonNode node) { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - return new HttpEntity<>(node.toString(), headers); - } -} diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/service/impl/SignatureV2ServiceImpl.java b/java/registry/src/main/java/dev/sunbirdrc/registry/service/impl/SignatureV2ServiceImpl.java index 5755d1e95..bcb251a41 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/service/impl/SignatureV2ServiceImpl.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/service/impl/SignatureV2ServiceImpl.java @@ -76,6 +76,7 @@ public Object sign(Map propertyValue) throws SignatureException. try { return this.issueCredential(title, credentialTemplate, data); } catch (Exception e) { + logger.error("Exception occurred while issuing a credential for {}: {}", title, ExceptionUtils.getStackTrace(e)); throw new SignatureException.CreationException(e.getMessage()); } } diff --git a/java/registry/src/test/java/dev/sunbirdrc/registry/service/impl/NativeSearchServiceTest.java b/java/registry/src/test/java/dev/sunbirdrc/registry/service/impl/NativeSearchServiceTest.java index ae19b2d1d..272f2bad8 100644 --- a/java/registry/src/test/java/dev/sunbirdrc/registry/service/impl/NativeSearchServiceTest.java +++ b/java/registry/src/test/java/dev/sunbirdrc/registry/service/impl/NativeSearchServiceTest.java @@ -24,6 +24,8 @@ import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; import org.keycloak.common.util.RandomString; +import org.kie.api.runtime.manager.audit.AuditService; +import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; @@ -51,6 +53,8 @@ public class NativeSearchServiceTest { private DBConnectionInfoMgr dbConnectionInfoMgr; @Autowired private ShardManager shardManager; + @Mock + private AuditService auditService; private NativeSearchService nativeSearchService; diff --git a/java/registry/src/test/resources/Student.json b/java/registry/src/test/resources/Student.json index 6d47912ff..1e62848a0 100644 --- a/java/registry/src/test/resources/Student.json +++ b/java/registry/src/test/resources/Student.json @@ -89,7 +89,7 @@ }, "address": { "$id": "#/properties/address", - "$ref": "#/definitions/Address", + "type": "string", "title": "Address" } } @@ -113,10 +113,7 @@ "internalFields": [ "$.contactDetails.email", "$.contactDetails.mobile", - "$.contactDetails.address.plot", - "$.contactDetails.address.street", - "$.contactDetails.address.landmark", - "$.contactDetails.address.locality" + "$.contactDetails.address" ], "signedFields": [], "indexFields": ["studentName"],