{
diff --git a/app/javascript/flavours/glitch/initial_state.js b/app/javascript/flavours/glitch/initial_state.js
index 52066aac420546..452f2991898686 100644
--- a/app/javascript/flavours/glitch/initial_state.js
+++ b/app/javascript/flavours/glitch/initial_state.js
@@ -70,6 +70,7 @@ export const hasMultiColumnPath = initialPath === '/'
* @property {InitialStateMeta} meta
* @property {object} local_settings
* @property {number} max_toot_chars
+ * @property {number} max_feed_hashtags
* @property {number} poll_limits
* @property {number} max_reactions
*/
@@ -136,6 +137,7 @@ export const sso_redirect = getMeta('sso_redirect');
// Glitch-soc-specific settings
export const maxChars = (initialState && initialState.max_toot_chars) || 500;
+export const maxFeedHashtags = (initialState && initialState.max_feed_hashtags) || 4;
export const favouriteModal = getMeta('favourite_modal');
export const pollLimits = (initialState && initialState.poll_limits);
export const defaultContentType = getMeta('default_content_type');
diff --git a/app/javascript/flavours/glitch/locales/id.json b/app/javascript/flavours/glitch/locales/id.json
index d360fed7222d91..f37788bc802eb5 100644
--- a/app/javascript/flavours/glitch/locales/id.json
+++ b/app/javascript/flavours/glitch/locales/id.json
@@ -1,4 +1,51 @@
{
+ "about.fork_disclaimer": "Glitch-soc adalah perangkat lunak sumber terbuka yang merupakan fork dari Mastodon.",
+ "account.disclaimer_full": "Informasi di bawah ini mungkin tidak mencerminkan profil pengguna secara lengkap.",
+ "account.follows": "Mengikuti",
+ "account.joined": "Bergabung {date}",
+ "account.suspended_disclaimer_full": "Pengguna ini telah ditangguhkan oleh moderator.",
+ "account.view_full_profile": "Tampilkan profil lengkap",
+ "advanced_options.icon_title": "Opsi lanjutan",
+ "advanced_options.local-only.long": "Jangan mengunggah ke instance lain",
+ "advanced_options.local-only.short": "Hanya lokal",
+ "advanced_options.local-only.tooltip": "Postingan ini hanya untuk lokal",
+ "advanced_options.threaded_mode.long": "Secara otomatis membuka balasan pada postingan",
+ "advanced_options.threaded_mode.short": "Mode Utasan",
+ "advanced_options.threaded_mode.tooltip": "Mode utasan dinyalakan",
+ "boost_modal.missing_description": "Toot ini berisi beberapa media tanpa deskripsi",
+ "column.favourited_by": "Disukai oleh",
+ "column.heading": "Lainnya",
+ "column.reblogged_by": "Dibagikan oleh",
+ "column.subheading": "Opsi lain-lain",
+ "column_header.profile": "Profil",
+ "column_subheading.lists": "Daftar",
+ "column_subheading.navigation": "Penelusuran",
+ "community.column_settings.allow_local_only": "Tampilkan toot lokal saja",
+ "compose.attach": "Lampirkan...",
+ "compose.attach.doodle": "Gambar sesuatu",
+ "compose.attach.upload": "Unggah file",
+ "compose.content-type.html": "HTML",
+ "compose.content-type.markdown": "Bahasa Markdown",
+ "compose.content-type.plain": "Teks biasa",
+ "compose_form.poll.multiple_choices": "Izinkan beberapa pilihan",
+ "compose_form.poll.single_choice": "Izinkan hanya satu pilihan",
+ "compose_form.spoiler": "Sembunyikan teks di balik peringatan",
+ "confirmation_modal.do_not_ask_again": "Jangan minta konfirmasi lagi",
+ "confirmations.deprecated_settings.confirm": "Gunakan preferensi Mastodon",
+ "confirmations.deprecated_settings.message": "Beberapa {app_settings} khusus perangkat Glitch-soc yang Anda gunakan telah digantikan oleh {preferences} Mastodon dan akan diganti:",
+ "confirmations.missing_media_description.confirm": "Tetap kirim",
+ "confirmations.missing_media_description.edit": "Sunting media",
+ "confirmations.missing_media_description.message": "Setidaknya satu lampiran media tidak memiliki deskripsi. Pertimbangkan untuk mendeskripsikan semua lampiran media untuk pengguna tunanetra sebelum mengirim toot Anda.",
+ "confirmations.unfilter.author": "Penulis",
+ "confirmations.unfilter.confirm": "Tampilkan",
+ "confirmations.unfilter.edit_filter": "Ubah saringan",
+ "content-type.change": "Jenis konten",
+ "direct.group_by_conversations": "Grupkan berdasarkan percakapan",
+ "endorsed_accounts_editor.endorsed_accounts": "Akun pilihan",
+ "favourite_modal.combo": "Anda dapat menekan {combo} untuk melewati ini lain kali",
+ "firehose.column_settings.allow_local_only": "Tampilkan postingan khusus lokal di \"Semua\"",
+ "home.column_settings.advanced": "Lanjutan",
+ "home.column_settings.filter_regex": "Saring dengan ekspresi reguler",
"settings.content_warnings": "Content warnings",
"settings.preferences": "Preferences"
}
diff --git a/app/javascript/flavours/glitch/locales/sv.json b/app/javascript/flavours/glitch/locales/sv.json
index d360fed7222d91..3212f7ff88d725 100644
--- a/app/javascript/flavours/glitch/locales/sv.json
+++ b/app/javascript/flavours/glitch/locales/sv.json
@@ -1,4 +1,52 @@
{
+ "account.follows": "Följer",
+ "account.joined": "Gick med {date}",
+ "account.suspended_disclaimer_full": "Denna användare har stängts av av en moderator.",
+ "account.view_full_profile": "Visa full profil",
+ "advanced_options.icon_title": "Avancerade inställningar",
+ "advanced_options.local-only.long": "Lägg inte ut på andra instanser",
+ "advanced_options.local-only.short": "Endast lokalt",
+ "advanced_options.local-only.tooltip": "Detta inlägg är endast tillgängligt lokalt",
+ "advanced_options.threaded_mode.long": "Öppnar automatiskt ett svar vid publicering",
+ "advanced_options.threaded_mode.short": "Tråd-läge",
+ "advanced_options.threaded_mode.tooltip": "Tråd-läge på",
+ "boost_modal.missing_description": "Denna toot innehåller viss media utan beskrivning",
+ "column.favourited_by": "Favoritmarkerad av",
+ "column.heading": "Övrigt",
+ "column.reblogged_by": "Boostad av",
+ "column.subheading": "Övriga val",
+ "column_header.profile": "Profil",
+ "column_subheading.lists": "Listor",
+ "column_subheading.navigation": "Navigering",
+ "community.column_settings.allow_local_only": "Visa endast lokala toots",
+ "compose.attach": "Bifoga...",
+ "compose.attach.doodle": "Rita något",
+ "compose.attach.upload": "Ladda upp en fil",
+ "compose.content-type.html": "HTML",
+ "compose.content-type.markdown": "Markdown",
+ "compose.content-type.plain": "Klartext",
+ "compose_form.poll.multiple_choices": "Tillåt flera val",
+ "compose_form.poll.single_choice": "Tillåt ett val",
+ "compose_form.spoiler": "Göm text bakom varning",
+ "confirmation_modal.do_not_ask_again": "Fråga mig inte igen",
+ "confirmations.deprecated_settings.confirm": "Använd Mastodon-preferenser",
+ "confirmations.deprecated_settings.message": "Några av de glitch-soc-enhetsspecifika {app_settings} som du använder har ersatts av Mastodon-{preferences} och kommer att åsidosättas:",
+ "confirmations.missing_media_description.confirm": "Lägg ut ändå",
+ "confirmations.missing_media_description.edit": "Redigera media",
+ "confirmations.missing_media_description.message": "Minst en mediebilaga saknar beskrivning. Överväg att beskriva all media för synskadade innan du skickar din toot.",
+ "confirmations.unfilter.author": "Användare",
+ "confirmations.unfilter.confirm": "Visa",
+ "confirmations.unfilter.edit_filter": "Redigera filter",
+ "confirmations.unfilter.filters": "Matchande {count, plural, one {filter} other {filters}}",
+ "content-type.change": "Innehållstyp",
+ "direct.group_by_conversations": "Sortera efter konversation",
+ "endorsed_accounts_editor.endorsed_accounts": "Utvalda konton",
+ "favourite_modal.combo": "Du kan trycka på {combo} för att skippa detta nästa gång",
+ "firehose.column_settings.allow_local_only": "Visa endast lokala inlägg i \"Alla\"",
+ "home.column_settings.advanced": "Avancerat",
+ "home.column_settings.filter_regex": "Filtrera bort med reguljära uttryck",
+ "home.column_settings.show_direct": "Visa privata omnämningar",
+ "home.settings": "Kolumninställningar",
"settings.content_warnings": "Content warnings",
"settings.preferences": "Preferences"
}
diff --git a/app/javascript/material-icons/400-24px/colors-fill.svg b/app/javascript/material-icons/400-24px/colors-fill.svg
new file mode 100644
index 00000000000000..5e4b534fe22f21
--- /dev/null
+++ b/app/javascript/material-icons/400-24px/colors-fill.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/javascript/material-icons/400-24px/colors.svg b/app/javascript/material-icons/400-24px/colors.svg
new file mode 100644
index 00000000000000..5e4b534fe22f21
--- /dev/null
+++ b/app/javascript/material-icons/400-24px/colors.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/javascript/material-icons/400-24px/undo-fill.svg b/app/javascript/material-icons/400-24px/undo-fill.svg
new file mode 100644
index 00000000000000..c451e1adc73700
--- /dev/null
+++ b/app/javascript/material-icons/400-24px/undo-fill.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/javascript/material-icons/400-24px/undo.svg b/app/javascript/material-icons/400-24px/undo.svg
new file mode 100644
index 00000000000000..c451e1adc73700
--- /dev/null
+++ b/app/javascript/material-icons/400-24px/undo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/lib/application_extension.rb b/app/lib/application_extension.rb
index fb442e2c2d2fca..400c51a023d09b 100644
--- a/app/lib/application_extension.rb
+++ b/app/lib/application_extension.rb
@@ -4,14 +4,34 @@ module ApplicationExtension
extend ActiveSupport::Concern
included do
+ include Redisable
+
has_many :created_users, class_name: 'User', foreign_key: 'created_by_application_id', inverse_of: :created_by_application
validates :name, length: { maximum: 60 }
validates :website, url: true, length: { maximum: 2_000 }, if: :website?
validates :redirect_uri, length: { maximum: 2_000 }
+
+ # The relationship used between Applications and AccessTokens is using
+ # dependent: delete_all, which means the ActiveRecord callback in
+ # AccessTokenExtension is not run, so instead we manually announce to
+ # streaming that these tokens are being deleted.
+ before_destroy :push_to_streaming_api, prepend: true
end
def confirmation_redirect_uri
redirect_uri.lines.first.strip
end
+
+ def push_to_streaming_api
+ # TODO: #28793 Combine into a single topic
+ payload = Oj.dump(event: :kill)
+ access_tokens.in_batches do |tokens|
+ redis.pipelined do |pipeline|
+ tokens.ids.each do |id|
+ pipeline.publish("timeline:access_token:#{id}", payload)
+ end
+ end
+ end
+ end
end
diff --git a/app/models/concerns/user/omniauthable.rb b/app/models/concerns/user/omniauthable.rb
index 113bfda23043eb..396a0598f87b82 100644
--- a/app/models/concerns/user/omniauthable.rb
+++ b/app/models/concerns/user/omniauthable.rb
@@ -19,17 +19,18 @@ def email_present?
end
class_methods do
- def find_for_oauth(auth, signed_in_resource = nil)
+ def find_for_omniauth(auth, signed_in_resource = nil)
# EOLE-SSO Patch
auth.uid = (auth.uid[0][:uid] || auth.uid[0][:user]) if auth.uid.is_a? Hashie::Array
- identity = Identity.find_for_oauth(auth)
+ identity = Identity.find_for_omniauth(auth)
# If a signed_in_resource is provided it always overrides the existing user
# to prevent the identity being locked with accidentally created accounts.
# Note that this may leave zombie accounts (with no associated identity) which
# can be cleaned up at a later date.
user = signed_in_resource || identity.user
- user ||= create_for_oauth(auth)
+ user ||= reattach_for_auth(auth)
+ user ||= create_for_auth(auth)
if identity.user.nil?
identity.user = user
@@ -39,19 +40,35 @@ def find_for_oauth(auth, signed_in_resource = nil)
user
end
- def create_for_oauth(auth)
- # Check if the user exists with provided email. If no email was provided,
- # we assign a temporary email and ask the user to verify it on
- # the next step via Auth::SetupController.show
+ private
- strategy = Devise.omniauth_configs[auth.provider.to_sym].strategy
- assume_verified = strategy&.security&.assume_email_is_verified
- email_is_verified = auth.info.verified || auth.info.verified_email || auth.info.email_verified || assume_verified
- email = auth.info.verified_email || auth.info.email
+ def reattach_for_auth(auth)
+ # If allowed, check if a user exists with the provided email address,
+ # and return it if they does not have an associated identity with the
+ # current authentication provider.
+
+ # This can be used to provide a choice of alternative auth providers
+ # or provide smooth gradual transition between multiple auth providers,
+ # but this is discouraged because any insecure provider will put *all*
+ # local users at risk, regardless of which provider they registered with.
+
+ return unless ENV['ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH'] == 'true'
- user = User.find_by(email: email) if email_is_verified
+ email, email_is_verified = email_from_auth(auth)
+ return unless email_is_verified
- return user unless user.nil?
+ user = User.find_by(email: email)
+ return if user.nil? || Identity.exists?(provider: auth.provider, user_id: user.id)
+
+ user
+ end
+
+ def create_for_auth(auth)
+ # Create a user for the given auth params. If no email was provided,
+ # we assign a temporary email and ask the user to verify it on
+ # the next step via Auth::SetupController.show
+
+ email, email_is_verified = email_from_auth(auth)
user = User.new(user_params_from_auth(email, auth))
@@ -66,7 +83,14 @@ def create_for_oauth(auth)
user
end
- private
+ def email_from_auth(auth)
+ strategy = Devise.omniauth_configs[auth.provider.to_sym].strategy
+ assume_verified = strategy&.security&.assume_email_is_verified
+ email_is_verified = auth.info.verified || auth.info.verified_email || auth.info.email_verified || assume_verified
+ email = auth.info.verified_email || auth.info.email
+
+ [email, email_is_verified]
+ end
def user_params_from_auth(email, auth)
{
diff --git a/app/models/identity.rb b/app/models/identity.rb
index c95a68a6f63abb..77821b78fa2550 100644
--- a/app/models/identity.rb
+++ b/app/models/identity.rb
@@ -17,7 +17,7 @@ class Identity < ApplicationRecord
validates :uid, presence: true, uniqueness: { scope: :provider }
validates :provider, presence: true
- def self.find_for_oauth(auth)
+ def self.find_for_omniauth(auth)
find_or_create_by(uid: auth.uid, provider: auth.provider)
end
end
diff --git a/app/models/tag_feed.rb b/app/models/tag_feed.rb
index fbbdbaae279656..051b0d13064079 100644
--- a/app/models/tag_feed.rb
+++ b/app/models/tag_feed.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class TagFeed < PublicFeed
- LIMIT_PER_MODE = 4
+ LIMIT_PER_MODE = (ENV['MAX_FEED_HASHTAGS'] || 4).to_i
# @param [Tag] tag
# @param [Account] account
diff --git a/app/models/user.rb b/app/models/user.rb
index cc56c2f54e7556..388be31fab7976 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -342,6 +342,16 @@ def revoke_access!
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
+
+ # Revoke each access token for the Streaming API, since `update_all``
+ # doesn't trigger ActiveRecord Callbacks:
+ # TODO: #28793 Combine into a single topic
+ payload = Oj.dump(event: :kill)
+ redis.pipelined do |pipeline|
+ batch.ids.each do |id|
+ pipeline.publish("timeline:access_token:#{id}", payload)
+ end
+ end
end
end
diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb
index f96246f6eb9265..c1d9f6d7002634 100644
--- a/app/serializers/initial_state_serializer.rb
+++ b/app/serializers/initial_state_serializer.rb
@@ -5,7 +5,7 @@ class InitialStateSerializer < ActiveModel::Serializer
attributes :meta, :compose, :accounts,
:media_attachments, :settings,
- :max_toot_chars, :poll_limits,
+ :max_toot_chars, :max_feed_hashtags, :poll_limits,
:languages, :max_reactions
attribute :critical_updates_pending, if: -> { object&.role&.can?(:view_devops) && SoftwareUpdate.check_enabled? }
@@ -17,6 +17,10 @@ def max_toot_chars
StatusLengthValidator::MAX_CHARS
end
+ def max_feed_hashtags
+ TagFeed::LIMIT_PER_MODE
+ end
+
def max_reactions
StatusReactionValidator::LIMIT
end
diff --git a/app/views/user_mailer/confirmation_instructions.html.haml b/app/views/user_mailer/confirmation_instructions.html.haml
index 74b2d49a4710fe..13e68c722b2fc8 100644
--- a/app/views/user_mailer/confirmation_instructions.html.haml
+++ b/app/views/user_mailer/confirmation_instructions.html.haml
@@ -8,9 +8,7 @@
%td.email-inner-card-td.email-prose
%p= t @resource.approved? ? 'devise.mailer.confirmation_instructions.explanation' : 'devise.mailer.confirmation_instructions.explanation_when_pending', host: site_hostname
- if @resource.created_by_application
- = render 'application/mailer/button', text: t('settings.account_settings'), url: edit_user_registration_url
- = link_to confirmation_url(@resource, confirmation_token: @token, redirect_to_app: 'true') do
- %span= t 'devise.mailer.confirmation_instructions.action_with_app', app: @resource.created_by_application.name
+ = render 'application/mailer/button', text: t('devise.mailer.confirmation_instructions.action_with_app', app: @resource.created_by_application.name), url: confirmation_url(@resource, confirmation_token: @token, redirect_to_app: 'true')
- else
= render 'application/mailer/button', text: t('devise.mailer.confirmation_instructions.action'), url: confirmation_url(@resource, confirmation_token: @token)
%p= t 'devise.mailer.confirmation_instructions.extra_html', terms_path: about_more_url, policy_path: privacy_policy_url
diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb
index fe3871d2e72fbb..f9d47a205ccd9c 100644
--- a/config/initializers/doorkeeper.rb
+++ b/config/initializers/doorkeeper.rb
@@ -21,9 +21,14 @@
user unless user&.otp_required_for_login?
end
- # If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
+ # Doorkeeper provides some administrative interfaces for managing OAuth
+ # Applications, allowing creation, edit, and deletion of applications from the
+ # server. At present, these administrative routes are not integrated into
+ # Mastodon, and as such, we've disabled them by always return a 403 forbidden
+ # response for them. This does not affect the ability for users to manage
+ # their own OAuth Applications.
admin_authenticator do
- current_user&.admin? || redirect_to(new_user_session_url)
+ head 403
end
# Authorization Code expiration time (default 10 minutes).
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index 9a2743ed5b0263..53b02edc40227e 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -26,6 +26,7 @@
'queue' => 'scheduler',
},
}
+ SidekiqScheduler::Scheduler.instance.reload_schedule!
end
end
diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml
index 4439397c8eead1..61bd33851b6fca 100644
--- a/config/locales/devise.en.yml
+++ b/config/locales/devise.en.yml
@@ -12,6 +12,7 @@ en:
last_attempt: You have one more attempt before your account is locked.
locked: Your account is locked.
not_found_in_database: Invalid %{authentication_keys} or password.
+ omniauth_user_creation_failure: Error creating an account for this identity.
pending: Your account is still under review.
timeout: Your session expired. Please login again to continue.
unauthenticated: You need to login or sign up before continuing.
diff --git a/config/routes.rb b/config/routes.rb
index 49116d08a28330..0b03acef2ee2da 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'sidekiq_unique_jobs/web'
+require 'sidekiq_unique_jobs/web' if ENV['ENABLE_SIDEKIQ_UNIQUE_JOBS_UI'] == true
require 'sidekiq-scheduler/web'
class RedirectWithVary < ActionDispatch::Routing::PathRedirect
diff --git a/lib/tasks/sidekiq_unique_jobs.rake b/lib/tasks/sidekiq_unique_jobs.rake
new file mode 100644
index 00000000000000..bedc8fe4c650c4
--- /dev/null
+++ b/lib/tasks/sidekiq_unique_jobs.rake
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+namespace :sidekiq_unique_jobs do
+ task delete_all_locks: :environment do
+ digests = SidekiqUniqueJobs::Digests.new
+ digests.delete_by_pattern('*', count: digests.count)
+
+ expiring_digests = SidekiqUniqueJobs::ExpiringDigests.new
+ expiring_digests.delete_by_pattern('*', count: expiring_digests.count)
+ end
+end
diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb
index 70224544433c8d..d5a2ffbc869fad 100644
--- a/spec/models/identity_spec.rb
+++ b/spec/models/identity_spec.rb
@@ -3,19 +3,19 @@
require 'rails_helper'
RSpec.describe Identity do
- describe '.find_for_oauth' do
+ describe '.find_for_omniauth' do
let(:auth) { Fabricate(:identity, user: Fabricate(:user)) }
it 'calls .find_or_create_by' do
allow(described_class).to receive(:find_or_create_by)
- described_class.find_for_oauth(auth)
+ described_class.find_for_omniauth(auth)
expect(described_class).to have_received(:find_or_create_by).with(uid: auth.uid, provider: auth.provider)
end
it 'returns an instance of Identity' do
- expect(described_class.find_for_oauth(auth)).to be_instance_of described_class
+ expect(described_class.find_for_omniauth(auth)).to be_instance_of described_class
end
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 213022e8301b95..3cc804af431687 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -438,7 +438,10 @@
let!(:access_token) { Fabricate(:access_token, resource_owner_id: user.id) }
let!(:web_push_subscription) { Fabricate(:web_push_subscription, access_token: access_token) }
+ let(:redis_pipeline_stub) { instance_double(Redis::Namespace, publish: nil) }
+
before do
+ allow(redis).to receive(:pipelined).and_yield(redis_pipeline_stub)
user.reset_password!
end
@@ -455,6 +458,10 @@
expect(Doorkeeper::AccessToken.active_for(user).count).to eq 0
end
+ it 'revokes streaming access for all access tokens' do
+ expect(redis_pipeline_stub).to have_received(:publish).with("timeline:access_token:#{access_token.id}", Oj.dump(event: :kill)).once
+ end
+
it 'removes push subscriptions' do
expect(Web::PushSubscription.where(user: user).or(Web::PushSubscription.where(access_token: access_token)).count).to eq 0
expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound)
diff --git a/spec/requests/disabled_oauth_endpoints_spec.rb b/spec/requests/disabled_oauth_endpoints_spec.rb
new file mode 100644
index 00000000000000..7c2c09f3804bf3
--- /dev/null
+++ b/spec/requests/disabled_oauth_endpoints_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe 'Disabled OAuth routes' do
+ # These routes are disabled via the doorkeeper configuration for
+ # `admin_authenticator`, as these routes should only be accessible by server
+ # administrators. For now, these routes are not properly designed and
+ # integrated into Mastodon, so we're disabling them completely
+ describe 'GET /oauth/applications' do
+ it 'returns 403 forbidden' do
+ get oauth_applications_path
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ describe 'POST /oauth/applications' do
+ it 'returns 403 forbidden' do
+ post oauth_applications_path
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ describe 'GET /oauth/applications/new' do
+ it 'returns 403 forbidden' do
+ get new_oauth_application_path
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ describe 'GET /oauth/applications/:id' do
+ let(:application) { Fabricate(:application, scopes: 'read') }
+
+ it 'returns 403 forbidden' do
+ get oauth_application_path(application)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ describe 'PATCH /oauth/applications/:id' do
+ let(:application) { Fabricate(:application, scopes: 'read') }
+
+ it 'returns 403 forbidden' do
+ patch oauth_application_path(application)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ describe 'PUT /oauth/applications/:id' do
+ let(:application) { Fabricate(:application, scopes: 'read') }
+
+ it 'returns 403 forbidden' do
+ put oauth_application_path(application)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ describe 'DELETE /oauth/applications/:id' do
+ let(:application) { Fabricate(:application, scopes: 'read') }
+
+ it 'returns 403 forbidden' do
+ delete oauth_application_path(application)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ describe 'GET /oauth/applications/:id/edit' do
+ let(:application) { Fabricate(:application, scopes: 'read') }
+
+ it 'returns 403 forbidden' do
+ get edit_oauth_application_path(application)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+end
diff --git a/spec/requests/omniauth_callbacks_spec.rb b/spec/requests/omniauth_callbacks_spec.rb
index 0d37c411403bdc..095535e48598e0 100644
--- a/spec/requests/omniauth_callbacks_spec.rb
+++ b/spec/requests/omniauth_callbacks_spec.rb
@@ -39,16 +39,35 @@
Fabricate(:user, email: 'user@host.example')
end
- it 'matches the existing user, creates an identity, and redirects to root path' do
- expect { subject }
- .to not_change(User, :count)
- .and change(Identity, :count)
- .by(1)
- .and change(LoginActivity, :count)
- .by(1)
+ context 'when ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH is set to true' do
+ around do |example|
+ ClimateControl.modify ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH: 'true' do
+ example.run
+ end
+ end
+
+ it 'matches the existing user, creates an identity, and redirects to root path' do
+ expect { subject }
+ .to not_change(User, :count)
+ .and change(Identity, :count)
+ .by(1)
+ .and change(LoginActivity, :count)
+ .by(1)
+
+ expect(Identity.find_by(user: User.last).uid).to eq('123')
+ expect(response).to redirect_to(root_path)
+ end
+ end
- expect(Identity.find_by(user: User.last).uid).to eq('123')
- expect(response).to redirect_to(root_path)
+ context 'when ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH is not set to true' do
+ it 'does not match the existing user or create an identity, and redirects to login page' do
+ expect { subject }
+ .to not_change(User, :count)
+ .and not_change(Identity, :count)
+ .and not_change(LoginActivity, :count)
+
+ expect(response).to redirect_to(new_user_session_url)
+ end
end
end
@@ -96,7 +115,7 @@
context 'when a user cannot be built' do
before do
- allow(User).to receive(:find_for_oauth).and_return(User.new)
+ allow(User).to receive(:find_for_omniauth).and_return(User.new)
end
it 'redirects to the new user signup page' do