Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5287a7c
Enable cors for .NET (core) when CORS_ALLOW_ORIGIN is defined at apps…
claudiamurialdo Jul 20, 2022
fadfc5e
Support Cors in ASP.NET Framework.
claudiamurialdo Jul 21, 2022
877937b
Rearrange usings.
claudiamurialdo Jul 22, 2022
f4515e9
Restore missing lines lost at merge.
claudiamurialdo Jul 23, 2022
09d0040
Support CORS configuration for API object in .NET Framework.
claudiamurialdo Aug 4, 2022
7bff61b
Revert changes made by mistake.
claudiamurialdo Aug 4, 2022
a4c2974
Enable CORS for WCF rest services in .NET Framework.
claudiamurialdo Aug 5, 2022
94d7422
Stop adding manually CORS headers in .NET (core) api objects.
claudiamurialdo Aug 5, 2022
22485d3
The CORS protocol does not allow specifying a wildcard (any) origin a…
claudiamurialdo Aug 8, 2022
2d5d795
Merge branch 'cors' of https://github.com/genexuslabs/DotNetClasses i…
claudiamurialdo Aug 8, 2022
870a739
Keep cors enabled for API Objects even when Preferences.CorsEnabled i…
claudiamurialdo Aug 8, 2022
b0baf83
Add Neutralization of CRLF Sequences in HTTP Headers
claudiamurialdo Aug 11, 2022
3eb4f7e
Unify behavior of headers returned by rest services, webpanels and AP…
claudiamurialdo Sep 9, 2022
0a413f7
Fix tests for cors.
claudiamurialdo Sep 12, 2022
5a1686f
Fix CORS_ALLOW_ORIGIN setting.
claudiamurialdo Sep 12, 2022
e008682
HttpResponse.get_Headers is not supported on WebServer: WebDev.WebSer…
claudiamurialdo Sep 13, 2022
1ad50fe
Fix ArgumentNullException when GX_CORS_ALLOW_ORIGIN is defined and re…
claudiamurialdo Sep 13, 2022
6f8de11
Remove System.Diagnostics.Debugger.Launch();
claudiamurialdo Sep 13, 2022
77bf1aa
Fix merge error.
claudiamurialdo Sep 13, 2022
f65bd55
Trim origin entries defined at CORS_ALLOW_ORIGIN.
claudiamurialdo Sep 20, 2022
03b1375
Merge branch 'master' into cors
claudiamurialdo Sep 20, 2022
fbc3cc3
Fix build error. Remove duplicate using.
claudiamurialdo Sep 20, 2022
5962171
Merge branch 'master' into cors
claudiamurialdo Sep 21, 2022
fb6093a
Fix format error.
claudiamurialdo Sep 21, 2022
179fdab
OPTIONS verb to API Objects returned all verbs on Allow header when I…
claudiamurialdo Sep 22, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXRouting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
Expand All @@ -20,6 +21,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Net.Http.Headers;

namespace GxClasses.Web.Middleware
{
Expand Down Expand Up @@ -254,26 +256,24 @@ public Task ProcessRestRequest(HttpContext context)
}
else if (HttpMethods.IsOptions(context.Request.Method))
{
string mthheaders = "OPTIONS,HEAD";
List<string> mthheaders = new List<string>() { $"{HttpMethod.Options.Method},{HttpMethod.Head.Method}" };
if (!String.IsNullOrEmpty(actualPath) && servicesMapData.ContainsKey(actualPath))
{
foreach (Tuple<string, string> t in servicesMapData[actualPath].Keys)
{
if (t.Item1.Equals(controllerWithParms.ToLower()))
{
mthheaders += "," + t.Item2;
mthheaders.Add(t.Item2);
}
}
}
else
{
mthheaders += ", GET, POST";
mthheaders.Add(HttpMethod.Get.Method);
mthheaders.Add(HttpMethod.Post.Method);
}
context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { (string)context.Request.Headers["Origin"] });
context.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "Origin, X-Requested-With, Content-Type, Accept" });
context.Response.Headers.Add("Access-Control-Allow-Methods", new[] { mthheaders });
context.Response.Headers.Add("Access-Control-Allow-Credentials", new[] { "true" });
context.Response.Headers.Add("Allow", mthheaders);
HttpHelper.CorsHeaders(context);
HttpHelper.AllowHeader(context, mthheaders);
context.Response.StatusCode = (int)HttpStatusCode.OK;
}
else
Expand Down
3 changes: 3 additions & 0 deletions dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
[assembly: InternalsVisibleTo("AzureFunctionsTest")]
[assembly: InternalsVisibleTo("GXQueue")]
[assembly: InternalsVisibleTo("DotNetCoreUnitTest")]
[assembly: InternalsVisibleTo("GeneXus.Deploy.AzureFunctions.Handlers")]
[assembly: InternalsVisibleTo("AzureFunctionsTest")]
[assembly: InternalsVisibleTo("GXMessageBroker")]
44 changes: 44 additions & 0 deletions dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ public class Startup
const string SWAGGER_DEFAULT_YAML = "default.yaml";
const string DEVELOPER_MENU = "developermenu.html";
const string SWAGGER_SUFFIX = "swagger";
const string CORS_POLICY_NAME = "AllowSpecificOriginsPolicy";
const string CORS_ANY_ORIGIN = "*";
const double CORS_MAX_AGE_SECONDS = 86400;

