diff options
Diffstat (limited to 'src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy')
5 files changed, 567 insertions, 0 deletions
diff --git a/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs new file mode 100644 index 0000000..f505a9b --- /dev/null +++ b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs @@ -0,0 +1,40 @@ +using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy {
+ /// <summary>
+ /// Well-known authentication policies defined in the PAPE extension spec or by a recognized
+ /// standards body.
+ /// </summary>
+ /// <remarks>
+ /// This is a class of constants rather than a flags enum because policies may be
+ /// freely defined and used by anyone, just by using a new Uri.
+ /// </remarks>
+ public static class AuthenticationPolicies {
+ /// <summary>
+ /// Used in a PAPE response to indicate that no PAPE authentication policies could be satisfied.
+ /// </summary>
+ /// <remarks>
+ /// Used internally by the PAPE extension, so that users don't have to know about it.
+ /// </remarks>
+ internal const string None = "http://schemas.openid.net/pape/policies/2007/06/none";
+ /// <summary>
+ /// An authentication mechanism where the End User does not provide a shared secret to a party potentially under the control of the Relying Party. (Note that the potentially malicious Relying Party controls where the User-Agent is redirected to and thus may not send it to the End User's actual OpenID Provider).
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Phishing")]
+ public const string PhishingResistant = "http://schemas.openid.net/pape/policies/2007/06/phishing-resistant";
+ /// <summary>
+ /// An authentication mechanism where the End User authenticates to the OpenID Provider by providing over one authentication factor. Common authentication factors are something you know, something you have, and something you are. An example would be authentication using a password and a software token or digital certificate.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "MultiFactor")]
+ public const string MultiFactor = "http://schemas.openid.net/pape/policies/2007/06/multi-factor";
+ /// <summary>
+ /// An authentication mechanism where the End User authenticates to the OpenID Provider by providing over one authentication factor where at least one of the factors is a physical factor such as a hardware device or biometric. Common authentication factors are something you know, something you have, and something you are. This policy also implies the Multi-Factor Authentication policy (http://schemas.openid.net/pape/policies/2007/06/multi-factor) and both policies MAY BE specified in conjunction without conflict. An example would be authentication using a password and a hardware token.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "MultiFactor")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi")]
+ public const string PhysicalMultiFactor = "http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical";
+ }
+}
diff --git a/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/Constants.cs b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/Constants.cs new file mode 100644 index 0000000..395ea36 --- /dev/null +++ b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/Constants.cs @@ -0,0 +1,90 @@ +using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy {
+ /// <summary>
+ /// OpenID Provider Authentication Policy extension constants.
+ /// </summary>
+ static class Constants {
+ /// <summary>
+ /// The namespace used by this extension in messages.
+ /// </summary>
+ internal const string TypeUri = "http://specs.openid.net/extensions/pape/1.0";
+ /// <summary>
+ /// The namespace alias to use for OpenID 1.x interop, where aliases are not defined in the message.
+ /// </summary>
+ internal const string pape_compatibility_alias = "pape";
+ /// <summary>
+ /// The string to prepend on an Auth Level Type alias definition.
+ /// </summary>
+ internal const string AuthLevelNamespaceDeclarationPrefix = "auth_level.ns.";
+
+ internal static class AuthenticationLevels {
+ internal static readonly IDictionary<string, string> PreferredTypeUriToAliasMap = new Dictionary<string, string> {
+ { NistTypeUri, nist_compatibility_alias },
+ };
+
+ internal const string nist_compatibility_alias = "nist";
+ internal const string NistTypeUri = "http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdf";
+ }
+
+ /// <summary>
+ /// Parameters to be included with PAPE requests.
+ /// </summary>
+ internal static class RequestParameters {
+ /// <summary>
+ /// Optional. If the End User has not actively authenticated to the OP within the number of seconds specified in a manner fitting the requested policies, the OP SHOULD authenticate the End User for this request.
+ /// </summary>
+ /// <value>Integer value greater than or equal to zero in seconds.</value>
+ /// <remarks>
+ /// The OP should realize that not adhering to the request for re-authentication most likely means that the End User will not be allowed access to the services provided by the RP. If this parameter is absent in the request, the OP should authenticate the user at its own discretion.
+ /// </remarks>
+ internal const string MaxAuthAge = "max_auth_age";
+ /// <summary>
+ /// Zero or more authentication policy URIs that the OP SHOULD conform to when authenticating the user. If multiple policies are requested, the OP SHOULD satisfy as many as it can.
+ /// </summary>
+ /// <value>Space separated list of authentication policy URIs.</value>
+ /// <remarks>
+ /// If no policies are requested, the RP may be interested in other information such as the authentication age.
+ /// </remarks>
+ internal const string PreferredAuthPolicies = "preferred_auth_policies";
+ /// <summary>
+ /// The space separated list of the name spaces of the custom Assurance Level that RP requests, in the order of its preference.
+ /// </summary>
+ internal const string PreferredAuthLevelTypes = "preferred_auth_level_types";
+ }
+ /// <summary>
+ /// Parameters to be included with PAPE responses.
+ /// </summary>
+ internal static class ResponseParameters {
+ /// <summary>
+ /// One or more authentication policy URIs that the OP conformed to when authenticating the End User.
+ /// </summary>
+ /// <value>Space separated list of authentication policy URIs.</value>
+ /// <remarks>
+ /// 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".
+ /// </remarks>
+ internal const string AuthPolicies = "auth_policies";
+ /// <summary>
+ /// Optional. The most recent timestamp when the End User has actively authenticated to the OP in a manner fitting the asserted policies.
+ /// </summary>
+ /// <value>
+ /// The timestamp MUST be formatted as specified in section 5.6 of [RFC3339] (Klyne, G. and C. Newman, “Date and Time on the Internet: Timestamps,” .), with the following restrictions:
+ /// * All times must be in the UTC timezone, indicated with a "Z".
+ /// * No fractional seconds are allowed
+ /// For example: 2005-05-15T17:11:51Z
+ /// </value>
+ /// <remarks>
+ /// 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.
+ /// </remarks>
+ internal const string AuthTime = "auth_time";
+ /// <summary>
+ /// The first part of a parameter name that gives the custom string value for
+ /// the assurance level. The second part of the parameter name is the alias for
+ /// that assurance level.
+ /// </summary>
+ internal const string AuthLevelAliasPrefix = "auth_level.";
+ }
+ }
+}
diff --git a/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/NistAssuranceLevel.cs b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/NistAssuranceLevel.cs new file mode 100644 index 0000000..6358294 --- /dev/null +++ b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/NistAssuranceLevel.cs @@ -0,0 +1,48 @@ +using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy {
+ /// <summary>
+ /// Descriptions for NIST-defined levels of assurance that a credential
+ /// has not been compromised and therefore the extent to which an
+ /// authentication assertion can be trusted.
+ /// </summary>
+ /// <remarks>
+ /// One using this enum should review the following publication for details
+ /// before asserting or interpreting what these levels signify, notwithstanding
+ /// the brief summaries attached to each level in DotNetOpenId documentation.
+ /// http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdf
+ ///
+ /// See PAPE spec Appendix A.1.2 (NIST Assurance Levels) for high-level example classifications of authentication methods within the defined levels.
+ /// </remarks>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Nist")]
+ public enum NistAssuranceLevel {
+ /// <summary>
+ /// Not an assurance level defined by NIST, but rather SHOULD be used to
+ /// signify that the OP recognizes the parameter and the End User
+ /// authentication did not meet the requirements of Level 1.
+ /// </summary>
+ InsufficientForLevel1 = 0,
+ /// <summary>
+ /// See this document for a thorough description:
+ /// http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdf
+ /// </summary>
+ Level1 = 1,
+ /// <summary>
+ /// See this document for a thorough description:
+ /// http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdf
+ /// </summary>
+ Level2 = 2,
+ /// <summary>
+ /// See this document for a thorough description:
+ /// http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdf
+ /// </summary>
+ Level3 = 3,
+ /// <summary>
+ /// See this document for a thorough description:
+ /// http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdf
+ /// </summary>
+ Level4 = 4,
+ }
+}
diff --git a/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/PolicyRequest.cs b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/PolicyRequest.cs new file mode 100644 index 0000000..eb78e1f --- /dev/null +++ b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/PolicyRequest.cs @@ -0,0 +1,195 @@ +using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Globalization;
+using System.Diagnostics;
+
+namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy {
+ /// <summary>
+ /// The PAPE request part of an OpenID Authentication request message.
+ /// </summary>
+ public sealed class PolicyRequest : IExtensionRequest {
+ /// <summary>
+ /// Instantiates a new <see cref="PolicyRequest"/>.
+ /// </summary>
+ public PolicyRequest() {
+ PreferredPolicies = new List<string>(1);
+ PreferredAuthLevelTypes = new List<string>(1);
+ }
+
+ /// <summary>
+ /// Optional. If the End User has not actively authenticated to the OP within the number of seconds specified in a manner fitting the requested policies, the OP SHOULD authenticate the End User for this request.
+ /// </summary>
+ /// <remarks>
+ /// The OP should realize that not adhering to the request for re-authentication most likely means that the End User will not be allowed access to the services provided by the RP. If this parameter is absent in the request, the OP should authenticate the user at its own discretion.
+ /// </remarks>
+ public TimeSpan? MaximumAuthenticationAge { get; set; }
+ /// <summary>
+ /// Zero or more authentication policy URIs that the OP SHOULD conform to when authenticating the user. If multiple policies are requested, the OP SHOULD satisfy as many as it can.
+ /// </summary>
+ /// <value>List of authentication policy URIs obtainable from the <see cref="AuthenticationPolicies"/> class or from a custom list.</value>
+ /// <remarks>
+ /// If no policies are requested, the RP may be interested in other information such as the authentication age.
+ /// </remarks>
+ public IList<string> PreferredPolicies { get; private set; }
+
+ /// <summary>
+ /// Zero or more name spaces of the custom Assurance Level the RP requests, in the order of its preference.
+ /// </summary>
+ public IList<string> PreferredAuthLevelTypes { get; private set; }
+
+ /// <summary>
+ /// Tests equality between two <see cref="PolicyRequest"/> instances.
+ /// </summary>
+ public override bool Equals(object obj) {
+ PolicyRequest other = obj as PolicyRequest;
+ if (other == null) return false;
+ if (MaximumAuthenticationAge != other.MaximumAuthenticationAge) return false;
+ if (PreferredPolicies.Count != other.PreferredPolicies.Count) return false;
+ foreach(string policy in PreferredPolicies) {
+ if (!other.PreferredPolicies.Contains(policy)) return false;
+ }
+ if (PreferredAuthLevelTypes.Count != other.PreferredAuthLevelTypes.Count) return false;
+ foreach (string authLevel in PreferredAuthLevelTypes) {
+ if (!other.PreferredAuthLevelTypes.Contains(authLevel)) return false;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Gets a hash code for this object.
+ /// </summary>
+ public override int GetHashCode() {
+ return PreferredPolicies.GetHashCode();
+ }
+
+ #region IExtensionRequest Members
+
+ IDictionary<string, string> IExtensionRequest.Serialize(DotNetOpenId.RelyingParty.IAuthenticationRequest authenticationRequest) {
+ var fields = new Dictionary<string, string>();
+
+ if (MaximumAuthenticationAge.HasValue) {
+ fields.Add(Constants.RequestParameters.MaxAuthAge,
+ MaximumAuthenticationAge.Value.TotalSeconds.ToString(CultureInfo.InvariantCulture));
+ }
+
+ // Even if empty, this parameter is required as part of the request message.
+ fields.Add(Constants.RequestParameters.PreferredAuthPolicies, SerializePolicies(PreferredPolicies));
+
+ if (PreferredAuthLevelTypes.Count > 0) {
+ AliasManager authLevelAliases = new AliasManager();
+ authLevelAliases.AssignAliases(PreferredAuthLevelTypes, Constants.AuthenticationLevels.PreferredTypeUriToAliasMap);
+
+ // Add a definition for each Auth Level Type alias.
+ foreach (string alias in authLevelAliases.Aliases) {
+ fields.Add(Constants.AuthLevelNamespaceDeclarationPrefix + alias, authLevelAliases.ResolveAlias(alias));
+ }
+
+ // Now use the aliases for those type URIs to list a preferred order.
+ fields.Add(Constants.RequestParameters.PreferredAuthLevelTypes, SerializeAuthLevels(PreferredAuthLevelTypes, authLevelAliases));
+ }
+
+ return fields;
+ }
+
+ bool IExtensionRequest.Deserialize(IDictionary<string, string> fields, DotNetOpenId.Provider.IRequest request, string typeUri) {
+ if (fields == null) return false;
+ if (!fields.ContainsKey(Constants.RequestParameters.PreferredAuthPolicies)) return false;
+
+ string maxAuthAge;
+ MaximumAuthenticationAge = fields.TryGetValue(Constants.RequestParameters.MaxAuthAge, out maxAuthAge) ?
+ TimeSpan.FromSeconds(double.Parse(maxAuthAge, CultureInfo.InvariantCulture)) : (TimeSpan?)null;
+
+ PreferredPolicies.Clear();
+ string[] preferredPolicies = fields[Constants.RequestParameters.PreferredAuthPolicies].Split(' ');
+ foreach (string policy in preferredPolicies) {
+ if (policy.Length > 0)
+ PreferredPolicies.Add(policy);
+ }
+
+ PreferredAuthLevelTypes.Clear();
+ AliasManager authLevelAliases = FindIncomingAliases(fields);
+ string preferredAuthLevelAliases;
+ if (fields.TryGetValue(Constants.RequestParameters.PreferredAuthLevelTypes, out preferredAuthLevelAliases)) {
+ foreach (string authLevelAlias in preferredAuthLevelAliases.Split(' ')) {
+ if (authLevelAlias.Length == 0) continue;
+ PreferredAuthLevelTypes.Add(authLevelAliases.ResolveAlias(authLevelAlias));
+ }
+ }
+
+ return true;
+ }
+
+ #endregion
+
+ #region IExtension Members
+
+ string IExtension.TypeUri {
+ get { return Constants.TypeUri; }
+ }
+
+ IEnumerable<string> IExtension.AdditionalSupportedTypeUris {
+ get { return new string[0]; }
+ }
+
+ #endregion
+
+ static internal string SerializePolicies(IList<string> policies) {
+ return ConcatenateListOfElements(policies);
+ }
+
+ private static string SerializeAuthLevels(IList<string> preferredAuthLevelTypes, AliasManager aliases) {
+ var aliasList = new List<string>();
+ foreach (string typeUri in preferredAuthLevelTypes) {
+ aliasList.Add(aliases.GetAlias(typeUri));
+ }
+
+ return ConcatenateListOfElements(aliasList);
+ }
+
+ /// <summary>
+ /// Looks at the incoming fields and figures out what the aliases and name spaces for auth level types are.
+ /// </summary>
+ internal static AliasManager FindIncomingAliases(IDictionary<string, string> fields) {
+ AliasManager aliasManager = new AliasManager();
+
+ foreach (var pair in fields) {
+ if (!pair.Key.StartsWith(Constants.AuthLevelNamespaceDeclarationPrefix, StringComparison.Ordinal)) {
+ continue;
+ }
+
+ string alias = pair.Key.Substring(Constants.AuthLevelNamespaceDeclarationPrefix.Length);
+ aliasManager.SetAlias(alias, pair.Value);
+ }
+
+ aliasManager.SetPreferredAliasesWhereNotSet(Constants.AuthenticationLevels.PreferredTypeUriToAliasMap);
+
+ return aliasManager;
+ }
+
+ internal static string ConcatenateListOfElements(IList<string> values) {
+ Debug.Assert(values != null);
+ StringBuilder valuesList = new StringBuilder();
+ foreach (string value in GetUniqueItems(values)) {
+ if (value.Contains(" ")) {
+ throw new FormatException(string.Format(CultureInfo.CurrentCulture,
+ Strings.InvalidUri, value));
+ }
+ valuesList.Append(value);
+ valuesList.Append(" ");
+ }
+ if (valuesList.Length > 0)
+ valuesList.Length -= 1; // remove trailing space
+ return valuesList.ToString();
+ }
+
+ static internal IEnumerable<T> GetUniqueItems<T>(IList<T> list) {
+ List<T> itemsSeen = new List<T>(list.Count);
+ foreach (T item in list) {
+ if (itemsSeen.Contains(item)) continue;
+ itemsSeen.Add(item);
+ yield return item;
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs new file mode 100644 index 0000000..0f5922f --- /dev/null +++ b/src/DotNetOpenId/Extensions/ProviderAuthenticationPolicy/PolicyResponse.cs @@ -0,0 +1,194 @@ +using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Diagnostics;
+using System.Globalization;
+
+namespace DotNetOpenId.Extensions.ProviderAuthenticationPolicy {
+ /// <summary>
+ /// The PAPE response part of an OpenID Authentication response message.
+ /// </summary>
+ public sealed class PolicyResponse : IExtensionResponse {
+ // This array of formats is not yet a complete list.
+ static readonly string[] PermissibleDateTimeFormats = { "yyyy-MM-ddTHH:mm:ssZ" };
+
+ /// <summary>
+ /// Instantiates a <see cref="PolicyResponse"/>.
+ /// </summary>
+ public PolicyResponse() {
+ ActualPolicies = new List<string>(1);
+ AssuranceLevels = new Dictionary<string, string>(1);
+ }
+
+ /// <summary>
+ /// One or more authentication policy URIs that the OP conformed to when authenticating the End User.
+ /// </summary>
+ /// <remarks>
+ /// 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".
+ /// </remarks>
+ public IList<string> ActualPolicies { get; private set; }
+ DateTime? authenticationTimeUtc;
+ /// <summary>
+ /// Optional. The most recent timestamp when the End User has actively authenticated to the OP in a manner fitting the asserted policies.
+ /// </summary>
+ /// <remarks>
+ /// 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.
+ /// </remarks>
+ public DateTime? AuthenticationTimeUtc {
+ get { return authenticationTimeUtc; }
+ set {
+ // Make sure that whatever is set here, it becomes UTC time.
+ if (value.HasValue) {
+ if (value.Value.Kind == DateTimeKind.Unspecified)
+ throw new ArgumentException(Strings.UnspecifiedDateTimeKindNotAllowed, "value");
+ authenticationTimeUtc = value.Value.ToUniversalTime();
+ } else {
+ authenticationTimeUtc = null;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Optional. The Assurance Level as defined by the National Institute of Standards and Technology (NIST) in Special Publication 800-63 (Burr, W., Dodson, D., and W. Polk, Ed., “Electronic Authentication Guideline,” April 2006.) [NIST_SP800‑63] corresponding to the authentication method and policies employed by the OP when authenticating the End User.
/// </summary>
+ /// <remarks>
/// See PAPE spec Appendix A.1.2 (NIST Assurance Levels) for high-level example classifications of authentication methods within the defined levels.
/// </remarks>
+ public NistAssuranceLevel? NistAssuranceLevel {
+ get {
+ string levelString;
+ if (AssuranceLevels.TryGetValue(Constants.AuthenticationLevels.NistTypeUri, out levelString)) {
+ return (NistAssuranceLevel)Enum.Parse(typeof(NistAssuranceLevel), levelString);
+ } else {
+ return null;
+ }
+ }
+ set {
+ if (value != null) {
+ AssuranceLevels[Constants.AuthenticationLevels.NistTypeUri] = ((int)value).ToString(CultureInfo.InvariantCulture);
+ } else {
+ AssuranceLevels.Remove(Constants.AuthenticationLevels.NistTypeUri);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets a dictionary where keys are the authentication level type URIs and
+ /// the values are the per authentication level defined custom value.
+ /// </summary>
+ /// <remarks>
+ /// A very common key is <see cref="Constants.AuthenticationLevels.NistTypeUri"/>
+ /// and values for this key are available in <see cref="NistAssuranceLevel"/>.
+ /// </remarks>
+ public IDictionary<string, string> AssuranceLevels { get; private set; }
+
+ /// <summary>
+ /// Tests equality between two <see cref="PolicyResponse"/> instances.
+ /// </summary>
+ public override bool Equals(object obj) {
+ PolicyResponse other = obj as PolicyResponse;
+ if (other == null) return false;
+ if (AuthenticationTimeUtc != other.AuthenticationTimeUtc) return false;
+ if (AssuranceLevels.Count != other.AssuranceLevels.Count) return false;
+ foreach (var pair in AssuranceLevels) {
+ if (!other.AssuranceLevels.Contains(pair)) return false;
+ }
+ if (ActualPolicies.Count != other.ActualPolicies.Count) return false;
+ foreach (string policy in ActualPolicies) {
+ if (!other.ActualPolicies.Contains(policy)) return false;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Gets a hash code for this object.
+ /// </summary>
+ public override int GetHashCode() {
+ return ActualPolicies.GetHashCode();
+ }
+
+ #region IExtensionResponse Members
+
+ IDictionary<string, string> IExtensionResponse.Serialize(DotNetOpenId.Provider.IRequest authenticationRequest) {
+ var fields = new Dictionary<string, string>();
+
+ fields.Add(Constants.ResponseParameters.AuthPolicies, SerializePolicies(ActualPolicies));
+ if (AuthenticationTimeUtc.HasValue) {
+ fields.Add(Constants.ResponseParameters.AuthTime, AuthenticationTimeUtc.Value.ToUniversalTime().ToString(PermissibleDateTimeFormats[0], CultureInfo.InvariantCulture));
+ }
+
+ if (AssuranceLevels.Count > 0) {
+ AliasManager aliases = new AliasManager();
+ aliases.AssignAliases(AssuranceLevels.Keys, Constants.AuthenticationLevels.PreferredTypeUriToAliasMap);
+
+ // Add a definition for each Auth Level Type alias.
+ foreach (string alias in aliases.Aliases) {
+ fields.Add(Constants.AuthLevelNamespaceDeclarationPrefix + alias, aliases.ResolveAlias(alias));
+ }
+
+ // Now use the aliases for those type URIs to list the individual values.
+ foreach (var pair in AssuranceLevels) {
+ fields.Add(Constants.ResponseParameters.AuthLevelAliasPrefix + aliases.GetAlias(pair.Key), pair.Value);
+ }
+ }
+
+ return fields;
+ }
+
+ bool IExtensionResponse.Deserialize(IDictionary<string, string> fields, DotNetOpenId.RelyingParty.IAuthenticationResponse response, string typeUri) {
+ if (fields == null) return false;
+ if (!fields.ContainsKey(Constants.ResponseParameters.AuthPolicies)) return false;
+
+ ActualPolicies.Clear();
+ string[] actualPolicies = fields[Constants.ResponseParameters.AuthPolicies].Split(' ');
+ foreach (string policy in actualPolicies) {
+ if (policy.Length > 0 && policy != AuthenticationPolicies.None)
+ ActualPolicies.Add(policy);
+ }
+
+ AuthenticationTimeUtc = null;
+ string authTime;
+ if (fields.TryGetValue(Constants.ResponseParameters.AuthTime, out authTime)) {
+ DateTime authDateTime;
+ if (DateTime.TryParse(authTime, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out authDateTime) &&
+ authDateTime.Kind == DateTimeKind.Utc) { // may be unspecified per our option above
+ AuthenticationTimeUtc = authDateTime;
+ } else {
+ Logger.ErrorFormat("Invalid format for {0} parameter: {1}",
+ Constants.ResponseParameters.AuthTime, authTime);
+ }
+ }
+
+ AssuranceLevels.Clear();
+ AliasManager authLevelAliases = PolicyRequest.FindIncomingAliases(fields);
+ foreach (string authLevelAlias in authLevelAliases.Aliases) {
+ string authValue;
+ if (fields.TryGetValue(Constants.ResponseParameters.AuthLevelAliasPrefix + authLevelAlias, out authValue)) {
+ string authLevelType = authLevelAliases.ResolveAlias(authLevelAlias);
+ AssuranceLevels[authLevelType] = authValue;
+ }
+ }
+
+ return true;
+ }
+
+ #endregion
+
+ #region IExtension Members
+
+ string IExtension.TypeUri {
+ get { return Constants.TypeUri; }
+ }
+
+ IEnumerable<string> IExtension.AdditionalSupportedTypeUris {
+ get { return new string[0]; }
+ }
+
+ #endregion
+
+ static internal string SerializePolicies(IList<string> policies) {
+ if (policies.Count == 0) {
+ return AuthenticationPolicies.None;
+ } else {
+ return PolicyRequest.ConcatenateListOfElements(policies);
+ }
+ }
+ }
+}
|