From c7030606f1310ed548ae074b6b1174a85451d8c0 Mon Sep 17 00:00:00 2001 From: tobi Date: Sun, 9 Feb 2025 15:12:37 +0100 Subject: [PATCH] [feature] make account sign-up / backlog limits configurable --- docs/admin/signups.md | 6 ++-- docs/configuration/accounts.md | 22 +++++++++++++ example/config.yaml | 22 +++++++++++++ internal/config/config.go | 10 +++--- internal/config/defaults.go | 10 +++--- internal/config/helpers.gen.go | 50 ++++++++++++++++++++++++++++ internal/processing/user/create.go | 53 +++++++++++++++++------------- test/envparsing.sh | 4 +++ testrig/config.go | 10 +++--- 9 files changed, 151 insertions(+), 36 deletions(-) diff --git a/docs/admin/signups.md b/docs/admin/signups.md index 78513380c..896059be9 100644 --- a/docs/admin/signups.md +++ b/docs/admin/signups.md @@ -46,12 +46,14 @@ If you **reject** the sign-up, you may wish to inform the applicant that their s ## Sign-Up Limits -To avoid sign-up backlogs overwhelming admins and moderators, GoToSocial limits the sign-up pending backlog to 20 accounts. Once there are 20 accounts pending in the backlog waiting to be handled by an admin or moderator, new sign-ups will not be accepted via the form. +By default, to avoid sign-up backlogs overwhelming admins and moderators, GoToSocial limits the sign-up pending backlog to 20 accounts. Once there are 20 accounts pending in the backlog waiting to be handled by an admin or moderator, new sign-ups will not be accepted via the form. -New sign-ups will also not be accepted via the form if 10 or more new account sign-ups have been approved in the last 24 hours, to avoid instances rapidly expanding beyond the capabilities of moderators. +By default, new sign-ups will also not be accepted via the form if 10 or more new account sign-ups have been approved in the last 24 hours, to avoid instances rapidly expanding beyond the capabilities of moderators. In both cases, applicants will be shown an error message explaining why they could not submit the form, and inviting them to try again later. +The limit of sign-ups per day, and the backlog size, can be configured or disabled altogether with the variables `accounts-registration-daily-limit` and `accounts-registration-backlog-limit`. See the [accounts config section](../configuration/accounts.md) for more info. + To combat spam accounts, GoToSocial account sign-ups **always** require manual approval by an administrator, and applicants must **always** confirm their email address before they are able to log in and post. ## Sign-Up Via Invite diff --git a/docs/configuration/accounts.md b/docs/configuration/accounts.md index 41fb3eaa9..788cf79ac 100644 --- a/docs/configuration/accounts.md +++ b/docs/configuration/accounts.md @@ -20,6 +20,28 @@ accounts-registration-open: false # Default: true accounts-reason-required: true +# Int. Number of approved sign-ups allowed within +# 24hrs before new account registration is closed. +# +# Leaving this count at the default essentially limits +# your instance to growing by 10 accounts per day. +# +# Setting this number to 0 or less removes the limit. +# +# Default: 10 +accounts-registration-daily-limit: 10 + +# Int. Number of new account sign-ups allowed in the pending +# approval queue before new account registration is closed. +# +# This can be used to essentially "throttle" the sign-up +# queue to prevent instance admins becoming overwhelmed. +# +# Setting this number to 0 or less removes the limit. +# +# Default: 20 +accounts-registration-backlog-limit: 20 + # Bool. Allow accounts on this instance to set custom CSS for their profile pages and statuses. # Enabling this setting will allow accounts to upload custom CSS via the /user settings page, # which will then be rendered on the web view of the account's profile and statuses. diff --git a/example/config.yaml b/example/config.yaml index 60d56bafc..b618ad7ba 100644 --- a/example/config.yaml +++ b/example/config.yaml @@ -475,6 +475,28 @@ accounts-registration-open: false # Default: true accounts-reason-required: true +# Int. Number of approved sign-ups allowed within +# 24hrs before new account registration is closed. +# +# Leaving this count at the default essentially limits +# your instance to growing by 10 accounts per day. +# +# Setting this number to 0 or less removes the limit. +# +# Default: 10 +accounts-registration-daily-limit: 10 + +# Int. Number of new account sign-ups allowed in the pending +# approval queue before new account registration is closed. +# +# This can be used to essentially "throttle" the sign-up +# queue to prevent instance admins becoming overwhelmed. +# +# Setting this number to 0 or less removes the limit. +# +# Default: 20 +accounts-registration-backlog-limit: 20 + # Bool. Allow accounts on this instance to set custom CSS for their profile pages and statuses. # Enabling this setting will allow accounts to upload custom CSS via the /user settings page, # which will then be rendered on the web view of the account's profile and statuses. diff --git a/internal/config/config.go b/internal/config/config.go index 5c59c47cc..33003d0f9 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -92,10 +92,12 @@ type Configuration struct { InstanceSubscriptionsProcessEvery time.Duration `name:"instance-subscriptions-process-every" usage:"Period to elapse between instance subscriptions processing jobs, starting from instance-subscriptions-process-from."` InstanceStatsMode string `name:"instance-stats-mode" usage:"Allows you to customize the way stats are served to crawlers: one of '', 'serve', 'zero', 'baffle'. Home page stats remain unchanged."` - AccountsRegistrationOpen bool `name:"accounts-registration-open" usage:"Allow anyone to submit an account signup request. If false, server will be invite-only."` - AccountsReasonRequired bool `name:"accounts-reason-required" usage:"Do new account signups require a reason to be submitted on registration?"` - AccountsAllowCustomCSS bool `name:"accounts-allow-custom-css" usage:"Allow accounts to enable custom CSS for their profile pages and statuses."` - AccountsCustomCSSLength int `name:"accounts-custom-css-length" usage:"Maximum permitted length (characters) of custom CSS for accounts."` + AccountsRegistrationOpen bool `name:"accounts-registration-open" usage:"Allow anyone to submit an account signup request. If false, server will be invite-only."` + AccountsReasonRequired bool `name:"accounts-reason-required" usage:"Do new account signups require a reason to be submitted on registration?"` + AccountsRegistrationDailyLimit int `name:"accounts-registration-daily-limit" usage:"Limit amount of approved account sign-ups allowed per 24hrs before registration is closed. 0 or less = no limit."` + AccountsRegistrationBacklogLimit int `name:"accounts-registration-backlog-limit" usage:"Limit how big the 'accounts pending approval' queue can grow before registration is closed. 0 or less = no limit."` + AccountsAllowCustomCSS bool `name:"accounts-allow-custom-css" usage:"Allow accounts to enable custom CSS for their profile pages and statuses."` + AccountsCustomCSSLength int `name:"accounts-custom-css-length" usage:"Maximum permitted length (characters) of custom CSS for accounts."` MediaDescriptionMinChars int `name:"media-description-min-chars" usage:"Min required chars for an image description"` MediaDescriptionMaxChars int `name:"media-description-max-chars" usage:"Max permitted chars for an image description"` diff --git a/internal/config/defaults.go b/internal/config/defaults.go index 157dfde0a..7f66e4209 100644 --- a/internal/config/defaults.go +++ b/internal/config/defaults.go @@ -68,10 +68,12 @@ InstanceSubscriptionsProcessFrom: "23:00", // 11pm, InstanceSubscriptionsProcessEvery: 24 * time.Hour, // 1/day. - AccountsRegistrationOpen: false, - AccountsReasonRequired: true, - AccountsAllowCustomCSS: false, - AccountsCustomCSSLength: 10000, + AccountsRegistrationOpen: false, + AccountsReasonRequired: true, + AccountsRegistrationDailyLimit: 10, + AccountsRegistrationBacklogLimit: 20, + AccountsAllowCustomCSS: false, + AccountsCustomCSSLength: 10000, MediaDescriptionMinChars: 0, MediaDescriptionMaxChars: 1500, diff --git a/internal/config/helpers.gen.go b/internal/config/helpers.gen.go index 54d1b62d9..d3ccf16ea 100644 --- a/internal/config/helpers.gen.go +++ b/internal/config/helpers.gen.go @@ -1132,6 +1132,56 @@ func GetAccountsReasonRequired() bool { return global.GetAccountsReasonRequired( // SetAccountsReasonRequired safely sets the value for global configuration 'AccountsReasonRequired' field func SetAccountsReasonRequired(v bool) { global.SetAccountsReasonRequired(v) } +// GetAccountsRegistrationDailyLimit safely fetches the Configuration value for state's 'AccountsRegistrationDailyLimit' field +func (st *ConfigState) GetAccountsRegistrationDailyLimit() (v int) { + st.mutex.RLock() + v = st.config.AccountsRegistrationDailyLimit + st.mutex.RUnlock() + return +} + +// SetAccountsRegistrationDailyLimit safely sets the Configuration value for state's 'AccountsRegistrationDailyLimit' field +func (st *ConfigState) SetAccountsRegistrationDailyLimit(v int) { + st.mutex.Lock() + defer st.mutex.Unlock() + st.config.AccountsRegistrationDailyLimit = v + st.reloadToViper() +} + +// AccountsRegistrationDailyLimitFlag returns the flag name for the 'AccountsRegistrationDailyLimit' field +func AccountsRegistrationDailyLimitFlag() string { return "accounts-registration-daily-limit" } + +// GetAccountsRegistrationDailyLimit safely fetches the value for global configuration 'AccountsRegistrationDailyLimit' field +func GetAccountsRegistrationDailyLimit() int { return global.GetAccountsRegistrationDailyLimit() } + +// SetAccountsRegistrationDailyLimit safely sets the value for global configuration 'AccountsRegistrationDailyLimit' field +func SetAccountsRegistrationDailyLimit(v int) { global.SetAccountsRegistrationDailyLimit(v) } + +// GetAccountsRegistrationBacklogLimit safely fetches the Configuration value for state's 'AccountsRegistrationBacklogLimit' field +func (st *ConfigState) GetAccountsRegistrationBacklogLimit() (v int) { + st.mutex.RLock() + v = st.config.AccountsRegistrationBacklogLimit + st.mutex.RUnlock() + return +} + +// SetAccountsRegistrationBacklogLimit safely sets the Configuration value for state's 'AccountsRegistrationBacklogLimit' field +func (st *ConfigState) SetAccountsRegistrationBacklogLimit(v int) { + st.mutex.Lock() + defer st.mutex.Unlock() + st.config.AccountsRegistrationBacklogLimit = v + st.reloadToViper() +} + +// AccountsRegistrationBacklogLimitFlag returns the flag name for the 'AccountsRegistrationBacklogLimit' field +func AccountsRegistrationBacklogLimitFlag() string { return "accounts-registration-backlog-limit" } + +// GetAccountsRegistrationBacklogLimit safely fetches the value for global configuration 'AccountsRegistrationBacklogLimit' field +func GetAccountsRegistrationBacklogLimit() int { return global.GetAccountsRegistrationBacklogLimit() } + +// SetAccountsRegistrationBacklogLimit safely sets the value for global configuration 'AccountsRegistrationBacklogLimit' field +func SetAccountsRegistrationBacklogLimit(v int) { global.SetAccountsRegistrationBacklogLimit(v) } + // GetAccountsAllowCustomCSS safely fetches the Configuration value for state's 'AccountsAllowCustomCSS' field func (st *ConfigState) GetAccountsAllowCustomCSS() (v bool) { st.mutex.RLock() diff --git a/internal/processing/user/create.go b/internal/processing/user/create.go index 0d848583e..f878d8320 100644 --- a/internal/processing/user/create.go +++ b/internal/processing/user/create.go @@ -44,34 +44,43 @@ func (p *Processor) Create( app *gtsmodel.Application, form *apimodel.AccountCreateRequest, ) (*gtsmodel.User, gtserror.WithCode) { - const ( - usersPerDay = 10 - regBacklog = 20 + var ( + usersPerDay = config.GetAccountsRegistrationDailyLimit() + regBacklog = config.GetAccountsRegistrationBacklogLimit() ) - // Ensure no more than usersPerDay + // If usersPerDay limit is in place, + // ensure no more than usersPerDay // have registered in the last 24h. - newUsersCount, err := p.state.DB.CountApprovedSignupsSince(ctx, time.Now().Add(-24*time.Hour)) - if err != nil { - err := fmt.Errorf("db error counting new users: %w", err) - return nil, gtserror.NewErrorInternalError(err) + if usersPerDay > 0 { + newUsersCount, err := p.state.DB.CountApprovedSignupsSince(ctx, time.Now().Add(-24*time.Hour)) + if err != nil { + err := fmt.Errorf("db error counting new users: %w", err) + return nil, gtserror.NewErrorInternalError(err) + } + + if newUsersCount >= usersPerDay { + err := fmt.Errorf("this instance has hit its limit of new sign-ups for today (%d); you can try again tomorrow", usersPerDay) + return nil, gtserror.NewErrorUnprocessableEntity(err, err.Error()) + } } - if newUsersCount >= usersPerDay { - err := fmt.Errorf("this instance has hit its limit of new sign-ups for today; you can try again tomorrow") - return nil, gtserror.NewErrorUnprocessableEntity(err, err.Error()) - } + // If registration backlog limit is + // in place, ensure backlog isn't full. + if regBacklog > 0 { + backlogLen, err := p.state.DB.CountUnhandledSignups(ctx) + if err != nil { + err := fmt.Errorf("db error counting registration backlog length: %w", err) + return nil, gtserror.NewErrorInternalError(err) + } - // Ensure the new users backlog isn't full. - backlogLen, err := p.state.DB.CountUnhandledSignups(ctx) - if err != nil { - err := fmt.Errorf("db error counting registration backlog length: %w", err) - return nil, gtserror.NewErrorInternalError(err) - } - - if backlogLen >= regBacklog { - err := fmt.Errorf("this instance's sign-up backlog is currently full; you must wait until pending sign-ups are handled by the admin(s)") - return nil, gtserror.NewErrorUnprocessableEntity(err, err.Error()) + if backlogLen >= regBacklog { + err := fmt.Errorf( + "this instance's sign-up backlog is currently full (%d sign-ups pending approval); "+ + "you must wait until some pending sign-ups are handled by the admin(s)", regBacklog, + ) + return nil, gtserror.NewErrorUnprocessableEntity(err, err.Error()) + } } emailAvailable, err := p.state.DB.IsEmailAvailable(ctx, form.Email) diff --git a/test/envparsing.sh b/test/envparsing.sh index 904dc8764..0e7c0db20 100755 --- a/test/envparsing.sh +++ b/test/envparsing.sh @@ -8,6 +8,8 @@ EXPECT=$(cat << "EOF" "accounts-allow-custom-css": true, "accounts-custom-css-length": 5000, "accounts-reason-required": false, + "accounts-registration-backlog-limit": 100, + "accounts-registration-daily-limit": 50, "accounts-registration-open": true, "advanced-cookies-samesite": "strict", "advanced-csp-extra-uris": [], @@ -252,6 +254,8 @@ GTS_INSTANCE_LANGUAGES="nl,en-gb" \ GTS_INSTANCE_STATS_MODE="baffle" \ GTS_ACCOUNTS_ALLOW_CUSTOM_CSS=true \ GTS_ACCOUNTS_CUSTOM_CSS_LENGTH=5000 \ +GTS_ACCOUNTS_REGISTRATION_BACKLOG_LIMIT=100 \ +GTS_ACCOUNTS_REGISTRATION_DAILY_LIMIT=50 \ GTS_ACCOUNTS_REGISTRATION_OPEN=true \ GTS_ACCOUNTS_REASON_REQUIRED=false \ GTS_MEDIA_DESCRIPTION_MIN_CHARS=69 \ diff --git a/testrig/config.go b/testrig/config.go index 0a957a831..f68a8ffb7 100644 --- a/testrig/config.go +++ b/testrig/config.go @@ -102,10 +102,12 @@ func testDefaults() config.Configuration { InstanceSubscriptionsProcessFrom: "23:00", // 11pm, InstanceSubscriptionsProcessEvery: 24 * time.Hour, // 1/day. - AccountsRegistrationOpen: true, - AccountsReasonRequired: true, - AccountsAllowCustomCSS: true, - AccountsCustomCSSLength: 10000, + AccountsRegistrationOpen: true, + AccountsReasonRequired: true, + AccountsRegistrationDailyLimit: 10, + AccountsRegistrationBacklogLimit: 20, + AccountsAllowCustomCSS: true, + AccountsCustomCSSLength: 10000, MediaDescriptionMinChars: 0, MediaDescriptionMaxChars: 500,