Add: #348 新規登録の上限人数 (#527)

* Add: #348 新規登録の上限人数

* Fix test

* Fix test

* Wip

* Fix test

* Add invite support

* Wip

* Fix test

* Fix test

* Fix test
This commit is contained in:
KMY(雪あすか) 2024-02-12 22:05:32 +09:00 committed by GitHub
parent d7cc6b788c
commit e317edecb8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 362 additions and 6 deletions

View file

@ -1,9 +1,18 @@
# frozen_string_literal: true
class Admin::Settings::RegistrationsController < Admin::SettingsController
include RegistrationLimitationHelper
before_action :set_limitation_counts, only: :show # rubocop:disable Rails/LexicallyScopedActionFilter
private
def after_update_redirect_path
admin_settings_registrations_path
end
def set_limitation_counts
@current_users_count = user_count_for_registration
@current_users_count_today = today_increase_user_count
end
end

View file

@ -2,6 +2,7 @@
class Auth::ConfirmationsController < Devise::ConfirmationsController
include Auth::CaptchaConcern
include RegistrationLimitationHelper
layout 'auth'
@ -16,6 +17,11 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
skip_before_action :require_functional!
def show
if reach_registrations_limit?
render :limitation_error
return
end
old_session_values = session.to_hash
reset_session
session.update old_session_values.except('session_id')

View file

@ -3,8 +3,10 @@
module RegistrationHelper
extend ActiveSupport::Concern
include RegistrationLimitationHelper
def allowed_registration?(remote_ip, invite)
!Rails.configuration.x.single_user_mode && !omniauth_only? && (registrations_open? || invite&.valid_for_use?) && !ip_blocked?(remote_ip)
!Rails.configuration.x.single_user_mode && !omniauth_only? && ((registrations_open? && !reach_registrations_limit?) || invite&.valid_for_use?) && !ip_blocked?(remote_ip)
end
def registrations_open?

View file

@ -0,0 +1,55 @@
# frozen_string_literal: true
module RegistrationLimitationHelper
def reach_registrations_limit?
return true unless registrations_in_time?
((Setting.registrations_limit.presence || 0).positive? && Setting.registrations_limit <= user_count_for_registration) ||
((Setting.registrations_limit_per_day.presence || 0).positive? && Setting.registrations_limit_per_day <= today_increase_user_count)
end
def user_count_for_registration
Rails.cache.fetch('registrations:user_count') { User.confirmed.enabled.joins(:account).merge(Account.without_suspended).count }
end
def today_increase_user_count
today_date = Time.now.utc.beginning_of_day.to_i
count = 0
if Rails.cache.fetch('registrations:today_date') { today_date } == today_date
count = Rails.cache.fetch('registrations:today_increase_user_count') { today_increase_user_count_value }
else
count = today_increase_user_count_value
Rails.cache.write('registrations:today_date', today_date)
Rails.cache.write('registrations:today_increase_user_count', count)
end
count
end
def today_increase_user_count_value
User.confirmed.enabled.where('users.created_at >= ?', Time.now.utc.beginning_of_day).joins(:account).merge(Account.without_suspended).count
end
def registrations_in_time?
start_hour = Setting.registrations_start_hour || 0
end_hour = Setting.registrations_end_hour || 24
secondary_start_hour = Setting.registrations_secondary_start_hour || 0
secondary_end_hour = Setting.registrations_secondary_end_hour || 0
return true if start_hour >= end_hour && secondary_start_hour >= secondary_end_hour
current_hour = Time.now.utc.hour
primary_permitted = false
primary_permitted = start_hour <= current_hour && current_hour < end_hour if start_hour < end_hour && end_hour.positive?
secondary_permitted = false
secondary_permitted = secondary_start_hour <= current_hour && current_hour < secondary_end_hour if secondary_start_hour < secondary_end_hour && secondary_end_hour.positive?
primary_permitted || secondary_permitted
end
def reset_registration_limit_caches!
Rails.cache.delete('registrations:user_count')
Rails.cache.delete('registrations:today_increase_user_count')
end
end

View file

