1
//-----------------------------------------------------------------------
2 // <copyright file="PolicyResponse.cs" company="Andrew Arnott">
3 // Copyright (c) Andrew Arnott. All rights reserved.
5 //-----------------------------------------------------------------------
7 namespace DotNetOpenAuth
.OpenId
.Extensions
.ProviderAuthenticationPolicy
{
9 using System
.Collections
.Generic
;
10 using System
.Diagnostics
.CodeAnalysis
;
11 using System
.Globalization
;
12 using DotNetOpenAuth
.Messaging
;
13 using DotNetOpenAuth
.OpenId
.Messages
;
16 /// The PAPE response part of an OpenID Authentication response message.
18 public sealed class PolicyResponse
: ExtensionBase
, IMessageWithEvents
{
20 /// The factory method that may be used in deserialization of this message.
22 internal static readonly OpenIdExtensionFactory
.CreateDelegate Factory
= (typeUri
, data
, baseMessage
) => {
23 if (typeUri
== Constants
.TypeUri
&& baseMessage
is IndirectSignedResponse
) {
24 return new PolicyResponse();
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.
35 private const string AuthLevelAliasPrefix
= "auth_level.";
38 /// One or more authentication policy URIs that the OP conformed to when authenticating the End User.
40 /// <value>Space separated list of authentication policy URIs.</value>
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".
44 [MessagePart("auth_policies", IsRequired
= true)]
45 private string actualPoliciesString
;
48 /// Backing field for the <see cref="AuthenticationTimeUtc"/> property.
50 private DateTime
? authenticationTimeUtc
;
53 /// Initializes a new instance of the <see cref="PolicyResponse"/> class.
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);
62 /// Gets a list of authentication policy URIs that the
63 /// OP conformed to when authenticating the End User.
65 public IList
<string> ActualPolicies { get; private set; }
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.
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.
77 [MessagePart("auth_time", Encoder
= typeof(DateTimeEncoder
))]
78 public DateTime
? AuthenticationTimeUtc
{
80 return this.authenticationTimeUtc
;
84 // Make sure that whatever is set here, it becomes UTC time.
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());
94 this.authenticationTimeUtc
= null;
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.
108 /// See PAPE spec Appendix A.1.2 (NIST Assurance Levels) for high-level
109 /// example classifications of authentication methods within the defined
112 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId
= "Nist", Justification
= "Acronym")]
113 public NistAssuranceLevel
? NistAssuranceLevel
{
116 if (this.AssuranceLevels
.TryGetValue(Constants
.AssuranceLevels
.NistTypeUri
, out levelString
)) {
117 return (NistAssuranceLevel
)Enum
.Parse(typeof(NistAssuranceLevel
), levelString
);
125 this.AssuranceLevels
[Constants
.AssuranceLevels
.NistTypeUri
] = ((int)value).ToString(CultureInfo
.InvariantCulture
);
127 this.AssuranceLevels
.Remove(Constants
.AssuranceLevels
.NistTypeUri
);
133 /// Gets a dictionary where keys are the authentication level type URIs and
134 /// the values are the per authentication level defined custom value.
137 /// A very common key is <see cref="Constants.AssuranceLevels.NistTypeUri"/>
138 /// and values for this key are available in <see cref="NistAssuranceLevel"/>.
140 public IDictionary
<string, string> AssuranceLevels { get; private set; }
142 #region IMessageWithEvents Members
145 /// Called when the message is about to be transmitted,
146 /// before it passes through the channel binding elements.
148 void IMessageWithEvents
.OnSending() {
149 var extraData
= ((IMessage
)this).ExtraData
;
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
);
171 /// Called when the message has been received,
172 /// after it passes through the channel binding elements.
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
) {
189 if (extraData
.TryGetValue(AuthLevelAliasPrefix
+ authLevelAlias
, out authValue
)) {
190 string authLevelType
= authLevelAliases
.ResolveAlias(authLevelAlias
);
191 this.AssuranceLevels
[authLevelType
] = authValue
;
199 /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
201 /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
203 /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
205 /// <exception cref="T:System.NullReferenceException">
206 /// The <paramref name="obj"/> parameter is null.
208 public override bool Equals(object obj
) {
209 PolicyResponse other
= obj
as PolicyResponse
;
214 if (this.AuthenticationTimeUtc
!= other
.AuthenticationTimeUtc
) {
218 if (this.AssuranceLevels
.Count
!= other
.AssuranceLevels
.Count
) {
222 foreach (var pair
in this.AssuranceLevels
) {
223 if (!other
.AssuranceLevels
.Contains(pair
)) {
228 if (this.ActualPolicies
.Count
!= other
.ActualPolicies
.Count
) {
232 foreach (string policy
in this.ActualPolicies
) {
233 if (!other
.ActualPolicies
.Contains(policy
)) {
242 /// Serves as a hash function for a particular type.
245 /// A hash code for the current <see cref="T:System.Object"/>.
247 public override int GetHashCode() {
248 // TODO: fix this to match Equals
249 return this.ActualPolicies
.GetHashCode();
253 /// Serializes the applied policies for transmission from the Provider
254 /// to the Relying Party.
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
;
262 return PapeUtilities
.ConcatenateListOfElements(policies
);