public List<string> servicesBase = new List<string>();

Expand Down Expand Up @@ -193,9 +196,41 @@ public void ConfigureServices(IServiceCollection services)
options.EnableForHttps = true;
});
}
DefineCorsPolicy(services);
services.AddMvc();
}

private void DefineCorsPolicy(IServiceCollection services)
{
if (Preferences.CorsEnabled)
{
string corsAllowedOrigins = Preferences.CorsAllowedOrigins();
if (!string.IsNullOrEmpty(corsAllowedOrigins))
{
string[] origins = corsAllowedOrigins.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
foreach (string origin in origins)
{
GXLogging.Info(log, $"Adding origin to CORS policy:", origin);
}
services.AddCors(options =>
{
options.AddPolicy(name: CORS_POLICY_NAME,
policy =>
{
policy.WithOrigins(origins);
if (!corsAllowedOrigins.Contains(CORS_ANY_ORIGIN))
{
policy.AllowCredentials();
}
policy.AllowAnyHeader();
policy.AllowAnyMethod();
policy.SetPreflightMaxAge(TimeSpan.FromSeconds(CORS_MAX_AGE_SECONDS));
});
});
}
}
}

private void ConfigureSessionService(IServiceCollection services, ISessionService sessionService)
{
if (sessionService is GxRedisSession)
Expand Down Expand Up @@ -246,6 +281,7 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos
app.UseCookiePolicy();
app.UseSession();
app.UseStaticFiles();
ConfigureCors(app);
ConfigureSwaggerUI(app, baseVirtualPath);

if (Directory.Exists(Path.Combine(LocalPath, RESOURCES_FOLDER)))
Expand Down Expand Up @@ -353,6 +389,14 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos
app.UseEnableRequestRewind();
}

private void ConfigureCors(IApplicationBuilder app)
{
if (Preferences.CorsEnabled)
{
app.UseCors(CORS_POLICY_NAME);
}
}

