add config options for testing the Origin header in cors requests
- allow_all_cors_origins - whether or not to allow all origins - allowed_cors_origins - a list of allowed origins (used if allow_all_cors_origins is false)
This commit is contained in:
parent
778ed01a55
commit
a19158cb62
|
@ -350,6 +350,9 @@ void Config::AssignValues()
|
||||||
add_header_cache_no_store_in_htmx_request = Bool(L"add_header_cache_no_store_in_htmx_request", true);
|
add_header_cache_no_store_in_htmx_request = Bool(L"add_header_cache_no_store_in_htmx_request", true);
|
||||||
|
|
||||||
request_queue_job_limit = Size(L"request_queue_job_limit", 1024);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -976,7 +976,17 @@ public:
|
||||||
// if the limit is reached then the http status 503 Service Unavailable is returned
|
// if the limit is reached then the http status 503 Service Unavailable is returned
|
||||||
size_t request_queue_job_limit;
|
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
|
||||||
|
// used only if allow_all_cors_origins is false
|
||||||
|
// default: empty
|
||||||
|
std::vector<std::wstring> allowed_cors_origins;
|
||||||
|
|
||||||
|
|
||||||
Config();
|
Config();
|
||||||
|
|
|
@ -1628,8 +1628,23 @@ void slice_by(const std::wstring & str, wchar_t c, std::vector<std::wstring> & o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool is_in_list(const std::wstring & item, const std::vector<std::wstring> & list)
|
||||||
|
{
|
||||||
|
return is_in_list_generic(item, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool is_in_list(const std::wstring & item, const std::list<std::wstring> & list)
|
||||||
|
{
|
||||||
|
return is_in_list_generic(item, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool is_in_list(const std::wstring & item, const std::set<std::wstring> & list)
|
||||||
|
{
|
||||||
|
return is_in_list_generic(item, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Winix
|
} // namespace Winix
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
#define headerfile_winix_core_misc
|
#define headerfile_winix_core_misc
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
@ -1032,6 +1034,28 @@ void timespec_to_stream_with_unit(timespec & val, pt::Stream & stream);
|
||||||
void slice_by(const std::wstring & str, wchar_t c, std::vector<std::wstring> & out);
|
void slice_by(const std::wstring & str, wchar_t c, std::vector<std::wstring> & out);
|
||||||
|
|
||||||
|
|
||||||
|
bool is_in_list(const std::wstring & item, const std::vector<std::wstring> & list);
|
||||||
|
bool is_in_list(const std::wstring & item, const std::list<std::wstring> & list);
|
||||||
|
bool is_in_list(const std::wstring & item, const std::set<std::wstring> & list);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<typename ContainerType>
|
||||||
|
bool is_in_list_generic(const std::wstring & item, const ContainerType & list)
|
||||||
|
{
|
||||||
|
for(const std::wstring & str : list)
|
||||||
|
{
|
||||||
|
if( str == item )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Winix
|
} // namespace Winix
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1536,10 +1536,25 @@ void Request::PrepareSessionCookie()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Request::CheckOriginHeader()
|
||||||
|
{
|
||||||
|
pt::Space * origin = headers_in.get_space_nc(L"Origin");
|
||||||
|
|
||||||
|
if( origin && origin->is_wstr() && !out_headers.has_key(Header::access_control_allow_origin) )
|
||||||
|
{
|
||||||
|
if( function && function->IsCorsOriginAvailable(*origin->get_wstr()) )
|
||||||
|
{
|
||||||
|
function->AddAccessControlAllowOriginHeader(*origin->get_wstr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Request::PrepareHeaders(bool compressing, int compress_encoding, size_t output_size)
|
void Request::PrepareHeaders(bool compressing, int compress_encoding, size_t output_size)
|
||||||
{
|
{
|
||||||
PrepareSessionCookie();
|
PrepareSessionCookie();
|
||||||
|
CheckOriginHeader();
|
||||||
|
|
||||||
if( send_as_attachment )
|
if( send_as_attachment )
|
||||||
{
|
{
|
||||||
|
|
|
@ -595,6 +595,7 @@ private:
|
||||||
int SelectDeflateVersion();
|
int SelectDeflateVersion();
|
||||||
void SelectCompression(size_t source_len, bool & compression_allowed, int & compression_encoding);
|
void SelectCompression(size_t source_len, bool & compression_allowed, int & compression_encoding);
|
||||||
void PrepareSessionCookie();
|
void PrepareSessionCookie();
|
||||||
|
void CheckOriginHeader();
|
||||||
void PrepareHeaders(bool compressing, int compress_encoding, size_t output_size);
|
void PrepareHeaders(bool compressing, int compress_encoding, size_t output_size);
|
||||||
void ModifyStatusForRedirect();
|
void ModifyStatusForRedirect();
|
||||||
void PrepareSendFileHeaderForStaticMountpoint();
|
void PrepareSendFileHeaderForStaticMountpoint();
|
||||||
|
|
|
@ -151,8 +151,22 @@ bool FunctionBase::IsCorsMethodAvailable(Request::Method method)
|
||||||
|
|
||||||
bool FunctionBase::IsCorsOriginAvailable(const std::wstring & origin_url)
|
bool FunctionBase::IsCorsOriginAvailable(const std::wstring & origin_url)
|
||||||
{
|
{
|
||||||
// true by default for all urles
|
if( config )
|
||||||
return true;
|
{
|
||||||
|
if( config->allow_all_cors_origins )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 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 false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -175,10 +189,25 @@ void FunctionBase::AddAccessControlAllowMethodsHeader(Request::Method method)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* origin_url is the value of Origin header sent by the client
|
* origin_url is the value of Origin header sent by the client
|
||||||
|
* origin_url can be: "null"
|
||||||
|
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
void FunctionBase::AddAccessControlAllowOriginHeader(const std::wstring & origin_url)
|
void FunctionBase::AddAccessControlAllowOriginHeader(const std::wstring & origin_url)
|
||||||
{
|
{
|
||||||
cur->request->AddHeader(Header::access_control_allow_origin, origin_url);
|
if( config )
|
||||||
|
{
|
||||||
|
if( config->allow_all_cors_origins )
|
||||||
|
{
|
||||||
|
cur->request->AddHeader(Header::access_control_allow_origin, L"*");
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -246,27 +275,34 @@ void FunctionBase::MakeOptions()
|
||||||
* (we allow Access-Control-Request-Headers not to be present)
|
* (we allow Access-Control-Request-Headers not to be present)
|
||||||
*/
|
*/
|
||||||
Request::Method method = Request::CheckRequestMethod(cors_method->get_wstr()->c_str());
|
Request::Method method = Request::CheckRequestMethod(cors_method->get_wstr()->c_str());
|
||||||
|
bool cors_available = false;
|
||||||
|
|
||||||
if( IsCorsMethodAvailable(method) && IsCorsOriginAvailable(*cors_origin->get_wstr()) )
|
if( IsCorsMethodAvailable(method) && IsCorsOriginAvailable(*cors_origin->get_wstr()) )
|
||||||
{
|
{
|
||||||
bool cors_available = true;
|
cors_available = true;
|
||||||
|
|
||||||
if( cors_headers && cors_headers->is_wstr() )
|
if( cors_headers && cors_headers->is_wstr() )
|
||||||
{
|
{
|
||||||
cors_available = AreCorsHeadersAvailable(*cors_headers->get_wstr());
|
cors_available = AreCorsHeadersAvailable(*cors_headers->get_wstr());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if( cors_available )
|
if( cors_available )
|
||||||
|
{
|
||||||
|
AddAccessControlAllowMethodsHeader(method);
|
||||||
|
AddAccessControlAllowOriginHeader(*cors_origin->get_wstr());
|
||||||
|
AddAccessControlMaxAgeHeader();
|
||||||
|
|
||||||
|
if( cors_headers && cors_headers->is_wstr() )
|
||||||
{
|
{
|
||||||
AddAccessControlAllowMethodsHeader(method);
|
AddAccessControlAllowHeadersHeader(*cors_headers->get_wstr());
|
||||||
AddAccessControlAllowOriginHeader(*cors_origin->get_wstr());
|
|
||||||
AddAccessControlMaxAgeHeader();
|
|
||||||
|
|
||||||
if( cors_headers && cors_headers->is_wstr() )
|
|
||||||
{
|
|
||||||
AddAccessControlAllowHeadersHeader(*cors_headers->get_wstr());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log << log3 << "FunctionBase: cors requests are permitted" << logend;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log << log2 << "FunctionBase: cors requests are not permitted" << logend;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
Loading…
Reference in New Issue