From d83faa1a8902c91a5dbd0bf3d9740e3e19c1d623 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 24 Aug 2022 19:00:37 +0200 Subject: [PATCH] Add ability to block sign-ups from IP (#19037) --- .../auth/registrations_controller.rb | 6 +- app/models/ip_block.rb | 1 + app/services/app_sign_up_service.rb | 66 +++++++++++++++---- config/locales/simple_form.en.yml | 2 + spec/services/app_sign_up_service_spec.rb | 2 +- 5 files changed, 64 insertions(+), 13 deletions(-) diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index 1c3adbd786..7e86e01baa 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -82,7 +82,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController end def check_enabled_registrations - redirect_to root_path if single_user_mode? || omniauth_only? || !allowed_registrations? + redirect_to root_path if single_user_mode? || omniauth_only? || !allowed_registrations? || ip_blocked? end def allowed_registrations? @@ -93,6 +93,10 @@ class Auth::RegistrationsController < Devise::RegistrationsController ENV['OMNIAUTH_ONLY'] == 'true' end + def ip_blocked? + IpBlock.where(severity: :sign_up_block).where('ip >>= ?', request.remote_ip.to_s).exists? + end + def invite_code if params[:user] params[:user][:invite_code] diff --git a/app/models/ip_block.rb b/app/models/ip_block.rb index aedd3ca0d4..e1ab59806e 100644 --- a/app/models/ip_block.rb +++ b/app/models/ip_block.rb @@ -19,6 +19,7 @@ class IpBlock < ApplicationRecord enum severity: { sign_up_requires_approval: 5000, + sign_up_block: 5500, no_access: 9999, } diff --git a/app/services/app_sign_up_service.rb b/app/services/app_sign_up_service.rb index e006941577..3833327bbc 100644 --- a/app/services/app_sign_up_service.rb +++ b/app/services/app_sign_up_service.rb @@ -2,23 +2,67 @@ class AppSignUpService < BaseService def call(app, remote_ip, params) - return unless allowed_registrations? + @app = app + @remote_ip = remote_ip + @params = params - user_params = params.slice(:email, :password, :agreement, :locale) - account_params = params.slice(:username) - invite_request_params = { text: params[:reason] } - user = User.create!(user_params.merge(created_by_application: app, sign_up_ip: remote_ip, password_confirmation: user_params[:password], account_attributes: account_params, invite_request_attributes: invite_request_params)) + raise Mastodon::NotPermittedError unless allowed_registrations? - Doorkeeper::AccessToken.create!(application: app, - resource_owner_id: user.id, - scopes: app.scopes, - expires_in: Doorkeeper.configuration.access_token_expires_in, - use_refresh_token: Doorkeeper.configuration.refresh_token_enabled?) + ApplicationRecord.transaction do + create_user! + create_access_token! + end + + @access_token end private + def create_user! + @user = User.create!( + user_params.merge(created_by_application: @app, sign_up_ip: @remote_ip, password_confirmation: user_params[:password], account_attributes: account_params, invite_request_attributes: invite_request_params) + ) + end + + def create_access_token! + @access_token = Doorkeeper::AccessToken.create!( + application: @app, + resource_owner_id: @user.id, + scopes: @app.scopes, + expires_in: Doorkeeper.configuration.access_token_expires_in, + use_refresh_token: Doorkeeper.configuration.refresh_token_enabled? + ) + end + + def user_params + @params.slice(:email, :password, :agreement, :locale) + end + + def account_params + @params.slice(:username) + end + + def invite_request_params + { text: @params[:reason] } + end + def allowed_registrations? - Setting.registrations_mode != 'none' && !Rails.configuration.x.single_user_mode + registrations_open? && !single_user_mode? && !omniauth_only? && !ip_blocked? + end + + def registrations_open? + Setting.registrations_mode != 'none' + end + + def single_user_mode? + Rails.configuration.x.single_user_mode + end + + def omniauth_only? + ENV['OMNIAUTH_ONLY'] == 'true' + end + + def ip_blocked? + IpBlock.where(severity: :sign_up_block).where('ip >>= ?', @remote_ip.to_s).exists? end end diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index c17a62cbea..28f78d5000 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -85,6 +85,7 @@ en: ip: Enter an IPv4 or IPv6 address. You can block entire ranges using the CIDR syntax. Be careful not to lock yourself out! severities: no_access: Block access to all resources + sign_up_block: New sign-ups will not be possible sign_up_requires_approval: New sign-ups will require your approval severity: Choose what will happen with requests from this IP rule: @@ -219,6 +220,7 @@ en: ip: IP severities: no_access: Block access + sign_up_block: Block sign-ups sign_up_requires_approval: Limit sign-ups severity: Rule notification_emails: diff --git a/spec/services/app_sign_up_service_spec.rb b/spec/services/app_sign_up_service_spec.rb index e0c83b7041..8ec4d4a7a6 100644 --- a/spec/services/app_sign_up_service_spec.rb +++ b/spec/services/app_sign_up_service_spec.rb @@ -11,7 +11,7 @@ RSpec.describe AppSignUpService, type: :service do it 'returns nil when registrations are closed' do tmp = Setting.registrations_mode Setting.registrations_mode = 'none' - expect(subject.call(app, remote_ip, good_params)).to be_nil + expect { subject.call(app, remote_ip, good_params) }.to raise_error Mastodon::NotPermittedError Setting.registrations_mode = tmp end