예제 #1
0
        private static CommandResult HandleResponse(UnbindResult unbindResult, StoredRequestState storedRequestState, IOptions options, Uri returnUrl)
        {
            var logoutResponse = Saml2LogoutResponse.FromXml(unbindResult.Data);
            var notificationHandledTheStatus = options.Notifications.ProcessSingleLogoutResponseStatus(logoutResponse, storedRequestState);

            if (!notificationHandledTheStatus)
            {
                var status = logoutResponse.Status;
                if (status != Saml2StatusCode.Success)
                {
                    throw new UnsuccessfulSamlOperationException(string.Format(CultureInfo.InvariantCulture,
                                                                               "Idp returned status \"{0}\", indicating that the single logout failed. The local session has been successfully terminated.",
                                                                               status));
                }
            }

            var commandResult = new CommandResult
            {
                HttpStatusCode = HttpStatusCode.SeeOther
            };

            if (!options.SPOptions.Compatibility.DisableLogoutStateCookie)
            {
                commandResult.ClearCookieName = "Kentor." + unbindResult.RelayState;
            }
            commandResult.Location = storedRequestState?.ReturnUrl ?? returnUrl;

            options.SPOptions.Logger.WriteInformation("Received logout response " + logoutResponse.Id
                                                      + ", redirecting to " + commandResult.Location);

            return(commandResult);
        }
예제 #2
0
        private static CommandResult HandleRequest(UnbindResult unbindResult, IOptions options)
        {
            var request = Saml2LogoutRequest.FromXml(unbindResult.Data);

            var idp = options.IdentityProviders[request.Issuer];

            if (options.SPOptions.SigningServiceCertificate == null)
            {
                throw new ConfigurationErrorsException(string.Format(CultureInfo.InvariantCulture,
                                                                     "Received a LogoutRequest from \"{0}\" but cannot reply because single logout responses must be signed and there is no signing certificate configured. Looks like the idp is configured for Single Logout despite AuthServices not exposing that functionality in the metadata.",
                                                                     request.Issuer.Id));
            }

            var response = new Saml2LogoutResponse(Saml2StatusCode.Success)
            {
                DestinationUrl     = idp.SingleLogoutServiceResponseUrl,
                SigningCertificate = options.SPOptions.SigningServiceCertificate,
                InResponseTo       = request.Id,
                Issuer             = options.SPOptions.EntityId,
                RelayState         = unbindResult.RelayState
            };

            var result = Saml2Binding.Get(idp.SingleLogoutServiceBinding).Bind(response);

            result.TerminateLocalSession = true;
            return(result);
        }
        public void Saml2ArtifactBinding_Unbind_FromGet()
        {
            var issuer = new EntityId("https://idp.example.com");
            var artifact = Uri.EscapeDataString(
                Convert.ToBase64String(
                    Saml2ArtifactBinding.CreateArtifact(issuer, 0x1234)));

            var relayState = MethodBase.GetCurrentMethod().Name;

            PrepareArtifactState(relayState, issuer);

            var r = new HttpRequestData(
                "GET",
                new Uri($"http://example.com/path/acs?SAMLart={artifact}&RelayState={relayState}"));

            StubServer.LastArtifactResolutionSoapActionHeader = null;

            var result = Saml2Binding.Get(Saml2BindingType.Artifact).Unbind(r, StubFactory.CreateOptions());

            var xmlDocument = new XmlDocument() { PreserveWhitespace = true };
            xmlDocument.LoadXml("<message>   <child-node /> </message>");

            var expected = new UnbindResult(xmlDocument.DocumentElement, relayState, TrustLevel.None);

            result.ShouldBeEquivalentTo(expected);
            StubServer.LastArtifactResolutionSoapActionHeader.Should().Be(
                "http://www.oasis-open.org/committees/security");
            StubServer.LastArtifactResolutionWasSigned.Should().BeFalse();
        }
예제 #4
0
        public CommandResult Run(HttpRequestData request, IOptions options)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            var binding = Saml2Binding.Get(request);

            if (binding != null)
            {
                UnbindResult unbindResult = null;
                try
                {
                    unbindResult = binding.Unbind(request, options);
                    var samlResponse = new Saml2Response(unbindResult.Data, request.StoredRequestState?.MessageId);

                    var result = ProcessResponse(options, samlResponse, request.StoredRequestState);
                    if (unbindResult.RelayState != null)
                    {
                        result.ClearCookieName = "Kentor." + unbindResult.RelayState;
                    }
                    return(result);
                }
                catch (FormatException ex)
                {
                    throw new BadFormatSamlResponseException(
                              "The SAML Response did not contain valid BASE64 encoded data.", ex);
                }
                catch (XmlException ex)
                {
                    var newEx = new BadFormatSamlResponseException(
                        "The SAML response contains incorrect XML", ex);

                    // Add the payload to the exception
                    if (unbindResult != null)
                    {
                        newEx.Data["Saml2Response"] = unbindResult.Data.OuterXml;
                    }
                    throw newEx;
                }
                catch (Exception ex)
                {
                    if (unbindResult != null)
                    {
                        // Add the payload to the existing exception
                        ex.Data["Saml2Response"] = unbindResult.Data.OuterXml;
                    }
                    throw;
                }
            }

            throw new NoSamlResponseFoundException();
        }
