From ba503e02d205e132881f7cd860d160c9562fb1b1 Mon Sep 17 00:00:00 2001 From: Tom Hughes Date: Sun, 29 Oct 2023 16:11:23 +0000 Subject: [PATCH] Enforce rate limit for API calls which make changes --- app/controllers/api/changesets_controller.rb | 4 ++++ app/controllers/api/nodes_controller.rb | 1 + app/controllers/api/relations_controller.rb | 1 + app/controllers/api/ways_controller.rb | 1 + app/controllers/api_controller.rb | 10 ++++++++++ test/controllers/api/changesets_controller_test.rb | 6 +++++- 6 files changed, 22 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/changesets_controller.rb b/app/controllers/api/changesets_controller.rb index 7bb7a5a4d..9bdf0f2bd 100644 --- a/app/controllers/api/changesets_controller.rb +++ b/app/controllers/api/changesets_controller.rb @@ -92,6 +92,10 @@ module Api 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 6934a13c0..fb808828c 100644 --- a/app/controllers/api/nodes_controller.rb +++ b/app/controllers/api/nodes_controller.rb @@ -14,6 +14,7 @@ module Api 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 19aed6a85..e833ae830 100644 --- a/app/controllers/api/relations_controller.rb +++ b/app/controllers/api/relations_controller.rb @@ -12,6 +12,7 @@ module Api 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 c4fce48d0..5e72cfe20 100644 --- a/app/controllers/api/ways_controller.rb +++ b/app/controllers/api/ways_controller.rb @@ -12,6 +12,7 @@ module Api 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 89388c0bb..7e1b06a8d 100644 --- a/app/controllers/api_controller.rb +++ b/app/controllers/api_controller.rb @@ -192,4 +192,14 @@ class ApiController < ApplicationController ActiveRecord::Base.connection.raw_connection.cancel raise OSM::APITimeoutError end + + ## + # check the api change rate limit + def check_rate_limit(new_changes = 1) + max_changes = ActiveRecord::Base.connection.select_value( + "SELECT api_rate_limit($1)", "api_rate_limit", [current_user.id] + ) + + raise OSM::APIRateLimitExceeded if new_changes > max_changes + end end diff --git a/test/controllers/api/changesets_controller_test.rb b/test/controllers/api/changesets_controller_test.rb index 802e006e1..8e4ba6ed4 100644 --- a/test/controllers/api/changesets_controller_test.rb +++ b/test/controllers/api/changesets_controller_test.rb @@ -2183,7 +2183,11 @@ module Api # 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 = "" -- 2.39.5