Пример #1
0
        public void SettingNegativePreflightMaxAge_Throws()
        {
            // Arrange
            var result = new CorsResult();

            // Act
            var exception = Assert.Throws <ArgumentOutOfRangeException>(() =>
            {
                result.PreflightMaxAge = TimeSpan.FromSeconds(-1);
            });

            // Assert
            Assert.Equal(
                $"PreflightMaxAge must be greater than or equal to 0.{Environment.NewLine}Parameter name: value",
                exception.Message);
        }
Пример #2
0
        public void ApplyResult_PreflightMaxAge_MaxAgeHeaderAdded()
        {
            // Arrange
            var result = new CorsResult
            {
                PreflightMaxAge = TimeSpan.FromSeconds(30)
            };
            var httpContext = new DefaultHttpContext();
            var service     = new CorsService(new TestCorsOptions());

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.Equal("30", httpContext.Response.Headers["Access-Control-Max-Age"]);
        }
Пример #3
0
        public void ApplyResult_OneAllowHeaders_AllowHeadersHeaderAdded()
        {
            // Arrange
            var result = new CorsResult();

            result.AllowedHeaders.Add("foo");

            var httpContext = new DefaultHttpContext();
            var service     = new CorsService(new TestCorsOptions());

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.Equal("foo", httpContext.Response.Headers["Access-Control-Allow-Headers"]);
        }
Пример #4
0
        public void ApplyResult_NoPreflightMaxAge_MaxAgeHeaderNotAdded()
        {
            // Arrange
            var result = new CorsResult
            {
                PreflightMaxAge = null
            };

            var httpContext = new DefaultHttpContext();
            var service     = new CorsService(new TestCorsOptions());

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.DoesNotContain("Access-Control-Max-Age", httpContext.Response.Headers.Keys);
        }
Пример #5
0
        public void ApplyResult_AllowOrigin_AllowOriginHeaderAdded()
        {
            // Arrange
            var result = new CorsResult
            {
                AllowedOrigin = "http://example.com"
            };

            var httpContext = new DefaultHttpContext();
            var service     = new CorsService(new TestCorsOptions());

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.Equal("http://example.com", httpContext.Response.Headers["Access-Control-Allow-Origin"]);
        }
Пример #6
0
        public void ApplyResult_NoAllowOrigin_AllowOriginHeaderNotAdded()
        {
            // Arrange
            var result = new CorsResult
            {
                AllowedOrigin = null
            };

            var httpContext = new DefaultHttpContext();
            var service     = new CorsService(new TestCorsOptions());

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.DoesNotContain("Access-Control-Allow-Origin", httpContext.Response.Headers.Keys);
        }
Пример #7
0
        public void ApplyResult_AddVaryHeader_VaryHeaderAdded()
        {
            // Arrange
            var result = new CorsResult
            {
                VaryByOrigin = true
            };

            var httpContext = new DefaultHttpContext();
            var service     = new CorsService(new TestCorsOptions());

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.Equal("Origin", httpContext.Response.Headers["Vary"]);
        }
Пример #8
0
        public void ApplyResult_NoAllowCredentials_AllowCredentialsHeaderNotAdded()
        {
            // Arrange
            var result = new CorsResult
            {
                SupportsCredentials = false
            };

            var httpContext = new DefaultHttpContext();
            var service     = new CorsService(new TestCorsOptions());

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.DoesNotContain("Access-Control-Allow-Credentials", httpContext.Response.Headers.Keys);
        }
Пример #9
0
        public void ApplyResult_NoAllowExposedHeaders_ExposedHeadersHeaderNotAdded()
        {
            // Arrange
            var result = new CorsResult
            {
                // AllowExposedHeaders is empty by default
            };

            var httpContext = new DefaultHttpContext();
            var service     = new CorsService(new TestCorsOptions());

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.DoesNotContain("Access-Control-Expose-Headers", httpContext.Response.Headers.Keys);
        }
