refactor the algorithm for testing the cors

while here:
- send cors headers even if the status is 404
- add: access_control_expose_headers config option - list of additional headers sent in Access-Control-Expose-Headers
- add: access_control_allow_credentials config option - if true return Access-Control-Allow-Credentials header equal "true"
This commit is contained in:
2022-09-08 03:12:44 +02:00
parent 04164ff967
commit 222a1c8a1f
9 changed files with 245 additions and 89 deletions

View File

@@ -654,8 +654,6 @@ void App::MakeRenameMeToABetterName()
////////////////////////
cur.request->PrepareAnswerType();
if( cur.session->ip_ban && cur.session->ip_ban->IsIPBanned() )
@@ -669,6 +667,11 @@ void App::MakeRenameMeToABetterName()
cur.request->http_status = Header::status_403_forbidden;
}
if( cur.request->function )
{
cur.request->function->CheckOriginHeader();
}
// cur.request->status can be changed by function_parser
if( cur.request->status == WINIX_ERR_OK && cur.request->http_status == Header::status_200_ok )
plugin.Call(WINIX_PREPARE_REQUEST);
@@ -687,7 +690,9 @@ void App::MakeRenameMeToABetterName()
AddDefaultModels();
if( cur.request->status == WINIX_ERR_OK && cur.request->http_status == Header::status_200_ok )
// what about 204 No Content from preflight requests? should we make MakeOptions()?
if( (cur.request->status == WINIX_ERR_OK && cur.request->http_status == Header::status_200_ok) ||
(cur.request->method == Request::Method::options && cur.request->http_status == Header::status_204_no_content))
functions.MakeFunction();
if( cur.request->run_state == Request::RunState::normal_run )

View File

@@ -353,6 +353,8 @@ void Config::AssignValues()
allow_all_cors_origins = Bool(L"allow_all_cors_origins", false);
ListText(L"allowed_cors_origins", allowed_cors_origins);
ListText(L"access_control_expose_headers", access_control_expose_headers);
access_control_allow_credentials = Bool(L"access_control_allow_credentials", false);
}

View File

@@ -984,10 +984,23 @@ public:
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
// default: empty
std::vector<std::wstring> allowed_cors_origins;
// list of additional headers sent in Access-Control-Expose-Headers header
// can be set per controller in a method: virtual void FunctionBase::AddAccessControlExposeHeadersHeader()
// default: empty
std::vector<std::wstring> access_control_expose_headers;
// if true return Access-Control-Allow-Credentials header equal "true"
// whethert credentials (e.g. cookies, authorization headers) are available in cors requests
// can be set per controller in a method: virtual bool FunctionBase::AreCorsCredentialsAvailable()
// default: false
bool access_control_allow_credentials;
Config();
bool ReadConfig(const std::wstring & config_file);

View File

@@ -69,6 +69,47 @@ void Header::prepare_status_value(int http_status, pt::WTextStream & value, bool
}
bool Header::is_header_value_char_correct(wchar_t c)
{
/*
* make sure to not allow at least \r or \r
*/
return c > 32 && c < 127;
}
bool Header::is_header_value_correct(const wchar_t * str)
{
for( ; *str ; ++str)
{
if( !is_header_value_char_correct(*str) )
{
return false;
}
}
return true;
}
bool Header::is_header_value_correct(const std::wstring & str)
{
/*
* dont use is_header_value_correct(str.c_str()) as there can be a null character (0) inside the string
*/
for(size_t i=0 ; i < str.size() ; ++i)
{
if( !is_header_value_char_correct(str[i]) )
{
return false;
}
}
return true;
}
}

View File

@@ -54,10 +54,13 @@ public:
static constexpr const wchar_t * accept_language = L"Accept-Language";
static constexpr const wchar_t * authorization = L"Authorization";
static constexpr const wchar_t * allow = L"Allow";
static constexpr const wchar_t * very = L"Very";
static constexpr const wchar_t * origin = L"Origin";
static constexpr const wchar_t * access_control_allow_methods = L"Access-Control-Allow-Methods";
static constexpr const wchar_t * access_control_allow_origin = L"Access-Control-Allow-Origin";
static constexpr const wchar_t * access_control_allow_headers = L"Access-Control-Allow-Headers";
static constexpr const wchar_t * access_control_allow_credentials = L"Access-Control-Allow-Credentials";
static constexpr const wchar_t * access_control_expose_headers = L"Access-Control-Expose-Headers";
static constexpr const wchar_t * access_control_max_age = L"Access-Control-Max-Age";
/*
@@ -132,6 +135,9 @@ public:
static const wchar_t * find_status_string_value(int http_status);
static void prepare_status_value(int http_status, pt::WTextStream & value, bool clear_value = true);
static bool is_header_value_char_correct(wchar_t c);
static bool is_header_value_correct(const wchar_t * str);
static bool is_header_value_correct(const std::wstring & str);
protected:

View File

@@ -1531,37 +1531,10 @@ void Request::PrepareSessionCookie()
}
// preflight request are tested in function->MakeOption()
void Request::CheckCorsHeaders()
{
pt::Space * origin = headers_in.get_space_nc(L"Origin");
if( origin && origin->is_wstr() && function )
{
if( !out_headers.has_key(Header::access_control_allow_origin) )
{
if( function->IsCorsOriginAvailable(*origin->get_wstr()) )
{
function->AddAccessControlAllowOriginHeader(*origin->get_wstr());
}
}
if( !out_headers.has_key(Header::access_control_allow_credentials) )
{
if( function->AreCorsCredentialsAvailable() )
{
function->AddAccessControlAllowCredentialsHeader();
}
}
}
}
void Request::PrepareHeaders(bool compressing, int compress_encoding, size_t output_size)
{
PrepareSessionCookie();
CheckCorsHeaders();
if( send_as_attachment )
{

View File

@@ -595,7 +595,6 @@ private:
int SelectDeflateVersion();
void SelectCompression(size_t source_len, bool & compression_allowed, int & compression_encoding);
void PrepareSessionCookie();
void CheckCorsHeaders();
void PrepareHeaders(bool compressing, int compress_encoding, size_t output_size);
void ModifyStatusForRedirect();
void PrepareSendFileHeaderForStaticMountpoint();