예제 #1
0
        /// <summary>
        /// Run the command, creating and returning the service provider metadata.
        /// </summary>
        /// <param name="request">Request data.</param>
        /// <param name="options">Options</param>
        /// <returns>CommandResult</returns>
        public virtual CommandResult Run(HttpRequestData request, IOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            var urls = new Saml2Urls(request, options);

            var metadata = options.SPOptions.CreateMetadata(urls);

            options.Notifications.MetadataCreated(metadata, urls);

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

            var fileName = CreateFileName(options.SPOptions.EntityId.Id);

            result.Headers.Add("Content-Disposition", "attachment; filename=\"" + fileName + "\"");

            options.Notifications.MetadataCommandResultCreated(result);

            options.SPOptions.Logger.WriteInformation("Created metadata");

            return(result);
        }
예제 #2
0
        /// <summary>
        /// Run the command, creating and returning the service provider metadata.
        /// </summary>
        /// <param name="request">Request data.</param>
        /// <param name="options">Options</param>
        /// <returns>CommandResult</returns>
        public CommandResult Run(HttpRequestData request, IOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            var urls = new Saml2Urls(request, options);

            var metadata = options.SPOptions.CreateMetadata(urls);

            options.Notifications.MetadataCreated(metadata, urls);

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

            options.Notifications.MetadataCommandResultCreated(result);

            options.SPOptions.Logger.WriteInformation("Created metadata");

            return(result);
        }
예제 #3
0
        private static CommandResult RedirectToDiscoveryService(
            string returnPath,
            SPOptions spOptions,
            Saml2Urls saml2Urls,
            IDictionary <string, string> relayData)
        {
            string returnUrl = saml2Urls.SignInUrl.OriginalString;

            var relayState = SecureKeyGenerator.CreateRelayState();

            returnUrl += "?RelayState=" + Uri.EscapeDataString(relayState);

            var redirectLocation = string.Format(
                CultureInfo.InvariantCulture,
                "{0}?entityID={1}&return={2}&returnIDParam=idp",
                spOptions.DiscoveryServiceUrl,
                Uri.EscapeDataString(spOptions.EntityId.Id),
                Uri.EscapeDataString(returnUrl));

            var requestState = new StoredRequestState(
                null,
                returnPath == null ? null : new Uri(returnPath, UriKind.RelativeOrAbsolute),
                null,
                relayData);

            return(new CommandResult()
            {
                HttpStatusCode = HttpStatusCode.SeeOther,
                Location = new Uri(redirectLocation),
                RequestState = requestState,
                SetCookieName = StoredRequestState.CookieNameBase + relayState
            });
        }
예제 #4
0
        public static CommandResult Run(
            EntityId idpEntityId,
            string returnPath,
            HttpRequestData request,
            IOptions options,
            IDictionary <string, string> relayData)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            var urls = new Saml2Urls(request, options);

            IdentityProvider idp = options.Notifications.SelectIdentityProvider(idpEntityId, relayData);

            if (idp == null)
            {
                var idpEntityIdString = idpEntityId?.Id;
                if (idpEntityIdString == null)
                {
                    if (options.SPOptions.DiscoveryServiceUrl != null)
                    {
                        var commandResult = RedirectToDiscoveryService(returnPath, options.SPOptions, urls, relayData);
                        options.Notifications.SignInCommandResultCreated(commandResult, relayData);
                        options.SPOptions.Logger.WriteInformation("Redirecting to Discovery Service to select Idp.");
                        return(commandResult);
                    }

                    idp = options.IdentityProviders.Default;
                    options.SPOptions.Logger.WriteVerbose(
                        "No specific idp requested and no Discovery Service configured. " +
                        "Falling back to use configured default Idp " + idp.EntityId.Id);
                }
                else
                {
                    if (!options.IdentityProviders.TryGetValue(idpEntityId, out idp))
                    {
                        throw new InvalidOperationException("Unknown idp " + idpEntityIdString);
                    }
                }
            }

            /*var returnUrl = string.IsNullOrEmpty(returnPath)
             *  ? null
             *  : new Uri(returnPath, UriKind.RelativeOrAbsolute);*/

            var returnUrl       = request.QueryString["returnUrl"].FirstOrDefault();
            var errorUrl        = request.QueryString["errorUrl"].FirstOrDefault();
            var unauthorizedUrl = request.QueryString["unauthorizedUrl"].FirstOrDefault();

            // some hacks
            var returnUri = new Uri($"{options.SPOptions.ReturnUrl.GetLeftPart(UriPartial.Path)}?returnUrl={returnUrl}&errorUrl={errorUrl}&unauthorizedUrl={unauthorizedUrl}");

            options.SPOptions.Logger.WriteInformation("Initiating login to " + idp.EntityId.Id);
            return(InitiateLoginToIdp(options, relayData, urls, idp, returnUri));
        }
