eu.operando.PolicyEvaluationService.java Source code

Java tutorial

Introduction

Here is the source code for eu.operando.PolicyEvaluationService.java

Source

/////////////////////////////////////////////////////////////////////////
//
//  University of Southampton IT Innovation Centre, 2016
//
// Copyright in this library belongs to the University of Southampton
// University Road, Highfield, Southampton, UK, SO17 1BJ
//
// This software may not be used, sold, licensed, transferred, copied
// or reproduced in whole or in part in any manner or form or in or
// on any media by any person other than in accordance with the terms
// of the Licence Agreement supplied with the software, or otherwise
// without the prior written consent of the copyright owners.
//
// This software is distributed WITHOUT ANY WARRANTY, without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE, except where stated in the Licence Agreement supplied with
// the software.
//
// Created By : Paul Grace
// Created for Project : OPERANDO (http://www.operando.eu)
//
/////////////////////////////////////////////////////////////////////////
//
//  License : GNU Lesser General Public License, version 3
//
/////////////////////////////////////////////////////////////////////////

package eu.operando;

import com.google.common.base.Charsets;
import com.google.common.io.CharStreams;
import com.google.common.io.Closeables;
import com.jayway.jsonpath.JsonPath;
import io.swagger.api.NotFoundException;
import eu.operando.core.pdb.common.model.OSPDataRequest;
import eu.operando.core.pdb.common.model.OSPDataRequest.ActionEnum;
import eu.operando.core.pdb.common.model.UserPreference;
import io.swagger.model.PolicyEvaluationReport;
import io.swagger.model.RequestEvaluation;
import io.swagger.model.UserPolicyEvaluationReport;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.minidev.json.JSONArray;
import org.apache.http.HttpEntity;
import org.apache.http.ParseException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

/**
 * The core service methods that implement the evaluation methods of the
 * web service.
 */
public class PolicyEvaluationService {

    private class EvalStatus {
        public boolean found;
        public boolean permit;
        public RequestEvaluation rEv;

        public EvalStatus(boolean found, boolean permit, RequestEvaluation rEv) {
            this.found = found;
            this.permit = permit;
            this.rEv = rEv;
        }

    }

    /**
     * This is a singleton class, that can be used as one instance by all
     * REST APIs of the PC component.
     */
    private static PolicyEvaluationService instance = null;
    private UPPMongo uppMongodb = null;

    /**
     * Operation to use to enforce singleton pattern.
     * @return The singleton instance.
     */
    public static PolicyEvaluationService getInstance() {
        if (instance == null) {
            instance = new PolicyEvaluationService();
        }
        return instance;
    }

    /**
     * The set of demo UPP profiles; that can be queried for unit testing of
     * the evaluation service.
     */
    private final HashMap<String, String> UppDB;

    /**
     * The Policy Evaluation Component depends upon multiple entries to different
     * components. Hence, testing and unit testing is impossible without
     * full integration testing. Hence, this component contains a set of
     * demo user preferences.
     *
     * This method loads these demo UPPs into local memory
     *
     * @param name The name of the user id to load into memory.
     * @param fileLoc The filename in the resources directory.
     */
    private void loadDemoUPP(String name, String fileLoc) {

        InputStream fis = null;
        try {
            fis = this.getClass().getClassLoader().getResourceAsStream(fileLoc);
            String content = CharStreams.toString(new InputStreamReader(fis, Charsets.UTF_8));
            Closeables.closeQuietly(fis);
            UppDB.put(name, content);
        } catch (IOException e) {
            // Display to console for debugging purposes.
            System.err.println("Error reading Configuration properties file");

            // Add logging code to log an error configuring the API on startup
        }
    }

