예제 #1
0
        public override CommandResult Bind(string payload, Uri destinationUrl, string messageName)
        {
            if (payload == null)
            {
                throw new ArgumentNullException(nameof(payload));
            }
            if (destinationUrl == null)
            {
                throw new ArgumentNullException(nameof(destinationUrl));
            }
            if (messageName == null)
            {
                throw new ArgumentNullException(nameof(messageName));
            }

            var encodedXml = Convert.ToBase64String(
                Encoding.UTF8.GetBytes(payload));

            var cr = new CommandResult()
            {
                ContentType = "text/html",
                Content = String.Format(
                    CultureInfo.InvariantCulture,
                    PostHtmlFormatString,
                    destinationUrl,
                    messageName,
                    encodedXml)
            };

            return cr;
        }
        public void CommandResultExtensions_Apply_Cookie()
        {
            var cr = new CommandResult()
            {
                RequestState = new StoredRequestState(
                    new EntityId("http://idp.example.com"),
                    new Uri("http://sp.example.com/loggedout"),
                    new Saml2Id("id123"),
                    null),
                SetCookieName = "CookieName"
            };

            var context = OwinTestHelpers.CreateOwinContext();

            var dataProtector = new StubDataProtector();
            cr.Apply(context, dataProtector);

            var setCookieHeader = context.Response.Headers["Set-Cookie"];

            var protectedData = HttpRequestData.ConvertBinaryData(
                StubDataProtector.Protect(cr.GetSerializedRequestState()));

            var expected = $"CookieName={protectedData}; path=/; HttpOnly";

            setCookieHeader.Should().Be(expected);
        }
        public void Saml2RedirectBinding_Bind()
        {
            // Example from http://en.wikipedia.org/wiki/SAML_2.0#HTTP_Redirect_Binding
            var xmlData = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<samlp:AuthnRequest
  xmlns:samlp=""urn:oasis:names:tc:SAML:2.0:protocol""
  xmlns:saml=""urn:oasis:names:tc:SAML:2.0:assertion""
  ID=""aaf23196-1773-2113-474a-fe114412ab72""
  Version=""2.0""
  IssueInstant=""2004-12-05T09:21:59Z""
  AssertionConsumerServiceIndex=""0""
  AttributeConsumingServiceIndex=""0"">
  <saml:Issuer>https://sp.example.com/SAML2</saml:Issuer>
  <samlp:NameIDPolicy
    AllowCreate=""true""
    Format=""urn:oasis:names:tc:SAML:2.0:nameid-format:transient""/>
</samlp:AuthnRequest>
";

            var serializedData = "fZFfa8IwFMXfBb9DyXvaJtZ1BqsURRC2Mabbw95ivc5Am3TJrXPffmmLY3%2FA15Pzuyf33On8XJXBCaxTRmeEhTEJQBdmr%2FRbRp63K3pL5rPhYOpkVdYib%2FCon%2BC9AYfDQRB4WDvRvWWksVoY6ZQTWlbgBBZik9%2FfCR7GorYGTWFK8pu6DknnwKL%2FWEetlxmR8sBHbHJDWZqOKGdsRJM0kfQAjCUJ43KX8s78ctnIz%2Blp5xpYa4dSo1fjOKGM03i8jSeCMzGevHa2%2FBK5MNo1FdgN2JMqPLmHc0b6WTmiVbsGoTf5qv66Zq2t60x0wXZ2RKydiCJXh3CWVV1CWJgqanfl0%2Bin8xutxYOvZL18NKUqPlvZR5el%2BVhYkAgZQdsA6fWVsZXE63W2itrTQ2cVaKV2CjSSqL1v9P%2FAXv4C";
            var destinationUrl = new Uri("http://www.example.com/acs");

            var subject = Saml2Binding.Get(Saml2BindingType.HttpRedirect).Bind(xmlData, destinationUrl, null);

            var expected = new CommandResult()
            {
                Location = new Uri("http://www.example.com/acs?SAMLRequest=" + serializedData),
                HttpStatusCode = System.Net.HttpStatusCode.SeeOther,
            };

            subject.ShouldBeEquivalentTo(expected);
        }
        public void CommandResultExtensions_ToActionResult_UknownStatusCode()
        {
            var cr = new CommandResult()
            {
                HttpStatusCode = System.Net.HttpStatusCode.SwitchingProtocols
            };

            Action a = () => cr.ToActionResult();

            a.ShouldThrow<NotImplementedException>();
        }
        public void CommandResultExtensions_ToActionResult_SeeOther()
        {
            var strLocation = "http://example.com/location?X=A%20B%3DZ";

            var cr = new CommandResult()
            {
                HttpStatusCode = System.Net.HttpStatusCode.SeeOther,
                Location = new Uri(strLocation)
            };

            var subject = cr.ToActionResult();

            subject.Should().BeOfType<RedirectResult>().And
                .Subject.As<RedirectResult>().Url.Should().Be(strLocation);
        }
        public void CommandResultExtensions_ToActionResult_Ok()
        {
            var cr = new CommandResult()
            {
                Content = "Some Content!",
                ContentType = "application/whatever+text"
            };

            var subject = cr.ToActionResult();

            subject.Should().BeOfType<ContentResult>().And
                .Subject.As<ContentResult>().Content.Should().Be(cr.Content);

            subject.As<ContentResult>().ContentType.Should().Contain(cr.ContentType);
        }
        public void CommandResultExtensions_Apply_Redirect()
        {
            string redirectUrl = "http://somewhere.else.example.com?Foo=Bar%20XYZ";
            var cr = new CommandResult()
            {
                HttpStatusCode = HttpStatusCode.SeeOther,
                Location = new Uri(redirectUrl)
            };

            var context = OwinTestHelpers.CreateOwinContext();

            cr.Apply(context);

            context.Response.StatusCode.Should().Be(303);
            context.Response.Headers["Location"].Should().Be(redirectUrl);
        }
        public void CommandResultExtensions_Apply_ClearCookie()
        {
            var cr = new CommandResult()
            {
                ClearCookieName = "CookieName"
            };

            var context = OwinTestHelpers.CreateOwinContext();
            var dataProtector = new StubDataProtector();
            cr.Apply(context, dataProtector);

            var setCookieHeader = context.Response.Headers["Set-Cookie"];

            var expected = "CookieName=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT";

            setCookieHeader.Should().Be(expected);
        }
        public void Saml2RedirectBinding_Bind()
        {
            var message = new Saml2MessageImplementation
            {
                XmlData = ExampleXmlData,
                DestinationUrl = new Uri("http://www.example.com/sso"),
                MessageName = "SAMLRequest"
            };

            var result = Saml2Binding.Get(Saml2BindingType.HttpRedirect).Bind(message);

            var expected = new CommandResult()
            {
                Location = new Uri("http://www.example.com/sso?SAMLRequest=" + ExampleSerializedData),
                HttpStatusCode = System.Net.HttpStatusCode.SeeOther,
            };

            result.ShouldBeEquivalentTo(expected);
        }
        public void Saml2PostBinding_Bind()
        {
            var message = new Saml2MessageImplementation
            {
                XmlData = "<root><content>data</content></root>",
                DestinationUrl = new Uri("http://www.example.com/acs"),
                MessageName = "SAMLMessageName"
            };

            var result = Saml2Binding.Get(Saml2BindingType.HttpPost).Bind(message);

            var expected = new CommandResult()
            {
                ContentType = "text/html",
                Content = @"<?xml version=""1.0"" encoding=""UTF-8""?>
            <!DOCTYPE html PUBLIC ""-//W3C//DTD XHTML 1.1//EN""
            ""http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"">
            <html xmlns=""http://www.w3.org/1999/xhtml"" xml:lang=""en"">
            <body onload=""document.forms[0].submit()"">
            <noscript>
            <p>
            <strong>Note:</strong> Since your browser does not support JavaScript,
            you must press the Continue button once to proceed.
            </p>
            </noscript>
            <form action=""http://www.example.com/acs"" method=""post"">
            <div>
            <input type=""hidden"" name=""SAMLMessageName""
            value=""PHJvb3Q+PGNvbnRlbnQ+ZGF0YTwvY29udGVudD48L3Jvb3Q+""/>
            </div>
            <noscript>
            <div>
            <input type=""submit"" value=""Continue""/>
            </div>
            </noscript>
            </form>
            </body>
            </html>"
            };

            result.ShouldBeEquivalentTo(expected);
        }
        private static void ApplyCookies(CommandResult commandResult, IOwinContext context, IDataProtector dataProtector)
        {
            var serializedCookieData = commandResult.GetSerializedRequestState();

            if (serializedCookieData != null)
            {
                var protectedData = HttpRequestData.ConvertBinaryData(
                        dataProtector.Protect(serializedCookieData));

                context.Response.Cookies.Append(
                    commandResult.SetCookieName,
                    protectedData,
                    new CookieOptions()
                    {
                        HttpOnly = true,
                    });
            }

            commandResult.ApplyClearCookie(context);
        }
        public void CommandResultExtensions_Apply_Cookie()
        {
            var cr = new CommandResult()
            {
                SetCookieData = "???>>>SomeData",
                SetCookieName = "CookieName"
            };

            var context = OwinTestHelpers.CreateOwinContext();

            var dataProtector = new StubDataProtector();
            cr.Apply(context, dataProtector);

            var setCookieHeader = context.Response.Headers["Set-Cookie"];

            var protectedData = StubDataProtector.Protect(cr.SetCookieData);

            var expected = $"CookieName={protectedData}; path=/; HttpOnly";

            setCookieHeader.Should().Be(expected);
        }
