예제 #1
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));
            }

            var corsResult = new CorsResult();
            var accessControlRequestMethod = context.Request.Headers[CorsConstants.AccessControlRequestMethod];
            if (string.Equals(context.Request.Method, CorsConstants.PreflightHttpMethod, StringComparison.Ordinal) &&
                !StringValues.IsNullOrEmpty(accessControlRequestMethod))
            {
                EvaluatePreflightRequest(context, policy, corsResult);
            }
            else
            {
                EvaluateRequest(context, policy, corsResult);
            }

            return corsResult;
        }
예제 #2
0
        public virtual void EvaluateRequest(HttpContext context, CorsPolicy policy, CorsResult result)
        {
            var origin = context.Request.Headers[CorsConstants.Origin];
            if (StringValues.IsNullOrEmpty(origin) || !policy.AllowAnyOrigin && !policy.Origins.Contains(origin))
            {
                return;
            }

            AddOriginToResult(origin, policy, result);
            result.SupportsCredentials = policy.SupportsCredentials;
            AddHeaderValues(result.AllowedExposedHeaders, policy.ExposedHeaders);
        }
예제 #3
0
        public void Default_Constructor()
        {
            // Arrange & Act
            var result = new CorsResult();

            // Assert
            Assert.Empty(result.AllowedHeaders);
            Assert.Empty(result.AllowedExposedHeaders);
            Assert.Empty(result.AllowedMethods);
            Assert.False(result.SupportsCredentials);
            Assert.Null(result.AllowedOrigin);
            Assert.Null(result.PreflightMaxAge);
        }
예제 #4
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);
        }
예제 #5
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);
        }
예제 #6
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);
        }
예제 #7
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;
     }
 }
예제 #8
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);
            }
        }
예제 #9
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"]);
        }
예제 #10
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);
        }
예제 #11
0
        public void ApplyResult_ManyAllowExposedHeaders_ExposedHeadersHeaderAdded()
        {
            // Arrange
            var result = new CorsResult();
            result.AllowedExposedHeaders.Add("foo");
            result.AllowedExposedHeaders.Add("bar");
            result.AllowedExposedHeaders.Add("baz");

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

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

            // Assert
            Assert.Contains("Access-Control-Expose-Headers", httpContext.Response.Headers.Keys);
            var value = Assert.Single(httpContext.Response.Headers.Values);
            Assert.Equal(new[] { "foo,bar,baz" }, value);
            string[] exposedHeaderValues = httpContext.Response.Headers.GetCommaSeparatedValues("Access-Control-Expose-Headers");
            Assert.Equal(3, exposedHeaderValues.Length);
            Assert.Contains("foo", exposedHeaderValues);
            Assert.Contains("bar", exposedHeaderValues);
            Assert.Contains("baz", exposedHeaderValues);
        }
예제 #12
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"]);
        }
예제 #13
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"]);
        }
예제 #14
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);
        }
예제 #15
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);
        }
예제 #16
0
        public void ApplyResult_SomeSimpleAllowMethods_AllowMethodsHeaderAddedForNonSimpleMethods()
        {
            // Arrange
            var result = new CorsResult();
            result.AllowedMethods.Add("PUT");
            result.AllowedMethods.Add("get");
            result.AllowedMethods.Add("DELETE");
            result.AllowedMethods.Add("POST");

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

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

            // Assert
            Assert.Contains("Access-Control-Allow-Methods", httpContext.Response.Headers.Keys);
            var value = Assert.Single(httpContext.Response.Headers.Values);
            Assert.Equal(new[] { "PUT,DELETE" }, value);
            string[] methods = httpContext.Response.Headers.GetCommaSeparatedValues("Access-Control-Allow-Methods");
            Assert.Equal(2, methods.Length);
            Assert.Contains("PUT", methods);
            Assert.Contains("DELETE", methods);
        }
예제 #17
0
        public void ApplyResult_OneAllowMethods_AllowMethodsHeaderAdded()
        {
            // Arrange
            var result = new CorsResult();
            result.AllowedMethods.Add("PUT");

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

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

            // Assert
            Assert.Equal("PUT", httpContext.Response.Headers["Access-Control-Allow-Methods"]);
        }
예제 #18
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);
        }
예제 #19
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"]);
        }
예제 #20
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);
        }
예제 #21
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);
        }
예제 #22
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);
        }
예제 #23
0
        public void ApplyResult_OneAllowExposedHeaders_ExposedHeadersHeaderAdded()
        {
            // Arrange
            var result = new CorsResult();
            result.AllowedExposedHeaders.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-Expose-Headers"]);
        }
        private CorsResult GetCorsResult(
            string origin = null,
            IList<string> headers = null,
            IList<string> methods = null,
            IList<string> exposedHeaders = null,
            long? preFlightMaxAge = null,
            bool? supportsCredentials = null)
        {
            var result = new CorsResult();

            if (origin != null)
            {
                result.AllowedOrigin = origin;
            }

            if (headers != null)
            {
                AddRange(result.AllowedHeaders, headers);
            }

            if (methods != null)
            {
                AddRange(result.AllowedMethods, methods);
            }

            if (exposedHeaders != null)
            {
                AddRange(result.AllowedExposedHeaders, exposedHeaders);
            }

            if (preFlightMaxAge != null)
            {
                result.PreflightMaxAge = TimeSpan.FromSeconds(preFlightMaxAge.Value);
            }

            if (supportsCredentials != null)
            {
                result.SupportsCredentials = supportsCredentials.Value;
            }

            return result;
        }
예제 #25
0
        public void ApplyResult_ReturnsNoHeaders_ByDefault()
        {
            // Arrange
            var result = new CorsResult();
            var httpContext = new DefaultHttpContext();
            var service = new CorsService(new TestCorsOptions());

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

            // Assert
            Assert.Empty(httpContext.Response.Headers);
        }