private void ConfigureSwaggerUI(IApplicationBuilder app, string baseVirtualPath)
{
try
Expand Down
20 changes: 17 additions & 3 deletions dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ namespace GeneXus.Configuration
#if NETCORE
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using System.Text;
#else
using System.Web;
#endif
using System.Configuration;
using System.Collections.Generic;
#endif
using System.Collections;
using System.Collections.Specialized;
using System.Xml;
Expand All @@ -23,9 +25,7 @@ namespace GeneXus.Configuration
using System.Collections.Concurrent;
using System.Reflection;
using System.Runtime.Serialization.Json;
using System.Collections.Generic;
using GxClasses.Helpers;
using System.Text;

public class Config
{
Expand Down Expand Up @@ -1312,6 +1312,19 @@ public static string ApplicationPath
set { _applicationPath = value; }
}

internal static bool CorsEnabled {
get {
return !string.IsNullOrEmpty(CorsAllowedOrigins());
}
}

internal static string CorsAllowedOrigins()
{
if (Config.GetValueOf("CORS_ALLOW_ORIGIN", out string corsOrigin))
return corsOrigin;
else
return string.Empty;
}
public static int GetMaximumOpenCursors()
{
if (maximumOpenCursors == 0)
Expand Down Expand Up @@ -1372,5 +1385,6 @@ public static int GetHttpClientMaxConnectionPerRoute()
return httpclient_max_per_route;

}

}
}
1 change: 1 addition & 0 deletions dotnet/src/dotnetframework/GxClasses/GxClasses.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<ItemGroup>
<PackageReference Include="ManagedFusion.Rewriter" Version="3.7.0" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.5.1" />
<PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.0" />
<PackageReference Include="MySqlConnector" Version="1.0.1" />
<PackageReference Include="Npgsql" Version="3.2.7" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.5.1" />
Expand Down
112 changes: 105 additions & 7 deletions dotnet/src/dotnetframework/GxClasses/Helpers/HttpHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Primitives;
using System.Net.Http;
using Microsoft.AspNetCore.Mvc.Formatters;
#else
using System.ServiceModel.Web;
using System.ServiceModel;

using System.ServiceModel.Channels;
#endif
using System;
using System.Collections.Generic;
Expand All @@ -25,6 +24,8 @@
using System.Runtime.Serialization;
using GeneXus.Mime;
using System.Text.RegularExpressions;
using Microsoft.Net.Http.Headers;
using System.Net.Http;