예제 #13
0
        public override CommandResult Bind(ISaml2Message message)
        {
            if(message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            var xml = message.ToXml();
            if(message.SigningCertificate != null)
            {
                var xmlDoc = new XmlDocument()
                {
                    PreserveWhitespace = true
                };

                xmlDoc.LoadXml(xml);
                xmlDoc.Sign(message.SigningCertificate, true);
                xml = xmlDoc.OuterXml;
            }

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

            var relayStateHtml = string.IsNullOrEmpty(message.RelayState) ? null
                : string.Format(CultureInfo.InvariantCulture, PostHtmlRelayStateFormatString, message.RelayState);

            var cr = new CommandResult()
            {
                ContentType = "text/html",
                Content = String.Format(
                    CultureInfo.InvariantCulture,
                    PostHtmlFormatString,
                    message.DestinationUrl,
                    relayStateHtml,
                    message.MessageName,
                    encodedXml)
            };

            return cr;
        }
        public void CommandResultExtensions_Apply_Content()
        {
            var cr = new CommandResult()
            {
                Content = "Some Content!",
                ContentType = "application/whatever+text"
            };

            var context = OwinTestHelpers.CreateOwinContext();

            cr.Apply(context, null);

            context.Response.StatusCode.Should().Be(200);
            context.Response.ContentType.Should().Be("application/whatever+text");
            context.Response.Body.Seek(0, SeekOrigin.Begin);

            using (var reader = new StreamReader(context.Response.Body))
            {
                var bodyText = reader.ReadToEnd();
                bodyText.Should().Be("Some Content!");
            }
        }
예제 #15
0
        public CommandResult Run(HttpRequestData request, IOptions options)
        {
            if(options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            var urls = new AuthServicesUrls(request, options);

            var metadata = options.SPOptions.CreateMetadata(urls);
            options.Notifications.MetadataCreated(metadata, urls);

            var result = new CommandResult()
            {
                Content = metadata.ToXmlString(options.SPOptions.SigningServiceCertificate),
                ContentType = "application/samlmetadata+xml"
            };

            options.Notifications.MetadataCommandResultCreated(result);

            return result;
        }
예제 #16
0
        public void SignInCommand_Run_ReturnsAuthnRequestForDefaultIdp()
        {
            var defaultDestination = Options.FromConfiguration.IdentityProviders.Default.SingleSignOnServiceUrl;

            var subject = new SignInCommand().Run(
                new HttpRequestData("GET", new Uri("http://example.com")),
                Options.FromConfiguration);

            var expected = new CommandResult()
            {
                HttpStatusCode = HttpStatusCode.SeeOther,
                Cacheability = (Cacheability) HttpCacheability.NoCache,
                Location = new Uri(defaultDestination + "?SAMLRequest=XYZ")
            };

            subject.ShouldBeEquivalentTo(expected, options => options.Excluding(cr => cr.Location));
            subject.Location.Host.Should().Be(defaultDestination.Host);

            var queries = HttpUtility.ParseQueryString(subject.Location.Query);

            queries.Should().HaveCount(1);
            queries.Keys[0].Should().Be("SAMLRequest");
            queries[0].Should().NotBeEmpty();
        }
예제 #17
0
        private static CommandResult InitiateLogout(HttpRequestData request, Uri returnUrl, IOptions options)
        {
            string idpEntityId = null;
            Claim sessionIndexClaim = null;
            if (request.User != null)
            {
                idpEntityId = request.User.FindFirst(AuthServicesClaimTypes.LogoutNameIdentifier)?.Issuer;
                sessionIndexClaim = request.User.FindFirst(AuthServicesClaimTypes.SessionIndex);
            }

            CommandResult commandResult;
            IdentityProvider idp;
            if(idpEntityId != null
                && options.IdentityProviders.TryGetValue(new EntityId(idpEntityId), out idp)
                && sessionIndexClaim != null
                && idp.SingleLogoutServiceUrl != null
                && options.SPOptions.SigningServiceCertificate != null
                && !idp.DisableOutboundLogoutRequests)
            {
                var logoutRequest = idp.CreateLogoutRequest(request.User);

                commandResult = Saml2Binding.Get(idp.SingleLogoutServiceBinding)
                    .Bind(logoutRequest);

                commandResult.RelayState = logoutRequest.RelayState;
                commandResult.RequestState = new StoredRequestState(
                    idp.EntityId,
                    returnUrl,
                    logoutRequest.Id,
                    null);

                if (!options.SPOptions.Compatibility.DisableLogoutStateCookie)
                {
                    commandResult.SetCookieName = "Kentor." + logoutRequest.RelayState;
                }
            }
            else
            {
                commandResult = new CommandResult
                {
                    HttpStatusCode = HttpStatusCode.SeeOther,
                    Location = returnUrl
                };
            }

            commandResult.TerminateLocalSession = true;

            return commandResult;
        }
예제 #18
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;
        }
        public void Saml2PostBinding_Bind_SignsXml()
        {
            var message = new Saml2MessageImplementation
            {
                DestinationUrl = new Uri("http://www.example.com/acs"),
                XmlData = "<root ID=\"id\"><content>data</content></root>",
                MessageName = "SAMLMessageName",
                RelayState = "ABC1234",
                SigningCertificate = SignedXmlHelper.TestCert
            };

            var signedXml = SignedXmlHelper.SignXml(message.XmlData, true);
            var expectedValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(signedXml));

            var result = Saml2Binding.Get(Saml2BindingType.HttpPost).Bind(message);

            var expected = new CommandResult()
            {
                ContentType = "text/html",
                Content = @"<?xml version=""1.0"" encoding=""UTF-8""?>
            <!DOCTYPE html PUBLIC ""-//W3C//DTD XHTML 1.1//EN""
            ""http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"">
            <html xmlns=""http://www.w3.org/1999/xhtml"" xml:lang=""en"">
            <body onload=""document.forms[0].submit()"">
            <noscript>
            <p>
            <strong>Note:</strong> Since your browser does not support JavaScript,
            you must press the Continue button once to proceed.
            </p>
            </noscript>
            <form action=""http://www.example.com/acs"" method=""post"">
            <div>
            <input type=""hidden"" name=""RelayState"" value=""ABC1234""/>
            <input type=""hidden"" name=""SAMLMessageName""
            value=""" + expectedValue + @"""/>
            </div>
            <noscript>
            <div>
            <input type=""submit"" value=""Continue""/>
            </div>
            </noscript>
            </form>
            </body>
            </html>"
            };

            result.ShouldBeEquivalentTo(expected);
        }
        public void LogoutCommand_Run_ReturnsLogoutRequest()
        {
            var user = new ClaimsPrincipal(
                new ClaimsIdentity(new Claim[]
                {
                    new Claim(AuthServicesClaimTypes.LogoutNameIdentifier, ",,,,NameId", null, "https://idp.example.com"),
                    new Claim(AuthServicesClaimTypes.SessionIndex, "SessionId", null, "https://idp.example.com")
                }, "Federation"));

            var request = new HttpRequestData("GET", new Uri("http://sp-internal.example.com/AuthServices/Logout"));
            request.User = user;

            var options = StubFactory.CreateOptions();
            options.SPOptions.ServiceCertificates.Add(SignedXmlHelper.TestCert);
            options.SPOptions.PublicOrigin = new Uri("https://sp.example.com/");

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

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

            actual.Should().BeSameAs(notifiedCommandResult);

            var expected = new CommandResult
            {
                HttpStatusCode = HttpStatusCode.SeeOther,
                TerminateLocalSession = true,
                // Deliberately not comparing Location.
                // Deliberately not comparing SetCookieName.
                RequestState = new StoredRequestState(
                    new EntityId("https://idp.example.com"),
                    new Uri("https://sp.example.com/"),
                    null,
                    null)
            };

            actual.ShouldBeEquivalentTo(expected, opt => opt
                .Excluding(cr => cr.Location)
                .Excluding(cr => cr.SetCookieName)
                .Excluding(cr => cr.RequestState.MessageId));

            var relayState = HttpUtility.ParseQueryString(actual.Location.Query)["RelayState"];
            actual.SetCookieName.Should().Be("Kentor." + relayState);
            actual.Location.GetLeftPart(UriPartial.Path).Should().Be("https://idp.example.com/logout");
        }
        public void Saml2ArtifactBinding_Bind()
        {
            var message = new Saml2MessageImplementation
            {
                DestinationUrl = new Uri("http://example.com/destination"),
                MessageName = "ShouldBeIgnored",
                RelayState = "ABC& needs escape",
                XmlData = "<XML />",
                Issuer = new EntityId("http://idp.example.com"),
            };

            var result = Saml2Binding.Get(Saml2BindingType.Artifact).Bind(message);

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

            result.ShouldBeEquivalentTo(expected, opt => opt.Excluding(r => r.Location));

            result.Location.Query.Count(c => c == '=').Should().Be(2, "there are 2 params and = inside values should have been escaped");
            var query = HttpUtility.ParseQueryString(result.Location.Query);

            Uri.UnescapeDataString(query["RelayState"]).Should().Be(message.RelayState);

            var artifact = Convert.FromBase64String(
                Uri.UnescapeDataString(query["SAMLart"]));

            ISaml2Message storedMessage;
            Saml2ArtifactBinding.PendingMessages.TryRemove(artifact, out storedMessage)
                .Should().BeTrue();

            storedMessage.Should().BeSameAs(message);
        }
        public void Saml2RedirectBinding_Bind_With_RelayState()
        {
            var message = new Saml2MessageImplementation
            {
                XmlData = "Data",
                RelayState = "SomeState that needs escaping #%=3",
                DestinationUrl = new Uri("http://host"),
                MessageName = "SAMLRequest"
            };

            var expected = new CommandResult()
            {
                Location = new Uri("http://host?SAMLRequest=c0ksSQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%3D%3D"
                + "&RelayState=" + Uri.EscapeDataString(message.RelayState)),
                HttpStatusCode = System.Net.HttpStatusCode.SeeOther
            };

            var result = Saml2Binding.Get(Saml2BindingType.HttpRedirect).Bind(message);

            result.ShouldBeEquivalentTo(expected);
        }
