예제 #1
0
파일: Logout.cs 프로젝트: jbparker/SAML2
 public Logout(IInternalLogger logger, SAML2.Config.Saml2Configuration config)
 {
     if (logger == null) throw new ArgumentNullException("logger");
     if (config == null) throw new ArgumentNullException("config"); 
     this.logger = logger;
     this.config = config;
 }
예제 #2
0
 /// <summary>
 /// Initializes a new instance of the <see cref="Saml20Assertion"/> class.
 /// </summary>
 /// <param name="assertion">The assertion.</param>
 /// <param name="trustedSigners">If <code>null</code>, the signature of the given assertion is not verified.</param>
 /// <param name="quirksMode">if set to <c>true</c> quirks mode is enabled.</param>
 public Saml20Assertion(XmlElement assertion, IEnumerable<AsymmetricAlgorithm> trustedSigners, bool quirksMode, Saml2Configuration config)
 {
     _quirksMode = quirksMode;
     _profile = null;
     _config = config;
     LoadXml(assertion, trustedSigners, config);
 }
예제 #3
0
 public MetadataUtils(Config.Saml2Configuration configuration, Logging.IInternalLogger logger)
 {
     if (configuration == null) throw new ArgumentNullException("configuration");
     if (logger == null) throw new ArgumentNullException("logger");
     this.configuration = configuration;
     this.logger = logger;
 }
예제 #4
0
 /// <summary>
 /// Constructor for LoginHandler
 /// </summary>
 /// <param name="configuration">SamlConfiguration</param>
 /// <param name="getFromCache">May be null unless doing artifact binding, this function will be called for artifact resolution</param>
 public SamlLoginHandler(SamlAuthenticationOptions options)
 {
     if (options == null) throw new ArgumentNullException("options");
     this.options = options;
     configuration = options.Configuration;
     getFromCache = options.GetFromCache;
     setInCache = options.SetInCache;
     session = options.Session;
 }
예제 #5
0
 /// <summary>
 /// Initializes a new instance of the <see cref="HttpArtifactBindingBuilder"/> class.
 /// </summary>
 /// <param name="context">The current http context.</param>
 /// <param name="redirect">Action to perform when redirecting. Parameter will be destination URL</param>
 /// <param name="sendResponseMessage">Action to send messages to response stream</param>
 public HttpArtifactBindingBuilder(Saml2Configuration config, Action<string> redirect, Action<string> sendResponseMessage)
 {
     if (config == null) throw new ArgumentNullException("config");
     if (redirect == null) throw new ArgumentNullException("redirect");
     if (sendResponseMessage == null) throw new ArgumentNullException("sendResponseMessage"); 
     this.redirect = redirect;
     this.config = config;
     this.sendResponseMessage = sendResponseMessage;
 }
예제 #6
0
        public Saml20SignonHandler(Saml2Configuration config)
        {
            _certificate = config.ServiceProvider.SigningCertificate;

            // Read the proper redirect url from config
            try {
                RedirectUrl = config.ServiceProvider.Endpoints.DefaultSignOnEndpoint.RedirectUrl;
            }
            catch (Exception e) {
                Logger.Error(e.Message, e);
            }
        }
예제 #7
0
 public Logout(IInternalLogger logger, SAML2.Config.Saml2Configuration config)
 {
     if (logger == null)
     {
         throw new ArgumentNullException(nameof(logger));
     }
     if (config == null)
     {
         throw new ArgumentNullException(nameof(config));
     }
     this.logger = logger;
     this.config = config;
 }
예제 #8
0
        /// <summary>
        /// Handles a request.
        /// </summary>
        /// <param name="context">The context.</param>
        public void Handle(HttpContext context, Saml2Configuration config)
        {
            Logger.Debug(TraceMessages.SignOnHandlerCalled);
            var getFromCache = new Func<string, object>(context.Cache.Get);
            var setInCache = new Action<string, object, DateTime>((s, o, d) => context.Cache.Insert(s, o, null, d, Cache.NoSlidingExpiration));
            var loginAction = new Action<Saml20Assertion>(a => DoSignOn(context, a, config));
            var session = SessionToDictionary(context.Session);

            // Some IdP's are known to fail to set an actual value in the SOAPAction header
            // so we just check for the existence of the header field.
            if (Array.Exists(context.Request.Headers.AllKeys, s => s == SoapConstants.SoapAction)) {
                Utility.HandleSoap(
                    GetBuilder(context),
                    context.Request.InputStream,
                    config,
                    loginAction,
                    getFromCache,
                    setInCache,
                    session);
                return;
            }

            if (!string.IsNullOrEmpty(context.Request.Params["SAMLart"])) {
                HandleArtifact(context, config, (c, s, conf) =>
                    Utility.HandleSoap(
                        GetBuilder(context),
                        s,
                        conf,
                        loginAction,
                        getFromCache,
                        setInCache,
                        session));
            }
            
            var samlResponse = context.Request.Params["SamlResponse"];
            if (!string.IsNullOrEmpty(samlResponse)) {
                var assertion = Utility.HandleResponse(config, samlResponse, session, getFromCache, setInCache);
                loginAction(assertion);
            } else {
                if (config.CommonDomainCookie.Enabled && context.Request.QueryString["r"] == null
                    && context.Request.Params["cidp"] == null) {
                    Logger.Debug(TraceMessages.CommonDomainCookieRedirectForDiscovery);
                    context.Response.Redirect(config.CommonDomainCookie.LocalReaderEndpoint);
                } else {
                    Logger.WarnFormat(ErrorMessages.UnauthenticatedAccess, context.Request.RawUrl);
                    SendRequest(context, config);
                }
            }
        }
