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
.Globalization
;
11 using DotNetOpenAuth
.Messaging
;
12 using DotNetOpenAuth
.OpenId
.Messages
;
15 /// The PAPE response part of an OpenID Authentication response message.
17 public sealed class PolicyResponse
: ExtensionBase
, IMessageWithEvents
{
19 /// The factory method that may be used in deserialization of this message.
21 internal static readonly OpenIdExtensionFactory
.CreateDelegate Factory
= (typeUri
, data
, baseMessage
) => {
22 if (typeUri
== Constants
.TypeUri
&& baseMessage
is IndirectSignedResponse
) {
23 return new PolicyResponse();
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.
34 private const string AuthLevelAliasPrefix
= "auth_level.";
37 /// One or more authentication policy URIs that the OP conformed to when authenticating the End User.
39 /// <value>Space separated list of authentication policy URIs.</value>
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".
43 [MessagePart("auth_policies", IsRequired
= true)]
44 private string actualPoliciesString
;
46 private DateTime
? authenticationTimeUtc
;
49 /// Instantiates a <see cref="PolicyResponse"/>.
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);
58 /// One or more authentication policy URIs that the OP conformed to when authenticating the End User.
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".
63 public IList
<string> ActualPolicies { get; private set; }
66 /// Optional. The most recent timestamp when the End User has actively authenticated to the OP in a manner fitting the asserted policies.
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.
71 [MessagePart("auth_time", Encoder
=typeof(DateTimeEncoder
))]
72 public DateTime
? AuthenticationTimeUtc
{
73 get { return authenticationTimeUtc; }
75 // Make sure that whatever is set here, it becomes UTC time.
77 if (value.Value
.Kind
== DateTimeKind
.Unspecified
)
78 throw new ArgumentException(OpenIdStrings
.UnspecifiedDateTimeKindNotAllowed
, "value");
79 authenticationTimeUtc
= value.Value
.ToUniversalTime();
81 authenticationTimeUtc
= null;
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.
90 /// See PAPE spec Appendix A.1.2 (NIST Assurance Levels) for high-level example classifications of authentication methods within the defined levels.
92 public NistAssuranceLevel
? NistAssuranceLevel
{
95 if (AssuranceLevels
.TryGetValue(Constants
.AuthenticationLevels
.NistTypeUri
, out levelString
)) {
96 return (NistAssuranceLevel
)Enum
.Parse(typeof(NistAssuranceLevel
), levelString
);
103 AssuranceLevels
[Constants
.AuthenticationLevels
.NistTypeUri
] = ((int)value).ToString(CultureInfo
.InvariantCulture
);
105 AssuranceLevels
.Remove(Constants
.AuthenticationLevels
.NistTypeUri
);
111 /// Gets a dictionary where keys are the authentication level type URIs and
112 /// the values are the per authentication level defined custom value.
115 /// A very common key is <see cref="Constants.AuthenticationLevels.NistTypeUri"/>
116 /// and values for this key are available in <see cref="NistAssuranceLevel"/>.
118 public IDictionary
<string, string> AssuranceLevels { get; private set; }
121 /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
123 /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
125 /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
127 /// <exception cref="T:System.NullReferenceException">
128 /// The <paramref name="obj"/> parameter is null.
130 public override bool Equals(object obj
) {
131 PolicyResponse other
= obj
as PolicyResponse
;
136 if (AuthenticationTimeUtc
!= other
.AuthenticationTimeUtc
) {
140 if (AssuranceLevels
.Count
!= other
.AssuranceLevels
.Count
) {
144 foreach (var pair
in AssuranceLevels
) {
145 if (!other
.AssuranceLevels
.Contains(pair
)) {
150 if (ActualPolicies
.Count
!= other
.ActualPolicies
.Count
) {
154 foreach (string policy
in ActualPolicies
) {
155 if (!other
.ActualPolicies
.Contains(policy
)) {
164 /// Serves as a hash function for a particular type.
167 /// A hash code for the current <see cref="T:System.Object"/>.
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
;
178 return PapeUtilities
.ConcatenateListOfElements(policies
);
182 #region IMessageWithEvents Members
185 /// Called when the message is about to be transmitted,
186 /// before it passes through the channel binding elements.
188 void IMessageWithEvents
.OnSending() {
189 var extraData
= ((IMessage
)this).ExtraData
;
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
);
211 /// Called when the message has been received,
212 /// after it passes through the channel binding elements.
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
) {
229 if (extraData
.TryGetValue(AuthLevelAliasPrefix
+ authLevelAlias
, out authValue
)) {
230 string authLevelType
= authLevelAliases
.ResolveAlias(authLevelAlias
);
231 this.AssuranceLevels
[authLevelType
] = authValue
;