FxCop fixes.
[dotnetoauth.git] / src / DotNetOpenAuth / OpenId / Extensions / ProviderAuthenticationPolicy / PolicyResponse.cs
blob727e663d93b9f4732b82e3da7b7402dd671002ad
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.Diagnostics.CodeAnalysis;
11 using System.Globalization;
12 using DotNetOpenAuth.Messaging;
13 using DotNetOpenAuth.OpenId.Messages;
15 /// <summary>
16 /// The PAPE response part of an OpenID Authentication response message.
17 /// </summary>
18 public sealed class PolicyResponse : ExtensionBase, IMessageWithEvents {
19 /// <summary>
20 /// The factory method that may be used in deserialization of this message.
21 /// </summary>
22 internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => {
23 if (typeUri == Constants.TypeUri && baseMessage is IndirectSignedResponse) {
24 return new PolicyResponse();
27 return null;
30 /// <summary>
31 /// The first part of a parameter name that gives the custom string value for
32 /// the assurance level. The second part of the parameter name is the alias for
33 /// that assurance level.
34 /// </summary>
35 private const string AuthLevelAliasPrefix = "auth_level.";
37 /// <summary>
38 /// One or more authentication policy URIs that the OP conformed to when authenticating the End User.
39 /// </summary>
40 /// <value>Space separated list of authentication policy URIs.</value>
41 /// <remarks>
42 /// 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".
43 /// </remarks>
44 [MessagePart("auth_policies", IsRequired = true)]
45 private string actualPoliciesString;
47 /// <summary>
48 /// Backing field for the <see cref="AuthenticationTimeUtc"/> property.
49 /// </summary>
50 private DateTime? authenticationTimeUtc;
52 /// <summary>
53 /// Initializes a new instance of the <see cref="PolicyResponse"/> class.
54 /// </summary>
55 public PolicyResponse()
56 : base(new Version(1, 0), Constants.TypeUri, null) {
57 this.ActualPolicies = new List<string>(1);
58 this.AssuranceLevels = new Dictionary<string, string>(1);
61 /// <summary>
62 /// Gets a list of authentication policy URIs that the
63 /// OP conformed to when authenticating the End User.
64 /// </summary>
65 public IList<string> ActualPolicies { get; private set; }
67 /// <summary>
68 /// Gets or sets the most recent timestamp when the End User has
69 /// actively authenticated to the OP in a manner fitting the asserted policies.
70 /// </summary>
71 /// <remarks>
72 /// If the RP's request included the "openid.max_auth_age" parameter
73 /// then the OP MUST include "openid.auth_time" in its response.
74 /// If "openid.max_auth_age" was not requested, the OP MAY choose to include
75 /// "openid.auth_time" in its response.
76 /// </remarks>
77 [MessagePart("auth_time", Encoder = typeof(DateTimeEncoder))]
78 public DateTime? AuthenticationTimeUtc {
79 get {
80 return this.authenticationTimeUtc;
83 set {
84 // Make sure that whatever is set here, it becomes UTC time.
85 if (value.HasValue) {
86 if (value.Value.Kind == DateTimeKind.Unspecified) {
87 throw new ArgumentException(OpenIdStrings.UnspecifiedDateTimeKindNotAllowed, "value");
90 // Convert to UTC and cut to the second, since the protocol only allows for
91 // that level of precision.
92 this.authenticationTimeUtc = OpenIdUtilities.CutToSecond(value.Value.ToUniversalTime());
93 } else {
94 this.authenticationTimeUtc = null;
99 /// <summary>
100 /// Gets or sets the Assurance Level as defined by the National
101 /// Institute of Standards and Technology (NIST) in Special Publication
102 /// 800-63 (Burr, W., Dodson, D., and W. Polk, Ed., “Electronic
103 /// Authentication Guideline,” April 2006.) [NIST_SP800‑63] corresponding
104 /// to the authentication method and policies employed by the OP when
105 /// authenticating the End User.
106 /// </summary>
107 /// <remarks>
108 /// See PAPE spec Appendix A.1.2 (NIST Assurance Levels) for high-level
109 /// example classifications of authentication methods within the defined
110 /// levels.
111 /// </remarks>
112 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Nist", Justification = "Acronym")]
113 public NistAssuranceLevel? NistAssuranceLevel {
114 get {
115 string levelString;
116 if (this.AssuranceLevels.TryGetValue(Constants.AssuranceLevels.NistTypeUri, out levelString)) {
117 return (NistAssuranceLevel)Enum.Parse(typeof(NistAssuranceLevel), levelString);
118 } else {
119 return null;
123 set {
124 if (value != null) {
125 this.AssuranceLevels[Constants.AssuranceLevels.NistTypeUri] = ((int)value).ToString(CultureInfo.InvariantCulture);
126 } else {
127 this.AssuranceLevels.Remove(Constants.AssuranceLevels.NistTypeUri);
132 /// <summary>
133 /// Gets a dictionary where keys are the authentication level type URIs and
134 /// the values are the per authentication level defined custom value.
135 /// </summary>
136 /// <remarks>
137 /// A very common key is <see cref="Constants.AssuranceLevels.NistTypeUri"/>
138 /// and values for this key are available in <see cref="NistAssuranceLevel"/>.
139 /// </remarks>
140 public IDictionary<string, string> AssuranceLevels { get; private set; }
142 #region IMessageWithEvents Members
144 /// <summary>
145 /// Called when the message is about to be transmitted,
146 /// before it passes through the channel binding elements.
147 /// </summary>
148 void IMessageWithEvents.OnSending() {
149 var extraData = ((IMessage)this).ExtraData;
150 extraData.Clear();
152 this.actualPoliciesString = SerializePolicies(this.ActualPolicies);
154 if (this.AssuranceLevels.Count > 0) {
155 AliasManager aliases = new AliasManager();
156 aliases.AssignAliases(this.AssuranceLevels.Keys, Constants.AssuranceLevels.PreferredTypeUriToAliasMap);
158 // Add a definition for each Auth Level Type alias.
159 foreach (string alias in aliases.Aliases) {
160 extraData.Add(Constants.AuthLevelNamespaceDeclarationPrefix + alias, aliases.ResolveAlias(alias));
163 // Now use the aliases for those type URIs to list the individual values.
164 foreach (var pair in this.AssuranceLevels) {
165 extraData.Add(AuthLevelAliasPrefix + aliases.GetAlias(pair.Key), pair.Value);
170 /// <summary>
171 /// Called when the message has been received,
172 /// after it passes through the channel binding elements.
173 /// </summary>
174 void IMessageWithEvents.OnReceiving() {
175 var extraData = ((IMessage)this).ExtraData;
177 this.ActualPolicies.Clear();
178 string[] actualPolicies = this.actualPoliciesString.Split(' ');
179 foreach (string policy in actualPolicies) {
180 if (policy.Length > 0 && policy != AuthenticationPolicies.None) {
181 this.ActualPolicies.Add(policy);
185 this.AssuranceLevels.Clear();
186 AliasManager authLevelAliases = PapeUtilities.FindIncomingAliases(extraData);
187 foreach (string authLevelAlias in authLevelAliases.Aliases) {
188 string authValue;
189 if (extraData.TryGetValue(AuthLevelAliasPrefix + authLevelAlias, out authValue)) {
190 string authLevelType = authLevelAliases.ResolveAlias(authLevelAlias);
191 this.AssuranceLevels[authLevelType] = authValue;
196 #endregion
198 /// <summary>
199 /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
200 /// </summary>
201 /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
202 /// <returns>
203 /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
204 /// </returns>
205 /// <exception cref="T:System.NullReferenceException">
206 /// The <paramref name="obj"/> parameter is null.
207 /// </exception>
208 public override bool Equals(object obj) {
209 PolicyResponse other = obj as PolicyResponse;
210 if (other == null) {
211 return false;
214 if (this.AuthenticationTimeUtc != other.AuthenticationTimeUtc) {
215 return false;
218 if (this.AssuranceLevels.Count != other.AssuranceLevels.Count) {
219 return false;
222 foreach (var pair in this.AssuranceLevels) {
223 if (!other.AssuranceLevels.Contains(pair)) {
224 return false;
228 if (this.ActualPolicies.Count != other.ActualPolicies.Count) {
229 return false;
232 foreach (string policy in this.ActualPolicies) {
233 if (!other.ActualPolicies.Contains(policy)) {
234 return false;
238 return true;
241 /// <summary>
242 /// Serves as a hash function for a particular type.
243 /// </summary>
244 /// <returns>
245 /// A hash code for the current <see cref="T:System.Object"/>.
246 /// </returns>
247 public override int GetHashCode() {
248 // TODO: fix this to match Equals
249 return this.ActualPolicies.GetHashCode();
252 /// <summary>
253 /// Serializes the applied policies for transmission from the Provider
254 /// to the Relying Party.
255 /// </summary>
256 /// <param name="policies">The applied policies.</param>
257 /// <returns>A space-delimited list of applied policies.</returns>
258 private static string SerializePolicies(IList<string> policies) {
259 if (policies.Count == 0) {
260 return AuthenticationPolicies.None;
261 } else {
262 return PapeUtilities.ConcatenateListOfElements(policies);