/** * Copyright (c) Members of the EGEE Collaboration. 2006-2009. * See http://www.eu-egee.org/partners/ for details on the copyright holders. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.glite.authz.pap.papmanagement; import java.util.ArrayList; import java.util.Collections; import java.util.GregorianCalendar; import java.util.HashSet; import java.util.List; import java.util.Set; import org.glite.authz.pap.common.PAPConfiguration; import org.glite.authz.pap.common.Pap; import org.glite.authz.pap.common.xacml.impl.TypeStringUtils; import org.glite.authz.pap.common.xacml.utils.PolicySetHelper; import org.glite.authz.pap.common.xacml.wizard.PolicySetWizard; import org.glite.authz.pap.monitoring.MonitoredProperties; import org.glite.authz.pap.repository.dao.DAOFactory; import org.glite.authz.pap.repository.dao.PapDAO; import org.glite.authz.pap.repository.dao.PolicyDAO; import org.glite.authz.pap.repository.dao.PolicySetDAO; import org.glite.authz.pap.repository.exceptions.AlreadyExistsException; import org.glite.authz.pap.repository.exceptions.InvalidVersionException; import org.glite.authz.pap.repository.exceptions.NotFoundException; import org.glite.authz.pap.repository.exceptions.RepositoryException; import org.joda.time.DateTime; import org.joda.time.chrono.ISOChronology; import org.opensaml.xacml.policy.PolicySetType; import org.opensaml.xacml.policy.PolicyType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class manages the content (the policies) of a <code>Pap</code>. * <p> * A pap has a root policy set which <i>id</i> is the same as the <i>pap id</i> (retrieved with * {@link Pap#getId()}). This policy set is the root of the tree of policies of the pap. There are * specific methods to create and retrieve this policy set ( * {@link PapContainer#createRootPolicySet(), {@link PapContainer#getRootPolicySet()}). * * @see PapManager * @see Pap * @see PapDAO */ public class PapContainer { private static final Logger log = LoggerFactory.getLogger(PapContainer.class); private static final Object notificationLock = new Object(); private final Pap pap; private final String papId; private final PolicyDAO policyDAO; private final PolicySetDAO policySetDAO; private final String rootPolicySetId; /** * Constructor. * * @param pap the <code>Pap</code> to be used as container. */ public PapContainer(Pap pap) { this.pap = pap; papId = pap.getId(); rootPolicySetId = papId; policySetDAO = DAOFactory.getDAOFactory().getPolicySetDAO(); policyDAO = DAOFactory.getDAOFactory().getPolicyDAO(); } /** * Convenience method to get a List of containers from a List of paps. * * @param papList List of paps. * @return the List of containers of the given paps. */ public static List<PapContainer> getContainers(List<Pap> papList) { List<PapContainer> papContainerList = new ArrayList<PapContainer>(papList.size()); for (Pap pap : papList) { papContainerList.add(new PapContainer(pap)); } return papContainerList; } /** * Adds a policy into a policy set. * * @param index position of the policy to added inside the list of policies already present in * the policy set. * @param policySetId the policy set id. * @param policy the policy to be added. * * @throws NotFoundException if the policy set id was not found. * @throws AlreadyExistsException if there's already a policy with the same id of the given one. * @throws InvalidVersionException if there was a concurrent modification of the policy set. * Getting this exception means that no policy has been added and the repository * hasn't been modified nor corrupted. */ public void addPolicy(int index, String policySetId, PolicyType policy) { if (!policySetDAO.exists(papId, policySetId)) { throw new NotFoundException("PolicySetId \"" + policySetId + "\" not found"); } String policyId = policy.getPolicyId(); policyDAO.store(papId, policy); int numberOfRules = policy.getRules().size(); TypeStringUtils.releaseUnneededMemory(policy); PolicySetType policySet = policySetDAO.getById(papId, policySetId); if (PolicySetHelper.referenceIdExists(policySet, policyId)) { throw new AlreadyExistsException("Reference id \"" + policyId + "\" alredy exists"); } if (index < 0) { PolicySetHelper.addPolicyReference(policySet, policyId); } else { PolicySetHelper.addPolicyReference(policySet, index, policyId); } String oldVersion = policySet.getVersion(); PolicySetWizard.increaseVersion(policySet); try { policySetDAO.update(papId, oldVersion, policySet); } catch (RepositoryException e) { policyDAO.delete(papId, policyId); throw e; } TypeStringUtils.releaseUnneededMemory(policySet); updatePapPolicyLastModificationTime(); notifyPoliciesAdded(numberOfRules); } /** * Adds a policy set into the root policy set of the pap. * * @param index * @param policySet * * @throws InvalidVersionException if there was a concurrent modification of the root policy * set. Getting this exception means that no policy has been added and the * repository hasn't been modified nor corrupted. */ public void addPolicySet(int index, PolicySetType policySet) { String policySetId = policySet.getPolicySetId(); policySetDAO.store(papId, policySet); PolicySetType rootPolicySet = policySetDAO.getById(papId, rootPolicySetId); if (PolicySetHelper.referenceIdExists(rootPolicySet, policySetId)) { throw new AlreadyExistsException("Reference id \"" + policySetId + "\" alredy exists"); } if (index < 0) { PolicySetHelper.addPolicySetReference(rootPolicySet, policySetId); } else { PolicySetHelper.addPolicySetReference(rootPolicySet, index, policySetId); } String oldVersion = rootPolicySet.getVersion(); PolicySetWizard.increaseVersion(rootPolicySet); try { policySetDAO.update(papId, oldVersion, rootPolicySet); } catch (RepositoryException e) { policySetDAO.delete(papId, policySetId); throw e; } TypeStringUtils.releaseUnneededMemory(rootPolicySet); updatePapPolicyLastModificationTime(); } /** * Creates the pap root policy set. * * @throws AlreadyExistsException if the pap root policy set already exists. */ public void createRootPolicySet() { PolicySetType rootPolicySet = PolicySetHelper.buildWithAnyTarget(pap.getId(), PolicySetHelper.COMB_ALG_FIRST_APPLICABLE); rootPolicySet.setVersion("0"); policySetDAO.store(papId, rootPolicySet); } /** * Delete all the policies of the pap. * <p> * Important: references of the deleted policies in policy sets are not deleted. See method * {@link PapContainer#removePolicyAndReferences(String)}. */ public void deleteAllPolicies() { // get the number of rules List<PolicyType> policyList = policyDAO.getAll(papId); int numberOfRules = 0; for (PolicyType policy : policyList) { numberOfRules += policy.getRules().size(); TypeStringUtils.releaseUnneededMemory(policy); } policyDAO.deleteAll(papId); updatePapPolicyLastModificationTime(); notifyPoliciesDeleted(numberOfRules); } /** * Delete all the policy sets of the pap. * <p> * Important: references of the deleted policy sets in policy sets (i.e. the root policy set) * are not deleted. See method {@link PapContainer#removePolicySetAndReferences(String)}. */ public void deleteAllPolicySets() { policySetDAO.deleteAll(papId); } public void deletePolicy(String id) throws NotFoundException, RepositoryException { PolicyType policy = policyDAO.getById(papId, id); int numberOfRules = policy.getRules().size(); policyDAO.delete(papId, id); updatePapPolicyLastModificationTime(); notifyPoliciesDeleted(numberOfRules); } /** * Delete a policy of the pap. * <p> * Important: references of the deleted policy in policy sets are not deleted. See method * {@link PapContainer#removePolicyAndReferences(String)}. * * @param id policy id of the policy to be deleted. * * @throws NotFoundException if the given policy id was not found. */ public void deletePolicySet(String id) throws NotFoundException, RepositoryException { policySetDAO.delete(papId, id); } /** * Returns a List of all the policies of the pap. * * @return a List of all the policies of the pap. * * @throws RepositoryException if an error occurred (e.g. a corrupted policy file). */ public List<PolicyType> getAllPolicies() { return policyDAO.getAll(papId); } /** * Return a list of all the policy sets of the pap. The root policy set is the first element of * the list. * * @return a list of all the policy sets of the pap where thr first element is the root policy * set. * * @throws RepositoryException if an error occurred (e.g. a corrupted policy file). */ public List<PolicySetType> getAllPolicySets() { List<PolicySetType> policySetList = policySetDAO.getAll(papId); // place the root policy set as the first element for (PolicySetType policySet : policySetList) { if (policySet.getPolicySetId().equals(rootPolicySetId)) { int rootPolicySetIndex = policySetList.indexOf(policySet); if (rootPolicySetIndex != 0) { Collections.swap(policySetList, 0, rootPolicySetIndex); } break; } } return policySetList; } /** * Returns the number of policies (i.e. number of rules) in the pap. * * @return the number of policies (i.e. number of rules) in the pap. */ public int getNumberOfPolicies() { List<PolicyType> policyList = policyDAO.getAll(papId); int numberOfRules = 0; for (PolicyType policy : policyList) { numberOfRules += policy.getRules().size(); TypeStringUtils.releaseUnneededMemory(policy); } return numberOfRules; } /** * Return the Pap object this container was build with. * * @return the Pap associated to this container. */ public Pap getPap() { return this.pap; } /** * Returns the root policy set of this pap. * * @return the root policy set of this pap. */ public PolicySetType getRootPolicySet() { return policySetDAO.getById(papId, rootPolicySetId); } /** * Returns the <i>id</i> of the root policy set of this pap. * * @return the <i>id</i> of the root policy set. */ public String getRootPolicySetId() { return rootPolicySetId; } /** * Get a policy by <i>id</i>. * * @param id policy id to search for. * @return the policy with the given <i>id</i>. * * @throws NotFoundException if no policy with the given id was found. * @throws RepositoryException if an error occurred (e.g. a corrupted policy file). */ public PolicyType getPolicy(String id) { return policyDAO.getById(papId, id); } /** * Get a policy set by <i>id</i>. * * @param id policy set id to search for. * @return the policy set with the given <i>id</i>. * * @throws NotFoundException if no policy set with the given id was found. * @throws RepositoryException if an error occurred (e.g. a corrupted policy set file). */ public PolicySetType getPolicySet(String id) { return policySetDAO.getById(papId, id); } /** * Checks for the existence of a policy with the given id. * * @param id policy id to search for. * @return <code>true</code> if a policy with the given id was found, <code>false</code> * otherwise. */ public boolean hasPolicy(String id) { return policyDAO.exists(papId, id); } /** * Checks for the existence of a policy set with the given id. * * @param id policy set id to search for. * @return <code>true</code> if a policy set with the given id was found, <code>false</code> * otherwise. */ public boolean hasPolicySet(String id) { return policySetDAO.exists(papId, id); } /** * Delete all the policies with no rules. References to that policies are deleted too. * * @throws RepositoryException if an error occurred (e.g. a corrupted policy file). */ public void purgePoliciesWithNoRules() { List<PolicyType> policyList = policyDAO.getAll(papId); for (PolicyType policy : policyList) { if (policy.getRules().size() == 0) { removePolicyAndReferences(policy.getPolicyId()); } } } /** * Delete all the policy sets with no policies. References to that policy sets are deleted too. * * @throws RepositoryException if an error occurred (e.g. a corrupted policy file). */ public void purgePolicySetWithNoPolicies() { List<PolicySetType> policySetList = policySetDAO.getAll(papId); for (PolicySetType policySet : policySetList) { String policySetId = policySet.getPolicySetId(); if (rootPolicySetId.equals(policySetId)) { continue; } if (policySet.getPolicyIdReferences().size() == 0) { removePolicySetAndReferences(policySetId); } } } /** * Delete all the policy sets found to be unreferenced (obviously the root policy set is not * deleted). */ public void purgeUnreferencedPolicySets() { Set<String> idSet = new HashSet<String>(); PolicySetType rootPS = policySetDAO.getById(papId, rootPolicySetId); idSet.add(rootPS.getPolicySetId()); for (String id : PolicySetHelper.getPolicySetIdReferencesValues(rootPS)) { idSet.add(id); } TypeStringUtils.releaseUnneededMemory(rootPS); for (PolicySetType policySet : policySetDAO.getAll(papId)) { String policySetId = policySet.getPolicySetId(); if (!idSet.contains(policySetId)) { log.info("Purging policy set " + policySetId); policySetDAO.delete(papId, policySetId); } } } /** * Delete all the policies found to be unreferenced. */ public void purgeUnreferencesPolicies() { Set<String> idSet = new HashSet<String>(); for (PolicySetType policySet : policySetDAO.getAll(papId)) { List<String> idList = PolicySetHelper.getPolicyIdReferencesValues(policySet); TypeStringUtils.releaseUnneededMemory(policySet); for (String id : idList) { idSet.add(id); } } for (PolicyType policy : policyDAO.getAll(papId)) { String policyId = policy.getPolicyId(); if (!idSet.contains(policyId)) { log.info("Purging policy " + policyId); policyDAO.delete(papId, policyId); } } } /** * Delete a policy and all its references. * * @param policyId the policy id of the policy to remove. * * @throws NotFoundException if no policy with the given id was found. * @throws InvalidVersionException if there was a concurrent modification of the policy set. * Getting this exception means that no policy has been added and the repository * hasn't been modified nor corrupted. * @throws RepositoryException if an error occurred (e.g. a corrupted policy file). */ public void removePolicyAndReferences(String policyId) { if (!policyDAO.exists(papId, policyId)) { throw new NotFoundException("PolicyId \"" + policyId + "\" does not exists"); } boolean policyAlreadyRemoved = false; List<PolicySetType> policySetList = policySetDAO.getAll(papId); for (PolicySetType policySet : policySetList) { if (PolicySetHelper.deletePolicyReference(policySet, policyId)) { if (policySet.getPolicyIdReferences().size() == 0) { removePolicySetAndReferences(policySet.getPolicySetId()); policyAlreadyRemoved = true; } else { String oldVersion = policySet.getVersion(); PolicySetWizard.increaseVersion(policySet); policySetDAO.update(papId, oldVersion, policySet); } TypeStringUtils.releaseUnneededMemory(policySet); } } if (policyAlreadyRemoved) { return; } PolicyType policy = policyDAO.getById(papId, policyId); int numberOfRules = policy.getRules().size(); policyDAO.delete(papId, policyId); updatePapPolicyLastModificationTime(); notifyPoliciesDeleted(numberOfRules); } /** * Delete a policy set and all its references. * * @param policySetId the policy set id of the policy set to remove. * * @throws NotFoundException if no policy set with the given id was found. * @throws InvalidVersionException if there was a concurrent modification of the root policy * set. Getting this exception means that no policy has been added and the * repository hasn't been modified nor corrupted. * @throws RepositoryException if an error occurred (e.g. a corrupted policy set file). */ public void removePolicySetAndReferences(String policySetId) { if (!policySetDAO.exists(papId, policySetId)) { throw new NotFoundException("PolicySetId \"" + policySetId + "\" does not exists"); } PolicySetType rootPolicySet = policySetDAO.getById(papId, rootPolicySetId); if (PolicySetHelper.deletePolicySetReference(rootPolicySet, policySetId)) { String oldVersion = rootPolicySet.getVersion(); PolicySetWizard.increaseVersion(rootPolicySet); policySetDAO.update(papId, oldVersion, rootPolicySet); TypeStringUtils.releaseUnneededMemory(rootPolicySet); } PolicySetType policySet = policySetDAO.getById(papId, policySetId); policySetDAO.delete(papId, policySetId); List<String> idList = PolicySetHelper.getPolicyIdReferencesValues(policySet); TypeStringUtils.releaseUnneededMemory(policySet); for (String policyId : idList) { PolicyType policy = policyDAO.getById(papId, policyId); int numberOfRules = policy.getRules().size(); TypeStringUtils.releaseUnneededMemory(policy); policyDAO.delete(papId, policyId); notifyPoliciesDeleted(numberOfRules); } updatePapPolicyLastModificationTime(); } /** * Store a policy. * * @param policy the policy to be stored. * * @throws AlreadyExistsException if a policy with the same id was found. */ public void storePolicy(PolicyType policy) { policyDAO.store(papId, policy); int numberOfRules = policy.getRules().size(); TypeStringUtils.releaseUnneededMemory(policy); updatePapPolicyLastModificationTime(); notifyPoliciesAdded(numberOfRules); } /** * Store a policy set. * * @param policySet the policy set to be stored. * * @throws AlreadyExistsException if a policy set with the same id was found. */ public void storePolicySet(PolicySetType policySet) { policySetDAO.store(papId, policySet); } /** * Update a policy. * * @param version version of the policy in the repository to be updated. * @param policy new policy replacing the one with the same id. * * @throws NotFoundException if a policy with the same id was not found. * @throws InvalidVersionException if there was a concurrent modification of the policy. Getting * this exception means that no policy has been updated and the repository hasn't * been modified nor corrupted. */ public void updatePolicy(String version, PolicyType policy) { PolicyType oldPolicy = policyDAO.getById(papId, policy.getPolicyId()); int numberOfRemovedRules = oldPolicy.getRules().size(); TypeStringUtils.releaseUnneededMemory(oldPolicy); int numberOfAddedRules = policy.getRules().size(); policyDAO.update(papId, version, policy); updatePapPolicyLastModificationTime(); notifyPoliciesDeleted(numberOfRemovedRules); notifyPoliciesAdded(numberOfAddedRules); } /** * Update a policy set. * * @param version version of the policy set in the repository to be updated. * @param newPolicySet new policy set replacing the one with the same id. * * @throws NotFoundException if a policy set with the same id was not found. * @throws InvalidVersionException if there was a concurrent modification of the policy set. * Getting this exception means that no policy set has been updated and the * repository hasn't been modified nor corrupted. */ public void updatePolicySet(String version, PolicySetType newPolicySet) { policySetDAO.update(papId, version, newPolicySet); updatePapPolicyLastModificationTime(); } /** * Notifies that some policies have been added. * * @param numOfAddedPolicies number of added policies. */ private void notifyPoliciesAdded(int numOfAddedPolicies) { String propName; if (pap.isLocal()) { propName = MonitoredProperties.NUM_OF_LOCAL_POLICIES_PROP_NAME; } else { propName = MonitoredProperties.NUM_OF_REMOTE_POLICIES_PROP_NAME; } synchronized (notificationLock) { Integer numOfPoliciesInteger = (Integer) PAPConfiguration.instance() .getMonitoringProperty(propName); if (numOfPoliciesInteger == null) { return; } int numOfPolicies = numOfPoliciesInteger.intValue() + numOfAddedPolicies; numOfPoliciesInteger = new Integer(numOfPolicies); PAPConfiguration.instance().setMonitoringProperty(propName, numOfPoliciesInteger); propName = MonitoredProperties.NUM_OF_POLICIES_PROP_NAME; numOfPoliciesInteger = (Integer) PAPConfiguration.instance().getMonitoringProperty(propName); numOfPolicies = numOfPoliciesInteger.intValue() + numOfAddedPolicies; numOfPoliciesInteger = new Integer(numOfPolicies); PAPConfiguration.instance().setMonitoringProperty(propName, numOfPoliciesInteger); } } /** * Notifies that some policies have been deleted. * * @param numOfDeletedPolicies number of deleted policies. */ private void notifyPoliciesDeleted(int numOfDeletedPolicies) { String propName; if (pap.isLocal()) { propName = MonitoredProperties.NUM_OF_LOCAL_POLICIES_PROP_NAME; } else { propName = MonitoredProperties.NUM_OF_REMOTE_POLICIES_PROP_NAME; } synchronized (notificationLock) { Integer numOfPoliciesInteger = (Integer) PAPConfiguration.instance() .getMonitoringProperty(propName); if (numOfPoliciesInteger == null) { return; } int numOfPolicies = numOfPoliciesInteger.intValue() - numOfDeletedPolicies; numOfPoliciesInteger = new Integer(numOfPolicies); PAPConfiguration.instance().setMonitoringProperty(propName, numOfPoliciesInteger); propName = MonitoredProperties.NUM_OF_POLICIES_PROP_NAME; numOfPoliciesInteger = (Integer) PAPConfiguration.instance().getMonitoringProperty(propName); numOfPolicies = numOfPoliciesInteger.intValue() - numOfDeletedPolicies; numOfPoliciesInteger = new Integer(numOfPolicies); PAPConfiguration.instance().setMonitoringProperty(propName, numOfPoliciesInteger); } } /** * Notifies the time of last policy modification. */ private void notifyPolicyLastModificationTimeUpdate() { if (pap.isRemote()) { return; } synchronized (notificationLock) { String lastModificationTimeString = pap.getPolicyLastModificationTimeInMilliseconds(); PAPConfiguration.instance() .setMonitoringProperty(MonitoredProperties.POLICY_LAST_MODIFICATION_TIME_MILLIS_PROP_NAME, lastModificationTimeString); DateTime lastModificationTime = new DateTime(Long.parseLong(lastModificationTimeString)) .withChronology(ISOChronology.getInstanceUTC()); PAPConfiguration.instance() .setMonitoringProperty(MonitoredProperties.POLICY_LAST_MODIFICATION_TIME_PROP_NAME, lastModificationTime); } } /** * Update the last policy modification time of the pap in the repository. */ private void updatePapPolicyLastModificationTime() { pap.setPolicyLastModificationTime((new GregorianCalendar()).getTimeInMillis()); synchronized (notificationLock) { DAOFactory.getDAOFactory().getPapDAO().update(pap); } notifyPolicyLastModificationTimeUpdate(); } }