diff options
Diffstat (limited to 'src/DotNetOpenAuth.OpenId/OpenId/Extensions/SimpleRegistration')
5 files changed, 821 insertions, 0 deletions
diff --git a/src/DotNetOpenAuth.OpenId/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs b/src/DotNetOpenAuth.OpenId/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs new file mode 100644 index 0000000..18f63d4 --- /dev/null +++ b/src/DotNetOpenAuth.OpenId/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs @@ -0,0 +1,316 @@ +//----------------------------------------------------------------------- +// <copyright file="ClaimsRequest.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +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; + + /// <summary> + /// Carries the request/require/none demand state of the simple registration fields. + /// </summary> + [Serializable] + public sealed class ClaimsRequest : ExtensionBase { + /// <summary> + /// The factory method that may be used in deserialization of this message. + /// </summary> + internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => { + if (typeUri == Constants.sreg_ns && isProviderRole) { + return new ClaimsRequest(typeUri); + } + + return null; + }; + + /// <summary> + /// The type URI that this particular (deserialized) extension was read in using, + /// allowing a response to alter be crafted using the same type URI. + /// </summary> + private string typeUriDeserializedFrom; + + /// <summary> + /// Initializes a new instance of the <see cref="ClaimsRequest"/> class. + /// </summary> + public ClaimsRequest() + : base(new Version(1, 0), Constants.sreg_ns, Constants.AdditionalTypeUris) { + } + + /// <summary> + /// Initializes a new instance of the <see cref="ClaimsRequest"/> class + /// by deserializing from a message. + /// </summary> + /// <param name="typeUri">The type URI this extension was recognized by in the OpenID message.</param> + internal ClaimsRequest(string typeUri) + : this() { + Requires.NotNullOrEmpty(typeUri, "typeUri"); + + this.typeUriDeserializedFrom = typeUri; + } + + /// <summary> + /// 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. + /// </summary> + [MessagePart(Constants.policy_url, IsRequired = false)] + public Uri PolicyUrl { get; set; } + + /// <summary> + /// Gets or sets the level of interest a relying party has in the nickname of the user. + /// </summary> + public DemandLevel Nickname { get; set; } + + /// <summary> + /// Gets or sets the level of interest a relying party has in the email of the user. + /// </summary> + public DemandLevel Email { get; set; } + + /// <summary> + /// Gets or sets the level of interest a relying party has in the full name of the user. + /// </summary> + public DemandLevel FullName { get; set; } + + /// <summary> + /// Gets or sets the level of interest a relying party has in the birthdate of the user. + /// </summary> + public DemandLevel BirthDate { get; set; } + + /// <summary> + /// Gets or sets the level of interest a relying party has in the gender of the user. + /// </summary> + public DemandLevel Gender { get; set; } + + /// <summary> + /// Gets or sets the level of interest a relying party has in the postal code of the user. + /// </summary> + public DemandLevel PostalCode { get; set; } + + /// <summary> + /// Gets or sets the level of interest a relying party has in the Country of the user. + /// </summary> + public DemandLevel Country { get; set; } + + /// <summary> + /// Gets or sets the level of interest a relying party has in the language of the user. + /// </summary> + public DemandLevel Language { get; set; } + + /// <summary> + /// Gets or sets the level of interest a relying party has in the time zone of the user. + /// </summary> + public DemandLevel TimeZone { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this <see cref="ClaimsRequest"/> instance + /// is synthesized from an AX request at the Provider. + /// </summary> + internal bool Synthesized { get; set; } + + /// <summary> + /// Gets or sets the value of the sreg.required parameter. + /// </summary> + /// <value>A comma-delimited list of sreg fields.</value> + [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); } + } + + /// <summary> + /// Gets or sets the value of the sreg.optional parameter. + /// </summary> + /// <value>A comma-delimited list of sreg fields.</value> + [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); } + } + + /// <summary> + /// Tests equality between two <see cref="ClaimsRequest"/> structs. + /// </summary> + /// <param name="one">One instance to compare.</param> + /// <param name="other">Another instance to compare.</param> + /// <returns>The result of the operator.</returns> + public static bool operator ==(ClaimsRequest one, ClaimsRequest other) { + return one.EqualsNullSafe(other); + } + + /// <summary> + /// Tests inequality between two <see cref="ClaimsRequest"/> structs. + /// </summary> + /// <param name="one">One instance to compare.</param> + /// <param name="other">Another instance to compare.</param> + /// <returns>The result of the operator.</returns> + public static bool operator !=(ClaimsRequest one, ClaimsRequest other) { + return !(one == other); + } + + /// <summary> + /// Tests equality between two <see cref="ClaimsRequest"/> structs. + /// </summary> + /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param> + /// <returns> + /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false. + /// </returns> + /// <exception cref="T:System.NullReferenceException"> + /// The <paramref name="obj"/> parameter is null. + /// </exception> + 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); + } + + /// <summary> + /// Serves as a hash function for a particular type. + /// </summary> + /// <returns> + /// A hash code for the current <see cref="T:System.Object"/>. + /// </returns> + 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; + } + + /// <summary> + /// Renders the requested information as a string. + /// </summary> + /// <returns> + /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>. + /// </returns> + 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); + } + + /// <summary> + /// Prepares a Simple Registration response extension that is compatible with the + /// version of Simple Registration used in the request message. + /// </summary> + /// <returns>The newly created <see cref="ClaimsResponse"/> instance.</returns> + public ClaimsResponse CreateResponse() { + if (this.typeUriDeserializedFrom == null) { + throw new InvalidOperationException(OpenIdStrings.CallDeserializeBeforeCreateResponse); + } + + return new ClaimsResponse(this.typeUriDeserializedFrom); + } + + /// <summary> + /// Sets the profile request properties according to a list of + /// field names that might have been passed in the OpenId query dictionary. + /// </summary> + /// <param name="fieldNames"> + /// The list of field names that should receive a given + /// <paramref name="requestLevel"/>. These field names should match + /// the OpenId specification for field names, omitting the 'openid.sreg' prefix. + /// </param> + /// <param name="requestLevel">The none/request/require state of the listed fields.</param> + internal void SetProfileRequestFromList(IEnumerable<string> 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; + } + } + } + + /// <summary> + /// Assembles the profile parameter names that have a given <see cref="DemandLevel"/>. + /// </summary> + /// <param name="level">The demand level (request, require, none).</param> + /// <returns>An array of the profile parameter names that meet the criteria.</returns> + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by messaging framework via reflection.")] + private string[] AssembleProfileFields(DemandLevel level) { + List<string> fields = new List<string>(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(); + } + } +} diff --git a/src/DotNetOpenAuth.OpenId/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs b/src/DotNetOpenAuth.OpenId/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs new file mode 100644 index 0000000..b50833b --- /dev/null +++ b/src/DotNetOpenAuth.OpenId/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs @@ -0,0 +1,357 @@ +//----------------------------------------------------------------------- +// <copyright file="ClaimsResponse.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration { + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; + using System.Globalization; + using System.Net.Mail; + using System.Text; + using System.Text.RegularExpressions; + using System.Xml.Serialization; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId.Messages; + + /// <summary> + /// A struct storing Simple Registration field values describing an + /// authenticating user. + /// </summary> + [Serializable] + public sealed class ClaimsResponse : ExtensionBase, IClientScriptExtensionResponse, IMessageWithEvents { + /// <summary> + /// The factory method that may be used in deserialization of this message. + /// </summary> + internal static readonly StandardOpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage, isProviderRole) => { + if ((typeUri == Constants.sreg_ns || Array.IndexOf(Constants.AdditionalTypeUris, typeUri) >= 0) && !isProviderRole) { + return new ClaimsResponse(typeUri); + } + + return null; + }; + + /// <summary> + /// The allowed format for birthdates. + /// </summary> + private static readonly Regex birthDateValidator = new Regex(@"^\d\d\d\d-\d\d-\d\d$"); + + /// <summary> + /// Storage for the raw string birthdate value. + /// </summary> + private string birthDateRaw; + + /// <summary> + /// Backing field for the <see cref="BirthDate"/> property. + /// </summary> + private DateTime? birthDate; + + /// <summary> + /// Backing field for the <see cref="Culture"/> property. + /// </summary> + private CultureInfo culture; + + /// <summary> + /// Initializes a new instance of the <see cref="ClaimsResponse"/> class. + /// </summary> + internal ClaimsResponse() + : this(Constants.sreg_ns) { + } + + /// <summary> + /// Initializes a new instance of the <see cref="ClaimsResponse"/> class. + /// </summary> + /// <param name="typeUriToUse"> + /// The type URI that must be used to identify this extension in the response message. + /// This value should be the same one the relying party used to send the extension request. + /// </param> + internal ClaimsResponse(string typeUriToUse) + : base(new Version(1, 0), typeUriToUse, Constants.AdditionalTypeUris) { + Requires.NotNullOrEmpty(typeUriToUse, "typeUriToUse"); + } + + /// <summary> + /// Gets or sets the nickname the user goes by. + /// </summary> + [MessagePart(Constants.nickname)] + public string Nickname { get; set; } + + /// <summary> + /// Gets or sets the user's email address. + /// </summary> + [MessagePart(Constants.email)] + public string Email { get; set; } + + /// <summary> + /// Gets or sets the full name of a user as a single string. + /// </summary> + [MessagePart(Constants.fullname)] + public string FullName { get; set; } + + /// <summary> + /// Gets or sets the user's birthdate. + /// </summary> + public DateTime? BirthDate { + get { + return this.birthDate; + } + + set { + this.birthDate = value; + + // Don't use property accessor for peer property to avoid infinite loop between the two proeprty accessors. + if (value.HasValue) { + this.birthDateRaw = value.Value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); + } else { + this.birthDateRaw = null; + } + } + } + + /// <summary> + /// Gets or sets the raw birth date string given by the extension. + /// </summary> + /// <value>A string in the format yyyy-MM-dd.</value> + [MessagePart(Constants.dob)] + public string BirthDateRaw { + get { + return this.birthDateRaw; + } + + set { + ErrorUtilities.VerifyArgument(value == null || birthDateValidator.IsMatch(value), OpenIdStrings.SregInvalidBirthdate); + if (value != null) { + // Update the BirthDate property, if possible. + // Don't use property accessor for peer property to avoid infinite loop between the two proeprty accessors. + // Some valid sreg dob values like "2000-00-00" will not work as a DateTime struct, + // in which case we null it out, but don't show any error. + DateTime newBirthDate; + if (DateTime.TryParse(value, out newBirthDate)) { + this.birthDate = newBirthDate; + } else { + Logger.OpenId.WarnFormat("Simple Registration birthdate '{0}' could not be parsed into a DateTime and may not include month and/or day information. Setting BirthDate property to null.", value); + this.birthDate = null; + } + } else { + this.birthDate = null; + } + + this.birthDateRaw = value; + } + } + + /// <summary> + /// Gets or sets the gender of the user. + /// </summary> + [MessagePart(Constants.gender, Encoder = typeof(GenderEncoder))] + public Gender? Gender { get; set; } + + /// <summary> + /// Gets or sets the zip code / postal code of the user. + /// </summary> + [MessagePart(Constants.postcode)] + public string PostalCode { get; set; } + + /// <summary> + /// Gets or sets the country of the user. + /// </summary> + [MessagePart(Constants.country)] + public string Country { get; set; } + + /// <summary> + /// Gets or sets the primary/preferred language of the user. + /// </summary> + [MessagePart(Constants.language)] + public string Language { get; set; } + + /// <summary> + /// Gets or sets the user's timezone. + /// </summary> + [MessagePart(Constants.timezone)] + public string TimeZone { get; set; } + + /// <summary> + /// Gets a combination of the user's full name and email address. + /// </summary> + public MailAddress MailAddress { + get { + if (string.IsNullOrEmpty(this.Email)) { + return null; + } else if (string.IsNullOrEmpty(this.FullName)) { + return new MailAddress(this.Email); + } else { + return new MailAddress(this.Email, this.FullName); + } + } + } + + /// <summary> + /// Gets or sets a combination o the language and country of the user. + /// </summary> + [XmlIgnore] + public CultureInfo Culture { + get { + if (this.culture == null && !string.IsNullOrEmpty(this.Language)) { + string cultureString = string.Empty; + cultureString = this.Language; + if (!string.IsNullOrEmpty(this.Country)) { + cultureString += "-" + this.Country; + } + this.culture = CultureInfo.GetCultureInfo(cultureString); + } + + return this.culture; + } + + set { + this.culture = value; + this.Language = (value != null) ? value.TwoLetterISOLanguageName : null; + int indexOfHyphen = (value != null) ? value.Name.IndexOf('-') : -1; + this.Country = indexOfHyphen > 0 ? value.Name.Substring(indexOfHyphen + 1) : null; + } + } + + /// <summary> + /// Gets a value indicating whether this extension is signed by the Provider. + /// </summary> + /// <value> + /// <c>true</c> if this instance is signed by the Provider; otherwise, <c>false</c>. + /// </value> + public bool IsSignedByProvider { + get { return this.IsSignedByRemoteParty; } + } + + /// <summary> + /// Tests equality of two <see cref="ClaimsResponse"/> objects. + /// </summary> + /// <param name="one">One instance to compare.</param> + /// <param name="other">Another instance to compare.</param> + /// <returns>The result of the operator.</returns> + public static bool operator ==(ClaimsResponse one, ClaimsResponse other) { + return one.EqualsNullSafe(other); + } + + /// <summary> + /// Tests inequality of two <see cref="ClaimsResponse"/> objects. + /// </summary> + /// <param name="one">One instance to compare.</param> + /// <param name="other">Another instance to compare.</param> + /// <returns>The result of the operator.</returns> + public static bool operator !=(ClaimsResponse one, ClaimsResponse other) { + return !(one == other); + } + + /// <summary> + /// Tests equality of two <see cref="ClaimsResponse"/> objects. + /// </summary> + /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param> + /// <returns> + /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false. + /// </returns> + /// <exception cref="T:System.NullReferenceException"> + /// The <paramref name="obj"/> parameter is null. + /// </exception> + public override bool Equals(object obj) { + ClaimsResponse other = obj as ClaimsResponse; + if (other == null) { + return false; + } + + return + this.BirthDateRaw.EqualsNullSafe(other.BirthDateRaw) && + this.Country.EqualsNullSafe(other.Country) && + this.Language.EqualsNullSafe(other.Language) && + this.Email.EqualsNullSafe(other.Email) && + this.FullName.EqualsNullSafe(other.FullName) && + this.Gender.Equals(other.Gender) && + this.Nickname.EqualsNullSafe(other.Nickname) && + this.PostalCode.EqualsNullSafe(other.PostalCode) && + this.TimeZone.EqualsNullSafe(other.TimeZone); + } + + /// <summary> + /// Serves as a hash function for a particular type. + /// </summary> + /// <returns> + /// A hash code for the current <see cref="T:System.Object"/>. + /// </returns> + public override int GetHashCode() { + return (this.Nickname != null) ? this.Nickname.GetHashCode() : base.GetHashCode(); + } + + #region IClientScriptExtension Members + + /// <summary> + /// Reads the extension information on an authentication response from the provider. + /// </summary> + /// <param name="response">The incoming OpenID response carrying the extension.</param> + /// <returns> + /// A Javascript snippet that when executed on the user agent returns an object with + /// the information deserialized from the extension response. + /// </returns> + /// <remarks> + /// This method is called <b>before</b> the signature on the assertion response has been + /// verified. Therefore all information in these fields should be assumed unreliable + /// and potentially falsified. + /// </remarks> + string IClientScriptExtensionResponse.InitializeJavaScriptData(IProtocolMessageWithExtensions response) { + var sreg = new Dictionary<string, string>(15); + + // Although we could probably whip up a trip with MessageDictionary + // to avoid explicitly setting each field, doing so would likely + // open ourselves up to security exploits from the OP as it would + // make possible sending arbitrary javascript in arbitrary field names. + sreg[Constants.nickname] = this.Nickname; + sreg[Constants.email] = this.Email; + sreg[Constants.fullname] = this.FullName; + sreg[Constants.dob] = this.BirthDateRaw; + sreg[Constants.gender] = this.Gender.HasValue ? this.Gender.Value.ToString() : null; + sreg[Constants.postcode] = this.PostalCode; + sreg[Constants.country] = this.Country; + sreg[Constants.language] = this.Language; + sreg[Constants.timezone] = this.TimeZone; + + return MessagingUtilities.CreateJsonObject(sreg, false); + } + + #endregion + + #region IMessageWithEvents Members + + /// <summary> + /// Called when the message is about to be transmitted, + /// before it passes through the channel binding elements. + /// </summary> + void IMessageWithEvents.OnSending() { + // Null out empty values so we don't send out a lot of empty parameters. + this.Country = EmptyToNull(this.Country); + this.Email = EmptyToNull(this.Email); + this.FullName = EmptyToNull(this.FullName); + this.Language = EmptyToNull(this.Language); + this.Nickname = EmptyToNull(this.Nickname); + this.PostalCode = EmptyToNull(this.PostalCode); + this.TimeZone = EmptyToNull(this.TimeZone); + } + + /// <summary> + /// Called when the message has been received, + /// after it passes through the channel binding elements. + /// </summary> + void IMessageWithEvents.OnReceiving() { + } + + #endregion + + /// <summary> + /// Translates an empty string value to null, or passes through non-empty values. + /// </summary> + /// <param name="value">The value to consider changing to null.</param> + /// <returns>Either null or a non-empty string.</returns> + private static string EmptyToNull(string value) { + return string.IsNullOrEmpty(value) ? null : value; + } + } +}
\ No newline at end of file diff --git a/src/DotNetOpenAuth.OpenId/OpenId/Extensions/SimpleRegistration/Constants.cs b/src/DotNetOpenAuth.OpenId/OpenId/Extensions/SimpleRegistration/Constants.cs new file mode 100644 index 0000000..9e00137 --- /dev/null +++ b/src/DotNetOpenAuth.OpenId/OpenId/Extensions/SimpleRegistration/Constants.cs @@ -0,0 +1,46 @@ +// <auto-generated/> // disable StyleCop on this file +//----------------------------------------------------------------------- +// <copyright file="Constants.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration { + using System; + using System.Collections.Generic; + using System.Text; + + /// <summary> + /// Simple Registration constants + /// </summary> + internal static class Constants { + internal const string sreg_ns = "http://openid.net/extensions/sreg/1.1"; + internal const string sreg_ns10 = "http://openid.net/sreg/1.0"; + internal const string sreg_ns11other = "http://openid.net/sreg/1.1"; + internal const string sreg_compatibility_alias = "sreg"; + internal const string policy_url = "policy_url"; + internal const string optional = "optional"; + internal const string required = "required"; + internal const string nickname = "nickname"; + internal const string email = "email"; + internal const string fullname = "fullname"; + internal const string dob = "dob"; + internal const string gender = "gender"; + internal const string postcode = "postcode"; + internal const string country = "country"; + internal const string language = "language"; + internal const string timezone = "timezone"; + internal static class Genders { + internal const string Male = "M"; + internal const string Female = "F"; + } + + /// <summary> + /// Additional type URIs that this extension is sometimes known by remote parties. + /// </summary> + internal static readonly string[] AdditionalTypeUris = new string[] { + Constants.sreg_ns10, + Constants.sreg_ns11other, + }; + } +} diff --git a/src/DotNetOpenAuth.OpenId/OpenId/Extensions/SimpleRegistration/DemandLevel.cs b/src/DotNetOpenAuth.OpenId/OpenId/Extensions/SimpleRegistration/DemandLevel.cs new file mode 100644 index 0000000..7129270 --- /dev/null +++ b/src/DotNetOpenAuth.OpenId/OpenId/Extensions/SimpleRegistration/DemandLevel.cs @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------- +// <copyright file="DemandLevel.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration { + /// <summary> + /// Specifies what level of interest a relying party has in obtaining the value + /// of a given field offered by the Simple Registration extension. + /// </summary> + public enum DemandLevel { + /// <summary> + /// The relying party has no interest in obtaining this field. + /// </summary> + NoRequest, + + /// <summary> + /// The relying party would like the value of this field, but wants + /// the Provider to display the field to the user as optionally provided. + /// </summary> + Request, + + /// <summary> + /// The relying party considers this a required field as part of + /// authentication. The Provider and/or user agent MAY still choose to + /// not provide the value of the field however, according to the + /// Simple Registration extension specification. + /// </summary> + Require, + } +}
\ No newline at end of file diff --git a/src/DotNetOpenAuth.OpenId/OpenId/Extensions/SimpleRegistration/Gender.cs b/src/DotNetOpenAuth.OpenId/OpenId/Extensions/SimpleRegistration/Gender.cs new file mode 100644 index 0000000..979c481 --- /dev/null +++ b/src/DotNetOpenAuth.OpenId/OpenId/Extensions/SimpleRegistration/Gender.cs @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------- +// <copyright file="Gender.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration { + using System; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.Messaging.Reflection; + + /// <summary> + /// Indicates the gender of a user. + /// </summary> + public enum Gender { + /// <summary> + /// The user is male. + /// </summary> + Male, + + /// <summary> + /// The user is female. + /// </summary> + Female, + } + + /// <summary> + /// Encodes/decodes the Simple Registration Gender type to its string representation. + /// </summary> + internal class GenderEncoder : IMessagePartEncoder { + #region IMessagePartEncoder Members + + /// <summary> + /// Encodes the specified value. + /// </summary> + /// <param name="value">The value. Guaranteed to never be null.</param> + /// <returns> + /// The <paramref name="value"/> in string form, ready for message transport. + /// </returns> + public string Encode(object value) { + var gender = (Gender?)value; + if (gender.HasValue) { + switch (gender.Value) { + case Gender.Male: return Constants.Genders.Male; + case Gender.Female: return Constants.Genders.Female; + } + } + + return null; + } + + /// <summary> + /// Decodes the specified value. + /// </summary> + /// <param name="value">The string value carried by the transport. Guaranteed to never be null, although it may be empty.</param> + /// <returns> + /// The deserialized form of the given string. + /// </returns> + /// <exception cref="FormatException">Thrown when the string value given cannot be decoded into the required object type.</exception> + public object Decode(string value) { + switch (value) { + case Constants.Genders.Male: return SimpleRegistration.Gender.Male; + case Constants.Genders.Female: return SimpleRegistration.Gender.Female; + default: throw new FormatException(); + } + } + + #endregion + } +}
\ No newline at end of file |