예제 #9
0
        /// <summary>
        /// Handles a request.
        /// </summary>
        /// <param name="context">The context.</param>
        public void Handle(HttpContext context, Saml2Configuration config)
        {
            Logger.Debug(TraceMessages.LogoutHandlerCalled);

            // Some IDP's are known to fail to set an actual value in the SOAPAction header
            // so we just check for the existence of the header field.
            if (Array.Exists(context.Request.Headers.AllKeys, s => s == SoapConstants.SoapAction))
            {
                HandleSoap(context, context.Request.InputStream, config);
                return;
            }

            if (!string.IsNullOrEmpty(context.Request.Params["SAMLart"]))
            {
                HandleArtifact(context, ConfigurationFactory.Instance.Configuration, HandleSoap);
                return;
            }

            if (!string.IsNullOrEmpty(context.Request.Params["SAMLResponse"]))
            {
                HandleResponse(context, config);
            }
            else if (!string.IsNullOrEmpty(context.Request.Params["SAMLRequest"]))
            {
                HandleRequest(context);
            }
            else
            {
                IdentityProvider idpEndpoint = null;

                // context.Session[IDPLoginSessionKey] may be null if IIS has been restarted
                if (context.Session[IdpSessionIdKey] != null)
                {
                    idpEndpoint = IdpSelectionUtil.RetrieveIDPConfiguration((string)context.Session[IdpLoginSessionKey], config);
                }

                if (idpEndpoint == null)
                {
                    // TODO: Reconsider how to accomplish this.
                    context.User = null;
                    FormsAuthentication.SignOut();

                    Logger.ErrorFormat(ErrorMessages.UnknownIdentityProvider, string.Empty);
                    throw new Saml20Exception(string.Format(ErrorMessages.UnknownIdentityProvider, string.Empty));
                }

                TransferClient(idpEndpoint, context, config);
            }
        }
예제 #10
0
        /// <summary>
        /// Handles the selection of an IDP. If only one IDP is found, the user is automatically redirected to it.
        /// If several are found, and nothing indicates to which one the user should be sent, this method returns null.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="config">Configuration.  If null, configuration will be populated from application config</param>
        /// <returns>The <see cref="IdentityProvider"/>.</returns>
        public IdentityProvider RetrieveIDP(NameValueCollection allparams, NameValueCollection queryString, Saml2Configuration config, Action<string> redirectToSelection)
        {
            // If idpChoice is set, use it value
            if (!string.IsNullOrEmpty(allparams[IdpChoiceParameterName])) {
                logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromQueryString, allparams[IdpChoiceParameterName]);
                var endPoint = config.IdentityProviders.FirstOrDefault(x => x.Id == allparams[IdpChoiceParameterName]);
                if (endPoint != null) {
                    return endPoint;
                }
            }

            // If we have a common domain cookie, use it's value
            // It must have been returned from the local common domain cookie reader endpoint.
            if (!string.IsNullOrEmpty(queryString["_saml_idp"])) {
                var cdc = new Protocol.CommonDomainCookie(queryString["_saml_idp"]);
                if (cdc.IsSet) {
                    var endPoint = config.IdentityProviders.FirstOrDefault(x => x.Id == cdc.PreferredIDP);
                    if (endPoint != null) {
                        logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromCommonDomainCookie, cdc.PreferredIDP);
                        return endPoint;
                    }

                    logger.WarnFormat(ErrorMessages.CommonDomainCookieIdentityProviderInvalid, cdc.PreferredIDP);
                }
            }

            // If there is only one configured IdentityProviderEndpointElement lets just use that
            if (config.IdentityProviders.Count == 1 && config.IdentityProviders[0].Metadata != null) {
                logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromDefault, config.IdentityProviders[0].Name);
                return config.IdentityProviders[0];
            }

            // If one of the endpoints are marked with default, use that one
            var defaultIDP = config.IdentityProviders.FirstOrDefault(idp => idp.Default);
            if (defaultIDP != null) {
                logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromDefault, defaultIDP.Id);
                return defaultIDP;
            }

            // In case an IDP selection url has been configured, redirect to that one.
            if (!string.IsNullOrEmpty(config.IdentityProviders.SelectionUrl)) {
                logger.DebugFormat(TraceMessages.IdentityProviderRetreivedFromSelection, config.IdentityProviders.SelectionUrl);
                redirectToSelection(config.IdentityProviders.SelectionUrl);
                return null;
            }

            // If an IDPSelectionEvent handler is present, request the handler for an IDP endpoint to use.
            return IdpSelectionUtil.InvokeIDPSelectionEventHandler(config.IdentityProviders);
        }
예제 #11
0
 public Saml2Configuration TestEnvironmentConfiguration()
 {
     var myconfig = new Saml2Configuration
     {
         ServiceProvider = new ServiceProvider
         {
             SigningCertificate = new System.Security.Cryptography.X509Certificates.X509Certificate2("../../Metadata-Test/certificate.pfx", "", System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.MachineKeySet),
             Server = "http://localhost:7777/identity",
             Id = "http://localhost:7777/identity"
         }
     };
     myconfig.ServiceProvider.Endpoints.AddRange(new[] {
         new ServiceProviderEndpoint(EndpointType.SignOn, "/identity/login", "/identity", BindingType.Redirect),
         new ServiceProviderEndpoint(EndpointType.Logout, "/identity/logout", "/identity", BindingType.Redirect),
         new ServiceProviderEndpoint(EndpointType.Metadata, "/identity/metadata")
     });
     myconfig.IdentityProviders.AddByMetadata("..\\..\\Metadata-Test\\uat.xml");
     SAML2.Logging.LoggerProvider.Configuration = myconfig;
     return myconfig;
 }
