sernet.verinice.bpm.gsm.GsmProcessParameterCreater.java Source code

Java tutorial

Introduction

Here is the source code for sernet.verinice.bpm.gsm.GsmProcessParameterCreater.java

Source

/*******************************************************************************
 * Copyright (c) 2013 Daniel Murygin.
 *
 * This program is free software: you can redistribute it and/or 
 * modify it under the terms of the GNU Lesser General Public License 
 * as published by the Free Software Foundation, either version 3 
 * of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,    
 * but WITHOUT ANY WARRANTY; without even the implied warranty 
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
 * See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. 
 * If not, see <http://www.gnu.org/licenses/>.
 * 
 * Contributors:
 *     Daniel Murygin <dm[at]sernet[dot]de> - initial API and implementation
 ******************************************************************************/
package sernet.verinice.bpm.gsm;

import java.text.DecimalFormat;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.jbpm.pvm.internal.model.ExecutionImpl;

import sernet.verinice.interfaces.IBaseDao;
import sernet.verinice.interfaces.IDao;
import sernet.verinice.interfaces.bpm.IGenericProcess;
import sernet.verinice.interfaces.bpm.IGsmService;
import sernet.verinice.interfaces.bpm.ITask;
import sernet.verinice.interfaces.graph.Edge;
import sernet.verinice.interfaces.graph.GraphElementLoader;
import sernet.verinice.interfaces.graph.IGraphElementLoader;
import sernet.verinice.interfaces.graph.IGraphService;
import sernet.verinice.interfaces.graph.VeriniceGraph;
import sernet.verinice.model.common.CnATreeElement;
import sernet.verinice.model.iso27k.Asset;
import sernet.verinice.model.iso27k.AssetGroup;
import sernet.verinice.model.iso27k.Control;
import sernet.verinice.model.iso27k.ControlGroup;
import sernet.verinice.model.iso27k.IncidentScenario;
import sernet.verinice.model.iso27k.PersonIso;

/**
 * Abstract class to create a list of parameters for the creation of
 * a GSM process. At the moment there two classes extends this class:
 * 
 * {@link ProcessCreatorForAssetGroups}
 * Creates one process for each asset group and control group which are
 * connected.
 * 
 * {@link ProcessCreaterForPersons}
 * Creates one process for each person and control group which are
 * connected.
 * 
 * GsmProcessParameterCreater uses an instance of 
 * sernet.verinice.graph.IGraphService to collect information
 * about verinice elements.
 * 
 * Configured in veriniceserver-jbpm.xml.
 * Used by sernet.verinice.bpm.gsm.GsmService
 *
 * @see IGraphService
 * @see GsmService
 * @author Daniel Murygin <dm[at]sernet[dot]de>
 */
public abstract class GsmProcessParameterCreater {

    private static final Logger LOG = Logger.getLogger(GsmProcessParameterCreater.class);

    private static final String[] typeIds = { Asset.TYPE_ID, AssetGroup.TYPE_ID, Control.TYPE_ID,
            ControlGroup.TYPE_ID, IncidentScenario.TYPE_ID };

    private static final String[] relationIds = { Control.REL_CONTROL_INCSCEN, AssetGroup.REL_PERSON_ISO,
            IncidentScenario.REL_INCSCEN_ASSET };

    /*
     * Configure this in veriniceserver-jbpm.xml
     * Process with risk value below lowPriorityRiskLimit: priority low
     * Process with risk value above lowPriorityRiskLimit: priority normal
     */
    private int lowPriorityRiskLimit;
    public static final int LOW_PRIORITY_RISK_LIMIT_DEFAULT = 100;

    /*
     * Configure this in veriniceserver-jbpm.xml
     * Process with risk value below normalPriorityRiskLimit: priority normal
     * Process with risk value above normalPriorityRiskLimit: priority high
     */
    private int normalPriorityRiskLimit;
    public static final int NORMAL_PRIORITY_RISK_LIMIT_DEFAULT = 500;

    /**
     * Every instance of GsmProcessStarter has an exclusive instance of a IGraphService
     * Spring scope of graphService in veriniceserver-jbpm.xml is 'prototype'
     */
    private IGraphService graphService;
    private VeriniceGraph graph;

    private IGsmService gsmService;

    private IBaseDao<CnATreeElement, Integer> elementDao;

    private IDao<ExecutionImpl, Long> jbpmExecutionDao;

