// // (C) Copyright 2007 VeriSign, Inc. All Rights Reserved. // // VeriSign, Inc. shall have no responsibility, financial or // otherwise, for any consequences arising out of the use of // this material. The program material is provided on an "AS IS" // BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. // // Distributed under an Apache License // http://www.apache.org/licenses/LICENSE-2.0 // package org.verisign.joid.extension; import org.verisign.joid.OpenIdException; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.LinkedHashSet; import java.util.List; import java.util.Iterator; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * Provider Authentication Policy Extension response message. See the * <a href="http://openid.net/specs/openid-provider-authentication-policy-extension-1_0.html">specification</a>. * <p> * Example of parsing incoming responses: * <pre> * Response resp = ResponseFactory.parse(s); * if (resp instanceof AuthenticationResponse) { * AuthenticationResponse ar = (AuthenticationResponse) resp; * PapeResponse pr = new PapeResponse(ar.getExtensions()); * if (pr.isValid()) { * ... * } * } * </pre> * </p> * * <p> * Example of inserting PAPE response to an outgoing reponse: * <pre> * Response resp = request.processUsing(serverInfo); * if (resp instanceof AuthenticationResponse) { * AuthenticationResponse ar = (AuthenticationResponse)resp; * PapeResponse pr = new PapeResponse(); * pr.setAuthAge(3600); * pr.setAuthPolicies(new String[] * { "http://schemas.openid.net/pape/policies/2007/06/phishing-resistant", * "http://schemas.openid.net/pape/policies/2007/06/multi-factor", * "http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical" }); * pr.setNistAuthLevel(4); * ar.addExtension(pr); * } * </pre> * </p> */ public class PapeResponse extends Extension implements PapeConstants { /** * PAPE response parameter: One or more authentication policy URIs * that the OP conformed to when authenticating the End User. If * no policies were met though the OP wishes to convey other * information in the response, this parameter MUST be included * with the value of "none". */ static String AUTH_POLICIES = "auth_policies"; static String EMPTY_AUTH_POLICIES = "http://schemas.openid.net/pape/policies/2007/06/none"; /** * PAPE response parameter: The most recent timestamp when the End * User has actively authenticated to the OP in a manner fitting * the asserted policies. If the RP's request included the * "openid.max_auth_age" parameter then the OP MUST include * "openid.auth_time" in its response. If "openid.max_auth_age" * was not requested, the OP MAY choose to include * "openid.auth_time" in its response. */ static String AUTH_TIME = "auth_time"; /** * The name space for the custom Assurance Level defined by * various parties, such as a country or industry specific * standards body, or other groups or individuals. */ static String AUTH_LEVEL_NS = "auth_level.ns."; /** * The Assurance Level as defined by the above standards body, * group, or individual that corresponds to the authentication * method and policies employed by the OP when authenticating the * End User. */ static String AUTH_LEVEL_ASSURANCE = "auth_level."; /** * Creates a new <code>PapeResponse</code> instance with the * correct namespace and an empty set of * attributes. <code>auth_policies</code> is initialized to the * empty value. */ public PapeResponse () { super(PAPE_NAMESPACE, PAPE_IDENTIFIER); // AUTH_POLICIES is a mandatory parameter setParam(AUTH_POLICIES, EMPTY_AUTH_POLICIES); } /** * Creates a new <code>PapeResponse</code> instance using the * given parameter mappings. Get the <code>extensionMap</code> * parameter from * <code>AuthenticationResponse.getExtensions()</code> * * @param extensionMap a <code>Map<String, String></code> containing the parameter mappings */ public PapeResponse (Map extensionMap) { super(PAPE_NAMESPACE, extensionMap); } /** * Retrieve the <code>auth_time</code> parameter. * * @return the authentication age as a <code>Date</code> value * @exception OpenIdException if the parameter didn't parse to a Date * @see #AUTH_TIME */ public Date getAuthTime () throws OpenIdException { return getDateParam(AUTH_TIME); } /** * Set the <code>auth_time</code> parameter with the given value. * * @param age authentication age in seconds as an <code>int</code> value * @see #AUTH_TIME */ public void setAuthTime (int age) { Date now = new Date(); long time = now.getTime(); time -= age * 1000; // decrement time by age seconds setAuthTime(new Date(time)); } /** * Set the <code>auth_time</code> parameter with the given value. * If <code>null</code> is specified as the value, the parameter * will be removed. Remember to include auth_time in the response * if it was in the request. * * @param age authentication age as a <code>Date</code> value * @see #AUTH_TIME */ public void setAuthTime (Date authTime) { if (authTime == null) { // auth_time is optional, remove it if set to null clearParam(AUTH_TIME); } else { setDateParam(AUTH_TIME, authTime); } } /** * Retrieve the <code>auth_policies</code> parameter values. * * @return authentication policies as a <code>Set<String></code> value * @see #AUTH_POLICIES */ public Set getAuthPolicies () { if (getParam(AUTH_POLICIES).equals(EMPTY_AUTH_POLICIES)) { return new LinkedHashSet(); } return getSetParam(AUTH_POLICIES, " "); } /** * Set the <code>auth_policies</code> parameter with the given * array of policy URI strings. Duplicate URIs will be discarded. * * @param policies a set of policy URIs as a <code>String[]</code> value * @see #AUTH_POLICIES */ public void setAuthPolicies (String[] policies) { setAuthPolicies(new LinkedHashSet(Arrays.asList(policies))); } /** * Set the <code>auth_policies</code> parameter with the given set * of policy URI strings. * * @param policies a set of policy URIs as a <code>Set<String></code> value * @see #AUTH_POLICIES */ public void setAuthPolicies (Set policies) { if (policies.isEmpty()) { setParam(AUTH_POLICIES, EMPTY_AUTH_POLICIES); } else { setListParam(AUTH_POLICIES, policies, " "); } } /** * Retrieve the <code>auth_level_ns</code> namespaces and values. * * @return namespaces as a <code>Map<String, String></code> * @see #AUTH_LEVEL_NS */ Map getAuthLevelNS () { Map map = new HashMap(); Iterator iter = getParamMap().keySet().iterator(); while (iter.hasNext()) { String key = (String) iter.next(); int i = key.indexOf(AUTH_LEVEL_NS); if (i >= 0) { key = key.substring(i); map.put(key.substring(AUTH_LEVEL_NS.length()), getParam(key)); } } return map; } /** * Retrieve the <code>auth_level_ns</code> namespaces and values. * * @param namespace name of namespace * @return namespace value * @see #AUTH_LEVEL_NS */ String getAuthLevelNS (String namespace) { return getParam(AUTH_LEVEL_NS + namespace); } /** * Set the <code>auth_level_ns</code> namespaces and values. * * @param authLevels a set of auth level namespaces as a <code>Map<String, String></code> * @see #AUTH_LEVEL_NS */ void setAuthLevelNS (Map authLevels) throws OpenIdException { Iterator iter = authLevels.keySet().iterator(); while (iter.hasNext()) { String key = (String) iter.next(); addAuthLevelNS(key, (String) authLevels.get(key)); } } /** * Add an <code>auth_level_ns</code> namespace. * * @param namespace the name of the namespace * @param value the value of the namespace's URL identifier * @see #AUTH_LEVEL_NS */ void addAuthLevelNS (String namespace, String value) throws OpenIdException { if ("ns".equals(namespace)) { throw new OpenIdException("Invalid " + AUTH_LEVEL_NS + " parameter; \"ns\" not allowed"); } setParam(AUTH_LEVEL_NS + namespace, value); } /** * Gets the base assurance level value for the given assurance * level namespace alias. The base assurance level parameter is * defined in section 5.2 as * <code>openid.pape.auth_level.<cust></code>. * * @param namespace assurance level URL namespace alias * @return string value of the parameter * @see #AUTH_LEVEL_ASSURANCE */ String getAuthNSAssuranceLevel (String namespace) { return getAuthNSAssuranceLevel(namespace, null); } /** * Gets an additional assurance level parameter value for the * given assurance level namespace alias. Additional assurance * level parameters are defined in section 5.2 as * <code>openid.pape.auth_level.<cust>.<parameter name></code>. * * @param namespace assurance level URL namespace alias * @param param the parameter name (if null, default to base parameter) * @return string value of the parameter * @see #AUTH_LEVEL_ASSURANCE */ String getAuthNSAssuranceLevel (String namespace, String param) { String paramName = new String(AUTH_LEVEL_ASSURANCE + namespace); if (param != null) { paramName += "." + param; } return getParam(paramName); } /** * Set the base assurance level value for the given assurance * level namespace alias. The base assurance level parameter is * defined in section 5.2 as * <code>openid.pape.auth_level.<cust></code>. * * @param namespace assurance level URL * @param value string defined according to this assurance level * @see #AUTH_LEVEL_ASSURANCE */ void setAuthNSAssuranceLevel (String namespace, String value) throws OpenIdException { setAuthNSAssuranceLevel(namespace, null, value); } /** * Sets an additional assurance level parameter value for the * given assurance level namespace alias. Additional assurance * level parameters are defined in section 5.2 as * <code>openid.pape.auth_level.<cust>.<parameter name></code>. * * @param namespace assurance level URL * @param param the parameter name (if null, default to base parameter) * @param value string defined according to this assurance level * @see #AUTH_LEVEL_ASSURANCE */ void setAuthNSAssuranceLevel (String namespace, String param, String value) throws OpenIdException { if ("ns".equals(namespace)) { throw new OpenIdException("Invalid " + AUTH_LEVEL_NS + " parameter; \"ns\" not allowed"); } String paramName = new String(AUTH_LEVEL_ASSURANCE + namespace); if (param != null) { paramName += "." + param; } setParam(paramName, value); } /** * Gets the base assurance level value for the given assurance * level namespace URL. The base assurance level parameter is * defined in section 5.2 as * <code>openid.pape.auth_level.<cust></code>. * * @param namespace assurance level URL * @return string value of the parameter * @see #AUTH_LEVEL_ASSURANCE */ public String getAuthAssuranceLevel (String namespace) { return getAuthAssuranceLevel(namespace, null); } /** * Gets an additional assurance level parameter value for the * given assurance level namespace URL. Additional assurance * level parameters are defined in section 5.2 as * <code>openid.pape.auth_level.<cust>.<parameter name></code>. * * @param namespace assurance level URL * @param param the parameter name (if null, default to base parameter) * @return string value of the parameter * @see #AUTH_LEVEL_ASSURANCE */ public String getAuthAssuranceLevel (String namespace, String param) { Map nsmap = getAuthLevelNS(); Iterator it = nsmap.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); if (namespace.equals((String) entry.getValue())) { return getAuthNSAssuranceLevel((String) entry.getKey(), param); } } return null; } /** * Set the base assurance level value for the given assurance * level namespace URL. The base assurance level parameter is * defined in section 5.2 as * <code>openid.pape.auth_level.<cust></code>. This method will * define a namespace alias for the URL if one is not already * defined. * * @param namespace assurance level URL * @param value string defined according to this assurance level * @see #AUTH_LEVEL_ASSURANCE */ public void setAuthAssuranceLevel (String namespace, String value) throws OpenIdException { setAuthAssuranceLevel(namespace, null, value); } /** * Sets an additional assurance level parameter value for the * given assurance level namespace URL. Additional assurance * level parameters are defined in section 5.2 as * <code>openid.pape.auth_level.<cust>.<parameter name></code>. * This method will define a namespace alias for the URL if one is * not already defined. * * @param namespace assurance level URL * @param param the parameter name (if null, default to base parameter) * @param value string defined according to this assurance level * @see #AUTH_LEVEL_ASSURANCE */ public void setAuthAssuranceLevel (String namespace, String param, String value) throws OpenIdException { Map nsmap = getAuthLevelNS(); Iterator it = nsmap.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); if (namespace.equals((String) entry.getValue())) { setAuthNSAssuranceLevel((String) entry.getKey(), param, value); return; } } int i = 0; String ns; do { ns = "ns" + Integer.toString(i++, 16); } while (nsmap.keySet().contains(ns) && (i < Integer.MAX_VALUE)); if (i == Integer.MAX_VALUE) { throw new OpenIdException("Couldn't find a free namespace"); } addAuthLevelNS(ns, namespace); setAuthNSAssuranceLevel(ns, param, value); } /** * Retrieve a set containing all of the defined assurance level * namespace URLs. * * @return assurance level URLs as a <code>Set<String></code> value */ public Set getAuthAssuranceLevelSet () { return new LinkedHashSet((Collection) getAuthLevelNS().values()); } }