//-----------------------------------------------------------------------
//
// Copyright (c) Outercurve Foundation. All rights reserved.
//
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Messages;
///
/// Carries the request/require/none demand state of the simple registration fields.
///
[Serializable]
public sealed class ClaimsRequest : ExtensionBase {
///
/// The factory method that may be used in deserialization of this message.
///
internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => {
if (typeUri == Constants.TypeUris.Standard && isProviderRole) {
return new ClaimsRequest(typeUri);
}
return null;
};
///
/// The type URI that this particular (deserialized) extension was read in using,
/// allowing a response to alter be crafted using the same type URI.
///
private string typeUriDeserializedFrom;
///
/// Initializes a new instance of the class.
///
public ClaimsRequest()
: base(new Version(1, 0), Constants.TypeUris.Standard, Constants.AdditionalTypeUris) {
}
///
/// Initializes a new instance of the class
/// by deserializing from a message.
///
/// The type URI this extension was recognized by in the OpenID message.
internal ClaimsRequest(string typeUri)
: this() {
Requires.NotNullOrEmpty(typeUri, "typeUri");
this.typeUriDeserializedFrom = typeUri;
}
///
/// Gets or sets the URL the consumer site provides for the authenticating user to review
/// for how his claims will be used by the consumer web site.
///
[MessagePart(Constants.policy_url, IsRequired = false)]
public Uri PolicyUrl { get; set; }
///
/// Gets or sets the level of interest a relying party has in the nickname of the user.
///
public DemandLevel Nickname { get; set; }
///
/// Gets or sets the level of interest a relying party has in the email of the user.
///
public DemandLevel Email { get; set; }
///
/// Gets or sets the level of interest a relying party has in the full name of the user.
///
public DemandLevel FullName { get; set; }
///
/// Gets or sets the level of interest a relying party has in the birthdate of the user.
///
public DemandLevel BirthDate { get; set; }
///
/// Gets or sets the level of interest a relying party has in the gender of the user.
///
public DemandLevel Gender { get; set; }
///
/// Gets or sets the level of interest a relying party has in the postal code of the user.
///
public DemandLevel PostalCode { get; set; }
///
/// Gets or sets the level of interest a relying party has in the Country of the user.
///
public DemandLevel Country { get; set; }
///
/// Gets or sets the level of interest a relying party has in the language of the user.
///
public DemandLevel Language { get; set; }
///
/// Gets or sets the level of interest a relying party has in the time zone of the user.
///
public DemandLevel TimeZone { get; set; }
///
/// Gets or sets a value indicating whether this instance
/// is synthesized from an AX request at the Provider.
///
internal bool Synthesized { get; set; }
///
/// Gets or sets the value of the sreg.required parameter.
///
/// A comma-delimited list of sreg fields.
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by messaging framework via reflection.")]
[MessagePart(Constants.required, AllowEmpty = true)]
private string RequiredList {
get { return string.Join(",", this.AssembleProfileFields(DemandLevel.Require)); }
set { this.SetProfileRequestFromList(value.Split(','), DemandLevel.Require); }
}
///
/// Gets or sets the value of the sreg.optional parameter.
///
/// A comma-delimited list of sreg fields.
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by messaging framework via reflection.")]
[MessagePart(Constants.optional, AllowEmpty = true)]
private string OptionalList {
get { return string.Join(",", this.AssembleProfileFields(DemandLevel.Request)); }
set { this.SetProfileRequestFromList(value.Split(','), DemandLevel.Request); }
}
///
/// Tests equality between two structs.
///
/// One instance to compare.
/// Another instance to compare.
/// The result of the operator.
public static bool operator ==(ClaimsRequest one, ClaimsRequest other) {
return one.EqualsNullSafe(other);
}
///
/// Tests inequality between two structs.
///
/// One instance to compare.
/// Another instance to compare.
/// The result of the operator.
public static bool operator !=(ClaimsRequest one, ClaimsRequest other) {
return !(one == other);
}
///
/// Tests equality between two structs.
///
/// The to compare with the current .
///
/// true if the specified is equal to the current ; otherwise, false.
///
///
/// The parameter is null.
///
public override bool Equals(object obj) {
ClaimsRequest other = obj as ClaimsRequest;
if (other == null) {
return false;
}
return
this.BirthDate.Equals(other.BirthDate) &&
this.Country.Equals(other.Country) &&
this.Language.Equals(other.Language) &&
this.Email.Equals(other.Email) &&
this.FullName.Equals(other.FullName) &&
this.Gender.Equals(other.Gender) &&
this.Nickname.Equals(other.Nickname) &&
this.PostalCode.Equals(other.PostalCode) &&
this.TimeZone.Equals(other.TimeZone) &&
this.PolicyUrl.EqualsNullSafe(other.PolicyUrl);
}
///
/// Serves as a hash function for a particular type.
///
///
/// A hash code for the current .
///
public override int GetHashCode() {
// It's important that if Equals returns true that the hash code also equals,
// so returning base.GetHashCode() is a BAD option.
// Return 1 is simple and poor for dictionary storage, but considering that every
// ClaimsRequest formulated at a single RP will likely have all the same fields,
// even a good hash code function will likely generate the same hash code. So
// we just cut to the chase and return a simple one.
return 1;
}
///
/// Renders the requested information as a string.
///
///
/// A that represents the current .
///
public override string ToString() {
string format = @"Nickname = '{0}'
Email = '{1}'
FullName = '{2}'
Birthdate = '{3}'
Gender = '{4}'
PostalCode = '{5}'
Country = '{6}'
Language = '{7}'
TimeZone = '{8}'";
return string.Format(CultureInfo.CurrentCulture, format, this.Nickname, this.Email, this.FullName, this.BirthDate, this.Gender, this.PostalCode, this.Country, this.Language, this.TimeZone);
}
///
/// Prepares a Simple Registration response extension that is compatible with the
/// version of Simple Registration used in the request message.
///
/// The newly created instance.
public ClaimsResponse CreateResponse() {
if (this.typeUriDeserializedFrom == null) {
throw new InvalidOperationException(OpenIdStrings.CallDeserializeBeforeCreateResponse);
}
return new ClaimsResponse(this.typeUriDeserializedFrom);
}
///
/// Sets the profile request properties according to a list of
/// field names that might have been passed in the OpenId query dictionary.
///
///
/// The list of field names that should receive a given
/// . These field names should match
/// the OpenId specification for field names, omitting the 'openid.sreg' prefix.
///
/// The none/request/require state of the listed fields.
internal void SetProfileRequestFromList(IEnumerable fieldNames, DemandLevel requestLevel) {
foreach (string field in fieldNames) {
switch (field) {
case "": // this occurs for empty lists
break;
case Constants.nickname:
this.Nickname = requestLevel;
break;
case Constants.email:
this.Email = requestLevel;
break;
case Constants.fullname:
this.FullName = requestLevel;
break;
case Constants.dob:
this.BirthDate = requestLevel;
break;
case Constants.gender:
this.Gender = requestLevel;
break;
case Constants.postcode:
this.PostalCode = requestLevel;
break;
case Constants.country:
this.Country = requestLevel;
break;
case Constants.language:
this.Language = requestLevel;
break;
case Constants.timezone:
this.TimeZone = requestLevel;
break;
default:
Logger.OpenId.WarnFormat("ClaimsRequest.SetProfileRequestFromList: Unrecognized field name '{0}'.", field);
break;
}
}
}
///
/// Assembles the profile parameter names that have a given .
///
/// The demand level (request, require, none).
/// An array of the profile parameter names that meet the criteria.
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by messaging framework via reflection.")]
private string[] AssembleProfileFields(DemandLevel level) {
List fields = new List(10);
if (this.Nickname == level) {
fields.Add(Constants.nickname);
} if (this.Email == level) {
fields.Add(Constants.email);
} if (this.FullName == level) {
fields.Add(Constants.fullname);
} if (this.BirthDate == level) {
fields.Add(Constants.dob);
} if (this.Gender == level) {
fields.Add(Constants.gender);
} if (this.PostalCode == level) {
fields.Add(Constants.postcode);
} if (this.Country == level) {
fields.Add(Constants.country);
} if (this.Language == level) {
fields.Add(Constants.language);
} if (this.TimeZone == level) {
fields.Add(Constants.timezone);
}
return fields.ToArray();
}
}
}