예제 #23
0
        public void AcsCommand_Run_WithReturnUrl_SuccessfulResult()
        {
            var idp = Options.FromConfiguration.IdentityProviders.Default;
            var request = idp.CreateAuthenticateRequest(
                new Uri("http://localhost/testUrl.aspx"),
                StubFactory.CreateAuthServicesUrls());

            var response =
            @"<saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol""
                xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion""
                ID = """ + MethodBase.GetCurrentMethod().Name + @""" InResponseTo = """ + request.Id + @""" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z"">
                <saml2:Issuer>
                    https://idp.example.com
                </saml2:Issuer>
                <saml2p:Status>
                    <saml2p:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Success"" />
                </saml2p:Status>
                <saml2:Assertion
                Version=""2.0"" ID=""" + MethodBase.GetCurrentMethod().Name + @"_Assertion2""
                IssueInstant=""2013-09-25T00:00:00Z"">
                    <saml2:Issuer>https://idp.example.com</saml2:Issuer>
                    <saml2:Subject>
                        <saml2:NameID>SomeUser</saml2:NameID>
                        <saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" />
                    </saml2:Subject>
                    <saml2:Conditions NotOnOrAfter=""2100-01-01T00:00:00Z"" />
                </saml2:Assertion>
            </saml2p:Response>";

            var formValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(
                SignedXmlHelper.SignXml(response)));

            var r = new HttpRequestData(
                "POST",
                new Uri("http://localhost"),
                "/ModulePath",
                new KeyValuePair<string, string[]>[]
                {
                    new KeyValuePair<string, string[]>("SAMLResponse", new string[] { formValue })
                });

            var ids = new ClaimsIdentity[] { new ClaimsIdentity("Federation"), new ClaimsIdentity("ClaimsAuthenticationManager") };
            ids[0].AddClaim(new Claim(ClaimTypes.NameIdentifier, "SomeUser", null, "https://idp.example.com"));
            ids[1].AddClaim(new Claim(ClaimTypes.Role, "RoleFromClaimsAuthManager", null, "ClaimsAuthenticationManagerStub"));

            var expected = new CommandResult()
            {
                Principal = new ClaimsPrincipal(ids),
                HttpStatusCode = HttpStatusCode.SeeOther,
                Location = new Uri("http://localhost/testUrl.aspx")
            };

            new AcsCommand().Run(r, StubFactory.CreateOptions())
                .ShouldBeEquivalentTo(expected, opt => opt.IgnoringCyclicReferences());
        }
        public void LogoutCommand_Run_HandlesLogoutResponse_InPost()
        {
            var relayState = "TestState";
            var response = new Saml2LogoutResponse(Saml2StatusCode.Success)
            {
                DestinationUrl = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer = new EntityId("https://idp.example.com"),
                InResponseTo = new Saml2Id(),
                SigningCertificate = SignedXmlHelper.TestCert
            };

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

            var responseData = 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[]>("SAMLResponse", new[] { responseData }),
                    new KeyValuePair<string, string[]>("RelayState", new[] { relayState })
                },
                Enumerable.Empty<KeyValuePair<string, string>>(),
                null);

            httpRequest.StoredRequestState = new StoredRequestState(null, new Uri("http://loggedout.example.com"), null, null);

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

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

            var expected = new CommandResult
            {
                Location = new Uri("http://loggedout.example.com"),
                HttpStatusCode = HttpStatusCode.SeeOther,
                ClearCookieName = "Kentor." + relayState
            };

            actual.ShouldBeEquivalentTo(expected);
        }
예제 #25
0
        private static CommandResult InitiateLogout(HttpRequestData request, string returnPath, IOptions options)
        {
            var idpEntityId = ClaimsPrincipal.Current.FindFirst(AuthServicesClaimTypes.LogoutNameIdentifier)?.Issuer
                ?? ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier)?.Issuer;

            CommandResult commandResult;
            IdentityProvider idp;
            if(idpEntityId != null
                && options.IdentityProviders.TryGetValue(new EntityId(idpEntityId), out idp)
                && ClaimsPrincipal.Current.FindFirst(AuthServicesClaimTypes.SessionIndex) != null
                && idp.SingleLogoutServiceUrl != null
                && options.SPOptions.SigningServiceCertificate != null)
            {
                var logoutRequest = idp.CreateLogoutRequest();

                commandResult = Saml2Binding.Get(Saml2BindingType.HttpRedirect)
                    .Bind(logoutRequest);

                commandResult.RequestState = new StoredRequestState(
                    idp.EntityId,
                    GetReturnUrl(request, returnPath, options),
                    logoutRequest.Id,
                    null);

                commandResult.SetCookieName = "Kentor." + logoutRequest.RelayState;
            }
            else
            {
                commandResult = new CommandResult
                {
                    HttpStatusCode = HttpStatusCode.SeeOther,
                    Location = GetReturnUrl(request, returnPath, options)
                };
            }

            commandResult.TerminateLocalSession = true;

            return commandResult;
        }
        private void LogoutCommand_Run_LocalLogout(IOptions options, ClaimsPrincipal user)
        {
            var subject = CommandFactory.GetCommand(CommandFactory.LogoutCommandName);

            var actual = subject.Run(
                new HttpRequestData("GET", new Uri("http://localhost/Logout?ReturnUrl=LoggedOut"))
                {
                    User = user
                },
                options);

            var expected = new CommandResult()
            {
                HttpStatusCode = HttpStatusCode.SeeOther,
                Location = new Uri("http://localhost/LoggedOut"),
                TerminateLocalSession = true
            };

            actual.ShouldBeEquivalentTo(expected);
        }
        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);
        }
        public void LogoutCommand_Run_ReturnsLogoutRequest_IgnoresThreadPrincipal()
        {
            Thread.CurrentPrincipal = new ClaimsPrincipal(
                new ClaimsIdentity(new Claim[]
                {
                    new Claim(ClaimTypes.NameIdentifier, "PrincipalWithNoSession"),
                }, "Federation"));

            var user = new ClaimsPrincipal(
                new ClaimsIdentity(new Claim[]
                {
                    new Claim(ClaimTypes.NameIdentifier, "ApplicationNameId"),
                    new Claim(AuthServicesClaimTypes.LogoutNameIdentifier, ",,,,Saml2NameId", null, "https://idp.example.com"),
                    new Claim(AuthServicesClaimTypes.SessionIndex, "SessionId", null, "https://idp.example.com")
                }, "Federation"));

            var request = new HttpRequestData("GET", new Uri("http://sp.example.com/AuthServices/Logout"));
            request.User = user;

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

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

            var expected = new CommandResult
            {
                HttpStatusCode = HttpStatusCode.SeeOther,
                TerminateLocalSession = true,
                // Deliberately not comparing Location.
                RequestState = new StoredRequestState(
                    new EntityId("https://idp.example.com"),
                    new Uri("http://sp.example.com/"),
                    null,
                    null)
            };

            actual.ShouldBeEquivalentTo(expected, opt => opt
                .Excluding(cr => cr.Location)
                .Excluding(cr => cr.SetCookieName)
                .Excluding(cr => cr.RequestState.MessageId));
            actual.Location.GetLeftPart(UriPartial.Path).Should().Be("https://idp.example.com/logout");
        }
        public void LogoutCommand_Run_HandlesLogoutResponse()
        {
            var relayState = "MyRelayState";
            var response = new Saml2LogoutResponse(Saml2StatusCode.Success)
            {
                DestinationUrl = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer = new EntityId("https://idp.example.com"),
                InResponseTo = new Saml2Id(),
                SigningCertificate = SignedXmlHelper.TestCert,
                RelayState = relayState
            };

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

            var request = new HttpRequestData("GET",
                bindResult.Location,
                "http://sp-internal.example.com/path/AuthServices",
                null,
                new StoredRequestState(null, new Uri("http://loggedout.example.com"), null, null));

            var options = StubFactory.CreateOptions();
            options.SPOptions.PublicOrigin = new Uri("https://sp.example.com/path/");

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

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

            actual.Should().BeSameAs(notifiedCommandResult);

            var expected = new CommandResult
            {
                Location = new Uri("http://loggedout.example.com"),
                HttpStatusCode = HttpStatusCode.SeeOther,
                ClearCookieName = "Kentor." + relayState
            };

            actual.ShouldBeEquivalentTo(expected);
        }
        public void CommandResultHttp_Apply_Content()
        {
            var response = Substitute.For<HttpResponseBase>();

            var commandResult = new CommandResult
            {
                Content = "Some Content!",
                ContentType = "text"
            };

            commandResult.Apply(response);

            response.Received().ContentType = "text";
            response.Received().Write("Some Content!");
        }