using System; using System.Collections.Generic; using System.Text; using DotNetOpenId.RelyingParty; using System.Globalization; using System.Diagnostics; namespace DotNetOpenId.Extensions.AttributeExchange { /// /// The Attribute Exchange Fetch message, response leg. /// public sealed class FetchResponse : IExtensionResponse { readonly string Mode = "fetch_response"; List attributesProvided = new List(); /// /// Enumerates over all the attributes included by the Provider. /// public IEnumerable Attributes { get { return attributesProvided; } } /// /// Used by the Provider to add attributes to the response for the relying party. /// public void AddAttribute(AttributeValues attribute) { if (attribute == null) throw new ArgumentNullException("attribute"); if (containsAttribute(attribute.TypeUri)) throw new ArgumentException( string.Format(CultureInfo.CurrentCulture, Strings.AttributeAlreadyAdded, attribute.TypeUri)); attributesProvided.Add(attribute); } /// /// Used by the Relying Party to get the value(s) returned by the OpenID Provider /// for a given attribute, or null if that attribute was not provided. /// public AttributeValues GetAttribute(string attributeTypeUri) { foreach (var att in attributesProvided) { if (att.TypeUri == attributeTypeUri) return att; } return null; } bool containsAttribute(string typeUri) { return GetAttribute(typeUri) != null; } /// /// Whether the OpenID Provider intends to honor the request for updates. /// public bool UpdateUrlSupported { get { return UpdateUrl != null; } } /// /// The URL the OpenID Provider will post updates to. Must be set if the Provider /// supports and will use this feature. /// public Uri UpdateUrl { get; set; } #region IExtensionResponse Members string IExtension.TypeUri { get { return Constants.TypeUri; } } IEnumerable IExtension.AdditionalSupportedTypeUris { get { return new string[0]; } } IDictionary IExtensionResponse.Serialize(Provider.IRequest authenticationRequest) { var fields = new Dictionary { { "mode", Mode }, }; if (UpdateUrlSupported) fields.Add("update_url", UpdateUrl.AbsoluteUri); SerializeAttributes(fields, attributesProvided); return fields; } internal static void SerializeAttributes(Dictionary fields, IEnumerable attributes) { Debug.Assert(fields != null && attributes != null); AliasManager aliasManager = new AliasManager(); foreach (var att in attributes) { string alias = aliasManager.GetAlias(att.TypeUri); fields.Add("type." + alias, att.TypeUri); if (att.Values == null) continue; if (att.Values.Count != 1) { fields.Add("count." + alias, att.Values.Count.ToString(CultureInfo.InvariantCulture)); for (int i = 0; i < att.Values.Count; i++) { fields.Add(string.Format(CultureInfo.InvariantCulture, "value.{0}.{1}", alias, i + 1), att.Values[i]); } } else { fields.Add("value." + alias, att.Values[0]); } } } bool IExtensionResponse.Deserialize(IDictionary fields, IAuthenticationResponse response, string typeUri) { if (fields == null) return false; string mode; fields.TryGetValue("mode", out mode); if (mode != Mode) return false; string updateUrl; fields.TryGetValue("update_url", out updateUrl); Uri updateUri; if (Uri.TryCreate(updateUrl, UriKind.Absolute, out updateUri)) UpdateUrl = updateUri; foreach (var att in DeserializeAttributes(fields)) AddAttribute(att); return true; } internal static IEnumerable DeserializeAttributes(IDictionary fields) { AliasManager aliasManager = parseAliases(fields); foreach (string alias in aliasManager.Aliases) { AttributeValues att = new AttributeValues(aliasManager.ResolveAlias(alias)); int count = 1; bool countSent = false; string countString; if (fields.TryGetValue("count." + alias, out countString)) { if (!int.TryParse(countString, out count) || count < 0) { Logger.ErrorFormat("Failed to parse count.{0} value to a non-negative integer.", alias); continue; } countSent = true; } if (countSent) { for (int i = 1; i <= count; i++) { string value; if (fields.TryGetValue(string.Format(CultureInfo.InvariantCulture, "value.{0}.{1}", alias, i), out value)) { att.Values.Add(value); } else { Logger.ErrorFormat("Missing value for attribute '{0}'.", att.TypeUri); continue; } } } else { string value; if (fields.TryGetValue("value." + alias, out value)) att.Values.Add(value); else { Logger.ErrorFormat("Missing value for attribute '{0}'.", att.TypeUri); continue; } } yield return att; } } static AliasManager parseAliases(IDictionary fields) { Debug.Assert(fields != null); AliasManager aliasManager = new AliasManager(); foreach (var pair in fields) { if (!pair.Key.StartsWith("type.", StringComparison.Ordinal)) continue; string alias = pair.Key.Substring(5); if (alias.IndexOfAny(new[] { '.', ',', ':' }) >= 0) { Logger.ErrorFormat("Illegal characters in alias name '{0}'.", alias); continue; } aliasManager.SetAlias(alias, pair.Value); } return aliasManager; } #endregion } }