Пример #10
0
        /// <inheritdoc />
        public CorsResult EvaluatePolicy(HttpContext context, CorsPolicy policy)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

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

            if (policy.AllowAnyOrigin && policy.SupportsCredentials)
            {
                throw new ArgumentException("The CORS protocol does not allow specifying a wildcard (any) origin and credentials at the same time. Configure the CORS policy by listing individual origins if credentials needs to be supported.", nameof(policy));
            }

            var requestHeaders = context.Request.Headers;
            var origin         = requestHeaders[CorsConstants.Origin];

            var isOptionsRequest   = string.Equals(context.Request.Method, CorsConstants.PreflightHttpMethod, StringComparison.OrdinalIgnoreCase);
            var isPreflightRequest = isOptionsRequest && requestHeaders.ContainsKey(CorsConstants.AccessControlRequestMethod);

            if (isOptionsRequest && !isPreflightRequest)
            {
                _logger.IsNotPreflightRequest();
            }

            var corsResult = new CorsResult
            {
                IsPreflightRequest = isPreflightRequest,
                IsOriginAllowed    = IsOriginAllowed(policy, origin),
            };

            if (isPreflightRequest)
            {
                EvaluatePreflightRequest(context, policy, corsResult);
            }
            else
            {
                EvaluateRequest(context, policy, corsResult);
            }

            return(corsResult);
        }
Пример #11
0
        /// <inheritdoc />
        public CorsResult EvaluatePolicy(HttpContext context, CorsPolicy policy)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

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

            if (policy.AllowAnyOrigin && policy.SupportsCredentials)
            {
                throw new ArgumentException(Resources.InsecureConfiguration, nameof(policy));
            }

            var origin         = context.Request.Headers[CorsConstants.Origin];
            var requestHeaders = context.Request.Headers;

            var isOptionsRequest   = string.Equals(context.Request.Method, CorsConstants.PreflightHttpMethod, StringComparison.OrdinalIgnoreCase);
            var isPreflightRequest = isOptionsRequest && requestHeaders.ContainsKey(CorsConstants.AccessControlRequestMethod);

            if (isOptionsRequest && !isPreflightRequest)
            {
                _logger.IsNotPreflightRequest();
            }

            var corsResult = new CorsResult
            {
                IsPreflightRequest = isPreflightRequest,
                IsOriginAllowed    = IsOriginAllowed(policy, origin),
            };

            if (isPreflightRequest)
            {
                EvaluatePreflightRequest(context, policy, corsResult);
            }
            else
            {
                EvaluateRequest(context, policy, corsResult);
            }

            return(corsResult);
        }
Пример #12
0
        public void ApplyResult_OneAllowExposedHeaders_ExposedHeadersHeaderAdded()
        {
            // Arrange
            var result = new CorsResult
            {
                IsOriginAllowed       = true,
                AllowedExposedHeaders = { "foo" },
            };

            var httpContext = new DefaultHttpContext();
            var service     = GetCorsService();

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.Equal("foo", httpContext.Response.Headers["Access-Control-Expose-Headers"]);
        }
Пример #13
0
        public void ApplyResult_NoAllowHeaders_AllowHeadersHeaderNotAdded()
        {
            // Arrange
            var result = new CorsResult
            {
                // AllowHeaders is empty by default
                IsOriginAllowed = true,
            };

            var httpContext = new DefaultHttpContext();
            var service     = GetCorsService();

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.DoesNotContain("Access-Control-Allow-Headers", httpContext.Response.Headers.Keys);
        }
Пример #14
0
        public void ApplyResult_SimpleAllowMethods_AllowMethodsHeaderNotAdded()
        {
            // Arrange
            var result = new CorsResult();

            result.AllowedMethods.Add("GET");
            result.AllowedMethods.Add("HEAD");
            result.AllowedMethods.Add("POST");

            var httpContext = new DefaultHttpContext();
            var service     = new CorsService(new TestCorsOptions());

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.DoesNotContain("Access-Control-Allow-Methods", httpContext.Response.Headers.Keys);
        }
Пример #15
0
        public void ApplyResult_PreflightMaxAge_MaxAgeHeaderAdded()
        {
            // Arrange
            var result = new CorsResult
            {
                IsOriginAllowed    = true,
                IsPreflightRequest = true,
                PreflightMaxAge    = TimeSpan.FromSeconds(30),
            };
            var httpContext = new DefaultHttpContext();
            var service     = GetCorsService();

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.Equal("30", httpContext.Response.Headers["Access-Control-Max-Age"]);
        }
Пример #16
0
        public void ApplyResult_SimpleAllowHeaders_AllowHeadersHeaderNotAdded()
        {
            // Arrange
            var result = new CorsResult();

            result.AllowedHeaders.Add("Accept");
            result.AllowedHeaders.Add("Accept-Language");
            result.AllowedHeaders.Add("Content-Language");

            var httpContext = new DefaultHttpContext();
            var service     = new CorsService(new TestCorsOptions());

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.DoesNotContain("Access-Control-Allow-Headers", httpContext.Response.Headers.Keys);
        }
