Skip to content

Commit

Permalink
Merge pull request Sunbird-RC#13 from kesavanp123/generic/apis
Browse files Browse the repository at this point in the history
Generic/apis
  • Loading branch information
tejash-jl authored Jun 29, 2021
2 parents 98254e7 + f4bc5de commit 1538f01
Show file tree
Hide file tree
Showing 30 changed files with 651 additions and 323 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ public class AttributeNames {
public static final String NOTES = "notes";
public static final String ATTESTED_DATA = "attestedData";
public static final String ACTION = "action";
public static final String PROPERTY_ID = "PROPERTY_ID";
public static final String PROPERTY = "PROPERTY";
public static final String PROPERTY_URI = "PROPERTY_URI";
public static final String ENTITY_ID = "ENTITY_ID";
public static final String ENTITY = "ENTITY";
public static final String LOWERCASE_ENTITY = "entity";;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

public class OpensaberApiUrlPaths {
public static final String ATTESTATION_PROPERTIES = "/api/v1/ENTITY/ENTITY_ID/attestationProperties";
public static final String ATTEST = "/api/v1/ENTITY/ENTITY_ID/PROPERTY/PROPERTY_ID/attest";
public static final String ATTEST = "/api/v1/ENTITY/ENTITY_ID/attest/PROPERTY_URI";
public static final String USER_INFO = "/api/v1/ENTITY";
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
package io.opensaber.claim.controller;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.node.ArrayNode;
import io.opensaber.claim.entity.Claim;
import io.opensaber.claim.exception.InvalidInputException;
import io.opensaber.claim.model.AttestorActions;
import io.opensaber.claim.service.ClaimService;
import io.opensaber.pojos.dto.ClaimDTO;
import org.slf4j.Logger;
Expand All @@ -19,11 +13,10 @@
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.*;
import java.util.List;

import static io.opensaber.claim.contants.AttributeNames.*;
import static io.opensaber.claim.contants.ErrorMessages.ACCESS_TOKEN_IS_MISSING;
import static io.opensaber.claim.contants.AttributeNames.ATTESTOR_INFO;
import static io.opensaber.claim.contants.AttributeNames.LOWERCASE_ENTITY;

@Controller
public class ClaimsController {
Expand All @@ -45,23 +38,6 @@ public ResponseEntity<List<Claim>> getClaims(@RequestHeader HttpHeaders headers,
return new ResponseEntity<>(claims, HttpStatus.OK);
}

private List<String> getAccessToken(@RequestHeader HttpHeaders headers) throws IOException {
String AUTHORIZATION = "AUTHORIZATION";
if(!headers.containsKey(AUTHORIZATION)) {
throw new InvalidInputException(ACCESS_TOKEN_IS_MISSING);
}
String accessToken = headers.get(AUTHORIZATION).get(0).split(" ")[1];
Base64.Decoder decoder = Base64.getDecoder();
String payloadPart = accessToken.split("\\.")[1];
String payloadStr = new String(decoder.decode(payloadPart));
ObjectMapper objectMapper = new ObjectMapper();
JsonNode payload = objectMapper.readTree(payloadStr);
ArrayNode node = (ArrayNode) payload.get(LOWERCASE_ENTITY);
ObjectReader reader = objectMapper.readerFor(new TypeReference<List<String>>() {
});
return reader.readValue(node);
}

@RequestMapping(value = "/api/v1/claims", method = RequestMethod.POST)
public ResponseEntity<Claim> save(@RequestBody ClaimDTO claimDTO) {
logger.info("Adding new claimDTO {} ", claimDTO.toString());
Expand All @@ -70,24 +46,13 @@ public ResponseEntity<Claim> save(@RequestBody ClaimDTO claimDTO) {
}

@RequestMapping(value = "/api/v1/claims/{claimId}", method = RequestMethod.POST)
public ResponseEntity<Object> attestClaims(@PathVariable String claimId,
@RequestHeader HttpHeaders headers,
@RequestBody JsonNode requestBody) throws IOException {
List<String> entities = getAccessToken(headers);
AttestorActions action = AttestorActions.valueOf(requestBody.get(ACTION).asText());
switch (action) {
case GRANTED:
claimService.grantClaim(claimId, entities, headers);
break;
case DENIED:
Optional<String> notes = Optional.empty();
if(requestBody.has(NOTES)) {
notes = Optional.of(requestBody.get(NOTES).asText());
}
claimService.updateNotes(claimId, notes, headers, entities);
break;
}
return new ResponseEntity<>(HttpStatus.OK);
public ResponseEntity<Object> attestClaims(@PathVariable String claimId, @RequestBody JsonNode requestBody) {
logger.info("Attesting claim : {}", claimId);
return claimService.attestClaim(
claimId,
requestBody.get(ATTESTOR_INFO),
requestBody
);
}

}
27 changes: 8 additions & 19 deletions java/claim/src/main/java/io/opensaber/claim/entity/Claim.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,8 @@ public class Claim {
@Column
private String entityId;
@Column
private String property;
@Column
private String propertyId;
@Column(name=Claim.CREATED_AT)
private String propertyURI;
@Column(name = Claim.CREATED_AT)
private Date createdAt;
@Column(name=Claim.ATTESTED_ON)
private Date attestedOn;
Expand Down Expand Up @@ -73,20 +71,12 @@ public void setEntityId(String entityId) {
this.entityId = entityId;
}

public String getProperty() {
return property;
}

public void setProperty(String property) {
this.property = property;
}

public String getPropertyId() {
return propertyId;
public String getPropertyURI() {
return propertyURI;
}

public void setPropertyId(String propertyId) {
this.propertyId = propertyId;
public void setPropertyURI(String propertyURI) {
this.propertyURI = propertyURI;
}

public Date getCreatedAt() {
Expand Down Expand Up @@ -126,8 +116,7 @@ public String toString() {
return "Claim{" +
"entity='" + entity + '\'' +
", entityId='" + entityId + '\'' +
", property='" + property + '\'' +
", propertyId='" + propertyId + '\'' +
", propertyURI='" + propertyURI + '\'' +
", status='" + status + '\'' +
'}';
}
Expand All @@ -154,7 +143,7 @@ public void setAttestorEntity(String attestorEntity) {

public static Claim fromDTO(ClaimDTO claimDTO) {
Claim claim = new Claim();
claim.setProperty(claimDTO.getPropertyURI());
claim.setPropertyURI(claimDTO.getPropertyURI());
claim.setEntity(claimDTO.getEntity());
claim.setEntityId(claimDTO.getEntityId());
claim.setConditions(claimDTO.getConditions());
Expand Down
102 changes: 21 additions & 81 deletions java/claim/src/main/java/io/opensaber/claim/service/ClaimService.java
Original file line number Diff line number Diff line change
@@ -1,43 +1,37 @@
package io.opensaber.claim.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import io.opensaber.claim.entity.Claim;
import io.opensaber.claim.exception.ClaimAlreadyProcessedException;
import io.opensaber.claim.exception.UnAuthorizedException;
import io.opensaber.claim.exception.ResourceNotFoundException;
import io.opensaber.claim.model.ClaimStatus;
import io.opensaber.claim.exception.UnAuthorizedException;
import io.opensaber.claim.repository.ClaimRepository;
import io.opensaber.pojos.attestation.AttestationPolicy;
import io.opensaber.registry.middleware.service.ConditionResolverService;
import net.minidev.json.JSONArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static io.opensaber.claim.contants.AttributeNames.PROPERTY_ID;
import static io.opensaber.claim.contants.ErrorMessages.*;

@Service
public class ClaimService {

private final ClaimRepository claimRepository;
private final OpenSaberClient openSaberClient;
private final ConditionResolverService conditionResolverService;
private final ClaimsAuthorizer claimsAuthorizer;
private static final Logger logger = LoggerFactory.getLogger(ClaimService.class);

@Autowired
public ClaimService(ClaimRepository claimRepository, OpenSaberClient openSaberClient, ConditionResolverService conditionResolverService) {
public ClaimService(ClaimRepository claimRepository, OpenSaberClient openSaberClient, ClaimsAuthorizer claimsAuthorizer) {
this.claimRepository = claimRepository;
this.openSaberClient = openSaberClient;
this.conditionResolverService = conditionResolverService;
this.claimsAuthorizer = claimsAuthorizer;
}

public Claim save(Claim claim) {
Expand All @@ -54,83 +48,29 @@ public List<Claim> findAll() {

public List<Claim> findClaimsForAttestor(String entity, JsonNode attestorNode) {
List<Claim> claims = claimRepository.findByAttestorEntity(entity);
return claims.stream().filter(claim -> {
String ATTESTOR = "ATTESTOR";
String resolvedCondition = conditionResolverService.resolve(attestorNode,
ATTESTOR, claim.getConditions(), Collections.emptyList());
return conditionResolverService.evaluate(resolvedCondition);
}).collect(Collectors.toList());
return claims.stream()
.filter(claim -> claimsAuthorizer.isAuthorized(claim, attestorNode))
.collect(Collectors.toList());
}

public void updateNotes(String claimId, Optional<String> notes, HttpHeaders headers, List<String> conditions) {
logger.info("Initiating denial action for claim with id{} ", claimId);
public ResponseEntity<Object> attestClaim(String claimId, JsonNode attestorNode, JsonNode request) {
Claim claim = findById(claimId).orElseThrow(() -> new ResourceNotFoundException(CLAIM_NOT_FOUND));
if(claim.isClosed()) {
if (claim.isClosed()) {
throw new ClaimAlreadyProcessedException(CLAIM_IS_ALREADY_PROCESSED);
}
if(!conditions.contains(claim.getAttestorEntity())) {
if (!claimsAuthorizer.isAuthorized(claim, attestorNode)) {
throw new UnAuthorizedException(USER_NOT_AUTHORIZED);
}
claim.setNotes(notes.orElse(""));
claim.setStatus(ClaimStatus.CLOSED.name());
claim.setAttestedOn(new Date());
save(claim);
openSaberClient.updateAttestedProperty(claim, headers);
logger.info("Clam with id {} is successfully denied", claimId);
updateClaim(request, claim);
return openSaberClient.sendAttestationResponseToRequester(claim, request);
}

public void grantClaim(String claimId, List<String> conditions, HttpHeaders header) {
logger.info("Initiating grant action for claim with id {} ", claimId);
Claim claim = findById(claimId).orElseThrow(() -> new ResourceNotFoundException(CLAIM_NOT_FOUND));
if(claim.isClosed()) {
throw new ClaimAlreadyProcessedException(CLAIM_IS_ALREADY_PROCESSED);
}
if(!conditions.contains(claim.getAttestorEntity())) {
throw new UnAuthorizedException(USER_NOT_AUTHORIZED);
private void updateClaim(JsonNode request, Claim claim) {
if(request.has("notes")) {
claim.setNotes(request.get("notes").asText());
}
AttestationPropertiesDTO attestationProperties = openSaberClient.getAttestationProperties(claim);
Optional<AttestationPolicy> attestationPolicyOptional = getAttestationPolicy(claim, attestationProperties);
AttestationPolicy attestationPolicy = attestationPolicyOptional.orElseThrow(() -> new ResourceNotFoundException(ATTESTATION_POLICY_IS_NOT_FOUND));
logger.info("Found the attestation policy {} ", attestationPolicy.toString());
Map<String, Object> attestedData = generateAttestedData(attestationProperties.getEntityAsJsonNode(), attestationPolicy, claim.getPropertyId());
claim.setStatus(ClaimStatus.CLOSED.name());
claim.setAttestedOn(new Date());
save(claim);
JsonNode node = new ObjectMapper().convertValue(attestedData, JsonNode.class);
openSaberClient.updateAttestedProperty(claim, node.toString(), header);
logger.info("Clam with id {} is successfully granted", claimId);
}

private Map<String, Object> generateAttestedData(JsonNode entityNode, AttestationPolicy attestationPolicy, String propertyId) {
Map<String, Object> attestedData = new HashMap<>();
for (String path: attestationPolicy.getPaths()) {
if(path.contains(PROPERTY_ID)) {
path = path.replace(PROPERTY_ID, propertyId);
}
DocumentContext context = JsonPath.parse(entityNode.toString());
Object result = context.read(path);
if(result.getClass().equals(JSONArray.class)) {
HashMap<String, Object> extractedVal = (HashMap) ((JSONArray) result).get(0);
attestedData.putAll(extractedVal);
} else if(result.getClass().equals(LinkedHashMap.class)) {
attestedData.putAll((HashMap) result);
}
else {
// It means it is just a value,
attestedData.putAll(
new HashMap<String, Object>(){{
put(attestationPolicy.getProperty(), result);
}}
);
}
}
return attestedData;
}

private Optional<AttestationPolicy> getAttestationPolicy(Claim claim, AttestationPropertiesDTO attestationProperties) {
return attestationProperties.getAttestationPolicies()
.stream()
.filter(policy -> policy.hasProperty(claim.getProperty()))
.findFirst();
claim.setStatus("CLOSED");
claimRepository.save(claim);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.opensaber.claim.service;

import com.fasterxml.jackson.databind.JsonNode;
import io.opensaber.claim.entity.Claim;
import io.opensaber.registry.middleware.service.ConditionResolverService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collections;

@Service
public class ClaimsAuthorizer {

private static final String ATTESTOR = "ATTESTOR";

@Autowired
private ConditionResolverService conditionResolverService;

public boolean isAuthorized(Claim claim, JsonNode attestorNode) {
String resolvedCondition = conditionResolverService.resolve(
attestorNode,
ATTESTOR,
claim.getConditions(),
Collections.emptyList()
);
return conditionResolverService.evaluate(resolvedCondition);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import io.opensaber.claim.contants.OpensaberApiUrlPaths;
import io.opensaber.claim.entity.Claim;
import io.opensaber.claim.exception.ResourceNotFoundException;
import io.opensaber.claim.model.AttestorActions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -14,7 +13,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;

import static io.opensaber.claim.contants.AttributeNames.*;


Expand All @@ -36,33 +35,13 @@ public AttestationPropertiesDTO getAttestationProperties(Claim claim) {
return restTemplate.getForObject(url, AttestationPropertiesDTO.class);
}

public void updateAttestedProperty(Claim claim, HttpHeaders headers) {
String url = openSaberUrl + OpensaberApiUrlPaths.ATTEST
.replace(ENTITY_ID, claim.getEntityId())
.replace(ENTITY, claim.getEntity())
.replace(PROPERTY_ID, claim.getPropertyId())
.replace(PROPERTY, claim.getProperty());
HashMap<String, Object> requestBody = new HashMap<String, Object>() {{
put(ACTION, AttestorActions.DENIED);
}};
HttpEntity<HashMap<String, Object>> entity = new HttpEntity<>(requestBody, headers);
logger.info("Sending request to {}", url);
restTemplate.postForObject(url, entity, Void.class);
}

public void updateAttestedProperty(Claim claim, String attestedData, HttpHeaders headers) {
HashMap<String, Object> requestBody = new HashMap<String, Object>() {{
put(ACTION, AttestorActions.GRANTED);
put(ATTESTED_DATA, attestedData);
}};
public ResponseEntity<Object> sendAttestationResponseToRequester(Claim claim, JsonNode request) {
String url = openSaberUrl + OpensaberApiUrlPaths.ATTEST
.replace(ENTITY_ID, claim.getEntityId())
.replace(ENTITY, claim.getEntity())
.replace(PROPERTY_ID, claim.getPropertyId())
.replace(PROPERTY, claim.getProperty());
HttpEntity<HashMap<String, Object>> entity = new HttpEntity<>(requestBody, headers);
logger.info("Sending request to {}", url);
restTemplate.postForObject(url, entity, Void.class);
.replace(PROPERTY_URI, claim.getPropertyURI());
logger.info("Sending attestation request to {}", url);
return restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(request), Object.class);
}

public JsonNode getEntity(String entity, HttpHeaders headers) {
Expand Down
Loading

0 comments on commit 1538f01

Please sign in to comment.