From 49219508bc2e01fe724830ca31a7cfa7adba15cf Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Sat, 3 Jul 2021 21:13:47 +0200
Subject: [PATCH 01/19] Fix anonymous access to outbox not being cached by the
 reverse proxy (#16458)

* Fix anonymous access to outbox not being cached by the reverse proxy

Up until now, anonymous access to outbox was marked as public, but with a
0 duration for caching, which means remote proxies would only serve from cache
when the server was completely overwhelmed.

Changed that cache duration to one minute, so that repeated anonymous access
to one account's outbox can be appropriately cached.

Also added `Signature` to the `Vary` header in case a page is requested, so
that authenticated fetches are never served from cache (which only contains
public toots).

* Remove Vary: Accept header from webfinger controller

Indeed, we have stopped returning xrd, and only ever return jrd, so the
Accept request header does not matter anymore.

* Cache negative webfinger hits for 3 minutes
---
 .../activitypub/outboxes_controller.rb           | 10 +++++++++-
 .../well_known/webfinger_controller.rb           |  3 ++-
 .../activitypub/outboxes_controller_spec.rb      | 16 ++++++++++++----
 .../well_known/webfinger_controller_spec.rb      |  4 ++++
 4 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/app/controllers/activitypub/outboxes_controller.rb b/app/controllers/activitypub/outboxes_controller.rb
index 4a52560aca8..b2aab56a56f 100644
--- a/app/controllers/activitypub/outboxes_controller.rb
+++ b/app/controllers/activitypub/outboxes_controller.rb
@@ -11,7 +11,11 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
   before_action :set_cache_headers
 
   def show
-    expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode? && !(signed_request_account.present? && page_requested?))
+    if page_requested?
+      expires_in(1.minute, public: public_fetch_mode? && signed_request_account.nil?)
+    else
+      expires_in(3.minutes, public: public_fetch_mode?)
+    end
     render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
   end
 
@@ -76,4 +80,8 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
   def set_account
     @account = params[:account_username].present? ? Account.find_local!(username_param) : Account.representative
   end
+
+  def set_cache_headers
+    response.headers['Vary'] = 'Signature' if authorized_fetch_mode? || page_requested?
+  end
 end
diff --git a/app/controllers/well_known/webfinger_controller.rb b/app/controllers/well_known/webfinger_controller.rb
index 0227f722a77..2b296ea3be4 100644
--- a/app/controllers/well_known/webfinger_controller.rb
+++ b/app/controllers/well_known/webfinger_controller.rb
@@ -4,7 +4,6 @@ module WellKnown
   class WebfingerController < ActionController::Base
     include RoutingHelper
 
-    before_action { response.headers['Vary'] = 'Accept' }
     before_action :set_account
     before_action :check_account_suspension
 
@@ -39,10 +38,12 @@ module WellKnown
     end
 
     def bad_request
+      expires_in(3.minutes, public: true)
       head 400
     end
 
     def not_found
+      expires_in(3.minutes, public: true)
       head 404
     end
 
diff --git a/spec/controllers/activitypub/outboxes_controller_spec.rb b/spec/controllers/activitypub/outboxes_controller_spec.rb
index d23f2c17cbc..1722690db1b 100644
--- a/spec/controllers/activitypub/outboxes_controller_spec.rb
+++ b/spec/controllers/activitypub/outboxes_controller_spec.rb
@@ -55,6 +55,10 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
 
         it_behaves_like 'cachable response'
 
+        it 'does not have a Vary header' do
+          expect(response.headers['Vary']).to be_nil
+        end
+
         context 'when account is permanently suspended' do
           before do
             account.suspend!
@@ -96,6 +100,10 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
 
         it_behaves_like 'cachable response'
 
+        it 'returns Vary header with Signature' do
+          expect(response.headers['Vary']).to include 'Signature'
+        end
+
         context 'when account is permanently suspended' do
           before do
             account.suspend!
@@ -144,7 +152,7 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
         end
 
         it 'returns private Cache-Control header' do
-          expect(response.headers['Cache-Control']).to eq 'max-age=0, private'
+          expect(response.headers['Cache-Control']).to eq 'max-age=60, private'
         end
       end
 
@@ -170,7 +178,7 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
         end
 
         it 'returns private Cache-Control header' do
-          expect(response.headers['Cache-Control']).to eq 'max-age=0, private'
+          expect(response.headers['Cache-Control']).to eq 'max-age=60, private'
         end
       end
 
@@ -195,7 +203,7 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
         end
 
         it 'returns private Cache-Control header' do
-          expect(response.headers['Cache-Control']).to eq 'max-age=0, private'
+          expect(response.headers['Cache-Control']).to eq 'max-age=60, private'
         end
       end
 
@@ -220,7 +228,7 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
         end
 
         it 'returns private Cache-Control header' do
-          expect(response.headers['Cache-Control']).to eq 'max-age=0, private'
+          expect(response.headers['Cache-Control']).to eq 'max-age=60, private'
         end
       end
     end
diff --git a/spec/controllers/well_known/webfinger_controller_spec.rb b/spec/controllers/well_known/webfinger_controller_spec.rb
index 1075456f336..8574d369d19 100644
--- a/spec/controllers/well_known/webfinger_controller_spec.rb
+++ b/spec/controllers/well_known/webfinger_controller_spec.rb
@@ -24,6 +24,10 @@ describe WellKnown::WebfingerController, type: :controller do
         expect(response).to have_http_status(200)
       end
 
+      it 'does not set a Vary header' do
+        expect(response.headers['Vary']).to be_nil
+      end
+
       it 'returns application/jrd+json' do
         expect(response.media_type).to eq 'application/jrd+json'
       end

