change how origin header is treated

Now check whether the origin is in allowed_origins table,
and if not check allow_all_origins config parameter.

While here:
- add are_cors_preflight_requests_available - if true then preflight
  requests are available (default false)
This commit is contained in:
Tomasz Sowa 2022-09-09 01:01:39 +02:00
parent 05ecac8426
commit 6138497fe0
4 changed files with 76 additions and 51 deletions

View File

@ -357,8 +357,9 @@ void Config::AssignValues()
request_queue_job_limit = Size(L"request_queue_job_limit", 1024);
allow_all_cors_origins = Bool(L"allow_all_cors_origins", false);
ListText(L"allowed_cors_origins", allowed_cors_origins);
ListText(L"allowed_origins", allowed_origins);
allow_all_origins = Bool(L"allow_all_origins", true);
are_cors_preflight_requests_available = Bool(L"are_cors_preflight_requests_available", false);
ListText(L"access_control_expose_headers", access_control_expose_headers);
access_control_allow_credentials = Bool(L"access_control_allow_credentials", false);
}

View File

@ -1002,18 +1002,21 @@ public:
// if the limit is reached then the http status 503 Service Unavailable is returned
size_t request_queue_job_limit;
// whether or not all origins are allowed in cors requests
// default: false;
// if false then we check allowed_cors_origins table to check whether the origin is available (origin is sent in Origin header),
// if allow_all_cors_origins is false and allowed_cors_origins is empty then by default we do not allow cors requests
// (but you can still allow it in your function/controller by overriding IsCorsOriginAvailable(...) method)
bool allow_all_cors_origins;
// list of allowed origins in cors requests
// can be set per controller in a method: virtual bool FunctionBase::IsCorsOriginAvailable(const std::wstring & origin_url)
// used only if allow_all_cors_origins is false
// can be set per controller in a method: virtual bool FunctionBase::IsOriginAvailable(const std::wstring & origin_url)
// default: empty
std::vector<std::wstring> allowed_cors_origins;
std::vector<std::wstring> allowed_origins;
// whether or not all origins are allowed if allowed_origins is empty
// default: true
// this is true by default because Origin header is sent not only in cors requests
// (you can still allow it in your function/controller by overriding IsOriginAvailable(...) method)
bool allow_all_origins;
// whether or not cors preflight requests are available
// https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
// default: false
bool are_cors_preflight_requests_available;
// list of additional headers sent in Access-Control-Expose-Headers header
// can be set per controller in a method: virtual void FunctionBase::AddAccessControlExposeHeadersHeader()

View File

