Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow changeset bounding boxes to be limited in size #409

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 28 additions & 0 deletions include/cgimap/options.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class global_settings_base {
virtual uint32_t get_ratelimiter_ratelimit(bool) const = 0;
virtual uint32_t get_ratelimiter_maxdebt(bool) const = 0;
virtual bool get_ratelimiter_upload() const = 0;
virtual double get_bbox_max_size_1d() const = 0;
virtual double get_bbox_max_size_2d() const = 0;
};

class global_settings_default : public global_settings_base {
Expand Down Expand Up @@ -97,6 +99,14 @@ class global_settings_default : public global_settings_base {
bool get_ratelimiter_upload() const override {
return false;
}

double get_bbox_max_size_1d() const override {
return 360.0;
}

double get_bbox_max_size_2d() const override {
return 180.0 * 360.0;
}
};

class global_settings_via_options : public global_settings_base {
Expand Down Expand Up @@ -175,6 +185,14 @@ class global_settings_via_options : public global_settings_base {
return m_ratelimiter_upload;
}

double get_bbox_max_size_1d() const override {
return m_bbox_max_size_1d;
}

double get_bbox_max_size_2d() const override {
return m_bbox_max_size_2d;
}

private:
void init_fallback_values(const global_settings_base &def);
void set_new_options(const po::variables_map &options);
Expand All @@ -191,6 +209,8 @@ class global_settings_via_options : public global_settings_base {
void set_ratelimiter_ratelimit(const po::variables_map &options);
void set_ratelimiter_maxdebt(const po::variables_map &options);
void set_ratelimiter_upload(const po::variables_map &options);
void set_bbox_max_size_1d(const po::variables_map &options);
void set_bbox_max_size_2d(const po::variables_map &options);
bool validate_timeout(const std::string &timeout) const;

uint32_t m_payload_max_size;
Expand All @@ -208,6 +228,8 @@ class global_settings_via_options : public global_settings_base {
uint32_t m_ratelimiter_maxdebt;
uint32_t m_moderator_ratelimiter_maxdebt;
bool m_ratelimiter_upload;
double m_bbox_max_size_1d;
double m_bbox_max_size_2d;
};

class global_settings final {
Expand Down Expand Up @@ -256,6 +278,12 @@ class global_settings final {
// Use ratelimiter for changeset uploads
static bool get_ratelimiter_upload() { return settings->get_ratelimiter_upload(); }

// Maximum size of a bounding box in either dimension (lat or lon)
static double get_bbox_max_size_1d() { return settings->get_bbox_max_size_1d(); }

// Maximum size of a bounding box in both dimensions (lat * lon)
static double get_bbox_max_size_2d() { return settings->get_bbox_max_size_2d(); }

private:
static std::unique_ptr<global_settings_base> settings; // gets initialized with global_settings_default instance
};
Expand Down
7 changes: 7 additions & 0 deletions src/backend/apidb/changeset_upload/changeset_updater.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ void ApiDB_Changeset_Updater::update_changeset(const uint32_t num_new_changes,
)");

if (valid_bbox) {
double dx = 1.0 * (cs_bbox.maxlon - cs_bbox.minlon) / global_settings::get_scale();
double dy = 1.0 * (cs_bbox.maxlat - cs_bbox.minlat) / global_settings::get_scale();
if (dy > global_settings::get_bbox_max_size_1d() ||
dx > global_settings::get_bbox_max_size_1d() ||
(dy * dx) > global_settings::get_bbox_max_size_2d()) {
throw http::bad_request("The changeset area is greater than the maximum allowed by this server.");
}
auto r = m.exec_prepared("changeset_update_w_bbox", cs_num_changes, cs_bbox.minlat, cs_bbox.minlon,
cs_bbox.maxlat, cs_bbox.maxlon,
global_settings::get_changeset_timeout_open_max(),
Expand Down
2 changes: 2 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ void get_options(int argc, char **argv, po::variables_map &options) {
("max-relation-members", po::value<int>(), "max number of relation members per relation")
("max-element-tags", po::value<int>(), "max number of tags per OSM element")
("ratelimit-upload", po::value<bool>(), "enable rate limiting for changeset upload")
("max-bbox-1d", po::value<double>(), "max size in degrees of a changeset in either dimension")
("max-bbox-2d", po::value<double>(), "max size in square degrees of a changeset in both dimensions")
;
// clang-format on

Expand Down
21 changes: 21 additions & 0 deletions src/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ void global_settings_via_options::init_fallback_values(const global_settings_bas
m_ratelimiter_maxdebt = def.get_ratelimiter_maxdebt(false);
m_moderator_ratelimiter_maxdebt = def.get_ratelimiter_maxdebt(true);
m_ratelimiter_upload = def.get_ratelimiter_upload();
m_bbox_max_size_1d = def.get_bbox_max_size_1d();
m_bbox_max_size_2d = def.get_bbox_max_size_2d();
}

void global_settings_via_options::set_new_options(const po::variables_map &options) {
Expand All @@ -50,6 +52,8 @@ void global_settings_via_options::set_new_options(const po::variables_map &optio
set_ratelimiter_ratelimit(options);
set_ratelimiter_maxdebt(options);
set_ratelimiter_upload(options);
set_bbox_max_size_1d(options);
set_bbox_max_size_2d(options);
}

void global_settings_via_options::set_payload_max_size(const po::variables_map &options) {
Expand Down Expand Up @@ -185,6 +189,23 @@ void global_settings_via_options::set_ratelimiter_upload(const po::variables_map
}
}

void global_settings_via_options::set_bbox_max_size_1d(const po::variables_map &options) {
if (options.count("max-bbox-1d")) {
m_bbox_max_size_1d = options["max-bbox-1d"].as<double>();
if (m_bbox_max_size_1d < 0 || m_bbox_max_size_1d > 360.0) {
throw std::invalid_argument("max-bbox-1d (in degrees) must be between 0 and 360");
}
}
}

void global_settings_via_options::set_bbox_max_size_2d(const po::variables_map &options) {
if (options.count("max-bbox-2d")) {
m_bbox_max_size_1d = options["max-bbox-2d"].as<double>();
if (m_bbox_max_size_1d < 0 || m_bbox_max_size_1d > 360.0*180.0) {
throw std::invalid_argument("max-bbox-2d (in degrees) must be between 0 and 360*180");
}
}
}

bool global_settings_via_options::validate_timeout(const std::string &timeout) const {
std::smatch sm;
Expand Down
39 changes: 33 additions & 6 deletions test/test_apidb_backend_changeset_uploads.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ class global_settings_enable_upload_rate_limiter_test_class : public global_sett
bool get_ratelimiter_upload() const override { return true; }
};

class global_settings_bbox_size_test_class : public global_settings_default {

public:
// limit bbox upload size
double get_bbox_max_size_1d() const override { return 1.0; }
double get_bbox_max_size_2d() const override { return 1.0; }
};


std::unique_ptr<xmlDoc, void (*)(xmlDoc *)> getDocument(const std::string &document)
{
Expand Down Expand Up @@ -1945,6 +1953,9 @@ TEST_CASE_METHOD( DatabaseTestsFixture, "test_osmchange_message", "[changeset][u

TEST_CASE_METHOD( DatabaseTestsFixture, "test_osmchange_end_to_end", "[changeset][upload][db]" ) {

auto test_settings = std::unique_ptr<global_settings_bbox_size_test_class>(new global_settings_bbox_size_test_class());
global_settings::set_configuration(std::move(test_settings));

const std::string bearertoken = "Bearer 4f41f2328befed5a33bcabdf14483081c8df996cbafc41e313417776e8fafae8";
const std::string generator = "Test";

Expand Down Expand Up @@ -2150,13 +2161,13 @@ TEST_CASE_METHOD( DatabaseTestsFixture, "test_osmchange_end_to_end", "[changeset
req.set_payload(R"(<?xml version="1.0" encoding="UTF-8"?>
<osmChange version="0.6" generator="iD">
<create>
<node id="-5" lon="11" lat="46" version="0" changeset="1">
<node id="-5" lon="11.11" lat="46.46" version="0" changeset="1">
<tag k="highway" v="bus_stop" />
</node>
<node id="-6" lon="13" lat="47" version="0" changeset="1">
<node id="-6" lon="11.13" lat="46.47" version="0" changeset="1">
<tag k="highway" v="bus_stop" />
</node>
<node id="-7" lon="-54" lat="12" version="0" changeset="1"/>
<node id="-7" lon="11.54" lat="46.12" version="0" changeset="1"/>
<way id="-10" version="0" changeset="1">
<nd ref="-5"/>
<nd ref="-6"/>
Expand Down Expand Up @@ -2232,11 +2243,11 @@ TEST_CASE_METHOD( DatabaseTestsFixture, "test_osmchange_end_to_end", "[changeset
req.set_payload(R"(<?xml version="1.0" encoding="UTF-8"?>
<osmChange version="0.6" generator="iD">
<create>
<node id="-15" lon="4" lat="2" version="0" changeset="1"/>
<node id="-16" lon="3" lat="7" version="0" changeset="1"/>
<node id="-15" lon="11.4" lat="46.2" version="0" changeset="1"/>
<node id="-16" lon="11.3" lat="46.7" version="0" changeset="1"/>
</create>
<modify>
<node id="12000000000" lon="-11" lat="-46" version="1" changeset="1">
<node id="12000000000" lon="11" lat="46" version="1" changeset="1">
<tag k="highway" v="bus_stop" />
<tag k="name" v="Repubblica" />
</node>
Expand Down Expand Up @@ -2398,6 +2409,22 @@ TEST_CASE_METHOD( DatabaseTestsFixture, "test_osmchange_end_to_end", "[changeset
REQUIRE(req.response_status() == 200);
}

SECTION("Try to upload data beyond the options-set maximum bbox size") {

req.set_payload(R"(<?xml version="1.0" encoding="UTF-8"?>
<osmChange version="0.6" generator="iD">
<create>
<node id="-6" lon="-5" lat="-40" version="0" changeset="1"/>
</create>
</osmChange>)" );

// execute the request
process_request(req, limiter, generator, route, *sel_factory, upd_factory.get());

REQUIRE(req.response_status() == 400);
REQUIRE_THAT(req.body().str(), Equals("The changeset area is greater than the maximum allowed by this server."));
}

}

TEST_CASE_METHOD( DatabaseTestsFixture, "test_osmchange_rate_limiter", "[changeset][upload][db]" ) {
Expand Down
4 changes: 4 additions & 0 deletions test/test_parse_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ TEST_CASE("Set all supported options" "[options]") {
vm.emplace("maxdebt", po::variable_value((long) 500, false));
vm.emplace("moderator-maxdebt", po::variable_value((long) 1000, false));
vm.emplace("ratelimit-upload", po::variable_value(true, false));
vm.emplace("max-bbox-1d", po::variable_value((double) 3.5, false));
vm.emplace("max-bbox-2d", po::variable_value((double) 16.0, false));
REQUIRE_NOTHROW(check_options(vm));

REQUIRE( global_settings::get_payload_max_size() == 40000 );
Expand All @@ -71,4 +73,6 @@ TEST_CASE("Set all supported options" "[options]") {
REQUIRE( global_settings::get_ratelimiter_ratelimit(true) == 10000000 );
REQUIRE( global_settings::get_ratelimiter_maxdebt(true) == 1000l * 1024 * 1024 );
REQUIRE( global_settings::get_ratelimiter_upload() == true );
REQUIRE( global_settings::get_bbox_max_size_1d() == 3.5 );
REQUIRE( global_settings::get_bbox_max_size_2d() == 16.0 );
}