@ -4,7 +4,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import { fetchServer } from 'mastodon/actions/server';
import { domain } from 'mastodon/initial_state';
import { domain, registrationsReachLimit } from 'mastodon/initial_state';
const mapStateToProps = state => ({
message: state.getIn(['server', 'server', 'registrations', 'message']),
@ -27,6 +27,16 @@ class ClosedRegistrationsModal extends ImmutablePureComponent {
dangerouslySetInnerHTML={{ __html: this.props.message }}
/>
);
} else if (registrationsReachLimit) {
closedRegistrationsMessage = (
<p className='prose'>
<FormattedMessage
id='closed_registrations_modal.description_when_reaching_limit'
defaultMessage='New registrations are currently temporarily restricted. Either the maximum number of registrations has been reached or it is outside the time frame available for registration. Please contact the administrator for more information or wait until the restriction is lifted.'
values={{ domain: <strong>{domain}</strong> }}
/>
</p>
);
} else {
closedRegistrationsMessage = (
<p className='prose'>

View file

@ -48,6 +48,7 @@
* @property {string=} owner
* @property {boolean} profile_directory
* @property {boolean} registrations_open
* @property {boolean} registrations_reach_limit
* @property {boolean} reduce_motion
* @property {string} repository
* @property {boolean} search_enabled
@ -134,6 +135,7 @@ export const owner = getMeta('owner');
export const profile_directory = getMeta('profile_directory');
export const reduceMotion = getMeta('reduce_motion');
export const registrationsOpen = getMeta('registrations_open');
export const registrationsReachLimit = getMeta('registrations_reach_limit');
export const repository = getMeta('repository');
export const searchEnabled = getMeta('search_enabled');
export const trendsEnabled = getMeta('trends_enabled');

View file

@ -113,6 +113,7 @@
"circles.edit": "Edit circle",
"closed_registrations.other_server_instructions": "Since Mastodon is decentralized, you can create an account on another server and still interact with this one.",
"closed_registrations_modal.description": "Creating an account on {domain} is currently not possible, but please keep in mind that you do not need an account specifically on {domain} to use Mastodon.",
"closed_registrations_modal.description_when_reaching_limit": "New registrations are currently temporarily restricted. Either the maximum number of registrations has been reached or it is outside the time frame available for registration. Please contact the administrator for more information or wait until the restriction is lifted.",
"closed_registrations_modal.find_another_server": "Find another server",
"closed_registrations_modal.preamble": "Mastodon is decentralized, so no matter where you create your account, you will be able to follow and interact with anyone on this server. You can even self-host it!",
"closed_registrations_modal.title": "Signing up on Mastodon",

View file

@ -170,6 +170,7 @@
"circles.subheading": "あなたのサークル",
"closed_registrations.other_server_instructions": "Mastodonは分散型なので他のサーバーにアカウントを作ってもこのサーバーとやり取りできます。",
"closed_registrations_modal.description": "現在{domain}でアカウント作成はできませんがMastodonは{domain}のアカウントでなくても利用できます。",
"closed_registrations_modal.description_when_reaching_limit": "新規登録は現在一時的に制限されています。登録の上限人数に達したか、または登録可能な時間帯の範囲外です。詳細を管理人に問い合わせるか、制限が解除されるまでお待ち下さい。",
"closed_registrations_modal.find_another_server": "別のサーバーを探す",
"closed_registrations_modal.preamble": "Mastodonは分散型なのでどのサーバーでアカウントを作成してもこのサーバーのユーザーを誰でもフォローして交流することができます。また自分でホスティングすることもできます",
"closed_registrations_modal.title": "Mastodonでアカウントを作成",

View file

@ -15,6 +15,12 @@ class Form::AdminSettings
registrations_mode
closed_registrations_message
registration_button_message
registrations_limit
registrations_limit_per_day
registrations_start_hour
registrations_end_hour
registrations_secondary_start_hour
registrations_secondary_end_hour
timeline_preview
bootstrap_timeline_accounts
theme
@ -61,6 +67,12 @@ class Form::AdminSettings
content_cache_retention_period
backups_retention_period
post_hash_tags_max
registrations_limit
registrations_limit_per_day
registrations_start_hour
registrations_end_hour
registrations_secondary_start_hour
registrations_secondary_end_hour
).freeze
BOOLEAN_KEYS = %i(

View file

@ -55,6 +55,7 @@ class User < ApplicationRecord
include LanguagesHelper
include Redisable
include RegistrationLimitationHelper
include User::HasSettings
include User::LdapAuthenticable
include User::Omniauthable
@ -192,6 +193,8 @@ class User < ApplicationRecord
end
def confirm
raise Mastodon::ValidationError, I18n.t('devise.registrations.sign_up_failed_because_reach_limit') if !invited? && reach_registrations_limit?
wrap_email_confirmation do
super
end
@ -482,6 +485,7 @@ class User < ApplicationRecord
ActivityTracker.record('activity:logins', id)
UserMailer.welcome(self).deliver_later
TriggerWebhookWorker.perform_async('account.approved', 'Account', account_id)
reset_registration_limit_caches!
end
def prepare_returning_user!

View file

@ -3,6 +3,7 @@
class InitialStateSerializer < ActiveModel::Serializer
include RoutingHelper
include DtlHelper
include RegistrationLimitationHelper
attributes :meta, :compose, :accounts,
:media_attachments, :settings,
@ -122,7 +123,8 @@ class InitialStateSerializer < ActiveModel::Serializer
locale: I18n.locale,
mascot: instance_presenter.mascot&.file&.url,
profile_directory: Setting.profile_directory,
registrations_open: Setting.registrations_mode != 'none' && !Rails.configuration.x.single_user_mode,
registrations_open: Setting.registrations_mode != 'none' && !reach_registrations_limit? && !Rails.configuration.x.single_user_mode,
registrations_reach_limit: Setting.registrations_mode != 'none' && reach_registrations_limit?,
repository: Mastodon::Version.repository,
search_enabled: Chewy.enabled?,
single_user_mode: Rails.configuration.x.single_user_mode,

View file

@ -3,6 +3,7 @@
class NodeInfo::Serializer < ActiveModel::Serializer
include RoutingHelper
include KmyblueCapabilitiesHelper
include RegistrationLimitationHelper
attributes :version, :software, :protocols, :services, :usage, :open_registrations, :metadata
@ -35,7 +36,7 @@ class NodeInfo::Serializer < ActiveModel::Serializer
end
def open_registrations
Setting.registrations_mode != 'none' && !Rails.configuration.x.single_user_mode
Setting.registrations_mode != 'none' && !reach_registrations_limit? && !Rails.configuration.x.single_user_mode
end
def metadata

View file

@ -9,6 +9,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer
include RoutingHelper
include KmyblueCapabilitiesHelper
include RegistrationLimitationHelper
attributes :domain, :title, :version, :source_url, :description,
:usage, :thumbnail, :languages, :configuration,
@ -110,6 +111,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer
{
enabled: registrations_enabled?,
approval_required: Setting.registrations_mode == 'approved',
limit_reached: Setting.registrations_mode != 'none' && reach_registrations_limit?,
message: registrations_enabled? ? nil : registrations_message,
url: ENV.fetch('SSO_ACCOUNT_SIGN_UP', nil),
}
@ -118,7 +120,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer
private
def registrations_enabled?
Setting.registrations_mode != 'none' && !Rails.configuration.x.single_user_mode
Setting.registrations_mode != 'none' && !reach_registrations_limit? && !Rails.configuration.x.single_user_mode
end
def registrations_message

View file

@ -3,6 +3,7 @@
class REST::V1::InstanceSerializer < ActiveModel::Serializer
include RoutingHelper
include KmyblueCapabilitiesHelper
include RegistrationLimitationHelper
attributes :uri, :title, :short_description, :description, :email,
:version, :urls, :stats, :thumbnail,
@ -109,7 +110,7 @@ class REST::V1::InstanceSerializer < ActiveModel::Serializer
end
def registrations
Setting.registrations_mode != 'none' && !Rails.configuration.x.single_user_mode
Setting.registrations_mode != 'none' && !reach_registrations_limit? && !Rails.configuration.x.single_user_mode
end
def approval_required

View file

@ -2,6 +2,7 @@
class DeleteAccountService < BaseService
include Payloadable
include RegistrationLimitationHelper
ASSOCIATIONS_ON_SUSPEND = %w(
account_notes
@ -143,6 +144,8 @@ class DeleteAccountService < BaseService
else
@account.user.destroy
end
reset_registration_limit_caches!
end
def purge_content!

View file

@ -27,5 +27,19 @@
.fields-group
= f.input :registration_button_message, as: :text, kmyblue: true, hint: false, wrapper: :with_label, input_html: { rows: 2 }
.fields-group
= f.input :registrations_limit, kmyblue: true, wrapper: :with_label, input_html: { pattern: '[0-9]+' }, label: I18n.t('simple_form.labels.form_admin_settings.registrations_limit', count: @current_users_count)
.fields-group
= f.input :registrations_limit_per_day, kmyblue: true, wrapper: :with_label, input_html: { pattern: '[0-9]+' }, label: I18n.t('simple_form.labels.form_admin_settings.registrations_limit_per_day', count: @current_users_count_today)
.fields-group
= f.input :registrations_start_hour, kmyblue: true, wrapper: :with_label, input_html: { pattern: '[0-9]+' }
= f.input :registrations_end_hour, kmyblue: true, wrapper: :with_label, input_html: { pattern: '[0-9]+' }
.fields-group
= f.input :registrations_secondary_start_hour, kmyblue: true, wrapper: :with_label, input_html: { pattern: '[0-9]+' }
= f.input :registrations_secondary_end_hour, kmyblue: true, wrapper: :with_label, input_html: { pattern: '[0-9]+' }
.actions
= f.button :button, t('generic.save_changes'), type: :submit

View file

@ -0,0 +1,11 @@
- content_for :page_title do
= t('auth.registration_limit.title')
= form_tag root_url, method: 'GET', class: 'simple_form' do
= render 'auth/shared/progress', stage: 'confirm'
%h1.title= t('auth.registration_limit.title')
%p.lead= t('auth.registration_limit.hint_html')
.actions
= button_tag t('challenge.confirm'), class: 'button', type: :submit

View file

@ -93,6 +93,7 @@ en:
updated_not_active: Your password has been changed successfully.
registrations:
destroyed: Bye! Your account has been successfully cancelled. We hope to see you again soon.
sign_up_failed_because_reach_limit: You cannot create account because reaching limit.
signed_up: Welcome! You have signed up successfully.
signed_up_but_inactive: You have signed up successfully. However, we could not sign you in because your account is not yet activated.
signed_up_but_locked: You have signed up successfully. However, we could not sign you in because your account is locked.

View file

@ -93,6 +93,7 @@ ja:
updated_not_active: パスワードは正常に更新されました。
registrations:
destroyed: アカウントの作成はキャンセルされました。またのご利用をお待ちしています。
sign_up_failed_because_reach_limit: 制限に到達しているため、アカウントを作成できません。
signed_up: アカウントの作成が完了しました。Mastodonへようこそ。
signed_up_but_inactive: アカウントの作成が完了しました。しかし、アカウントが有効化されていないためログインできませんでした。
signed_up_but_locked: アカウントの作成が完了しました。しかし、アカウントがロックされているためログインできませんでした。

View file

@ -1267,6 +1267,9 @@ en:
saml: SAML
register: Sign up
registration_closed: "%{instance} is not accepting new members"
registration_limit:
hint_html: The maximum number of new registrations allowed, or the time of day, is restricted by the administrator. You will not be able to complete authentication while the restriction is in effect. Please contact the administrator or wait until the restriction is lifted. If you have reached the limit, you will need to register again.
title: New registrations are currently restricted
resend_confirmation: Resend confirmation link
reset_password: Reset password
rules:

View file

@ -1255,6 +1255,9 @@ ja:
saml: SAML
register: 登録する
registration_closed: "%{instance}は現在、新規登録停止中です"
registration_limit:
hint_html: 新規登録可能な上限人数、もしくは時間帯は、管理者によって制限されています。制限中は認証を完了することができません。管理者に問い合わせるか、制限が解除されるまでお待ちください。なお認証期限に達した場合は、再度新規登録を行う必要があります。
title: 新規登録は現在制限中です
resend_confirmation: 確認メールを再送
reset_password: パスワードを再発行
rules:

View file

@ -354,7 +354,13 @@ en:
profile_directory: Enable profile directory
receive_other_servers_emoji_reaction: Receive emoji reaction between other server users
registration_button_message: Register button message
registrations_limit: User registration limit
registrations_limit_per_day: User registration limit per day
registrations_mode: Who can sign-up
registrations_end_hour: Registration end hour (UTC 0-24)
registrations_start_hour: Registration start hour (UTC 0-24)
registrations_secondary_end_hour: Secondary registration end hour (UTC 0-24) If input 0, secondary hour is disabled.
registrations_secondary_start_hour: Secondary registration start hour (UTC 0-24)
require_invite_text: Require a reason to join
show_domain_blocks: Show domain blocks
show_domain_blocks_rationale: Show why domains were blocked

View file

@ -117,6 +117,10 @@ ja:
peers_api_enabled: このサーバーが Fediverse で遭遇したドメイン名のリストです。このサーバーが知っているだけで、特定のサーバーと連合しているかのデータは含まれません。これは一般的に Fediverse に関する統計情報を収集するサービスによって使用されます。
profile_directory: ディレクトリには、掲載する設定をしたすべてのユーザーが一覧表示されます。
receive_other_servers_emoji_reaction: 負荷の原因になります。人が少ない場合にのみ有効にすることをおすすめします。
registrations_limit: 現在のユーザー数がこれを超過すると、管理者がこの数値を増やさない限り新規登録できません。0を指定すると、この制限を無効化します。
registrations_limit_per_day: 本日登録されたユーザー数がこれを超過すると、UTC時刻で翌日0時にならない限り新規登録できません。0を指定すると、この制限を無効化します。
registrations_end_hour: 新規登録可能な時間帯の開始時間を指定します。これより前の時間に登録することはできません。終了時間より後にすることはできません。
registrations_start_hour: 新規登録可能な時間帯の終了時間を指定します。これより後の時間に登録することはできません。開始時間より前にすることはできません。
require_invite_text: アカウント登録が承認制の場合、登録の際の申請事由の入力を必須にします
site_contact_email: 法律またはサポートに関する問い合わせ先
site_contact_username: マストドンでの連絡方法
@ -363,7 +367,13 @@ ja:
profile_directory: プロフィール一覧を有効にする
receive_other_servers_emoji_reaction: 他のサーバーのユーザーが他のサーバーの投稿につけた絵文字リアクションを受け入れる
registration_button_message: 新規登録ボタンの直上に表示するメッセージ
registrations_limit: 登録ユーザー数上限 (現在 %{count} 名)
registrations_limit_per_day: 1日あたり登録ユーザー数上限 (現在 %{count} 名)
registrations_mode: 新規登録が可能な人
registrations_end_hour: 登録受付終了時間 A (UTC 0〜24)
registrations_start_hour: 登録受付開始時間 A (UTC 0〜24)
registrations_secondary_end_hour: 登録受付終了時間 B (UTC 0〜24) ここで0を指定した場合、時間Bの設定は無効化されます
registrations_secondary_start_hour: 登録受付開始時間 B (UTC 0〜24)
require_invite_text: 申請事由の入力を必須にする
show_domain_blocks: ドメインブロックを表示
show_domain_blocks_rationale: ドメインがブロックされた理由を表示

View file

@ -10,6 +10,12 @@ defaults: &defaults
site_contact_username: ''
site_contact_email: ''
registrations_mode: 'open'
registrations_limit: 0
registrations_limit_per_day: 0
registrations_start_hour: 0
registrations_end_hour: 24
registrations_secondary_start_hour: 0
registrations_secondary_end_hour: 0
profile_directory: true
closed_registrations_message: ''
timeline_preview: true

View file

@ -280,6 +280,164 @@ RSpec.describe Auth::RegistrationsController do
end
end
context 'when max user count is set' do
subject do
post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', agreement: 'true' } }
end
let(:users_max) { 3 }
before do
Fabricate(:user)
Fabricate(:user)
Setting.registrations_mode = 'open'
Setting.registrations_limit = users_max
request.headers['Accept-Language'] = accept_language
end
it 'redirects to setup' do
subject
expect(response).to redirect_to auth_setup_path
end
it 'creates user' do
subject
user = User.find_by(email: 'test@example.com')
expect(user).to_not be_nil
expect(user.locale).to eq(accept_language)
end
context 'when limit is reached' do
let(:users_max) { 2 }
it 'does not create user' do
subject
user = User.find_by(email: 'test@example.com')
expect(user).to be_nil
end
context 'with invite' do
subject do
post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', invite_code: invite.code, agreement: 'true' } }
end
let(:invite) { Fabricate(:invite) }
it 'creates user' do
subject
user = User.find_by(email: 'test@example.com')
expect(user).to_not be_nil
expect(user.locale).to eq(accept_language)
end
end
end
end
context 'when max user count per day is set' do
subject do
post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', agreement: 'true' } }
end
let(:users_max) { 2 }
let(:created_at) { Time.now.utc }
let(:precreate_users) { true }
before do
Fabricate(:user, created_at: created_at) if precreate_users
Setting.registrations_mode = 'open'
Setting.registrations_limit_per_day = users_max
request.headers['Accept-Language'] = accept_language
end
it 'creates user' do
subject
user = User.find_by(email: 'test@example.com')
expect(user).to_not be_nil
expect(user.locale).to eq(accept_language)
end
context 'when limit is reached' do
let(:users_max) { 2 }
let(:created_at) { Time.now.utc - 1.day }
let(:precreate_users) { false }
before do
travel_to Time.now.utc - 1.day
Fabricate(:user)
create_other_user
end
it 'does not create user yesterday' do
subject
user = User.find_by(email: 'test@example.com')
expect(user).to be_nil
end
it 'creates user' do
travel_to Time.now.utc + 1.day
subject
user = User.find_by(email: 'test@example.com')
expect(user).to_not be_nil
expect(user.locale).to eq(accept_language)
end
end
def create_other_user
post :create, params: { user: { account_attributes: { username: 'ohagi' }, email: 'test@ohagi.com', password: 'ohagi_must_be_tsubuan', password_confirmation: 'ohagi_must_be_tsubuan', agreement: 'true' } }
end
end
context 'when registration time range is set' do
subject do
post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', agreement: 'true' } }
end
shared_examples 'registration with time' do |header, start_hour_val, end_hour_val, secondary_start_hour_val, secondary_end_hour_val, result| # rubocop:disable Metrics/ParameterLists
context header do
let(:start_hour) { start_hour_val }
let(:end_hour) { end_hour_val }
let(:secondary_start_hour) { secondary_start_hour_val }
let(:secondary_end_hour) { secondary_end_hour_val }
before do
Setting.registrations_mode = 'open'
Setting.registrations_start_hour = start_hour
Setting.registrations_end_hour = end_hour
Setting.registrations_secondary_start_hour = secondary_start_hour
Setting.registrations_secondary_end_hour = secondary_end_hour
request.headers['Accept-Language'] = accept_language
travel_to Time.now.utc.beginning_of_day + 10.hours
end
if result
it 'creates user' do
subject
user = User.find_by(email: 'test@example.com')
expect(user).to_not be_nil
expect(user.locale).to eq(accept_language)
end
else
it 'does not create user' do
subject
user = User.find_by(email: 'test@example.com')
expect(user).to be_nil
end
end
end
end
it_behaves_like 'registration with time', 'time range is not set', 0, 24, 0, 0, true
it_behaves_like 'registration with time', 'time range is set', 9, 12, 0, 0, true
it_behaves_like 'registration with time', 'time range is out of range', 12, 15, 0, 0, false
it_behaves_like 'registration with time', 'time range is invalid', 20, 15, 0, 0, true
it_behaves_like 'registration with time', 'secondary time range is set', 0, 4, 9, 12, true
it_behaves_like 'registration with time', 'secondary time range is out of range', 0, 4, 12, 15, false
it_behaves_like 'registration with time', 'secondary time range is invalid', 0, 4, 20, 15, false
it_behaves_like 'registration with time', 'both time range are invalid', 4, 0, 20, 15, true
it_behaves_like 'registration with time', 'only secondary time range is set', 0, 0, 9, 12, true
end
include_examples 'checks for enabled registrations', :create
end

View file

@ -233,6 +233,38 @@ RSpec.describe User do
expect(TriggerWebhookWorker).to_not have_received(:perform_async).with('account.approved', 'Account', user.account_id)
end
end
context 'when max user count is set' do
let(:users_max) { 3 }
before do
Fabricate(:user)
Fabricate(:user)
Setting.registrations_limit = users_max
end
it 'creates user' do
expect { subject }.to_not raise_error
expect(user.confirmed?).to be true
end
context 'with limit is reached' do
let(:users_max) { 2 }
it 'does not create user' do
expect { subject }.to raise_error Mastodon::ValidationError
expect(user.confirmed?).to be false
end
it 'but creates user when invited' do
invite = Fabricate(:invite, user: Fabricate(:user), max_uses: nil, expires_at: 1.hour.from_now)
user.update!(invite: invite)
expect { subject }.to_not raise_error
expect(user.confirmed?).to be true
end
end
end
end
end