Example #1
0
        /// <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());
            }
        }
Example #2
0
        /// <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());
        }
Example #3
0
        /// <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);
            }
        }
Example #10
0
        /// <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();
            }
        }
Example #11
0
        /// <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);
            }
        }
Example #12
0
        /// <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());
        }
Example #13
0
        /// <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();
            }
        }
Example #14
0
        /// <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());
        }
Example #15
0
        /// <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();
                }
            }
        }
Example #17
0
        /// <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);
            }
        }
Example #18
0
        /// <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);
        }
Example #19
0
        /// <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();
                }
            }
        }
Example #20
0
        /// <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);
        }