//----------------------------------------------------------------------- // // Copyright (c) Outercurve Foundation. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOpenAuth.OpenId { using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; using System.Text; using System.Xml; using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.RelyingParty; using DotNetOpenAuth.Xrds; using DotNetOpenAuth.Yadis; /// /// The discovery service for XRI identifiers that uses an XRI proxy resolver for discovery. /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Xri", Justification = "Acronym")] public class XriDiscoveryProxyService : IIdentifierDiscoveryService { /// /// The magic URL that will provide us an XRDS document for a given XRI identifier. /// /// /// We use application/xrd+xml instead of application/xrds+xml because it gets /// xri.net to automatically give us exactly the right XRD element for community i-names /// automatically, saving us having to choose which one to use out of the result. /// The ssl=true parameter tells the proxy resolver to accept only SSL connections /// when resolving community i-names. /// private const string XriResolverProxyTemplate = "https://{1}/{0}?_xrd_r=application/xrd%2Bxml;sep=false"; /// /// Initializes a new instance of the class. /// public XriDiscoveryProxyService() { } #region IDiscoveryService Members /// /// Performs discovery on the specified identifier. /// /// The identifier to perform discovery on. /// The means to place outgoing HTTP requests. /// if set to true, no further discovery services will be called for this identifier. /// /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. /// public IEnumerable Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) { abortDiscoveryChain = false; var xriIdentifier = identifier as XriIdentifier; if (xriIdentifier == null) { return Enumerable.Empty(); } return DownloadXrds(xriIdentifier, requestHandler).XrdElements.CreateServiceEndpoints(xriIdentifier); } #endregion /// /// Downloads the XRDS document for this XRI. /// /// The identifier. /// The request handler. /// The XRDS document. private static XrdsDocument DownloadXrds(XriIdentifier identifier, IDirectWebRequestHandler requestHandler) { Requires.NotNull(identifier, "identifier"); Requires.NotNull(requestHandler, "requestHandler"); Contract.Ensures(Contract.Result() != null); XrdsDocument doc; using (var xrdsResponse = Yadis.Request(requestHandler, GetXrdsUrl(identifier), identifier.IsDiscoverySecureEndToEnd)) { doc = new XrdsDocument(XmlReader.Create(xrdsResponse.ResponseStream)); } ErrorUtilities.VerifyProtocol(doc.IsXrdResolutionSuccessful, OpenIdStrings.XriResolutionFailed); return doc; } /// /// Gets the URL from which this XRI's XRDS document may be downloaded. /// /// The identifier. /// The URI to HTTP GET from to get the services. private static Uri GetXrdsUrl(XriIdentifier identifier) { ErrorUtilities.VerifyProtocol(OpenIdElement.Configuration.XriResolver.Enabled, OpenIdStrings.XriResolutionDisabled); string xriResolverProxy = XriResolverProxyTemplate; if (identifier.IsDiscoverySecureEndToEnd) { // Indicate to xri.net that we require SSL to be used for delegated resolution // of community i-names. xriResolverProxy += ";https=true"; } return new Uri( string.Format( CultureInfo.InvariantCulture, xriResolverProxy, identifier, OpenIdElement.Configuration.XriResolver.Proxy.Name)); } } }