Пример #17
0
        public void ApplyResult_AllowCredentials_AllowCredentialsHeaderAdded()
        {
            // Arrange
            var result = new CorsResult
            {
                SupportsCredentials = true
            };

            var service = new CorsService(new TestCorsOptions());

            // Act
            var httpContext = new DefaultHttpContext();

            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.Equal("true", httpContext.Response.Headers["Access-Control-Allow-Credentials"]);
        }
Пример #18
0
 private void AddOriginToResult(string origin, CorsPolicy policy, CorsResult result)
 {
     if (policy.AllowAnyOrigin)
     {
         if (policy.SupportsCredentials)
         {
             result.AllowedOrigin = origin;
             result.VaryByOrigin  = true;
         }
         else
         {
             result.AllowedOrigin = CorsConstants.AnyOrigin;
         }
     }
     else if (policy.Origins.Contains(origin))
     {
         result.AllowedOrigin = origin;
     }
 }
Пример #19
0
        public void ApplyResult_OneAllowMethods_AllowMethodsHeaderAdded()
        {
            // Arrange
            var result = new CorsResult
            {
                IsOriginAllowed    = true,
                IsPreflightRequest = true,
                AllowedMethods     = { "PUT" }
            };

            var httpContext = new DefaultHttpContext();
            var service     = GetCorsService();

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.Equal("PUT", httpContext.Response.Headers["Access-Control-Allow-Methods"]);
        }
Пример #20
0
        public void ApplyResult_PreflightRequest_ExposesHeadersNotAdded()
        {
            // Arrange
            var result = new CorsResult
            {
                IsOriginAllowed       = true,
                IsPreflightRequest    = true,
                AllowedExposedHeaders = { "foo", "bar" },
            };

            var httpContext = new DefaultHttpContext();
            var service     = GetCorsService();

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.DoesNotContain("Access-Control-Expose-Headers", httpContext.Response.Headers.Keys);
        }
Пример #21
0
        public void ApplyResult_NoPreflightRequest_ExposesHeadersAdded()
        {
            // Arrange
            var result = new CorsResult
            {
                IsOriginAllowed       = true,
                IsPreflightRequest    = false,
                AllowedExposedHeaders = { "foo", "bar" },
            };

            var httpContext = new DefaultHttpContext();
            var service     = GetCorsService();

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.Equal("foo,bar", httpContext.Response.Headers[CorsConstants.AccessControlExposeHeaders]);
        }
Пример #22
0
        public void ApplyResult_AppendsVaryHeader()
        {
            // Arrange
            var result = new CorsResult
            {
                IsOriginAllowed = true,
                VaryByOrigin    = true
            };

            var httpContext = new DefaultHttpContext();

            httpContext.Response.Headers["Vary"] = "Cookie";
            var service = GetCorsService();

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.Equal("Cookie,Origin", httpContext.Response.Headers["Vary"]);
        }
Пример #23
0
        public void ApplyResult_SomeSimpleAllowHeaders_AllowHeadersHeaderAddedForNonSimpleHeaders()
        {
            // Arrange
            var result = new CorsResult();

            result.AllowedHeaders.Add("Content-Language");
            result.AllowedHeaders.Add("foo");
            result.AllowedHeaders.Add("bar");
            result.AllowedHeaders.Add("Accept");

            var httpContext = new DefaultHttpContext();
            var service     = new CorsService(new TestCorsOptions());

            // Act
            service.ApplyResult(result, httpContext.Response);

            // Assert
            Assert.Contains("Access-Control-Allow-Headers", httpContext.Response.Headers.Keys);
            string[] headerValues = httpContext.Response.Headers.GetCommaSeparatedValues("Access-Control-Allow-Headers");
            Assert.Equal(2, headerValues.Length);
            Assert.Contains("foo", headerValues);
            Assert.Contains("bar", headerValues);
        }
Пример #24
0
        public void ToString_ReturnsThePropertyValues()
        {
            // Arrange
            var corsResult = new CorsResult
            {
                SupportsCredentials = true,
                PreflightMaxAge     = TimeSpan.FromSeconds(30),
                AllowedOrigin       = "*"
            };

            corsResult.AllowedExposedHeaders.Add("foo");
            corsResult.AllowedHeaders.Add("bar");
            corsResult.AllowedHeaders.Add("baz");
            corsResult.AllowedMethods.Add("GET");

            // Act
            var result = corsResult.ToString();

            // Assert
            Assert.Equal(
                @"AllowCredentials: True, PreflightMaxAge: 30, AllowOrigin: *," +
                " AllowExposedHeaders: {foo}, AllowHeaders: {bar,baz}, AllowMethods: {GET}",
                result);
        }
