Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto Raise Claim on Add or Update Entity effecting Attestation policy paths #101

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@ public static void addField(ObjectNode parent, String childKey, List<String> 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)
*
Expand Down Expand Up @@ -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<String, List<String>> requestBodyProperty = objectMapper.convertValue(requestBody.get("propertiesOSID"), Map.class);
Iterator<JsonNode> claimIterator = arrayNode.elements();
while (claimIterator.hasNext()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ public ResponseEntity<Object> 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);
Expand All @@ -180,7 +180,7 @@ public ResponseEntity<Object> 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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,10 @@ public ResponseEntity<Object> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> getAttestationProperties() {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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.*;
Expand Down Expand Up @@ -81,6 +88,9 @@ public class RegistryHelper {
@Autowired
IValidate validationService;

@Autowired
ConditionResolverService conditionResolverService;

@Autowired
private ISearchService searchService;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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<AttestationPolicy> 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));
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<String> 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<AttestationPolicy> 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<String, List<String>> 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();
Expand Down Expand Up @@ -622,6 +712,17 @@ private String fetchUserIdFromToken(HttpServletRequest request) throws Exception
throw new Exception("Forbidden");
}

private String fetchEmailIdFromEntity(JsonNode entityNode,String entityName){
List<OwnershipsAttributes> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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());
Expand Down Expand Up @@ -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());
Expand Down