    /**
     * Creates a list of parameters for the creation of
     * a GSM process.
     * 
     * @param orgId db-id of an organization
     * @return A list of parameters to create a GSM-process
     */
    public List<GsmServiceParameter> createProcessParameterForOrganization(Integer orgId) {

        initGraph(orgId);

        List<CnATreeElement> rightElementList = getRightHandElements(orgId);
        List<CnATreeElement> leftElementList = getLeftHandElements(orgId);

        if (LOG.isInfoEnabled()) {
            LOG.info(rightElementList.size() + " control groups");
            LOG.info(leftElementList.size() + " persons");
        }

        List<GsmServiceParameter> allParameterList = new LinkedList<GsmServiceParameter>();
        for (CnATreeElement controlGroup : rightElementList) {
            for (CnATreeElement leftElement : leftElementList) {
                List<ExecutionImpl> processList = findProcesses(leftElement, controlGroup);
                if (processList != null && !processList.isEmpty()) {
                    deleteProcesses(processList);
                }
                List<GsmServiceParameter> parameterList = createParameter(leftElement, controlGroup);
                String uuidOrg = loadOrgUuid(orgId);
                for (GsmServiceParameter parameter : parameterList) {
                    parameter.setUuidOrg(uuidOrg);
                    allParameterList.add(parameter);
                }
            }
        }

        if (LOG.isInfoEnabled()) {
            LOG.info(allParameterList.size() + " process parameter objects created");
        }

        return allParameterList;
    }

    protected String loadOrgUuid(Integer orgId) {
        List<String> uuidList = getElementDao().findByQuery("select e.uuid from CnATreeElement e where e.dbId = ?",
                new Object[] { orgId });
        return uuidList.get(0);
    }

    /**
     * @param processList
     */
    private void deleteProcesses(List<ExecutionImpl> processList) {
        for (ExecutionImpl executionImpl : processList) {
            getGsmService().deleteProcess(executionImpl.getId());
        }
    }

    /**
     * Returns the left hand ends of the element grid which is loaded by this class
     * for process creation (persons or asset-groups). The right hand element is a control group.
     * 
     * @param orgId Db-id of an organization
     * @return A set of tree elements
     */
    protected abstract List<CnATreeElement> getLeftHandElements(Integer orgId);

    /**
     * Returns all elements ehich are connected to the left hand end.
     * 
     * @param leftElement Left hand end (a person are asset-group)
     * @return A set of tree elements
     */
    protected abstract Set<CnATreeElement> getObjectsForLeftElement(CnATreeElement leftElement);

    /**
     * Returns the responsible person(s) for the left hand element.
     * The left hand element is a person or asset-group.
     * 
     * @param leftElement A person or asset-group
     * @return A {@link Set} of persons, usually one person
     */
    protected abstract Set<CnATreeElement> getPersonForLeftElement(CnATreeElement leftElement);

    protected List<CnATreeElement> getRightHandElements(Integer orgId) {
        return get2ndLevelControlGroups(orgId);
    }

    private List<GsmServiceParameter> createParameter(CnATreeElement leftElement, CnATreeElement controlGroup) {
        List<GsmServiceParameter> parameterList = new LinkedList<GsmServiceParameter>();
        Set<CnATreeElement> elementSet = getAllElements(controlGroup, leftElement);
        Set<CnATreeElement> personSet = getPersonForLeftElement(leftElement);
        if (!elementSet.isEmpty() && personSet != null) {
            for (CnATreeElement person : personSet) {
                GsmServiceParameter parameter = new GsmServiceParameter(controlGroup, person);
                parameter.setProcessId(GsmService.createProcessId(leftElement, controlGroup));
                parameter.setElementSet(elementSet);
                Double riskValueDouble = getRiskValue(elementSet);
                parameter.setRiskValue(convertRiskValueToString(riskValueDouble));
                parameter.setPriority(convertRiskValueToPriority(riskValueDouble));
                parameterList.add(parameter);
            }

        }
        return parameterList;
    }

    /**
     * Returns all processes for a person or asset-group and a control-group.
     * 
     * @param leftElement A person or asset-group
     * @param controlGroup A control-group 
     * @return A list of processes (ExecutionImpl)
     */
    private List<ExecutionImpl> findProcesses(CnATreeElement leftElement, CnATreeElement controlGroup) {
        String value = GsmService.createProcessId(leftElement, controlGroup);
        return searchProcessByVariable(IGenericProcess.VAR_PROCESS_ID, value);
    }