Пример #25
0
        public virtual void EvaluatePreflightRequest(HttpContext context, CorsPolicy policy, CorsResult result)
        {
            var origin = context.Request.Headers[CorsConstants.Origin];

            if (StringValues.IsNullOrEmpty(origin) || !policy.AllowAnyOrigin && !policy.Origins.Contains(origin))
            {
                return;
            }

            var accessControlRequestMethod = context.Request.Headers[CorsConstants.AccessControlRequestMethod];

            if (StringValues.IsNullOrEmpty(accessControlRequestMethod))
            {
                return;
            }

            var requestHeaders =
                context.Request.Headers.GetCommaSeparatedValues(CorsConstants.AccessControlRequestHeaders);

            if (!policy.AllowAnyMethod && !policy.Methods.Contains(accessControlRequestMethod))
            {
                return;
            }

            if (!policy.AllowAnyHeader &&
                requestHeaders != null &&
                !requestHeaders.All(header => CorsConstants.SimpleRequestHeaders.Contains(header, StringComparer.OrdinalIgnoreCase) ||
                                    policy.Headers.Contains(header, StringComparer.OrdinalIgnoreCase)))
            {
                return;
            }

            AddOriginToResult(origin, policy, result);
            result.SupportsCredentials = policy.SupportsCredentials;
            result.PreflightMaxAge     = policy.PreflightMaxAge;
            result.AllowedMethods.Add(accessControlRequestMethod);
            AddHeaderValues(result.AllowedHeaders, requestHeaders);
        }
Пример #26
0
        /// <inheritdoc />
        public virtual void ApplyResult(CorsResult result, HttpResponse response)
        {
            if (result == null)
            {
                throw new ArgumentNullException(nameof(result));
            }

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

            var headers = response.Headers;

            if (result.AllowedOrigin != null)
            {
                headers[CorsConstants.AccessControlAllowOrigin] = result.AllowedOrigin;
            }

            if (result.VaryByOrigin)
            {
                headers["Vary"] = "Origin";
            }

            if (result.SupportsCredentials)
            {
                headers[CorsConstants.AccessControlAllowCredentials] = "true";
            }

            if (result.AllowedMethods.Count > 0)
            {
                // Filter out simple methods
                var nonSimpleAllowMethods = result.AllowedMethods
                                            .Where(m =>
                                                   !CorsConstants.SimpleMethods.Contains(m, StringComparer.OrdinalIgnoreCase))
                                            .ToArray();

                if (nonSimpleAllowMethods.Length > 0)
                {
                    headers.SetCommaSeparatedValues(
                        CorsConstants.AccessControlAllowMethods,
                        nonSimpleAllowMethods);
                }
            }

            if (result.AllowedHeaders.Count > 0)
            {
                // Filter out simple request headers
                var nonSimpleAllowRequestHeaders = result.AllowedHeaders
                                                   .Where(header =>
                                                          !CorsConstants.SimpleRequestHeaders.Contains(header, StringComparer.OrdinalIgnoreCase))
                                                   .ToArray();

                if (nonSimpleAllowRequestHeaders.Length > 0)
                {
                    headers.SetCommaSeparatedValues(
                        CorsConstants.AccessControlAllowHeaders,
                        nonSimpleAllowRequestHeaders);
                }
            }

            if (result.AllowedExposedHeaders.Count > 0)
            {
                // Filter out simple response headers
                var nonSimpleAllowResponseHeaders = result.AllowedExposedHeaders
                                                    .Where(header =>
                                                           !CorsConstants.SimpleResponseHeaders.Contains(header, StringComparer.OrdinalIgnoreCase))
                                                    .ToArray();

                if (nonSimpleAllowResponseHeaders.Length > 0)
                {
                    headers.SetCommaSeparatedValues(
                        CorsConstants.AccessControlExposeHeaders,
                        nonSimpleAllowResponseHeaders);
                }
            }

            if (result.PreflightMaxAge.HasValue)
            {
                headers[CorsConstants.AccessControlMaxAge]
                    = result.PreflightMaxAge.Value.TotalSeconds.ToString(CultureInfo.InvariantCulture);
            }
        }
