]> git.openstreetmap.org Git - rails.git/commitdiff
Merge remote-tracking branch 'upstream/pull/4212'
authorTom Hughes <tom@compton.nu>
Thu, 31 Aug 2023 10:12:03 +0000 (11:12 +0100)
committerTom Hughes <tom@compton.nu>
Thu, 31 Aug 2023 10:12:03 +0000 (11:12 +0100)
18 files changed:
app/controllers/api/changeset_comments_controller.rb
app/controllers/api/changesets_controller.rb
app/controllers/api/notes_controller.rb
app/controllers/changesets_controller.rb
app/controllers/diary_entries_controller.rb
app/controllers/issues_controller.rb
app/controllers/messages_controller.rb
app/controllers/user_roles_controller.rb
app/models/changeset.rb
app/models/trace.rb
app/models/user.rb
config/initializers/omniauth.rb
config/settings.yml
config/settings/test.yml
test/controllers/api/changeset_comments_controller_test.rb
test/controllers/diary_entries_controller_test.rb
test/factories/issues.rb
test/models/trace_test.rb

index a9e80630e22226447bc9797f363ac2b854801987..bb77e1106609b2cea7b0190a61fcb160fd4032b0 100644 (file)
@@ -17,7 +17,7 @@ module Api
       # Check the arguments are sane
       raise OSM::APIBadUserInput, "No id was given" unless params[:id]
       raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
-      raise OSM::APIRateLimitExceeded if current_user.changeset_comments.where("created_at >= ?", Time.now.utc - 1.hour).count >= current_user.max_changeset_comments_per_hour
+      raise OSM::APIRateLimitExceeded if rate_limit_exceeded?
 
       # Extract the arguments
       id = params[:id].to_i
@@ -99,5 +99,15 @@ module Api
         format.json
       end
     end
+
+    private
+
+    ##
+    # Check if the current user has exceed the rate limit for comments
+    def rate_limit_exceeded?
+      recent_comments = current_user.changeset_comments.where("created_at >= ?", Time.now.utc - 1.hour).count
+
+      recent_comments >= current_user.max_changeset_comments_per_hour
+    end
   end
 end
index a3dff4f1cd7e4fc935986a009c71e9b31ac7c411..7bb7a5a4de14bddb49f988f7f9285d96de074b1e 100644 (file)
@@ -325,7 +325,7 @@ module Api
           raise OSM::APINotFoundError if current_user.nil? || current_user != u
         end
 
-        changesets.where(:user_id => u.id)
+        changesets.where(:user => u)
       end
     end
 
index 83024288db43d9963249c301faca3180f457fce8..e6f391ede96e3767e7c5c08f9c210e3a744a9f8c 100644 (file)
@@ -251,7 +251,7 @@ module Api
       end
 
       # Find the comments we want to return
-      @comments = NoteComment.where(:note_id => notes).order("created_at DESC").limit(result_limit).preload(:note)
+      @comments = NoteComment.where(:note => notes).order("created_at DESC").limit(result_limit).preload(:note)
 
       # Render the result
       respond_to do |format|
index 22d3356b79fee7425865fcff5de6098b2cc0b153..fef4d85eb51273596dfe81ccfc0cad88ba81c19f 100644 (file)
@@ -48,16 +48,16 @@ class ChangesetsController < ApplicationController
 
       if @params[:display_name]
         changesets = if user.data_public? || user == current_user
-                       changesets.where(:user_id => user.id)
+                       changesets.where(:user => user)
                      else
                        changesets.where("false")
                      end
       elsif @params[:bbox]
         changesets = conditions_bbox(changesets, BoundingBox.from_bbox_params(params))
       elsif @params[:friends] && current_user
-        changesets = changesets.where(:user_id => current_user.friends.identifiable)
+        changesets = changesets.where(:user => current_user.friends.identifiable)
       elsif @params[:nearby] && current_user
-        changesets = changesets.where(:user_id => current_user.nearby)
+        changesets = changesets.where(:user => current_user.nearby)
       end
 
       changesets = changesets.where("changesets.id <= ?", @params[:max_id]) if @params[:max_id]
index ea6d1d27601db89980fa6366308b90a5ad996f45..dcb625d836932a9746e2aac99bc5d2952577ae92 100644 (file)
@@ -27,7 +27,7 @@ class DiaryEntriesController < ApplicationController
     elsif params[:friends]
       if current_user
         @title = t ".title_friends"
-        entries = DiaryEntry.where(:user_id => current_user.friends)
+        entries = DiaryEntry.where(:user => current_user.friends)
       else
         require_user
         return
@@ -35,7 +35,7 @@ class DiaryEntriesController < ApplicationController
     elsif params[:nearby]
       if current_user
         @title = t ".title_nearby"
