private static void Initialize() { lock (initializationSync) { if (!broken && !initialized) { try { file = GetIsolatedStorage(); reportOriginIdentity = GetOrCreateOriginIdentity(); webRequestHandler = new StandardWebRequestHandler(); observations.Add(observedRequests = new PersistentHashSet(file, "requests.txt", 3)); observations.Add(observedCultures = new PersistentHashSet(file, "cultures.txt", 20)); observations.Add(observedFeatures = new PersistentHashSet(file, "features.txt", int.MaxValue)); // Record site-wide features in use. if (HttpContext.Current != null && HttpContext.Current.ApplicationInstance != null) { // MVC or web forms? // front-end or back end web farm? // url rewriting? ////RecordFeatureUse(IsMVC ? "ASP.NET MVC" : "ASP.NET Web Forms"); } initialized = true; } catch (Exception e) { // This is supposed to be as low-risk as possible, so if it fails, just disable reporting // and avoid rethrowing. broken = true; Logger.Library.Error("Error while trying to initialize reporting.", e); } } } }
/// <summary> /// Gets the host-meta for a given identifier. /// </summary> /// <param name="identifier">The identifier.</param> /// <param name="requestHandler">The request handler.</param> /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param> /// <returns> /// The host-meta response, or <c>null</c> if no host-meta document could be obtained. /// </returns> private IncomingWebResponse GetHostMeta(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) { Contract.Requires <ArgumentNullException>(identifier != null); Contract.Requires <ArgumentNullException>(requestHandler != null); foreach (var hostMetaProxy in this.GetHostMetaLocations(identifier)) { var hostMetaLocation = hostMetaProxy.GetProxy(identifier); var request = (HttpWebRequest)WebRequest.Create(hostMetaLocation); request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy; var options = DirectWebRequestOptions.AcceptAllHttpResponses; if (identifier.IsDiscoverySecureEndToEnd) { options |= DirectWebRequestOptions.RequireSsl; } var response = requestHandler.GetResponse(request, options); if (response.Status == HttpStatusCode.OK) { Logger.Yadis.InfoFormat("Found host-meta for {0} at: {1}", identifier.Uri.Host, hostMetaLocation); signingHost = hostMetaProxy.GetSigningHost(identifier); return(response); } else { Logger.Yadis.InfoFormat("Could not obtain host-meta for {0} from {1}", identifier.Uri.Host, hostMetaLocation); } } signingHost = null; return(null); }
/// <summary> /// Gets the location of the XRDS document that describes a given identifier. /// </summary> /// <param name="identifier">The identifier under discovery.</param> /// <param name="requestHandler">The request handler.</param> /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param> /// <returns>An absolute URI, or <c>null</c> if one could not be determined.</returns> private Uri GetXrdsLocation(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) { Contract.Requires <ArgumentNullException>(identifier != null); Contract.Requires <ArgumentNullException>(requestHandler != null); var hostMetaResponse = this.GetHostMeta(identifier, requestHandler, out signingHost); if (hostMetaResponse == null) { return(null); } using (var sr = hostMetaResponse.GetResponseReader()) { string line = sr.ReadLine(); Match m = HostMetaLink.Match(line); if (m.Success) { Uri location = new Uri(m.Groups["location"].Value); Logger.Yadis.InfoFormat("Found link to XRDS at {0} in host-meta document {1}.", location, hostMetaResponse.FinalUri); return(location); } } Logger.Yadis.WarnFormat("Could not find link to XRDS in host-meta document: {0}", hostMetaResponse.FinalUri); return(null); }
/// <summary> /// Sends a multipart HTTP POST request (useful for posting files). /// </summary> /// <param name="request">The HTTP request.</param> /// <param name="requestHandler">The request handler.</param> /// <param name="parts">The parts to include in the POST entity.</param> /// <returns>The HTTP response.</returns> public static IncomingWebResponse PostMultipart(this HttpWebRequest request, IDirectWebRequestHandler requestHandler, IEnumerable <MultipartPostPart> parts) { Contract.Requires <ArgumentNullException>(request != null); Contract.Requires <ArgumentNullException>(requestHandler != null); Contract.Requires <ArgumentNullException>(parts != null); PostMultipartNoGetResponse(request, requestHandler, parts); return(requestHandler.GetResponse(request)); }
/// <summary> /// Performs discovery on the specified identifier. /// </summary> /// <param name="identifier">The identifier to perform discovery on.</param> /// <param name="requestHandler">The means to place outgoing HTTP requests.</param> /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param> /// <returns> /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. /// </returns> public IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) { abortDiscoveryChain = false; var xriIdentifier = identifier as XriIdentifier; if (xriIdentifier == null) { return Enumerable.Empty<IdentifierDiscoveryResult>(); } return DownloadXrds(xriIdentifier, requestHandler).XrdElements.CreateServiceEndpoints(xriIdentifier); }
/// <summary> /// Gets a value indicating whether verification of the return URL claimed by the Relying Party /// succeeded. /// </summary> /// <param name="requestHandler">The request handler.</param> /// <returns> /// Result of realm discovery. /// </returns> /// <remarks> /// Return URL verification is only attempted if this property is queried. /// The result of the verification is cached per request so calling this /// property getter multiple times in one request is not a performance hit. /// See OpenID Authentication 2.0 spec section 9.2.1. /// </remarks> public RelyingPartyDiscoveryResult IsReturnUrlDiscoverable(IDirectWebRequestHandler requestHandler) { if (!this.realmDiscoveryResult.HasValue) { this.realmDiscoveryResult = this.IsReturnUrlDiscoverableCore(requestHandler); } return(this.realmDiscoveryResult.Value); }
/// <summary> /// Performs discovery on the specified identifier. /// </summary> /// <param name="identifier">The identifier to perform discovery on.</param> /// <param name="requestHandler">The means to place outgoing HTTP requests.</param> /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param> /// <returns> /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. /// </returns> public IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) { var mockIdentifier = identifier as MockIdentifier; if (mockIdentifier == null) { abortDiscoveryChain = false; return Enumerable.Empty<IdentifierDiscoveryResult>(); } abortDiscoveryChain = true; return mockIdentifier.DiscoveryEndpoints; }
/// <summary> /// Downloads the XRDS document for this XRI. /// </summary> /// <param name="requestHandler">The request handler.</param> /// <returns>The XRDS document.</returns> private XrdsDocument DownloadXrds(IDirectWebRequestHandler requestHandler) { XrdsDocument doc; using (var xrdsResponse = Yadis.Request(requestHandler, this.XrdsUrl, this.IsDiscoverySecureEndToEnd)) { doc = new XrdsDocument(XmlReader.Create(xrdsResponse.ResponseStream)); } ErrorUtilities.VerifyProtocol(doc.IsXrdResolutionSuccessful, OpenIdStrings.XriResolutionFailed); return(doc); }
public override void SetUp() { base.SetUp(); this.RelyingPartySecuritySettings = DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.SecuritySettings.CreateSecuritySettings(); this.ProviderSecuritySettings = DotNetOpenAuthSection.Configuration.OpenId.Provider.SecuritySettings.CreateSecuritySettings(); this.MockResponder = MockHttpRequest.CreateUntrustedMockHttpHandler(); this.RequestHandler = this.MockResponder.MockWebRequestHandler; this.AutoProviderScenario = Scenarios.AutoApproval; Identifier.EqualityOnStrings = true; }
/// <summary> /// Downloads the XRDS document for this XRI. /// </summary> /// <param name="identifier">The identifier.</param> /// <param name="requestHandler">The request handler.</param> /// <returns>The XRDS document.</returns> private static XrdsDocument DownloadXrds(XriIdentifier identifier, IDirectWebRequestHandler requestHandler) { Requires.NotNull(identifier, "identifier"); Requires.NotNull(requestHandler, "requestHandler"); XrdsDocument doc; using (var xrdsResponse = Yadis.Request(requestHandler, GetXrdsUrl(identifier), identifier.IsDiscoverySecureEndToEnd)) { var readerSettings = MessagingUtilities.CreateUntrustedXmlReaderSettings(); doc = new XrdsDocument(XmlReader.Create(xrdsResponse.ResponseStream, readerSettings)); } ErrorUtilities.VerifyProtocol(doc.IsXrdResolutionSuccessful, OpenIdStrings.XriResolutionFailed); return doc; }
/// <summary> /// Downloads the XRDS document for this XRI. /// </summary> /// <param name="identifier">The identifier.</param> /// <param name="requestHandler">The request handler.</param> /// <returns>The XRDS document.</returns> private static XrdsDocument DownloadXrds(XriIdentifier identifier, IDirectWebRequestHandler requestHandler) { Requires.NotNull(identifier, "identifier"); Requires.NotNull(requestHandler, "requestHandler"); Contract.Ensures(Contract.Result<XrdsDocument>() != 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; }
/// <summary> /// Sends a multipart HTTP POST request (useful for posting files) but doesn't call GetResponse on it. /// </summary> /// <param name="request">The HTTP request.</param> /// <param name="requestHandler">The request handler.</param> /// <param name="parts">The parts to include in the POST entity.</param> internal static void PostMultipartNoGetResponse(this HttpWebRequest request, IDirectWebRequestHandler requestHandler, IEnumerable <MultipartPostPart> parts) { Contract.Requires <ArgumentNullException>(request != null); Contract.Requires <ArgumentNullException>(requestHandler != null); Contract.Requires <ArgumentNullException>(parts != null); Reporting.RecordFeatureUse("MessagingUtilities.PostMultipart"); parts = parts.CacheGeneratedResults(); string boundary = Guid.NewGuid().ToString(); string initialPartLeadingBoundary = string.Format(CultureInfo.InvariantCulture, "--{0}\r\n", boundary); string partLeadingBoundary = string.Format(CultureInfo.InvariantCulture, "\r\n--{0}\r\n", boundary); string finalTrailingBoundary = string.Format(CultureInfo.InvariantCulture, "\r\n--{0}--\r\n", boundary); var contentType = new ContentType("multipart/form-data") { Boundary = boundary, CharSet = Channel.PostEntityEncoding.WebName, }; request.Method = "POST"; request.ContentType = contentType.ToString(); long contentLength = parts.Sum(p => partLeadingBoundary.Length + p.Length) + finalTrailingBoundary.Length; if (parts.Any()) { contentLength -= 2; // the initial part leading boundary has no leading \r\n } request.ContentLength = contentLength; var requestStream = requestHandler.GetRequestStream(request); try { StreamWriter writer = new StreamWriter(requestStream, Channel.PostEntityEncoding); bool firstPart = true; foreach (var part in parts) { writer.Write(firstPart ? initialPartLeadingBoundary : partLeadingBoundary); firstPart = false; part.Serialize(writer); part.Dispose(); } writer.Write(finalTrailingBoundary); writer.Flush(); } finally { // We need to be sure to close the request stream... // unless it is a MemoryStream, which is a clue that we're in // a mock stream situation and closing it would preclude reading it later. if (!(requestStream is MemoryStream)) { requestStream.Dispose(); } } }
/// <summary> /// Gets the URL of the RP icon for the OP to display. /// </summary> /// <param name="realm">The realm of the RP where the authentication request originated.</param> /// <param name="webRequestHandler">The web request handler to use for discovery. /// Usually available via <see cref="Channel.WebRequestHandler">OpenIdProvider.Channel.WebRequestHandler</see>.</param> /// <returns> /// A sequence of the RP's icons it has available for the Provider to display, in decreasing preferred order. /// </returns> /// <value>The icon URL.</value> /// <remarks> /// This property is automatically set for the OP with the result of RP discovery. /// RPs should set this value by including an entry such as this in their XRDS document. /// <example> /// <Service xmlns="xri://$xrd*($v*2.0)"> /// <Type>http://specs.openid.net/extensions/ui/icon</Type> /// <URI>http://consumer.example.com/images/image.jpg</URI> /// </Service> /// </example> /// </remarks> public static IEnumerable<Uri> GetRelyingPartyIconUrls(Realm realm, IDirectWebRequestHandler webRequestHandler) { Requires.NotNull(realm, "realm"); Requires.NotNull(webRequestHandler, "webRequestHandler"); ErrorUtilities.VerifyArgumentNotNull(realm, "realm"); ErrorUtilities.VerifyArgumentNotNull(webRequestHandler, "webRequestHandler"); XrdsDocument xrds = realm.Discover(webRequestHandler, false); if (xrds == null) { return Enumerable.Empty<Uri>(); } else { return xrds.FindRelyingPartyIcons(); } }
/// <summary> /// Downloads the XRDS document for this XRI. /// </summary> /// <param name="identifier">The identifier.</param> /// <param name="requestHandler">The request handler.</param> /// <returns>The XRDS document.</returns> private static XrdsDocument DownloadXrds(XriIdentifier identifier, IDirectWebRequestHandler requestHandler) { Requires.NotNull(identifier, "identifier"); Requires.NotNull(requestHandler, "requestHandler"); XrdsDocument doc; using (var xrdsResponse = Yadis.Request(requestHandler, GetXrdsUrl(identifier), identifier.IsDiscoverySecureEndToEnd)) { var readerSettings = MessagingUtilities.CreateUntrustedXmlReaderSettings(); doc = new XrdsDocument(XmlReader.Create(xrdsResponse.ResponseStream, readerSettings)); } ErrorUtilities.VerifyProtocol(doc.IsXrdResolutionSuccessful, OpenIdStrings.XriResolutionFailed); return(doc); }
/// <summary> /// Downloads the XRDS document for this XRI. /// </summary> /// <param name="identifier">The identifier.</param> /// <param name="requestHandler">The request handler.</param> /// <returns>The XRDS document.</returns> private static XrdsDocument DownloadXrds(XriIdentifier identifier, IDirectWebRequestHandler requestHandler) { Requires.NotNull(identifier, "identifier"); Requires.NotNull(requestHandler, "requestHandler"); Contract.Ensures(Contract.Result <XrdsDocument>() != 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); }
/// <summary> /// Gets a value indicating whether verification of the return URL claimed by the Relying Party /// succeeded. /// </summary> /// <param name="requestHandler">The request handler.</param> /// <returns> /// Result of realm discovery. /// </returns> private RelyingPartyDiscoveryResult IsReturnUrlDiscoverableCore(IDirectWebRequestHandler requestHandler) { Requires.NotNull(requestHandler, "requestHandler"); ErrorUtilities.VerifyInternal(this.Realm != null, "Realm should have been read or derived by now."); try { if (this.SecuritySettings.RequireSsl && this.Realm.Scheme != Uri.UriSchemeHttps) { Logger.OpenId.WarnFormat("RP discovery failed because RequireSsl is true and RP discovery would begin at insecure URL {0}.", this.Realm); return(RelyingPartyDiscoveryResult.NoServiceDocument); } var returnToEndpoints = this.Realm.DiscoverReturnToEndpoints(requestHandler, false); if (returnToEndpoints == null) { return(RelyingPartyDiscoveryResult.NoServiceDocument); } foreach (var returnUrl in returnToEndpoints) { Realm discoveredReturnToUrl = returnUrl.ReturnToEndpoint; // The spec requires that the return_to URLs given in an RPs XRDS doc // do not contain wildcards. if (discoveredReturnToUrl.DomainWildcard) { Logger.Yadis.WarnFormat("Realm {0} contained return_to URL {1} which contains a wildcard, which is not allowed.", Realm, discoveredReturnToUrl); continue; } // Use the same rules as return_to/realm matching to check whether this // URL fits the return_to URL we were given. if (discoveredReturnToUrl.Contains(this.RequestMessage.ReturnTo)) { // no need to keep looking after we find a match return(RelyingPartyDiscoveryResult.Success); } } } catch (ProtocolException ex) { // Don't do anything else. We quietly fail at return_to verification and return false. Logger.Yadis.InfoFormat("Relying party discovery at URL {0} failed. {1}", Realm, ex); return(RelyingPartyDiscoveryResult.NoServiceDocument); } catch (WebException ex) { // Don't do anything else. We quietly fail at return_to verification and return false. Logger.Yadis.InfoFormat("Relying party discovery at URL {0} failed. {1}", Realm, ex); return(RelyingPartyDiscoveryResult.NoServiceDocument); } return(RelyingPartyDiscoveryResult.NoMatchingReturnTo); }
/// <summary> /// Performs YADIS discovery on some identifier. /// </summary> /// <param name="requestHandler">The mechanism to use for sending HTTP requests.</param> /// <param name="uri">The URI to perform discovery on.</param> /// <param name="requireSsl">Whether discovery should fail if any step of it is not encrypted.</param> /// <returns> /// The result of discovery on the given URL. /// Null may be returned if an error occurs, /// or if <paramref name="requireSsl"/> is true but part of discovery /// is not protected by SSL. /// </returns> public static DiscoveryResult Discover(IDirectWebRequestHandler requestHandler, UriIdentifier uri, bool requireSsl) { CachedDirectWebResponse response; try { if (requireSsl && !string.Equals(uri.Uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)) { Logger.Yadis.WarnFormat("Discovery on insecure identifier '{0}' aborted.", uri); return null; } response = Request(requestHandler, uri, requireSsl, ContentTypes.Html, ContentTypes.XHtml, ContentTypes.Xrds).GetSnapshot(MaximumResultToScan); if (response.Status != System.Net.HttpStatusCode.OK) { Logger.Yadis.ErrorFormat("HTTP error {0} {1} while performing discovery on {2}.", (int)response.Status, response.Status, uri); return null; } } catch (ArgumentException ex) { // Unsafe URLs generate this Logger.Yadis.WarnFormat("Unsafe OpenId URL detected ({0}). Request aborted. {1}", uri, ex); return null; } CachedDirectWebResponse response2 = null; if (IsXrdsDocument(response)) { Logger.Yadis.Debug("An XRDS response was received from GET at user-supplied identifier."); response2 = response; } else { string uriString = response.Headers.Get(HeaderName); Uri url = null; if (uriString != null) { if (Uri.TryCreate(uriString, UriKind.Absolute, out url)) { Logger.Yadis.DebugFormat("{0} found in HTTP header. Preparing to pull XRDS from {1}", HeaderName, url); } } if (url == null && response.ContentType != null && response.ContentType.MediaType == ContentTypes.Html) { url = FindYadisDocumentLocationInHtmlMetaTags(response.GetResponseString()); if (url != null) { Logger.Yadis.DebugFormat("{0} found in HTML Http-Equiv tag. Preparing to pull XRDS from {1}", HeaderName, url); } } if (url != null) { if (!requireSsl || string.Equals(url.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)) { response2 = Request(requestHandler, url, requireSsl, ContentTypes.Xrds).GetSnapshot(MaximumResultToScan); if (response2.Status != HttpStatusCode.OK) { Logger.Yadis.ErrorFormat("HTTP error {0} {1} while performing discovery on {2}.", (int)response2.Status, response2.Status, uri); return null; } } else { Logger.Yadis.WarnFormat("XRDS document at insecure location '{0}'. Aborting YADIS discovery.", url); } } } return new DiscoveryResult(uri, response, response2); }
/// <summary> /// Gets the XRDS HTTP response for a given identifier. /// </summary> /// <param name="identifier">The identifier.</param> /// <param name="requestHandler">The request handler.</param> /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param> /// <returns>A HTTP response carrying an XRDS document, or <c>null</c> if one could not be obtained.</returns> /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception> private IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) { Contract.Requires <ArgumentNullException>(identifier != null); Contract.Requires <ArgumentNullException>(requestHandler != null); Uri xrdsLocation = this.GetXrdsLocation(identifier, requestHandler, out signingHost); if (xrdsLocation == null) { return(null); } var response = GetXrdsResponse(identifier, requestHandler, xrdsLocation); return(response); }
/// <summary> /// Gets the XRDS HTTP response for a given identifier. /// </summary> /// <param name="identifier">The identifier.</param> /// <param name="requestHandler">The request handler.</param> /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param> /// <returns>A HTTP response carrying an XRDS document, or <c>null</c> if one could not be obtained.</returns> /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception> private IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) { Requires.NotNull(identifier, "identifier"); Requires.NotNull(requestHandler, "requestHandler"); Uri xrdsLocation = this.GetXrdsLocation(identifier, requestHandler, out signingHost); if (xrdsLocation == null) { return(null); } var response = GetXrdsResponse(identifier, requestHandler, xrdsLocation); return(response); }
/// <summary> /// Initializes a new instance of the <see cref="WrappingWebRequestHandler"/> class. /// </summary> /// <param name="wrappedHandler">The HTTP handler to wrap.</param> /// <param name="action">The action to perform on outgoing HTTP requests.</param> internal WrappingWebRequestHandler(IDirectWebRequestHandler wrappedHandler, Action <HttpWebRequest> action) { if (wrappedHandler == null) { throw new ArgumentNullException("wrappedHandler"); } if (action == null) { throw new ArgumentNullException("action"); } _wrappedHandler = wrappedHandler; _action = action; }
/// <summary> /// Performs discovery on the Identifier. /// </summary> /// <param name="requestHandler">The web request handler to use for discovery.</param> /// <returns> /// An initialized structure containing the discovered provider endpoint information. /// </returns> internal override IEnumerable <ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler) { List <ServiceEndpoint> endpoints = new List <ServiceEndpoint>(); // Attempt YADIS discovery DiscoveryResult yadisResult = Yadis.Discover(requestHandler, this, IsDiscoverySecureEndToEnd); if (yadisResult != null) { if (yadisResult.IsXrds) { XrdsDocument xrds = new XrdsDocument(yadisResult.ResponseText); var xrdsEndpoints = xrds.CreateServiceEndpoints(yadisResult.NormalizedUri, this); // Filter out insecure endpoints if high security is required. if (IsDiscoverySecureEndToEnd) { xrdsEndpoints = xrdsEndpoints.Where(se => se.IsSecure); } endpoints.AddRange(xrdsEndpoints); } // Failing YADIS discovery of an XRDS document, we try HTML discovery. if (endpoints.Count == 0) { var htmlEndpoints = new List <ServiceEndpoint>(DiscoverFromHtml(yadisResult.NormalizedUri, this, yadisResult.ResponseText)); if (htmlEndpoints.Any()) { Logger.DebugFormat("Total services discovered in HTML: {0}", htmlEndpoints.Count); Logger.Debug(htmlEndpoints.ToStringDeferred(true)); endpoints.AddRange(htmlEndpoints.Where(ep => !IsDiscoverySecureEndToEnd || ep.IsSecure)); if (endpoints.Count == 0) { Logger.Info("No HTML discovered endpoints met the security requirements."); } } else { Logger.Debug("HTML discovery failed to find any endpoints."); } } else { Logger.Debug("Skipping HTML discovery because XRDS contained service endpoints."); } } return(endpoints); }
/// <summary> /// Performs discovery on the specified identifier. /// </summary> /// <param name="identifier">The identifier to perform discovery on.</param> /// <param name="requestHandler">The means to place outgoing HTTP requests.</param> /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param> /// <returns> /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. /// </returns> public IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) { abortDiscoveryChain = false; var uriIdentifier = identifier as UriIdentifier; if (uriIdentifier == null) { return Enumerable.Empty<IdentifierDiscoveryResult>(); } var endpoints = new List<IdentifierDiscoveryResult>(); // Attempt YADIS discovery DiscoveryResult yadisResult = Yadis.Discover(requestHandler, uriIdentifier, identifier.IsDiscoverySecureEndToEnd); if (yadisResult != null) { if (yadisResult.IsXrds) { try { XrdsDocument xrds = new XrdsDocument(yadisResult.ResponseText); var xrdsEndpoints = xrds.XrdElements.CreateServiceEndpoints(yadisResult.NormalizedUri, uriIdentifier); // Filter out insecure endpoints if high security is required. if (uriIdentifier.IsDiscoverySecureEndToEnd) { xrdsEndpoints = xrdsEndpoints.Where(se => se.ProviderEndpoint.IsTransportSecure()); } endpoints.AddRange(xrdsEndpoints); } catch (XmlException ex) { Logger.Yadis.Error("Error while parsing the XRDS document. Falling back to HTML discovery.", ex); } } // Failing YADIS discovery of an XRDS document, we try HTML discovery. if (endpoints.Count == 0) { yadisResult.TryRevertToHtmlResponse(); var htmlEndpoints = new List<IdentifierDiscoveryResult>(DiscoverFromHtml(yadisResult.NormalizedUri, uriIdentifier, yadisResult.ResponseText)); if (htmlEndpoints.Any()) { Logger.Yadis.DebugFormat("Total services discovered in HTML: {0}", htmlEndpoints.Count); Logger.Yadis.Debug(htmlEndpoints.ToStringDeferred(true)); endpoints.AddRange(htmlEndpoints.Where(ep => !uriIdentifier.IsDiscoverySecureEndToEnd || ep.ProviderEndpoint.IsTransportSecure())); if (endpoints.Count == 0) { Logger.Yadis.Info("No HTML discovered endpoints met the security requirements."); } } else { Logger.Yadis.Debug("HTML discovery failed to find any endpoints."); } } else { Logger.Yadis.Debug("Skipping HTML discovery because XRDS contained service endpoints."); } } return endpoints; }
/// <summary> /// Searches for an XRDS document at the realm URL. /// </summary> /// <param name="requestHandler">The mechanism to use for sending HTTP requests.</param> /// <param name="allowRedirects">Whether redirects may be followed when discovering the Realm. /// This may be true when creating an unsolicited assertion, but must be /// false when performing return URL verification per 2.0 spec section 9.2.1.</param> /// <returns> /// The XRDS document if found; or <c>null</c> if no service document was discovered. /// </returns> internal virtual XrdsDocument Discover(IDirectWebRequestHandler requestHandler, bool allowRedirects) { // Attempt YADIS discovery DiscoveryResult yadisResult = Yadis.Discover(requestHandler, this.UriWithWildcardChangedToWww, false); if (yadisResult != null) { // Detect disallowed redirects, since realm discovery never allows them for security. ErrorUtilities.VerifyProtocol(allowRedirects || yadisResult.NormalizedUri == yadisResult.RequestUri, OpenIdStrings.RealmCausedRedirectUponDiscovery, yadisResult.RequestUri); if (yadisResult.IsXrds) { try { return new XrdsDocument(yadisResult.ResponseText); } catch (XmlException ex) { throw ErrorUtilities.Wrap(ex, XrdsStrings.InvalidXRDSDocument); } } } return null; }
/// <summary> /// Initializes a new instance of the <see cref="UntrustedWebRequestHandler"/> class. /// </summary> /// <param name="chainedWebRequestHandler">The chained web request handler.</param> public UntrustedWebRequestHandler(IDirectWebRequestHandler chainedWebRequestHandler) { Requires.NotNull(chainedWebRequestHandler, "chainedWebRequestHandler"); this.chainedWebRequestHandler = chainedWebRequestHandler; if (Debugger.IsAttached) { // Since a debugger is attached, requests may be MUCH slower, // so give ourselves huge timeouts. this.ReadWriteTimeout = TimeSpan.FromHours(1); this.Timeout = TimeSpan.FromHours(1); } else { this.ReadWriteTimeout = Configuration.ReadWriteTimeout; this.Timeout = Configuration.Timeout; } }
/// <summary> /// Gets the URL of the RP icon for the OP to display. /// </summary> /// <param name="realm">The realm of the RP where the authentication request originated.</param> /// <param name="webRequestHandler">The web request handler to use for discovery. /// Usually available via <see cref="Channel.WebRequestHandler">OpenIdProvider.Channel.WebRequestHandler</see>.</param> /// <returns> /// A sequence of the RP's icons it has available for the Provider to display, in decreasing preferred order. /// </returns> /// <value>The icon URL.</value> /// <remarks> /// This property is automatically set for the OP with the result of RP discovery. /// RPs should set this value by including an entry such as this in their XRDS document. /// <example> /// <Service xmlns="xri://$xrd*($v*2.0)"> /// <Type>http://specs.openid.net/extensions/ui/icon</Type> /// <URI>http://consumer.example.com/images/image.jpg</URI> /// </Service> /// </example> /// </remarks> public static IEnumerable <Uri> GetRelyingPartyIconUrls(Realm realm, IDirectWebRequestHandler webRequestHandler) { Contract.Requires(realm != null); Contract.Requires(webRequestHandler != null); ErrorUtilities.VerifyArgumentNotNull(realm, "realm"); ErrorUtilities.VerifyArgumentNotNull(webRequestHandler, "webRequestHandler"); XrdsDocument xrds = realm.Discover(webRequestHandler, false); if (xrds == null) { return(Enumerable.Empty <Uri>()); } else { return(xrds.FindRelyingPartyIcons()); } }
/// <summary> /// Sends a YADIS HTTP request as part of identifier discovery. /// </summary> /// <param name="requestHandler">The request handler to use to actually submit the request.</param> /// <param name="uri">The URI to GET.</param> /// <param name="requireSsl">Whether only HTTPS URLs should ever be retrieved.</param> /// <param name="acceptTypes">The value of the Accept HTTP header to include in the request.</param> /// <returns>The HTTP response retrieved from the request.</returns> internal static IncomingWebResponse Request(IDirectWebRequestHandler requestHandler, Uri uri, bool requireSsl, params string[] acceptTypes) { Contract.Requires <ArgumentNullException>(requestHandler != null); Contract.Requires <ArgumentNullException>(uri != null); Contract.Ensures(Contract.Result <IncomingWebResponse>() != null); Contract.Ensures(Contract.Result <IncomingWebResponse>().ResponseStream != null); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); request.CachePolicy = IdentifierDiscoveryCachePolicy; if (acceptTypes != null) { request.Accept = string.Join(",", acceptTypes); } DirectWebRequestOptions options = DirectWebRequestOptions.None; if (requireSsl) { options |= DirectWebRequestOptions.RequireSsl; } try { return(requestHandler.GetResponse(request, options)); } catch (ProtocolException ex) { var webException = ex.InnerException as WebException; if (webException != null) { var response = webException.Response as HttpWebResponse; if (response != null && response.IsFromCache) { // We don't want to report error responses from the cache, since the server may have fixed // whatever was causing the problem. So try again with cache disabled. Logger.Messaging.Error("An HTTP error response was obtained from the cache. Retrying with cache disabled.", ex); var nonCachingRequest = request.Clone(); nonCachingRequest.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.Reload); return(requestHandler.GetResponse(nonCachingRequest, options)); } } throw; } }
/// <summary> /// Gets the XRDS HTTP response for a given identifier. /// </summary> /// <param name="identifier">The identifier.</param> /// <param name="requestHandler">The request handler.</param> /// <param name="xrdsLocation">The location of the XRDS document to retrieve.</param> /// <returns> /// A HTTP response carrying an XRDS document. /// </returns> /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception> private static IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, Uri xrdsLocation) { Requires.NotNull(identifier, "identifier"); Requires.NotNull(requestHandler, "requestHandler"); Requires.NotNull(xrdsLocation, "xrdsLocation"); var request = (HttpWebRequest)WebRequest.Create(xrdsLocation); request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy; request.Accept = ContentTypes.Xrds; var options = identifier.IsDiscoverySecureEndToEnd ? DirectWebRequestOptions.RequireSsl : DirectWebRequestOptions.None; var response = requestHandler.GetResponse(request, options).GetSnapshot(Yadis.MaximumResultToScan); if (!string.Equals(response.ContentType.MediaType, ContentTypes.Xrds, StringComparison.Ordinal)) { Logger.Yadis.WarnFormat("Host-meta pointed to XRDS at {0}, but Content-Type at that URL was unexpected value '{1}'.", xrdsLocation, response.ContentType); } return(response); }
/// <summary> /// Gets the XRDS HTTP response for a given identifier. /// </summary> /// <param name="identifier">The identifier.</param> /// <param name="requestHandler">The request handler.</param> /// <param name="xrdsLocation">The location of the XRDS document to retrieve.</param> /// <returns> /// A HTTP response carrying an XRDS document. /// </returns> /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception> private static IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, Uri xrdsLocation) { Contract.Requires <ArgumentNullException>(identifier != null); Contract.Requires <ArgumentNullException>(requestHandler != null); Contract.Requires <ArgumentNullException>(xrdsLocation != null); Contract.Ensures(Contract.Result <IncomingWebResponse>() != null); var request = (HttpWebRequest)WebRequest.Create(xrdsLocation); request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy; request.Accept = ContentTypes.Xrds; var options = identifier.IsDiscoverySecureEndToEnd ? DirectWebRequestOptions.RequireSsl : DirectWebRequestOptions.None; var response = requestHandler.GetResponse(request, options); if (!string.Equals(response.ContentType.MediaType, ContentTypes.Xrds, StringComparison.Ordinal)) { Logger.Yadis.WarnFormat("Host-meta pointed to XRDS at {0}, but Content-Type at that URL was unexpected value '{1}'.", xrdsLocation, response.ContentType); } return(response); }
/// <summary> /// Searches for an XRDS document at the realm URL, and if found, searches /// for a description of a relying party endpoints (OpenId login pages). /// </summary> /// <param name="requestHandler">The mechanism to use for sending HTTP requests.</param> /// <param name="allowRedirects">Whether redirects may be followed when discovering the Realm. /// This may be true when creating an unsolicited assertion, but must be /// false when performing return URL verification per 2.0 spec section 9.2.1.</param> /// <returns> /// The details of the endpoints if found, otherwise null. /// </returns> internal IEnumerable <RelyingPartyEndpointDescription> Discover(IDirectWebRequestHandler requestHandler, bool allowRedirects) { // Attempt YADIS discovery DiscoveryResult yadisResult = Yadis.Discover(requestHandler, this.UriWithWildcardChangedToWww, false); if (yadisResult != null) { // Detect disallowed redirects, since realm discovery never allows them for security. ErrorUtilities.VerifyProtocol(allowRedirects || yadisResult.NormalizedUri == yadisResult.RequestUri, OpenIdStrings.RealmCausedRedirectUponDiscovery, yadisResult.RequestUri); if (yadisResult.IsXrds) { try { XrdsDocument xrds = new XrdsDocument(yadisResult.ResponseText); return(xrds.FindRelyingPartyReceivingEndpoints()); } catch (XmlException ex) { throw ErrorUtilities.Wrap(ex, XrdsStrings.InvalidXRDSDocument); } } } return(Enumerable.Empty <RelyingPartyEndpointDescription>()); }
/// <summary> /// Sends a YADIS HTTP request as part of identifier discovery. /// </summary> /// <param name="requestHandler">The request handler to use to actually submit the request.</param> /// <param name="uri">The URI to GET.</param> /// <param name="requireSsl">Whether only HTTPS URLs should ever be retrieved.</param> /// <param name="acceptTypes">The value of the Accept HTTP header to include in the request.</param> /// <returns>The HTTP response retrieved from the request.</returns> internal static DirectWebResponse Request(IDirectWebRequestHandler requestHandler, Uri uri, bool requireSsl, params string[] acceptTypes) { ErrorUtilities.VerifyArgumentNotNull(requestHandler, "requestHandler"); ErrorUtilities.VerifyArgumentNotNull(uri, "uri"); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); request.CachePolicy = IdentifierDiscoveryCachePolicy; if (acceptTypes != null) { request.Accept = string.Join(",", acceptTypes); } DirectWebRequestOptions options = DirectWebRequestOptions.None; if (requireSsl) { options |= DirectWebRequestOptions.RequireSsl; } return(requestHandler.GetResponse(request, options)); }
/// <summary> /// Sends a YADIS HTTP request as part of identifier discovery. /// </summary> /// <param name="requestHandler">The request handler to use to actually submit the request.</param> /// <param name="uri">The URI to GET.</param> /// <param name="requireSsl">Whether only HTTPS URLs should ever be retrieved.</param> /// <param name="acceptTypes">The value of the Accept HTTP header to include in the request.</param> /// <returns>The HTTP response retrieved from the request.</returns> internal static IncomingWebResponse Request(IDirectWebRequestHandler requestHandler, Uri uri, bool requireSsl, params string[] acceptTypes) { Contract.Requires <ArgumentNullException>(requestHandler != null); Contract.Requires <ArgumentNullException>(uri != null); Contract.Ensures(Contract.Result <IncomingWebResponse>() != null); Contract.Ensures(Contract.Result <IncomingWebResponse>().ResponseStream != null); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); request.CachePolicy = IdentifierDiscoveryCachePolicy; if (acceptTypes != null) { request.Accept = string.Join(",", acceptTypes); } DirectWebRequestOptions options = DirectWebRequestOptions.None; if (requireSsl) { options |= DirectWebRequestOptions.RequireSsl; } return(requestHandler.GetResponse(request, options)); }
/// <summary> /// Gets the host-meta for a given identifier. /// </summary> /// <param name="identifier">The identifier.</param> /// <param name="requestHandler">The request handler.</param> /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param> /// <returns> /// The host-meta response, or <c>null</c> if no host-meta document could be obtained. /// </returns> private IncomingWebResponse GetHostMeta(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) { Requires.NotNull(identifier, "identifier"); Requires.NotNull(requestHandler, "requestHandler"); foreach (var hostMetaProxy in this.GetHostMetaLocations(identifier)) { var hostMetaLocation = hostMetaProxy.GetProxy(identifier); var request = (HttpWebRequest)WebRequest.Create(hostMetaLocation); request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy; var options = DirectWebRequestOptions.AcceptAllHttpResponses; if (identifier.IsDiscoverySecureEndToEnd) { options |= DirectWebRequestOptions.RequireSsl; } var response = requestHandler.GetResponse(request, options).GetSnapshot(Yadis.MaximumResultToScan); try { if (response.Status == HttpStatusCode.OK) { Logger.Yadis.InfoFormat("Found host-meta for {0} at: {1}", identifier.Uri.Host, hostMetaLocation); signingHost = hostMetaProxy.GetSigningHost(identifier); return(response); } else { Logger.Yadis.InfoFormat("Could not obtain host-meta for {0} from {1}", identifier.Uri.Host, hostMetaLocation); response.Dispose(); } } catch { response.Dispose(); throw; } } signingHost = null; return(null); }
/// <summary> /// Gets the host-meta for a given identifier. /// </summary> /// <param name="identifier">The identifier.</param> /// <param name="requestHandler">The request handler.</param> /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param> /// <returns> /// The host-meta response, or <c>null</c> if no host-meta document could be obtained. /// </returns> private IncomingWebResponse GetHostMeta(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) { Contract.Requires<ArgumentNullException>(identifier != null); Contract.Requires<ArgumentNullException>(requestHandler != null); foreach (var hostMetaProxy in this.GetHostMetaLocations(identifier)) { var hostMetaLocation = hostMetaProxy.GetProxy(identifier); var request = (HttpWebRequest)WebRequest.Create(hostMetaLocation); request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy; var options = DirectWebRequestOptions.AcceptAllHttpResponses; if (identifier.IsDiscoverySecureEndToEnd) { options |= DirectWebRequestOptions.RequireSsl; } var response = requestHandler.GetResponse(request, options).GetSnapshot(Yadis.MaximumResultToScan); try { if (response.Status == HttpStatusCode.OK) { Logger.Yadis.InfoFormat("Found host-meta for {0} at: {1}", identifier.Uri.Host, hostMetaLocation); signingHost = hostMetaProxy.GetSigningHost(identifier); return response; } else { Logger.Yadis.InfoFormat("Could not obtain host-meta for {0} from {1}", identifier.Uri.Host, hostMetaLocation); response.Dispose(); } } catch { response.Dispose(); throw; } } signingHost = null; return null; }
/// <summary> /// Gets the location of the XRDS document that describes a given identifier. /// </summary> /// <param name="identifier">The identifier under discovery.</param> /// <param name="requestHandler">The request handler.</param> /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param> /// <returns>An absolute URI, or <c>null</c> if one could not be determined.</returns> private Uri GetXrdsLocation(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) { Contract.Requires<ArgumentNullException>(identifier != null); Contract.Requires<ArgumentNullException>(requestHandler != null); using (var hostMetaResponse = this.GetHostMeta(identifier, requestHandler, out signingHost)) { if (hostMetaResponse == null) { return null; } using (var sr = hostMetaResponse.GetResponseReader()) { string line = sr.ReadLine(); Match m = HostMetaLink.Match(line); if (m.Success) { Uri location = new Uri(m.Groups["location"].Value); Logger.Yadis.InfoFormat("Found link to XRDS at {0} in host-meta document {1}.", location, hostMetaResponse.FinalUri); return location; } } Logger.Yadis.WarnFormat("Could not find link to XRDS in host-meta document: {0}", hostMetaResponse.FinalUri); return null; } }
/// <summary> /// Gets a value indicating whether verification of the return URL claimed by the Relying Party /// succeeded. /// </summary> /// <param name="requestHandler">The request handler.</param> /// <returns> /// Result of realm discovery. /// </returns> private RelyingPartyDiscoveryResult IsReturnUrlDiscoverableCore(IDirectWebRequestHandler requestHandler) { Requires.NotNull(requestHandler, "requestHandler"); ErrorUtilities.VerifyInternal(this.Realm != null, "Realm should have been read or derived by now."); try { if (this.SecuritySettings.RequireSsl && this.Realm.Scheme != Uri.UriSchemeHttps) { Logger.OpenId.WarnFormat("RP discovery failed because RequireSsl is true and RP discovery would begin at insecure URL {0}.", this.Realm); return RelyingPartyDiscoveryResult.NoServiceDocument; } var returnToEndpoints = this.Realm.DiscoverReturnToEndpoints(requestHandler, false); if (returnToEndpoints == null) { return RelyingPartyDiscoveryResult.NoServiceDocument; } foreach (var returnUrl in returnToEndpoints) { Realm discoveredReturnToUrl = returnUrl.ReturnToEndpoint; // The spec requires that the return_to URLs given in an RPs XRDS doc // do not contain wildcards. if (discoveredReturnToUrl.DomainWildcard) { Logger.Yadis.WarnFormat("Realm {0} contained return_to URL {1} which contains a wildcard, which is not allowed.", Realm, discoveredReturnToUrl); continue; } // Use the same rules as return_to/realm matching to check whether this // URL fits the return_to URL we were given. if (discoveredReturnToUrl.Contains(this.RequestMessage.ReturnTo)) { // no need to keep looking after we find a match return RelyingPartyDiscoveryResult.Success; } } } catch (ProtocolException ex) { // Don't do anything else. We quietly fail at return_to verification and return false. Logger.Yadis.InfoFormat("Relying party discovery at URL {0} failed. {1}", Realm, ex); return RelyingPartyDiscoveryResult.NoServiceDocument; } catch (WebException ex) { // Don't do anything else. We quietly fail at return_to verification and return false. Logger.Yadis.InfoFormat("Relying party discovery at URL {0} failed. {1}", Realm, ex); return RelyingPartyDiscoveryResult.NoServiceDocument; } return RelyingPartyDiscoveryResult.NoMatchingReturnTo; }
/// <summary> /// Performs discovery on the Identifier. /// </summary> /// <param name="requestHandler">The web request handler to use for discovery.</param> /// <returns> /// An initialized structure containing the discovered provider endpoint information. /// </returns> internal override IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler) { return this.DownloadXrds(requestHandler).CreateServiceEndpoints(this); }
/// <summary> /// Initializes a new instance of the <see cref="WrappingWebRequestHandler"/> class. /// </summary> /// <param name="wrappedHandler">The HTTP handler to wrap.</param> /// <param name="action">The action to perform on outgoing HTTP requests.</param> internal WrappingWebRequestHandler(IDirectWebRequestHandler wrappedHandler, Action<HttpWebRequest> action) { if (wrappedHandler == null) { throw new ArgumentNullException("wrappedHandler"); } if (action == null) { throw new ArgumentNullException("action"); } this.wrappedHandler = wrappedHandler; this.action = action; }
/// <summary> /// Gets the services for an identifier that are described by an external XRDS document. /// </summary> /// <param name="xrds">The XRD elements to search for described-by services.</param> /// <param name="identifier">The identifier under discovery.</param> /// <param name="requestHandler">The request handler.</param> /// <returns>The discovered services.</returns> private static IEnumerable <IdentifierDiscoveryResult> GetExternalServices(IEnumerable <XrdElement> xrds, UriIdentifier identifier, IDirectWebRequestHandler requestHandler) { Contract.Requires <ArgumentNullException>(xrds != null); Contract.Requires <ArgumentNullException>(identifier != null); Contract.Requires <ArgumentNullException>(requestHandler != null); Contract.Ensures(Contract.Result <IEnumerable <IdentifierDiscoveryResult> >() != null); var results = new List <IdentifierDiscoveryResult>(); foreach (var serviceElement in GetDescribedByServices(xrds)) { var templateNode = serviceElement.Node.SelectSingleNode("google:URITemplate", serviceElement.XmlNamespaceResolver); var nextAuthorityNode = serviceElement.Node.SelectSingleNode("google:NextAuthority", serviceElement.XmlNamespaceResolver); if (templateNode != null) { Uri externalLocation = new Uri(templateNode.Value.Trim().Replace("{%uri}", Uri.EscapeDataString(identifier.Uri.AbsoluteUri))); string nextAuthority = nextAuthorityNode != null?nextAuthorityNode.Value.Trim() : identifier.Uri.Host; try { var externalXrdsResponse = GetXrdsResponse(identifier, requestHandler, externalLocation); XrdsDocument externalXrds = new XrdsDocument(XmlReader.Create(externalXrdsResponse.ResponseStream)); ValidateXmlDSig(externalXrds, identifier, externalXrdsResponse, nextAuthority); results.AddRange(GetXrdElements(externalXrds, identifier).CreateServiceEndpoints(identifier, identifier)); } catch (ProtocolException ex) { Logger.Yadis.WarnFormat("HTTP GET error while retrieving described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex); } catch (XmlException ex) { Logger.Yadis.ErrorFormat("Error while parsing described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex); } } } return(results); }
/// <summary> /// Performs discovery on the specified identifier. /// </summary> /// <param name="identifier">The identifier to perform discovery on.</param> /// <param name="requestHandler">The means to place outgoing HTTP requests.</param> /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param> /// <returns> /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. /// </returns> IEnumerable<IdentifierDiscoveryResult> IIdentifierDiscoveryService.Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) { Contract.Requires<ArgumentNullException>(identifier != null); Contract.Requires<ArgumentNullException>(requestHandler != null); Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null); throw new NotImplementedException(); }
/// <summary> /// Searches for an XRDS document at the realm URL, and if found, searches /// for a description of a relying party endpoints (OpenId login pages). /// </summary> /// <param name="requestHandler">The mechanism to use for sending HTTP requests.</param> /// <param name="allowRedirects">Whether redirects may be followed when discovering the Realm. /// This may be true when creating an unsolicited assertion, but must be /// false when performing return URL verification per 2.0 spec section 9.2.1.</param> /// <returns> /// The details of the endpoints if found; or <c>null</c> if no service document was discovered. /// </returns> internal virtual IEnumerable<RelyingPartyEndpointDescription> DiscoverReturnToEndpoints(IDirectWebRequestHandler requestHandler, bool allowRedirects) { XrdsDocument xrds = this.Discover(requestHandler, allowRedirects); if (xrds != null) { return xrds.FindRelyingPartyReceivingEndpoints(); } return null; }
/// <summary> /// Searches for an XRDS document at the realm URL, and if found, searches /// for a description of a relying party endpoints (OpenId login pages). /// </summary> /// <param name="requestHandler">The mechanism to use for sending HTTP requests.</param> /// <param name="allowRedirects">Whether redirects may be followed when discovering the Realm. /// This may be true when creating an unsolicited assertion, but must be /// false when performing return URL verification per 2.0 spec section 9.2.1.</param> /// <returns> /// The details of the endpoints if found, otherwise null. /// </returns> internal override IEnumerable <RelyingPartyEndpointDescription> DiscoverReturnToEndpoints(IDirectWebRequestHandler requestHandler, bool allowRedirects) { return(this.relyingPartyDescriptions); }
/// <summary> /// Initializes a new instance of the <see cref="UntrustedWebRequestHandler"/> class. /// </summary> /// <param name="chainedWebRequestHandler">The chained web request handler.</param> public UntrustedWebRequestHandler(IDirectWebRequestHandler chainedWebRequestHandler) { ErrorUtilities.VerifyArgumentNotNull(chainedWebRequestHandler, "chainedWebRequestHandler"); this.chainedWebRequestHandler = chainedWebRequestHandler; if (Debugger.IsAttached) { // Since a debugger is attached, requests may be MUCH slower, // so give ourselves huge timeouts. this.ReadWriteTimeout = TimeSpan.FromHours(1); this.Timeout = TimeSpan.FromHours(1); } else { this.ReadWriteTimeout = Configuration.ReadWriteTimeout; this.Timeout = Configuration.Timeout; } }
/// <summary> /// Performs discovery on the Identifier. /// </summary> /// <param name="requestHandler">The web request handler to use for discovery.</param> /// <returns> /// An initialized structure containing the discovered provider endpoint information. /// </returns> internal abstract IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler);
/// <summary> /// Performs YADIS discovery on some identifier. /// </summary> /// <param name="requestHandler">The mechanism to use for sending HTTP requests.</param> /// <param name="uri">The URI to perform discovery on.</param> /// <param name="requireSsl">Whether discovery should fail if any step of it is not encrypted.</param> /// <returns> /// The result of discovery on the given URL. /// Null may be returned if an error occurs, /// or if <paramref name="requireSsl"/> is true but part of discovery /// is not protected by SSL. /// </returns> public static DiscoveryResult Discover(IDirectWebRequestHandler requestHandler, UriIdentifier uri, bool requireSsl) { CachedDirectWebResponse response; try { if (requireSsl && !string.Equals(uri.Uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)) { Logger.WarnFormat("Discovery on insecure identifier '{0}' aborted.", uri); return(null); } response = Request(requestHandler, uri, requireSsl, ContentTypes.Html, ContentTypes.XHtml, ContentTypes.Xrds).GetSnapshot(MaximumResultToScan); if (response.Status != System.Net.HttpStatusCode.OK) { Logger.ErrorFormat("HTTP error {0} {1} while performing discovery on {2}.", (int)response.Status, response.Status, uri); return(null); } } catch (ArgumentException ex) { // Unsafe URLs generate this Logger.WarnFormat("Unsafe OpenId URL detected ({0}). Request aborted. {1}", uri, ex); return(null); } CachedDirectWebResponse response2 = null; if (IsXrdsDocument(response)) { Logger.Debug("An XRDS response was received from GET at user-supplied identifier."); response2 = response; } else { string uriString = response.Headers.Get(HeaderName); Uri url = null; if (uriString != null) { if (Uri.TryCreate(uriString, UriKind.Absolute, out url)) { Logger.DebugFormat("{0} found in HTTP header. Preparing to pull XRDS from {1}", HeaderName, url); } } if (url == null && response.ContentType.MediaType == ContentTypes.Html) { url = FindYadisDocumentLocationInHtmlMetaTags(response.Body); if (url != null) { Logger.DebugFormat("{0} found in HTML Http-Equiv tag. Preparing to pull XRDS from {1}", HeaderName, url); } } if (url != null) { if (!requireSsl || string.Equals(url.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)) { response2 = Request(requestHandler, url, requireSsl, ContentTypes.Xrds).GetSnapshot(MaximumResultToScan); if (response2.Status != HttpStatusCode.OK) { Logger.ErrorFormat("HTTP error {0} {1} while performing discovery on {2}.", (int)response2.Status, response2.Status, uri); return(null); } } else { Logger.WarnFormat("XRDS document at insecure location '{0}'. Aborting YADIS discovery.", url); } } } return(new DiscoveryResult(uri, response, response2)); }
/// <summary> /// Gets a value indicating whether verification of the return URL claimed by the Relying Party /// succeeded. /// </summary> /// <param name="requestHandler">The request handler.</param> /// <returns> /// Result of realm discovery. /// </returns> /// <remarks> /// Return URL verification is only attempted if this property is queried. /// The result of the verification is cached per request so calling this /// property getter multiple times in one request is not a performance hit. /// See OpenID Authentication 2.0 spec section 9.2.1. /// </remarks> public RelyingPartyDiscoveryResult IsReturnUrlDiscoverable(IDirectWebRequestHandler requestHandler) { if (!this.realmDiscoveryResult.HasValue) { this.realmDiscoveryResult = this.IsReturnUrlDiscoverableCore(requestHandler); } return this.realmDiscoveryResult.Value; }
private MockHttpRequest(IDirectWebRequestHandler mockHandler) { Requires.NotNull(mockHandler, "mockHandler"); this.MockWebRequestHandler = mockHandler; }
/// <summary> /// Performs discovery on the specified identifier. /// </summary> /// <param name="identifier">The identifier to perform discovery on.</param> /// <param name="requestHandler">The means to place outgoing HTTP requests.</param> /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param> /// <returns> /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. /// </returns> public IEnumerable <IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) { var mockIdentifier = identifier as MockIdentifier; if (mockIdentifier == null) { abortDiscoveryChain = false; return(Enumerable.Empty <IdentifierDiscoveryResult>()); } abortDiscoveryChain = true; return(mockIdentifier.DiscoveryEndpoints); }
/// <summary> /// Performs discovery on the specified identifier. /// </summary> /// <param name="identifier">The identifier to perform discovery on.</param> /// <param name="requestHandler">The means to place outgoing HTTP requests.</param> /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param> /// <returns> /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. /// </returns> public IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) { abortDiscoveryChain = false; // Google Apps are always URIs -- not XRIs. var uriIdentifier = identifier as UriIdentifier; if (uriIdentifier == null) { return Enumerable.Empty<IdentifierDiscoveryResult>(); } var results = new List<IdentifierDiscoveryResult>(); string signingHost; using (var response = GetXrdsResponse(uriIdentifier, requestHandler, out signingHost)) { if (response != null) { try { var document = new XrdsDocument(XmlReader.Create(response.ResponseStream)); ValidateXmlDSig(document, uriIdentifier, response, signingHost); var xrds = GetXrdElements(document, uriIdentifier.Uri.Host); // Look for claimed identifier template URIs for an additional XRDS document. results.AddRange(GetExternalServices(xrds, uriIdentifier, requestHandler)); // If we couldn't find any claimed identifiers, look for OP identifiers. // Normally this would be the opposite (OP Identifiers take precedence over // claimed identifiers, but for Google Apps, XRDS' always have OP Identifiers // mixed in, which the OpenID spec mandate should eclipse Claimed Identifiers, // which would break positive assertion checks). if (results.Count == 0) { results.AddRange(xrds.CreateServiceEndpoints(uriIdentifier, uriIdentifier)); } abortDiscoveryChain = true; } catch (XmlException ex) { Logger.Yadis.ErrorFormat("Error while parsing XRDS document at {0} pointed to by host-meta: {1}", response.FinalUri, ex); } } } return results; }
/// <summary> /// Performs discovery on the specified identifier. /// </summary> /// <param name="identifier">The identifier to perform discovery on.</param> /// <param name="requestHandler">The means to place outgoing HTTP requests.</param> /// <param name="abortDiscoveryChain">if set to <c>true</c>, no further discovery services will be called for this identifier.</param> /// <returns> /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. /// </returns> public IEnumerable <IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) { abortDiscoveryChain = false; // Google Apps are always URIs -- not XRIs. var uriIdentifier = identifier as UriIdentifier; if (uriIdentifier == null) { return(Enumerable.Empty <IdentifierDiscoveryResult>()); } var results = new List <IdentifierDiscoveryResult>(); string signingHost; var response = GetXrdsResponse(uriIdentifier, requestHandler, out signingHost); if (response != null) { try { var document = new XrdsDocument(XmlReader.Create(response.ResponseStream)); ValidateXmlDSig(document, uriIdentifier, response, signingHost); var xrds = GetXrdElements(document, uriIdentifier.Uri.Host); // Look for claimed identifier template URIs for an additional XRDS document. results.AddRange(GetExternalServices(xrds, uriIdentifier, requestHandler)); // If we couldn't find any claimed identifiers, look for OP identifiers. // Normally this would be the opposite (OP Identifiers take precedence over // claimed identifiers, but for Google Apps, XRDS' always have OP Identifiers // mixed in, which the OpenID spec mandate should eclipse Claimed Identifiers, // which would break positive assertion checks). if (results.Count == 0) { results.AddRange(xrds.CreateServiceEndpoints(uriIdentifier, uriIdentifier)); } abortDiscoveryChain = true; } catch (XmlException ex) { Logger.Yadis.ErrorFormat("Error while parsing XRDS document at {0} pointed to by host-meta: {1}", response.FinalUri, ex); } } return(results); }
/// <summary> /// Gets the services for an identifier that are described by an external XRDS document. /// </summary> /// <param name="xrds">The XRD elements to search for described-by services.</param> /// <param name="identifier">The identifier under discovery.</param> /// <param name="requestHandler">The request handler.</param> /// <returns>The discovered services.</returns> private static IEnumerable<IdentifierDiscoveryResult> GetExternalServices(IEnumerable<XrdElement> xrds, UriIdentifier identifier, IDirectWebRequestHandler requestHandler) { Contract.Requires<ArgumentNullException>(xrds != null); Contract.Requires<ArgumentNullException>(identifier != null); Contract.Requires<ArgumentNullException>(requestHandler != null); Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null); var results = new List<IdentifierDiscoveryResult>(); foreach (var serviceElement in GetDescribedByServices(xrds)) { var templateNode = serviceElement.Node.SelectSingleNode("google:URITemplate", serviceElement.XmlNamespaceResolver); var nextAuthorityNode = serviceElement.Node.SelectSingleNode("google:NextAuthority", serviceElement.XmlNamespaceResolver); if (templateNode != null) { Uri externalLocation = new Uri(templateNode.Value.Trim().Replace("{%uri}", Uri.EscapeDataString(identifier.Uri.AbsoluteUri))); string nextAuthority = nextAuthorityNode != null ? nextAuthorityNode.Value.Trim() : identifier.Uri.Host; try { using (var externalXrdsResponse = GetXrdsResponse(identifier, requestHandler, externalLocation)) { XrdsDocument externalXrds = new XrdsDocument(XmlReader.Create(externalXrdsResponse.ResponseStream)); ValidateXmlDSig(externalXrds, identifier, externalXrdsResponse, nextAuthority); results.AddRange(GetXrdElements(externalXrds, identifier).CreateServiceEndpoints(identifier, identifier)); } } catch (ProtocolException ex) { Logger.Yadis.WarnFormat("HTTP GET error while retrieving described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex); } catch (XmlException ex) { Logger.Yadis.ErrorFormat("Error while parsing described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex); } } } return results; }
/// <summary> /// Gets the XRDS HTTP response for a given identifier. /// </summary> /// <param name="identifier">The identifier.</param> /// <param name="requestHandler">The request handler.</param> /// <param name="xrdsLocation">The location of the XRDS document to retrieve.</param> /// <returns> /// A HTTP response carrying an XRDS document. /// </returns> /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception> private static IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, Uri xrdsLocation) { Contract.Requires<ArgumentNullException>(identifier != null); Contract.Requires<ArgumentNullException>(requestHandler != null); Contract.Requires<ArgumentNullException>(xrdsLocation != null); Contract.Ensures(Contract.Result<IncomingWebResponse>() != null); var request = (HttpWebRequest)WebRequest.Create(xrdsLocation); request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy; request.Accept = ContentTypes.Xrds; var options = identifier.IsDiscoverySecureEndToEnd ? DirectWebRequestOptions.RequireSsl : DirectWebRequestOptions.None; var response = requestHandler.GetResponse(request, options).GetSnapshot(Yadis.MaximumResultToScan); if (!string.Equals(response.ContentType.MediaType, ContentTypes.Xrds, StringComparison.Ordinal)) { Logger.Yadis.WarnFormat("Host-meta pointed to XRDS at {0}, but Content-Type at that URL was unexpected value '{1}'.", xrdsLocation, response.ContentType); } return response; }
/// <summary> /// Performs discovery on THIS identifier, but generates <see cref="ServiceEndpoint"/> /// instances that treat another given identifier as the user-supplied identifier. /// </summary> /// <param name="requestHandler">The request handler to use in discovery.</param> /// <param name="userSuppliedIdentifier">The user supplied identifier, which may differ from this XRI instance due to multiple discovery steps.</param> /// <returns>A list of service endpoints offered for this identifier.</returns> internal IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler, XriIdentifier userSuppliedIdentifier) { Contract.Requires(requestHandler != null); Contract.Requires(userSuppliedIdentifier != null); return this.DownloadXrds(requestHandler).CreateServiceEndpoints(userSuppliedIdentifier); }
/// <summary> /// Gets the XRDS HTTP response for a given identifier. /// </summary> /// <param name="identifier">The identifier.</param> /// <param name="requestHandler">The request handler.</param> /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param> /// <returns>A HTTP response carrying an XRDS document, or <c>null</c> if one could not be obtained.</returns> /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception> private IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) { Contract.Requires<ArgumentNullException>(identifier != null); Contract.Requires<ArgumentNullException>(requestHandler != null); Uri xrdsLocation = this.GetXrdsLocation(identifier, requestHandler, out signingHost); if (xrdsLocation == null) { return null; } var response = GetXrdsResponse(identifier, requestHandler, xrdsLocation); return response; }
private MockHttpRequest(IDirectWebRequestHandler mockHandler) { ErrorUtilities.VerifyArgumentNotNull(mockHandler, "mockHandler"); this.MockWebRequestHandler = mockHandler; }
/// <summary> /// Performs discovery on the Identifier. /// </summary> /// <param name="requestHandler">The web request handler to use for discovery.</param> /// <returns> /// An initialized structure containing the discovered provider endpoint information. /// </returns> internal override IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler) { Contract.Requires(requestHandler != null); Contract.Ensures(Contract.Result<IEnumerable<ServiceEndpoint>>() != null); throw new NotImplementedException(); }
private MockHttpRequest(IDirectWebRequestHandler mockHandler) { Contract.Requires<ArgumentNullException>(mockHandler != null); this.MockWebRequestHandler = mockHandler; }
/// <summary> /// Performs discovery on the Identifier. /// </summary> /// <param name="requestHandler">The web request handler to use for discovery.</param> /// <returns> /// An initialized structure containing the discovered provider endpoint information. /// </returns> internal override IEnumerable<ServiceEndpoint> Discover(IDirectWebRequestHandler requestHandler) { return Enumerable.Empty<ServiceEndpoint>(); }