//-----------------------------------------------------------------------
//
// Copyright (c) Outercurve Foundation. All rights reserved.
//
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
using System;
using System.Collections.ObjectModel;
using System.Linq;
using DotNetOpenAuth.Logging;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Messages;
///
/// The Attribute Exchange Fetch message, response leg.
///
[Serializable]
public sealed class FetchResponse : ExtensionBase, IMessageWithEvents {
///
/// 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.TypeUri && !isProviderRole) {
string mode;
if (data.TryGetValue("mode", out mode) && mode == Mode) {
return new FetchResponse();
}
}
return null;
};
///
/// The value of the 'mode' parameter.
///
[MessagePart("mode", IsRequired = true)]
private const string Mode = "fetch_response";
///
/// The collection of provided attributes. This field will never be null.
///
private readonly KeyedCollection attributesProvided = new KeyedCollectionDelegate(av => av.TypeUri);
///
/// Initializes a new instance of the class.
///
public FetchResponse()
: base(new Version(1, 0), Constants.TypeUri, null) {
}
///
/// Gets a sequence of the attributes whose values are provided by the OpenID Provider.
///
public KeyedCollection Attributes {
get {
return this.attributesProvided;
}
}
///
/// Gets a value indicating whether the OpenID Provider intends to
/// honor the request for updates.
///
public bool UpdateUrlSupported {
get { return this.UpdateUrl != null; }
}
///
/// Gets or sets the URL the OpenID Provider will post updates to.
/// Must be set if the Provider supports and will use this feature.
///
[MessagePart("update_url", IsRequired = false)]
public Uri UpdateUrl { get; set; }
///
/// Gets a value indicating whether this extension is signed by the Provider.
///
///
/// true if this instance is signed by the Provider; otherwise, false.
///
public bool IsSignedByProvider {
get { return this.IsSignedByRemoteParty; }
}
///
/// Gets the first attribute value provided for a given attribute Type URI.
///
///
/// The type URI of the attribute.
/// Usually a constant from .
///
/// The first value provided for the attribute, or null if the attribute is missing or no values were provided.
///
///
/// This is meant as a helper method for the common case of just wanting one attribute value.
/// For greater flexibility or to retrieve more than just the first value for an attribute,
/// use the collection directly.
///
public string GetAttributeValue(string typeUri) {
if (this.Attributes.Contains(typeUri)) {
return this.Attributes[typeUri].Values.FirstOrDefault();
} else {
return null;
}
}
///
/// Determines whether the specified is equal to the current .
///
/// 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) {
FetchResponse other = obj as FetchResponse;
if (other == null) {
return false;
}
if (this.Version != other.Version) {
return false;
}
if (this.UpdateUrl != other.UpdateUrl) {
return false;
}
if (!MessagingUtilities.AreEquivalentUnordered(this.Attributes.ToList(), other.Attributes.ToList())) {
return false;
}
return true;
}
///
/// Serves as a hash function for a particular type.
///
///
/// A hash code for the current .
///
public override int GetHashCode() {
unchecked {
int hashCode = this.Version.GetHashCode();
if (this.UpdateUrl != null) {
hashCode += this.UpdateUrl.GetHashCode();
}
foreach (AttributeValues value in this.Attributes) {
hashCode += value.GetHashCode();
}
return hashCode;
}
}
#region IMessageWithEvents Members
///
/// Called when the message is about to be transmitted,
/// before it passes through the channel binding elements.
///
void IMessageWithEvents.OnSending() {
var extraData = ((IMessage)this).ExtraData;
AXUtilities.SerializeAttributes(extraData, this.attributesProvided);
}
///
/// Called when the message has been received,
/// after it passes through the channel binding elements.
///
void IMessageWithEvents.OnReceiving() {
var extraData = ((IMessage)this).ExtraData;
foreach (var att in AXUtilities.DeserializeAttributes(extraData)) {
this.Attributes.Add(att);
}
}
#endregion
///
/// Checks the message state for conformity to the protocol specification
/// and throws an exception if the message is invalid.
///
///
/// Some messages have required fields, or combinations of fields that must relate to each other
/// in specialized ways. After deserializing a message, this method checks the state of the
/// message to see if it conforms to the protocol.
/// Note that this property should not check signatures or perform any state checks
/// outside this scope of this particular message.
///
/// Thrown if the message is invalid.
protected override void EnsureValidMessage() {
base.EnsureValidMessage();
if (this.UpdateUrl != null && !this.UpdateUrl.IsAbsoluteUri) {
this.UpdateUrl = null;
Logger.OpenId.ErrorFormat("The AX fetch response update_url parameter was not absolute ('{0}'). Ignoring value.", this.UpdateUrl);
}
}
}
}