예제 #5
0
        public static CommandResult Run(
            EntityId idpEntityId,
            string returnPath,
            HttpRequestData request,
            IOptions options,
            IDictionary <string, string> relayData)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            var urls = new Saml2Urls(request, options);

            if (!string.IsNullOrEmpty(returnPath))
            {
                returnPath = options.Notifications.RewriteRedirectUrl(new Uri(returnPath, UriKind.RelativeOrAbsolute), urls.ApplicationUrl).ToString();
            }

            IdentityProvider idp = options.Notifications.SelectIdentityProvider(idpEntityId, relayData);

            if (idp == null)
            {
                var idpEntityIdString = idpEntityId?.Id;
                if (idpEntityIdString == null)
                {
                    if (options.SPOptions.DiscoveryServiceUrl != null)
                    {
                        var commandResult = RedirectToDiscoveryService(returnPath, options.SPOptions, urls, relayData);
                        options.Notifications.SignInCommandResultCreated(commandResult, relayData);
                        options.SPOptions.Logger.WriteInformation("Redirecting to Discovery Service to select Idp.");
                        return(commandResult);
                    }
                    idp = options.IdentityProviders.Default;
                    options.SPOptions.Logger.WriteVerbose(
                        "No specific idp requested and no Discovery Service configured. " +
                        "Falling back to use configured default Idp " + idp.EntityId.Id);
                }
                else
                {
                    if (!options.IdentityProviders.TryGetValue(idpEntityId, out idp))
                    {
                        throw new InvalidOperationException("Unknown idp " + idpEntityIdString);
                    }
                }
            }

            var returnUrl = string.IsNullOrEmpty(returnPath)
                ? null
                : new Uri(returnPath, UriKind.RelativeOrAbsolute);

            options.SPOptions.Logger.WriteInformation("Initiating login to " + idp.EntityId.Id);
            return(InitiateLoginToIdp(options, relayData, urls, idp, returnUrl));
        }
예제 #6
0
        public static CommandResult Run(
            HttpRequestData request,
            string returnPath,
            IOptions options)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

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

            CommandResult commandResult;
            var           returnUrl = GetReturnUrl(request, returnPath, options);
            var           binding   = options.Notifications.GetBinding(request);

            if (binding != null)
            {
                var unbindResult = binding.Unbind(request, options);
                options.Notifications.MessageUnbound(unbindResult);

                switch (unbindResult.Data.LocalName)
                {
                case "LogoutRequest":
                    VerifyMessageIsSigned(unbindResult, options);
                    commandResult = HandleRequest(unbindResult, request, options);
                    break;

                case "LogoutResponse":
                    if (!options.SPOptions.Compatibility.AcceptUnsignedLogoutResponses)
                    {
                        VerifyMessageIsSigned(unbindResult, options);
                    }
                    var storedRequestState = options.Notifications.GetLogoutResponseState(request);
                    var urls = new Saml2Urls(request, options);
                    commandResult = HandleResponse(unbindResult, storedRequestState, options, returnUrl, urls);
                    break;

                default:
                    throw new NotImplementedException();
                }
            }
            else
            {
                commandResult = InitiateLogout(request, returnUrl, options, true);
            }
            options.Notifications.LogoutCommandResultCreated(commandResult);
            return(commandResult);
        }
예제 #7
0
        private static CommandResult InitiateLoginToIdp(IOptions options, IDictionary <string, string> relayData,
                                                        Saml2Urls urls, IdentityProvider idp, Uri returnUrl)
        {
            var authnRequest = idp.CreateAuthenticateRequest(urls);

            options.Notifications.AuthenticationRequestCreated(authnRequest, idp, relayData);

            var commandResult = idp.Bind(authnRequest);

            commandResult.RequestState = new StoredRequestState(
                idp.EntityId, returnUrl, authnRequest.Id, relayData);
            commandResult.SetCookieName = StoredRequestState.CookieNameBase + authnRequest.RelayState;

            options.Notifications.SignInCommandResultCreated(commandResult, relayData);

            return(commandResult);
        }
예제 #8
0
        private static CommandResult HandleResponse(
            UnbindResult unbindResult,
            StoredRequestState storedRequestState,
            IOptions options,
            Uri returnUrl,
            Saml2Urls urls)
        {
            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     = StoredRequestState.CookieNameBase + unbindResult.RelayState;
                commandResult.SetCookieSecureFlag = urls.LogoutUrl.IsHttps();
            }
            commandResult.Location = storedRequestState?.ReturnUrl ?? returnUrl;

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

            return(commandResult);
        }
