/* * Copyright (c) Members of the EGEE Collaboration. 2006-2010. * 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.pep.obligation.dfpmap; import java.io.File; import javax.security.auth.x500.X500Principal; import org.glite.authz.common.config.AbstractConfigurationBuilder; import org.glite.authz.common.config.ConfigurationException; import org.glite.authz.common.config.IniConfigUtil; import org.glite.authz.common.config.IniSectionConfigurationParser; import org.glite.authz.common.fqan.FQAN; import org.glite.authz.common.profile.GLiteAuthorizationProfileConstants; import org.glite.authz.pep.obligation.ObligationHandler; import org.glite.authz.pep.obligation.dfpmap.UpdatingDFPM.DFPMFactory; import org.ini4j.Ini; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.helpers.MessageFormatter; /** * INI configuration parser that constructs {@link DFPMObligationHandler}s. */ public class DFPMObligationHandlerConfigurationParser implements IniSectionConfigurationParser<ObligationHandler> { /** * The name of the {@value} property which gives ID of the obligation * handled by this obligation handler */ public static final String HANDLED_OBLIGATION_ID_PROP= "handledObligationId"; /** * The default value of the {@value #HANDLED_OBLIGATION_ID_PROP} * property: {@value} . */ public static final String HANDLED_OBLIGATION_ID_DEFAULT= GLiteAuthorizationProfileConstants.ID_OBLIGATION_LOCAL_ENV_MAP; /** * The name of the {@value} property which gives the absolute path to the * mapping file that maps subjects to account indicator. */ public static final String ACCOUNT_MAP_FILE_PROP= "accountMapFile"; /** * The name of the {@value} property that indicates the account indicator * associated with the subject's DN should be preferred over the one from * the primary FQAN. */ public static final String PREFER_DN_FOR_LOGIN_NAME_PROP= "preferDNForLoginName"; /** * The name of the {@value} property that indicates the primary group name * associated with the subject's DN should be preferred over the one from * the primary FQAN. */ public static final String PREFER_DN_FOR_PRIMARY_GROUP_NAME_PROP= "preferDNForPrimaryGroupName"; /** * The name of the {@value} property that indicates that the failure to find * a primary group mapping in the group map file cause the obligation * handler to fail. */ public static final String NO_PRIMARY_GROUP_NAME_IS_ERROR_PROP= "noPrimaryGroupNameIsError"; /** * The name of the {@value} property which gives the absolute path to the * mapping file that maps subjects to groups. */ public static final String GROUP_MAP_FILE_PROP= "groupMapFile"; /** * The name of the {@value} property which gives the interval, in minutes, * mapping files are checked for changes. */ public static final String MAP_REFRESH_PERIOD_PROP= "refreshPeriod"; /** * The name of the {@value} property which gives the the absolute path to * the directory that grid mappings are stored. */ public static final String GRID_MAP_DIR_PROP= "gridMapDir"; /** * The default value of the {@value #PREFER_DN_FOR_LOGIN_NAME_PROP} * property: {@value} . */ public static final boolean PREFER_DN_FOR_LOGIN_NAME_DEFAULT= true; /** * The default value of the {@value #PREFER_DN_FOR_PRIMARY_GROUP_NAME_PROP} * property: {@value} */ public static final boolean PREFER_DN_FOR_PRIMARY_GOURP_NAME_DEFAULT= true; /** * The default value of the {@value #NO_PRIMARY_GROUP_NAME_IS_ERROR_PROP} * property: {@value} */ public static final boolean NO_PRIMARY_GROUP_NAME_IS_ERROR_DEFAULT= false; /** * The default value of the * {@value IniOHConfigurationParser#PRECEDENCE_PROP} property: {@value} */ public static final int PRECENDENCE_DEFAULT= 0; /** * The default value (in minutes) of the {@value #MAP_REFRESH_PERIOD_PROP} * property: * {@value} */ public static final int MAP_REFRESH_PERIOD_DEFAULT= 15; /** * The name of the {@value} property that indicate that the OH will be only * applied if the request subject contains a key-info attribute. */ public static final String REQUIRE_SUBJECT_KEYINFO_PROP= "requireSubjectKeyInfo"; /** * The default value of the {@value #REQUIRE_SUBJECT_KEYINFO_PROP} property: * * {@value} */ public static final boolean REQUIRE_SUBJECT_KEYINFO_DEFAULT= true; /** * The name of the {@value} property that determine if the lease filename in * the {@value #GRID_MAP_DIR_PROP} contains or not the secondary groups. */ public static final String USE_SECONDARY_GROUP_NAMES_FOR_MAPPING_PROP= "useSecondaryGroupNamesForMapping"; /** * The default value of the * {@value #USE_SECONDARY_GROUP_NAMES_FOR_MAPPING_PROP} property: {@value} */ public static final boolean USE_SECONDARY_GROUP_NAMES_FOR_MAPPING_DEFAULT= true; /** Class logger. */ private final Logger log= LoggerFactory.getLogger(DFPMObligationHandlerConfigurationParser.class); /** {@inheritDoc} */ public ObligationHandler parse(Ini.Section iniConfig, AbstractConfigurationBuilder<?> configBuilder) throws ConfigurationException { String name= iniConfig.getName(); // get handled obligation ID String obligationId= IniConfigUtil.getString(iniConfig, HANDLED_OBLIGATION_ID_PROP, HANDLED_OBLIGATION_ID_DEFAULT); log.info("{}: handled obligationID: {}", name, obligationId); /* grid-mapfile */ String accountMapFile= IniConfigUtil.getString(iniConfig, ACCOUNT_MAP_FILE_PROP); log.info("{}: user mapping file: {}", name, accountMapFile); /* group-mapfile */ String groupMapFile= IniConfigUtil.getString(iniConfig, GROUP_MAP_FILE_PROP); log.info("{}: group mapping file: {}", name, groupMapFile); /* grid-mapfile and group-mapfile refresh timer */ int mapRefreshPeriod= IniConfigUtil.getInt(iniConfig, MAP_REFRESH_PERIOD_PROP, MAP_REFRESH_PERIOD_DEFAULT, 1, Integer.MAX_VALUE); log.info("{}: mapping file(s) refresh period: {} mins", name, mapRefreshPeriod); /* mapping options: DN have precedence over FQAN ? */ boolean preferDNForLoginName= IniConfigUtil.getBoolean(iniConfig, PREFER_DN_FOR_LOGIN_NAME_PROP, PREFER_DN_FOR_LOGIN_NAME_DEFAULT); log.info("{}: prefer DN login name mappings: {}", name, preferDNForLoginName); boolean preferDNForPrimaryGroupName= IniConfigUtil.getBoolean(iniConfig, PREFER_DN_FOR_PRIMARY_GROUP_NAME_PROP, PREFER_DN_FOR_PRIMARY_GOURP_NAME_DEFAULT); log.info("{}: prefer DN primary group mappings: {}", name, preferDNForPrimaryGroupName); /* gridmapdir */ String gridMapDir= IniConfigUtil.getString(iniConfig, GRID_MAP_DIR_PROP); log.info("{}: grid mapping directory: {}", name, gridMapDir); boolean noPrimaryGroupNameIsError= IniConfigUtil.getBoolean(iniConfig, NO_PRIMARY_GROUP_NAME_IS_ERROR_PROP, NO_PRIMARY_GROUP_NAME_IS_ERROR_DEFAULT); log.info("{}: no primary group name mapping is error: {}", name, noPrimaryGroupNameIsError); // BUG FIX: https://savannah.cern.ch/bugs/?83317 boolean useSecondaryGroupNamesForMapping= IniConfigUtil.getBoolean(iniConfig, USE_SECONDARY_GROUP_NAMES_FOR_MAPPING_PROP, USE_SECONDARY_GROUP_NAMES_FOR_MAPPING_DEFAULT); log.info("{}: use secondary group names for mapping (lease filename): {}", name, useSecondaryGroupNamesForMapping); AccountMapper accountMapper= buildAccountMapper(accountMapFile, preferDNForLoginName, groupMapFile, preferDNForPrimaryGroupName, mapRefreshPeriod * 60 * 1000, gridMapDir, noPrimaryGroupNameIsError, useSecondaryGroupNamesForMapping); DFPMObligationHandler obligationHandler= new DFPMObligationHandler(name, obligationId, accountMapper); /* apply OH only if subject key-info attribute is present in request ? */ boolean requireSubjectKeyInfo= IniConfigUtil.getBoolean(iniConfig, REQUIRE_SUBJECT_KEYINFO_PROP, REQUIRE_SUBJECT_KEYINFO_DEFAULT); log.info("{}: requires subject key-info attribute to apply: {}", name, requireSubjectKeyInfo); obligationHandler.setRequireSubjectKeyInfo(requireSubjectKeyInfo); return obligationHandler; } /** * Builds an account mapper for the obligation handler. * * @param accountMapFile * file containing mappings to account indicators * @param preferDNMappingForAccountIndicator * whether account indicators derived from DN mappings should be * preferred over those derived from FQAN mappings * @param groupMapFile * file containing mappings to groups * @param preferDNMappingForPrimaryGroupName * whether primary group derived from DN mappings should be * preferred over those derived from FQAN mappings * @param mapRefreshPeriod * mapping file re-read and refresh period in milliseconds * @param gridMapDir * directory used as backing store for mappings * @param noPrimaryGroupNameIsError * whether the failure to map a primary group name cause an error * or not * @param useSecondaryGroupNamesForMapping * if the lease filename should contain secondary group names or * not * @return the constructed account mapper * * @throws ConfigurationException * thrown if the mapping files can not be read or parsed or if * the grid map directory is not read and writable */ private AccountMapper buildAccountMapper(String accountMapFile, boolean preferDNMappingForAccountIndicator, String groupMapFile, boolean preferDNMappingForPrimaryGroupName, int mapRefreshPeriod, String gridMapDir, boolean noPrimaryGroupNameIsError, boolean useSecondaryGroupNamesForMapping) throws ConfigurationException { DFPMMatchStrategy<X500Principal> dnMatchStrategy= new X509MatchStrategy(); DFPMMatchStrategy<FQAN> fqanMatchStrategy= new FQANMatchStrategy(); DFPM accountIndicatorMap= buildMapping(accountMapFile, mapRefreshPeriod); DFPM groupMap= buildMapping(groupMapFile, mapRefreshPeriod); PoolAccountManager poolAccountManager= buildPoolAccountManager(gridMapDir, useSecondaryGroupNamesForMapping); // account indicator mapping AccountIndicatorMappingStrategy aimStrategy= new DNPrimaryFQANAccountIndicatorMappingStrategy(accountIndicatorMap, dnMatchStrategy, fqanMatchStrategy, preferDNMappingForAccountIndicator); // group names mapping GroupNameMappingStrategy gnmStrategy= new DNFQANGroupNameMappingStrategy(groupMap, dnMatchStrategy, fqanMatchStrategy, preferDNMappingForPrimaryGroupName); return new AccountMapper(aimStrategy, gnmStrategy, poolAccountManager, noPrimaryGroupNameIsError); } /** * Builds an mapping set that refreshes with the given period. * * @param mappingFilePath * file containing the mapping information * @param refreshPeriod * period between refresh of the mapping from the mapping file in * milliseconds * * @return the built mapping * * @throws ConfigurationException * thrown if the is a problem reading the mapping file */ private DFPM buildMapping(String mappingFilePath, int refreshPeriod) throws ConfigurationException { DFPMFactory dfpmFactory= new DFPMFactory() { /** {@inheritDoc} */ public DFPM newInstance() { return new OrderedDFPM(); } }; return new UpdatingDFPM(dfpmFactory, mappingFilePath, refreshPeriod); } /** * Builds a pool account manager. * * @param gridMapDirPath * path used to persist pool account mappings on the filesystem * @param useSecondaryGroupNamesForMapping * if the lease filename in the gridmapDir should contains * secondary group names or not * * @return the pool account manager * * @throws ConfigurationException * thrown if the given grid map directory is not a directory, * can not be read, or can not be written to */ private PoolAccountManager buildPoolAccountManager(String gridMapDirPath, boolean useSecondaryGroupNamesForMapping) throws ConfigurationException { File gridMapDir= new File(gridMapDirPath); if (!gridMapDir.exists()) { String errMsg= MessageFormatter.format("Grid map directory {} does not exist", gridMapDir.getAbsolutePath()); log.error(errMsg); throw new ConfigurationException(errMsg); } if (!gridMapDir.canRead()) { String errMsg= MessageFormatter.format("Grid map directory {} is not readable by this process", gridMapDir.getAbsolutePath()); log.error(errMsg); throw new ConfigurationException(errMsg); } if (!gridMapDir.canWrite()) { String errMsg= MessageFormatter.format("Grid map directory {} is not writable by this process", gridMapDir.getAbsolutePath()); log.error(errMsg); throw new ConfigurationException(errMsg); } GridMapDirPoolAccountManager poolAccountManager= new GridMapDirPoolAccountManager(gridMapDir, useSecondaryGroupNamesForMapping); return poolAccountManager; } }