コード例 #1
0
        /// <summary>
        /// Create Saml2LogoutRequest from data in Xml.
        /// </summary>
        /// <param name="xml">Xml data to initialize the Saml2LogoutRequest from.</param>
        public static Saml2LogoutRequest FromXml(XmlElement xml)
        {
            if (xml == null)
            {
                throw new ArgumentNullException(nameof(xml));
            }

            var request = new Saml2LogoutRequest()
            {
                NameId = new Saml2NameIdentifier(
                    xml["NameID", Saml2Namespaces.Saml2Name].InnerText),
                RelayState = null
            };

            request.ReadBaseProperties(xml);

            var format = xml["NameID", Saml2Namespaces.Saml2Name].GetAttribute("Format");

            if (!string.IsNullOrEmpty(format))
            {
                request.NameId.Format = new Uri(format);
            }

            var sessionIndexElement = xml["SessionIndex", Saml2Namespaces.Saml2PName];

            if (sessionIndexElement != null)
            {
                request.SessionIndex = sessionIndexElement.InnerText;
            }

            return(request);
        }
コード例 #2
0
        /// <summary>
        /// Create Saml2LogoutRequest from data in Xml.
        /// </summary>
        /// <param name="xml">Xml data to initialize the Saml2LogoutRequest from.</param>
        public static Saml2LogoutRequest FromXml(XmlElement xml)
        {
            if(xml == null)
            {
                throw new ArgumentNullException(nameof(xml));
            }

            var request = new Saml2LogoutRequest()
            {
                NameId = new Saml2NameIdentifier(
                    xml["NameID", Saml2Namespaces.Saml2Name].InnerText),
                RelayState = null
            };

            request.ReadBaseProperties(xml);

            var format = xml["NameID", Saml2Namespaces.Saml2Name].GetAttribute("Format");
            if(!string.IsNullOrEmpty(format))
            {
                request.NameId.Format = new Uri(format);
            }

            var sessionIndexElement = xml["SessionIndex", Saml2Namespaces.Saml2PName];
            if(sessionIndexElement != null)
            {
                request.SessionIndex = sessionIndexElement.InnerText;
            }

            return request;
        }