    private List<ExecutionImpl> searchProcessByVariable(String key, String value) {
        DetachedCriteria executionCrit = DetachedCriteria.forClass(ExecutionImpl.class);
        DetachedCriteria variableCrit = executionCrit.createCriteria("variables");
        variableCrit.add(Restrictions.eq("key", key));
        variableCrit.add(Restrictions.eq("string", value));
        return getJbpmExecutionDao().findByCriteria(executionCrit);
    }

    /**
     * Calculate the risk value of a process.
     * 
     * CVSS value of incidentScenario: CVSS
     * Number of assets linked to incidentScenario: NUMBER_OF_LINKED_ASSETS
     * 
     * RISK_VALUE = [[CVSS_1 * NUMBER_OF_LINKED_ASSETS_1] + [CVSS_2 * NUMBER_OF_LINKED_ASSETS_2] + ...]
     * 
     * RISK_VALUE is rounded and has two digits to the right of the decimal point.
     * All CVSS are null (no CVSS is set at all): RISK_VALUE = "not determinable"
     * Total number of linked assets is 0: RISK_VALUE = 0
     * 
     * @param elementSet Elements of process
     * @return risk value of a process
     */
    private Double getRiskValue(Set<CnATreeElement> elementSet) {
        Double riskValueDouble = null;
        for (CnATreeElement element : elementSet) {
            if (IncidentScenario.TYPE_ID.equals(element.getTypeId())) {
                Double currentValue = getRiskValue(element, elementSet);
                if (riskValueDouble == null) {
                    riskValueDouble = currentValue;
                } else {
                    riskValueDouble += currentValue;
                }
            }
        }
        return riskValueDouble;
    }

    private String convertRiskValueToString(Double riskValueDouble) {
        String riskValue = Messages.getString("GsmService.5"); //$NON-NLS-1$
        if (riskValueDouble != null) {
            DecimalFormat formatterAndRounder = new DecimalFormat("###.##"); //$NON-NLS-1$
            riskValue = formatterAndRounder.format(riskValueDouble);
        }
        return riskValue;
    }

    private String convertRiskValueToPriority(Double riskValueDouble) {
        String priority = ITask.PRIO_NORMAL;
        if (riskValueDouble != null) {
            priority = ITask.PRIO_LOW;
            if (riskValueDouble.doubleValue() > getNormalPriorityRiskLimit()) {
                priority = ITask.PRIO_HIGH;
            } else if (riskValueDouble.doubleValue() > getLowPriorityRiskLimit()) {
                priority = ITask.PRIO_NORMAL;
            }
        }
        return priority;
    }

    private Double getRiskValue(CnATreeElement element, Set<CnATreeElement> processElementSet) {
        Double riskValue = null;
        IncidentScenario scenario = (IncidentScenario) element;
        Double cvss = scenario.getGsmCvss();
        if (cvss != null) {
            Set<CnATreeElement> allAssetList = getGraph().getLinkTargets(scenario,
                    IncidentScenario.REL_INCSCEN_ASSET);
            int numberOfLinkedAssets = 0;
            for (CnATreeElement linkedAsset : allAssetList) {
                if (processElementSet.contains(linkedAsset)) {
                    numberOfLinkedAssets++;
                }
            }
            riskValue = cvss * numberOfLinkedAssets;
        }
        return riskValue;
    }

    private void initGraph(Integer orgId) {
        try {
            IGraphElementLoader loader1 = new GraphElementLoader();
            loader1.setTypeIds(typeIds);
            loader1.setScopeId(orgId);
            loader1.setElementFilter(new TopElementFilter(orgId));

            IGraphElementLoader loader2 = new GraphElementLoader();
            loader2.setTypeIds(new String[] { PersonIso.TYPE_ID });

            getGraphService().setLoader(loader1, loader2);

            getGraphService().setRelationIds(relationIds);
            graph = getGraphService().create();
        } catch (Exception e) {
            LOG.error("Error while initialization", e);
        }
    }