    /**
     * Initiate the evaluation service component, and create a set of three
     * example users for unit testing.
     */
    protected PolicyEvaluationService() {
        UppDB = new HashMap<String, String>();
        Properties props = loadDbProperties();

        uppMongodb = new UPPMongo(props.getProperty("mongo.server.host"),
                Integer.parseInt(props.getProperty("mongo.server.port")));
        try {
            loadDemoUPP("_demo_user1", "upp1.json");
            loadDemoUPP("_demo_user2", "upp2.json");
            loadDemoUPP("_demo_user3", "upp3.json");
            loadDemoUPP("osp1", "osp1.json");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Load the configuration properties from the resource file in JAR/WAR and
     * turn then into JAVA properties class.
     * @return The list of JAVA properties reflecting the configuration.
     */
    private Properties loadDbProperties() {
        Properties props;
        props = new Properties();

        InputStream fis = null;
        try {
            fis = this.getClass().getClassLoader().getResourceAsStream("operando.properties");
            props.load(fis);
        } catch (IOException e) {
            // Display to console for debugging purposes.
            System.err.println("Error reading Configuration properties file");

            // Add logging code to log an error configuring the API on startup
        }

        return props;
    }

    /**
     * Retrieve a demo user privacy profile (UPP) from the set of in-memory
     * test cases.
     * @param id The user id of the UPP requested
     * @return The UPP file in json format.
     */
    public String getUPP(String id) {
        return UppDB.get(id);
    }

    /**
     * Update a demo user privacy profile (UPP) from the set of in-memory
     * test cases.
     * @param id The user id of the
     * @param upp The new policy
     * @return
     */
    public String putUPP(String id, String upp) {
        return UppDB.put(id, upp);
    }

    private OSPDataRequest actionCheck(OSPDataRequest ospRequest) {
        if (ospRequest.getAction().compareTo(OSPDataRequest.ActionEnum.SELECT) == 0) {
            ospRequest.setAction(OSPDataRequest.ActionEnum.ACCESS);
        } else if (ospRequest.getAction().compareTo(OSPDataRequest.ActionEnum.INSERT) == 0) {
            ospRequest.setAction(OSPDataRequest.ActionEnum.CREATE);
        }
        return ospRequest;
    }

    private String getUPPviaPDB(String userId, String pdbURL) {
        String uppProfile = null;
        try {

            /**
             * Get the UPP from the PDB.
             */
            CloseableHttpClient httpclient = HttpClients.createDefault();
            HttpGet httpget = new HttpGet(pdbURL + "/" + userId);
            CloseableHttpResponse response1 = httpclient.execute(httpget);

            /**
             * If there is no UPP, then it returns an non-compliance report
             * with a NO_POLICY statement.
             */
            HttpEntity entity = response1.getEntity();
            if (response1.getStatusLine().getStatusCode() == 404) {
                return null;
            }
            uppProfile = EntityUtils.toString(entity);
            httpclient.close();
            response1.close();
            httpget.releaseConnection();

        } catch (IOException ex) {
            return null;
        }
        return uppProfile;
    }

    private String getUPPviaMongo(String userId) {
        return uppMongodb.getUPPById(userId);
    }

    PolicyEvaluationReport ownDataReport(List<OSPDataRequest> ospRequests) {
        PolicyEvaluationReport rp = new PolicyEvaluationReport();

        for (OSPDataRequest ospReq : ospRequests) {
            RequestEvaluation rEv = new RequestEvaluation();
            rEv.setDatauser(ospReq.getSubject());
            rEv.setDatafield(ospReq.getRequestedUrl());
            rEv.setAction(ospReq.getAction().name());
            rEv.setResult(Boolean.TRUE);
            rp.addEvaluationsItem(rEv);
        }
        rp.setStatus("true");
        rp.setCompliance("VALID");
        return rp;
    }

    public boolean isOwnData(String userId, List<OSPDataRequest> ospRequests) {
        for (OSPDataRequest ospReq : ospRequests) {
            if (!ospReq.getRequesterId().equalsIgnoreCase(userId))
                return false;
        }
        return true;
    }

    /**
     * Core implementation of the policy evaluation service. Evaluates if a set
     * of requests matches a user's privacy preferences.
     *
     * @param ospId The ID of the OSP. This is used to identify existing user policies already computed.
     * @param userId The unique OPERANDO id of the user, obtained when they register with the OPERANDO dashboard.
     * @param ospRequest The array of individual ODATA field requests.
     * @param pdbURL The URL of the PDB server where UPPs are deployed.
     * @return
     * @throws NotFoundException
     */
    public PolicyEvaluationReport evaluate(String ospId, String userId, List<OSPDataRequest> ospRequest,
            String pdbURL, String ospURL) throws NotFoundException {

        try {
            if (isOwnData(userId, ospRequest))
                return ownDataReport(ospRequest);

            /**
             * The response to be sent - yes/no along with a report of why something
             * has been denied.
             */
            PolicyEvaluationReport rp = new PolicyEvaluationReport();
            String uppProfile = "";
            if (userId.startsWith("_demo_")) {
                uppProfile = getUPP(userId);
                /**
                 * If someone sends an idiot demo request then fail the request
                 */
                if (uppProfile == null) {
                    rp.setStatus("false");
                    rp.setCompliance("NO_POLICY");
                    return rp;
                }
            } else {
                uppProfile = getUPPviaPDB(userId, pdbURL);
                //                uppProfile = getUPPviaMongo(userId);
                if (uppProfile == null) {
                    rp.setStatus("false");
                    rp.setCompliance("NO_POLICY");
                    String res = (new PolicyComputerService()).ospPolicyComputerPost(userId, ospId,
                            new ArrayList<UserPreference>(), pdbURL, ospURL, instance);
                    if (!res.equalsIgnoreCase("success")) {
                        return rp;
                    }
                    return evaluate(ospId, userId, ospRequest, pdbURL, ospURL);
                }
            }

            boolean permit = true;
            ODATAPolicies odata = new ODATAPolicies();
            /**
             * Evaluate the oData field request against the UPP user access policies
             */
            for (OSPDataRequest rIn : ospRequest) {
                ActionEnum actionInput = rIn.getAction();
                OSPDataRequest r = actionCheck(rIn);
                String oDataURL = r.getRequestedUrl();
                String Category = odata.getElementDataPath(oDataURL);
                JSONArray access_policies = JsonPath.read(uppProfile, "$.subscribed_osp_policies[?(@.osp_id=='"
                        + ospId + "')].access_policies[?(@.resource=='" + Category + "')]");
                while ((access_policies.isEmpty()) && (Category.length() > 0)) {
                    try {
                        Category = Category.substring(0, Category.lastIndexOf("/"));
                        System.out.println("Category 22 = " + Category);
                        access_policies = JsonPath.read(uppProfile, "$.subscribed_osp_policies[?(@.osp_id=='"
                                + ospId + "')].access_policies[?(@.resource=='" + Category + "')]");
                        System.out.println("Acces policies 22 = " + access_policies.size());
                    } catch (Exception e) {
                        break;
                    }

                }

                boolean found = false;
                // For each of the access requests in the list
                for (Object aP : access_policies) {
                    String subject = JsonPath.read(aP, "$.subject");
                    if (subject.equalsIgnoreCase(r.getSubject())) { // Check the subject
                        if (JsonPath.read(aP, "$.action").toString().equalsIgnoreCase(r.getAction().name())) { // Check the action
                            found = true;
                            boolean perm = Boolean.parseBoolean(JsonPath.read(aP, "$.permission").toString());
                            RequestEvaluation rEv = new RequestEvaluation();
                            rEv.setDatauser(r.getSubject());
                            rEv.setDatafield(oDataURL);
                            rEv.setAction(actionInput.name());
                            if (!perm) {
                                permit = false;
                                rEv.setResult(false);
                                rp.addEvaluationsItem(rEv);
                            } else {
                                rEv.setResult(true);
                                rp.addEvaluationsItem(rEv);
                            }
                        }
                        //                        else {
                        //                            permit = false;
                        //                            RequestEvaluation rEv = new RequestEvaluation();
                        //                                rEv.setDatauser(r.getSubject());
                        //                                rEv.setDatafield(oDataURL);
                        //                                rEv.setAction(r.getAction().name());
                        //                                rEv.setResult(false);
                        //                                rp.addEvaluationsItem(rEv);
                        //                        }
                    }
                }
                if (!found) {
                    /**
                     * If no policy is found then we use the preferences
                     */
                    String role = odata.getEPSOSRole(r.getSubject());
                    int prefWeighting = odata.getPreferenceRank(uppProfile, Category, role);
                    permit = odata.grantOnWeighting(uppProfile, "Medical", role, prefWeighting);

                    RequestEvaluation rEv = new RequestEvaluation();
                    rEv.setDatauser(r.getSubject());
                    rEv.setDatafield(oDataURL);
                    rEv.setAction(actionInput.name());
                    rEv.setResult(permit);
                    rp.addEvaluationsItem(rEv);
                }
            }

            if (permit) {
                rp.setStatus("true");
                rp.setCompliance("VALID");
            } else {
                rp.setStatus("false");
                rp.setCompliance("PREFS_CONFLICT");
            }

            String policyReport = rp.toString();
            System.out.println(policyReport);
            System.gc();
            return rp;
        } catch (InvalidPreferenceException | ParseException ex) {
            System.err.println("Evaluation error - " + ex.getMessage());
            PolicyEvaluationReport rp = new PolicyEvaluationReport();
            rp.setStatus("false");
            rp.setCompliance("NO_POLICY");
            return rp;
        }
    }

    /**
     * Core implementation of the policy evaluation service. Evaluates if a set
     * of requests matches a user's privacy preferences.
     *
     * @param ospId The ID of the OSP. This is used to identify existing user policies already computed.
     * @param ospRequest The array of individual ODATA field requests.
     * @return
     * @throws NotFoundException
     */
    public String batchEvaluate(String ospId, List<OSPDataRequest> ospRequest, String upps)
            throws NotFoundException {
        // for each user create an evaluation report
        String report = "[";
        JSONArray uppElements = JsonPath.read(upps, "$.*.user_id");
        for (Object Upp : uppElements) {
            String uppCal = Upp.toString();

            UserPolicyEvaluationReport idividualEval = idividualEval(upps, uppCal, ospId, ospRequest);
            report += idividualEval.toString() + ",";
        }
        if (report.length() > 1) {
            report = report.substring(0, report.length() - 1);
        } else {
            report += "]";
        }
        return report;
    }

    private UserPolicyEvaluationReport idividualEval(String uppIn, String userId, String ospId,
            List<OSPDataRequest> ospRequest) {
        UserPolicyEvaluationReport rp = new UserPolicyEvaluationReport();
        rp.setId(userId);

        boolean permit = true;
        ODATAPolicies odata = new ODATAPolicies();
        /**
         * Evaluate the oData field request against the UPP user access policies
         */
        for (OSPDataRequest rIn : ospRequest) {
            ActionEnum actionInput = rIn.getAction();
            OSPDataRequest r = actionCheck(rIn);
            String oDataURL = r.getRequestedUrl();
            String Category = "ALL";
            try {
                Category = odata.getElementDataPath(oDataURL);
            } catch (InvalidPreferenceException ex) {
                Logger.getLogger(PolicyEvaluationService.class.getName()).log(Level.SEVERE, null, ex);
            }
            JSONArray access_policies = JsonPath.read(uppIn,
                    "$..[?(@.user_id=='" + userId + "')].subscribed_osp_policies[?(@.osp_id=='" + ospId
                            + "')].access_policies[?(@.resource=='" + Category + "')]");
            while ((access_policies.isEmpty()) && (Category.length() > 0)) {
                try {
                    Category = Category.substring(0, Category.lastIndexOf("/"));
                    System.out.println("Category 22 = " + Category);
                    access_policies = JsonPath.read(uppIn,
                            "$..[?(@.user_id=='" + userId + "')].subscribed_osp_policies[?(@.osp_id=='" + ospId
                                    + "')].access_policies[?(@.resource=='" + Category + "')]");
                    System.out.println("Acces policies 22 = " + access_policies.size());
                } catch (Exception e) {
                    break;
                }

            }

            // For each of the access requests in the list
            for (Object aP : access_policies) {
                String subject = JsonPath.read(aP, "$.subject");
                if (subject.equalsIgnoreCase(r.getSubject())) { // Check the subject
                    if (JsonPath.read(aP, "$.action").toString().equalsIgnoreCase(r.getAction().name())) { // Check the action
                        boolean perm = Boolean.parseBoolean(JsonPath.read(aP, "$.permission").toString());
                        RequestEvaluation rEv = new RequestEvaluation();
                        rEv.setDatauser(r.getSubject());
                        rEv.setDatafield(oDataURL);
                        rEv.setAction(actionInput.name());
                        if (!perm) {
                            permit = false;
                            rEv.setResult(false);
                            rp.addEvaluationsItem(rEv);
                        } else {
                            rEv.setResult(true);
                            rp.addEvaluationsItem(rEv);
                        }

                    }
                }
            }
        }
        if (permit) {
            rp.setStatus("true");
            rp.setCompliance("VALID");
        } else {
            rp.setStatus("false");
            rp.setCompliance("PREFS_CONFLICT");
        }

        String policyReport = rp.toString();
        System.out.println(policyReport);
        System.gc();
        return rp;
    }
}