예제 #12
0
파일: Startup.cs 프로젝트: jbparker/SAML2
 private Saml2Configuration GetSamlConfiguration()
 {
     var myconfig = new Saml2Configuration
     {
         ServiceProvider = new ServiceProvider
         {
             SigningCertificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(FileEmbeddedResource("SelfHostOwinSPExample.sts_dev_certificate.pfx"), "test1234", System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.MachineKeySet),
             Server = "https://localhost:44333/core",
             Id = "https://localhost:44333/core"
         },
         AllowedAudienceUris = new System.Collections.Generic.List<Uri>(new[] { new Uri("https://localhost:44333/core") })
     };
     myconfig.ServiceProvider.Endpoints.AddRange(new[] {
         new ServiceProviderEndpoint(EndpointType.SignOn, "/core/saml2/login", "/core"),
         new ServiceProviderEndpoint(EndpointType.Logout, "/core/saml2/logout", "/core"),
         new ServiceProviderEndpoint(EndpointType.Metadata, "/core/saml2/metadata")
     });
     myconfig.IdentityProviders.AddByMetadataDirectory("..\\..\\Metadata");
     //myconfig.IdentityProviders.AddByMetadataUrl(new Uri("https://tas.fhict.nl/identity/saml2/metadata"));
     myconfig.IdentityProviders.First().OmitAssertionSignatureCheck = true;
     myconfig.LoggingFactoryType = "SAML2.Logging.DebugLoggerFactory";
     return myconfig;
 }
예제 #13
0
        /// <summary>
        /// Validates the SAML20Federation configuration.
        /// </summary>
        /// <returns>True if validation passes, false otherwise</returns>
        public static bool ValidateConfiguration(Saml2Configuration config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config", ErrorMessages.ConfigMissingSaml2Element);
            }

            if (config.ServiceProvider == null)
            {
                throw new ArgumentOutOfRangeException("config", ErrorMessages.ConfigMissingServiceProviderElement);
            }

            if (string.IsNullOrEmpty(config.ServiceProvider.Id))
            {
                throw new ArgumentOutOfRangeException("config", ErrorMessages.ConfigMissingServiceProviderIdAttribute);
            }

            if (config.ServiceProvider.SigningCertificate == null)
            {
                throw new ArgumentOutOfRangeException("config", ErrorMessages.ConfigMissingSigningCertificateElement);
            }

            // This will throw if no certificate or multiple certificates are found
            var certificate = config.ServiceProvider.SigningCertificate;
            if (!certificate.HasPrivateKey)
            {
                throw new ArgumentOutOfRangeException("config", ErrorMessages.ConfigSigningCertificateMissingPrivateKey);
            }

            if (config.IdentityProviders == null)
            {
                throw new ArgumentOutOfRangeException("config", ErrorMessages.ConfigMissingIdentityProvidersElement);
            }

            return true;
        }
예제 #14
0
        /// <summary>
        /// Transfers the client.
        /// </summary>
        /// <param name="identityProvider">The identity provider.</param>
        /// <param name="request">The request.</param>
        /// <param name="context">The context.</param>
        private void TransferClient(IdentityProvider identityProvider, Saml20AuthnRequest request, HttpContext context, Saml2Configuration config)
        {
            IdentityProviderEndpoint destination = ConfigureRequest(identityProvider, request, context);

            switch (destination.Binding) {
            case BindingType.Redirect:
                Logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpRedirect);

                var redirectBuilder = new HttpRedirectBindingBuilder
                {
                    SigningKey = _certificate.PrivateKey,
                    Request = request.GetXml().OuterXml
                };

                Logger.DebugFormat(TraceMessages.AuthnRequestSent, redirectBuilder.Request);

                var redirectLocation = request.Destination + "?" + redirectBuilder.ToQuery();
                context.Response.Redirect(redirectLocation, true);
                break;
            case BindingType.Post:
                Logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpPost);

                var postBuilder = new HttpPostBindingBuilder(destination);

                // Honor the ForceProtocolBinding and only set this if it's not already set
                if (string.IsNullOrEmpty(request.ProtocolBinding)) {
                    request.ProtocolBinding = Saml20Constants.ProtocolBindings.HttpPost;
                }

                var requestXml = request.GetXml();
                XmlSignatureUtils.SignDocument(requestXml, request.Id, config.ServiceProvider.SigningCertificate);
                postBuilder.Request = requestXml.OuterXml;

                Logger.DebugFormat(TraceMessages.AuthnRequestSent, postBuilder.Request);

                context.Response.Write(postBuilder.GetPage());
                break;
            case BindingType.Artifact:
                Logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpArtifact);

                var artifactBuilder = GetBuilder(context);

                // Honor the ForceProtocolBinding and only set this if it's not already set
                if (string.IsNullOrEmpty(request.ProtocolBinding)) {
                    request.ProtocolBinding = Saml20Constants.ProtocolBindings.HttpArtifact;
                }

                Logger.DebugFormat(TraceMessages.AuthnRequestSent, request.GetXml().OuterXml);

                artifactBuilder.RedirectFromLogin(destination, request, context.Request.Params["relayState"], (s, o) => context.Cache.Insert(s, o, null, DateTime.Now.AddMinutes(1), Cache.NoSlidingExpiration));
                break;
            default:
                Logger.Error(ErrorMessages.EndpointBindingInvalid);
                throw new Saml20Exception(ErrorMessages.EndpointBindingInvalid);
            }
        }
예제 #15
0
        /// <summary>
        /// Handles executing the login.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="assertion">The assertion.</param>
        private void DoSignOn(HttpContext context, Saml20Assertion assertion, Saml2Configuration config)
        {
            // User is now logged in at IDP specified in tmp
            context.Items[IdpLoginSessionKey] = context.Session != null ? context.Session[IdpTempSessionKey] : context.Items[IdpTempSessionKey];
            context.Items[IdpSessionIdKey] = assertion.SessionIndex;
            context.Items[IdpNameIdFormat] = assertion.Subject.Format;
            context.Items[IdpNameId] = assertion.Subject.Value;

            Logger.DebugFormat(TraceMessages.SignOnProcessed, assertion.SessionIndex, assertion.Subject.Value, assertion.Subject.Format);

            Logger.Debug(TraceMessages.SignOnActionsExecuting);
            // TODO: Signon event
            //foreach (var action in Actions.Actions.GetActions(config))
            //{
            //    Logger.DebugFormat("{0}.{1} called", action.GetType(), "LoginAction()");

            //    action.SignOnAction(this, context, assertion, config);

            //    Logger.DebugFormat("{0}.{1} finished", action.GetType(), "LoginAction()");
            //}
        }