예제 #5
0
        public CommandResult Run(HttpRequestData request, IOptions options)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            var binding = Saml2Binding.Get(request);

            if (binding != null)
            {
                UnbindResult unbindResult = null;
                try
                {
                    unbindResult = binding.Unbind(request);
                    var samlResponse = Saml2Response.Read(unbindResult.Data, unbindResult.RelayState);

                    return(ProcessResponse(options, samlResponse));
                }
                catch (FormatException ex)
                {
                    throw new BadFormatSamlResponseException(
                              "The SAML Response did not contain valid BASE64 encoded data.", ex);
                }
                catch (XmlException ex)
                {
                    var newEx = new BadFormatSamlResponseException(
                        "The SAML response contains incorrect XML", ex);

                    // Add the payload to the exception
                    newEx.Data["Saml2Response"] = unbindResult.Data;
                    throw newEx;
                }
                catch (Exception ex)
                {
                    // Add the payload to the existing exception
                    ex.Data["Saml2Response"] = unbindResult.Data;
                    throw;
                }
            }

            throw new NoSamlResponseFoundException();
        }
예제 #6
0
        private static CommandResult HandleResponse(UnbindResult unbindResult, HttpRequestData request)
        {
            var status = Saml2LogoutResponse.FromXml(unbindResult.Data).Status;

            if (status != Saml2StatusCode.Success)
            {
                throw new UnsuccessfulSamlOperationException(string.Format(CultureInfo.InvariantCulture,
                                                                           "Idp returned status \"{0}\", indicating that the single logout failed. The local session has been successfully terminated.",
                                                                           status));
            }

            return(new CommandResult()
            {
                HttpStatusCode = HttpStatusCode.SeeOther,
                Location = request.StoredRequestState.ReturnUrl,
                ClearCookieName = "Kentor." + request.QueryString["RelayState"].Single()
            });
        }
예제 #7
0
        private static CommandResult HandleRequest(UnbindResult unbindResult, IOptions options)
        {
            var request = Saml2LogoutRequest.FromXml(unbindResult.Data);

            var idp = options.IdentityProviders[request.Issuer];

            if (options.SPOptions.SigningServiceCertificate == null)
            {
                throw new ConfigurationErrorsException(string.Format(CultureInfo.InvariantCulture,
                                                                     "Received a LogoutRequest from \"{0}\" but cannot reply because single logout responses " +
                                                                     "must be signed and there is no signing certificate configured. Looks like the idp is " +
                                                                     "configured for Single Logout despite AuthServices not exposing that functionality in the metadata.",
                                                                     request.Issuer.Id));
            }

            if (idp.SingleLogoutServiceResponseUrl == null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture,
                                                                  "Received a LogoutRequest from \"{0}\" but cannot reply because on logout endpoint is " +
                                                                  "configured on the idp. Set a SingleLogoutServiceUrl if the idp is configured manually, " +
                                                                  "or check that the idp metadata contains a SingleLogoutService endpoint.",
                                                                  idp.EntityId.Id));
            }

            var response = new Saml2LogoutResponse(Saml2StatusCode.Success)
            {
                DestinationUrl     = idp.SingleLogoutServiceResponseUrl,
                SigningCertificate = options.SPOptions.SigningServiceCertificate,
                SigningAlgorithm   = idp.OutboundSigningAlgorithm,
                InResponseTo       = request.Id,
                Issuer             = options.SPOptions.EntityId,
                RelayState         = unbindResult.RelayState
            };

            options.SPOptions.Logger.WriteInformation("Got a logout request " + request.Id
                                                      + ", responding with logout response " + response.Id);

            var result = Saml2Binding.Get(idp.SingleLogoutServiceBinding).Bind(response);

            result.TerminateLocalSession = true;
            return(result);
        }
예제 #8
0
        private static void VerifyMessageIsSigned(UnbindResult unbindResult, IOptions options)
        {
            if (unbindResult.TrustLevel < TrustLevel.Signature)
            {
                var issuer = unbindResult.Data["Issuer", Saml2Namespaces.Saml2Name]?.InnerText;

                if (issuer == null)
                {
                    throw new InvalidSignatureException("There is no Issuer element in the message, so there is no way to know what certificate to use to validate the signature.");
                }
                var idp = options.IdentityProviders[new EntityId(issuer)];

                if (!unbindResult.Data.IsSignedByAny(idp.SigningKeys, options.SPOptions.ValidateCertificates))
                {
                    throw new UnsuccessfulSamlOperationException(string.Format(CultureInfo.InvariantCulture,
                                                                               "Received a {0} from {1} that cannot be processed because it is not signed.",
                                                                               unbindResult.Data.LocalName,
                                                                               unbindResult.Data["Issuer", Saml2Namespaces.Saml2Name].InnerText));
                }
            }
        }