From 7b14b8074d937c1ab4ea2656269a39563cf24f09 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 4 Jul 2021 05:07:32 +0900
Subject: [PATCH 02/19] Bump rails from 6.1.3.2 to 6.1.4 (#16436)

Bumps [rails](https://github.com/rails/rails) from 6.1.3.2 to 6.1.4.
- [Release notes](https://github.com/rails/rails/releases)
- [Commits](https://github.com/rails/rails/compare/v6.1.3.2...v6.1.4)

---
updated-dependencies:
- dependency-name: rails
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 Gemfile      |   2 +-
 Gemfile.lock | 116 +++++++++++++++++++++++++--------------------------
 2 files changed, 59 insertions(+), 59 deletions(-)

diff --git a/Gemfile b/Gemfile
index abb8f9195f2..56f52f3e122 100644
--- a/Gemfile
+++ b/Gemfile
@@ -6,7 +6,7 @@ ruby '>= 2.5.0', '< 3.1.0'
 gem 'pkg-config', '~> 1.4'
 
 gem 'puma', '~> 5.3'
-gem 'rails', '~> 6.1.3'
+gem 'rails', '~> 6.1.4'
 gem 'sprockets', '~> 3.7.2'
 gem 'thor', '~> 1.1'
 gem 'rack', '~> 2.2.3'
diff --git a/Gemfile.lock b/Gemfile.lock
index 97d2e222295..b14dbc12919 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,40 +1,40 @@
 GEM
   remote: https://rubygems.org/
   specs:
-    actioncable (6.1.3.2)
-      actionpack (= 6.1.3.2)
-      activesupport (= 6.1.3.2)
+    actioncable (6.1.4)
+      actionpack (= 6.1.4)
+      activesupport (= 6.1.4)
       nio4r (~> 2.0)
       websocket-driver (>= 0.6.1)
-    actionmailbox (6.1.3.2)
-      actionpack (= 6.1.3.2)
-      activejob (= 6.1.3.2)
-      activerecord (= 6.1.3.2)
-      activestorage (= 6.1.3.2)
-      activesupport (= 6.1.3.2)
+    actionmailbox (6.1.4)
+      actionpack (= 6.1.4)
+      activejob (= 6.1.4)
+      activerecord (= 6.1.4)
+      activestorage (= 6.1.4)
+      activesupport (= 6.1.4)
       mail (>= 2.7.1)
-    actionmailer (6.1.3.2)
-      actionpack (= 6.1.3.2)
-      actionview (= 6.1.3.2)
-      activejob (= 6.1.3.2)
-      activesupport (= 6.1.3.2)
+    actionmailer (6.1.4)
+      actionpack (= 6.1.4)
+      actionview (= 6.1.4)
+      activejob (= 6.1.4)
+      activesupport (= 6.1.4)
       mail (~> 2.5, >= 2.5.4)
       rails-dom-testing (~> 2.0)
-    actionpack (6.1.3.2)
-      actionview (= 6.1.3.2)
-      activesupport (= 6.1.3.2)
+    actionpack (6.1.4)
+      actionview (= 6.1.4)
+      activesupport (= 6.1.4)
       rack (~> 2.0, >= 2.0.9)
       rack-test (>= 0.6.3)
       rails-dom-testing (~> 2.0)
       rails-html-sanitizer (~> 1.0, >= 1.2.0)
-    actiontext (6.1.3.2)
-      actionpack (= 6.1.3.2)
-      activerecord (= 6.1.3.2)
-      activestorage (= 6.1.3.2)
-      activesupport (= 6.1.3.2)
+    actiontext (6.1.4)
+      actionpack (= 6.1.4)
+      activerecord (= 6.1.4)
+      activestorage (= 6.1.4)
+      activesupport (= 6.1.4)
       nokogiri (>= 1.8.5)
-    actionview (6.1.3.2)
-      activesupport (= 6.1.3.2)
+    actionview (6.1.4)
+      activesupport (= 6.1.4)
       builder (~> 3.1)
       erubi (~> 1.4)
       rails-dom-testing (~> 2.0)
@@ -45,22 +45,22 @@ GEM
       case_transform (>= 0.2)
       jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
     active_record_query_trace (1.8)
-    activejob (6.1.3.2)
-      activesupport (= 6.1.3.2)
+    activejob (6.1.4)
+      activesupport (= 6.1.4)
       globalid (>= 0.3.6)
-    activemodel (6.1.3.2)
-      activesupport (= 6.1.3.2)
-    activerecord (6.1.3.2)
-      activemodel (= 6.1.3.2)
-      activesupport (= 6.1.3.2)
-    activestorage (6.1.3.2)
-      actionpack (= 6.1.3.2)
-      activejob (= 6.1.3.2)
-      activerecord (= 6.1.3.2)
-      activesupport (= 6.1.3.2)
+    activemodel (6.1.4)
+      activesupport (= 6.1.4)
+    activerecord (6.1.4)
+      activemodel (= 6.1.4)
+      activesupport (= 6.1.4)
+    activestorage (6.1.4)
+      actionpack (= 6.1.4)
+      activejob (= 6.1.4)
+      activerecord (= 6.1.4)
+      activesupport (= 6.1.4)
       marcel (~> 1.0.0)
-      mini_mime (~> 1.0.2)
-    activesupport (6.1.3.2)
+      mini_mime (>= 1.1.0)
+    activesupport (6.1.4)
       concurrent-ruby (~> 1.0, >= 1.0.2)
       i18n (>= 1.6, < 2)
       minitest (>= 5.1)
@@ -353,7 +353,7 @@ GEM
     mimemagic (0.3.10)
       nokogiri (~> 1)
       rake
-    mini_mime (1.0.3)
+    mini_mime (1.1.0)
     mini_portile2 (2.5.3)
     minitest (5.14.4)
     msgpack (1.4.2)
@@ -443,20 +443,20 @@ GEM
       rack
     rack-test (1.1.0)
       rack (>= 1.0, < 3)
-    rails (6.1.3.2)
-      actioncable (= 6.1.3.2)
-      actionmailbox (= 6.1.3.2)
-      actionmailer (= 6.1.3.2)
-      actionpack (= 6.1.3.2)
-      actiontext (= 6.1.3.2)
-      actionview (= 6.1.3.2)
-      activejob (= 6.1.3.2)
-      activemodel (= 6.1.3.2)
-      activerecord (= 6.1.3.2)
-      activestorage (= 6.1.3.2)
-      activesupport (= 6.1.3.2)
+    rails (6.1.4)
+      actioncable (= 6.1.4)
+      actionmailbox (= 6.1.4)
+      actionmailer (= 6.1.4)
+      actionpack (= 6.1.4)
+      actiontext (= 6.1.4)
+      actionview (= 6.1.4)
+      activejob (= 6.1.4)
+      activemodel (= 6.1.4)
+      activerecord (= 6.1.4)
+      activestorage (= 6.1.4)
+      activesupport (= 6.1.4)
       bundler (>= 1.15.0)
-      railties (= 6.1.3.2)
+      railties (= 6.1.4)
       sprockets-rails (>= 2.0.0)
     rails-controller-testing (1.0.5)
       actionpack (>= 5.0.1.rc1)
@@ -472,11 +472,11 @@ GEM
       railties (>= 6.0.0, < 7)
     rails-settings-cached (0.6.6)
       rails (>= 4.2.0)
-    railties (6.1.3.2)
-      actionpack (= 6.1.3.2)
-      activesupport (= 6.1.3.2)
+    railties (6.1.4)
+      actionpack (= 6.1.4)
+      activesupport (= 6.1.4)
       method_source
-      rake (>= 0.8.7)
+      rake (>= 0.13)
       thor (~> 1.0)
     rainbow (3.0.0)
     rake (13.0.3)
@@ -658,7 +658,7 @@ GEM
     webpush (0.3.8)
       hkdf (~> 0.2)
       jwt (~> 2.0)
-    websocket-driver (0.7.3)
+    websocket-driver (0.7.5)
       websocket-extensions (>= 0.1.0)
     websocket-extensions (0.1.5)
     wisper (2.0.1)
@@ -757,7 +757,7 @@ DEPENDENCIES
   rack (~> 2.2.3)
   rack-attack (~> 6.5)
   rack-cors (~> 1.1)
-  rails (~> 6.1.3)
+  rails (~> 6.1.4)
   rails-controller-testing (~> 1.0)
   rails-i18n (~> 6.0)
   rails-settings-cached (~> 0.6)

From 23c180b02bac27a4cd1d80349e186235999355cc Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 4 Jul 2021 05:07:46 +0900
Subject: [PATCH 03/19] Bump @rails/ujs from 6.1.3 to 6.1.4 (#16442)

Bumps [@rails/ujs](https://github.com/rails/rails) from 6.1.3 to 6.1.4.
- [Release notes](https://github.com/rails/rails/releases)
- [Commits](https://github.com/rails/rails/compare/v6.1.3...v6.1.4)

---
updated-dependencies:
- dependency-name: "@rails/ujs"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 package.json | 2 +-
 yarn.lock    | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/package.json b/package.json
index 538bd057fa8..967551ccca8 100644
--- a/package.json
+++ b/package.json
@@ -68,7 +68,7 @@
     "@babel/runtime": "^7.14.6",
     "@gamestdio/websocket": "^0.3.2",
     "@github/webauthn-json": "^0.5.7",
-    "@rails/ujs": "^6.1.3",
+    "@rails/ujs": "^6.1.4",
     "array-includes": "^3.1.3",
     "arrow-key-navigation": "^1.2.0",
     "autoprefixer": "^9.8.6",
diff --git a/yarn.lock b/yarn.lock
index 441fe5649dd..35066b1bc9b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1370,10 +1370,10 @@
   resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.11.tgz#aeb16f50649a91af79dbe36574b66d0f9e4d9f71"
   integrity sha512-3NsZsJIA/22P3QUyrEDNA2D133H4j224twJrdipXN38dpnIOzAbUDtOwkcJ5pXmn75w7LSQDjA4tO9dm1XlqlA==
 
-"@rails/ujs@^6.1.3":
-  version "6.1.3"
-  resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-6.1.3.tgz#90ef26caa0925492b1a3b1495db09cfbe49e745e"
-  integrity sha512-9mip5o+LVouWAqLMNJWhxda+D5uP+4RziNECgOGJlL6k3rc5SC/ljCHpV9Cym4i3oeGZkpZJ2tu4frCwt84kzQ==
+"@rails/ujs@^6.1.4":
+  version "6.1.4"
+  resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-6.1.4.tgz#093d5341595a02089ed309dec40f3c37da7b1b10"
+  integrity sha512-O3lEzL5DYbxppMdsFSw36e4BHIlfz/xusynwXGv3l2lhSlvah41qviRpsoAlKXxl37nZAqK+UUF5cnGGK45Mfw==
 
 "@sinonjs/commons@^1.7.0":
   version "1.8.1"

From 9f76be5e7d15bc0deefa1c9227246dce54277382 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 4 Jul 2021 11:59:37 +0900
Subject: [PATCH 04/19] Bump eslint from 7.29.0 to 7.30.0 (#16461)

Bumps [eslint](https://github.com/eslint/eslint) from 7.29.0 to 7.30.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v7.29.0...v7.30.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 package.json |  2 +-
 yarn.lock    | 23 +++++++++++++++++++----
 2 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/package.json b/package.json
index 967551ccca8..6cf923249ff 100644
--- a/package.json
+++ b/package.json
@@ -175,7 +175,7 @@
     "@testing-library/react": "^11.2.7",
     "babel-eslint": "^10.1.0",
     "babel-jest": "^27.0.6",
-    "eslint": "^7.29.0",
+    "eslint": "^7.30.0",
     "eslint-plugin-import": "~2.23.4",
     "eslint-plugin-jsx-a11y": "~6.4.1",
     "eslint-plugin-promise": "~5.1.0",
diff --git a/yarn.lock b/yarn.lock
index 35066b1bc9b..bdf2c6c339f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1129,6 +1129,20 @@
   resolved "https://registry.yarnpkg.com/@github/webauthn-json/-/webauthn-json-0.5.7.tgz#143bc67f6e0f75f8d188e565741507bb08c31214"
   integrity sha512-SUYsttDxFSvWvvJssJpwzjmRCqYfdfqC9VCmAHQYfdKCVelyJteCHo9/lK1CB72mx/jrl6cFNY08aua4J2jIyg==
 
+"@humanwhocodes/config-array@^0.5.0":
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9"
+  integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==
+  dependencies:
+    "@humanwhocodes/object-schema" "^1.2.0"
+    debug "^4.1.1"
+    minimatch "^3.0.4"
+
+"@humanwhocodes/object-schema@^1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf"
+  integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==
+
 "@istanbuljs/load-nyc-config@^1.0.0":
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
@@ -4436,13 +4450,14 @@ eslint@^2.7.0:
     text-table "~0.2.0"
     user-home "^2.0.0"
 
-eslint@^7.29.0:
-  version "7.29.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.29.0.tgz#ee2a7648f2e729485e4d0bd6383ec1deabc8b3c0"
-  integrity sha512-82G/JToB9qIy/ArBzIWG9xvvwL3R86AlCjtGw+A29OMZDqhTybz/MByORSukGxeI+YPCR4coYyITKk8BFH9nDA==
+eslint@^7.30.0:
+  version "7.30.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.30.0.tgz#6d34ab51aaa56112fd97166226c9a97f505474f8"
+  integrity sha512-VLqz80i3as3NdloY44BQSJpFw534L9Oh+6zJOUaViV4JPd+DaHwutqP7tcpkW3YiXbK6s05RZl7yl7cQn+lijg==
   dependencies:
     "@babel/code-frame" "7.12.11"
     "@eslint/eslintrc" "^0.4.2"
+    "@humanwhocodes/config-array" "^0.5.0"
     ajv "^6.10.0"
     chalk "^4.0.0"
     cross-spawn "^7.0.2"

From 1381e0e1d9f27bd108d8b9349896f10ffe996cb2 Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Mon, 5 Jul 2021 19:16:06 +0200
Subject: [PATCH 05/19] Fix pop-in player display when poster has long username
 or handle (#16468)

---
 app/javascript/styles/mastodon/components.scss | 1 +
 1 file changed, 1 insertion(+)

diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 0579c3c6f44..35c10b096d0 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -7291,6 +7291,7 @@ noscript {
     &__account {
       display: flex;
       text-decoration: none;
+      overflow: hidden;
     }
 
     .account__avatar {

From 67226acf7e8e91a44dc9599aecb17dcd73a72ea9 Mon Sep 17 00:00:00 2001
From: Ikko Ashimine <eltociear@gmail.com>
Date: Tue, 6 Jul 2021 02:16:21 +0900
Subject: [PATCH 06/19] Fix typo in tag_feed_spec.rb (#16466)

existant -> existent
---
 spec/models/tag_feed_spec.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/spec/models/tag_feed_spec.rb b/spec/models/tag_feed_spec.rb
index 17d88eb9993..819fe376577 100644
--- a/spec/models/tag_feed_spec.rb
+++ b/spec/models/tag_feed_spec.rb
@@ -37,7 +37,7 @@ describe TagFeed, type: :service do
       expect(results).to     include both
     end
 
-    it 'handles being passed non existant tag names' do
+    it 'handles being passed non existent tag names' do
       results = described_class.new(tag1, nil, any: ['wark']).get(20)
       expect(results).to     include status1
       expect(results).to_not include status2

From 8af7f3b063a9d082b95e087bc7bf6feac7d13631 Mon Sep 17 00:00:00 2001
From: Akihiko Odaki <akihiko.odaki@gmail.com>
Date: Tue, 6 Jul 2021 02:16:35 +0900
Subject: [PATCH 07/19] Preload libjemalloc.so for long-running Ruby (#16462)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Always mark jemalloc needed if jemalloc is enabled by akihikodaki · Pull Request #4627 · ruby/ruby
https://github.com/ruby/ruby/pull/4627
> Symbols exported by jemalloc is referred by the shared library but not
> by the executables when building Ruby as a shared library with
> jemalloc. It causes shared libraries such as the GNU C++ library
> occasionally rely on the memory allocator provided by the standard C
> library. Worse, the resolved symbols can later be replaced with
> jemalloc, and jemalloc may see pointers from the standard C library,
> which results in various failures.
> e.g. https://github.com/tootsuite/mastodon/issues/15751

As a workaround, do not rely on jemalloc enablement of Ruby, and
preload libjemalloc.so instead.
---
 dist/mastodon-sidekiq.service | 1 +
 dist/mastodon-web.service     | 1 +
 2 files changed, 2 insertions(+)

diff --git a/dist/mastodon-sidekiq.service b/dist/mastodon-sidekiq.service
index 9dd21b8a005..35b121cd77e 100644
--- a/dist/mastodon-sidekiq.service
+++ b/dist/mastodon-sidekiq.service
@@ -9,6 +9,7 @@ WorkingDirectory=/home/mastodon/live
 Environment="RAILS_ENV=production"
 Environment="DB_POOL=25"
 Environment="MALLOC_ARENA_MAX=2"
+Environment="LD_PRELOAD=libjemalloc.so"
 ExecStart=/home/mastodon/.rbenv/shims/bundle exec sidekiq -c 25
 TimeoutSec=15
 Restart=always
diff --git a/dist/mastodon-web.service b/dist/mastodon-web.service
index c106a48608c..f41efd2b095 100644
--- a/dist/mastodon-web.service
+++ b/dist/mastodon-web.service
@@ -8,6 +8,7 @@ User=mastodon
 WorkingDirectory=/home/mastodon/live
 Environment="RAILS_ENV=production"
 Environment="PORT=3000"
+Environment="LD_PRELOAD=libjemalloc.so"
 ExecStart=/home/mastodon/.rbenv/shims/bundle exec puma -C config/puma.rb
 ExecReload=/bin/kill -SIGUSR1 $MAINPID
 TimeoutSec=15

From 225c6582d17b8e744c279e254d4e96062afb7a3b Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Wed, 7 Jul 2021 21:12:43 +0200
Subject: [PATCH 08/19] Add tests for BootstrapTimelineService (#16476)

---
 .../bootstrap_timeline_service_spec.rb        | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/spec/services/bootstrap_timeline_service_spec.rb b/spec/services/bootstrap_timeline_service_spec.rb
index 880ca4f0dbd..16f3e9962e4 100644
--- a/spec/services/bootstrap_timeline_service_spec.rb
+++ b/spec/services/bootstrap_timeline_service_spec.rb
@@ -1,4 +1,37 @@
 require 'rails_helper'
 
 RSpec.describe BootstrapTimelineService, type: :service do
+  subject { BootstrapTimelineService.new }
+
+  context 'when the new user has registered from an invite' do
+    let(:service)    { double }
+    let(:autofollow) { false }
+    let(:inviter)    { Fabricate(:user, confirmed_at: 2.days.ago) }
+    let(:invite)     { Fabricate(:invite, user: inviter, max_uses: nil, expires_at: 1.hour.from_now, autofollow: autofollow) }
+    let(:new_user)   { Fabricate(:user, invite_code: invite.code) }
+
+    before do
+      allow(FollowService).to receive(:new).and_return(service)
+      allow(service).to receive(:call)
+    end
+
+    context 'when the invite has auto-follow enabled' do
+      let(:autofollow) { true }
+
+      it 'calls FollowService to follow the inviter' do
+        subject.call(new_user.account)
+        expect(service).to have_received(:call).with(new_user.account, inviter.account)
+      end
+    end
+
+    context 'when the invite does not have auto-follow enable' do
+      let(:autofollow) { false }
+
+      it 'calls FollowService to follow the inviter' do
+        subject.call(new_user.account)
+        expect(service).to_not have_received(:call)
+      end
+    end
+
+  end
 end

From e2844b7e58e1ebdf666c5fecc97a75620da7bddf Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Wed, 7 Jul 2021 21:13:08 +0200
Subject: [PATCH 09/19] Change number_to_human calls to always use 3-digits
 precision (#16469)

Fixes #16435
---
 app/helpers/accounts_helper.rb                   |  6 +++---
 app/helpers/application_helper.rb                | 11 +++++++++++
 app/views/about/more.html.haml                   |  4 ++--
 app/views/about/show.html.haml                   |  4 ++--
 app/views/accounts/_header.html.haml             | 10 +++++-----
 app/views/accounts/show.html.haml                |  2 +-
 app/views/admin/dashboard/index.html.haml        | 16 ++++++++--------
 .../follow_recommendations/_account.html.haml    |  4 ++--
 app/views/admin/instances/_instance.html.haml    |  2 +-
 app/views/admin/tags/_tag.html.haml              |  2 +-
 app/views/directories/index.html.haml            |  4 ++--
 app/views/relationships/_account.html.haml       |  4 ++--
 app/views/settings/featured_tags/index.html.haml |  2 +-
 app/views/statuses/_detailed_status.html.haml    |  6 +++---
 14 files changed, 44 insertions(+), 33 deletions(-)

diff --git a/app/helpers/accounts_helper.rb b/app/helpers/accounts_helper.rb
index 134217734e5..db3da2b0507 100644
--- a/app/helpers/accounts_helper.rb
+++ b/app/helpers/accounts_helper.rb
@@ -80,17 +80,17 @@ module AccountsHelper
   def account_description(account)
     prepend_str = [
       [
-        number_to_human(account.statuses_count, strip_insignificant_zeros: true),
+        number_to_human(account.statuses_count, precision: 3, strip_insignificant_zeros: true),
         I18n.t('accounts.posts', count: account.statuses_count),
       ].join(' '),
 
       [
-        number_to_human(account.following_count, strip_insignificant_zeros: true),
+        number_to_human(account.following_count, precision: 3, strip_insignificant_zeros: true),
         I18n.t('accounts.following', count: account.following_count),
       ].join(' '),
 
       [
-        number_to_human(account.followers_count, strip_insignificant_zeros: true),
+        number_to_human(account.followers_count, precision: 3, strip_insignificant_zeros: true),
         I18n.t('accounts.followers', count: account.followers_count),
       ].join(' '),
     ].join(', ')
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index bf5742d34f2..a39e8e5bf85 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -14,6 +14,17 @@ module ApplicationHelper
     ku
   ).freeze
 
+  def friendly_number_to_human(number, **options)
+    # By default, the number of precision digits used by number_to_human
+    # is looked up from the locales definition, and rails-i18n comes with
+    # values that don't seem to make much sense for many languages, so
+    # override these values with a default of 3 digits of precision.
+    options[:precision] = 3
+    options[:strip_insignificant_zeros] = true
+
+    number_to_human(number, **options)
+  end
+
   def active_nav_class(*paths)
     paths.any? { |path| current_page?(path) } ? 'active' : ''
   end
diff --git a/app/views/about/more.html.haml b/app/views/about/more.html.haml
index cd93b0f5801..f4429622c57 100644
--- a/app/views/about/more.html.haml
+++ b/app/views/about/more.html.haml
@@ -17,11 +17,11 @@
         .row__information-board
           .information-board__section
             %span= t 'about.user_count_before'
-            %strong= number_to_human @instance_presenter.user_count, strip_insignificant_zeros: true
+            %strong= friendly_number_to_human @instance_presenter.user_count
             %span= t 'about.user_count_after', count: @instance_presenter.user_count
           .information-board__section
             %span= t 'about.status_count_before'
-            %strong= number_to_human @instance_presenter.status_count, strip_insignificant_zeros: true
+            %strong= friendly_number_to_human @instance_presenter.status_count
             %span= t 'about.status_count_after', count: @instance_presenter.status_count
         .row__mascot
           .landing-page__mascot
diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml
index 565c4ed593e..6ae9e6ae070 100644
--- a/app/views/about/show.html.haml
+++ b/app/views/about/show.html.haml
@@ -70,10 +70,10 @@
 
             .hero-widget__counters__wrapper
               .hero-widget__counter
-                %strong= number_to_human @instance_presenter.user_count, strip_insignificant_zeros: true
+                %strong= friendly_number_to_human @instance_presenter.user_count
                 %span= t 'about.user_count_after', count: @instance_presenter.user_count
               .hero-widget__counter
-                %strong= number_to_human @instance_presenter.active_user_count, strip_insignificant_zeros: true
+                %strong= friendly_number_to_human @instance_presenter.active_user_count
                 %span
                   = t 'about.active_count_after'
                   %abbr{ title: t('about.active_footnote') } *
diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml
index cae5a5ac92b..d9966723a74 100644
--- a/app/views/accounts/_header.html.haml
+++ b/app/views/accounts/_header.html.haml
@@ -15,17 +15,17 @@
         .details-counters
           .counter{ class: active_nav_class(short_account_url(account), short_account_with_replies_url(account), short_account_media_url(account)) }
             = link_to short_account_url(account), class: 'u-url u-uid', title: number_with_delimiter(account.statuses_count) do
-              %span.counter-number= number_to_human account.statuses_count, strip_insignificant_zeros: true
+              %span.counter-number= friendly_number_to_human account.statuses_count
               %span.counter-label= t('accounts.posts', count: account.statuses_count)
 
           .counter{ class: active_nav_class(account_following_index_url(account)) }
             = link_to account_following_index_url(account), title: number_with_delimiter(account.following_count) do
-              %span.counter-number= number_to_human account.following_count, strip_insignificant_zeros: true
+              %span.counter-number= friendly_number_to_human account.following_count
               %span.counter-label= t('accounts.following', count: account.following_count)
 
           .counter{ class: active_nav_class(account_followers_url(account)) }
             = link_to account_followers_url(account), title: number_with_delimiter(account.followers_count) do
-              %span.counter-number= number_to_human account.followers_count, strip_insignificant_zeros: true
+              %span.counter-number= friendly_number_to_human account.followers_count
               %span.counter-label= t('accounts.followers', count: account.followers_count)
         .spacer
         .public-account-header__tabs__tabs__buttons
@@ -36,8 +36,8 @@
 
       .public-account-header__extra__links
         = link_to account_following_index_url(account) do
-          %strong= number_to_human account.following_count, strip_insignificant_zeros: true
+          %strong= friendly_number_to_human account.following_count
           = t('accounts.following', count: account.following_count)
         = link_to account_followers_url(account) do
-          %strong= number_to_human account.followers_count, strip_insignificant_zeros: true
+          %strong= friendly_number_to_human account.followers_count
           = t('accounts.followers', count: account.followers_count)
diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml
index 1a81b96f6c9..72e9c661110 100644
--- a/app/views/accounts/show.html.haml
+++ b/app/views/accounts/show.html.haml
@@ -81,6 +81,6 @@
                   = t('accounts.nothing_here')
                 - else
                   %time.formatted{ datetime: featured_tag.last_status_at.iso8601, title: l(featured_tag.last_status_at) }= l featured_tag.last_status_at
-            .trends__item__current= number_to_human featured_tag.statuses_count, strip_insignificant_zeros: true
+            .trends__item__current= friendly_number_to_human featured_tag.statuses_count
 
     = render 'application/sidebar'
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index e8a2b46fd77..05b585ab6d1 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -13,42 +13,42 @@
   %div
     = link_to admin_accounts_url(local: 1, recent: 1) do
       .dashboard__counters__num{ title: number_with_delimiter(@users_count, strip_insignificant_zeros: true) }
-        = number_to_human @users_count, strip_insignificant_zeros: true
+        = friendly_number_to_human @users_count
       .dashboard__counters__label= t 'admin.dashboard.total_users'
   %div
     %div
       .dashboard__counters__num{ title: number_with_delimiter(@registrations_week, strip_insignificant_zeros: true) }
-        = number_to_human @registrations_week, strip_insignificant_zeros: true
+        = friendly_number_to_human @registrations_week
       .dashboard__counters__label= t 'admin.dashboard.week_users_new'
   %div
     %div
       .dashboard__counters__num{ title: number_with_delimiter(@logins_week, strip_insignificant_zeros: true) }
-        = number_to_human @logins_week, strip_insignificant_zeros: true
+        = friendly_number_to_human @logins_week
       .dashboard__counters__label= t 'admin.dashboard.week_users_active'
   %div
     = link_to admin_pending_accounts_path do
       .dashboard__counters__num{ title: number_with_delimiter(@pending_users_count, strip_insignificant_zeros: true) }
-        = number_to_human @pending_users_count, strip_insignificant_zeros: true
+        = friendly_number_to_human @pending_users_count
       .dashboard__counters__label= t 'admin.dashboard.pending_users'
   %div
     = link_to admin_reports_url do
       .dashboard__counters__num{ title: number_with_delimiter(@reports_count, strip_insignificant_zeros: true) }
-        = number_to_human @reports_count, strip_insignificant_zeros: true
+        = friendly_number_to_human @reports_count
       .dashboard__counters__label= t 'admin.dashboard.open_reports'
   %div
     = link_to admin_tags_path(pending_review: '1') do
       .dashboard__counters__num{ title: number_with_delimiter(@pending_tags_count, strip_insignificant_zeros: true) }
-        = number_to_human @pending_tags_count, strip_insignificant_zeros: true
+        = friendly_number_to_human @pending_tags_count
       .dashboard__counters__label= t 'admin.dashboard.pending_tags'
   %div
     %div
       .dashboard__counters__num{ title: number_with_delimiter(@interactions_week, strip_insignificant_zeros: true) }
-        = number_to_human @interactions_week, strip_insignificant_zeros: true
+        = friendly_number_to_human @interactions_week
       .dashboard__counters__label= t 'admin.dashboard.week_interactions'
   %div
     = link_to sidekiq_url do
       .dashboard__counters__num{ title: number_with_delimiter(@queue_backlog, strip_insignificant_zeros: true) }
-        = number_to_human @queue_backlog, strip_insignificant_zeros: true
+        = friendly_number_to_human @queue_backlog
       .dashboard__counters__label= t 'admin.dashboard.backlog'
 
 .dashboard__widgets
diff --git a/app/views/admin/follow_recommendations/_account.html.haml b/app/views/admin/follow_recommendations/_account.html.haml
index af5a4aaf7d0..00196dd01a8 100644
--- a/app/views/admin/follow_recommendations/_account.html.haml
+++ b/app/views/admin/follow_recommendations/_account.html.haml
@@ -7,10 +7,10 @@
         %tr
           %td= account_link_to account
           %td.accounts-table__count.optional
-            = number_to_human account.statuses_count, strip_insignificant_zeros: true
+            = friendly_number_to_human account.statuses_count
             %small= t('accounts.posts', count: account.statuses_count).downcase
           %td.accounts-table__count.optional
-            = number_to_human account.followers_count, strip_insignificant_zeros: true
+            = friendly_number_to_human account.followers_count
             %small= t('accounts.followers', count: account.followers_count).downcase
           %td.accounts-table__count
             - if account.last_status_at.present?
diff --git a/app/views/admin/instances/_instance.html.haml b/app/views/admin/instances/_instance.html.haml
index 990cf9ec88d..dc81007ac90 100644
--- a/app/views/admin/instances/_instance.html.haml
+++ b/app/views/admin/instances/_instance.html.haml
@@ -30,4 +30,4 @@
           = ' / '
           %span.negative-hint
             = t('admin.instances.delivery.unavailable_message')
-    .trends__item__current{ title: t('admin.instances.known_accounts', count: instance.accounts_count) }= number_to_human instance.accounts_count, strip_insignificant_zeros: true
+    .trends__item__current{ title: t('admin.instances.known_accounts', count: instance.accounts_count) }= friendly_number_to_human instance.accounts_count
diff --git a/app/views/admin/tags/_tag.html.haml b/app/views/admin/tags/_tag.html.haml
index adf4ca7b2f8..ac0c728165a 100644
--- a/app/views/admin/tags/_tag.html.haml
+++ b/app/views/admin/tags/_tag.html.haml
@@ -16,4 +16,4 @@
             = fa_icon 'fire fw'
             = t('admin.tags.trending_right_now')
 
-      .trends__item__current= number_to_human tag.history.first[:uses], strip_insignificant_zeros: true
+      .trends__item__current= friendly_number_to_human tag.history.first[:uses]
diff --git a/app/views/directories/index.html.haml b/app/views/directories/index.html.haml
index 7975ee9997b..04639e32c22 100644
--- a/app/views/directories/index.html.haml
+++ b/app/views/directories/index.html.haml
@@ -39,10 +39,10 @@
 
         .directory__card__extra
           .accounts-table__count
-            = number_to_human account.statuses_count, strip_insignificant_zeros: true
+            = friendly_number_to_human account.statuses_count
             %small= t('accounts.posts', count: account.statuses_count).downcase
           .accounts-table__count
-            = number_to_human account.followers_count, strip_insignificant_zeros: true
+            = friendly_number_to_human account.followers_count
             %small= t('accounts.followers', count: account.followers_count).downcase
           .accounts-table__count
             - if account.last_status_at.present?
diff --git a/app/views/relationships/_account.html.haml b/app/views/relationships/_account.html.haml
index f521aff2258..0fa3cffb552 100644
--- a/app/views/relationships/_account.html.haml
+++ b/app/views/relationships/_account.html.haml
@@ -9,10 +9,10 @@
             = interrelationships_icon(@relationships, account.id)
           %td= account_link_to account
           %td.accounts-table__count.optional
-            = number_to_human account.statuses_count, strip_insignificant_zeros: true
+            = friendly_number_to_human account.statuses_count
             %small= t('accounts.posts', count: account.statuses_count).downcase
           %td.accounts-table__count.optional
-            = number_to_human account.followers_count, strip_insignificant_zeros: true
+            = friendly_number_to_human account.followers_count
             %small= t('accounts.followers', count: account.followers_count).downcase
           %td.accounts-table__count
             - if account.last_status_at.present?
diff --git a/app/views/settings/featured_tags/index.html.haml b/app/views/settings/featured_tags/index.html.haml
index 297379893ab..65de7f8f30d 100644
--- a/app/views/settings/featured_tags/index.html.haml
+++ b/app/views/settings/featured_tags/index.html.haml
@@ -28,4 +28,4 @@
           - else
             %time{ datetime: featured_tag.last_status_at.iso8601, title: l(featured_tag.last_status_at) }= l featured_tag.last_status_at
           = table_link_to 'trash', t('filters.index.delete'), settings_featured_tag_path(featured_tag), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') }
-      .trends__item__current= number_to_human featured_tag.statuses_count, strip_insignificant_zeros: true
+      .trends__item__current= friendly_number_to_human featured_tag.statuses_count
diff --git a/app/views/statuses/_detailed_status.html.haml b/app/views/statuses/_detailed_status.html.haml
index daf164949c3..6b3b8130672 100644
--- a/app/views/statuses/_detailed_status.html.haml
+++ b/app/views/statuses/_detailed_status.html.haml
@@ -55,18 +55,18 @@
         = fa_icon('reply')
       - else
         = fa_icon('reply-all')
-      %span.detailed-status__reblogs>= number_to_human status.replies_count, strip_insignificant_zeros: true
+      %span.detailed-status__reblogs>= friendly_number_to_human status.replies_count
       = " "
     ·
     - if status.public_visibility? || status.unlisted_visibility?
       = link_to remote_interaction_path(status, type: :reblog), class: 'modal-button detailed-status__link' do
         = fa_icon('retweet')
-        %span.detailed-status__reblogs>= number_to_human status.reblogs_count, strip_insignificant_zeros: true
+        %span.detailed-status__reblogs>= friendly_number_to_human status.reblogs_count
         = " "
       ·
     = link_to remote_interaction_path(status, type: :favourite), class: 'modal-button detailed-status__link' do
       = fa_icon('star')
-      %span.detailed-status__favorites>= number_to_human status.favourites_count, strip_insignificant_zeros: true
+      %span.detailed-status__favorites>= friendly_number_to_human status.favourites_count
       = " "
 
     - if user_signed_in?

From 8569126c2e52cdde6dba2dd86c9a8e802b0c5e4c Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Wed, 7 Jul 2021 21:13:30 +0200
Subject: [PATCH 10/19] Fix upgrade path from 3.4.0 (#16465)

3.4.1 dropped account_stats.lock_version, but in a way breaking the usual
upgrade path by requiring services to be reloaded after the post-migrations.

Indeed, `self.locking_column = nil` was not enough for Rails to ignore the
`lock_version` column when preparing statements on application load, resulting
in some ActiveRecord queries (typically those involving
`includes(:account_stat)`) erroring out with:

  ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR:  column account_stats.lock_version does not exist
---
 app/models/account_stat.rb | 1 +
 1 file changed, 1 insertion(+)

diff --git a/app/models/account_stat.rb b/app/models/account_stat.rb
index 44da4f0d00c..e702fa4a44f 100644
--- a/app/models/account_stat.rb
+++ b/app/models/account_stat.rb
@@ -15,6 +15,7 @@
 
 class AccountStat < ApplicationRecord
   self.locking_column = nil
+  self.ignored_columns = %w(lock_version)
 
   belongs_to :account, inverse_of: :account_stat
 

From 2e0eac71ddf0f5b61b6e395f57b7bc3f805cbf87 Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Wed, 7 Jul 2021 21:17:00 +0200
Subject: [PATCH 11/19] Add --by-uri option to `tootctl domains purge` (#16434)

Fixes #16410
---
 lib/mastodon/domains_cli.rb | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/lib/mastodon/domains_cli.rb b/lib/mastodon/domains_cli.rb
index 4ebd8a1e2cd..a7c78c4a7aa 100644
--- a/lib/mastodon/domains_cli.rb
+++ b/lib/mastodon/domains_cli.rb
@@ -17,6 +17,7 @@ module Mastodon
     option :verbose, type: :boolean, aliases: [:v]
     option :dry_run, type: :boolean
     option :limited_federation_mode, type: :boolean
+    option :by_uri, type: :boolean
     desc 'purge [DOMAIN...]', 'Remove accounts from a DOMAIN without a trace'
     long_desc <<-LONG_DESC
       Remove all accounts from a given DOMAIN without leaving behind any
@@ -26,6 +27,12 @@ module Mastodon
       When the --limited-federation-mode option is given, instead of purging accounts
       from a single domain, all accounts from domains that have not been explicitly allowed
       are removed from the database.
+
+      When the --by-uri option is given, DOMAIN is used to match the domain part of actor
+      URIs rather than the domain part of the webfinger handle. For instance, an account
+      that has the handle `foo@bar.com` but whose profile is at the URL
+      `https://mastodon-bar.com/users/foo`, would be purged by either
+      `tootctl domains purge bar.com` or `tootctl domains purge --by-uri mastodon-bar.com`.
     LONG_DESC
     def purge(*domains)
       dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
@@ -34,7 +41,11 @@ module Mastodon
         if options[:limited_federation_mode]
           Account.remote.where.not(domain: DomainAllow.pluck(:domain))
         elsif !domains.empty?
-          Account.remote.where(domain: domains)
+          if options[:by_uri]
+            domains.map { |domain| Account.remote.where(Account.arel_table[:uri].matches("https://#{domain}/%", false, true)) }.reduce(:or)
+          else
+            Account.remote.where(domain: domains)
+          end
         else
           say('No domain(s) given', :red)
           exit(1)

From 771c9d4ba87a388dc306c58139d11bf510680c98 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Thu, 8 Jul 2021 05:31:28 +0200
Subject: [PATCH 12/19] Add ability to skip sign-in token authentication for
 specific users (#16427)

Remove "active within last two weeks" exception for sign in token requirement

Change admin reset password to lock access until the password is reset
---
 app/controllers/admin/resets_controller.rb    |  4 +-
 ...ign_in_token_authentications_controller.rb | 27 ++++++++++++
 .../two_factor_authentications_controller.rb  |  2 +-
 app/models/user.rb                            | 25 ++++++++++-
 app/policies/user_policy.rb                   |  8 ++++
 app/views/admin/accounts/show.html.haml       | 24 +++++++++--
 config/locales/en.yml                         | 42 ++++++++++++-------
 config/routes.rb                              |  1 +
 ...1221010_add_skip_sign_in_token_to_users.rb |  5 +++
 db/schema.rb                                  |  1 +
 lib/mastodon/accounts_cli.rb                  | 15 +++++--
 .../admin/resets_controller_spec.rb           |  2 +-
 ..._factor_authentications_controller_spec.rb |  8 ++--
 spec/models/user_spec.rb                      | 28 +++++++++++++
 14 files changed, 160 insertions(+), 32 deletions(-)
 create mode 100644 app/controllers/admin/sign_in_token_authentications_controller.rb
 create mode 100644 db/migrate/20210621221010_add_skip_sign_in_token_to_users.rb

diff --git a/app/controllers/admin/resets_controller.rb b/app/controllers/admin/resets_controller.rb
index db8f61d64cb..7962b7a58c6 100644
--- a/app/controllers/admin/resets_controller.rb
+++ b/app/controllers/admin/resets_controller.rb
@@ -6,9 +6,9 @@ module Admin
 
     def create
       authorize @user, :reset_password?
-      @user.send_reset_password_instructions
+      @user.reset_password!
       log_action :reset_password, @user
-      redirect_to admin_accounts_path
+      redirect_to admin_account_path(@user.account_id)
     end
   end
 end
diff --git a/app/controllers/admin/sign_in_token_authentications_controller.rb b/app/controllers/admin/sign_in_token_authentications_controller.rb
new file mode 100644
index 00000000000..e620ab2926d
--- /dev/null
+++ b/app/controllers/admin/sign_in_token_authentications_controller.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Admin
+  class SignInTokenAuthenticationsController < BaseController
+    before_action :set_target_user
+
+    def create
+      authorize @user, :enable_sign_in_token_auth?
+      @user.update(skip_sign_in_token: false)
+      log_action :enable_sign_in_token_auth, @user
+      redirect_to admin_account_path(@user.account_id)
+    end
+
+    def destroy
+      authorize @user, :disable_sign_in_token_auth?
+      @user.update(skip_sign_in_token: true)
+      log_action :disable_sign_in_token_auth, @user
+      redirect_to admin_account_path(@user.account_id)
+    end
+
+    private
+
+    def set_target_user
+      @user = User.find(params[:user_id])
+    end
+  end
+end
diff --git a/app/controllers/admin/two_factor_authentications_controller.rb b/app/controllers/admin/two_factor_authentications_controller.rb
index 0652c3a7a4a..f7fb7eb8fe3 100644
--- a/app/controllers/admin/two_factor_authentications_controller.rb
+++ b/app/controllers/admin/two_factor_authentications_controller.rb
@@ -9,7 +9,7 @@ module Admin
       @user.disable_two_factor!
       log_action :disable_2fa, @user
       UserMailer.two_factor_disabled(@user).deliver_later!
-      redirect_to admin_accounts_path
+      redirect_to admin_account_path(@user.account_id)
     end
 
     private
diff --git a/app/models/user.rb b/app/models/user.rb
index 4973c68b61b..4059c96b569 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -42,6 +42,7 @@
 #  sign_in_token_sent_at     :datetime
 #  webauthn_id               :string
 #  sign_up_ip                :inet
+#  skip_sign_in_token        :boolean
 #
 
 class User < ApplicationRecord
@@ -200,7 +201,7 @@ class User < ApplicationRecord
   end
 
   def suspicious_sign_in?(ip)
-    !otp_required_for_login? && current_sign_in_at.present? && current_sign_in_at < 2.weeks.ago && !recent_ip?(ip)
+    !otp_required_for_login? && !skip_sign_in_token? && current_sign_in_at.present? && !recent_ip?(ip)
   end
 
   def functional?
@@ -329,12 +330,32 @@ class User < ApplicationRecord
     super
   end
 
-  def reset_password!(new_password, new_password_confirmation)
+  def reset_password(new_password, new_password_confirmation)
     return false if encrypted_password.blank?
 
     super
   end
 
+  def reset_password!
+    # First, change password to something random, invalidate the remember-me token,
+    # and deactivate all sessions
+    transaction do
+      update(remember_token: nil, remember_created_at: nil, password: SecureRandom.hex)
+      session_activations.destroy_all
+    end
+
+    # Then, remove all authorized applications and connected push subscriptions
+    Doorkeeper::AccessGrant.by_resource_owner(self).in_batches.update_all(revoked_at: Time.now.utc)
+
+    Doorkeeper::AccessToken.by_resource_owner(self).in_batches do |batch|
+      batch.update_all(revoked_at: Time.now.utc)
+      Web::PushSubscription.where(access_token_id: batch).delete_all
+    end
+
+    # Finally, send a reset password prompt to the user
+    send_reset_password_instructions
+  end
+
   def show_all_media?
     setting_display_media == 'show_all'
   end
diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb
index d832bff75d4..6695a0ddf89 100644
--- a/app/policies/user_policy.rb
+++ b/app/policies/user_policy.rb
@@ -13,6 +13,14 @@ class UserPolicy < ApplicationPolicy
     admin? && !record.staff?
   end
 
+  def disable_sign_in_token_auth?
+    staff?
+  end
+
+  def enable_sign_in_token_auth?
+    staff?
+  end
+
   def confirm?
     staff? && !record.confirmed?
   end
diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml
index 27e1f80a722..66eb4934275 100644
--- a/app/views/admin/accounts/show.html.haml
+++ b/app/views/admin/accounts/show.html.haml
@@ -129,6 +129,27 @@
               - else
                 = t('admin.accounts.confirming')
             %td= table_link_to 'refresh', t('admin.accounts.resend_confirmation.send'), resend_admin_account_confirmation_path(@account.id), method: :post if can?(:confirm, @account.user)
+          %tr
+            %th{ rowspan: can?(:reset_password, @account.user) ? 2 : 1 }= t('admin.accounts.security')
+            %td{ rowspan: can?(:reset_password, @account.user) ? 2 : 1 }
+              - if @account.user&.two_factor_enabled?
+                = t 'admin.accounts.security_measures.password_and_2fa'
+              - elsif @account.user&.skip_sign_in_token?
+                = t 'admin.accounts.security_measures.only_password'
+              - else
+                = t 'admin.accounts.security_measures.password_and_sign_in_token'
+            %td
+              - if @account.user&.two_factor_enabled?
+                = table_link_to 'unlock', t('admin.accounts.disable_two_factor_authentication'), admin_user_two_factor_authentication_path(@account.user.id), method: :delete if can?(:disable_2fa, @account.user)
+              - elsif @account.user&.skip_sign_in_token?
+                = table_link_to 'lock', t('admin.accounts.enable_sign_in_token_auth'), admin_user_sign_in_token_authentication_path(@account.user.id), method: :post if can?(:enable_sign_in_token_auth, @account.user)
+              - else
+                = table_link_to 'unlock', t('admin.accounts.disable_sign_in_token_auth'), admin_user_sign_in_token_authentication_path(@account.user.id), method: :delete if can?(:disable_sign_in_token_auth, @account.user)
+
+          - if can?(:reset_password, @account.user)
+            %tr
+              %td
+                = table_link_to 'key', t('admin.accounts.reset_password'), admin_account_reset_path(@account.id), method: :create, data: { confirm: t('admin.accounts.are_you_sure') }
 
           %tr
             %th= t('simple_form.labels.defaults.locale')
@@ -221,9 +242,6 @@
 
       %div
         - if @account.local?
-          = link_to t('admin.accounts.reset_password'), admin_account_reset_path(@account.id), method: :create, class: 'button' if can?(:reset_password, @account.user)
-          - if @account.user&.otp_required_for_login?
-            = link_to t('admin.accounts.disable_two_factor_authentication'), admin_user_two_factor_authentication_path(@account.user.id), method: :delete, class: 'button' if can?(:disable_2fa, @account.user)
           - if !@account.memorial? && @account.user_approved?
             = link_to t('admin.accounts.memorialize'), memorialize_admin_account_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive' if can?(:memorialize, @account)
         - else
diff --git a/config/locales/en.yml b/config/locales/en.yml
index cdb2e3df7f3..51764a0e169 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -44,7 +44,7 @@ en:
       rejecting_media: 'Media files from these servers will not be processed or stored, and no thumbnails will be displayed, requiring manual click-through to the original file:'
       rejecting_media_title: Filtered media
       silenced: 'Posts from these servers will be hidden in public timelines and conversations, and no notifications will be generated from their users interactions, unless you are following them:'
-      silenced_title: Silenced servers
+      silenced_title: Limited servers
       suspended: 'No data from these servers will be processed, stored or exchanged, making any interaction or communication with users from these servers impossible:'
       suspended_title: Suspended servers
     unavailable_content_html: Mastodon generally allows you to view content from and interact with users from any other server in the fediverse. These are the exceptions that have been made on this particular server.
@@ -119,6 +119,7 @@ en:
       demote: Demote
       destroyed_msg: "%{username}'s data is now queued to be deleted imminently"
       disable: Freeze
+      disable_sign_in_token_auth: Disable e-mail token authentication
       disable_two_factor_authentication: Disable 2FA
       disabled: Frozen
       display_name: Display name
@@ -127,6 +128,7 @@ en:
       email: Email
       email_status: Email status
       enable: Unfreeze
+      enable_sign_in_token_auth: Enable e-mail token authentication
       enabled: Enabled
       enabled_msg: Successfully unfroze %{username}'s account
       followers: Followers
@@ -151,7 +153,7 @@ en:
         active: Active
         all: All
         pending: Pending
-        silenced: Silenced
+        silenced: Limited
         suspended: Suspended
         title: Moderation
       moderation_notes: Moderation notes
@@ -191,8 +193,12 @@ en:
       search: Search
       search_same_email_domain: Other users with the same e-mail domain
       search_same_ip: Other users with the same IP
-      sensitive: Sensitive
-      sensitized: marked as sensitive
+      security_measures:
+        only_password: Only password
+        password_and_2fa: Password and 2FA
+        password_and_sign_in_token: Password and e-mail token
+      sensitive: Force-sensitive
+      sensitized: Marked as sensitive
       shared_inbox_url: Shared inbox URL
       show:
         created_reports: Made reports
@@ -207,10 +213,10 @@ en:
       time_in_queue: Waiting in queue %{time}
       title: Accounts
       unconfirmed_email: Unconfirmed email
-      undo_sensitized: Undo sensitive
-      undo_silenced: Undo silence
+      undo_sensitized: Undo force-sensitive
+      undo_silenced: Undo limit
       undo_suspension: Undo suspension
-      unsilenced_msg: Successfully unlimited %{username}'s account
+      unsilenced_msg: Successfully undid limit of %{username}'s account
       unsubscribe: Unsubscribe
       unsuspended_msg: Successfully unsuspended %{username}'s account
       username: Username
@@ -236,14 +242,16 @@ en:
         destroy_custom_emoji: Delete Custom Emoji
         destroy_domain_allow: Delete Domain Allow
         destroy_domain_block: Delete Domain Block
-        destroy_email_domain_block: Delete e-mail domain block
+        destroy_email_domain_block: Delete E-mail Domain Block
         destroy_ip_block: Delete IP rule
         destroy_status: Delete Post
         destroy_unavailable_domain: Delete Unavailable Domain
         disable_2fa_user: Disable 2FA
         disable_custom_emoji: Disable Custom Emoji
+        disable_sign_in_token_auth_user: Disable E-mail Token Authentication for User
         disable_user: Disable User
         enable_custom_emoji: Enable Custom Emoji
+        enable_sign_in_token_auth_user: Enable E-mail Token Authentication for User
         enable_user: Enable User
         memorialize_account: Memorialize Account
         promote_user: Promote User
@@ -251,12 +259,12 @@ en:
         reopen_report: Reopen Report
         reset_password_user: Reset Password
         resolve_report: Resolve Report
-        sensitive_account: Mark the media in your account as sensitive
-        silence_account: Silence Account
+        sensitive_account: Force-Sensitive Account
+        silence_account: Limit Account
         suspend_account: Suspend Account
         unassigned_report: Unassign Report
-        unsensitive_account: Unmark the media in your account as sensitive
-        unsilence_account: Unsilence Account
+        unsensitive_account: Undo Force-Sensitive Account
+        unsilence_account: Undo Limit Account
         unsuspend_account: Unsuspend Account
         update_announcement: Update Announcement
         update_custom_emoji: Update Custom Emoji
@@ -285,8 +293,10 @@ en:
         destroy_unavailable_domain_html: "%{name} resumed delivery to domain %{target}"
         disable_2fa_user_html: "%{name} disabled two factor requirement for user %{target}"
         disable_custom_emoji_html: "%{name} disabled emoji %{target}"
+        disable_sign_in_token_auth_user_html: "%{name} disabled e-mail token authentication for %{target}"
         disable_user_html: "%{name} disabled login for user %{target}"
         enable_custom_emoji_html: "%{name} enabled emoji %{target}"
+        enable_sign_in_token_auth_user_html: "%{name} enabled e-mail token authentication for %{target}"
         enable_user_html: "%{name} enabled login for user %{target}"
         memorialize_account_html: "%{name} turned %{target}'s account into a memoriam page"
         promote_user_html: "%{name} promoted user %{target}"
@@ -295,11 +305,11 @@ en:
         reset_password_user_html: "%{name} reset password of user %{target}"
         resolve_report_html: "%{name} resolved report %{target}"
         sensitive_account_html: "%{name} marked %{target}'s media as sensitive"
-        silence_account_html: "%{name} silenced %{target}'s account"
+        silence_account_html: "%{name} limited %{target}'s account"
         suspend_account_html: "%{name} suspended %{target}'s account"
         unassigned_report_html: "%{name} unassigned report %{target}"
         unsensitive_account_html: "%{name} unmarked %{target}'s media as sensitive"
-        unsilence_account_html: "%{name} unsilenced %{target}'s account"
+        unsilence_account_html: "%{name} undid limit of %{target}'s account"
         unsuspend_account_html: "%{name} unsuspended %{target}'s account"
         update_announcement_html: "%{name} updated announcement %{target}"
         update_custom_emoji_html: "%{name} updated emoji %{target}"
@@ -421,14 +431,14 @@ en:
       rejecting_media: rejecting media files
       rejecting_reports: rejecting reports
       severity:
-        silence: silenced
+        silence: limited
         suspend: suspended
       show:
         affected_accounts:
           one: One account in the database affected
           other: "%{count} accounts in the database affected"
         retroactive:
-          silence: Unsilence existing affected accounts from this domain
+          silence: Undo limit of existing affected accounts from this domain
           suspend: Unsuspend existing affected accounts from this domain
         title: Undo domain block for %{domain}
         undo: Undo
diff --git a/config/routes.rb b/config/routes.rb
index eb618324a97..0c4b2954649 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -283,6 +283,7 @@ Rails.application.routes.draw do
 
     resources :users, only: [] do
       resource :two_factor_authentication, only: [:destroy]
+      resource :sign_in_token_authentication, only: [:create, :destroy]
     end
 
     resources :custom_emojis, only: [:index, :new, :create] do
diff --git a/db/migrate/20210621221010_add_skip_sign_in_token_to_users.rb b/db/migrate/20210621221010_add_skip_sign_in_token_to_users.rb
new file mode 100644
index 00000000000..43ad9b95455
--- /dev/null
+++ b/db/migrate/20210621221010_add_skip_sign_in_token_to_users.rb
@@ -0,0 +1,5 @@
+class AddSkipSignInTokenToUsers < ActiveRecord::Migration[6.1]
+  def change
+    add_column :users, :skip_sign_in_token, :boolean
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 935e2a56429..b2929f693c9 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -927,6 +927,7 @@ ActiveRecord::Schema.define(version: 2021_06_30_000137) do
     t.datetime "sign_in_token_sent_at"
     t.string "webauthn_id"
     t.inet "sign_up_ip"
+    t.boolean "skip_sign_in_token"
     t.index ["account_id"], name: "index_users_on_account_id"
     t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
     t.index ["created_by_application_id"], name: "index_users_on_created_by_application_id"
diff --git a/lib/mastodon/accounts_cli.rb b/lib/mastodon/accounts_cli.rb
index 74162256ff7..05019480162 100644
--- a/lib/mastodon/accounts_cli.rb
+++ b/lib/mastodon/accounts_cli.rb
@@ -54,7 +54,8 @@ module Mastodon
 
     option :email, required: true
     option :confirmed, type: :boolean
-    option :role, default: 'user'
+    option :role, default: 'user', enum: %w(user moderator admin)
+    option :skip_sign_in_token, type: :boolean
     option :reattach, type: :boolean
     option :force, type: :boolean
     desc 'create USERNAME', 'Create a new user'
@@ -68,6 +69,9 @@ module Mastodon
       With the --role option one of  "user", "admin" or "moderator"
       can be supplied. Defaults to "user"
 
+      With the --skip-sign-in-token option, you can ensure that
+      the user is never asked for an e-mailed security code.
+
       With the --reattach option, the new user will be reattached
       to a given existing username of an old account. If the old
       account is still in use by someone else, you can supply
@@ -77,7 +81,7 @@ module Mastodon
     def create(username)
       account  = Account.new(username: username)
       password = SecureRandom.hex
-      user     = User.new(email: options[:email], password: password, agreement: true, approved: true, admin: options[:role] == 'admin', moderator: options[:role] == 'moderator', confirmed_at: options[:confirmed] ? Time.now.utc : nil, bypass_invite_request_check: true)
+      user     = User.new(email: options[:email], password: password, agreement: true, approved: true, admin: options[:role] == 'admin', moderator: options[:role] == 'moderator', confirmed_at: options[:confirmed] ? Time.now.utc : nil, bypass_invite_request_check: true, skip_sign_in_token: options[:skip_sign_in_token])
 
       if options[:reattach]
         account = Account.find_local(username) || Account.new(username: username)
@@ -113,7 +117,7 @@ module Mastodon
       end
     end
 
-    option :role
+    option :role, enum: %w(user moderator admin)
     option :email
     option :confirm, type: :boolean
     option :enable, type: :boolean
@@ -121,6 +125,7 @@ module Mastodon
     option :disable_2fa, type: :boolean
     option :approve, type: :boolean
     option :reset_password, type: :boolean
+    option :skip_sign_in_token, type: :boolean
     desc 'modify USERNAME', 'Modify a user'
     long_desc <<-LONG_DESC
       Modify a user account.
@@ -142,6 +147,9 @@ module Mastodon
 
       With the --reset-password option, the user's password is replaced by
       a randomly-generated one, printed in the output.
+
+      With the --skip-sign-in-token option, you can ensure that
+      the user is never asked for an e-mailed security code.
     LONG_DESC
     def modify(username)
       user = Account.find_local(username)&.user
@@ -163,6 +171,7 @@ module Mastodon
       user.disabled = true if options[:disable]
       user.approved = true if options[:approve]
       user.otp_required_for_login = false if options[:disable_2fa]
+      user.skip_sign_in_token = options[:skip_sign_in_token] unless options[:skip_sign_in_token].nil?
       user.confirm if options[:confirm]
 
       if user.save
diff --git a/spec/controllers/admin/resets_controller_spec.rb b/spec/controllers/admin/resets_controller_spec.rb
index a20a460bd05..c1e34b7f968 100644
--- a/spec/controllers/admin/resets_controller_spec.rb
+++ b/spec/controllers/admin/resets_controller_spec.rb
@@ -16,7 +16,7 @@ describe Admin::ResetsController do
 
       post :create, params: { account_id: account.id }
 
-      expect(response).to redirect_to(admin_accounts_path)
+      expect(response).to redirect_to(admin_account_path(account.id))
     end
   end
 end
diff --git a/spec/controllers/admin/two_factor_authentications_controller_spec.rb b/spec/controllers/admin/two_factor_authentications_controller_spec.rb
index b0e82d3d663..c650957290f 100644
--- a/spec/controllers/admin/two_factor_authentications_controller_spec.rb
+++ b/spec/controllers/admin/two_factor_authentications_controller_spec.rb
@@ -15,12 +15,12 @@ describe Admin::TwoFactorAuthenticationsController do
         user.update(otp_required_for_login: true)
       end
 
-      it 'redirects to admin accounts page' do
+      it 'redirects to admin account page' do
         delete :destroy, params: { user_id: user.id }
 
         user.reload
         expect(user.otp_enabled?).to eq false
-        expect(response).to redirect_to(admin_accounts_path)
+        expect(response).to redirect_to(admin_account_path(user.account_id))
       end
     end
 
@@ -38,13 +38,13 @@ describe Admin::TwoFactorAuthenticationsController do
                   nickname: 'Security Key')
       end
 
-      it 'redirects to admin accounts page' do
+      it 'redirects to admin account page' do
         delete :destroy, params: { user_id: user.id }
 
         user.reload
         expect(user.otp_enabled?).to eq false
         expect(user.webauthn_enabled?).to eq false
-        expect(response).to redirect_to(admin_accounts_path)
+        expect(response).to redirect_to(admin_account_path(user.account_id))
       end
     end
   end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 5db249be2a9..54bb6db7f96 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -344,6 +344,34 @@ RSpec.describe User, type: :model do
     end
   end
 
+  describe '#reset_password!' do
+    subject(:user) { Fabricate(:user, password: 'foobar12345') }
+
+    let!(:session_activation) { Fabricate(:session_activation, user: user) }
+    let!(:access_token) { Fabricate(:access_token, resource_owner_id: user.id) }
+    let!(:web_push_subscription) { Fabricate(:web_push_subscription, access_token: access_token) }
+
+    before do
+      user.reset_password!
+    end
+
+    it 'changes the password immediately' do
+      expect(user.external_or_valid_password?('foobar12345')).to be false
+    end
+
+    it 'deactivates all sessions' do
+      expect(user.session_activations.count).to eq 0
+    end
+
+    it 'revokes all access tokens' do
+      expect(Doorkeeper::AccessToken.active_for(user).count).to eq 0
+    end
+
+    it 'removes push subscriptions' do
+      expect(Web::PushSubscription.where(user: user).or(Web::PushSubscription.where(access_token: access_token)).count).to eq 0
+    end
+  end
+
   describe '#confirm!' do
     subject(:user) { Fabricate(:user, confirmed_at: confirmed_at) }
 

From 895f1a531d1c2f7ef3352388a9e3502699222c01 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 8 Jul 2021 14:34:32 +0900
Subject: [PATCH 13/19] Bump ws from 7.5.1 to 7.5.2 (#16475)

Bumps [ws](https://github.com/websockets/ws) from 7.5.1 to 7.5.2.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.5.1...7.5.2)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 package.json | 2 +-
 yarn.lock    | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/package.json b/package.json
index 6cf923249ff..517030f7868 100644
--- a/package.json
+++ b/package.json
@@ -168,7 +168,7 @@
     "webpack-cli": "^3.3.12",
     "webpack-merge": "^5.8.0",
     "wicg-inert": "^3.1.1",
-    "ws": "^7.5.1"
+    "ws": "^7.5.2"
   },
   "devDependencies": {
     "@testing-library/jest-dom": "^5.14.1",
diff --git a/yarn.lock b/yarn.lock
index bdf2c6c339f..53b0e722abe 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -11720,10 +11720,10 @@ ws@^6.2.1:
   dependencies:
     async-limiter "~1.0.0"
 
-ws@^7.2.3, ws@^7.3.1, ws@^7.5.1:
-  version "7.5.1"
-  resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.1.tgz#44fc000d87edb1d9c53e51fbc69a0ac1f6871d66"
-  integrity sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow==
+ws@^7.2.3, ws@^7.3.1, ws@^7.5.2:
+  version "7.5.2"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.2.tgz#09cc8fea3bec1bc5ed44ef51b42f945be36900f6"
+  integrity sha512-lkF7AWRicoB9mAgjeKbGqVUekLnSNO4VjKVnuPHpQeOxZOErX6BPXwJk70nFslRCEEA8EVW7ZjKwXaP9N+1sKQ==
 
 xml-name-validator@^3.0.0:
   version "3.0.0"

From d36cfb99da14804e058efa2c9d828c008401c2ad Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 8 Jul 2021 14:35:20 +0900
Subject: [PATCH 14/19] Bump addressable from 2.7.0 to 2.8.0 (#16474)

Bumps [addressable](https://github.com/sporkmonger/addressable) from 2.7.0 to 2.8.0.
- [Release notes](https://github.com/sporkmonger/addressable/releases)
- [Changelog](https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sporkmonger/addressable/compare/addressable-2.7.0...addressable-2.8.0)

---
updated-dependencies:
- dependency-name: addressable
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 Gemfile      | 2 +-
 Gemfile.lock | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Gemfile b/Gemfile
index 56f52f3e122..a76e69ac3ba 100644
--- a/Gemfile
+++ b/Gemfile
@@ -24,7 +24,7 @@ gem 'paperclip', '~> 6.0'
 gem 'blurhash', '~> 0.1'
 
 gem 'active_model_serializers', '~> 0.10'
-gem 'addressable', '~> 2.7'
+gem 'addressable', '~> 2.8'
 gem 'bootsnap', '~> 1.6.0', require: false
 gem 'browser'
 gem 'charlock_holmes', '~> 0.7.7'
diff --git a/Gemfile.lock b/Gemfile.lock
index b14dbc12919..2875bb3e3ba 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -66,7 +66,7 @@ GEM
       minitest (>= 5.1)
       tzinfo (~> 2.0)
       zeitwerk (~> 2.3)
-    addressable (2.7.0)
+    addressable (2.8.0)
       public_suffix (>= 2.0.2, < 5.0)
     airbrussh (1.4.0)
       sshkit (>= 1.6.1, != 1.7.0)
@@ -673,7 +673,7 @@ PLATFORMS
 DEPENDENCIES
   active_model_serializers (~> 0.10)
   active_record_query_trace (~> 1.8)
-  addressable (~> 2.7)
+  addressable (~> 2.8)
   annotate (~> 3.1)
   aws-sdk-s3 (~> 1.96)
   better_errors (~> 2.9)

From e5b4b672f0b8d2768c7d1d5ae80a9698830882a7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 8 Jul 2021 14:35:34 +0900
Subject: [PATCH 15/19] Bump oj from 3.11.7 to 3.11.8 (#16473)

Bumps [oj](https://github.com/ohler55/oj) from 3.11.7 to 3.11.8.
- [Release notes](https://github.com/ohler55/oj/releases)
- [Changelog](https://github.com/ohler55/oj/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/ohler55/oj/compare/v3.11.7...v3.11.8)

---
updated-dependencies:
- dependency-name: oj
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 Gemfile.lock | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Gemfile.lock b/Gemfile.lock
index 2875bb3e3ba..17b6f152b16 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -374,7 +374,7 @@ GEM
       concurrent-ruby (~> 1.0, >= 1.0.2)
       sidekiq (>= 3.5)
       statsd-ruby (~> 1.4, >= 1.4.0)
-    oj (3.11.7)
+    oj (3.11.8)
     omniauth (1.9.1)
       hashie (>= 3.4.6)
       rack (>= 1.6.2, < 3)

From 6eaa39c3873adc8c0ade8e8138773b590b7ccc1d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 8 Jul 2021 14:35:45 +0900
Subject: [PATCH 16/19] Bump sidekiq-unique-jobs from 7.1.1 to 7.1.2 (#16472)

Bumps [sidekiq-unique-jobs](https://github.com/mhenrixon/sidekiq-unique-jobs) from 7.1.1 to 7.1.2.
- [Release notes](https://github.com/mhenrixon/sidekiq-unique-jobs/releases)
- [Changelog](https://github.com/mhenrixon/sidekiq-unique-jobs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mhenrixon/sidekiq-unique-jobs/compare/v7.1.1...v7.1.2)

---
updated-dependencies:
- dependency-name: sidekiq-unique-jobs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 Gemfile.lock | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Gemfile.lock b/Gemfile.lock
index 17b6f152b16..efc3f544fc1 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -569,7 +569,7 @@ GEM
       sidekiq (>= 3)
       thwait
       tilt (>= 1.4.0)
-    sidekiq-unique-jobs (7.1.1)
+    sidekiq-unique-jobs (7.1.2)
       brpoplpush-redis_script (> 0.1.1, <= 2.0.0)
       concurrent-ruby (~> 1.0, >= 1.0.5)
       sidekiq (>= 5.0, < 7.0)

From ca662c13f03b50aa50479110db2d11777dd8d453 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 8 Jul 2021 14:37:06 +0900
Subject: [PATCH 17/19] Bump rubocop-rails from 2.11.1 to 2.11.2 (#16471)

Bumps [rubocop-rails](https://github.com/rubocop/rubocop-rails) from 2.11.1 to 2.11.2.
- [Release notes](https://github.com/rubocop/rubocop-rails/releases)
- [Changelog](https://github.com/rubocop/rubocop-rails/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop/rubocop-rails/compare/v2.11.1...v2.11.2)

---
updated-dependencies:
- dependency-name: rubocop-rails
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 Gemfile.lock | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Gemfile.lock b/Gemfile.lock
index efc3f544fc1..6885c28af60 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -535,7 +535,7 @@ GEM
       unicode-display_width (>= 1.4.0, < 3.0)
     rubocop-ast (1.7.0)
       parser (>= 3.0.1.1)
-    rubocop-rails (2.11.1)
+    rubocop-rails (2.11.2)
       activesupport (>= 4.2.0)
       rack (>= 1.1)
       rubocop (>= 1.7.0, < 2.0)

From c4568e3b606cc4007739f881c334bdfe8dc77745 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 8 Jul 2021 14:37:18 +0900
Subject: [PATCH 18/19] Bump rubocop from 1.18.1 to 1.18.2 (#16470)

Bumps [rubocop](https://github.com/rubocop/rubocop) from 1.18.1 to 1.18.2.
- [Release notes](https://github.com/rubocop/rubocop/releases)
- [Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop/rubocop/compare/v1.18.1...v1.18.2)

---
updated-dependencies:
- dependency-name: rubocop
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 Gemfile.lock | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Gemfile.lock b/Gemfile.lock
index 6885c28af60..96d89fbae57 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -524,7 +524,7 @@ GEM
     rspec-support (3.10.2)
     rspec_junit_formatter (0.4.1)
       rspec-core (>= 2, < 4, != 2.12.0)
-    rubocop (1.18.1)
+    rubocop (1.18.2)
       parallel (~> 1.10)
       parser (>= 3.0.0.0)
       rainbow (>= 2.2.2, < 4.0)

From 101f8616feb845f70ef89fa0d0b3ebc37c472930 Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Mon, 5 Jul 2021 19:16:06 +0200
Subject: [PATCH 19/19] [Glitch] Fix pop-in player display when poster has long
 username or handle

Port 1381e0e1d9f27bd108d8b9349896f10ffe996cb2 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
---
 app/javascript/flavours/glitch/styles/components/status.scss | 1 +
 1 file changed, 1 insertion(+)

diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss
index e906a72619e..e9d30544fc9 100644
--- a/app/javascript/flavours/glitch/styles/components/status.scss
+++ b/app/javascript/flavours/glitch/styles/components/status.scss
@@ -1095,6 +1095,7 @@ a.status-card.compact:hover {
     &__account {
       display: flex;
       text-decoration: none;
+      overflow: hidden;
     }
 
     .account__avatar {