예제 #16
0
        /// <summary>
        /// Send an authentication request to the IDP.
        /// </summary>
        /// <param name="context">The context.</param>
        private void SendRequest(HttpContext context, Saml2Configuration config)
        {
            // See if the "ReturnUrl" - parameter is set.
            var returnUrl = context.Request.QueryString["ReturnUrl"];
            if (!string.IsNullOrEmpty(returnUrl) && context.Session != null) {
                context.Session["RedirectUrl"] = returnUrl;
            }

            var isRedirected = false;
            var selectionUtil = new IdpSelectionUtil(Logger);
            var idp = selectionUtil.RetrieveIDP(context.Request.Params, context.Request.QueryString, config, s => { context.Response.Redirect(s); isRedirected = true; });
            if (isRedirected) return;
            if (idp == null) {
                // Display a page to the user where she can pick the IDP
                Logger.DebugFormat(TraceMessages.IdentityProviderRedirect);

                var page = new SelectSaml20IDP();
                page.ProcessRequest(context);
                return;
            }

            var authnRequest = Saml20AuthnRequest.GetDefault(config);
            TransferClient(idp, authnRequest, context, config);
        }
예제 #17
0
 /// <summary>
 /// Gets a default instance of this class with meaningful default values set.
 /// </summary>
 /// <returns>The default <see cref="Saml20AttributeQuery"/>.</returns>
 public static Saml20AttributeQuery GetDefault(Saml2Configuration config)
 {
     return new Saml20AttributeQuery { Issuer = config.ServiceProvider.Id };
 }
예제 #18
0
        /// <summary>
        /// Resolves an artifact.
        /// </summary>
        /// <returns>A stream containing the artifact response from the IdP</returns>
        /// <param name="artifact">artifact from request ("SAMLart")</param>
        public Stream ResolveArtifact(string artifact, string relayState, Saml2Configuration config)
        {
            var idpEndPoint = DetermineIdp(artifact);
            if (idpEndPoint == null)
            {
                throw new InvalidOperationException(ErrorMessages.ArtifactResolveIdentityProviderUnknown);
            }

            var endpointIndex = ArtifactUtil.GetEndpointIndex(artifact);
            var endpointUrl = idpEndPoint.Metadata.GetIDPARSEndpoint(endpointIndex);

            Logger.DebugFormat(TraceMessages.ArtifactResolveForKnownIdentityProvider, artifact, idpEndPoint.Id, endpointUrl);
            
            var resolve = Saml20ArtifactResolve.GetDefault(config.ServiceProvider.Id);
            resolve.Artifact = artifact;

            var doc = resolve.GetXml();
            if (doc.FirstChild is XmlDeclaration)
            {
                doc.RemoveChild(doc.FirstChild);
            }

            XmlSignatureUtils.SignDocument(doc, resolve.Id, config.ServiceProvider.SigningCertificate);

            var artifactResolveString = doc.OuterXml;

            Logger.DebugFormat(TraceMessages.ArtifactResolved, artifactResolveString);

            return GetResponse(endpointUrl, artifactResolveString, idpEndPoint.ArtifactResolution, relayState);
        }
        /// <summary>
        /// Handles the artifact.
        /// </summary>
        /// <param name="context">The context.</param>
        protected void HandleArtifact(HttpContext context, Saml2Configuration config, Action<HttpContext, Stream, Saml2Configuration> handleSoap)
        {
            var builder = GetBuilder(context);
            var inputStream = builder.ResolveArtifact(context.Request.Params["SAMLart"], context.Request.Params["relayState"], ConfigurationFactory.Instance.Configuration);

            handleSoap(context, inputStream, config);
        }
예제 #20
0
        /// <summary>
        /// Transfers the client.
        /// </summary>
        /// <param name="idp">The identity provider.</param>
        /// <param name="context">The context.</param>
        private void TransferClient(IdentityProvider idp, HttpContext context, Saml2Configuration config)
        {
            var request = Saml20LogoutRequest.GetDefault(config);

            // Determine which endpoint to use from the configuration file or the endpoint metadata.
            var destination = IdpSelectionUtil.DetermineEndpointConfiguration(BindingType.Redirect, idp.Endpoints.DefaultLogoutEndpoint, idp.Metadata.IDPSLOEndpoints);
            request.Destination = destination.Url;

            var nameIdFormat = (string)context.Session[IdpNameIdFormat];
            request.SubjectToLogOut.Format = nameIdFormat;

            // Handle POST binding
            if (destination.Binding == BindingType.Post)
            {
                var builder = new HttpPostBindingBuilder(destination);
                request.Destination = destination.Url;
                request.Reason = Saml20Constants.Reasons.User;
                request.SubjectToLogOut.Value = (string)context.Session[IdpNameId];
                request.SessionIndex = (string)context.Session[IdpSessionIdKey];

                var requestDocument = request.GetXml();
                XmlSignatureUtils.SignDocument(requestDocument, request.Id, config.ServiceProvider.SigningCertificate);
                builder.Request = requestDocument.OuterXml;

                Logger.DebugFormat(TraceMessages.LogoutRequestSent, idp.Id, "POST", builder.Request);

                context.Response.Write(builder.GetPage());
                context.Response.End();
                return;
            }

            // Handle Redirect binding
            if (destination.Binding == BindingType.Redirect)
            {
                request.Destination = destination.Url;
                request.Reason = Saml20Constants.Reasons.User;
                request.SubjectToLogOut.Value = (string)context.Session[IdpNameId];
                request.SessionIndex = (string)context.Session[IdpSessionIdKey];

                var builder = new HttpRedirectBindingBuilder
                                  {
                                      Request = request.GetXml().OuterXml,
                                      SigningKey = config.ServiceProvider.SigningCertificate.PrivateKey
                                  };

                var redirectUrl = destination.Url + (destination.Url.Contains("?") ? "&" : "?") + builder.ToQuery();
                Logger.DebugFormat(TraceMessages.LogoutRequestSent, idp.Id, "REDIRECT", redirectUrl);

                context.Response.Redirect(redirectUrl, true);
                return;
            }

            // Handle Artifact binding
            if (destination.Binding == BindingType.Artifact)
            {
                request.Destination = destination.Url;
                request.Reason = Saml20Constants.Reasons.User;
                request.SubjectToLogOut.Value = (string)context.Session[IdpNameId];
                request.SessionIndex = (string)context.Session[IdpSessionIdKey];

                Logger.DebugFormat(TraceMessages.LogoutRequestSent, idp.Id, "ARTIFACT", request.GetXml().OuterXml);

                var builder = GetBuilder(context);
                builder.RedirectFromLogout(destination, request, Guid.NewGuid().ToString("N"), (s, o) => context.Cache.Insert(s, o, null, DateTime.Now.AddMinutes(1), Cache.NoSlidingExpiration));
            }

            Logger.Error(ErrorMessages.EndpointBindingInvalid);
            throw new Saml20Exception(ErrorMessages.EndpointBindingInvalid);
        }