-        entries = DiaryEntry.where(:user_id => current_user.nearby)
+        entries = DiaryEntry.where(:user => current_user.nearby)
       else
         require_user
         return
index 88c7a46acd5f0bb3859f9e1dce567815d4328fd9..c24054f77ff3adf4ab44073cea134c9748403b51 100644 (file)
@@ -24,7 +24,7 @@ class IssuesController < ApplicationController
     if params[:search_by_user].present?
       @find_user = User.find_by(:display_name => params[:search_by_user])
       if @find_user
-        @issues = @issues.where(:reported_user_id => @find_user.id)
+        @issues = @issues.where(:reported_user => @find_user)
       else
         @issues = @issues.none
         flash.now[:warning] = t(".user_not_found")
index e0b5b05d31900dca9032e070df3ea127fa45cd18..adb53b43ba3842d8cd0b3faf5aaa898abde4b213 100644 (file)
@@ -57,7 +57,7 @@ class MessagesController < ApplicationController
 
   # Destroy the message.
   def destroy
-    @message = Message.where("to_user_id = ? OR from_user_id = ?", current_user.id, current_user.id).find(params[:id])
+    @message = Message.where(:recipient => current_user).or(Message.where(:sender => current_user.id)).find(params[:id])
     @message.from_user_visible = false if @message.sender == current_user
     @message.to_user_visible = false if @message.recipient == current_user
     if @message.save && !request.xhr?
@@ -109,7 +109,7 @@ class MessagesController < ApplicationController
 
   # Set the message as being read or unread.
   def mark
-    @message = Message.where("to_user_id = ? OR from_user_id = ?", current_user.id, current_user.id).find(params[:message_id])
+    @message = Message.where(:recipient => current_user).or(Message.where(:sender => current_user)).find(params[:message_id])
     if params[:mark] == "unread"
       message_read = false
       notice = t ".as_unread"
index b54cd0bd7a3eee82ccfecb01ce14882ba1d7c3cc..469b2c40b626845191e7a5265e873c340da7c4f4 100644 (file)
@@ -22,7 +22,7 @@ class UserRolesController < ApplicationController
     if current_user == @user && @role == "administrator"
       flash[:error] = t("user_role.filter.not_revoke_admin_current_user")
     else
-      UserRole.where(:user_id => @user.id, :role => @role).delete_all
+      UserRole.where(:user => @user, :role => @role).delete_all
     end
     redirect_to user_path(@user)
   end
index f23a4e356e883bd51f0fb0d01cb52897a75fd0ab..ce09438245844be0ff4ba241818973cdf26b5bab 100644 (file)
@@ -170,7 +170,7 @@ class Changeset < ApplicationRecord
       save!
 
       tags = self.tags
-      ChangesetTag.where(:changeset_id => id).delete_all
+      ChangesetTag.where(:changeset => id).delete_all
 
       tags.each do |k, v|
         tag = ChangesetTag.new
index 35fee0bf4f800d7fd22f906d1b9cc3040ea5727f..2411fb9b7a3c56c2f2adc57787a6194f83058c51 100644 (file)
@@ -209,7 +209,7 @@ class Trace < ApplicationRecord
       first = true
 
       # If there are any existing points for this trace then delete them
-      Tracepoint.where(:gpx_id => id).delete_all
+      Tracepoint.where(:trace => id).delete_all
 
       gpx.points.each_slice(1_000) do |points|
         # Gather the trace points together for a bulk import
@@ -242,10 +242,10 @@ class Trace < ApplicationRecord
       end
 
       if gpx.actual_points.positive?
-        max_lat = Tracepoint.where(:gpx_id => id).maximum(:latitude)
-        min_lat = Tracepoint.where(:gpx_id => id).minimum(:latitude)
-        max_lon = Tracepoint.where(:gpx_id => id).maximum(:longitude)
-        min_lon = Tracepoint.where(:gpx_id => id).minimum(:longitude)
+        max_lat = Tracepoint.where(:trace => id).maximum(:latitude)
+        min_lat = Tracepoint.where(:trace => id).minimum(:latitude)
+        max_lon = Tracepoint.where(:trace => id).maximum(:longitude)
+        min_lon = Tracepoint.where(:trace => id).minimum(:longitude)
 
         max_lat = max_lat.to_f / 10000000
         min_lat = min_lat.to_f / 10000000
index fe2a98d61e31f6af42e6a085bdcf19e93a21d546..3eb03a2fe4ae93bfb2010b359265f169b8315f79 100644 (file)
@@ -397,14 +397,14 @@ class User < ApplicationRecord
 
   def max_changeset_comments_per_hour
     if moderator?