예제 #9
0
        public static CommandResult InitiateLogout(HttpRequestData request, Uri returnUrl, IOptions options, bool terminateLocalSession)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

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

            string idpEntityId       = null;
            Claim  sessionIndexClaim = null;

            if (request.User != null)
            {
                idpEntityId       = request.User.FindFirst(Saml2ClaimTypes.LogoutNameIdentifier)?.Issuer;
                sessionIndexClaim = request.User.FindFirst(Saml2ClaimTypes.SessionIndex);
            }

            var knownIdp = options.IdentityProviders.TryGetValue(new EntityId(idpEntityId), out IdentityProvider idp);

            options.SPOptions.Logger.WriteVerbose("Initiating logout, checking requirements for federated logout"
                                                  + "\n  Issuer of LogoutNameIdentifier claim (should be Idp entity id): " + idpEntityId
                                                  + "\n  Issuer is a known Idp: " + knownIdp
                                                  + "\n  Session index claim (should have a value): " + sessionIndexClaim
                                                  + "\n  Idp has SingleLogoutServiceUrl: " + idp?.SingleLogoutServiceUrl?.OriginalString
                                                  + "\n  There is a signingCertificate in SPOptions: " + (options.SPOptions.SigningServiceCertificate != null)
                                                  + "\n  Idp configured to DisableOutboundLogoutRequests (should be false): " + idp?.DisableOutboundLogoutRequests);

            CommandResult commandResult;

            if (idpEntityId != null &&
                knownIdp &&
                sessionIndexClaim != null &&
                idp.SingleLogoutServiceUrl != null &&
                options.SPOptions.SigningServiceCertificate != null &&
                !idp.DisableOutboundLogoutRequests)
            {
                var logoutRequest = idp.CreateLogoutRequest(request.User);

                options.Notifications.LogoutRequestCreated(logoutRequest, request.User, idp);

                commandResult = Saml2Binding.Get(idp.SingleLogoutServiceBinding)
                                .Bind(logoutRequest, options.SPOptions.Logger, options.Notifications.LogoutRequestXmlCreated);

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

                if (!options.SPOptions.Compatibility.DisableLogoutStateCookie)
                {
                    commandResult.SetCookieName = StoredRequestState.CookieNameBase + logoutRequest.RelayState;

                    var urls = new Saml2Urls(request, options);
                    commandResult.SetCookieSecureFlag = urls.LogoutUrl.IsHttps();
                }

                commandResult.TerminateLocalSession = terminateLocalSession;

                options.SPOptions.Logger.WriteInformation("Sending logout request to " + idp.EntityId.Id);
            }
            else
            {
                commandResult = new CommandResult
                {
                    HttpStatusCode        = HttpStatusCode.SeeOther,
                    Location              = returnUrl,
                    TerminateLocalSession = terminateLocalSession
                };

                options.SPOptions.Logger.WriteInformation(
                    "Federated logout not possible, redirecting to post-logout"
                    + (terminateLocalSession ? " and clearing local session" : ""));
            }

            return(commandResult);
        }
예제 #10
0
        private static CommandResult InitiateLoginToIdp(IOptions options, IDictionary <string, string> relayData, Saml2Urls urls, IdentityProvider idp, Uri returnUrl, HttpRequestData request)
        {
            var authnRequest = idp.CreateAuthenticateRequest(urls);

            var forceAuthnString = request.QueryString["ForceAuthn"].SingleOrDefault();

            if (!string.IsNullOrWhiteSpace(forceAuthnString))
            {
                authnRequest.ForceAuthentication = bool.Parse(forceAuthnString);
            }

            var isPassiveString = request.QueryString["IsPassive"].SingleOrDefault();

            if (!string.IsNullOrWhiteSpace(isPassiveString))
            {
                authnRequest.IsPassive = bool.Parse(isPassiveString);
            }

            options.Notifications.AuthenticationRequestCreated(authnRequest, idp, relayData);

            var commandResult = idp.Bind(authnRequest);

            commandResult.RequestState = new StoredRequestState(
                idp.EntityId, returnUrl, authnRequest.Id, relayData);

            commandResult.SetCookieSecureFlag = urls.AssertionConsumerServiceUrl.IsHttps();
            commandResult.SetCookieName       = StoredRequestState.CookieNameBase + authnRequest.RelayState;

            options.Notifications.SignInCommandResultCreated(commandResult, relayData);

            return(commandResult);
        }
예제 #11
0
파일: AcsCommand.cs 프로젝트: zxjon22/Saml2
        /// <summary>
        /// Run the command, initiating or handling the assertion consumer sequence.
        /// </summary>
        /// <param name="request">Request data.</param>
        /// <param name="options">Options</param>
        /// <returns>CommandResult</returns>
        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 = options.Notifications.GetBinding(request);

            if (binding != null)
            {
                UnbindResult unbindResult = null;
                try
                {
                    unbindResult = binding.Unbind(request, options);

                    options.Notifications.MessageUnbound(unbindResult);

                    var samlResponse = new Saml2Response(unbindResult.Data, request.StoredRequestState?.MessageId, options);

                    var idpContext = GetIdpContext(unbindResult.Data, request, options);

                    var result = ProcessResponse(options, samlResponse, request.StoredRequestState, idpContext, unbindResult.RelayState);

                    if (request.StoredRequestState != null)
                    {
                        var urls = new Saml2Urls(request, options);
                        result.ClearCookieName     = StoredRequestState.CookieNameBase + unbindResult.RelayState;
                        result.SetCookieSecureFlag = urls.AssertionConsumerServiceUrl.IsHttps();
                    }

                    options.Notifications.AcsCommandResultCreated(result, samlResponse);

                    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();
        }