/// <summary> /// Adds the specified LogoutRequest to the collection of previously /// sent requests, maintaining the imposed limit as defined by /// MaximumRequestsStored. This collection is represented as a /// queue and is attached to the user's session. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <param name="logoutRequest"> /// LogoutRequest to add to the collection. /// </param> internal static void AddSentLogoutRequest(HttpContextBase context, LogoutRequest logoutRequest) { Queue logoutRequests = GetSentLogoutRequests(context); if (logoutRequests == null) { logoutRequests = new Queue(MaximumRequestsStored); } if (logoutRequests.Count == MaximumRequestsStored) { logoutRequests.Dequeue(); } logoutRequests.Enqueue(logoutRequest); context.Session[LogoutRequestSessionAttribute] = logoutRequests; ILogger logger = LoggerFactory.GetLogger(typeof(LogoutRequestCache)); if (logger.IsInfoEnabled) { var message = new StringBuilder(); message.AppendLine("LogoutRequestCache:"); foreach (AuthnRequest a in logoutRequests) { message.AppendLine(a.Id); } logger.Info(message.ToString()); } }
/// <summary> /// Adds the specified LogoutRequest to the collection of previously /// sent requests, maintaining the imposed limit as defined by /// MaximumRequestsStored. This collection is represented as a /// queue and is attached to the user's session. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <param name="logoutRequest"> /// LogoutRequest to add to the collection. /// </param> internal static void AddSentLogoutRequest(HttpContext context, LogoutRequest logoutRequest) { Queue logoutRequests = LogoutRequestCache.GetSentLogoutRequests(context); if (logoutRequests == null) { logoutRequests = new Queue(LogoutRequestCache.MaximumRequestsStored); } if (logoutRequests.Count == LogoutRequestCache.MaximumRequestsStored) { logoutRequests.Dequeue(); } logoutRequests.Enqueue(logoutRequest); context.Session[LogoutRequestCache.LogoutRequestSessionAttribute] = logoutRequests; StringBuilder message = new StringBuilder(); message.Append("LogoutRequestCache:\r\n"); IEnumerator i = logoutRequests.GetEnumerator(); while (i.MoveNext()) { LogoutRequest l = (LogoutRequest)i.Current; message.Append(l.Id + "\r\n"); } FedletLogger.Info(message.ToString()); }
/// <summary> /// Removes the LogoutRequest from the collection of previously /// sent requests based on the provided LogoutRequest.Id value. /// This collection is represented as a queue and is attached to /// the user's session. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <param name="logoutRequestId"> /// ID of the LogoutRequest to be removed from the cache. /// </param> internal static void RemoveSentLogoutRequest(HttpContext context, string logoutRequestId) { Queue originalCache = LogoutRequestCache.GetSentLogoutRequests(context); if (originalCache != null) { Queue revisedCache = new Queue(); while (originalCache.Count > 0) { LogoutRequest temp = (LogoutRequest)originalCache.Dequeue(); if (temp.Id != logoutRequestId) { revisedCache.Enqueue(temp); } } context.Session[LogoutRequestCache.LogoutRequestSessionAttribute] = revisedCache; } }
/// <summary> /// Gets the HTML for use of submitting the LogoutRequest with POST. /// </summary> /// <param name="logoutRequest"> /// LogoutRequest to packaged for a POST. /// </param> /// <param name="idpEntityId">Entity ID of the IDP.</param> /// <param name="parameters"> /// NameVallueCollection of additional parameters. /// </param> /// <returns> /// HTML with auto-form submission with POST of the LogoutRequest /// </returns> public string GetLogoutRequestPostHtml(LogoutRequest logoutRequest, string idpEntityId, NameValueCollection parameters) { if (logoutRequest == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutRequestIsNull); } IdentityProvider idp = (IdentityProvider)this.IdentityProviders[idpEntityId]; if (idp == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound); } string sloPostLocation = idp.GetSingleLogoutServiceLocation(Saml2Constants.HttpPostProtocolBinding); if (sloPostLocation == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpSingleLogoutSvcLocNotDefined); } string relayState = null; if (parameters != null && !string.IsNullOrEmpty(parameters[Saml2Constants.RelayState])) { relayState = parameters[Saml2Constants.RelayState]; Saml2Utils.ValidateRelayState(relayState, this.ServiceProvider.RelayStateUrlList); } XmlDocument logoutRequestXml = (XmlDocument)logoutRequest.XmlDom; if (idp.WantLogoutRequestSigned) { if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias); } else { Saml2Utils.SignXml( this.ServiceProvider.SigningCertificateAlias, logoutRequestXml, logoutRequest.Id, true); } } string packagedLogoutRequest = Saml2Utils.ConvertToBase64(logoutRequestXml.InnerXml); string inputFieldFormat = "<input type=\"hidden\" name=\"{0}\" value=\"{1}\" />"; StringBuilder html = new StringBuilder(); html.Append("<html><head><title>OpenSSO - SP initiated SLO</title></head>"); html.Append("<body onload=\"document.forms[0].submit();\">"); html.Append("<form method=\"post\" action=\""); html.Append(sloPostLocation); html.Append("\">"); html.Append("<input type=\"hidden\" name=\""); html.Append(Saml2Constants.RequestParameter); html.Append("\" value=\""); html.Append(packagedLogoutRequest); html.Append("\" />"); if (!string.IsNullOrEmpty(relayState)) { html.Append(string.Format( CultureInfo.InvariantCulture, inputFieldFormat, Saml2Constants.RelayState, HttpUtility.HtmlEncode(relayState))); } html.Append("</form>"); html.Append("</body>"); html.Append("</html>"); return html.ToString(); }
/// <summary> /// Gets the LogoutRequest location along with querystring parameters /// to be used for actual browser requests. /// </summary> /// <param name="logoutRequest"> /// LogoutRequest to packaged for a redirect. /// </param> /// <param name="idpEntityId">Entity ID of the IDP.</param> /// <param name="parameters"> /// NameVallueCollection of additional parameters. /// </param> /// <returns> /// URL with query string parameter for the specified IDP. /// </returns> public string GetLogoutRequestRedirectLocation(LogoutRequest logoutRequest, string idpEntityId, NameValueCollection parameters) { if (logoutRequest == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutRequestIsNull); } IdentityProvider idp = (IdentityProvider)this.IdentityProviders[idpEntityId]; if (idp == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound); } string sloRedirectLocation = idp.GetSingleLogoutServiceLocation(Saml2Constants.HttpRedirectProtocolBinding); if (sloRedirectLocation == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpSingleLogoutSvcLocNotDefined); } string packagedLogoutRequest = Saml2Utils.CompressConvertToBase64UrlEncode(logoutRequest.XmlDom); string queryString = Saml2Constants.RequestParameter + "=" + packagedLogoutRequest; if (parameters != null && !string.IsNullOrEmpty(parameters[Saml2Constants.RelayState])) { string relayState = parameters[Saml2Constants.RelayState]; Saml2Utils.ValidateRelayState(relayState, this.ServiceProvider.RelayStateUrlList); queryString += "&" + Saml2Constants.RelayState; queryString += "=" + HttpUtility.UrlEncode(relayState); } if (idp.WantLogoutRequestSigned) { if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias); } else { queryString += "&" + Saml2Constants.SignatureAlgorithm; queryString += "=" + HttpUtility.UrlEncode(Saml2Constants.SignatureAlgorithmRsa); queryString = Saml2Utils.SignQueryString(this.ServiceProvider.SigningCertificateAlias, queryString); } } StringBuilder redirectUrl = new StringBuilder(); redirectUrl.Append(sloRedirectLocation); redirectUrl.Append(Saml2Utils.GetQueryStringDelimiter(sloRedirectLocation)); redirectUrl.Append(queryString); FedletLogger.Info("LogoutRequest via Redirect:\r\n" + redirectUrl); return redirectUrl.ToString(); }
/// <summary> /// Checks the signature of the given LogoutRequest with /// the raw query string. /// </summary> /// <param name="logoutRequest">SAMLv2 LogoutRequest object.</param> /// <param name="queryString"> /// Raw query string that contains the request and possible signature. /// </param> private void CheckSignature(LogoutRequest logoutRequest, string queryString) { IdentityProvider idp = (IdentityProvider)this.IdentityProviders[logoutRequest.Issuer]; if (idp == null) { throw new Saml2Exception(Resources.InvalidIssuer); } Saml2Utils.ValidateSignedQueryString(idp.SigningCertificate, queryString); }
/// <summary> /// Retrieve the LogoutRequest object found within the HttpRequest /// in the context of the HttpContext, performing validation of /// the LogoutRequest prior to returning to the user. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <returns>LogoutRequest object</returns> public LogoutRequest GetLogoutRequest(HttpContext context) { HttpRequest request = context.Request; string samlRequest = null; // Obtain the LogoutRequest object... if (request.HttpMethod == "GET") { samlRequest = Saml2Utils.ConvertFromBase64Decompress(request[Saml2Constants.RequestParameter]); } else if (request.HttpMethod == "POST") { // something posted...check if soap vs form post if (!String.IsNullOrEmpty(request[Saml2Constants.RequestParameter])) { samlRequest = Saml2Utils.ConvertFromBase64(request[Saml2Constants.RequestParameter]); } else { StreamReader reader = new StreamReader(request.InputStream); string requestContent = reader.ReadToEnd(); XmlDocument soapRequest = new XmlDocument(); soapRequest.PreserveWhitespace = true; soapRequest.LoadXml(requestContent); XmlNamespaceManager soapNsMgr = new XmlNamespaceManager(soapRequest.NameTable); soapNsMgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/"); soapNsMgr.AddNamespace("samlp", "urn:oasis:names:tc:SAML:2.0:protocol"); soapNsMgr.AddNamespace("saml", "urn:oasis:names:tc:SAML:2.0:assertion"); soapNsMgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#"); XmlElement root = soapRequest.DocumentElement; XmlNode requestXml = root.SelectSingleNode("/soap:Envelope/soap:Body/samlp:LogoutRequest", soapNsMgr); samlRequest = requestXml.OuterXml; } } // Check if a saml request was received... if (samlRequest == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityNoSamlRequestReceived); } LogoutRequest logoutRequest = new LogoutRequest(samlRequest); XmlDocument xmlDoc = (XmlDocument)logoutRequest.XmlDom; StringBuilder logMessage = new StringBuilder(); logMessage.Append("LogoutRequest:\r\n").Append(xmlDoc.OuterXml); FedletLogger.Info(logMessage.ToString()); try { if (request.HttpMethod == "GET") { string queryString = request.RawUrl.Substring(request.RawUrl.IndexOf("?", StringComparison.Ordinal) + 1); FedletLogger.Info("LogoutRequest query string:\r\n" + queryString); this.ValidateForRedirect(logoutRequest, queryString); } else { this.ValidateForPost(logoutRequest); } } catch (Saml2Exception se) { // log and throw again... logMessage = new StringBuilder(); logMessage.Append(se.Message).Append("\r\n").Append(xmlDoc.InnerXml); FedletLogger.Warning(logMessage.ToString()); throw; } return logoutRequest; }
/// <summary> /// Validates the given LogoutRequest. /// </summary> /// <param name="logoutRequest">LogoutRequest object.</param> public void ValidateForPost(LogoutRequest logoutRequest) { this.CheckIssuer(logoutRequest.Issuer); if (this.ServiceProvider.WantLogoutRequestSigned) { this.CheckSignature(logoutRequest); } }
/// <summary> /// Validates the given LogoutRequest. /// </summary> /// <param name="logoutRequest">LogoutRequest object.</param> /// <param name="queryString"> /// Raw query string that contains the request and possible signature /// </param> public void ValidateForRedirect(LogoutRequest logoutRequest, string queryString) { this.CheckIssuer(logoutRequest.Issuer); if (this.ServiceProvider.WantLogoutRequestSigned) { this.CheckSignature(logoutRequest, queryString); } }
/// <summary> /// Send the SAML LogoutResponse message based on the received /// LogoutRequest. POST (default) or Redirect is supported. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <param name="logoutRequest"> /// LogoutRequest corresponding to the ensuing LogoutResponse to send. /// </param> public void SendLogoutResponse(HttpContextBase context, LogoutRequest logoutRequest) { if (logoutRequest == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutRequestIsNull); } IIdentityProvider idp; if (!IdentityProviders.TryGetValue(logoutRequest.Issuer, out idp)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpNotDeterminedFromLogoutRequest); } // send logout response based on how it was received if (context.Request.HttpMethod == "GET") { var parameters = new NameValueCollection(); parameters[Saml2Constants.Binding] = Saml2Constants.HttpRedirectProtocolBinding; var logoutResponse = new LogoutResponse(idp, ServiceProvider, logoutRequest, parameters, _saml2Utils); var xmlDoc = (XmlDocument) logoutResponse.XmlDom; _logger.Info("LogoutResponse:\r\n{0}", xmlDoc.OuterXml); parameters = _saml2Utils.GetRequestParameters(context.Request); string redirectUrl = GetLogoutResponseRedirectLocation(logoutResponse, idp.EntityId, parameters); context.Response.Redirect(redirectUrl, true); } else { var parameters = new NameValueCollection(); parameters[Saml2Constants.Binding] = Saml2Constants.HttpPostProtocolBinding; var logoutResponse = new LogoutResponse(idp, ServiceProvider, logoutRequest, parameters, _saml2Utils); var xmlDoc = (XmlDocument) logoutResponse.XmlDom; _logger.Info("LogoutResponse:\r\n{0}", xmlDoc.OuterXml); parameters = _saml2Utils.GetRequestParameters(context.Request); string postHtml = GetLogoutResponsePostHtml(logoutResponse, idp.EntityId, parameters); context.Response.Write(postHtml); context.Response.End(); } }
/// <summary> /// Sends a LogoutRequest to the specified IDP with the given /// parameters. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <param name="idpEntityId">Entity ID of the IDP.</param> /// <param name="parameters"> /// NameValueCollection of varying parameters for use in the /// construction of the LogoutRequest. /// </param> public void SendLogoutRequest(HttpContextBase context, string idpEntityId, NameValueCollection parameters) { IIdentityProvider idp; if (!IdentityProviders.TryGetValue(idpEntityId, out idp)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound); } if (parameters == null) { parameters = new NameValueCollection(); } var logoutRequest = new LogoutRequest(idp, ServiceProvider, parameters, _saml2Utils); var xmlDoc = (XmlDocument) logoutRequest.XmlDom; _logger.Info("LogoutRequest:\r\n{0}", xmlDoc.OuterXml); // Send with Redirect, POST, or SOAP based on the 'Binding' parameter. if (parameters[Saml2Constants.Binding] == Saml2Constants.HttpPostProtocolBinding) { LogoutRequestCache.AddSentLogoutRequest(context, logoutRequest); string postHtml = GetLogoutRequestPostHtml(logoutRequest, idpEntityId, parameters); context.Response.Write(postHtml); context.Response.End(); } else if (parameters[Saml2Constants.Binding] == Saml2Constants.HttpRedirectProtocolBinding) { LogoutRequestCache.AddSentLogoutRequest(context, logoutRequest); string redirectUrl = GetLogoutRequestRedirectLocation(logoutRequest, idpEntityId, parameters); context.Response.Redirect(redirectUrl, true); } else if (parameters[Saml2Constants.Binding] == Saml2Constants.HttpSoapProtocolBinding) { SendSoapLogoutRequest(logoutRequest, idpEntityId); } else { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityUnsupportedLogoutBinding); } }
/// <summary> /// Initializes a new instance of the LogoutResponse class based on /// the complimentary logout request. /// </summary> /// <param name="identityProvider"> /// IdentityProvider of the LogoutResponse /// </param> /// <param name="serviceProvider"> /// ServiceProvider of the LogoutResponse /// </param> /// <param name="logoutRequest"> /// Logout request that requires this response /// </param> /// <param name="parameters"> /// NameValueCollection of varying parameters for use in the /// construction of the LogoutResponse. /// </param> public LogoutResponse( IdentityProvider identityProvider, ServiceProvider serviceProvider, LogoutRequest logoutRequest, NameValueCollection parameters) { if (identityProvider == null) { throw new Saml2Exception(Resources.LogoutResponseIdentityProviderIsNull); } else if (serviceProvider == null) { throw new Saml2Exception(Resources.LogoutResponseServiceProviderIsNull); } else if (logoutRequest == null) { throw new Saml2Exception(Resources.LogoutResponseLogoutRequestIsNull); } if (parameters == null) { parameters = new NameValueCollection(); } string inResponseToValue = logoutRequest.Id; string issuerValue = serviceProvider.EntityId; string binding = parameters[Saml2Constants.Binding]; if (string.IsNullOrEmpty(binding)) { binding = Saml2Constants.HttpPostProtocolBinding; } string idpSvcResponseLocation = null; if (binding != Saml2Constants.HttpSoapProtocolBinding) { idpSvcResponseLocation = identityProvider.GetSingleLogoutServiceResponseLocation(binding); } this.xml = new XmlDocument(); this.xml.PreserveWhitespace = true; this.nsMgr = new XmlNamespaceManager(this.xml.NameTable); this.nsMgr.AddNamespace("samlp", "urn:oasis:names:tc:SAML:2.0:protocol"); this.nsMgr.AddNamespace("saml", "urn:oasis:names:tc:SAML:2.0:assertion"); StringBuilder rawXml = new StringBuilder(); rawXml.Append("<samlp:LogoutResponse xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" "); rawXml.Append(" ID=\"" + Saml2Utils.GenerateId() + "\" Version=\"2.0\" "); rawXml.Append(" IssueInstant=\"" + Saml2Utils.GenerateIssueInstant() + "\" "); if (idpSvcResponseLocation != null) { rawXml.Append(" Destination=\"" + idpSvcResponseLocation + "\" "); } rawXml.Append(" InResponseTo=\"" + inResponseToValue + "\">"); rawXml.Append(" <saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">" + issuerValue + "</saml:Issuer>"); rawXml.Append(" <samlp:Status xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\">"); rawXml.Append(" <samlp:StatusCode "); rawXml.Append(" xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" "); rawXml.Append(" Value=\"" + Saml2Constants.Success + "\">"); rawXml.Append(" </samlp:StatusCode>"); rawXml.Append(" </samlp:Status>"); rawXml.Append("</samlp:LogoutResponse>"); this.xml.LoadXml(rawXml.ToString()); }
/// <summary> /// Send the SAML LogoutResponse message based on the received /// LogoutRequest. POST (default) or Redirect is supported. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <param name="logoutRequest"> /// LogoutRequest corresponding to the ensuing LogoutResponse to send. /// </param> public void SendLogoutResponse(HttpContext context, LogoutRequest logoutRequest) { if (logoutRequest == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutRequestIsNull); } IdentityProvider idp = (IdentityProvider)this.IdentityProviders[logoutRequest.Issuer]; if (idp == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpNotDeterminedFromLogoutRequest); } // send logout response based on how it was received if (context.Request.HttpMethod == "GET") { NameValueCollection parameters = new NameValueCollection(); parameters[Saml2Constants.Binding] = Saml2Constants.HttpRedirectProtocolBinding; LogoutResponse logoutResponse = new LogoutResponse(idp, this.ServiceProvider, logoutRequest, parameters); StringBuilder logMessage = new StringBuilder(); XmlDocument xmlDoc = (XmlDocument)logoutResponse.XmlDom; logMessage.Append("LogoutResponse:\r\n").Append(xmlDoc.OuterXml); FedletLogger.Info(logMessage.ToString()); parameters = Saml2Utils.GetRequestParameters(context.Request); string redirectUrl = this.GetLogoutResponseRedirectLocation(logoutResponse, idp.EntityId, parameters); context.Response.Redirect(redirectUrl.ToString(), true); } else { NameValueCollection parameters = new NameValueCollection(); parameters[Saml2Constants.Binding] = Saml2Constants.HttpPostProtocolBinding; LogoutResponse logoutResponse = new LogoutResponse(idp, this.ServiceProvider, logoutRequest, parameters); StringBuilder logMessage = new StringBuilder(); XmlDocument xmlDoc = (XmlDocument)logoutResponse.XmlDom; logMessage.Append("LogoutResponse:\r\n").Append(xmlDoc.OuterXml); FedletLogger.Info(logMessage.ToString()); parameters = Saml2Utils.GetRequestParameters(context.Request); string postHtml = this.GetLogoutResponsePostHtml(logoutResponse, idp.EntityId, parameters); context.Response.Write(postHtml); context.Response.End(); } }
/// <summary> /// Initializes a new instance of the LogoutResponse class based on /// the complimentary logout request. /// </summary> /// <param name="identityProvider"> /// IdentityProvider of the LogoutResponse /// </param> /// <param name="serviceProvider"> /// ServiceProvider of the LogoutResponse /// </param> /// <param name="logoutRequest"> /// Logout request that requires this response /// </param> /// <param name="parameters"> /// NameValueCollection of varying parameters for use in the /// construction of the LogoutResponse. /// </param> public LogoutResponse( IIdentityProvider identityProvider, IServiceProvider serviceProvider, LogoutRequest logoutRequest, NameValueCollection parameters) { if (identityProvider == null) { throw new Saml2Exception(Resources.LogoutResponseIdentityProviderIsNull); } if (serviceProvider == null) { throw new Saml2Exception(Resources.LogoutResponseServiceProviderIsNull); } if (logoutRequest == null) { throw new Saml2Exception(Resources.LogoutResponseLogoutRequestIsNull); } if (parameters == null) { parameters = new NameValueCollection(); } var inResponseToValue = logoutRequest.Id; var issuerValue = serviceProvider.EntityId; var binding = parameters[Saml2Constants.Binding]; if (string.IsNullOrEmpty(binding)) { binding = Saml2Constants.HttpPostProtocolBinding; } string idpSvcResponseLocation = null; if (binding != Saml2Constants.HttpSoapProtocolBinding) { idpSvcResponseLocation = identityProvider.GetSingleLogoutServiceResponseLocation(binding); } m_xml = new XmlDocument { PreserveWhitespace = true }; m_nsMgr = new XmlNamespaceManager(m_xml.NameTable); m_nsMgr.AddNamespace("samlp", "urn:oasis:names:tc:SAML:2.0:protocol"); m_nsMgr.AddNamespace("saml", "urn:oasis:names:tc:SAML:2.0:assertion"); var rawXml = new StringBuilder(); rawXml.Append("<samlp:LogoutResponse xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" "); rawXml.Append(" ID=\"" + Saml2Utils.GenerateId() + "\" Version=\"2.0\" "); rawXml.Append(" IssueInstant=\"" + Saml2Utils.GenerateIssueInstant() + "\" "); if (idpSvcResponseLocation != null) { rawXml.Append(" Destination=\"" + idpSvcResponseLocation + "\" "); } rawXml.Append(" InResponseTo=\"" + inResponseToValue + "\">"); rawXml.Append(" <saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">" + issuerValue + "</saml:Issuer>"); rawXml.Append(" <samlp:Status xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\">"); rawXml.Append(" <samlp:StatusCode "); rawXml.Append(" xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" "); rawXml.Append(" Value=\"" + Saml2Constants.Success + "\">"); rawXml.Append(" </samlp:StatusCode>"); rawXml.Append(" </samlp:Status>"); rawXml.Append("</samlp:LogoutResponse>"); m_xml.LoadXml(rawXml.ToString()); }
/// <summary> /// Writes a SOAP LogoutResponse to the Response object found within /// the given HttpContext based on the given logout request. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <param name="logoutRequest"> /// LogoutRequest object. /// </param> public void SendSoapLogoutResponse(HttpContextBase context, LogoutRequest logoutRequest) { IIdentityProvider idp; if (!IdentityProviders.TryGetValue(logoutRequest.Issuer, out idp)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound); } var parameters = new NameValueCollection(); parameters[Saml2Constants.Binding] = Saml2Constants.HttpSoapProtocolBinding; var logoutResponse = new LogoutResponse(idp, ServiceProvider, logoutRequest, parameters, _saml2Utils); var logoutResponseXml = (XmlDocument) logoutResponse.XmlDom; if (idp.WantLogoutResponseSigned) { if (string.IsNullOrEmpty(ServiceProvider.SigningCertificateAlias)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias); } _saml2Utils.SignXml( ServiceProvider.SigningCertificateAlias, logoutResponseXml, logoutResponse.Id, true); } _logger.Info("LogoutResponse:\r\n{0}", logoutResponseXml.OuterXml); string soapMessage = _saml2Utils.CreateSoapMessage(logoutResponseXml.OuterXml); context.Response.ContentType = "text/xml"; context.Response.Write(soapMessage); }
/// <summary> /// Sends a SOAP LogoutRequest to the specified IDP. /// </summary> /// <param name="logoutRequest"> /// LogoutRequest object. /// </param> /// <param name="idpEntityId">Entity ID of the IDP.</param> public void SendSoapLogoutRequest(LogoutRequest logoutRequest, string idpEntityId) { HttpWebRequest request = null; HttpWebResponse response = null; LogoutResponse logoutResponse = null; IdentityProvider idp = (IdentityProvider)this.IdentityProviders[idpEntityId]; if (logoutRequest == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutRequestIsNull); } else if (idp == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound); } else if (idp.GetSingleLogoutServiceLocation(Saml2Constants.HttpSoapProtocolBinding) == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpSingleLogoutSvcLocNotDefined); } try { Uri soapLogoutSvcUri = new Uri(idp.GetSingleLogoutServiceLocation(Saml2Constants.HttpSoapProtocolBinding)); if (soapLogoutSvcUri.Scheme == "https") { System.Net.ServicePointManager.ServerCertificateValidationCallback += delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors) { if (ServiceProvider.TrustAllCerts || sslPolicyErrors.HasFlag(System.Net.Security.SslPolicyErrors.None)) { return true; } StringBuilder logErrorMessage = new StringBuilder(); logErrorMessage.Append("SSLPolicyError: ").Append(sslPolicyErrors); FedletLogger.Error(logErrorMessage.ToString()); return false; }; } request = (HttpWebRequest)WebRequest.Create(soapLogoutSvcUri); string authCertAlias = ConfigurationManager.AppSettings[Saml2Constants.MutualAuthCertAlias]; if (soapLogoutSvcUri.Scheme == "https" && !string.IsNullOrWhiteSpace(authCertAlias)) { X509Certificate2 cert = FedletCertificateFactory.GetCertificateByFriendlyName(authCertAlias); if (cert != null) { request.ClientCertificates.Add(cert); } } XmlDocument logoutRequestXml = (XmlDocument)logoutRequest.XmlDom; if (idp.WantLogoutRequestSigned) { if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias); } else { Saml2Utils.SignXml( this.ServiceProvider.SigningCertificateAlias, logoutRequestXml, logoutRequest.Id, true, this.ServiceProvider); } } string soapMessage = Saml2Utils.CreateSoapMessage(logoutRequestXml.InnerXml); byte[] byteArray = Encoding.UTF8.GetBytes(soapMessage); request.ContentType = "text/xml"; request.ContentLength = byteArray.Length; request.AllowAutoRedirect = false; request.Method = "POST"; Stream requestStream = request.GetRequestStream(); requestStream.Write(byteArray, 0, byteArray.Length); requestStream.Close(); response = (HttpWebResponse)request.GetResponse(); StreamReader streamReader = new StreamReader(response.GetResponseStream()); string responseContent = streamReader.ReadToEnd(); streamReader.Close(); XmlDocument soapResponse = new XmlDocument(); soapResponse.PreserveWhitespace = true; soapResponse.LoadXml(responseContent); XmlNamespaceManager soapNsMgr = new XmlNamespaceManager(soapResponse.NameTable); soapNsMgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/"); soapNsMgr.AddNamespace("samlp", "urn:oasis:names:tc:SAML:2.0:protocol"); soapNsMgr.AddNamespace("saml", "urn:oasis:names:tc:SAML:2.0:assertion"); soapNsMgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#"); XmlElement root = soapResponse.DocumentElement; XmlNode responseXml = root.SelectSingleNode("/soap:Envelope/soap:Body/samlp:LogoutResponse", soapNsMgr); string logoutResponseXml = responseXml.OuterXml; logoutResponse = new LogoutResponse(logoutResponseXml); StringBuilder logMessage = new StringBuilder(); logMessage.Append("LogoutResponse:\r\n").Append(logoutResponseXml); FedletLogger.Info(logMessage.ToString()); ArrayList logoutRequests = new ArrayList(); logoutRequests.Add(logoutRequest); this.Validate(logoutResponse, logoutRequests); } catch (WebException we) { throw new ServiceProviderUtilityException(Resources.LogoutRequestWebException, we); } finally { if (response != null) { response.Close(); } } }
/// <summary> /// Sends a LogoutRequest to the specified IDP with the given /// parameters. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <param name="idpEntityId">Entity ID of the IDP.</param> /// <param name="parameters"> /// NameValueCollection of varying parameters for use in the /// construction of the LogoutRequest. /// </param> public void SendLogoutRequest(HttpContext context, string idpEntityId, NameValueCollection parameters) { IdentityProvider idp = (IdentityProvider)this.IdentityProviders[idpEntityId]; if (idp == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound); } if (parameters == null) { parameters = new NameValueCollection(); } LogoutRequest logoutRequest = new LogoutRequest(idp, this.ServiceProvider, parameters); XmlDocument xmlDoc = (XmlDocument)logoutRequest.XmlDom; StringBuilder logMessage = new StringBuilder(); logMessage.Append("LogoutRequest:\r\n").Append(xmlDoc.OuterXml); FedletLogger.Info(logMessage.ToString()); // Send with Redirect, POST, or SOAP based on the 'Binding' parameter. if (parameters[Saml2Constants.Binding] == Saml2Constants.HttpPostProtocolBinding) { LogoutRequestCache.AddSentLogoutRequest(context, logoutRequest); string postHtml = this.GetLogoutRequestPostHtml(logoutRequest, idpEntityId, parameters); context.Response.Write(postHtml); context.Response.End(); } else if (parameters[Saml2Constants.Binding] == Saml2Constants.HttpRedirectProtocolBinding) { LogoutRequestCache.AddSentLogoutRequest(context, logoutRequest); string redirectUrl = this.GetLogoutRequestRedirectLocation(logoutRequest, idpEntityId, parameters); context.Response.Redirect(redirectUrl.ToString(), true); } else if (parameters[Saml2Constants.Binding] == Saml2Constants.HttpSoapProtocolBinding) { this.SendSoapLogoutRequest(logoutRequest, idpEntityId); } else { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityUnsupportedLogoutBinding); } }
/// <summary> /// Writes a SOAP LogoutResponse to the Response object found within /// the given HttpContext based on the given logout request. /// </summary> /// <param name="context"> /// HttpContext containing session, request, and response objects. /// </param> /// <param name="logoutRequest"> /// LogoutRequest object. /// </param> public void SendSoapLogoutResponse(HttpContext context, LogoutRequest logoutRequest) { IdentityProvider idp = (IdentityProvider)this.IdentityProviders[logoutRequest.Issuer]; if (idp == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound); } NameValueCollection parameters = new NameValueCollection(); parameters[Saml2Constants.Binding] = Saml2Constants.HttpSoapProtocolBinding; LogoutResponse logoutResponse = new LogoutResponse(idp, this.ServiceProvider, logoutRequest, parameters); XmlDocument logoutResponseXml = (XmlDocument)logoutResponse.XmlDom; if (idp.WantLogoutResponseSigned) { if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias); } else { Saml2Utils.SignXml( this.ServiceProvider.SigningCertificateAlias, logoutResponseXml, logoutResponse.Id, true); } } StringBuilder logMessage = new StringBuilder(); logMessage.Append("LogoutResponse:\r\n").Append(logoutResponseXml.OuterXml); FedletLogger.Info(logMessage.ToString()); string soapMessage = Saml2Utils.CreateSoapMessage(logoutResponseXml.OuterXml); context.Response.ContentType = "text/xml"; context.Response.Write(soapMessage); }
/// <summary> /// Sends a SOAP LogoutRequest to the specified IDP. /// </summary> /// <param name="logoutRequest"> /// LogoutRequest object. /// </param> /// <param name="idpEntityId">Entity ID of the IDP.</param> public void SendSoapLogoutRequest(LogoutRequest logoutRequest, string idpEntityId) { HttpWebRequest request = null; HttpWebResponse response = null; LogoutResponse logoutResponse = null; IdentityProvider idp = (IdentityProvider)this.IdentityProviders[idpEntityId]; if (logoutRequest == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityLogoutRequestIsNull); } else if (idp == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdentityProviderNotFound); } else if (idp.GetSingleLogoutServiceLocation(Saml2Constants.HttpSoapProtocolBinding) == null) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilityIdpSingleLogoutSvcLocNotDefined); } try { Uri soapLogoutSvcUri = new Uri(idp.GetSingleLogoutServiceLocation(Saml2Constants.HttpSoapProtocolBinding)); request = (HttpWebRequest)WebRequest.Create(soapLogoutSvcUri); XmlDocument logoutRequestXml = (XmlDocument)logoutRequest.XmlDom; if (idp.WantLogoutRequestSigned) { if (string.IsNullOrEmpty(this.ServiceProvider.SigningCertificateAlias)) { throw new ServiceProviderUtilityException(Resources.ServiceProviderUtilitySignFailedNoCertAlias); } else { Saml2Utils.SignXml( this.ServiceProvider.SigningCertificateAlias, logoutRequestXml, logoutRequest.Id, true); } } string soapMessage = Saml2Utils.CreateSoapMessage(logoutRequestXml.InnerXml); byte[] byteArray = Encoding.UTF8.GetBytes(soapMessage); request.ContentType = "text/xml"; request.ContentLength = byteArray.Length; request.AllowAutoRedirect = false; request.Method = "POST"; Stream requestStream = request.GetRequestStream(); requestStream.Write(byteArray, 0, byteArray.Length); requestStream.Close(); response = (HttpWebResponse)request.GetResponse(); StreamReader streamReader = new StreamReader(response.GetResponseStream()); string responseContent = streamReader.ReadToEnd(); streamReader.Close(); XmlDocument soapResponse = new XmlDocument(); soapResponse.PreserveWhitespace = true; soapResponse.LoadXml(responseContent); XmlNamespaceManager soapNsMgr = new XmlNamespaceManager(soapResponse.NameTable); soapNsMgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/"); soapNsMgr.AddNamespace("samlp", "urn:oasis:names:tc:SAML:2.0:protocol"); soapNsMgr.AddNamespace("saml", "urn:oasis:names:tc:SAML:2.0:assertion"); soapNsMgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#"); XmlElement root = soapResponse.DocumentElement; XmlNode responseXml = root.SelectSingleNode("/soap:Envelope/soap:Body/samlp:LogoutResponse", soapNsMgr); string logoutResponseXml = responseXml.OuterXml; logoutResponse = new LogoutResponse(logoutResponseXml); StringBuilder logMessage = new StringBuilder(); logMessage.Append("LogoutResponse:\r\n").Append(logoutResponseXml); FedletLogger.Info(logMessage.ToString()); ArrayList logoutRequests = new ArrayList(); logoutRequests.Add(logoutRequest); this.Validate(logoutResponse, logoutRequests); } catch (WebException we) { throw new ServiceProviderUtilityException(Resources.LogoutRequestWebException, we); } finally { if (response != null) { response.Close(); } } }
/// <summary> /// Checks the signature of the given LogoutRequest with /// the raw query string. /// </summary> /// <param name="logoutRequest">SAMLv2 LogoutRequest object.</param> /// <param name="queryString"> /// Raw query string that contains the request and possible signature. /// </param> private void CheckSignature(LogoutRequest logoutRequest, string queryString) { IIdentityProvider idp; if (!IdentityProviders.TryGetValue(logoutRequest.Issuer, out idp)) { throw new Saml2Exception(Resources.InvalidIssuer); } _saml2Utils.ValidateSignedQueryString(idp.SigningCertificate, queryString); }