예제 #9
0
        private static void VerifyMessageIsSigned(UnbindResult unbindResult, IOptions options)
        {
            if (unbindResult.TrustLevel < TrustLevel.Signature)
            {
                var issuer = unbindResult.Data["Issuer", Saml2Namespaces.Saml2Name]?.InnerText;

                if(issuer == null)
                {
                    throw new InvalidSignatureException("There is no Issuer element in the message, so there is no way to know what certificate to use to validate the signature.");
                }
                var idp = options.IdentityProviders[new EntityId(issuer)];

                if (!unbindResult.Data.IsSignedByAny(idp.SigningKeys, options.SPOptions.ValidateCertificates))
                {
                    throw new UnsuccessfulSamlOperationException(string.Format(CultureInfo.InvariantCulture,
                        "Received a {0} from {1} that cannot be processed because it is not signed.",
                        unbindResult.Data.LocalName,
                        unbindResult.Data["Issuer", Saml2Namespaces.Saml2Name].InnerText));
                }
            }
        }
예제 #10
0
        private static CommandResult HandleResponse(UnbindResult unbindResult, StoredRequestState storedRequestState, IOptions options, Uri returnUrl)
        {
            var status = Saml2LogoutResponse.FromXml(unbindResult.Data).Status;
            if(status != Saml2StatusCode.Success)
            {
                throw new UnsuccessfulSamlOperationException(string.Format(CultureInfo.InvariantCulture,
                    "Idp returned status \"{0}\", indicating that the single logout failed. The local session has been successfully terminated.",
                    status));
            }

            var commandResult = new CommandResult
            {
                HttpStatusCode = HttpStatusCode.SeeOther
            };
            if (!options.SPOptions.Compatibility.DisableLogoutStateCookie)
            {
                commandResult.ClearCookieName = "Kentor." + unbindResult.RelayState;
            }
            commandResult.Location = storedRequestState?.ReturnUrl ?? returnUrl;
            return commandResult;
        }
예제 #11
0
        private static CommandResult HandleRequest(UnbindResult unbindResult, IOptions options)
        {
            var request = Saml2LogoutRequest.FromXml(unbindResult.Data);

            var idp = options.IdentityProviders[request.Issuer];

            if(options.SPOptions.SigningServiceCertificate == null)
            {
                throw new ConfigurationErrorsException(string.Format(CultureInfo.InvariantCulture,
                    "Received a LogoutRequest from \"{0}\" but cannot reply because single logout responses must be signed and there is no signing certificate configured. Looks like the idp is configured for Single Logout despite AuthServices not exposing that functionality in the metadata.",
                    request.Issuer.Id));
            }

            var response = new Saml2LogoutResponse(Saml2StatusCode.Success)
            {
                DestinationUrl = idp.SingleLogoutServiceResponseUrl,
                SigningCertificate = options.SPOptions.SigningServiceCertificate,
                InResponseTo = request.Id,
                Issuer = options.SPOptions.EntityId,
                RelayState = unbindResult.RelayState
            };

            var result = Saml2Binding.Get(idp.SingleLogoutServiceBinding).Bind(response);
            result.TerminateLocalSession = true;
            return result;
        }
예제 #12
0
        private static CommandResult HandleResponse(UnbindResult unbindResult, HttpRequestData request)
        {
            var status = Saml2LogoutResponse.FromXml(unbindResult.Data).Status;
            if(status != Saml2StatusCode.Success)
            {
                throw new UnsuccessfulSamlOperationException(string.Format(CultureInfo.InvariantCulture,
                    "Idp returned status \"{0}\", indicating that the single logout failed. The local session has been successfully terminated.",
                    status));
            }

            return new CommandResult()
            {
                HttpStatusCode = HttpStatusCode.SeeOther,
                Location = request.StoredRequestState.ReturnUrl,
                ClearCookieName = "Kentor." + request.QueryString["RelayState"].Single()
            };
        }
        public void Saml2ArtifactBinding_Unbind_FromPostWithoutRelayState()
        {
            var issuer = new EntityId("https://idp.example.com");
            var artifact = Convert.ToBase64String(
                    Saml2ArtifactBinding.CreateArtifact(issuer, 0x1234));

            var r = new HttpRequestData(
                "POST",
                new Uri("http://example.com"),
                "/ModulePath",
                new KeyValuePair<string, string[]>[]
                {
                    new KeyValuePair<string, string[]>("SAMLart", new[] { artifact }),
                },
                null,
                null);

            StubServer.LastArtifactResolutionSoapActionHeader = null;

            var result = Saml2Binding.Get(Saml2BindingType.Artifact).Unbind(r, StubFactory.CreateOptions());

            var xmlDocument = new XmlDocument() { PreserveWhitespace = true };
            xmlDocument.LoadXml("<message>   <child-node /> </message>");

            var expected = new UnbindResult(xmlDocument.DocumentElement, null, TrustLevel.None);

            result.ShouldBeEquivalentTo(expected);
            StubServer.LastArtifactResolutionSoapActionHeader.Should().Be(
                "http://www.oasis-open.org/committees/security");
        }