package org.exist.security.xacml; import java.net.URI; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.apache.log4j.Logger; import org.exist.security.User; import com.sun.xacml.EvaluationCtx; import com.sun.xacml.attr.AttributeDesignator; import com.sun.xacml.attr.AttributeValue; import com.sun.xacml.attr.BagAttribute; import com.sun.xacml.attr.StringAttribute; import com.sun.xacml.cond.EvaluationResult; import com.sun.xacml.ctx.Status; import com.sun.xacml.finder.AttributeFinderModule; /** * This class looks up attributes for a Subject with a subject-category * of access-subject. The currently supported attributes are * {@link XACMLConstants#USER_NAME_ATTRIBUTE user name} and * {@link XACMLConstants#GROUP_ATTRIBUTE groups}. This is a possible * implementation point for LDAP lookup if this is desired * in the future. */ public class UserAttributeModule extends AttributeFinderModule { private static final Logger LOG = Logger.getLogger(UserAttributeModule.class); private ExistPDP pdp; private UserAttributeModule() {} /** * Creates an <code>AttributeFinderModule</code> capable of retrieving attributes * for a <code>User</code>. * * @param pdp The <code>ExistPDP</code> that is used to obtain information * about a given <code>User</code>. */ public UserAttributeModule(ExistPDP pdp) { this.pdp = pdp; } public EvaluationResult findAttribute(URI attributeType, URI attributeId, URI issuer, URI subjectCategory, EvaluationCtx context, int designatorType) { if(designatorType != AttributeDesignator.SUBJECT_TARGET) return errorResult("Invalid designator type: UserAttributeModule only handles subjects"); if(issuer != null) return errorResult("UserAttributeModule cannot handle requests with an issuer specified."); if(!XACMLConstants.ACCESS_SUBJECT.equals(subjectCategory)) return errorResult("UserAttributeModule can only handle subject category '" + XACMLConstants.ACCESS_SUBJECT + "'"); if(!XACMLConstants.STRING_TYPE.equals(attributeType)) return errorResult("UserAttributeModule can only handle data type '" + XACMLConstants.STRING_TYPE + "'"); EvaluationResult subjectID = context.getSubjectAttribute(attributeType, XACMLConstants.SUBJECT_ID_ATTRIBUTE, issuer, subjectCategory); if(subjectID.indeterminate()) return subjectID; AttributeValue value = subjectID.getAttributeValue(); if(value == null) return errorResult("Could not find user for context: null subject-id"); if(value.isBag()) { BagAttribute bag = (BagAttribute)value; if(bag.isEmpty()) return errorResult("Could not find user for context: no subject-id found"); if(bag.size() > 1) return errorResult("Error finding attribute: Subject-id attribute is not unique."); value = (AttributeValue)bag.iterator().next(); } if(!(value instanceof StringAttribute)) return errorResult("Error finding attribute: Subject-id attribute must be a string."); String uid = ((StringAttribute)value).getValue(); User user = pdp.getBrokerPool().getSecurityManager().getUser(uid); if(user == null) return errorResult("No user exists for UID '" + uid + "'"); if(XACMLConstants.GROUP_ATTRIBUTE.equals(attributeId)) return getGroups(user); else if(XACMLConstants.USER_NAME_ATTRIBUTE.equals(attributeId)) return new EvaluationResult(new StringAttribute(user.getName())); else return errorResult("UserAttributeModule cannot handle attribute '" + attributeId + "'"); } //gets a bag consisting of the groups of the user private EvaluationResult getGroups(User user) { String[] groupArray = user.getGroups(); int size = (groupArray == null) ? 0 : groupArray.length; Set groupAttributes = new HashSet(size); for(int i = 0; i < size; ++i) groupAttributes.add(new StringAttribute(groupArray[i])); AttributeValue value = new BagAttribute(XACMLConstants.STRING_TYPE, groupAttributes); return new EvaluationResult(value); } //logs the specified message and exception //then, returns a result with status Indeterminate and the given message private static EvaluationResult errorResult(String message) { LOG.warn(message); return new EvaluationResult(new Status(Collections.singletonList(Status.STATUS_PROCESSING_ERROR), message)); } /** * Indicates support of looking up attributes by * data supplied by an AttributeDesignator element, * specifically, a SubjectAttributeDesignator element. * * @return true to indicate that this module supports * this method of looking up attributes */ public boolean isDesignatorSupported() { return true; } /** * Returns a <code>Set</code> containing * <code>AttributeDesignator.SUBJECT_TARGET</code> * to indicate that this module only supports * <code>Subject</code>s. * * @return A <code>Set</code> indicating the supported * designator type. */ public Set getSupportedDesignatorTypes() { return Collections.singleton(new Integer(AttributeDesignator.SUBJECT_TARGET)); } /** * A <code>Set</code> containing the <code>URI</code>s * {@link XACMLConstants#USER_NAME_ATTRIBUTE user name} and * {@link XACMLConstants#GROUP_ATTRIBUTE groups} to indicate that * these are the only attributes supported by this module. * * @return A <code>Set</code> indicating the supported * attribute ids. */ public Set getSupportedIds() { Set set = new HashSet(4); set.add(XACMLConstants.GROUP_ATTRIBUTE); set.add(XACMLConstants.USER_NAME_ATTRIBUTE); return set; } }