diff --git a/app/controllers/api/changesets_controller.rb b/app/controllers/api/changesets_controller.rb index 7bb7a5a4de1..9bdf0f2bd3b 100644 --- a/app/controllers/api/changesets_controller.rb +++ b/app/controllers/api/changesets_controller.rb @@ -92,6 +92,10 @@ def upload diff_reader = DiffReader.new(request.raw_post, changeset) Changeset.transaction do result = diff_reader.commit + # the number of changes in this changeset has already been + # updated and is visible in this transaction so we don't need + # to allow for any more when checking the limit + check_rate_limit(0) render :xml => result.to_s end end diff --git a/app/controllers/api/nodes_controller.rb b/app/controllers/api/nodes_controller.rb index 6934a13c04b..fb808828c80 100644 --- a/app/controllers/api/nodes_controller.rb +++ b/app/controllers/api/nodes_controller.rb @@ -14,6 +14,7 @@ class NodesController < ApiController around_action :api_call_handle_error, :api_call_timeout before_action :set_request_formats, :except => [:create, :update, :delete] + before_action :check_rate_limit, :only => [:create, :update, :delete] # Dump the details on many nodes whose ids are given in the "nodes" parameter. def index diff --git a/app/controllers/api/relations_controller.rb b/app/controllers/api/relations_controller.rb index 19aed6a85db..e833ae8307b 100644 --- a/app/controllers/api/relations_controller.rb +++ b/app/controllers/api/relations_controller.rb @@ -12,6 +12,7 @@ class RelationsController < ApiController around_action :api_call_handle_error, :api_call_timeout before_action :set_request_formats, :except => [:create, :update, :delete] + before_action :check_rate_limit, :only => [:create, :update, :delete] def index raise OSM::APIBadUserInput, "The parameter relations is required, and must be of the form relations=id[,id[,id...]]" unless params["relations"] diff --git a/app/controllers/api/ways_controller.rb b/app/controllers/api/ways_controller.rb index c4fce48d07d..5e72cfe209d 100644 --- a/app/controllers/api/ways_controller.rb +++ b/app/controllers/api/ways_controller.rb @@ -12,6 +12,7 @@ class WaysController < ApiController around_action :api_call_handle_error, :api_call_timeout before_action :set_request_formats, :except => [:create, :update, :delete] + before_action :check_rate_limit, :only => [:create, :update, :delete] def index raise OSM::APIBadUserInput, "The parameter ways is required, and must be of the form ways=id[,id[,id...]]" unless params["ways"] diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb index 89388c0bbf0..fd84af0cd34 100644 --- a/app/controllers/api_controller.rb +++ b/app/controllers/api_controller.rb @@ -192,4 +192,11 @@ def api_call_timeout(&block) ActiveRecord::Base.connection.raw_connection.cancel raise OSM::APITimeoutError end + + ## + # check the api change rate limit + def check_rate_limit(new_changes = 1) + recent_changes = current_user.changesets.where("created_at > ?", Time.now.utc - 1.hour).sum(:num_changes) + raise OSM::APIRateLimitExceeded if recent_changes + new_changes > current_user.max_changes_per_hour + end end diff --git a/test/controllers/api/changesets_controller_test.rb b/test/controllers/api/changesets_controller_test.rb index 802e006e119..8e4ba6ed412 100644 --- a/test/controllers/api/changesets_controller_test.rb +++ b/test/controllers/api/changesets_controller_test.rb @@ -2183,7 +2183,11 @@ def test_changeset_update_invalid # check that a changeset can contain a certain max number of changes. ## FIXME should be changed to an integration test due to the with_controller def test_changeset_limits - auth_header = basic_authorization_header create(:user).email, "test" + user = create(:user) + auth_header = basic_authorization_header user.email, "test" + + # create an old changeset to ensure we have the maximum rate limit + create(:changeset, :user => user, :created_at => Time.now.utc - 28.days) # open a new changeset xml = ""