예제 #21
0
 /// <summary>
 /// Initializes a new instance of the <see cref="Saml20MetadataDocument"/> class.
 /// </summary>
 /// <param name="config">The config.</param>
 /// <param name="keyinfo">key information for the service provider certificate.</param>
 /// <param name="sign">if set to <c>true</c> the metadata document will be signed.</param>
 public Saml20MetadataDocument(Saml2Configuration config, KeyInfo keyinfo, bool sign)
     : this(sign)
 {
     ConvertToMetadata(config, keyinfo);
 }
예제 #22
0
 public SamlMetadataWriter(Saml2Configuration configuration)
 {
     this.configuration = configuration;
     logger = LoggerProvider.LoggerFor(typeof(Saml2Configuration));
 }
예제 #23
0
        /// <summary>
        /// Takes the configuration class and converts it to a SAML2.0 metadata document.
        /// </summary>
        /// <param name="config">The config.</param>
        /// <param name="keyInfo">The keyInfo.</param>
        private void ConvertToMetadata(Saml2Configuration config, KeyInfo keyInfo)
        {
            var entity = CreateDefaultEntity();
            entity.EntityID = config.ServiceProvider.Id;
            entity.ValidUntil = DateTime.Now.AddDays(7);

            var serviceProviderDescriptor = new SpSsoDescriptor
                                   {
                                       ProtocolSupportEnumeration = new[] { Saml20Constants.Protocol },
                                       AuthnRequestsSigned = XmlConvert.ToString(true),
                                       WantAssertionsSigned = XmlConvert.ToString(true)
                                   };

            if (config.ServiceProvider.NameIdFormats.Count > 0)
            {
                serviceProviderDescriptor.NameIdFormat = new string[config.ServiceProvider.NameIdFormats.Count];
                var count = 0;
                foreach (var elem in config.ServiceProvider.NameIdFormats)
                {
                    serviceProviderDescriptor.NameIdFormat[count++] = elem.Format;
                }
            }
            
            var baseUrl = new Uri(config.ServiceProvider.Server);
            var logoutServiceEndpoints = new List<Endpoint>();
            var signonServiceEndpoints = new List<IndexedEndpoint>();

            var artifactResolutionEndpoints = new List<IndexedEndpoint>(2);

            // Include endpoints.
            foreach (var endpoint in config.ServiceProvider.Endpoints)
            {
                if (endpoint.Type == EndpointType.SignOn)
                {
                    var loginEndpoint = new IndexedEndpoint
                                            {
                                                Index = endpoint.Index,
                                                IsDefault = true,
                                                Location = new Uri(baseUrl, endpoint.LocalPath).ToString(),
                                                Binding = GetBinding(endpoint.Binding, Saml20Constants.ProtocolBindings.HttpPost)
                                            };
                    signonServiceEndpoints.Add(loginEndpoint);

                    var artifactSignonEndpoint = new IndexedEndpoint
                                                     {
                                                         Binding = Saml20Constants.ProtocolBindings.HttpSoap,
                                                         Index = loginEndpoint.Index,
                                                         Location = loginEndpoint.Location
                                                     };
                    artifactResolutionEndpoints.Add(artifactSignonEndpoint);

                    continue;
                }

                if (endpoint.Type == EndpointType.Logout)
                {
                    var logoutEndpoint = new Endpoint
                                             {
                                                 Location = new Uri(baseUrl, endpoint.LocalPath).ToString()
                                             };
                    logoutEndpoint.ResponseLocation = logoutEndpoint.Location;
                    logoutEndpoint.Binding = GetBinding(endpoint.Binding, Saml20Constants.ProtocolBindings.HttpPost);
                    logoutServiceEndpoints.Add(logoutEndpoint);

                    // TODO: Look at this...
                    logoutEndpoint = new Endpoint
                                         {
                                             Location = new Uri(baseUrl, endpoint.LocalPath).ToString()
                                         };
                    logoutEndpoint.ResponseLocation = logoutEndpoint.Location;
                    logoutEndpoint.Binding = GetBinding(endpoint.Binding, Saml20Constants.ProtocolBindings.HttpRedirect);
                    logoutServiceEndpoints.Add(logoutEndpoint);

                    var artifactLogoutEndpoint = new IndexedEndpoint
                                                     {
                                                         Binding = Saml20Constants.ProtocolBindings.HttpSoap,
                                                         Index = endpoint.Index,
                                                         Location = logoutEndpoint.Location
                                                     };
                    artifactResolutionEndpoints.Add(artifactLogoutEndpoint);
                    
                    continue;
                }
            }           

            serviceProviderDescriptor.SingleLogoutService = logoutServiceEndpoints.ToArray();
            serviceProviderDescriptor.AssertionConsumerService = signonServiceEndpoints.ToArray();
            
            // Attribute consuming service. 
            if (config.Metadata.RequestedAttributes.Count > 0)
            {
                var attConsumingService = new AttributeConsumingService();
                serviceProviderDescriptor.AttributeConsumingService = new[] { attConsumingService };
                attConsumingService.Index = signonServiceEndpoints[0].Index;
                attConsumingService.IsDefault = true;
                attConsumingService.ServiceName = new[] { new LocalizedName("SP", "en") };

                attConsumingService.RequestedAttribute = new RequestedAttribute[config.Metadata.RequestedAttributes.Count];

                for (var i = 0; i < config.Metadata.RequestedAttributes.Count; i++)
                {
                    attConsumingService.RequestedAttribute[i] = new RequestedAttribute
                                                                    {
                                                                        Name = config.Metadata.RequestedAttributes[i].Name
                                                                    };
                    if (config.Metadata.RequestedAttributes[i].IsRequired)
                    {
                        attConsumingService.RequestedAttribute[i].IsRequired = true;
                    }

                    attConsumingService.RequestedAttribute[i].NameFormat = SamlAttribute.NameformatBasic;
                }
            }
            else
            {
                serviceProviderDescriptor.AttributeConsumingService = new AttributeConsumingService[0];
            }

            if (config.Metadata == null || !config.Metadata.ExcludeArtifactEndpoints)
            {
                serviceProviderDescriptor.ArtifactResolutionService = artifactResolutionEndpoints.ToArray();
            }

            entity.Items = new object[] { serviceProviderDescriptor };

            // Keyinfo
            var keySigning = new KeyDescriptor();
            var keyEncryption = new KeyDescriptor();
            serviceProviderDescriptor.KeyDescriptor = new[] { keySigning, keyEncryption };
            
            keySigning.Use = KeyTypes.Signing;
            keySigning.UseSpecified = true;

            keyEncryption.Use = KeyTypes.Encryption;
            keyEncryption.UseSpecified = true;
                       
            // Ugly conversion between the .Net framework classes and our classes ... avert your eyes!!
            keySigning.KeyInfo = Serialization.DeserializeFromXmlString<Schema.XmlDSig.KeyInfo>(keyInfo.GetXml().OuterXml);            
            keyEncryption.KeyInfo = keySigning.KeyInfo;

            // apply the <Organization> element
            if (config.Metadata.Organization != null)
            {
                entity.Organization = new Schema.Metadata.Organization
                                          {
                                              OrganizationName = new[] { new LocalizedName { Value = config.Metadata.Organization.Name } },
                                              OrganizationDisplayName = new[] { new LocalizedName { Value = config.Metadata.Organization.DisplayName } },
                                              OrganizationURL = new[] { new LocalizedURI { Value = config.Metadata.Organization.Url } }
                                          };
            }

            if (config.Metadata.Contacts != null && config.Metadata.Contacts.Any())
            {
                entity.ContactPerson = config.Metadata.Contacts.Select(x => new Schema.Metadata.Contact
                {
                                                                                    ContactType =
                                                                                        (Schema.Metadata.ContactType)
                                                                                        ((int)x.Type),
                                                                                    Company = x.Company,
                                                                                    GivenName = x.GivenName,
                                                                                    SurName = x.SurName,
                                                                                    EmailAddress = new[] { x.Email },
                                                                                    TelephoneNumber = new[] { x.Phone }
                                                                                }).ToArray();
            }
        }
