/* * 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.text.ParseException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.security.auth.x500.X500Principal; import org.glite.authz.common.fqan.FQAN; import org.glite.authz.common.model.Attribute; import org.glite.authz.common.model.AttributeAssignment; import org.glite.authz.common.model.Obligation; import org.glite.authz.common.model.Request; import org.glite.authz.common.model.Result; import org.glite.authz.common.model.Subject; import org.glite.authz.common.profile.GLiteAuthorizationProfileConstants; import org.glite.authz.pep.obligation.AbstractObligationHandler; import org.glite.authz.pep.obligation.ObligationProcessingException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * An obligation handler that transforms an * {@value GLiteAuthorizationProfileConstants#ID_OBLIGATION_LOCAL_ENV_MAP} obligation * in to a {@value GLiteAuthorizationProfileConstants#ID_OBLIGATION_POSIX_ENV_MAP} * obligation. The POSIX login name and primary and secondary group values are * determined by mapping the {@value Attribute#ID_SUB_ID}, * {@value GLiteAuthorizationProfileConstants#ID_ATTRIBUTE_PRIMARY_FQAN} and * {@value GLiteAuthorizationProfileConstants#ID_ATTRIBUTE_FQAN} attributes found * within the {@link Subject} of the authorization request. */ public class DFPMObligationHandler extends AbstractObligationHandler { /** Class logger. */ private final Logger log= LoggerFactory.getLogger(DFPMObligationHandler.class); /** DN/FQAN to POSIX account mapper. */ private AccountMapper accountMapper; /** * Request subject must contain a key-info attribute for this OH to apply: * Default {@value} */ private boolean requireSubjectKeyInfo= true; /** * Constructor. * * @param name * The obligation handler unique id (name) * @param obligationId * The handled obligation ID * @param mapper * used to map a subject to a POSIX account */ public DFPMObligationHandler(String name, String obligationId, AccountMapper mapper) { super(name, obligationId); if (mapper == null) { throw new IllegalArgumentException("Account mapper may not be null"); } accountMapper= mapper; } /** * Constructor. Default handled obligation ID: * {@value GLiteAuthorizationProfileConstants#ID_OBLIGATION_LOCAL_ENV_MAP} * * @param name * the obligation handler name * @param mapper * mapper used to map a subject to a POSIX account */ public DFPMObligationHandler(String name, AccountMapper mapper) { this(name, GLiteAuthorizationProfileConstants.ID_OBLIGATION_LOCAL_ENV_MAP, mapper); } /** * Constructor. * * @param name * the obligation handler name * @param precedence * precendence of this obligation handler * @param mapper * mapper used to map a subject to a POSIX account */ public DFPMObligationHandler(String name, int precedence, AccountMapper mapper) { this(name, GLiteAuthorizationProfileConstants.ID_OBLIGATION_LOCAL_ENV_MAP, mapper); setHanderPrecedence(precedence); } /** {@inheritDoc} */ public boolean evaluateObligation(Request request, Result result) throws ObligationProcessingException { boolean applied= false; Subject subject= getSubject(request); // check for the key-info attribute in the request, if required and not // present, don't apply OH if (requireSubjectKeyInfo && !subjectContainsKeyInfo(subject)) { log.info("{}: Does not apply. Requires a request subject key-info attribute, but none found.", getId()); return false; } X500Principal subjectDN= getDN(subject); FQAN primaryFQAN= getPrimaryFQAN(subject); List<FQAN> secondaryFQANs= getSecondaryFQANs(subject); PosixAccount mappedAccount= accountMapper.mapToAccount(subjectDN, primaryFQAN, secondaryFQANs); if (mappedAccount != null) { addPosixMappingObligation(result, mappedAccount); applied= true; // Remove the local environment mapping obligation (even if it // appears multiple times) // since we've handled it and replaced it with the POSIX mapping // obligations Iterator<Obligation> obligationItr= result.getObligations().iterator(); Obligation obligation; List<Obligation> removedObligations= new ArrayList<Obligation>(); while (obligationItr.hasNext()) { obligation= obligationItr.next(); if (getObligationId().equals(obligation.getId())) { removedObligations.add(obligation); } } result.getObligations().removeAll(removedObligations); log.info("{}: DN: {} pFQAN: {} FQANs: {} mapped to POSIX account: {}", new Object[] { getId(), subjectDN.getName(X500Principal.RFC2253), primaryFQAN, secondaryFQANs, mappedAccount }); } log.debug("Finished processing DN/FQAN to POSIX account mapping obligation for subject {}", subjectDN.getName()); return applied; } /** * Gets the subject from the request. * * @param request * authorization request * * @return the subject of the request * * @throws ObligationProcessingException * thrown if there is zero or more than one subject in the * request */ private Subject getSubject(Request request) throws ObligationProcessingException { Set<Subject> subjects= request.getSubjects(); if (subjects == null || subjects.isEmpty()) { throw new ObligationProcessingException("Unable to process request, it does not contain a subject"); } if (subjects.size() != 1) { log.warn("Request contains '{}' subject, unable to process it", subjects.size()); throw new ObligationProcessingException("Requests contains more than one subject"); } return subjects.iterator().next(); } /** * Checks if the subject contains at least one key-info attribute. * * @param subject * the Subject to check * @return <code>true</code> if the subject contains a key-info attribute */ private boolean subjectContainsKeyInfo(Subject subject) { Set<Attribute> attributes= subject.getAttributes(); if (attributes != null) { for (Attribute attribute : attributes) { if (Attribute.ID_SUB_KEY_INFO.equals(attribute.getId())) { return true; } } } return false; } /** * Gets the subject's DN from the subject DN attribute. * * @param subject * the subject of the request * * @return the subject DN * * @throws ObligationProcessingException * thrown if the given attribute contains no values, is not of * the right data type, or its value is not a valid DN */ private X500Principal getDN(Subject subject) throws ObligationProcessingException { Attribute dnAttribute= null; for (Attribute attribute : subject.getAttributes()) { if (Attribute.ID_SUB_ID.equals(attribute.getId()) && Attribute.DT_X500_NAME.equals(attribute.getDataType())) { log.debug("Extracted subject attribute from request: {}", attribute); dnAttribute= attribute; break; } } if (dnAttribute == null) { log.error("Subject of the authorization request did not contain a subject ID attribute {} datatype {}", Attribute.ID_SUB_ID, Attribute.DT_X500_NAME); throw new ObligationProcessingException("Invalid request, missing subject attribute: " + Attribute.ID_SUB_ID + " datatype: " + Attribute.DT_X500_NAME); } Set<?> values= dnAttribute.getValues(); if (values == null || values.isEmpty()) { log.error("Subject ID attribute of the authorization request did not contain any values"); throw new ObligationProcessingException("Invalid request, subject attribute did not contain any values"); } if (values.size() > 1) { log.warn("Subject ID attribute contains more than one value, only the first will be used"); } try { String subjectDN= values.iterator().next().toString(); return new X500Principal(subjectDN); } catch (IllegalArgumentException e) { log.error("Value of the Subject ID attribute of the authorization request was not a valid X.509 DN"); throw new ObligationProcessingException("Invalid request, value of the Subject ID attribute was not a valid X.509 DN"); } } /** * @param requireSubjectKeyInfo * the requireSubjectKeyInfo to set */ protected void setRequireSubjectKeyInfo(boolean requireSubjectKeyInfo) { this.requireSubjectKeyInfo= requireSubjectKeyInfo; } /** * Gets the primary FQAN from the request subject. * * @param subject * the subject of the request * * @return the primary FQAN * * @throws ObligationProcessingException * thrown if the given attribute contains no values, is not of * the right data type, or its value is not a valid FQAN */ private FQAN getPrimaryFQAN(Subject subject) throws ObligationProcessingException { Attribute primaryFQANAttribute= null; for (Attribute attribute : subject.getAttributes()) { if (GLiteAuthorizationProfileConstants.ID_ATTRIBUTE_PRIMARY_FQAN.equals(attribute.getId())) { log.debug("Extracted primary FQAN attribute from request: {}", attribute); primaryFQANAttribute= attribute; break; } } if (primaryFQANAttribute == null) { log.debug("Subject of the authorization request did not contain a subject primary FQAN attribute"); return null; } if (!GLiteAuthorizationProfileConstants.DATATYPE_FQAN.equals(primaryFQANAttribute.getDataType())) { log.error("Subject primary FQAN attribute of the authorization request was of the incorrect data type: {}", primaryFQANAttribute.getDataType()); throw new ObligationProcessingException("Invalid request, subject attribute of invalid data type"); } Set<?> values= primaryFQANAttribute.getValues(); if (values == null || values.isEmpty()) { log.error("Subject primary FQAN attribute of the authorization request did not contain any values"); throw new ObligationProcessingException("Invalid request, subject attribute did not contain any values"); } if (values.size() > 1) { log.warn("Primary FQAN attribute contains more than one value, only the first will be used"); } try { return FQAN.parseFQAN(values.iterator().next().toString()); } catch (ParseException e) { log.error("Value of the Subject primary FQAN attribute of the authorization request was not a valid FQAN", e); throw new ObligationProcessingException("Invalid request, subject's primary FQAN attribute value was invalid", e); } } /** * Gets the secondary FQANs from the request subject. * * @param subject * the subject of the request * * @return the secondary FQANs * * @throws ObligationProcessingException * thrown if the given attribute contains no values, is not of * the right data type, or its value is not a valid FQAN */ private List<FQAN> getSecondaryFQANs(Subject subject) throws ObligationProcessingException { Attribute secondaryFQANsAttribute= null; for (Attribute attribute : subject.getAttributes()) { if (GLiteAuthorizationProfileConstants.ID_ATTRIBUTE_FQAN.equals(attribute.getId())) { log.debug("Extracted secondary FQAN attribute from request: {}", attribute); secondaryFQANsAttribute= attribute; break; } } if (secondaryFQANsAttribute == null) { log.debug("Subject of the authorization request did not contain a subject secondary FQAN attribute"); return null; } if (!GLiteAuthorizationProfileConstants.DATATYPE_FQAN.equals(secondaryFQANsAttribute.getDataType())) { log.error("Subject secondary FQAN attribute of the authorization request was of the incorrect data type: {}", secondaryFQANsAttribute.getDataType()); throw new ObligationProcessingException("Invalid request, subject attribute of invalid data type"); } Set<?> values= secondaryFQANsAttribute.getValues(); if (values == null || values.isEmpty()) { log.error("Subject secondary FQAN attribute of the authorization request did not contain any values"); throw new ObligationProcessingException("Invalid request, subject attribute did not contain any values"); } if (values.size() > 1) { log.warn("Secondary FQAN attribute contains more than one value, only the first will be used"); } ArrayList<FQAN> secondaryFQANs= new ArrayList<FQAN>(); Iterator<?> valueItr= values.iterator(); String value= null; while (valueItr.hasNext()) { try { value= valueItr.next().toString(); FQAN parsedFQAN= FQAN.parseFQAN(value); secondaryFQANs.add(parsedFQAN); } catch (ParseException e) { log.error("Subject's secondary FQAN attribute value " + value + " is not a valid FQAN"); throw new ObligationProcessingException("Invalid request, subject's secondary FQAN attribute value was invalid"); } } return secondaryFQANs; } /** * Adds a {@link WorkerNodeProfileV1Constants#OBL_POSIX_ENV_MAP} to the * result. * * @param result * current result * @param account * account whose information will be used to populate the * {@link GLiteAuthorizationProfileConstants#ID_ATTRIBUTE_USER_ID}, * {@link GLiteAuthorizationProfileConstants#ID_ATTRIBUTE_PRIMARY_GROUP_ID} * , and * {@link GLiteAuthorizationProfileConstants#ID_ATTRIBUTE_GROUP_ID} * attribute assignments of the obligation */ protected void addPosixMappingObligation(Result result, PosixAccount account) { Obligation posixMapping= new Obligation(); posixMapping.setId(GLiteAuthorizationProfileConstants.ID_OBLIGATION_POSIX_ENV_MAP); posixMapping.setFulfillOn(Result.DECISION_PERMIT); AttributeAssignment userid= new AttributeAssignment(); userid.setAttributeId(GLiteAuthorizationProfileConstants.ID_ATTRIBUTE_USER_ID); userid.setDataType(Attribute.DT_STRING); userid.setValue(account.getLoginName()); posixMapping.getAttributeAssignments().add(userid); if (account.getPrimaryGroup() != null) { String groupId= account.getPrimaryGroup(); AttributeAssignment primaryGroupId= new AttributeAssignment(); primaryGroupId.setAttributeId(GLiteAuthorizationProfileConstants.ID_ATTRIBUTE_PRIMARY_GROUP_ID); primaryGroupId.setDataType(Attribute.DT_STRING); primaryGroupId.setValue(groupId); posixMapping.getAttributeAssignments().add(primaryGroupId); // BUG FIX: profile attribute/group-id doesn't contain primary group // see https://savannah.cern.ch/bugs/index.php?64340 AttributeAssignment secondaryGroupId= new AttributeAssignment(); secondaryGroupId.setAttributeId(GLiteAuthorizationProfileConstants.ID_ATTRIBUTE_GROUP_ID); secondaryGroupId.setDataType(Attribute.DT_STRING); secondaryGroupId.setValue(groupId); posixMapping.getAttributeAssignments().add(secondaryGroupId); } if (account.getSecondaryGroups() != null && !account.getSecondaryGroups().isEmpty()) { for (String secondaryGroup : account.getSecondaryGroups()) { AttributeAssignment secondaryGroupId= new AttributeAssignment(); secondaryGroupId.setAttributeId(GLiteAuthorizationProfileConstants.ID_ATTRIBUTE_GROUP_ID); secondaryGroupId.setDataType(Attribute.DT_STRING); secondaryGroupId.setValue(secondaryGroup); posixMapping.getAttributeAssignments().add(secondaryGroupId); } } result.getObligations().add(posixMapping); } }