about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--app/models/account.rb2
-rw-r--r--app/validators/unreserved_validator.rb15
-rw-r--r--config/locales/en.yml1
-rw-r--r--config/settings.yml9
-rw-r--r--spec/models/account_spec.rb6
5 files changed, 31 insertions, 2 deletions
diff --git a/app/models/account.rb b/app/models/account.rb
index 5b0d2d65c..918d5b0f1 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -56,7 +56,7 @@ class Account < ApplicationRecord
 
   # Local user validations
   with_options if: :local? do
-    validates :username, format: { with: /\A[a-z0-9_]+\z/i }, uniqueness: { scope: :domain, case_sensitive: false }, length: { maximum: 30 }
+    validates :username, format: { with: /\A[a-z0-9_]+\z/i }, uniqueness: { scope: :domain, case_sensitive: false }, length: { maximum: 30 }, unreserved: true
     validates :display_name, length: { maximum: 30 }
     validates :note, length: { maximum: 160 }
   end
diff --git a/app/validators/unreserved_validator.rb b/app/validators/unreserved_validator.rb
new file mode 100644
index 000000000..4e5b9dafc
--- /dev/null
+++ b/app/validators/unreserved_validator.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class UnreservedValidator < ActiveModel::EachValidator
+  def validate_each(record, attribute, value)
+    return if value.nil?
+    record.errors.add(attribute, I18n.t('accounts.reserved_username')) if reserved_username?(value)
+  end
+
+  private
+
+  def reserved_username?(value)
+    return false unless Setting.reserved_usernames
+    Setting.reserved_usernames.include?(value.downcase)
+  end
+end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 2ff2f7eec..965bfbe47 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -40,6 +40,7 @@ en:
     posts: Posts
     remote_follow: Remote follow
     unfollow: Unfollow
+    reserved_username: The username is reserved
   activitypub:
     activity:
       announce:
diff --git a/config/settings.yml b/config/settings.yml
index 4811213cb..bffb7052a 100644
--- a/config/settings.yml
+++ b/config/settings.yml
@@ -7,7 +7,7 @@
 # For more information, see docs/Running-Mastodon/Administration-guide.md
 #
 defaults: &defaults
-  site_title: 'Mastodon'
+  site_title: Mastodon
   site_description: ''
   site_extended_description: ''
   site_contact_username: ''
@@ -27,6 +27,13 @@ defaults: &defaults
   interactions:
     must_be_follower: false
     must_be_following: false
+  reserved_usernames:
+    - admin
+    - support
+    - help
+    - root
+    - webmaster
+    - administrator
 
 development:
   <<: *defaults
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index ebec463e1..ab4de9aa1 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -381,6 +381,12 @@ RSpec.describe Account, type: :model do
       expect(account_2).to model_have_error_on_field(:username)
     end
 
+    it 'is invalid if the username is reserved' do
+      account = Fabricate.build(:account, username: 'support')
+      account.valid?
+      expect(account).to model_have_error_on_field(:username)
+    end
+
     context 'when is local' do
       it 'is invalid if the username doesn\'t only contains letters, numbers and underscores' do
         account = Fabricate.build(:account, username: 'the-doctor')