예제 #24
0
파일: Logout.cs 프로젝트: TecVector/SAML2
 public Logout(IInternalLogger logger, SAML2.Config.Saml2Configuration config)
 {
     this.logger = logger ?? throw new ArgumentNullException("logger");
     this.config = config ?? throw new ArgumentNullException("config");
 }
예제 #25
0
        private string AuthnRequestForIdp(IdentityProvider identityProvider, Saml20AuthnRequest request, IOwinContext context, Saml2Configuration config)
        {
            var logger = SAML2.Logging.LoggerProvider.LoggerFor(typeof(SamlMessage));

            context.Set(IdpTempSessionKey, identityProvider.Id);

            // Determine which endpoint to use from the configuration file or the endpoint metadata.
            var destination = IdpSelectionUtil.DetermineEndpointConfiguration(BindingType.Redirect, identityProvider.Endpoints.DefaultSignOnEndpoint, identityProvider.Metadata.SSOEndpoints);
            request.Destination = destination.Url;

            if (identityProvider.ForceAuth) {
                request.ForceAuthn = true;
            }

            // Check isPassive status
            if (context.Get<bool>(IdpIsPassive)) {
                request.IsPassive = true;
            }

            if (identityProvider.IsPassive) {
                request.IsPassive = true;
            }

            // Check if request should forceAuthn            
            if (context.Get<bool>(IdpForceAuthn)) {
                request.ForceAuthn = true;
            }

            // Check if protocol binding should be forced
            if (identityProvider.Endpoints.DefaultSignOnEndpoint != null) {
                if (!string.IsNullOrEmpty(identityProvider.Endpoints.DefaultSignOnEndpoint.ForceProtocolBinding)) {
                    request.ProtocolBinding = identityProvider.Endpoints.DefaultSignOnEndpoint.ForceProtocolBinding;
                }
            }

            // Save request message id to session
            Utility.AddExpectedResponseId(request.Id);

            switch (destination.Binding) {
            case BindingType.Redirect:
                logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpRedirect);

                var redirectBuilder = new HttpRedirectBindingBuilder
                {
                    SigningKey = config.ServiceProvider.SigningCertificate.PrivateKey,
                    Request = request.GetXml().OuterXml
                };
                if (context.Authentication != null &&
                    context.Authentication.AuthenticationResponseChallenge != null &&
                    context.Authentication.AuthenticationResponseChallenge.Properties != null &&
                    context.Authentication.AuthenticationResponseChallenge.Properties.Dictionary != null &&
                    context.Authentication.AuthenticationResponseChallenge.Properties.Dictionary.Count > 0)
                    redirectBuilder.RelayState = context.Authentication.AuthenticationResponseChallenge.Properties.Dictionary.ToDelimitedString();
                logger.DebugFormat(TraceMessages.AuthnRequestSent, redirectBuilder.Request);

                var redirectLocation = request.Destination + "?" + redirectBuilder.ToQuery();
                return redirectLocation;
            case BindingType.Post:
                throw new NotImplementedException();
                //logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpPost);

                //var postBuilder = new HttpPostBindingBuilder(destination);

                //// Honor the ForceProtocolBinding and only set this if it's not already set
                //if (string.IsNullOrEmpty(request.ProtocolBinding)) {
                //    request.ProtocolBinding = Saml20Constants.ProtocolBindings.HttpPost;
                //}

                //var requestXml = request.GetXml();
                //XmlSignatureUtils.SignDocument(requestXml, request.Id, config.ServiceProvider.SigningCertificate);
                //postBuilder.Request = requestXml.OuterXml;

                //logger.DebugFormat(TraceMessages.AuthnRequestSent, postBuilder.Request);

                //context.Response.Write(postBuilder.GetPage());
                //break;
            case BindingType.Artifact:
                throw new NotImplementedException();
                //logger.DebugFormat(TraceMessages.AuthnRequestPrepared, identityProvider.Id, Saml20Constants.ProtocolBindings.HttpArtifact);

                //var artifactBuilder = new HttpArtifactBindingBuilder(context, config);

                //// Honor the ForceProtocolBinding and only set this if it's not already set
                //if (string.IsNullOrEmpty(request.ProtocolBinding)) {
                //    request.ProtocolBinding = Saml20Constants.ProtocolBindings.HttpArtifact;
                //}

                //logger.DebugFormat(TraceMessages.AuthnRequestSent, request.GetXml().OuterXml);

                //artifactBuilder.RedirectFromLogin(destination, request);
                //break;
            default:
                logger.Error(SAML2.ErrorMessages.EndpointBindingInvalid);
                throw new Saml20Exception(SAML2.ErrorMessages.EndpointBindingInvalid);
            }
            throw new NotImplementedException();
        }
