Fixed error when user could override settings in readonly mode, using HTTP interface [#METR-15268].

This commit is contained in:
Alexey Milovidov 2016-02-12 06:32:05 +03:00
parent 6a2a082ea7
commit a33ab9cbe1
3 changed files with 78 additions and 13 deletions

View File

@ -30,6 +30,7 @@ namespace DB
namespace ErrorCodes
{
extern const int READONLY;
extern const int UNKNOWN_COMPRESSION_METHOD;
}
@ -40,9 +41,6 @@ void HTTPHandler::processQuery(Poco::Net::HTTPServerRequest & request, Poco::Net
HTMLForm params(request);
std::istream & istr = request.stream();
/// Если метод GET, то это эквивалентно выставлению настройки readonly.
bool readonly = request.getMethod() == Poco::Net::HTTPServerRequest::HTTP_GET;
BlockInputStreamPtr query_plan;
/** Часть запроса может быть передана в параметре query, а часть - POST-ом
@ -171,7 +169,29 @@ void HTTPHandler::processQuery(Poco::Net::HTTPServerRequest & request, Poco::Net
else
in = new ConcatReadBuffer(*in_param, *in_post_maybe_compressed);
/// Настройки могут быть переопределены в запросе.
/** Настройки могут быть переопределены в запросе.
* Некоторые параметры (database, default_format, и все что использовались выше),
* не относятся к обычным настройкам (Settings).
*
* Среди настроек есть также readonly.
* readonly = 0 - можно выполнять любые запросы и изменять любые настройки
* readonly = 1 - можно выполнять только запросы на чтение, нельзя изменять настройки
* readonly = 2 - можно выполнять только запросы на чтение, можно изменять настройки кроме настройки readonly
*
* Заметим, что в запросе, если до этого readonly было равно 0,
* пользователь может изменить любые настройки и одновременно выставить readonly в другое значение.
*/
auto & limits = context.getSettingsRef().limits;
/// Если метод GET, то это эквивалентно настройке readonly, выставленной в ненулевое значение.
if (request.getMethod() == Poco::Net::HTTPServerRequest::HTTP_GET)
{
if (limits.readonly == 0)
limits.readonly = 2;
}
auto readonly_before_query = limits.readonly;
for (Poco::Net::NameValueCollection::ConstIterator it = params.begin(); it != params.end(); ++it)
{
if (it->first == "database")
@ -182,10 +202,6 @@ void HTTPHandler::processQuery(Poco::Net::HTTPServerRequest & request, Poco::Net
{
context.setDefaultFormat(it->second);
}
else if (readonly && it->first == "readonly")
{
throw Exception("Setting 'readonly' cannot be overrided in readonly mode", ErrorCodes::READONLY);
}
else if (it->first == "query"
|| it->first == "compress"
|| it->first == "decompress"
@ -195,12 +211,19 @@ void HTTPHandler::processQuery(Poco::Net::HTTPServerRequest & request, Poco::Net
|| it->first == "query_id")
{
}
else /// Все неизвестные параметры запроса рассматриваются, как настройки.
context.setSetting(it->first, it->second);
}
else
{
/// Все остальные параметры запроса рассматриваются, как настройки.
if (readonly)
context.getSettingsRef().limits.readonly = true;
if (readonly_before_query == 1)
throw Exception("Cannot override setting (" + it->first + ") in readonly mode", ErrorCodes::READONLY);
if (readonly_before_query && it->first == "readonly")
throw Exception("Setting 'readonly' cannot be overrided in readonly mode", ErrorCodes::READONLY);
context.setSetting(it->first, it->second);
}
}
if (http_response_compress)
used_output.out->setCompressionLevel(context.getSettingsRef().http_zlib_compression_level);

View File

@ -0,0 +1,22 @@
name value changed
max_rows_to_read 10000 1
readonly 0 0
name value changed
max_rows_to_read 10000 1
readonly 2 1
name value changed
max_rows_to_read 10000 1
readonly 1 1
name value changed
max_rows_to_read 10000 1
readonly 2 1
Ok
Ok
0
0
Ok
Ok

View File

@ -0,0 +1,20 @@
#!/bin/bash
# При POST можно делать что угодно.
curl -sS "http://localhost:8123/?query=SELECT+*+FROM+system.settings+WHERE+name+IN+('readonly','max_rows_to_read')&max_rows_to_read=10000&default_format=PrettySpaceNoEscapes" -d' '
# При GET выставляется readonly = 2.
curl -sS "http://localhost:8123/?query=SELECT+*+FROM+system.settings+WHERE+name+IN+('readonly','max_rows_to_read')&max_rows_to_read=10000&default_format=PrettySpaceNoEscapes"
# Можно самому усилить readonly и при этом изменить какие-то ещё настройки.
curl -sS "http://localhost:8123/?query=SELECT+*+FROM+system.settings+WHERE+name+IN+('readonly','max_rows_to_read')&readonly=1&max_rows_to_read=10000&default_format=PrettySpaceNoEscapes" -d' '
curl -sS "http://localhost:8123/?query=SELECT+*+FROM+system.settings+WHERE+name+IN+('readonly','max_rows_to_read')&readonly=2&max_rows_to_read=10000&default_format=PrettySpaceNoEscapes" -d' '
curl -vsS "http://localhost:8123/?query=DROP+TABLE+IF+EXISTS+test.nonexistent" 2>&1 | grep -q '500 Internal Server Error' && echo 'Ok' || echo 'Fail'
curl -vsS "http://localhost:8123/?readonly=0&query=DROP+TABLE+IF+EXISTS+test.nonexistent" 2>&1 | grep -q '500 Internal Server Error' && echo 'Ok' || echo 'Fail'
curl -sS "http://localhost:8123/?query=DROP+TABLE+IF+EXISTS+test.nonexistent" -d ' ' | wc -l
curl -sS "http://localhost:8123/?readonly=0&query=DROP+TABLE+IF+EXISTS+test.nonexistent" -d ' ' | wc -l
curl -vsS "http://localhost:8123/?readonly=1&query=DROP+TABLE+IF+EXISTS+test.nonexistent" -d ' ' 2>&1 | grep -q '500 Internal Server Error' && echo 'Ok' || echo 'Fail'
curl -vsS "http://localhost:8123/?readonly=2&query=DROP+TABLE+IF+EXISTS+test.nonexistent" -d ' ' 2>&1 | grep -q '500 Internal Server Error' && echo 'Ok' || echo 'Fail'