コード例 #3
0
        public void Saml2LogoutRequest_ToXml()
        {
            var subject = new Saml2LogoutRequest()
            {
                DestinationUrl = new Uri("http://idp.example.com/logout"),
                Issuer = new EntityId("http://sp.example.com/"),
                NameId = new Saml2NameIdentifier(
                    "005a06e0-ad82-110d-a556-004005b13a2b",
                    new Uri(NameIdFormat.Persistent.GetString())),
                SessionIndex = "SessionId"
            };

            var actual = XElement.Parse(subject.ToXml());

            var expected = XElement.Parse(
@"<saml2p:LogoutRequest xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol""
xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion""
 ID=""d2b7c388cec36fa7c39c28fd298644a8"" Version=""2.0"" IssueInstant=""2004-01-21T19:00:49Z""
 Destination=""http://idp.example.com/logout"">
 <saml2:Issuer>http://sp.example.com/</saml2:Issuer>
 <saml2:NameID Format=""urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"">005a06e0-ad82-110d-a556-004005b13a2b</saml2:NameID>
 <saml2p:SessionIndex>SessionId</saml2p:SessionIndex>
</saml2p:LogoutRequest>");

            // Set generated expected values to the actual.
            expected.Attribute("ID").Value = actual.Attribute("ID").Value;
            expected.Attribute("IssueInstant").Value = actual.Attribute("IssueInstant").Value;

            actual.Should().BeEquivalentTo(expected);
        }
コード例 #4
0
        public void Saml2LogoutRequest_FromXml()
        {
            var xmlData =
            @"<saml2p:LogoutRequest xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol""
xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion""
 ID=""d2b7c388cec36fa7c39c28fd298644a8"" Version=""2.0"" IssueInstant=""2004-01-21T19:00:49Z""
 Destination=""http://idp.example.com/logout"">
 <saml2:Issuer>http://sp.example.com/</saml2:Issuer>
 <saml2:NameID Format=""urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"">005a06e0-ad82-110d-a556-004005b13a2b</saml2:NameID>
 <saml2p:SessionIndex>SessionId</saml2p:SessionIndex>
</saml2p:LogoutRequest>";

            var xmlDoc = XmlHelpers.FromString(xmlData);

            var subject = Saml2LogoutRequest.FromXml(xmlDoc.DocumentElement);

            var expected = new Saml2LogoutRequest(new Saml2Id("d2b7c388cec36fa7c39c28fd298644a8"))
            {
                DestinationUrl = new Uri("http://idp.example.com/logout"),
                Issuer = new EntityId("http://sp.example.com/"),
                NameId = new Saml2NameIdentifier("005a06e0-ad82-110d-a556-004005b13a2b", new Uri(NameIdFormat.Persistent.GetString())),
                SessionIndex = "SessionId",
            };

            subject.ShouldBeEquivalentTo(expected);
        }
コード例 #5
0
        public void Saml2LogoutRequest_FromXml_OnlyRequiredData()
        {
            var xmlData =
            @"<saml2p:LogoutRequest xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol""
            xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion""
             ID=""d2b7c388cec36fa7c39c28fd298644a8"" Version=""2.0"" IssueInstant=""2004-01-21T19:00:49Z"">
             <saml2:NameID>005a06e0-ad82-110d-a556-004005b13a2b</saml2:NameID>
            </saml2p:LogoutRequest>";

            var xmlDoc = XmlHelpers.FromString(xmlData);

            var subject = Saml2LogoutRequest.FromXml(xmlDoc.DocumentElement);

            var expected = new Saml2LogoutRequest(new Saml2Id("d2b7c388cec36fa7c39c28fd298644a8"))
            {
                NameId = new Saml2NameIdentifier("005a06e0-ad82-110d-a556-004005b13a2b"),
            };

            subject.ShouldBeEquivalentTo(expected);
        }
コード例 #6
0
        public void LogoutCommand_Run_DetectsSignatureInLogoutRequestReceivedThroughPostBinding()
        {
            var request = new Saml2LogoutRequest()
            {
                DestinationUrl = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer = new EntityId("https://idp.example.com"),
                SigningCertificate = SignedXmlHelper.TestCert,
                NameId = new Saml2NameIdentifier("NameId"),
                SessionIndex = "SessionID",
            };

            var xml = XmlHelpers.FromString(request.ToXml());
            xml.Sign(SignedXmlHelper.TestCert);

            var requestData = Convert.ToBase64String(Encoding.UTF8.GetBytes(xml.OuterXml));

            var httpRequest = new HttpRequestData(
                "POST",
                new Uri("http://something"),
                "/path",
                new KeyValuePair<string, string[]>[]
                {
                    new KeyValuePair<string, string[]>("SAMLRequest", new[] { requestData })
                },
                null,
                null);

            var options = StubFactory.CreateOptions();
            options.SPOptions.ServiceCertificates.Add(SignedXmlHelper.TestCert);

            var actual = CommandFactory.GetCommand(CommandFactory.LogoutCommandName)
                .Run(httpRequest, options);

            HttpUtility.ParseQueryString(actual.Location.Query)
                .Keys.Should().Contain("SAMLResponse", "if the request was properly detected a response should be generated");
        }
        public async Task KentorAuthServicesAuthenticationMiddleware_LogoutsOnLogoutRequest()
        {
            var options = new KentorAuthServicesAuthenticationOptions(true);
            var subject = new KentorAuthServicesAuthenticationMiddleware(null, CreateAppBuilder(), options);

            var context = OwinTestHelpers.CreateOwinContext();

            var request = new Saml2LogoutRequest()
            {
                SessionIndex = "SessionId",
                DestinationUrl = new Uri("http://sp.example.com/AuthServices/Logout"),
                NameId = new Saml2NameIdentifier("NameId"),
                Issuer = new EntityId("https://idp.example.com"),
                SigningCertificate = SignedXmlHelper.TestCert
            };

            var url = Saml2Binding.Get(Saml2BindingType.HttpRedirect)
                .Bind(request).Location;

            context.Request.Path = new PathString(url.AbsolutePath);
            context.Request.QueryString = new QueryString(url.Query.TrimStart('?'));

            await subject.Invoke(context);

            context.Response.StatusCode.Should().Be(303);
            context.Response.Headers["Location"].Should().StartWith("https://idp.example.com/logout?SAMLResponse");

            context.Authentication.AuthenticationResponseRevoke.Should().NotBeNull();
            context.Authentication.AuthenticationResponseRevoke.AuthenticationTypes
                .Should().BeEmpty();
        }
コード例 #8
0
        public void LogoutCommand_Run_HandlesLogoutRequest_ReceivedThroughRedirectBinding()
        {
            var request = new Saml2LogoutRequest()
            {
                DestinationUrl = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer = new EntityId("https://idp.example.com"),
                SigningCertificate = SignedXmlHelper.TestCert,
                NameId = new Saml2NameIdentifier("NameId"),
                SessionIndex = "SessionID",
            };

            var bindResult = Saml2Binding.Get(Saml2BindingType.HttpRedirect)
                .Bind(request);

            var httpRequest = new HttpRequestData("GET", bindResult.Location);

            var options = StubFactory.CreateOptions();
            options.SPOptions.ServiceCertificates.Add(SignedXmlHelper.TestCert);

            CommandResult notifiedCommandResult = null;
            options.Notifications.LogoutCommandResultCreated = cr =>
            {
                notifiedCommandResult = cr;
            };

            // We're using unbind to verify the created message and UnBind
            // expects the issuer to be a known Idp for signature validation.
            // Add a dummy with the right issuer name and key.
            var dummyIdp = new IdentityProvider(options.SPOptions.EntityId, options.SPOptions);
            dummyIdp.SigningKeys.AddConfiguredKey(SignedXmlHelper.TestCert);
            options.IdentityProviders.Add(dummyIdp);

            var actual = CommandFactory.GetCommand(CommandFactory.LogoutCommandName)
                .Run(httpRequest, options);

            var expected = new CommandResult()
            {
                HttpStatusCode = HttpStatusCode.SeeOther,
                TerminateLocalSession = true
                // Deliberately not comparing Location
            };

            HttpUtility.ParseQueryString(actual.Location.Query)["Signature"]
                .Should().NotBeNull("LogoutResponse should be signed");

            actual.ShouldBeEquivalentTo(expected, opt => opt.Excluding(cr => cr.Location));
            actual.Should().BeSameAs(notifiedCommandResult);

            var actualUnbindResult = Saml2Binding.Get(Saml2BindingType.HttpRedirect)
                .Unbind(new HttpRequestData("GET", actual.Location), options);

            var actualMessage = actualUnbindResult.Data;

            var expectedMessage = XmlHelpers.FromString(
                $@"<samlp:LogoutResponse xmlns:samlp=""urn:oasis:names:tc:SAML:2.0:protocol""
                    xmlns=""urn:oasis:names:tc:SAML:2.0:assertion""
                    Destination=""https://idp.example.com/logout""
                    Version=""2.0"">
                    <Issuer>{options.SPOptions.EntityId.Id}</Issuer>
                    <samlp:Status>
                        <samlp:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Success""/>
                    </samlp:Status>
                </samlp:LogoutResponse>").DocumentElement;

            // Set generated attributes to actual values.
            expectedMessage.SetAttribute("ID", actualMessage.GetAttribute("ID"));
            expectedMessage.SetAttribute("IssueInstant", actualMessage.GetAttribute("IssueInstant"));
            expectedMessage.SetAttribute("InResponseTo", request.Id.Value);

            actualMessage.Should().BeEquivalentTo(expectedMessage);

            actualUnbindResult.RelayState.Should().Be(request.RelayState);
            actualUnbindResult.TrustLevel.Should().Be(TrustLevel.Signature);
        }
コード例 #9
0
        public void LogoutCommand_Run_ThrowsOnSignatureInLogoutRequestReceivedThroughPostBindingIfCertificateIsntValid_WhenCertificateValidationIsConfigured()
        {
            var request = new Saml2LogoutRequest()
            {
                DestinationUrl = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer = new EntityId("https://idp.example.com"),
                SigningCertificate = SignedXmlHelper.TestCert,
                NameId = new Saml2NameIdentifier("NameId"),
                SessionIndex = "SessionID",
            };

            var xml = XmlHelpers.FromString(request.ToXml());
            xml.Sign(SignedXmlHelper.TestCert);

            var requestData = Convert.ToBase64String(Encoding.UTF8.GetBytes(xml.OuterXml));

            var httpRequest = new HttpRequestData(
                "POST",
                new Uri("http://something"),
                "/path",
                new KeyValuePair<string, string[]>[]
                {
                    new KeyValuePair<string, string[]>("SAMLRequest", new[] { requestData })
                },
                null,
                null);

            var options = StubFactory.CreateOptions();
            options.SPOptions.ServiceCertificates.Add(SignedXmlHelper.TestCert);
            options.SPOptions.ValidateCertificates = true;

            var actual = CommandFactory.GetCommand(CommandFactory.LogoutCommandName)
                .Invoking(c => c.Run(httpRequest, options))
                .ShouldThrow<InvalidSignatureException>()
                .WithMessage("The signature was valid, but the verification of the certificate failed. Is it expired or revoked? Are you sure you really want to enable ValidateCertificates (it's normally not needed)?");
        }
コード例 #10
0
        public void LogoutCommand_Run_IncomingRequest_ThrowsOnNoConfiguredSigningCert()
        {
            var request = new Saml2LogoutRequest()
            {
                DestinationUrl = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer = new EntityId("https://idp.example.com"),
                SigningCertificate = SignedXmlHelper.TestCert,
                NameId = new Saml2NameIdentifier("NameId"),
                SessionIndex = "SessionID"
            };

            var bindResult = Saml2Binding.Get(Saml2BindingType.HttpRedirect)
                .Bind(request);

            var httpRequest = new HttpRequestData("GET", bindResult.Location);

            var options = StubFactory.CreateOptions();

            CommandFactory.GetCommand(CommandFactory.LogoutCommandName)
                .Invoking(c => c.Run(httpRequest, options))
                .ShouldThrow<ConfigurationErrorsException>()
                .WithMessage("Received a LogoutRequest from \"https://idp.example.com\" 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.");
        }
コード例 #11
0
        public void LogoutCommand_Run_IncomingRequest_ThroughRedirectBinding_ThrowsOnMissingSignature()
        {
            var request = new Saml2LogoutRequest()
            {
                DestinationUrl = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer = new EntityId("https://idp.example.com"),
                NameId = new Saml2NameIdentifier("NameId"),
                SessionIndex = "SessionID"
            };

            var bindResult = Saml2Binding.Get(Saml2BindingType.HttpRedirect)
                .Bind(request);

            var httpRequest = new HttpRequestData("GET", bindResult.Location);

            var options = StubFactory.CreateOptions();
            options.SPOptions.ServiceCertificates.Add(SignedXmlHelper.TestCert);

            CommandFactory.GetCommand(CommandFactory.LogoutCommandName)
                .Invoking(c => c.Run(httpRequest, options))
                .ShouldThrow<UnsuccessfulSamlOperationException>()
                .WithMessage("Received a LogoutRequest from https://idp.example.com that cannot be processed because it is not signed.");
        }
        public async Task KentorAuthServicesAuthenticationMiddleware_LogoutRequest_HonorsCommandResultHandled()
        {
            var options = new KentorAuthServicesAuthenticationOptions(true)
            {
                Notifications = new KentorAuthServicesNotifications
                {
                    LogoutCommandResultCreated = cr =>
                    {
                        cr.HandledResult = true;
                    }
                }
            };

            var subject = new KentorAuthServicesAuthenticationMiddleware(null, CreateAppBuilder(), options);

            var context = OwinTestHelpers.CreateOwinContext();

            var request = new Saml2LogoutRequest()
            {
                SessionIndex = "SessionId",
                DestinationUrl = new Uri("http://sp.example.com/AuthServices/Logout"),
                NameId = new Saml2NameIdentifier("NameId"),
                Issuer = new EntityId("https://idp.example.com"),
                SigningCertificate = SignedXmlHelper.TestCert
            };

            var url = Saml2Binding.Get(Saml2BindingType.HttpRedirect)
                .Bind(request).Location;

            context.Request.Path = new PathString(url.AbsolutePath);
            context.Request.QueryString = new QueryString(url.Query.TrimStart('?'));

            await subject.Invoke(context);

            context.Response.StatusCode.Should().Be(200);
        }