From dd457c69a1b54384203d3b3e57a08ae14e2bba30 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Sat, 26 Jul 2025 17:03:07 -0400 Subject: [PATCH] Extract `User::Registration` concern --- app/models/concerns/user/registration.rb | 19 +++++++++++++++++++ app/models/user.rb | 4 ++-- .../registration_form_time_validator.rb | 9 --------- spec/models/user_spec.rb | 9 +++++++++ 4 files changed, 30 insertions(+), 11 deletions(-) create mode 100644 app/models/concerns/user/registration.rb delete mode 100644 app/validators/registration_form_time_validator.rb diff --git a/app/models/concerns/user/registration.rb b/app/models/concerns/user/registration.rb new file mode 100644 index 00000000000..ad353ecd338 --- /dev/null +++ b/app/models/concerns/user/registration.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module User::Registration + extend ActiveSupport::Concern + + REGISTRATION_ATTEMPT_WAIT_TIME = 3.seconds.freeze + + included do + attribute :registration_form_time, :datetime + + validate :validate_registration_wait, on: :create, if: :registration_form_time? + end + + private + + def validate_registration_wait + errors.add(:base, I18n.t('auth.too_fast')) if registration_form_time > REGISTRATION_ATTEMPT_WAIT_TIME.ago + end +end diff --git a/app/models/user.rb b/app/models/user.rb index aca72daff04..4a9bf575ba3 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -64,6 +64,7 @@ class User < ApplicationRecord include User::LdapAuthenticable include User::Omniauthable include User::PamAuthenticable + include User::Registration devise :two_factor_authenticatable, otp_secret_length: 32 @@ -99,9 +100,8 @@ class User < ApplicationRecord validates :agreement, acceptance: { allow_nil: false, accept: [true, 'true', '1'] }, on: :create # Honeypot/anti-spam fields - attr_accessor :registration_form_time, :website, :confirm_password + attr_accessor :website, :confirm_password - validates_with RegistrationFormTimeValidator, on: :create validates :website, absence: true, on: :create validates :confirm_password, absence: true, on: :create validates :date_of_birth, presence: true, date_of_birth: true, on: :create, if: -> { Setting.min_age.present? && !bypass_registration_checks? } diff --git a/app/validators/registration_form_time_validator.rb b/app/validators/registration_form_time_validator.rb deleted file mode 100644 index ba7c7e6c646..00000000000 --- a/app/validators/registration_form_time_validator.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class RegistrationFormTimeValidator < ActiveModel::Validator - REGISTRATION_FORM_MIN_TIME = 3.seconds.freeze - - def validate(user) - user.errors.add(:base, I18n.t('auth.too_fast')) if user.registration_form_time.present? && user.registration_form_time > REGISTRATION_FORM_MIN_TIME.ago - end -end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index a9ab15a956e..d14c66c66d6 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -39,6 +39,15 @@ RSpec.describe User do end it { is_expected.to allow_value('admin@localhost').for(:email) } + + context 'when registration form time is present' do + subject { Fabricate.build :user } + + before { stub_const 'User::REGISTRATION_ATTEMPT_WAIT_TIME', 3.seconds } + + it { is_expected.to allow_value(10.seconds.ago).for(:registration_form_time) } + it { is_expected.to_not allow_value(1.second.ago).for(:registration_form_time).against(:base) } + end end describe 'Normalizations' do