namespace GeneXus.Http
{
Expand Down Expand Up @@ -82,6 +83,99 @@ public class HttpHelper
const string GAM_CODE_TOKEN_EXPIRED = "103";
static Regex CapitalsToTitle = new Regex(@"(?<=[A-Z])(?=[A-Z][a-z]) | (?<=[^A-Z])(?=[A-Z]) | (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace);

const string CORS_MAX_AGE_SECONDS = "86400";
internal static void CorsHeaders(HttpContext httpContext)
{
if (Preferences.CorsEnabled)
{
string[] origins = Preferences.CorsAllowedOrigins().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (httpContext != null)
{
string requestHeaders = httpContext.Request.Headers[HeaderNames.AccessControlRequestHeaders];
string requestMethod = httpContext.Request.Headers[HeaderNames.AccessControlRequestMethod];
CorsValuesToHeaders(httpContext.Response, origins, requestHeaders, requestMethod);
}
}
}
#if !NETCORE
internal static void CorsHeaders(HttpResponseMessageProperty response, string requestHeaders, string requestMethods)
{
if (Preferences.CorsEnabled)
{
string[] origins = Preferences.CorsAllowedOrigins().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
CorsValuesToHeaders(response, origins, requestHeaders, requestMethods);
}
}
internal static void CorsHeaders(WebOperationContext wcfContext)
{
if (Preferences.CorsEnabled)
{
string[] origins = Preferences.CorsAllowedOrigins().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (wcfContext != null)
{
string requestHeaders = wcfContext.IncomingRequest.Headers[HeaderNames.AccessControlRequestHeaders];
string requestMethods = wcfContext.IncomingRequest.Headers[HeaderNames.AccessControlRequestMethod];
CorsValuesToHeaders(wcfContext.OutgoingResponse, origins, requestHeaders, requestMethods);
}

}
}
static void CorsValuesToHeaders(OutgoingWebResponseContext httpResponse, string[] origins, string requestHeaders, string requestMethods)
{
foreach (string origin in origins)
{
if (!string.IsNullOrEmpty(origin))
httpResponse.Headers[HeaderNames.AccessControlAllowOrigin] = origin;
}
httpResponse.Headers[HeaderNames.AccessControlAllowCredentials] = true.ToString();

if (!string.IsNullOrEmpty(requestHeaders))
httpResponse.Headers[HeaderNames.AccessControlAllowHeaders] = requestHeaders;

if (!string.IsNullOrEmpty(requestMethods))
httpResponse.Headers[HeaderNames.AccessControlAllowMethods] = requestMethods;

httpResponse.Headers[HeaderNames.AccessControlMaxAge] = CORS_MAX_AGE_SECONDS;

}
static void CorsValuesToHeaders(HttpResponseMessageProperty httpResponse, string[] origins, string requestHeaders, string requestMethods)
{
foreach (string origin in origins)
{
if (!string.IsNullOrEmpty(origin))
httpResponse.Headers[HeaderNames.AccessControlAllowOrigin] = origin;
}
httpResponse.Headers[HeaderNames.AccessControlAllowCredentials] = true.ToString();

if (!string.IsNullOrEmpty(requestHeaders))
httpResponse.Headers[HeaderNames.AccessControlAllowHeaders] = requestHeaders;

if (!string.IsNullOrEmpty(requestMethods))
httpResponse.Headers[HeaderNames.AccessControlAllowMethods] = requestMethods;

httpResponse.Headers[HeaderNames.AccessControlMaxAge] = CORS_MAX_AGE_SECONDS;
}

#endif
static void CorsValuesToHeaders(HttpResponse httpResponse, string[] origins, string requestHeaders, string requestMethods)
{
//AppendHeader must be used on httpResponse (instead of httpResponse.Headers[]) to support WebDev.WevServer2
foreach (string origin in origins)
{
if (!string.IsNullOrEmpty(origin))
httpResponse.AppendHeader(HeaderNames.AccessControlAllowOrigin, origin);
}
httpResponse.AppendHeader(HeaderNames.AccessControlAllowCredentials, true.ToString());

if (!string.IsNullOrEmpty(requestHeaders))
httpResponse.AppendHeader(HeaderNames.AccessControlAllowHeaders, requestHeaders);

if (!string.IsNullOrEmpty(requestMethods))
httpResponse.AppendHeader(HeaderNames.AccessControlAllowMethods, requestMethods);

httpResponse.AppendHeader(HeaderNames.AccessControlMaxAge, CORS_MAX_AGE_SECONDS);
}

public static void SetResponseStatus(HttpContext httpContext, string statusCode, string statusDescription)
{
HttpStatusCode httpStatusCode = MapStatusCode(statusCode);
Expand Down Expand Up @@ -282,7 +376,7 @@ public static string RequestPhysicalApplicationPath(HttpContext context = null)
#if NETCORE
public static byte[] DownloadFile(string url, out HttpStatusCode statusCode)
{
var buffer = Array.Empty<byte>();
byte[] buffer = Array.Empty<byte>();
using (var client = new HttpClient())
{
using (HttpResponseMessage response = client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead).Result)
Expand Down Expand Up @@ -350,6 +444,10 @@ public static string[] GetParameterValues(string query)
}
}

internal static void AllowHeader(HttpContext httpContext, List<string> methods)
{
httpContext.Response.AppendHeader(HeaderNames.Allow, string.Join(",", methods));
}
}
#if NETCORE
public class HttpCookieCollection : Dictionary<string, HttpCookie>
Expand Down Expand Up @@ -671,7 +769,7 @@ public static NameValueCollection GetQueryString(this HttpRequest request)
#if NETCORE
NameValueCollection paramValues = new NameValueCollection();

foreach (var key in request.Query.Keys)
foreach (string key in request.Query.Keys)
{
paramValues.Add(key, request.Query[key].ToString());
}
Expand All @@ -691,15 +789,15 @@ public static NameValueCollection GetParams(this HttpRequest request)
NameValueCollection paramValues = request.GetQueryString();
try
{
foreach (var key in request.Form.Keys)
foreach (string key in request.Form.Keys)
{
paramValues.Add(key, request.Form[key].ToString());
}
}
catch (InvalidOperationException) {
//The Form property is populated when the HTTP request Content-Type value is either "application/x-www-form-urlencoded" or "multipart/form-data".
}
foreach (var key in request.Cookies.Keys)
foreach (string key in request.Cookies.Keys)
{
paramValues.Add(key, request.Cookies[key]);
}
Expand Down Expand Up @@ -878,7 +976,7 @@ public static string GetAbsolutePath(this HttpRequest request)
public static string GetFilePath(this HttpRequest request)
{
#if NETCORE
var basePath = string.Empty;
string basePath = string.Empty;
if (request.PathBase.HasValue)
basePath = request.PathBase.Value;
if (request.Path.HasValue)
Expand Down
Loading