-      36000
+      Settings.moderator_changeset_comments_per_hour
     else
       previous_comments = changeset_comments.limit(200).count
       active_reports = issues.with_status(:open).sum(:reports_count)
       max_comments = previous_comments / 200.0 * Settings.max_changeset_comments_per_hour
-      max_comments = max_comments.floor.clamp(Settings.min_changeset_comments_per_hour, Settings.max_changeset_comments_per_hour)
+      max_comments = max_comments.floor.clamp(Settings.initial_changeset_comments_per_hour, Settings.max_changeset_comments_per_hour)
       max_comments /= 2**active_reports
-      max_comments.floor.clamp(1, Settings.max_changeset_comments_per_hour)
+      max_comments.floor.clamp(Settings.min_changeset_comments_per_hour, Settings.max_changeset_comments_per_hour)
     end
   end
 
index 3964a6729bca132b1c86048be04d6fb082ff34f1..bce82b3c944230f520a9fb4dae628e6ad877a3b3 100644 (file)
@@ -23,7 +23,7 @@ end
 
 openid_options = { :name => "openid", :store => openid_store }
 google_options = { :name => "google", :scope => "email", :access_type => "online" }
-facebook_options = { :name => "facebook", :scope => "email", :client_options => { :site => "https://graph.facebook.com/v4.0", :authorize_url => "https://www.facebook.com/v4.0/dialog/oauth" } }
+facebook_options = { :name => "facebook", :scope => "email", :client_options => { :site => "https://graph.facebook.com/v17.0", :authorize_url => "https://www.facebook.com/v17.0/dialog/oauth" } }
 microsoft_options = { :name => "microsoft", :scope => "openid User.Read" }
 github_options = { :name => "github", :scope => "user:email" }
 wikipedia_options = { :name => "wikipedia", :client_options => { :site => "https://meta.wikimedia.org" } }
index 5330ed678c47333acd0e5ca388709da14100227b..3ea298efcb4378f4073397d02d301d785c772ff2 100644 (file)
@@ -56,8 +56,10 @@ max_messages_per_hour: 60
 # Rate limit for friending
 max_friends_per_hour: 60
 # Rate limit for changeset comments
-min_changeset_comments_per_hour: 6
+min_changeset_comments_per_hour: 1
+initial_changeset_comments_per_hour: 6
 max_changeset_comments_per_hour: 60
+moderator_changeset_comments_per_hour: 36000
 # Domain for handling message replies
 #messages_domain: "messages.openstreetmap.org"
 # MaxMind GeoIPv2 database
index 64039e362c4f8c879c0f419e3ddd4bd04a64717b..5f00259256d6ef81873ad323810657d9ce289c06 100644 (file)
@@ -19,3 +19,6 @@ avatar_storage: "test"
 trace_file_storage: "test"
 trace_image_storage: "test"
 trace_icon_storage: "test"
+# Lower some rate limits for testing
+max_changeset_comments_per_hour: 30
+moderator_changeset_comments_per_hour: 60
index 624b8a35808645c1964c3b7fd40aa5fa5f46b262..e25926c78e41dfcf5bb691f268101256a83c3ce3 100644 (file)
@@ -133,15 +133,80 @@ module Api
     end
 
     ##
-    # create comment rate limit
-    def test_create_comment_rate_limit
+    # create comment rate limit for new users
+    def test_create_comment_new_user_rate_limit
       changeset = create(:changeset, :closed)
       user = create(:user)
 
       auth_header = basic_authorization_header user.email, "test"
 
