/** * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at opensso/legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * $Id: IDRepoResponseProvider.java,v 1.4 2008/06/25 05:43:51 qcheng Exp $ * */ /* * Portions Copyrighted [2011] [ForgeRock AS] */ package com.sun.identity.policy.plugins; import com.sun.identity.policy.PolicyException; import com.sun.identity.policy.PolicyUtils; import com.sun.identity.policy.PolicyConfig; import com.sun.identity.policy.ResBundleUtils; import com.sun.identity.policy.PolicyManager; import com.sun.identity.policy.interfaces.ResponseProvider; import com.sun.identity.policy.Syntax; import com.iplanet.sso.SSOToken; import com.iplanet.sso.SSOException; import com.sun.identity.idm.AMIdentity; import com.sun.identity.idm.IdRepoException; import com.sun.identity.idm.IdUtils; import com.sun.identity.shared.debug.Debug; import java.util.List; import java.util.Iterator; import java.util.ArrayList; import java.util.Locale; import java.util.Map; import java.util.HashSet; import java.util.HashMap; import java.util.Set; import java.util.Collections; /** * This class is an out of the box implementation of * ResponseProvider interface. It defines 2 types of user * attributes which it can fetch the values of: STATIC * and DYNAMIC. * It relies on underlying Identity repository service to * fetch the attribute values for the Subject(s) defined in the policy. * It computes a Map of response attributes * based on the sso token, resource name and env map passed * in the method call getResponseDecision(). * * Policy framework would make a call to the ResponseProvider to fetch * response attributes in a policy only if the policy is applicable to a * request as determined by sso token, resource name, Subjects and Conditions. * */ public class IDRepoResponseProvider implements ResponseProvider { private static final Debug DEBUG = Debug.getInstance(PolicyManager.POLICY_DEBUG_NAME); public static final String STATIC_ATTRIBUTE = "StaticAttribute"; public static final String DYNAMIC_ATTRIBUTE = "DynamicAttribute"; public static final String ATTR_DELIMITER = "="; public static final String VAL_DELIMITER = "|"; private Map properties; private static List propertyNames = new ArrayList(2); private boolean initialized=false; private String orgName = null; private Set validDynamicAttrNames = null; private Map staticResponse = null; private Set responseAttrNames = null; //for dynamic attributes private Set repoAttrNames = null; //for dynamic attributes private Map responseAttrToRepoAttr = null; static { propertyNames.add(STATIC_ATTRIBUTE); propertyNames.add(DYNAMIC_ATTRIBUTE); } /** * No argument constructor. */ public IDRepoResponseProvider () { } /** * Initialize the IDRepoResponseProvider object by using the configuration * information passed by the Policy Framework. * @param configParams the configuration information * @exception PolicyException if an error occured during * initialization of the instance */ public void initialize(Map configParams) throws PolicyException { if (DEBUG.messageEnabled()) { DEBUG.message("IDRepoResponseProvider.initialize():" + "entering"); } if (configParams == null) { throw (new PolicyException(ResBundleUtils.rbName, "idrepo_initialization_failed", null, null)); } // get the organization name Set orgNameSet = (Set) configParams.get( PolicyManager.ORGANIZATION_NAME); if ((orgNameSet != null) && (!orgNameSet.isEmpty())) { Iterator items = orgNameSet.iterator(); orgName = (String) items.next(); } validDynamicAttrNames = (Set)configParams.get( PolicyConfig.SELECTED_DYNAMIC_ATTRIBUTES); if ( validDynamicAttrNames == null) { validDynamicAttrNames = Collections.EMPTY_SET; } initialized = true; if (DEBUG.messageEnabled()) { DEBUG.message("IDRepoResponseProvider.initialize():" + "initialized with:" + "orgName=" + orgName + ",validDynamicAttrNames=" + validDynamicAttrNames); } } /** * Returns a list of property names for the responseprovider. * * @return List of property names */ public List getPropertyNames() { return propertyNames; } /** * Returns the syntax for a property name * @see com.sun.identity.policy.Syntax * * @param property property name * * @return Syntax for the property name */ public Syntax getPropertySyntax(String property) { if (property.equals(STATIC_ATTRIBUTE)) { return (Syntax.ANY); } if (property.equals(DYNAMIC_ATTRIBUTE)) { return (Syntax.MULTIPLE_CHOICE); } return (Syntax.ANY); } /** * Gets the display name for the property name. * The locale variable could be used by the plugin to * customize the display name for the given locale. * The locale variable could be null, in which * case the plugin must use the default locale. * * @param property property name * @param locale locale for which the property name must be customized * @return display name for the property name. * @throws PolicyException */ public String getDisplayName(String property, Locale locale) throws PolicyException { return property; } /** * Returns a set of valid values given the property name. This method * is called if the property Syntax is either the SINGLE_CHOICE or * MULTIPLE_CHOICE. * * @param property property name * from the PolicyConfig Service configured for the specified realm. * @return Set of valid values for the property. * @exception PolicyException if unable to get the Syntax. */ public Set getValidValues(String property) throws PolicyException { if (!initialized) { throw (new PolicyException(ResBundleUtils.rbName, "idrepo_response_provider_not_yet_initialized", null, null)); } if (property.equals(DYNAMIC_ATTRIBUTE)) { return validDynamicAttrNames; } else { return Collections.EMPTY_SET; } } /** Sets the properties of the responseProvider plugin. * This influences the response attribute-value Map that would be * computed by a call to method getResponseDecision(Map) * These attribute-value pairs are encapsulated in * ResponseAttribute element tag which is a child of the * PolicyDecision element in the PolicyResponse xml * if the policy is applicable to the user for the resource, subject and * conditions defined. * @param properties the properties of the responseProvider * Keys of the properties have to be String. * Value corresponding to each key have to be a Set of String * elements. Each implementation of ResponseProvider could add * further restrictions on the keys and values of this map. * @throws PolicyException for any abnormal condition */ public void setProperties(Map properties) throws PolicyException { if (DEBUG.messageEnabled()) { DEBUG.message("IDRepoResponseProvider.setProperties():" + "entering with properties=" + properties); } if ( (properties == null) || ( properties.isEmpty()) ) { throw new PolicyException( ResBundleUtils.rbName, "properties_can_not_be_null_or_empty", null, null); } this.properties = properties; //Check if the keys needed for this provider are present namely // STATIC_ATTRIBUTE and DYNAMIC_ATTRIBUTE if (!properties.containsKey(STATIC_ATTRIBUTE) && !properties.containsKey(DYNAMIC_ATTRIBUTE)) { String args[] = { STATIC_ATTRIBUTE,DYNAMIC_ATTRIBUTE }; throw new PolicyException( ResBundleUtils.rbName, "missing_required_property", args, null); } //validates STATIC_ATTRIBUTE and caches parsed static attributes map Set staticSet = (Set)properties.get(STATIC_ATTRIBUTE); if (staticSet != null) { validateStaticAttribute(staticSet); } //validates DYNAMIC_ATTRIBUTE and caches parsed // responseAttrNames, repoAttrNames, responseAttrToRepoAttr Set dynamicSet = (Set)properties.get(DYNAMIC_ATTRIBUTE); if (dynamicSet != null) { validateDynamicAttribute(dynamicSet); } if (DEBUG.messageEnabled()) { DEBUG.message("IDRepoResponseProvider.setProperties():" + "returning"); } } /** Gets the properties of the responseprovider * @return properties of the responseprovider * @see #setProperties */ public Map getProperties() { return (properties == null) ? null : Collections.unmodifiableMap(properties); } /** * Gets the response attributes computed by this ResponseProvider object, * based on the sso token and map of environment parameters * * @param token single-sign-on token of the user * * @param env specific environment map of key/value pairs * @return a Map of response attributes. * Keys of the Map are attribute names STATIC_ATTRIBUTE or * DYNAMIC_ATTRIBUTE. * Value is a Set of Strings representing response attribute * values. * * @throws PolicyException if the decision could not be computed * @throws SSOException if SSO token is not valid * */ public Map getResponseDecision(SSOToken token, Map env) throws PolicyException, SSOException { if (DEBUG.messageEnabled()) { DEBUG.message("IDRepoResponseProvider.getResponseDecision():" + "entering"); } Map returnValues = new HashMap(); if (staticResponse != null) { if (DEBUG.messageEnabled()) { DEBUG.message("IDRepoResponseProvider." + "getResponseDecision():" + "adding staticResponse=" + staticResponse); } PolicyUtils.appendMapToMap(staticResponse, returnValues); } if ((repoAttrNames != null) && !repoAttrNames.isEmpty()) { Map dynamicResponse = new HashMap(); Map idRepoMap = new HashMap(); try { if (token.getPrincipal() != null) { AMIdentity id = IdUtils.getIdentity(token); idRepoMap = id.getAttributes(repoAttrNames); if (idRepoMap != null) { for (Iterator iter = responseAttrNames.iterator(); iter.hasNext(); ) { String responseAttrName = (String)iter.next(); Set idRepoAttrNames = (Set)responseAttrToRepoAttr.get( responseAttrName); Set values = new HashSet(); for (Iterator iter1 = idRepoAttrNames.iterator(); iter1.hasNext();) { String repoAttrName = (String)iter1.next(); Set subValues = (Set)idRepoMap.get(repoAttrName); if (subValues != null) { values.addAll(subValues); } } dynamicResponse.put(responseAttrName, values); } } if (DEBUG.messageEnabled()) { DEBUG.message("IDRepoResponseProvider." + "getResponseDecision():" + "adding dynamicResponse=" + dynamicResponse); } PolicyUtils.appendMapToMap(dynamicResponse,returnValues); } else { DEBUG.error("IDRepoResponseProvider:"+ "getResponseDecision(): Principal is null"); throw (new PolicyException(ResBundleUtils.rbName, "token_principal_null", null, null)); } } catch (IdRepoException ide) { DEBUG.error("IDRepoResponseProvider:"+ "getResponseDecision():" +"IdRepoException", ide); throw new PolicyException(ide); } } if (DEBUG.messageEnabled()) { DEBUG.message("IDRepoResponseProvider.getResponseDecision():" + "returning response=" + returnValues); } return returnValues; } /** * This method validates the STATIC_ATTRIBUTE data * for format and caches parsed static attributes map * Needs to be in "attr=val" format. * Else, throws PolicyException */ private void validateStaticAttribute(Set staticSet) throws PolicyException { if (DEBUG.messageEnabled()) { DEBUG.message("IDRepoResponseProvider.validateStaticAttribute():" + "entering with staticSet=" + staticSet); } if (!staticSet.isEmpty()) { staticResponse = new HashMap(); for (Iterator it = staticSet.iterator(); it.hasNext();) { String attrValueString = (String)it.next(); if (attrValueString.indexOf(ATTR_DELIMITER) == -1 ) { clearProperties(); DEBUG.error("IDRepoResponseProvider" + ".validateStaticAttribute():" + " Invalid format in defining StaticAttribute, needs" + " to be attr=value format"); String args[] = {attrValueString}; throw new PolicyException(ResBundleUtils.rbName, "invalid_format_static_property", args, null); } else { int index = attrValueString.indexOf(ATTR_DELIMITER); String attrName = attrValueString.substring(0,index).trim(); String attrValue = attrValueString.substring(index+1); Set values = PolicyUtils.delimStringToSet(attrValue, VAL_DELIMITER); PolicyUtils.appendElementToMap(attrName, values, staticResponse); if (DEBUG.messageEnabled()) { DEBUG.message("IDRepoResponseProvider." + "validateStaticAttribute():" + "attrName=" + attrName + ",values=" + values); DEBUG.message("IDRepoResponseProvider." + "validateStaticAttribute():" + "caching staticResponse:" + staticResponse ); } } } } if (DEBUG.messageEnabled()) { DEBUG.message("IDRepoResponseProvider.validateStaticAttribute():" + "returning"); } } /** * This method validates the DYNAMIC_ATTRIBUTE data * for format and caches parsed * responseAttrNames, repoAttrNames * Strings in the Set need to be in "responseAttr=repoAttr" format * Else, throws PolicyException */ private void validateDynamicAttribute(Set dynamicSet) throws PolicyException { if (DEBUG.messageEnabled()) { DEBUG.message("IDRepoResponseProvider.validateDynamicAttribute():" + "entering with dynamicSet=" + dynamicSet); } responseAttrNames = new HashSet(); repoAttrNames = new HashSet(); responseAttrToRepoAttr = new HashMap(); /* check if the attribute names being set in DYNAMIC_ATTRIBUTE * are valid i.e are as defined in policy config service. * Parse and store responseAttrNames and repoAttrNames */ if (DEBUG.messageEnabled()) { DEBUG.message("IDRepoResponseProvider.validateDynamicAttribute():" +"valid dynamic attributes:" + validDynamicAttrNames); } Set dynamicAttrs = ((Set)properties.get(DYNAMIC_ATTRIBUTE)); if (DEBUG.messageEnabled()) { DEBUG.message("IDRepoResponseProvider.validateDynamicAttribute():" +"selected dynamic attributes:" + dynamicAttrs); } Iterator dynamicAttrsIter = dynamicAttrs.iterator(); while (dynamicAttrsIter.hasNext()) { String attr = (String) dynamicAttrsIter.next(); if (!validDynamicAttrNames.contains(attr)) { if (DEBUG.warningEnabled()) { DEBUG.warning("IDReporesponseProvider." +"validateDynamicAttribute():Invalid dynamic property " +"encountered:"+attr); } continue; } String[] attrNames = parseDynamicAttrName(attr); String responseAttrName = attrNames[0]; String repoAttrName = attrNames[1]; responseAttrNames.add(responseAttrName); repoAttrNames.add(repoAttrName); addToResponseAttrToRepoAttrMap(responseAttrName, repoAttrName); if (DEBUG.messageEnabled()) { DEBUG.message("IDRepoResponseProvider." + "validateDynamicAttribute():" +"responseAttrName=" + responseAttrName + ", repoAttrName=" + repoAttrName); } } if (DEBUG.messageEnabled()) { DEBUG.message("IDRepoResponseProvider.validateDynamicAttribute():" + "responseAttrToRepoAttr=" + responseAttrToRepoAttr); DEBUG.message("IDRepoResponseProvider.validateDynamicAttribute():" + "returning"); } } private String[] parseDynamicAttrName(String dynamicAttrName) throws PolicyException { String[] parsedNames = new String[2]; if (dynamicAttrName != null) { int delimiterIndex = dynamicAttrName.indexOf(ATTR_DELIMITER); if (delimiterIndex == 0) { clearProperties(); String args[] = { dynamicAttrName }; throw new PolicyException( ResBundleUtils.rbName, "invalid_dynamic_property_being_set", args, null); } else if (delimiterIndex < 0) { String value = dynamicAttrName.trim(); if (value.length() == 0) { clearProperties(); String args[] = { dynamicAttrName }; throw new PolicyException( ResBundleUtils.rbName, "invalid_dynamic_property_being_set", args, null); } parsedNames[0] = value; parsedNames[1] = value; } else { String value1 = dynamicAttrName.substring(0, delimiterIndex).trim(); String value2 = dynamicAttrName.substring(delimiterIndex + 1).trim(); if ((value1.length() == 0) || (value2.length() == 0)) { clearProperties(); String args[] = { dynamicAttrName }; throw new PolicyException( ResBundleUtils.rbName, "invalid_dynamic_property_being_set", args, null); } parsedNames[0] = value1; parsedNames[1] = value2; } } return parsedNames; } private void addToResponseAttrToRepoAttrMap(String responseAttrName, String repoAttrName) throws PolicyException { Set idRepoAttrNames = (Set)responseAttrToRepoAttr.get(responseAttrName); if (idRepoAttrNames == null) { idRepoAttrNames = new HashSet(); responseAttrToRepoAttr.put(responseAttrName, idRepoAttrNames); } idRepoAttrNames.add(repoAttrName); } private void clearProperties() { properties = null; staticResponse = null; responseAttrNames = null; repoAttrNames = null; responseAttrToRepoAttr = null; } /** * Returns a copy of this object. * * @return a copy of this object */ public Object clone() { IDRepoResponseProvider theClone = null; try { theClone = (IDRepoResponseProvider)super.clone(); } catch (CloneNotSupportedException e) { // this should never happen throw new InternalError(); } if (validDynamicAttrNames != null) { theClone.validDynamicAttrNames = new HashSet(); theClone.validDynamicAttrNames.addAll(validDynamicAttrNames); } if (properties != null) { theClone.properties = new HashMap(); Iterator iter = properties.keySet().iterator(); while (iter.hasNext()) { Object obj = iter.next(); Set values = new HashSet(); values.addAll((Set) properties.get(obj)); theClone.properties.put(obj, values); } } if (staticResponse != null) { theClone.staticResponse = new HashMap(); Iterator iter = staticResponse.keySet().iterator(); while (iter.hasNext()) { Object obj = iter.next(); Set values = new HashSet(); values.addAll((Set) staticResponse.get(obj)); theClone.staticResponse.put(obj, values); } } if (responseAttrNames != null) { theClone.responseAttrNames = new HashSet(); theClone.responseAttrNames.addAll(responseAttrNames); } if (repoAttrNames != null) { theClone.repoAttrNames = new HashSet(); theClone.repoAttrNames.addAll(repoAttrNames); } if (responseAttrToRepoAttr != null) { theClone.responseAttrToRepoAttr = new HashMap(); Iterator iter = responseAttrToRepoAttr.keySet().iterator(); while (iter.hasNext()) { Object obj = iter.next(); Set values = new HashSet(); values.addAll((Set) responseAttrToRepoAttr.get(obj)); theClone.responseAttrToRepoAttr.put(obj, values); } } return theClone; } }