예제 #26
0
        /// <summary>
        /// Handles executing the logout.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="idpInitiated">if set to <c>true</c> identity provider is initiated.</param>
        private void DoLogout(HttpContext context, bool idpInitiated = false, Saml2Configuration config = null)
        {
            Logger.Debug(TraceMessages.LogoutActionsExecuting);
            // TODO: Event for logout actions
            //foreach (var action in Actions.Actions.GetActions(config))
            //{
            //    Logger.DebugFormat("{0}.{1} called", action.GetType(), "LogoutAction()");

            //    action.LogoutAction(this, context, idpInitiated);

            //    Logger.DebugFormat("{0}.{1} finished", action.GetType(), "LogoutAction()");
            //}
        }
예제 #27
0
        /// <summary>
        /// Handles the SOAP message.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="inputStream">The input stream.</param>
        private void HandleSoap(HttpContext context, Stream inputStream, Saml2Configuration config)
        {
            var parser = new HttpArtifactBindingParser(inputStream);
            Logger.DebugFormat(TraceMessages.SOAPMessageParse, parser.SamlMessage.OuterXml);

            var builder = GetBuilder(context);
            var idp = IdpSelectionUtil.RetrieveIDPConfiguration(parser.Issuer, config);
            
            if (parser.IsArtifactResolve)
            {
                Logger.DebugFormat(TraceMessages.ArtifactResolveReceived, parser.SamlMessage);

                if (!parser.CheckSamlMessageSignature(idp.Metadata.Keys))
                {
                    Logger.ErrorFormat(ErrorMessages.ArtifactResolveSignatureInvalid);
                    throw new Saml20Exception(ErrorMessages.ArtifactResolveSignatureInvalid);
                }

                builder.RespondToArtifactResolve(parser.ArtifactResolve, parser.SamlMessage);
            }
            else if (parser.IsArtifactResponse)
            {
                Logger.DebugFormat(TraceMessages.ArtifactResponseReceived, parser.SamlMessage);

                if (!parser.CheckSamlMessageSignature(idp.Metadata.Keys))
                {
                    Logger.Error(ErrorMessages.ArtifactResponseSignatureInvalid);
                    throw new Saml20Exception(ErrorMessages.ArtifactResponseSignatureInvalid);
                }

                var status = parser.ArtifactResponse.Status;
                if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success)
                {
                    Logger.ErrorFormat(ErrorMessages.ArtifactResponseStatusCodeInvalid, status.StatusCode.Value);
                    throw new Saml20Exception(string.Format(ErrorMessages.ArtifactResponseStatusCodeInvalid, status.StatusCode.Value));
                }

                if (parser.ArtifactResponse.Any.LocalName == LogoutRequest.ElementName)
                {
                    Logger.DebugFormat(TraceMessages.LogoutRequestReceived, parser.ArtifactResponse.Any.OuterXml);

                    var req = Serialization.DeserializeFromXmlString<LogoutRequest>(parser.ArtifactResponse.Any.OuterXml);

                    // Send logoutresponse via artifact
                    var response = new Saml20LogoutResponse
                                       {
                                           Issuer = config.ServiceProvider.Id,
                                           StatusCode = Saml20Constants.StatusCodes.Success,
                                           InResponseTo = req.Id
                                       };

                    var endpoint = IdpSelectionUtil.RetrieveIDPConfiguration((string)context.Session[IdpLoginSessionKey], config);
                    var destination = IdpSelectionUtil.DetermineEndpointConfiguration(BindingType.Redirect, endpoint.Endpoints.DefaultLogoutEndpoint, endpoint.Metadata.IDPSLOEndpoints);

                    builder.RedirectFromLogout(destination, response, context.Request.Params["relayState"], (s, o) => context.Cache.Insert(s, o, null, DateTime.Now.AddMinutes(1), Cache.NoSlidingExpiration));
                }
                else if (parser.ArtifactResponse.Any.LocalName == LogoutResponse.ElementName)
                {
                    DoLogout(context, false, config);
                }
                else
                {
                    Logger.ErrorFormat(ErrorMessages.ArtifactResponseMissingResponse);
                    throw new Saml20Exception(ErrorMessages.ArtifactResponseMissingResponse);
                }
            }
            else if (parser.IsLogoutReqest)
            {
                Logger.DebugFormat(TraceMessages.LogoutRequestReceived, parser.SamlMessage.OuterXml);

                var req = parser.LogoutRequest;
                
                // Build the response object
                var response = new Saml20LogoutResponse
                                   {
                                       Issuer = config.ServiceProvider.Id,
                                       StatusCode = Saml20Constants.StatusCodes.Success,
                                       InResponseTo = req.Id
                                   };

                // response.Destination = destination.Url;
                var doc = response.GetXml();
                XmlSignatureUtils.SignDocument(doc, response.Id, config.ServiceProvider.SigningCertificate);
                if (doc.FirstChild is XmlDeclaration)
                {
                    doc.RemoveChild(doc.FirstChild);
                }
                
                SendResponseMessage(doc.OuterXml, context);
            }
            else
            {
                Logger.ErrorFormat(ErrorMessages.SOAPMessageUnsupportedSamlMessage);
                throw new Saml20Exception(ErrorMessages.SOAPMessageUnsupportedSamlMessage);
            }
        }