-      assert_difference "ChangesetComment.count", Settings.min_changeset_comments_per_hour do
-        1.upto(Settings.min_changeset_comments_per_hour) do |count|
+      assert_difference "ChangesetComment.count", Settings.initial_changeset_comments_per_hour do
+        1.upto(Settings.initial_changeset_comments_per_hour) do |count|
+          post changeset_comment_path(:id => changeset, :text => "Comment #{count}"), :headers => auth_header
+          assert_response :success
+        end
+      end
+
+      assert_no_difference "ChangesetComment.count" do
+        post changeset_comment_path(:id => changeset, :text => "One comment too many"), :headers => auth_header
+        assert_response :too_many_requests
+      end
+    end
+
+    ##
+    # create comment rate limit for experienced users
+    def test_create_comment_experienced_user_rate_limit
+      changeset = create(:changeset, :closed)
+      user = create(:user)
+      create_list(:changeset_comment, 200, :author_id => user.id, :created_at => Time.now.utc - 1.day)
+
+      auth_header = basic_authorization_header user.email, "test"
+
+      assert_difference "ChangesetComment.count", Settings.max_changeset_comments_per_hour do
+        1.upto(Settings.max_changeset_comments_per_hour) do |count|
+          post changeset_comment_path(:id => changeset, :text => "Comment #{count}"), :headers => auth_header
+          assert_response :success
+        end
+      end
+
+      assert_no_difference "ChangesetComment.count" do
+        post changeset_comment_path(:id => changeset, :text => "One comment too many"), :headers => auth_header
+        assert_response :too_many_requests
+      end
+    end
+
+    ##
+    # create comment rate limit for reported users
+    def test_create_comment_reported_user_rate_limit
+      changeset = create(:changeset, :closed)
+      user = create(:user)
+      create(:issue_with_reports, :reportable => user, :reported_user => user)
+
+      auth_header = basic_authorization_header user.email, "test"
+
+      assert_difference "ChangesetComment.count", Settings.initial_changeset_comments_per_hour / 2 do
+        1.upto(Settings.initial_changeset_comments_per_hour / 2) do |count|
+          post changeset_comment_path(:id => changeset, :text => "Comment #{count}"), :headers => auth_header
+          assert_response :success
+        end
+      end
+
+      assert_no_difference "ChangesetComment.count" do
+        post changeset_comment_path(:id => changeset, :text => "One comment too many"), :headers => auth_header
+        assert_response :too_many_requests
+      end
+    end
+
+    ##
+    # create comment rate limit for moderator users
+    def test_create_comment_moderator_user_rate_limit
+      changeset = create(:changeset, :closed)
+      user = create(:moderator_user)
+
+      auth_header = basic_authorization_header user.email, "test"
+
+      assert_difference "ChangesetComment.count", Settings.moderator_changeset_comments_per_hour do
+        1.upto(Settings.moderator_changeset_comments_per_hour) do |count|
           post changeset_comment_path(:id => changeset, :text => "Comment #{count}"), :headers => auth_header
           assert_response :success
         end
index 6365d46e5de0aba5210c181131194d15dde78ca7..ea918cb6bbd765b577d6460901cebba1b9985e71 100644 (file)
@@ -164,7 +164,7 @@ class DiaryEntriesControllerTest < ActionDispatch::IntegrationTest
     assert_response :success
     assert_template :new
 
-    assert_nil UserPreference.where(:user_id => user.id, :k => "diary.default_language").first
+    assert_nil UserPreference.where(:user => user, :k => "diary.default_language").first
   end
 
   def test_create
@@ -189,7 +189,7 @@ class DiaryEntriesControllerTest < ActionDispatch::IntegrationTest
     # checks if user was subscribed
     assert_equal 1, entry.subscribers.length
 
-    assert_equal "en", UserPreference.where(:user_id => user.id, :k => "diary.default_language").first.v
+    assert_equal "en", UserPreference.where(:user => user, :k => "diary.default_language").first.v
   end
 
   def test_create_german
@@ -216,7 +216,7 @@ class DiaryEntriesControllerTest < ActionDispatch::IntegrationTest
     # checks if user was subscribed
     assert_equal 1, entry.subscribers.length
 
-    assert_equal "de", UserPreference.where(:user_id => user.id, :k => "diary.default_language").first.v
+    assert_equal "de", UserPreference.where(:user => user, :k => "diary.default_language").first.v
   end
 
   def test_new_spammy
index c575c3398e3fe16108329098316eb2430cb822d4..bb6b2dd5222d517fbe95ca35a812991dc95d2b38 100644 (file)
@@ -6,5 +6,16 @@ FactoryBot.define do
 
     # Default to assigning to an administrator
     assigned_role { "administrator" }
+
+    # Optionally create some reports for this issue
+    factory :issue_with_reports do
+      transient do
+        reports_count { 1 }
+      end
+
+      after(:create) do |issue, evaluator|
+        create_list(:report, evaluator.reports_count, :issue => issue)
+      end
+    end
   end
 end
index 762df66484d185c82bc52f305e739a989421a1cc..66107771ae5a299c7155e0097add0ed06e8800cb 100644 (file)
@@ -193,16 +193,16 @@ class TraceTest < ActiveSupport::TestCase
 
   def test_import_creates_tracepoints
     trace = create(:trace, :fixture => "a")
-    assert_equal 0, Tracepoint.where(:gpx_id => trace.id).count
+    assert_equal 0, Tracepoint.where(:trace => trace).count
 
     trace.import
 
     trace.reload
-    assert_equal 1, Tracepoint.where(:gpx_id => trace.id).count
+    assert_equal 1, Tracepoint.where(:trace => trace).count
 
     # Check that the tile has been set prior to the bulk import
     # i.e. that the callbacks have been run correctly
-    assert_equal 3221331576, Tracepoint.where(:gpx_id => trace.id).first.tile
+    assert_equal 3221331576, Tracepoint.where(:trace => trace).first.tile
   end
 
   def test_import_creates_icon