1
//-----------------------------------------------------------------------
2 // <copyright file="ClaimsRequest.cs" company="Andrew Arnott">
3 // Copyright (c) Andrew Arnott. All rights reserved.
5 //-----------------------------------------------------------------------
7 namespace DotNetOpenAuth
.OpenId
.Extensions
.SimpleRegistration
{
9 using System
.Collections
.Generic
;
10 using System
.Diagnostics
;
11 using System
.Diagnostics
.CodeAnalysis
;
12 using System
.Globalization
;
14 using DotNetOpenAuth
.Messaging
;
15 using DotNetOpenAuth
.OpenId
.Messages
;
18 /// Carries the request/require/none demand state of the simple registration fields.
20 public sealed class ClaimsRequest
: ExtensionBase
{
22 /// The factory method that may be used in deserialization of this message.
24 internal static readonly OpenIdExtensionFactory
.CreateDelegate Factory
= (typeUri
, data
, baseMessage
) => {
25 if (typeUri
== Constants
.sreg_ns
&& baseMessage
is SignedResponseRequest
) {
26 return new ClaimsRequest();
33 /// Additional type URIs that this extension is sometimes known by remote parties.
35 private static readonly string[] additionalTypeUris
= new string[] {
37 Constants
.sreg_ns11other
,
41 /// The type URI that this particular (deserialized) extension was read in using,
42 /// allowing a response to alter be crafted using the same type URI.
44 private string typeUriDeserializedFrom
;
47 /// Initializes a new instance of the <see cref="ClaimsRequest"/> class.
49 public ClaimsRequest()
50 : base(new Version(1, 0), Constants
.sreg_ns
, additionalTypeUris
) {
54 /// Initializes a new instance of the <see cref="ClaimsRequest"/> class
55 /// by deserializing from a message.
57 /// <param name="typeUri">The type URI this extension was recognized by in the OpenID message.</param>
58 internal ClaimsRequest(string typeUri
)
60 ErrorUtilities
.VerifyNonZeroLength(typeUri
, "typeUri");
62 this.typeUriDeserializedFrom
= typeUri
;
66 /// Gets or sets the URL the consumer site provides for the authenticating user to review
67 /// for how his claims will be used by the consumer web site.
69 [MessagePart(Constants
.policy_url
, IsRequired
= false)]
70 public Uri PolicyUrl { get; set; }
73 /// Gets or sets the level of interest a relying party has in the nickname of the user.
75 public DemandLevel Nickname { get; set; }
78 /// Gets or sets the level of interest a relying party has in the email of the user.
80 public DemandLevel Email { get; set; }
83 /// Gets or sets the level of interest a relying party has in the full name of the user.
85 public DemandLevel FullName { get; set; }
88 /// Gets or sets the level of interest a relying party has in the birthdate of the user.
90 public DemandLevel BirthDate { get; set; }
93 /// Gets or sets the level of interest a relying party has in the gender of the user.
95 public DemandLevel Gender { get; set; }
98 /// Gets or sets the level of interest a relying party has in the postal code of the user.
100 public DemandLevel PostalCode { get; set; }
103 /// Gets or sets the level of interest a relying party has in the Country of the user.
105 public DemandLevel Country { get; set; }
108 /// Gets or sets the level of interest a relying party has in the language of the user.
110 public DemandLevel Language { get; set; }
113 /// Gets or sets the level of interest a relying party has in the time zone of the user.
115 public DemandLevel TimeZone { get; set; }
118 /// Gets or sets the value of the sreg.required parameter.
120 /// <value>A comma-delimited list of sreg fields.</value>
121 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification
= "Called by messaging framework via reflection.")]
122 [MessagePart(Constants
.required
, AllowEmpty
= true)]
123 private string RequiredList
{
124 get { return string.Join(",", this.AssembleProfileFields(DemandLevel.Require)); }
125 set { this.SetProfileRequestFromList(value.Split(','), DemandLevel.Require); }
129 /// Gets or sets the value of the sreg.optional parameter.
131 /// <value>A comma-delimited list of sreg fields.</value>
132 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification
= "Called by messaging framework via reflection.")]
133 [MessagePart(Constants
.optional
, AllowEmpty
= true)]
134 private string OptionalList
{
135 get { return string.Join(",", this.AssembleProfileFields(DemandLevel.Request)); }
136 set { this.SetProfileRequestFromList(value.Split(','), DemandLevel.Request); }
140 /// Tests equality between two <see cref="ClaimsRequest"/> structs.
142 /// <param name="one">One instance to compare.</param>
143 /// <param name="other">Another instance to compare.</param>
144 /// <returns>The result of the operator.</returns>
145 public static bool operator ==(ClaimsRequest one
, ClaimsRequest other
) {
146 return one
.EqualsNullSafe(other
);
150 /// Tests inequality between two <see cref="ClaimsRequest"/> structs.
152 /// <param name="one">One instance to compare.</param>
153 /// <param name="other">Another instance to compare.</param>
154 /// <returns>The result of the operator.</returns>
155 public static bool operator !=(ClaimsRequest one
, ClaimsRequest other
) {
156 return !(one
== other
);
160 /// Tests equality between two <see cref="ClaimsRequest"/> structs.
162 /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
164 /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
166 /// <exception cref="T:System.NullReferenceException">
167 /// The <paramref name="obj"/> parameter is null.
169 public override bool Equals(object obj
) {
170 ClaimsRequest other
= obj
as ClaimsRequest
;
176 this.BirthDate
.Equals(other
.BirthDate
) &&
177 this.Country
.Equals(other
.Country
) &&
178 this.Language
.Equals(other
.Language
) &&
179 this.Email
.Equals(other
.Email
) &&
180 this.FullName
.Equals(other
.FullName
) &&
181 this.Gender
.Equals(other
.Gender
) &&
182 this.Nickname
.Equals(other
.Nickname
) &&
183 this.PostalCode
.Equals(other
.PostalCode
) &&
184 this.TimeZone
.Equals(other
.TimeZone
) &&
185 this.PolicyUrl
.EqualsNullSafe(other
.PolicyUrl
);
189 /// Serves as a hash function for a particular type.
192 /// A hash code for the current <see cref="T:System.Object"/>.
194 public override int GetHashCode() {
195 // It's important that if Equals returns true that the hash code also equals,
196 // so returning base.GetHashCode() is a BAD option.
197 // Return 1 is simple and poor for dictionary storage, but considering that every
198 // ClaimsRequest formulated at a single RP will likely have all the same fields,
199 // even a good hash code function will likely generate the same hash code. So
200 // we just cut to the chase and return a simple one.
205 /// Renders the requested information as a string.
208 /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
210 public override string ToString() {
211 string format
= @"Nickname = '{0}'
220 return string.Format(CultureInfo
.CurrentCulture
, format
, this.Nickname
, this.Email
, this.FullName
, this.BirthDate
, this.Gender
, this.PostalCode
, this.Country
, this.Language
, this.TimeZone
);
224 /// Prepares a Simple Registration response extension that is compatible with the
225 /// version of Simple Registration used in the request message.
227 /// <returns>The newly created <see cref="ClaimsResponse"/> instance.</returns>
228 public ClaimsResponse
CreateResponse() {
229 if (this.typeUriDeserializedFrom
== null) {
230 throw new InvalidOperationException(OpenIdStrings
.CallDeserializeBeforeCreateResponse
);
233 return new ClaimsResponse(this.typeUriDeserializedFrom
);
237 /// Sets the profile request properties according to a list of
238 /// field names that might have been passed in the OpenId query dictionary.
240 /// <param name="fieldNames">
241 /// The list of field names that should receive a given
242 /// <paramref name="requestLevel"/>. These field names should match
243 /// the OpenId specification for field names, omitting the 'openid.sreg' prefix.
245 /// <param name="requestLevel">The none/request/require state of the listed fields.</param>
246 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification
= "Called by messaging framework via reflection.")]
247 private void SetProfileRequestFromList(ICollection
<string> fieldNames
, DemandLevel requestLevel
) {
248 foreach (string field
in fieldNames
) {
250 case Constants
.nickname
:
251 this.Nickname
= requestLevel
;
253 case Constants
.email
:
254 this.Email
= requestLevel
;
256 case Constants
.fullname
:
257 this.FullName
= requestLevel
;
260 this.BirthDate
= requestLevel
;
262 case Constants
.gender
:
263 this.Gender
= requestLevel
;
265 case Constants
.postcode
:
266 this.PostalCode
= requestLevel
;
268 case Constants
.country
:
269 this.Country
= requestLevel
;
271 case Constants
.language
:
272 this.Language
= requestLevel
;
274 case Constants
.timezone
:
275 this.TimeZone
= requestLevel
;
278 Logger
.WarnFormat("OpenIdProfileRequest.SetProfileRequestFromList: Unrecognized field name '{0}'.", field
);
285 /// Assembles the profile parameter names that have a given <see cref="DemandLevel"/>.
287 /// <param name="level">The demand level (request, require, none).</param>
288 /// <returns>An array of the profile parameter names that meet the criteria.</returns>
289 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification
= "Called by messaging framework via reflection.")]
290 private string[] AssembleProfileFields(DemandLevel level
) {
291 List
<string> fields
= new List
<string>(10);
292 if (this.Nickname
== level
) {
293 fields
.Add(Constants
.nickname
);
294 } if (this.Email
== level
) {
295 fields
.Add(Constants
.email
);
296 } if (this.FullName
== level
) {
297 fields
.Add(Constants
.fullname
);
298 } if (this.BirthDate
== level
) {
299 fields
.Add(Constants
.dob
);
300 } if (this.Gender
== level
) {
301 fields
.Add(Constants
.gender
);
302 } if (this.PostalCode
== level
) {
303 fields
.Add(Constants
.postcode
);
304 } if (this.Country
== level
) {
305 fields
.Add(Constants
.country
);
306 } if (this.Language
== level
) {
307 fields
.Add(Constants
.language
);
308 } if (this.TimeZone
== level
) {
309 fields
.Add(Constants
.timezone
);
312 return fields
.ToArray();