Hooked up PAPE extension factory methods.
[dotnetoauth.git] / src / DotNetOpenAuth / OpenId / Extensions / ProviderAuthenticationPolicy / PolicyResponse.cs
blobe4ff8a87f88c1022a114ec7d1aa8c22b60e42c95
1 //-----------------------------------------------------------------------
2 // <copyright file="PolicyResponse.cs" company="Andrew Arnott">
3 // Copyright (c) Andrew Arnott. All rights reserved.
4 // </copyright>
5 //-----------------------------------------------------------------------
7 namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy {
8 using System;
9 using System.Collections.Generic;
10 using System.Globalization;
11 using DotNetOpenAuth.Messaging;
12 using DotNetOpenAuth.OpenId.Messages;
14 /// <summary>
15 /// The PAPE response part of an OpenID Authentication response message.
16 /// </summary>
17 public sealed class PolicyResponse : ExtensionBase, IMessageWithEvents {
18 /// <summary>
19 /// The factory method that may be used in deserialization of this message.
20 /// </summary>
21 internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => {
22 if (typeUri == Constants.TypeUri && baseMessage is IndirectSignedResponse) {
23 return new PolicyResponse();
26 return null;
29 /// <summary>
30 /// The first part of a parameter name that gives the custom string value for
31 /// the assurance level. The second part of the parameter name is the alias for
32 /// that assurance level.
33 /// </summary>
34 private const string AuthLevelAliasPrefix = "auth_level.";
36 /// <summary>
37 /// One or more authentication policy URIs that the OP conformed to when authenticating the End User.
38 /// </summary>
39 /// <value>Space separated list of authentication policy URIs.</value>
40 /// <remarks>
41 /// 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".
42 /// </remarks>
43 [MessagePart("auth_policies", IsRequired = true)]
44 private string actualPoliciesString;
46 private DateTime? authenticationTimeUtc;
48 /// <summary>
49 /// Instantiates a <see cref="PolicyResponse"/>.
50 /// </summary>
51 public PolicyResponse()
52 : base(new Version(1, 0), Constants.TypeUri, null) {
53 ActualPolicies = new List<string>(1);
54 AssuranceLevels = new Dictionary<string, string>(1);
57 /// <summary>
58 /// One or more authentication policy URIs that the OP conformed to when authenticating the End User.
59 /// </summary>
60 /// <remarks>
61 /// 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".
62 /// </remarks>
63 public IList<string> ActualPolicies { get; private set; }
65 /// <summary>
66 /// Optional. The most recent timestamp when the End User has actively authenticated to the OP in a manner fitting the asserted policies.
67 /// </summary>
68 /// <remarks>
69 /// 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.
70 /// </remarks>
71 [MessagePart("auth_time", Encoder=typeof(DateTimeEncoder))]
72 public DateTime? AuthenticationTimeUtc {
73 get { return authenticationTimeUtc; }
74 set {
75 // Make sure that whatever is set here, it becomes UTC time.
76 if (value.HasValue) {
77 if (value.Value.Kind == DateTimeKind.Unspecified)
78 throw new ArgumentException(OpenIdStrings.UnspecifiedDateTimeKindNotAllowed, "value");
79 authenticationTimeUtc = value.Value.ToUniversalTime();
80 } else {
81 authenticationTimeUtc = null;
86 /// <summary>
87 /// 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.
88 /// </summary>
89 /// <remarks>
90 /// See PAPE spec Appendix A.1.2 (NIST Assurance Levels) for high-level example classifications of authentication methods within the defined levels.
91 /// </remarks>
92 public NistAssuranceLevel? NistAssuranceLevel {
93 get {
94 string levelString;
95 if (AssuranceLevels.TryGetValue(Constants.AuthenticationLevels.NistTypeUri, out levelString)) {
96 return (NistAssuranceLevel)Enum.Parse(typeof(NistAssuranceLevel), levelString);
97 } else {
98 return null;
101 set {
102 if (value != null) {
103 AssuranceLevels[Constants.AuthenticationLevels.NistTypeUri] = ((int)value).ToString(CultureInfo.InvariantCulture);
104 } else {
105 AssuranceLevels.Remove(Constants.AuthenticationLevels.NistTypeUri);
110 /// <summary>
111 /// Gets a dictionary where keys are the authentication level type URIs and
112 /// the values are the per authentication level defined custom value.
113 /// </summary>
114 /// <remarks>
115 /// A very common key is <see cref="Constants.AuthenticationLevels.NistTypeUri"/>
116 /// and values for this key are available in <see cref="NistAssuranceLevel"/>.
117 /// </remarks>
118 public IDictionary<string, string> AssuranceLevels { get; private set; }
120 /// <summary>
121 /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
122 /// </summary>
123 /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
124 /// <returns>
125 /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
126 /// </returns>
127 /// <exception cref="T:System.NullReferenceException">
128 /// The <paramref name="obj"/> parameter is null.
129 /// </exception>
130 public override bool Equals(object obj) {
131 PolicyResponse other = obj as PolicyResponse;
132 if (other == null) {
133 return false;
136 if (AuthenticationTimeUtc != other.AuthenticationTimeUtc) {
137 return false;
140 if (AssuranceLevels.Count != other.AssuranceLevels.Count) {
141 return false;
144 foreach (var pair in AssuranceLevels) {
145 if (!other.AssuranceLevels.Contains(pair)) {
146 return false;
150 if (ActualPolicies.Count != other.ActualPolicies.Count) {
151 return false;
154 foreach (string policy in ActualPolicies) {
155 if (!other.ActualPolicies.Contains(policy)) {
156 return false;
160 return true;
163 /// <summary>
164 /// Serves as a hash function for a particular type.
165 /// </summary>
166 /// <returns>
167 /// A hash code for the current <see cref="T:System.Object"/>.
168 /// </returns>
169 public override int GetHashCode() {
170 // TODO: fix this to match Equals
171 return ActualPolicies.GetHashCode();
174 private static string SerializePolicies(IList<string> policies) {
175 if (policies.Count == 0) {
176 return AuthenticationPolicies.None;
177 } else {
178 return PapeUtilities.ConcatenateListOfElements(policies);
182 #region IMessageWithEvents Members
184 /// <summary>
185 /// Called when the message is about to be transmitted,
186 /// before it passes through the channel binding elements.
187 /// </summary>
188 void IMessageWithEvents.OnSending() {
189 var extraData = ((IMessage)this).ExtraData;
190 extraData.Clear();
192 this.actualPoliciesString = SerializePolicies(this.ActualPolicies);
194 if (AssuranceLevels.Count > 0) {
195 AliasManager aliases = new AliasManager();
196 aliases.AssignAliases(AssuranceLevels.Keys, Constants.AuthenticationLevels.PreferredTypeUriToAliasMap);
198 // Add a definition for each Auth Level Type alias.
199 foreach (string alias in aliases.Aliases) {
200 extraData.Add(Constants.AuthLevelNamespaceDeclarationPrefix + alias, aliases.ResolveAlias(alias));
203 // Now use the aliases for those type URIs to list the individual values.
204 foreach (var pair in AssuranceLevels) {
205 extraData.Add(AuthLevelAliasPrefix + aliases.GetAlias(pair.Key), pair.Value);
210 /// <summary>
211 /// Called when the message has been received,
212 /// after it passes through the channel binding elements.
213 /// </summary>
214 void IMessageWithEvents.OnReceiving() {
215 var extraData = ((IMessage)this).ExtraData;
217 this.ActualPolicies.Clear();
218 string[] actualPolicies = actualPoliciesString.Split(' ');
219 foreach (string policy in actualPolicies) {
220 if (policy.Length > 0 && policy != AuthenticationPolicies.None) {
221 this.ActualPolicies.Add(policy);
225 this.AssuranceLevels.Clear();
226 AliasManager authLevelAliases = PapeUtilities.FindIncomingAliases(extraData);
227 foreach (string authLevelAlias in authLevelAliases.Aliases) {
228 string authValue;
229 if (extraData.TryGetValue(AuthLevelAliasPrefix + authLevelAlias, out authValue)) {
230 string authLevelType = authLevelAliases.ResolveAlias(authLevelAlias);
231 this.AssuranceLevels[authLevelType] = authValue;
236 #endregion