FxCop fixes.
[dotnetoauth.git] / src / DotNetOpenAuth / OpenId / Extensions / SimpleRegistration / ClaimsRequest.cs
blob6860abc25c8202b86cda035ec42297df2a722ee4
1 //-----------------------------------------------------------------------
2 // <copyright file="ClaimsRequest.cs" company="Andrew Arnott">
3 // Copyright (c) Andrew Arnott. All rights reserved.
4 // </copyright>
5 //-----------------------------------------------------------------------
7 namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
8 using System;
9 using System.Collections.Generic;
10 using System.Diagnostics;
11 using System.Diagnostics.CodeAnalysis;
12 using System.Globalization;
13 using System.Text;
14 using DotNetOpenAuth.Messaging;
15 using DotNetOpenAuth.OpenId.Messages;
17 /// <summary>
18 /// Carries the request/require/none demand state of the simple registration fields.
19 /// </summary>
20 public sealed class ClaimsRequest : ExtensionBase {
21 /// <summary>
22 /// The factory method that may be used in deserialization of this message.
23 /// </summary>
24 internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => {
25 if (typeUri == Constants.sreg_ns && baseMessage is SignedResponseRequest) {
26 return new ClaimsRequest();
29 return null;
32 /// <summary>
33 /// Additional type URIs that this extension is sometimes known by remote parties.
34 /// </summary>
35 private static readonly string[] additionalTypeUris = new string[] {
36 Constants.sreg_ns10,
37 Constants.sreg_ns11other,
40 /// <summary>
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.
43 /// </summary>
44 private string typeUriDeserializedFrom;
46 /// <summary>
47 /// Initializes a new instance of the <see cref="ClaimsRequest"/> class.
48 /// </summary>
49 public ClaimsRequest()
50 : base(new Version(1, 0), Constants.sreg_ns, additionalTypeUris) {
53 /// <summary>
54 /// Initializes a new instance of the <see cref="ClaimsRequest"/> class
55 /// by deserializing from a message.
56 /// </summary>
57 /// <param name="typeUri">The type URI this extension was recognized by in the OpenID message.</param>
58 internal ClaimsRequest(string typeUri)
59 : this() {
60 ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
62 this.typeUriDeserializedFrom = typeUri;
65 /// <summary>
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.
68 /// </summary>
69 [MessagePart(Constants.policy_url, IsRequired = false)]
70 public Uri PolicyUrl { get; set; }
72 /// <summary>
73 /// Gets or sets the level of interest a relying party has in the nickname of the user.
74 /// </summary>
75 public DemandLevel Nickname { get; set; }
77 /// <summary>
78 /// Gets or sets the level of interest a relying party has in the email of the user.
79 /// </summary>
80 public DemandLevel Email { get; set; }
82 /// <summary>
83 /// Gets or sets the level of interest a relying party has in the full name of the user.
84 /// </summary>
85 public DemandLevel FullName { get; set; }
87 /// <summary>
88 /// Gets or sets the level of interest a relying party has in the birthdate of the user.
89 /// </summary>
90 public DemandLevel BirthDate { get; set; }
92 /// <summary>
93 /// Gets or sets the level of interest a relying party has in the gender of the user.
94 /// </summary>
95 public DemandLevel Gender { get; set; }
97 /// <summary>
98 /// Gets or sets the level of interest a relying party has in the postal code of the user.
99 /// </summary>
100 public DemandLevel PostalCode { get; set; }
102 /// <summary>
103 /// Gets or sets the level of interest a relying party has in the Country of the user.
104 /// </summary>
105 public DemandLevel Country { get; set; }
107 /// <summary>
108 /// Gets or sets the level of interest a relying party has in the language of the user.
109 /// </summary>
110 public DemandLevel Language { get; set; }
112 /// <summary>
113 /// Gets or sets the level of interest a relying party has in the time zone of the user.
114 /// </summary>
115 public DemandLevel TimeZone { get; set; }
117 /// <summary>
118 /// Gets or sets the value of the sreg.required parameter.
119 /// </summary>
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); }
128 /// <summary>
129 /// Gets or sets the value of the sreg.optional parameter.
130 /// </summary>
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); }
139 /// <summary>
140 /// Tests equality between two <see cref="ClaimsRequest"/> structs.
141 /// </summary>
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);
149 /// <summary>
150 /// Tests inequality between two <see cref="ClaimsRequest"/> structs.
151 /// </summary>
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);
159 /// <summary>
160 /// Tests equality between two <see cref="ClaimsRequest"/> structs.
161 /// </summary>
162 /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
163 /// <returns>
164 /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
165 /// </returns>
166 /// <exception cref="T:System.NullReferenceException">
167 /// The <paramref name="obj"/> parameter is null.
168 /// </exception>
169 public override bool Equals(object obj) {
170 ClaimsRequest other = obj as ClaimsRequest;
171 if (other == null) {
172 return false;
175 return
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);
188 /// <summary>
189 /// Serves as a hash function for a particular type.
190 /// </summary>
191 /// <returns>
192 /// A hash code for the current <see cref="T:System.Object"/>.
193 /// </returns>
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.
201 return 1;
204 /// <summary>
205 /// Renders the requested information as a string.
206 /// </summary>
207 /// <returns>
208 /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
209 /// </returns>
210 public override string ToString() {
211 string format = @"Nickname = '{0}'
212 Email = '{1}'
213 FullName = '{2}'
214 Birthdate = '{3}'
215 Gender = '{4}'
216 PostalCode = '{5}'
217 Country = '{6}'
218 Language = '{7}'
219 TimeZone = '{8}'";
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);
223 /// <summary>
224 /// Prepares a Simple Registration response extension that is compatible with the
225 /// version of Simple Registration used in the request message.
226 /// </summary>
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);
236 /// <summary>
237 /// Sets the profile request properties according to a list of
238 /// field names that might have been passed in the OpenId query dictionary.
239 /// </summary>
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.
244 /// </param>
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) {
249 switch (field) {
250 case Constants.nickname:
251 this.Nickname = requestLevel;
252 break;
253 case Constants.email:
254 this.Email = requestLevel;
255 break;
256 case Constants.fullname:
257 this.FullName = requestLevel;
258 break;
259 case Constants.dob:
260 this.BirthDate = requestLevel;
261 break;
262 case Constants.gender:
263 this.Gender = requestLevel;
264 break;
265 case Constants.postcode:
266 this.PostalCode = requestLevel;
267 break;
268 case Constants.country:
269 this.Country = requestLevel;
270 break;
271 case Constants.language:
272 this.Language = requestLevel;
273 break;
274 case Constants.timezone:
275 this.TimeZone = requestLevel;
276 break;
277 default:
278 Logger.WarnFormat("OpenIdProfileRequest.SetProfileRequestFromList: Unrecognized field name '{0}'.", field);
279 break;
284 /// <summary>
285 /// Assembles the profile parameter names that have a given <see cref="DemandLevel"/>.
286 /// </summary>
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();