using System;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.IO;
using DotNetOpenId.Yadis;
using System.Diagnostics;
using DotNetOpenId.Extensions;
using System.Globalization;
namespace DotNetOpenId.RelyingParty {
///
/// Represents information discovered about a user-supplied Identifier.
///
[DebuggerDisplay("ClaimedIdentifier: {ClaimedIdentifier}, ProviderEndpoint: {ProviderEndpoint}, OpenId: {Protocol.Version}")]
internal class ServiceEndpoint : IXrdsProviderEndpoint {
///
/// The URL which accepts OpenID Authentication protocol messages.
///
///
/// Obtained by performing discovery on the User-Supplied Identifier.
/// This value MUST be an absolute HTTP or HTTPS URL.
///
public Uri ProviderEndpoint { get; private set; }
Uri IProviderEndpoint.Uri { get { return ProviderEndpoint; } }
/*
///
/// An Identifier for an OpenID Provider.
///
public Identifier ProviderIdentifier { get; private set; }
*/
///
/// An Identifier that was presented by the end user to the Relying Party,
/// or selected by the user at the OpenID Provider.
/// During the initiation phase of the protocol, an end user may enter
/// either their own Identifier or an OP Identifier. If an OP Identifier
/// is used, the OP may then assist the end user in selecting an Identifier
/// to share with the Relying Party.
///
public Identifier UserSuppliedIdentifier { get; private set; }
///
/// The Identifier that the end user claims to own.
///
public Identifier ClaimedIdentifier { get; private set; }
///
/// An alternate Identifier for an end user that is local to a
/// particular OP and thus not necessarily under the end user's
/// control.
///
public Identifier ProviderLocalIdentifier { get; private set; }
string friendlyIdentifierForDisplay;
///
/// Supports the property.
///
public string FriendlyIdentifierForDisplay {
get {
if (friendlyIdentifierForDisplay == null) {
XriIdentifier xri = ClaimedIdentifier as XriIdentifier;
UriIdentifier uri = ClaimedIdentifier as UriIdentifier;
if (xri != null) {
if (UserSuppliedIdentifier == null || String.Equals(UserSuppliedIdentifier, ClaimedIdentifier, StringComparison.OrdinalIgnoreCase)) {
friendlyIdentifierForDisplay = ClaimedIdentifier;
} else {
friendlyIdentifierForDisplay = String.Format(CultureInfo.CurrentCulture, "{0} ({1})",
ClaimedIdentifier, UserSuppliedIdentifier);
}
} else if (uri != null) {
string displayUri = uri.Uri.Authority + uri.Uri.PathAndQuery;
displayUri = displayUri.TrimEnd('/');
// Multi-byte unicode characters get encoded by the Uri class for transit.
// Since this is for display purposes, we want to reverse this and display a readable
// representation of these foreign characters.
friendlyIdentifierForDisplay = Uri.UnescapeDataString(displayUri);
} else {
Debug.Fail("Doh! We never should have reached here.");
friendlyIdentifierForDisplay = ClaimedIdentifier;
}
}
return friendlyIdentifierForDisplay;
}
}
///
/// Gets the list of services available at this OP Endpoint for the
/// claimed Identifier.
///
public string[] ProviderSupportedServiceTypeUris { get; private set; }
ServiceEndpoint(Identifier claimedIdentifier, Identifier userSuppliedIdentifier,
Uri providerEndpoint, Identifier providerLocalIdentifier,
string[] providerSupportedServiceTypeUris, int? servicePriority, int? uriPriority) {
if (claimedIdentifier == null) throw new ArgumentNullException("claimedIdentifier");
if (providerEndpoint == null) throw new ArgumentNullException("providerEndpoint");
if (providerSupportedServiceTypeUris == null) throw new ArgumentNullException("providerSupportedServiceTypeUris");
ClaimedIdentifier = claimedIdentifier;
UserSuppliedIdentifier = userSuppliedIdentifier;
ProviderEndpoint = providerEndpoint;
ProviderLocalIdentifier = providerLocalIdentifier ?? claimedIdentifier;
ProviderSupportedServiceTypeUris = providerSupportedServiceTypeUris;
this.servicePriority = servicePriority;
this.uriPriority = uriPriority;
}
///
/// Used for deserializing from authentication responses.
///
ServiceEndpoint(Identifier claimedIdentifier, Identifier userSuppliedIdentifier,
Uri providerEndpoint, Identifier providerLocalIdentifier, Protocol protocol) {
ClaimedIdentifier = claimedIdentifier;
UserSuppliedIdentifier = userSuppliedIdentifier;
ProviderEndpoint = providerEndpoint;
ProviderLocalIdentifier = providerLocalIdentifier ?? claimedIdentifier;
this.protocol = protocol;
}
internal static ServiceEndpoint CreateForProviderIdentifier(
Identifier providerIdentifier, Uri providerEndpoint,
string[] providerSupportedServiceTypeUris, int? servicePriority, int? uriPriority) {
Protocol protocol = Protocol.Detect(providerSupportedServiceTypeUris);
return new ServiceEndpoint(protocol.ClaimedIdentifierForOPIdentifier, providerIdentifier,
providerEndpoint, protocol.ClaimedIdentifierForOPIdentifier,
providerSupportedServiceTypeUris, servicePriority, uriPriority);
}
internal static ServiceEndpoint CreateForClaimedIdentifier(
Identifier claimedIdentifier, Identifier providerLocalIdentifier,
Uri providerEndpoint,
string[] providerSupportedServiceTypeUris, int? servicePriority, int? uriPriority) {
return CreateForClaimedIdentifier(claimedIdentifier, null, providerLocalIdentifier,
providerEndpoint, providerSupportedServiceTypeUris, servicePriority, uriPriority);
}
internal static ServiceEndpoint CreateForClaimedIdentifier(
Identifier claimedIdentifier, Identifier userSuppliedIdentifier, Identifier providerLocalIdentifier,
Uri providerEndpoint,
string[] providerSupportedServiceTypeUris, int? servicePriority, int? uriPriority) {
return new ServiceEndpoint(claimedIdentifier, userSuppliedIdentifier, providerEndpoint,
providerLocalIdentifier, providerSupportedServiceTypeUris, servicePriority, uriPriority);
}
Protocol protocol;
///
/// Gets the OpenID protocol used by the Provider.
///
public Protocol Protocol {
get {
if (protocol == null) {
protocol = Protocol.Detect(ProviderSupportedServiceTypeUris);
}
if (protocol != null) return protocol;
throw new InvalidOperationException("Unable to determine the version of OpenID the Provider supports.");
}
}
public bool IsExtensionSupported(string extensionUri) {
if (ProviderSupportedServiceTypeUris == null)
throw new InvalidOperationException("Cannot lookup extension support on a rehydrated ServiceEndpoint.");
return Array.IndexOf(ProviderSupportedServiceTypeUris, extensionUri) >= 0;
}
public bool IsExtensionSupported(IExtension extension) {
if (extension == null) throw new ArgumentNullException("extension");
// Consider the primary case.
if (IsExtensionSupported(extension.TypeUri)) {
return true;
}
// Consider the secondary cases.
if (extension.AdditionalSupportedTypeUris != null) {
foreach (string extensionTypeUri in extension.AdditionalSupportedTypeUris) {
if (IsExtensionSupported(extensionTypeUri)) {
return true;
}
}
}
return false;
}
public bool IsExtensionSupported() where T : Extensions.IExtension, new() {
T extension = new T();
return IsExtensionSupported(extension);
}
public bool IsExtensionSupported(Type extensionType) {
if (extensionType == null) throw new ArgumentNullException("extensionType");
if (!typeof(Extensions.IExtension).IsAssignableFrom(extensionType))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Strings.TypeMustImplementX, typeof(Extensions.IExtension).FullName),
"extensionType");
var extension = (Extensions.IExtension)Activator.CreateInstance(extensionType);
return IsExtensionSupported(extension);
}
Version IProviderEndpoint.Version { get { return Protocol.Version; } }
///
/// Saves the discovered information about this endpoint
/// for later comparison to validate assertions.
///
internal void Serialize(TextWriter writer) {
writer.WriteLine(ClaimedIdentifier);
writer.WriteLine(ProviderLocalIdentifier);
writer.WriteLine(UserSuppliedIdentifier);
writer.WriteLine(ProviderEndpoint);
writer.WriteLine(Protocol.Version);
// No reason to serialize priority. We only needed priority to decide whether to use this endpoint.
}
///
/// Reads previously discovered information about an endpoint
/// from a solicited authentication assertion for validation.
///
///
/// A object that has everything
/// except the
/// deserialized.
///
internal static ServiceEndpoint Deserialize(TextReader reader) {
var claimedIdentifier = Identifier.Parse(reader.ReadLine());
var providerLocalIdentifier = Identifier.Parse(reader.ReadLine());
string userSuppliedIdentifier = reader.ReadLine();
if (userSuppliedIdentifier.Length == 0) userSuppliedIdentifier = null;
var providerEndpoint = new Uri(reader.ReadLine());
var protocol = Util.FindBestVersion(p => p.Version, new[] { new Version(reader.ReadLine()) });
return new ServiceEndpoint(claimedIdentifier, userSuppliedIdentifier,
providerEndpoint, providerLocalIdentifier, protocol);
}
internal static ServiceEndpoint ParseFromAuthResponse(IDictionary query, Identifier userSuppliedIdentifier) {
Protocol protocol = Protocol.Detect(query);
Debug.Assert(protocol.openid.op_endpoint != null, "This method should only be called in OpenID 2.0 contexts.");
return new ServiceEndpoint(
Util.GetRequiredArg(query, protocol.openid.claimed_id),
userSuppliedIdentifier,
new Uri(Util.GetRequiredArg(query, protocol.openid.op_endpoint)),
Util.GetRequiredArg(query, protocol.openid.identity),
protocol);
}
public static bool operator ==(ServiceEndpoint se1, ServiceEndpoint se2) {
if ((object)se1 == null ^ (object)se2 == null) return false;
if ((object)se1 == null) return true;
return se1.Equals(se2);
}
public static bool operator !=(ServiceEndpoint se1, ServiceEndpoint se2) {
return !(se1 == se2);
}
public override bool Equals(object obj) {
ServiceEndpoint other = obj as ServiceEndpoint;
if (other == null) return false;
// We specifically do not check our ProviderSupportedServiceTypeUris array
// or the priority field
// as that is not persisted in our tokens, and it is not part of the
// important assertion validation that is part of the spec.
return
this.ClaimedIdentifier == other.ClaimedIdentifier &&
this.ProviderEndpoint == other.ProviderEndpoint &&
this.ProviderLocalIdentifier == other.ProviderLocalIdentifier &&
this.Protocol == other.Protocol;
}
public override int GetHashCode() {
return ClaimedIdentifier.GetHashCode();
}
public override string ToString() {
return ProviderEndpoint.AbsoluteUri;
}
#region IXrdsProviderEndpoint Members
private int? servicePriority;
///
/// Gets the priority associated with this service that may have been given
/// in the XRDS document.
///
int? IXrdsProviderEndpoint.ServicePriority {
get { return servicePriority; }
}
private int? uriPriority;
///
/// Gets the priority associated with the service endpoint URL.
///
int? IXrdsProviderEndpoint.UriPriority {
get { return uriPriority; }
}
#endregion
}
}