using System; using System.Collections.Generic; using System.Text; using System.Globalization; using System.Diagnostics; namespace DotNetOpenId.Extensions.AttributeExchange { /// /// The Attribute Exchange Fetch message, request leg. /// public sealed class FetchRequest : IExtensionRequest { readonly string Mode = "fetch_request"; List attributesRequested = new List(); /// /// Enumerates the attributes whose values are requested by the Relying Party. /// public IEnumerable Attributes { get { return attributesRequested; } } /// /// Used by the Relying Party to add a request for the values of a given attribute. /// public void AddAttribute(AttributeRequest attribute) { if (attribute == null) throw new ArgumentNullException("attribute"); if (containsAttribute(attribute.TypeUri)) throw new ArgumentException( string.Format(CultureInfo.CurrentCulture, Strings.AttributeAlreadyAdded, attribute.TypeUri), "attribute"); attributesRequested.Add(attribute); } /// /// Used by the Provider to find out whether the value(s) of a given attribute is requested. /// /// Null if the Relying Party did not ask for the values of the given attribute. public AttributeRequest GetAttribute(string attributeTypeUri) { foreach (var attribute in attributesRequested) if (string.Equals(attribute.TypeUri, attributeTypeUri, StringComparison.Ordinal)) return attribute; return null; } bool containsAttribute(string typeUri) { return GetAttribute(typeUri) != null; } /// /// If set, the OpenID Provider may re-post the fetch response message to the /// specified URL at some time after the initial response has been sent, using an /// OpenID Authentication Positive Assertion to inform the relying party of updates /// to the requested fields. /// public Uri UpdateUrl { get; set; } #region IExtensionRequest Members string IExtension.TypeUri { get { return Constants.TypeUri; } } IEnumerable IExtension.AdditionalSupportedTypeUris { get { return new string[0]; } } IDictionary IExtensionRequest.Serialize(RelyingParty.IAuthenticationRequest authenticationRequest) { var fields = new Dictionary { { "mode", Mode }, }; if (UpdateUrl != null) fields.Add("update_url", UpdateUrl.AbsoluteUri); List requiredAliases = new List(), optionalAliases = new List(); AliasManager aliasManager = new AliasManager(); foreach (var att in attributesRequested) { string alias = aliasManager.GetAlias(att.TypeUri); // define the alias<->typeUri mapping fields.Add("type." + alias, att.TypeUri); // set how many values the relying party wants max fields.Add("count." + alias, att.Count.ToString(CultureInfo.InvariantCulture)); if (att.IsRequired) requiredAliases.Add(alias); else optionalAliases.Add(alias); } // Set optional/required lists if (optionalAliases.Count > 0) fields.Add("if_available", string.Join(",", optionalAliases.ToArray())); if (requiredAliases.Count > 0) fields.Add("required", string.Join(",", requiredAliases.ToArray())); return fields; } bool IExtensionRequest.Deserialize(IDictionary fields, DotNetOpenId.Provider.IRequest request, 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; string requiredAliasString, optionalAliasString; fields.TryGetValue("if_available", out optionalAliasString); fields.TryGetValue("required", out requiredAliasString); var requiredAliases = parseAliasList(requiredAliasString); var optionalAliases = parseAliasList(optionalAliasString); // if an alias shows up in both lists, an exception will result implicitly. var allAliases = new List(requiredAliases.Count + optionalAliases.Count); allAliases.AddRange(requiredAliases); allAliases.AddRange(optionalAliases); if (allAliases.Count == 0) { Logger.Error("Attribute Exchange extension did not provide any aliases in the if_available or required lists."); return false; } AliasManager aliasManager = new AliasManager(); foreach (var alias in allAliases) { string attributeTypeUri; if (fields.TryGetValue("type." + alias, out attributeTypeUri)) { aliasManager.SetAlias(alias, attributeTypeUri); AttributeRequest att = new AttributeRequest { TypeUri = attributeTypeUri, IsRequired = requiredAliases.Contains(alias), }; string countString; if (fields.TryGetValue("count." + alias, out countString)) { if (countString == "unlimited") att.Count = int.MaxValue; else { int count; if (int.TryParse(countString, out count) && count > 0) { att.Count = count; } else { Logger.Error("count." + alias + " could not be parsed into a positive integer."); } } } else { att.Count = 1; } AddAttribute(att); } else { Logger.Error("Type URI definition of alias " + alias + " is missing."); } } return true; } static List parseAliasList(string aliasList) { List result = new List(); if (string.IsNullOrEmpty(aliasList)) return result; if (aliasList.Contains(".") || aliasList.Contains("\n")) { Logger.ErrorFormat("Illegal characters found in Attribute Exchange alias list."); return result; } result.AddRange(aliasList.Split(',')); return result; } #endregion } }