    @SuppressWarnings("unchecked")
    private List<CnATreeElement> get2ndLevelControlGroups(Integer orgId) {
        StringBuffer hql = new StringBuffer("select e.dbId from CnATreeElement e "); //$NON-NLS-1$      
        hql.append("where e.objectType = ? and e.parent.parent.dbId = ?"); //$NON-NLS-1$ 
        List<Integer> dbIdResult = getElementDao().findByQuery(hql.toString(),
                new Object[] { ControlGroup.TYPE_ID, orgId });
        if (dbIdResult == null || dbIdResult.isEmpty()) {
            return Collections.emptyList();
        }
        DetachedCriteria crit = createDefaultCriteria();
        Integer[] dbIdArray = dbIdResult.toArray(new Integer[dbIdResult.size()]);
        crit.add(Restrictions.in("dbId", dbIdArray));
        return getElementDao().findByCriteria(crit);
    }

    protected DetachedCriteria createDefaultCriteria() {
        DetachedCriteria crit = DetachedCriteria.forClass(CnATreeElement.class);
        crit.setFetchMode("entity.typedPropertyLists", FetchMode.JOIN);
        crit.setFetchMode("entity.typedPropertyLists.properties", FetchMode.JOIN);
        crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        return crit;
    }

    private Set<CnATreeElement> getAllElements(CnATreeElement controlGroup, CnATreeElement leftElement) {
        Set<CnATreeElement> setA = getObjectsForControlGroup(controlGroup);
        Set<CnATreeElement> setB = getObjectsForLeftElement(leftElement);
        Set<CnATreeElement> result = createIntersection(setA, setB);
        if (LOG.isDebugEnabled() && !result.isEmpty()) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Task for \"" + leftElement.getTitle() + "\" and group \"" + controlGroup.getTitle()
                        + "\"");
            }
            logElementSet(result);
        }
        return result;
    }

    private Set<CnATreeElement> getObjectsForControlGroup(CnATreeElement controlGroup) {
        // elements is a set of controls
        Set<CnATreeElement> elements = getGraph().getLinkTargets(controlGroup);
        Set<CnATreeElement> scenarios = new HashSet<CnATreeElement>();
        for (CnATreeElement control : elements) {
            scenarios.addAll(getGraph().getLinkTargets(control, Control.REL_CONTROL_INCSCEN));
        }
        Set<CnATreeElement> assets = new HashSet<CnATreeElement>();
        for (CnATreeElement scen : scenarios) {
            assets.addAll(getGraph().getLinkTargets(scen, IncidentScenario.REL_INCSCEN_ASSET));
        }
        Set<CnATreeElement> assetGroups = new HashSet<CnATreeElement>();
        for (CnATreeElement asset : assets) {
            assetGroups.addAll(getGraph().getLinkTargets(asset, Edge.RELATIVES));
        }
        elements.addAll(scenarios);
        elements.addAll(assets);
        elements.addAll(assetGroups);
        return elements;
    }

    private Set<CnATreeElement> createIntersection(Set<CnATreeElement> elementList1,
            Set<CnATreeElement> elementList2) {
        elementList1.retainAll(elementList2);
        return elementList1;
    }

    private void logElementSet(Set<CnATreeElement> elementSet) {
        LOG.debug(GsmService.createElementInformation(elementSet));
    }

    public int getLowPriorityRiskLimit() {
        return lowPriorityRiskLimit;
    }

    public void setLowPriorityRiskLimit(int lowPriorityRiskLimit) {
        this.lowPriorityRiskLimit = lowPriorityRiskLimit;
    }

    public int getNormalPriorityRiskLimit() {
        return normalPriorityRiskLimit;
    }

    public void setNormalPriorityRiskLimit(int normalPriorityRiskLimit) {
        this.normalPriorityRiskLimit = normalPriorityRiskLimit;
    }

    public IGraphService getGraphService() {
        return graphService;
    }

    public void setGraphService(IGraphService graphService) {
        this.graphService = graphService;
    }

    public VeriniceGraph getGraph() {
        return graph;
    }

    public IGsmService getGsmService() {
        return gsmService;
    }

    public void setGsmService(IGsmService gsmService) {
        this.gsmService = gsmService;
    }

    public IBaseDao<CnATreeElement, Integer> getElementDao() {
        return elementDao;
    }

    public void setElementDao(IBaseDao<CnATreeElement, Integer> elementDao) {
        this.elementDao = elementDao;
    }

    public IDao<ExecutionImpl, Long> getJbpmExecutionDao() {
        return jbpmExecutionDao;
    }

    public void setJbpmExecutionDao(IDao<ExecutionImpl, Long> jbpmExecutionDao) {
        this.jbpmExecutionDao = jbpmExecutionDao;
    }

}