diff --git a/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/JSONUtil.java b/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/JSONUtil.java index b5eb22894..bfb51b3b8 100644 --- a/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/JSONUtil.java +++ b/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/JSONUtil.java @@ -270,6 +270,10 @@ public static void addField(ObjectNode parent, String childKey, List chi parent.set(childKey, arrayNode); } + public static String parseJsonTree(String path, JsonNode input) { + return JsonPath.parse(input.toString()).read(path).toString(); + } + /** * Remove a node of given key from parent's hierarchy(including nested objects) * @@ -495,7 +499,7 @@ public static String getOSIDFromArrNode(JsonNode resultNode, JsonNode requestBod } private static JsonNode searchClaimOsIdFromRequestProperties(ArrayNode arrayNode, JsonNode requestBody) { - if (requestBody.get("propertiesOSID") != null) { + if (requestBody!=null && requestBody.get("propertiesOSID") != null) { Map> requestBodyProperty = objectMapper.convertValue(requestBody.get("propertiesOSID"), Map.class); Iterator claimIterator = arrayNode.elements(); while (claimIterator.hasNext()) { diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryClaimsController.java b/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryClaimsController.java index 1dcc59c4e..d02e61abb 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryClaimsController.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryClaimsController.java @@ -158,7 +158,7 @@ public ResponseEntity riseAttestation(HttpServletRequest request, @Reque ((ObjectNode)requestBody).put("propertyData", propertyData.toString()); } registryHelper.addAttestationProperty(entityName, entityId, attestationName, requestBody, request); - String attestationOSID = registryHelper.getAttestationOSID(requestBody, entityName, entityId, attestationName); + String attestationOSID = registryHelper.getAttestationOSID(requestBody,entityName, entityId, attestationName); // Resolve condition for REQUESTER String condition = conditionResolverService.resolve(propertyData, "REQUESTER", attestationPolicy.getConditions(), Collections.emptyList()); updateGetFileUrl(additionalInput); @@ -180,7 +180,7 @@ public ResponseEntity riseAttestation(HttpServletRequest request, @Reque } else { try { registryHelper.addAttestationProperty(entityName, entityId, attestationName, requestBody, request); - String attestationOSID = registryHelper.getAttestationOSID(requestBody, entityName, entityId, attestationName); + String attestationOSID = registryHelper.getAttestationOSID(requestBody,entityName, entityId, attestationName); PluginRequestMessage pluginRequestMessage = PluginRequestMessageCreator.create( "", "", attestationOSID, entityName, registryHelper.fetchEmailIdFromToken(request, entityName), entityId, additionalInput, Action.RAISE_CLAIM.name(), attestationPolicy.getName(), 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 a27925f29..fe1c181ff 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 @@ -188,9 +188,10 @@ public ResponseEntity putEntity( String tag = "RegistryController.update " + entityName; watch.start(tag); // TODO: get userID from auth header + JsonNode existingNode = registryHelper.readEntity(newRootNode, userId); registryHelper.updateEntityAndState(newRootNode, userId); - registryHelper.invalidateAttestation(entityName, entityId, userId); - + registryHelper.invalidateAttestation(entityName, entityId,userId); + registryHelper.autoRaiseClaim(entityName,entityId,existingNode,registryHelper.readEntity(newRootNode,entityId),userId); responseParams.setErrmsg(""); responseParams.setStatus(Response.Status.SUCCESSFUL); watch.stop(tag); diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/entities/AttestationPolicy.java b/java/registry/src/main/java/dev/sunbirdrc/registry/entities/AttestationPolicy.java index 74ea30f94..4babc66d3 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/entities/AttestationPolicy.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/entities/AttestationPolicy.java @@ -77,6 +77,9 @@ public String getNodePath() { public boolean isInternal() { return this.attestorPlugin.split(PLUGIN_SPLITTER)[1].equals(AttestorPluginType.internal.name()); } + public boolean hasAttestationPlugin(){ + return this.attestorPlugin!=null; + } public Map getAttestationProperties() { try { 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 6f43b2088..46ecf257d 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 @@ -10,6 +10,11 @@ import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import com.flipkart.zjsonpatch.JsonPatch; +import dev.sunbirdrc.actors.factory.PluginRouter; +import dev.sunbirdrc.keycloak.KeycloakAdminUtil; +import dev.sunbirdrc.pojos.OwnershipsAttributes; +import dev.sunbirdrc.pojos.PluginRequestMessage; +import dev.sunbirdrc.pojos.PluginRequestMessageCreator; import dev.sunbirdrc.pojos.PluginResponseMessage; import dev.sunbirdrc.pojos.SunbirdRCInstrumentation; import dev.sunbirdrc.pojos.attestation.Action; @@ -19,6 +24,7 @@ import dev.sunbirdrc.registry.exception.SignatureException; import dev.sunbirdrc.registry.exception.UnAuthorizedException; import dev.sunbirdrc.registry.middleware.MiddlewareHaltException; +import dev.sunbirdrc.registry.middleware.service.ConditionResolverService; import dev.sunbirdrc.registry.middleware.util.JSONUtil; import dev.sunbirdrc.registry.middleware.util.OSSystemFields; import dev.sunbirdrc.registry.model.DBConnectionInfoMgr; @@ -47,6 +53,7 @@ import javax.servlet.http.HttpServletRequest; import java.io.ByteArrayInputStream; import java.util.*; +import java.util.stream.Collectors; import static dev.sunbirdrc.registry.Constants.*; import static dev.sunbirdrc.registry.exception.ErrorMessages.*; @@ -81,6 +88,9 @@ public class RegistryHelper { @Autowired IValidate validationService; + @Autowired + ConditionResolverService conditionResolverService; + @Autowired private ISearchService searchService; @@ -132,7 +142,7 @@ public class RegistryHelper { @Autowired private EntityTypeHandler entityTypeHandler; - public String getAttestationOSID(JsonNode requestBody, String entityName, String entityId, String propertyName) throws Exception { + public String getAttestationOSID(JsonNode requestBody,String entityName, String entityId, String propertyName) throws Exception { JsonNode resultNode = readEntity("", entityName, entityId, false, null, false) .get(entityName) .get(propertyName); @@ -168,7 +178,7 @@ public String addEntity(JsonNode inputJson, String userId) throws Exception { } public String inviteEntity(JsonNode inputJson, String userId) throws Exception { - String entityId = addEntityHandler(inputJson, userId, true); + String entityId = newAddEntityHandler(inputJson, userId, true); sendInviteNotification(inputJson); return entityId; } @@ -196,6 +206,20 @@ private String addEntityHandler(JsonNode inputJson, String userId, boolean isInv return addEntity(inputJson, userId, entityType, isInvite); } + private String newAddEntityHandler(JsonNode inputJson, String userId, boolean isInvite) throws Exception { + String entityType = inputJson.fields().next().getKey(); + validationService.validate(entityType, objectMapper.writeValueAsString(inputJson), isInvite); + String entityName = inputJson.fields().next().getKey(); + List attestationPolicies = getAttestationPolicies(entityName); + entityStateHelper.applyWorkflowTransitions(JSONUtil.convertStringJsonNode("{}"), inputJson, attestationPolicies); + String entityId = addEntity(inputJson, userId, entityType,isInvite); + String keyCloakUserId = inputJson.fields().next().getValue().get("osOwner")!=null ?inputJson.fields().next().getValue().get("osOwner").get(0).asText():null; + if(keyCloakUserId!=null){ + autoRaiseClaim(entityName,entityId,objectMapper.createObjectNode(),inputJson,keyCloakUserId); + } + return entityId; + } + private void sendInviteNotification(JsonNode inputJson) throws Exception { String entityType = inputJson.fields().next().getKey(); sendNotificationToOwners(inputJson, INVITE, String.format(INVITE_SUBJECT_TEMPLATE, entityType), String.format(INVITE_BODY_TEMPLATE, entityType)); @@ -411,6 +435,16 @@ public String addAttestationProperty(String entityName, String entityId, String return updateEntityAndState(existingEntityNode, nodeToUpdate, userId); } + public String newAddAttestationProperty(JsonNode existingEntityNode, String propertyName, JsonNode inputJson) throws Exception { + JsonNode nodeToUpdate = existingEntityNode.deepCopy(); + String entityName = existingEntityNode.fields().next().getKey(); + JsonNode parentNode = nodeToUpdate.get(entityName); + JsonNode propertyNode = parentNode.get(propertyName); + createOrUpdateProperty(entityName, inputJson, nodeToUpdate, propertyName, (ObjectNode) parentNode, propertyNode); + String userId = existingEntityNode.fields().next().getValue().get("osOwner").get(0).asText(); + return updateEntityAndState(existingEntityNode, nodeToUpdate,userId); + } + private void createOrUpdateProperty(String entityName, JsonNode inputJson, JsonNode updateNode, String propertyName, ObjectNode parentNode, JsonNode propertyNode) throws JsonProcessingException { if (propertyNode != null && !propertyNode.isMissingNode()) { updateProperty(inputJson, propertyName, parentNode, propertyNode); @@ -495,6 +529,62 @@ public void attestEntity(String entityName, JsonNode node, String[] jsonPaths, S updateEntity(node, userId); } + private boolean hasPolicyPathChanged(AttestationPolicy policy, JsonNode existingNode, JsonNode updatedNode, String entityName){ + List paths = new ArrayList<>(policy.getAttestationProperties()==null?CollectionUtils.emptyCollection():policy.getAttestationProperties().values()); + boolean result = false; + for (String path : paths) { + if (!StringUtils.isEmpty(path)) { + if (existingNode.isEmpty() || !JSONUtil.parseJsonTree(path, updatedNode.get(entityName)).equals(JSONUtil.parseJsonTree(path, existingNode.get(entityName)))){ + result = true; + } + } + } + return result; + } + + public void autoRaiseClaim(String entityName, String entityId, JsonNode existingNode, JsonNode updatedNode, String userId) throws Exception { + List attestationPolicies = getAttestationPolicies(entityName); + for (AttestationPolicy policy : + attestationPolicies) { + if(hasPolicyPathChanged(policy,existingNode,updatedNode,entityName)){ + logger.info("Calling auto attestation actor"); + String attestationName = policy.getName(); + ObjectNode claimProperties = objectMapper.createObjectNode(); + claimProperties.put("entityName",entityName); + claimProperties.put("entityId",entityId); + claimProperties.put("name",attestationName); + JsonNode entityNode = readEntity(userId, entityName, entityId, false, null, false); + if(policy.hasAttestationPlugin()){ + if(policy.isInternal()) { + Map> propertyOSIDMapper = objectMapper.convertValue(claimProperties.get("propertiesOSID"), Map.class); + JsonNode propertyData = JSONUtil.extractPropertyDataFromEntity(entityNode.get(entityName), policy.getAttestationProperties(), propertyOSIDMapper); + if(!propertyData.isNull()) { + claimProperties.put("propertyData", propertyData.toString()); + } + newAddAttestationProperty(entityNode,attestationName, claimProperties); + String attestationOSID = getAttestationOSID(null,entityName, entityId, attestationName); + String condition = conditionResolverService.resolve(propertyData, "REQUESTER", policy.getConditions(), Collections.emptyList()); + PluginRequestMessage message = PluginRequestMessageCreator.create( + propertyData.toString(), condition, attestationOSID, + entityName,fetchEmailIdFromEntity(entityNode,entityName), entityId, null, Action.RAISE_CLAIM.name(), policy.getName(), + policy.getAttestorPlugin(), policy.getAttestorEntity(), + policy.getAttestorSignin()); + PluginRouter.route(message); + } else { + newAddAttestationProperty(entityNode,attestationName, claimProperties); + String attestationOSID = getAttestationOSID(null,entityName, entityId, attestationName); + PluginRequestMessage pluginRequestMessage = PluginRequestMessageCreator.create( + "", "", attestationOSID, + entityName,fetchEmailIdFromEntity(entityNode,entityName), entityId, null, Action.RAISE_CLAIM.name(), policy.getName(), + policy.getAttestorPlugin(), policy.getAttestorEntity(), + policy.getAttestorSignin()); + PluginRouter.route(pluginRequestMessage); + } + } + } + } + } + public void updateState(PluginResponseMessage pluginResponseMessage) throws Exception { String attestationName = pluginResponseMessage.getPolicyName(); String attestationOSID = pluginResponseMessage.getAttestationOSID(); @@ -622,6 +712,17 @@ private String fetchUserIdFromToken(HttpServletRequest request) throws Exception throw new Exception("Forbidden"); } + private String fetchEmailIdFromEntity(JsonNode entityNode,String entityName){ + List ownershipAttributes = definitionsManager.getOwnershipAttributes(entityName); + String entityEmail=""; + for (OwnershipsAttributes attributes:ownershipAttributes){ + String email = entityNode.at(String.format("/%s%s", entityName,attributes.getEmail())).asText(""); + if(StringUtils.isNotEmpty(email)){ + entityEmail = email; + } + } + return entityEmail; + } public String fetchEmailIdFromToken(HttpServletRequest request, String entityName) throws Exception { if (doesEntityContainOwnershipAttributes(entityName) || getManageRoles(entityName).size() > 0) { KeycloakAuthenticationToken principal = (KeycloakAuthenticationToken) request.getUserPrincipal(); diff --git a/java/registry/src/test/java/dev/sunbirdrc/registry/helper/RegistryHelperTest.java b/java/registry/src/test/java/dev/sunbirdrc/registry/helper/RegistryHelperTest.java index 65398cb40..f9c5150d1 100644 --- a/java/registry/src/test/java/dev/sunbirdrc/registry/helper/RegistryHelperTest.java +++ b/java/registry/src/test/java/dev/sunbirdrc/registry/helper/RegistryHelperTest.java @@ -317,6 +317,7 @@ public void shouldCreateOwnersForInvite() throws Exception { when(keycloakAdminUtil.createUser(any(), any(), any(), any())).thenReturn(testUserId); when(registryService.addEntity(any(), any(), any(), anyBoolean())).thenReturn(UUID.randomUUID().toString()); when(shardManager.getShard(any())).thenReturn(new Shard()); + when(readService.getEntity(any(),any(),any(),any(),any())).thenReturn(inviteJson); ReflectionTestUtils.setField(registryHelper, "workflowEnabled", true); registryHelper.inviteEntity(inviteJson, ""); Mockito.verify(registryService).addEntity(shardCapture.capture(), userIdCapture.capture(), inputJsonCapture.capture(), anyBoolean()); @@ -331,6 +332,7 @@ public void shouldSendInviteInvitationsAfterCreatingOwners() throws Exception { when(keycloakAdminUtil.createUser(any(), any(), any(), any())).thenReturn(testUserId); when(registryService.addEntity(any(), any(), any(), anyBoolean())).thenReturn(UUID.randomUUID().toString()); when(shardManager.getShard(any())).thenReturn(new Shard()); + when(readService.getEntity(any(),any(),any(),any(),any())).thenReturn(inviteJson); registryHelper.inviteEntity(inviteJson, ""); Mockito.verify(registryService).addEntity(shardCapture.capture(), userIdCapture.capture(), inputJsonCapture.capture(), anyBoolean()); Mockito.verify(registryService, atLeastOnce()).callNotificationActors(operationCapture.capture(), toCapture.capture(), subjectCapture.capture(), messageCapture.capture()); @@ -361,6 +363,7 @@ public void shouldSendMultipleInviteInvitationsAfterCreatingOwners() throws Exce when(keycloakAdminUtil.createUser(any(), any(), any(), any())).thenReturn(testUserId); when(registryService.addEntity(any(), any(), any(), anyBoolean())).thenReturn(UUID.randomUUID().toString()); when(shardManager.getShard(any())).thenReturn(new Shard()); + when(readService.getEntity(any(),any(),any(),any(),any())).thenReturn(inviteJson); mockDefinitionManager(); registryHelper.inviteEntity(inviteJson, ""); Mockito.verify(registryService).addEntity(shardCapture.capture(), userIdCapture.capture(), inputJsonCapture.capture(), anyBoolean());