@ -158,20 +158,23 @@ bool FunctionBase::IsCorsMethodAvailable(Request::Method method)
}
bool FunctionBase::IsCorsOriginAvailable(const std::wstring & origin_url)
bool FunctionBase::IsOriginAvailable(const std::wstring & origin_url)
{
if( config )
{
if( config->allow_all_cors_origins )
{
return true;
}
else
if( !config->allowed_origins.empty() )
{
// origin_url can be a "null" string
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin
// but in such a case the "null" should be put to the config as well
return is_in_list(origin_url, config->allowed_cors_origins);
return is_in_list(origin_url, config->allowed_origins);
}
else
{
if( config->allow_all_origins )
{
return true;
}
}
}
@ -212,15 +215,18 @@ void FunctionBase::AddAccessControlAllowOriginHeader(const std::wstring & origin
{
if( config )
{
if( config->allow_all_cors_origins )
if( !config->allowed_origins.empty() )
{
cur->request->AddHeader(Header::access_control_allow_origin, L"*");
// method IsOriginAvailable(..) was called beforehand so now we assume
// that the origin_url is permitted (and is valid as a header)
cur->request->AddHeader(Header::access_control_allow_origin, origin_url);
}
else
{
// method IsCorsOriginAvailable(..) was called beforehand so now we assume
// that the origin_url is permitted
cur->request->AddHeader(Header::access_control_allow_origin, origin_url);
if( config->allow_all_origins )
{
cur->request->AddHeader(Header::access_control_allow_origin, L"*");
}
}
}
}
@ -307,12 +313,47 @@ void FunctionBase::AddCorsNormalRequestHeaders(const std::wstring & origin)
}
void FunctionBase::CheckCorsPreflightRequest(const std::wstring & origin, const std::wstring & method_string)
{
pt::Space * cors_headers = cur->request->headers_in.get_space_nc(L"Access_Control_Request_Headers");
Request::Method method = Request::CheckRequestMethod(method_string.c_str());
if( IsCorsMethodAvailable(method) )
{
bool cors_headers_available = true;
std::wstring * headers = nullptr;
if( cors_headers && cors_headers->is_wstr() )
{
headers = cors_headers->get_wstr();
cors_headers_available = AreCorsHeadersAvailable(*headers);
}
if( cors_headers_available )
{
AddCorsPreflightRequestHeaders(origin, method, headers);
}
else
{
if( headers )
{
log << log2 << "FunctionBase: these cors headers: " << *headers << " are not permitted in cors requests" << logend;
}
}
}
else
{
log << log2 << "FunctionBase: this method: " << method_string << " is not permitted in cors requests" << logend;
}
}
void FunctionBase::AddResponseHeadersForOrigin(const std::wstring & origin)
{
if( cur->request->method == Request::Method::options )
{
pt::Space * cors_method = cur->request->headers_in.get_space_nc(L"Access_Control_Request_Method"); // FastCGI changes '-' to '_'
pt::Space * cors_headers = cur->request->headers_in.get_space_nc(L"Access_Control_Request_Headers");
cur->request->http_status = Header::status_204_no_content;
if( cors_method && cors_method->is_wstr() )
{
@ -321,35 +362,14 @@ void FunctionBase::AddResponseHeadersForOrigin(const std::wstring & origin)
* https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
* (we allow Access-Control-Request-Headers not to be present)
*/
Request::Method method = Request::CheckRequestMethod(cors_method->get_wstr()->c_str());
cur->request->http_status = Header::status_204_no_content;
if( IsCorsMethodAvailable(method) )
if( config->are_cors_preflight_requests_available )
{
bool cors_headers_available = true;
std::wstring * headers = nullptr;
if( cors_headers && cors_headers->is_wstr() )
{
headers = cors_headers->get_wstr();
cors_headers_available = AreCorsHeadersAvailable(*headers);
}
if( cors_headers_available )
{
AddCorsPreflightRequestHeaders(origin, method, headers);
}
else
{
if( headers )
{
log << log2 << "FunctionBase: these cors headers: " << *headers << " are not permitted in cors requests" << logend;
}
}
CheckCorsPreflightRequest(origin, *cors_method->get_wstr());
}
else
{
log << log2 << "FunctionBase: this method: " << *cors_method->get_wstr() << " is not permitted in cors requests" << logend;
log << log2 << "FunctionBase: cors requests are disabled" << logend;
}
}
else
@ -374,7 +394,7 @@ void FunctionBase::CheckOriginHeader()
if( origin && origin->is_wstr() )
{
if( IsCorsOriginAvailable(*origin->get_wstr()) )
if( IsOriginAvailable(*origin->get_wstr()) )
{
AddResponseHeadersForOrigin(*origin->get_wstr());
}

View File

@ -114,7 +114,7 @@ public:
virtual void AddAllowMethodsHeader();
virtual bool IsCorsMethodAvailable(Request::Method method);
virtual bool IsCorsOriginAvailable(const std::wstring & origin_url);
virtual bool IsOriginAvailable(const std::wstring & origin_url);
virtual bool AreCorsCredentialsAvailable();
virtual bool AreCorsHeadersAvailable(const std::wstring & headers);
@ -126,6 +126,7 @@ public:
virtual void AddAccessControlExposeHeadersHeader();
virtual void AddCorsPreflightRequestHeaders(const std::wstring & origin, Request::Method method, const std::wstring * request_headers);
virtual void AddCorsNormalRequestHeaders(const std::wstring & origin);
virtual void CheckCorsPreflightRequest(const std::wstring & origin, const std::wstring & method_string);
virtual void AddResponseHeadersForOrigin(const std::wstring & origin);
virtual void CheckOriginHeader();