예제 #28
0
 public SamlMessage(IOwinContext context, Saml2Configuration config, Saml20Assertion assertion) : this(null, context, config)
 {
     Assertion = assertion;
 }
예제 #29
0
 /// <summary>
 /// Signs an XmlDocument with an xml signature using the signing certificate specified in the
 /// configuration file.
 /// </summary>
 /// <param name="doc">The XmlDocument to be signed</param>
 /// <param name="id">The id of the topmost element in the XmlDocument</param>
 public static void SignDocument(XmlDocument doc, string id, Saml2Configuration config)
 {
     SignDocument(doc, id, config.ServiceProvider.SigningCertificate);
 }
예제 #30
0
 public SamlMessage(IFormCollection form, IOwinContext context, SAML2.Config.Saml2Configuration config) : this(form)
 {
     this.context = context;
     this.config = config;
 }
            public void CanDecryptFOBSAssertion()
            {
                // Arrange
                var doc = AssertionUtil.LoadBase64EncodedXmlDocument(@"Assertions\fobs-assertion2");
                var encryptedList = doc.GetElementsByTagName(EncryptedAssertion.ElementName, Saml20Constants.Assertion);

                // Do some mock configuration.
                var config = new Saml2Configuration
                {
                    AllowedAudienceUris = new System.Collections.Generic.List<Uri>(),
                    IdentityProviders = new IdentityProviders()
                };
                config.AllowedAudienceUris.Add(new Uri("https://saml.safewhere.net"));
                config.IdentityProviders.AddByMetadataDirectory(@"Protocol\MetadataDocs\FOBS"); // Set it manually.

                var cert = new X509Certificate2(@"Certificates\SafewhereTest_SFS.pfx", "test1234");
                var encryptedAssertion = new Saml20EncryptedAssertion((RSA)cert.PrivateKey);

                encryptedAssertion.LoadXml((XmlElement)encryptedList[0]);

                // Act
                encryptedAssertion.Decrypt();

                // Retrieve metadata
                var assertion = new Saml20Assertion(encryptedAssertion.Assertion.DocumentElement, null, false, TestConfiguration.Configuration);
                var endp = config.IdentityProviders.FirstOrDefault(x => x.Id == assertion.Issuer);

                // Assert
                Assert.That(encryptedList.Count == 1);
                Assert.IsNotNull(endp, "Endpoint not found");
                Assert.IsNotNull(endp.Metadata, "Metadata not found");

                try {
                    assertion.CheckValid(AssertionUtil.GetTrustedSigners(assertion.Issuer));
                    Assert.Fail("Verification should fail. Token does not include its signing key.");
                }
                catch (InvalidOperationException) {
                }

                Assert.IsNull(assertion.SigningKey, "Signing key is already present on assertion. Modify test.");
                //Assert.IsTrue("We have tested this next test" == "");
                //Assert.That(assertion.CheckSignature(Saml20SignonHandler.GetTrustedSigners(endp.Metadata.GetKeys(KeyTypes.Signing), endp)));
                //Assert.IsNotNull(assertion.SigningKey, "Signing key was not set on assertion instance.");
            }
예제 #32
0
 public SamlMessage(IFormCollection form, IOwinContext context, SAML2.Config.Saml2Configuration config) : this(form)
 {
     this.context = context;
     this.config  = config;
 }
예제 #33
0
 /// <summary>
 /// Handles the response.
 /// </summary>
 /// <param name="context">The context.</param>
 private void HandleResponse(HttpContext context, Saml2Configuration config)
 {
     var requestType = context.Request.RequestType;
     var requestParams = context.Request.Params;
     var requestUrl = context.Request.Url;
     new Logout(Logger, config).ValidateLogoutRequest(requestType, requestParams, requestUrl);
     // Log the user out locally
     DoLogout(context, false, config);
 }