/** * 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.LinkedList; import java.util.List; import org.glite.authz.pap.common.Pap; import org.glite.authz.pap.common.utils.Utils; import org.glite.authz.pap.distribution.DistributionConfiguration; import org.glite.authz.pap.distribution.DistributionConfigurationException; import org.glite.authz.pap.distribution.DistributionModule; import org.glite.authz.pap.repository.dao.DAOFactory; import org.glite.authz.pap.repository.dao.PapDAO; import org.glite.authz.pap.repository.exceptions.AlreadyExistsException; import org.glite.authz.pap.repository.exceptions.NotFoundException; import org.glite.authz.pap.repository.exceptions.RepositoryException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class provides methods to manage {@link Pap} objects (i.e. add, update, delete, etc). It * also provides methods to manage the <code>Pap</code> ordering. All of these methods affects both * the repository and the configuration. * <p> * Use the {@link PapManager#initialize()} method before doing anything with this class. * <p> * The PAP service has always one (special) pap defined. This pap is commonly identified in the code * with the word <i>default</i>. The alias of this special pap is defined by the constant * {@link Pap#DEFAULT_PAP_ALIAS} (its value is {@value Pap#DEFAULT_PAP_ALIAS}). The <i>default</i> * pap is local and public. * <p> * <b>Paps ordering</b><br> * For the policy evaluation process (performed by the <i>PDP</i>) the oder of the policies matters. * Therefore it is possible to specify an ordering for the <code>Pap</code> objects (which are * containers of policies). The policies of <code>Pap</code> <i>A</i> are evaluated before the * policies of <code>Pap</code> <i>B</i> if <i>A</i> comes before <i>B</i> in the order. There are * two points concerning the pap ordering that must be kept in mind:<br> * 1. if the <i>defdault</i> pap is <b>not</b> listed in the pap ordering then it is automatically * placed as the first pap in the order;<br> * 2. if the pap ordering does not contains all the defined paps then the paps not listed in the * order comes after (except the <i>default</i> pap, see point 1). * <p> * <b>Repository and configuration</b><br> * The information associated to a <code>Pap</code> object are stored both in the repository and in * the configuration. Since the configuration can be changed off-line (when the PAP service is not * running), synchronization between the configuration and the repository is needed at startup time. * See the method {@link PapManager#synchronizeRepositoryWithConfiguration()}.<br> * Information like the polling interval and the pap ordering are only in the configuration. * * @see Pap * @see PapContainer * @see DistributionConfiguration * @see PapDAO * @see DistributionModule */ public class PapManager { private static PapManager instance = null; private static final Logger log = LoggerFactory.getLogger(PapManager.class); private String[] configurationPapOrdering; private DistributionConfiguration distributionConfiguration; private PapDAO papDAO; /** * Constructor. * <p> * The <i>default</i> pap is created if it doesn't exists. */ private PapManager() { papDAO = DAOFactory.getDAOFactory().getPapDAO(); createDefaultPapIfNotExists(); distributionConfiguration = DistributionConfiguration.getInstance(); configurationPapOrdering = distributionConfiguration.getPapOrdering(); synchronizeRepositoryWithConfiguration(); } public static PapManager getInstance() { if (instance == null) { throw new PapManagerException("Please initialize configuration before calling the instance method!"); } return instance; } /** * Initialization method. The main purpose of this method is to create (through the constructor) * the <i>default</i> pap it doesn't exits. */ public static void initialize() { if (instance == null) { instance = new PapManager(); } } /** * Add a pap to the repository and to the configuration. * * @param pap the pap to be added. * * @throws PapManagerException if the alias of the pap is equal to {@link Pap#DEFAULT_PAP_ALIAS} * . * @throws AlreadyExistsException if the alias of the pap already exists. */ public void addPap(Pap pap) { String papAlias = pap.getAlias(); if (Pap.DEFAULT_PAP_ALIAS.equals(papAlias)) { throw new PapManagerException("Forbidden alias: " + papAlias); } if (exists(papAlias)) { throw new AlreadyExistsException("pap \"" + papAlias + "\" already exists"); } distributionConfiguration.savePap(pap); papDAO.store(pap); PapContainer papContainer = new PapContainer(pap); papContainer.createRootPolicySet(); } /** * Delete a pap from the configuration and from the repository. * * @param papAlias the alias of the pap to remove. * * @throws NotFoundException if a pap with the given alias does not exists. * @throws PapManagerException if the alias is equal to {@link Pap#DEFAULT_PAP_ALIAS}. */ public void deletePap(String papAlias) { if (Pap.DEFAULT_PAP_ALIAS.equals(papAlias)) { throw new PapManagerException("Deleting the default pap is not allowed"); } distributionConfiguration.removePap(papAlias); papDAO.delete(papAlias); // pap ordering could be modified. configurationPapOrdering = distributionConfiguration.getPapOrdering(); } /** * Checks for the existence of a pap. * * @param papAlias alias of the pap. * @return <code>true</code> if a pap with the given alias exists, <code>false</code> otherwise. */ public boolean exists(String papAlias) { return papDAO.exists(papAlias); } /** * Returns a <code>List</code> of all the defined paps.<br> * The paps in the list are ordered following the pap ordering as specified in the * configuration. * * @return an <i>ordered</i> <code>List</code> of all the defined paps. */ public List<Pap> getAllPaps() { return getPapList(); } /** * Returns a <code>List</code> of all the defined local paps.<br> * The paps in the list are ordered following the pap ordering as specified in the * configuration. * * @return an <i>ordered</i> <code>List</code> of all the defined local paps. */ public List<Pap> getLocalPaps() { List<Pap> resultList = new LinkedList<Pap>(); for (Pap pap : getPapList()) { if (pap.isLocal()) { resultList.add(pap); } } return resultList; } /** * Returns a <code>List</code> of all the defined remote paps.<br> * The paps in the list are ordered following the pap ordering as specified in the * configuration. * * @return an <i>ordered</i> <code>List</code> of all the defined remote paps. */ public List<Pap> getRemotePaps() { List<Pap> remotePapList = new LinkedList<Pap>(); for (Pap pap : getPapList()) { if (pap.isRemote()) { remotePapList.add(pap); } } return remotePapList; } /** * Returns the pap identified by the given alias. * * @param papAlias the alias of the pap to retrieve. * @return the <code>Pap</code> associated to the given alias. * * @throws NotFoundException if a pap with the given alias was not found. */ public Pap getPap(String papAlias) { return papDAO.get(papAlias); } /** * Convenience method to get a <code>PapContainer</code> from a pap alias. * * @param papAlias alias of the pap. * @return the <code>PapContainer</code> of the pap with the given alias. * * @throws NotFoundException if a pap with the given alias was not found. */ public PapContainer getPapContainer(String papAlias) { return new PapContainer(getPap(papAlias)); } /** * Returns the pap ordering as defined in the configuration. * * @return the pap ordering as defined in the configuration. */ public String[] getPapOrdering() { return configurationPapOrdering; } /** * Returns a <code>List</code> of all the defined public paps.<br> * The paps in the list are ordered following the pap ordering as specified in the * configuration. * * @return an <i>ordered</i> <code>List</code> of all the defined public paps. */ public List<Pap> getPublicPaps() { List<Pap> resultList = new LinkedList<Pap>(); for (Pap pap : getPapList()) { if (pap.isVisibilityPublic()) { resultList.add(pap); } } return resultList; } /** * Set the pap ordering in the configuration.<br> * If the given array is <code>null</code> or <code>empty</code> then the previous order (if * any) is cleared. * * @param aliasArray array of pap aliases defining the new pap ordering (can be * <code>null</code> or <code>empty</code>). * * @throws DistributionConfigurationException if the new ordering contains duplicated or unknown * aliases. */ public void setPapOrdering(String[] aliasArray) { distributionConfiguration.savePapOrdering(aliasArray); // update to the new order configurationPapOrdering = distributionConfiguration.getPapOrdering(); } /** * Update the information associated to a pap. * * @param newPap update the information of the pap with the same alias of the given one. * * @throws NotFoundException if a pap with the same alias of the given one was not found. * @throws PapManagerException if the given pap is <code>null</code>. */ public void updatePap(Pap newPap) { if (newPap == null) { throw new PapManagerException("pap cannot be null"); } String alias = newPap.getAlias(); if (Pap.DEFAULT_PAP_ALIAS.equals(alias)) { updateDefaultPap(newPap); return; } Pap oldPap = papDAO.get(alias); // id (and alias) cannot change newPap.setId(oldPap.getId()); distributionConfiguration.savePap(newPap); papDAO.update(newPap); } /** * Builds a list of alias of all the defined paps that follows the pap ordering defined in the * configuration. * * @return a list of alias of all the defined paps that follows the pap ordering defined in the * configuration. */ private List<String> buildOrderedAliasList() { List<String> repositoryAliasList = papDAO.getAliasList(); int configurationArraySize = configurationPapOrdering.length; if (configurationArraySize > repositoryAliasList.size()) { throw new PapManagerException("BUG: configuration contains more paps than the repository"); } int defaultPapAliasIdx = repositoryAliasList.indexOf(Pap.DEFAULT_PAP_ALIAS); if (defaultPapAliasIdx == -1) { throw new PapManagerException("BUG: default pap does not exist in the repository"); } /* enforce the order specified in the configuration file */ List<String> configurationAliasOrderedList = new LinkedList<String>(); // if the default pap is not specified in the order in configuration then it goes for first if (Utils.indexOf(Pap.DEFAULT_PAP_ALIAS, configurationPapOrdering) == -1) { configurationAliasOrderedList.add(Pap.DEFAULT_PAP_ALIAS); } for (int i = 0; i < configurationArraySize; i++) { configurationAliasOrderedList.add(configurationPapOrdering[i]); } // follow the order specified in the configuration file for (int i = 0; i < configurationAliasOrderedList.size(); i++) { String alias = configurationAliasOrderedList.get(i); int aliasIndex = repositoryAliasList.indexOf(alias); if (aliasIndex == -1) { throw new PapManagerException("BUG: initialization error. pap defined in the configuration is not in the repository"); } Collections.swap(repositoryAliasList, aliasIndex, i); } return repositoryAliasList; } /** Create the <i>default</i> pap if it doesn't exist. */ private void createDefaultPapIfNotExists() { Pap defaultPap; if (!papDAO.exists(Pap.DEFAULT_PAP_ALIAS)) { defaultPap = Pap.makeDefaultPAP(); papDAO.store(defaultPap); } else { defaultPap = papDAO.get(Pap.DEFAULT_PAP_ALIAS); } PapContainer defaultPapContainer = getPapContainer(Pap.DEFAULT_PAP_ALIAS); // check if the root policy set exists if (defaultPapContainer.hasPolicySet(defaultPapContainer.getRootPolicySetId())) { return; } // create the root policy set defaultPapContainer.createRootPolicySet(); } /** * Returns a <code>List</code> of all the defined paps.<br> * The paps in the list are ordered following the pap ordering as specified in the * configuration. * * @return an <i>ordered</i> <code>List</code> of all the defined paps. */ private List<Pap> getPapList() { List<String> aliasOrderedList = buildOrderedAliasList(); List<Pap> papList = new ArrayList<Pap>(aliasOrderedList.size()); for (String alias : aliasOrderedList) { papList.add(papDAO.get(alias)); } return papList; } /** * The information associated to a <code>Pap</code> object are stored both in the repository and * in the configuration. Since the configuration can be changed off-line (when the PAP service * is not running), synchronization between the configuration and the repository is needed. This * method synchronize these information doing the following steps:<br> * 1. add to the repository all the paps defined in the configuration that are not present in * the repository;<br> * 2. if a pap is defined both in the repository and in the configuration then update the * information in the repository with the one found in the configuration (if they differs);<br> * 3. delete from the repository the paps that are not in the configuration. * */ private void synchronizeRepositoryWithConfiguration() { List<Pap> papListFromConfiguration = DistributionConfiguration.getInstance().getPapList(); if (papListFromConfiguration.isEmpty()) { log.info("No remote paps has been defined"); } // make sure that all paps defined in the configuration are also in the repository for (Pap papFromConfiguration : papListFromConfiguration) { try { String papAlias = papFromConfiguration.getAlias(); Pap papFromRepository = papDAO.get(papAlias); if (papFromConfiguration.equals(papFromRepository)) { continue; } // paps in the repository but must be updated with the information of the paps found // in the configuration. Since the cache of a pap must be removed we can delete the // pap and store it again log.info("Settings for pap \"" + papAlias + "\" has been updated. Invalidating cache"); papDAO.delete(papAlias); } catch (NotFoundException e) { // the pap is not in the repository therefore go on and store it } papDAO.store(papFromConfiguration); PapContainer papContainer = new PapContainer(papFromConfiguration); papContainer.createRootPolicySet(); } // remove from the repository paps that are not in the configuration for (String alias : papDAO.getAliasList()) { // do not remove the default pap if (alias.equals(Pap.DEFAULT_PAP_ALIAS)) { continue; } boolean notFoundInConfiguration = true; for (Pap papFromConfiguration : papListFromConfiguration) { if (alias.equals(papFromConfiguration.getAlias())) { notFoundInConfiguration = false; break; } } if (notFoundInConfiguration) { papDAO.delete(alias); log.info("Removed pap \"" + alias + "\" from the repository because it hasn't been found in the configuration file."); } } } /** * Updates the <i>default</i> pap. * * @param newDefaultPap */ private void updateDefaultPap(Pap newDefaultPap) { if (!Pap.DEFAULT_PAP_ALIAS.equals(newDefaultPap.getAlias())) { throw new RepositoryException("Invalid alias for default pap. Cannot perform updateDefaultPap request."); } Pap oldDefaultPap = getPap(Pap.DEFAULT_PAP_ALIAS); newDefaultPap.setId(oldDefaultPap.getId()); papDAO.update(newDefaultPap); } }