about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.babelrc1
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock12
-rw-r--r--app/models/account.rb6
-rw-r--r--app/validators/unique_username_validator.rb14
-rw-r--r--lib/mastodon/version.rb2
6 files changed, 26 insertions, 11 deletions
diff --git a/.babelrc b/.babelrc
index ed28aa500..190b5038c 100644
--- a/.babelrc
+++ b/.babelrc
@@ -4,7 +4,6 @@
     [
       "env",
       {
-        "debug": true,
         "exclude": ["transform-async-to-generator", "transform-regenerator"],
         "loose": true,
         "modules": false,
diff --git a/Gemfile b/Gemfile
index 195ff6e14..47d941994 100644
--- a/Gemfile
+++ b/Gemfile
@@ -73,7 +73,7 @@ gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
 gem 'rqrcode', '~> 0.10'
 gem 'ruby-oembed', '~> 0.12', require: 'oembed'
 gem 'ruby-progressbar', '~> 1.4'
-gem 'sanitize', '~> 4.4'
+gem 'sanitize', '~> 4.6.4'
 gem 'sidekiq', '~> 5.0'
 gem 'sidekiq-scheduler', '~> 2.1'
 gem 'sidekiq-unique-jobs', '~> 5.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 4ae407f79..f30cd518e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -290,7 +290,7 @@ GEM
       activesupport (>= 4, < 5.2)
       railties (>= 4, < 5.2)
       request_store (~> 1.0)
-    loofah (2.1.1)
+    loofah (2.2.1)
       crass (~> 1.0.2)
       nokogiri (>= 1.5.9)
     mail (2.7.0)
@@ -318,9 +318,9 @@ GEM
       net-ssh (>= 2.6.5)
     net-ssh (4.2.0)
     nio4r (2.1.0)
-    nokogiri (1.8.1)
+    nokogiri (1.8.2)
       mini_portile2 (~> 2.3.0)
-    nokogumbo (1.4.13)
+    nokogumbo (1.5.0)
       nokogiri
     nsa (0.2.4)
       activesupport (>= 4.2, < 6)
@@ -499,10 +499,10 @@ GEM
     rufus-scheduler (3.4.2)
       et-orbi (~> 1.0)
     safe_yaml (1.0.4)
-    sanitize (4.5.0)
+    sanitize (4.6.4)
       crass (~> 1.0.2)
       nokogiri (>= 1.4.4)
-      nokogumbo (~> 1.4.1)
+      nokogumbo (~> 1.4)
     sass (3.5.3)
       sass-listen (~> 4.0.0)
     sass-listen (4.0.0)
@@ -704,7 +704,7 @@ DEPENDENCIES
   rubocop
   ruby-oembed (~> 0.12)
   ruby-progressbar (~> 1.4)
-  sanitize (~> 4.4)
+  sanitize (~> 4.6.4)
   scss_lint (~> 0.55)
   sidekiq (~> 5.0)
   sidekiq-bulk (~> 0.1.1)
diff --git a/app/models/account.rb b/app/models/account.rb
index 61f81ab70..16a256bfc 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -47,7 +47,8 @@
 #
 
 class Account < ApplicationRecord
-  MENTION_RE = /(?<=^|[^\/[:word:]])@(([a-z0-9_]+)(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i
+  USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.]+[a-z0-9_]+)?/i
+  MENTION_RE  = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE}?)(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i
 
   include AccountAvatar
   include AccountFinderConcern
@@ -70,7 +71,8 @@ class Account < ApplicationRecord
   validates :username, uniqueness: { scope: :domain, case_sensitive: true }, if: -> { !local? && will_save_change_to_username? }
 
   # Local user validations
-  validates :username, format: { with: /\A[a-z0-9_]+\z/i }, uniqueness: { scope: :domain, case_sensitive: false }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? }
+  validates :username, format: { with: /\A#{USERNAME_RE}\z/i }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? }
+  validates_with UniqueUsernameValidator, if: -> { local? && will_save_change_to_username? }
   validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? }
   validates :display_name, length: { maximum: 30 }, if: -> { local? && will_save_change_to_display_name? }
   validate :note_length_does_not_exceed_length_limit, if: -> { local? && will_save_change_to_note? }
diff --git a/app/validators/unique_username_validator.rb b/app/validators/unique_username_validator.rb
new file mode 100644
index 000000000..c76407b16
--- /dev/null
+++ b/app/validators/unique_username_validator.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class UniqueUsernameValidator < ActiveModel::Validator
+  def validate(account)
+    return if account.username.nil?
+
+    normalized_username = account.username.downcase.delete('.')
+
+    scope = Account.where(domain: nil, username: normalized_username)
+    scope = scope.where.not(id: account.id) if account.persisted?
+
+    account.errors.add(:username, :taken) if scope.exists?
+  end
+end
diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb
index 78a2dd901..80650761a 100644
--- a/lib/mastodon/version.rb
+++ b/lib/mastodon/version.rb
@@ -21,7 +21,7 @@ module Mastodon
     end
 
     def flags
-      'rc3'
+      'rc4'
     end
 
     def to_a