Пример #27
0
        public virtual void EvaluatePreflightRequest(HttpContext context, CorsPolicy policy, CorsResult result)
        {
            var origin = context.Request.Headers[CorsConstants.Origin];

            if (!IsOriginAllowed(policy, origin))
            {
                return;
            }

            var accessControlRequestMethod = context.Request.Headers[CorsConstants.AccessControlRequestMethod];

            if (StringValues.IsNullOrEmpty(accessControlRequestMethod))
            {
                return;
            }

            var requestHeaders =
                context.Request.Headers.GetCommaSeparatedValues(CorsConstants.AccessControlRequestHeaders);

            if (!policy.AllowAnyMethod)
            {
                var found = false;
                for (var i = 0; i < policy.Methods.Count; i++)
                {
                    var method = policy.Methods[i];
                    if (string.Equals(method, accessControlRequestMethod, StringComparison.OrdinalIgnoreCase))
                    {
                        found = true;
                        break;
                    }
                }

                if (!found)
                {
                    _logger?.PolicyFailure();
                    _logger?.AccessControlMethodNotAllowed(accessControlRequestMethod);
                    return;
                }
            }

            if (!policy.AllowAnyHeader &&
                requestHeaders != null)
            {
                foreach (var requestHeader in requestHeaders)
                {
                    if (!CorsConstants.SimpleRequestHeaders.Contains(requestHeader, StringComparer.OrdinalIgnoreCase) &&
                        !policy.Headers.Contains(requestHeader, StringComparer.OrdinalIgnoreCase))
                    {
                        _logger?.PolicyFailure();
                        _logger?.RequestHeaderNotAllowed(requestHeader);
                        return;
                    }
                }
            }

            AddOriginToResult(origin, policy, result);
            result.SupportsCredentials = policy.SupportsCredentials;
            result.PreflightMaxAge     = policy.PreflightMaxAge;
            result.AllowedMethods.Add(accessControlRequestMethod);
            AddHeaderValues(result.AllowedHeaders, requestHeaders);
            _logger?.PolicySuccess();
        }
Пример #28
0
        /// <inheritdoc />
        public virtual void ApplyResult(CorsResult result, HttpResponse response)
        {
            if (result == null)
            {
                throw new ArgumentNullException(nameof(result));
            }

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

            if (!result.IsOriginAllowed)
            {
                // In case a server does not wish to participate in the CORS protocol, its HTTP response to the
                // CORS or CORS-preflight request must not include any of the above headers.
                return;
            }

            var headers = response.Headers;

            headers[CorsConstants.AccessControlAllowOrigin] = result.AllowedOrigin;

            if (result.SupportsCredentials)
            {
                headers[CorsConstants.AccessControlAllowCredentials] = "true";
            }

            if (result.IsPreflightRequest)
            {
                _logger.IsPreflightRequest();

                // An HTTP response to a CORS-preflight request can include the following headers:
                // `Access-Control-Allow-Methods`, `Access-Control-Allow-Headers`, `Access-Control-Max-Age`
                if (result.AllowedHeaders.Count > 0)
                {
                    headers.SetCommaSeparatedValues(CorsConstants.AccessControlAllowHeaders, result.AllowedHeaders.ToArray());
                }

                if (result.AllowedMethods.Count > 0)
                {
                    headers.SetCommaSeparatedValues(CorsConstants.AccessControlAllowMethods, result.AllowedMethods.ToArray());
                }

                if (result.PreflightMaxAge.HasValue)
                {
                    headers[CorsConstants.AccessControlMaxAge] = result.PreflightMaxAge.Value.TotalSeconds.ToString(CultureInfo.InvariantCulture);
                }
            }
            else
            {
                // An HTTP response to a CORS request that is not a CORS-preflight request can also include the following header:
                // `Access-Control-Expose-Headers`
                if (result.AllowedExposedHeaders.Count > 0)
                {
                    headers.SetCommaSeparatedValues(CorsConstants.AccessControlExposeHeaders, result.AllowedExposedHeaders.ToArray());
                }
            }

            if (result.VaryByOrigin)
            {
                headers.Append("Vary", "Origin");
            }
        }
Пример #29
0
 public virtual void EvaluatePreflightRequest(HttpContext context, CorsPolicy policy, CorsResult result)
 {
     PopulateResult(context, policy, result);
 }