diff --git a/.rubocop.yml b/.rubocop.yml index 333b632670062d..89c04eeef9a75d 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -75,7 +75,6 @@ Metrics/AbcSize: Exclude: - 'app/serializers/initial_state_serializer.rb' - 'lib/mastodon/cli/*.rb' - - db/*migrate/**/* # Reason: Currently disabled in .rubocop_todo.yml # https://docs.rubocop.org/rubocop/cops_metrics.html#metricscyclomaticcomplexity @@ -87,7 +86,6 @@ Metrics/CyclomaticComplexity: - 'app/services/delivery_antenna_service.rb' - 'app/services/post_status_service.rb' - lib/mastodon/cli/*.rb - - db/*migrate/**/* # Reason: # https://docs.rubocop.org/rubocop/cops_metrics.html#metricsparameterlists diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index a8f0c3c79daa9d..2cba62159594be 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -167,7 +167,6 @@ Rails/WhereExists: - 'app/validators/reaction_validator.rb' - 'app/validators/vote_validator.rb' - 'app/workers/move_worker.rb' - - 'db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb' - 'lib/tasks/tests.rake' - 'spec/models/account_spec.rb' - 'spec/services/activitypub/process_collection_service_spec.rb' @@ -255,8 +254,6 @@ Style/GuardClause: - 'app/workers/redownload_media_worker.rb' - 'app/workers/remote_account_refresh_worker.rb' - 'config/initializers/devise.rb' - - 'db/migrate/20170901141119_truncate_preview_cards.rb' - - 'db/post_migrate/20220704024901_migrate_settings_to_user_roles.rb' - 'lib/devise/strategies/two_factor_ldap_authenticatable.rb' - 'lib/devise/strategies/two_factor_pam_authenticatable.rb' - 'lib/mastodon/cli/accounts.rb' @@ -277,7 +274,6 @@ Style/HashAsLastArrayItem: - 'app/models/status.rb' - 'app/services/batched_remove_status_service.rb' - 'app/services/notify_service.rb' - - 'db/migrate/20181024224956_migrate_account_conversations.rb' # This cop supports unsafe autocorrection (--autocorrect-all). Style/HashTransformValues: diff --git a/Gemfile.lock b/Gemfile.lock index 71e597d117e626..06d4d5b8eb9cc8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -474,7 +474,7 @@ GEM net-imap (0.4.4) date net-protocol - net-ldap (0.18.0) + net-ldap (0.19.0) net-pop (0.1.2) net-protocol net-protocol (0.2.2) @@ -678,7 +678,7 @@ GEM unicode-display_width (>= 2.4.0, < 3.0) rubocop-ast (1.30.0) parser (>= 3.2.1.0) - rubocop-capybara (2.19.0) + rubocop-capybara (2.20.0) rubocop (~> 1.41) rubocop-factory_bot (2.24.0) rubocop (~> 1.33) diff --git a/app/controllers/admin/email_domain_blocks_controller.rb b/app/controllers/admin/email_domain_blocks_controller.rb index 4a3228ec300466..ff754bc0b47033 100644 --- a/app/controllers/admin/email_domain_blocks_controller.rb +++ b/app/controllers/admin/email_domain_blocks_controller.rb @@ -40,7 +40,7 @@ def create (@email_domain_block.other_domains || []).uniq.each do |domain| next if EmailDomainBlock.where(domain: domain).exists? - other_email_domain_block = EmailDomainBlock.create!(domain: domain, parent: @email_domain_block) + other_email_domain_block = EmailDomainBlock.create!(domain: domain, allow_with_approval: @email_domain_block.allow_with_approval, parent: @email_domain_block) log_action :create, other_email_domain_block end end @@ -65,7 +65,7 @@ def set_resolved_records end def resource_params - params.require(:email_domain_block).permit(:domain, other_domains: []) + params.require(:email_domain_block).permit(:domain, :allow_with_approval, other_domains: []) end def form_email_domain_block_batch_params diff --git a/app/controllers/admin/export_domain_blocks_controller.rb b/app/controllers/admin/export_domain_blocks_controller.rb index 433b8a158787a7..ffc4478172634d 100644 --- a/app/controllers/admin/export_domain_blocks_controller.rb +++ b/app/controllers/admin/export_domain_blocks_controller.rb @@ -68,7 +68,7 @@ def export_headers def export_data CSV.generate(headers: export_headers, write_headers: true) do |content| - DomainBlock.with_limitations.each do |instance| + DomainBlock.with_limitations.order(id: :asc).each do |instance| content << [instance.domain, instance.severity, instance.reject_media, instance.reject_reports, instance.public_comment, instance.obfuscate] end end diff --git a/app/controllers/api/v1/admin/email_domain_blocks_controller.rb b/app/controllers/api/v1/admin/email_domain_blocks_controller.rb index 850eda62241655..df54b9f0a404d1 100644 --- a/app/controllers/api/v1/admin/email_domain_blocks_controller.rb +++ b/app/controllers/api/v1/admin/email_domain_blocks_controller.rb @@ -55,7 +55,7 @@ def set_email_domain_block end def resource_params - params.permit(:domain) + params.permit(:domain, :allow_with_approval) end def insert_pagination_headers diff --git a/app/controllers/concerns/signature_verification.rb b/app/controllers/concerns/signature_verification.rb index f0a344f1c97d64..35391e64c44390 100644 --- a/app/controllers/concerns/signature_verification.rb +++ b/app/controllers/concerns/signature_verification.rb @@ -91,14 +91,23 @@ def signed_request_actor raise SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if actor.nil? signature = Base64.decode64(signature_params['signature']) - compare_signed_string = build_signed_string + compare_signed_string = build_signed_string(include_query_string: true) return actor unless verify_signature(actor, signature, compare_signed_string).nil? + # Compatibility quirk with older Mastodon versions + compare_signed_string = build_signed_string(include_query_string: false) + return actor unless verify_signature(actor, signature, compare_signed_string).nil? + actor = stoplight_wrap_request { actor_refresh_key!(actor) } raise SignatureVerificationError, "Could not refresh public key #{signature_params['keyId']}" if actor.nil? + compare_signed_string = build_signed_string(include_query_string: true) + return actor unless verify_signature(actor, signature, compare_signed_string).nil? + + # Compatibility quirk with older Mastodon versions + compare_signed_string = build_signed_string(include_query_string: false) return actor unless verify_signature(actor, signature, compare_signed_string).nil? fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)", signed_string: compare_signed_string, signature: signature_params['signature'] @@ -180,11 +189,18 @@ def verify_signature(actor, signature, compare_signed_string) nil end - def build_signed_string + def build_signed_string(include_query_string: true) signed_headers.map do |signed_header| case signed_header when Request::REQUEST_TARGET - "#{Request::REQUEST_TARGET}: #{request.method.downcase} #{request.path}" + if include_query_string + "#{Request::REQUEST_TARGET}: #{request.method.downcase} #{request.original_fullpath}" + else + # Current versions of Mastodon incorrectly omit the query string from the (request-target) pseudo-header. + # Therefore, temporarily support such incorrect signatures for compatibility. + # TODO: remove eventually some time after release of the fixed version + "#{Request::REQUEST_TARGET}: #{request.method.downcase} #{request.path}" + end when '(created)' raise SignatureVerificationError, 'Invalid pseudo-header (created) for rsa-sha256' unless signature_algorithm == 'hs2019' raise SignatureVerificationError, 'Pseudo-header (created) used but corresponding argument missing' if signature_params['created'].blank? diff --git a/app/controllers/well_known/webfinger_controller.rb b/app/controllers/well_known/webfinger_controller.rb index 364fbf8a18c47e..72f0ea890fc654 100644 --- a/app/controllers/well_known/webfinger_controller.rb +++ b/app/controllers/well_known/webfinger_controller.rb @@ -21,7 +21,7 @@ def set_account username = username_from_resource @account = begin - if username == Rails.configuration.x.local_domain + if username == Rails.configuration.x.local_domain || username == Rails.configuration.x.web_domain Account.representative else Account.find_local!(username) diff --git a/app/javascript/mastodon/features/status/index.jsx b/app/javascript/mastodon/features/status/index.jsx index 2d42b2a5424f36..601f7480fa9b7f 100644 --- a/app/javascript/mastodon/features/status/index.jsx +++ b/app/javascript/mastodon/features/status/index.jsx @@ -654,16 +654,20 @@ class Status extends ImmutablePureComponent { )); } - setRef = c => { + setContainerRef = c => { this.node = c; }; + setStatusRef = c => { + this.statusNode = c; + }; + _scrollStatusIntoView () { const { status, multiColumn } = this.props; if (status) { - window.requestAnimationFrame(() => { - this.node?.querySelector('.detailed-status__wrapper')?.scrollIntoView(true); + requestIdleCallback(() => { + this.statusNode?.scrollIntoView(true); // In the single-column interface, `scrollIntoView` will put the post behind the header, // so compensate for that. @@ -701,9 +705,8 @@ class Status extends ImmutablePureComponent { } // Scroll to focused post if it is loaded - const child = this.node?.querySelector('.detailed-status__wrapper'); - if (child) { - return [0, child.offsetTop]; + if (this.statusNode) { + return [0, this.statusNode.offsetTop]; } // Do not scroll otherwise, `componentDidUpdate` will take care of that @@ -768,12 +771,12 @@ class Status extends ImmutablePureComponent { /> -
+
{references} {ancestors} -
+
>= ?', sign_up_ip.to_s).exists? end + def sign_up_email_requires_approval? + return false unless email.present? || unconfirmed_email.present? + + EmailDomainBlock.requires_approval?(email.presence || unconfirmed_email, attempt_ip: sign_up_ip) + end + def open_registrations? Setting.registrations_mode == 'open' end diff --git a/app/serializers/rest/admin/email_domain_block_serializer.rb b/app/serializers/rest/admin/email_domain_block_serializer.rb index a026ff680e74c1..afe7722cb58ba7 100644 --- a/app/serializers/rest/admin/email_domain_block_serializer.rb +++ b/app/serializers/rest/admin/email_domain_block_serializer.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class REST::Admin::EmailDomainBlockSerializer < ActiveModel::Serializer - attributes :id, :domain, :created_at, :history + attributes :id, :domain, :created_at, :history, :allow_with_approval def id object.id.to_s diff --git a/app/views/admin/email_domain_blocks/_email_domain_block.html.haml b/app/views/admin/email_domain_blocks/_email_domain_block.html.haml index 7cb973c4b4e047..f6a6e826670ff4 100644 --- a/app/views/admin/email_domain_blocks/_email_domain_block.html.haml +++ b/app/views/admin/email_domain_blocks/_email_domain_block.html.haml @@ -12,3 +12,7 @@ · = t('admin.email_domain_blocks.attempts_over_week', count: email_domain_block.history.reduce(0) { |sum, day| sum + day.accounts }) + + - if email_domain_block.allow_with_approval? + · + = t('admin.email_domain_blocks.allow_registrations_with_approval') diff --git a/app/views/admin/email_domain_blocks/new.html.haml b/app/views/admin/email_domain_blocks/new.html.haml index fa1d950ad2b333..3d31487733ee34 100644 --- a/app/views/admin/email_domain_blocks/new.html.haml +++ b/app/views/admin/email_domain_blocks/new.html.haml @@ -7,6 +7,9 @@ .fields-group = f.input :domain, wrapper: :with_block_label, label: t('admin.email_domain_blocks.domain'), input_html: { readonly: defined?(@resolved_records) } + .fields-group + = f.input :allow_with_approval, wrapper: :with_label, hint: false, label: I18n.t('admin.email_domain_blocks.allow_registrations_with_approval') + - if defined?(@resolved_records) %p.hint= t('admin.email_domain_blocks.resolved_dns_records_hint_html') diff --git a/config/initializers/1_hosts.rb b/config/initializers/1_hosts.rb index 6ff0845c4665cf..5c59e28bd12764 100644 --- a/config/initializers/1_hosts.rb +++ b/config/initializers/1_hosts.rb @@ -23,7 +23,7 @@ if Rails.env.production? "ws#{https ? 's' : ''}://#{web_host}" else - "ws://#{ENV['REMOTE_DEV'] == 'true' ? host.split(':').first : 'localhost'}:4000" + "ws://#{host.split(':').first}:4000" end end diff --git a/config/locales/devise.ie.yml b/config/locales/devise.ie.yml index e4f2e6fb190f17..687e7115a6d152 100644 --- a/config/locales/devise.ie.yml +++ b/config/locales/devise.ie.yml @@ -46,6 +46,10 @@ ie: title: 2FA desvalidat two_factor_enabled: title: 2FA permisset + two_factor_recovery_codes_changed: + explanation: Li anteyan codes de recuperation ha esset ínvalidat, e novis generat. + subject: 'Mastodon: 2-factor codes de recuperation regenerat' + title: 2FA codes de recuperation changeat unlock_instructions: subject: 'Mastodon: Desserral instructiones' webauthn_credential: @@ -55,8 +59,11 @@ ie: webauthn_disabled: subject: 'Mastodon: Autentication con claves de securitá desactivisat' title: Claves de securitá desactivisat + webauthn_enabled: + title: Claves de securitá activisat omniauth_callbacks: failure: Ne posset autenticar te de %{kind} pro "%{reason}". + success: Successosimen autenticat de conto %{kind}. passwords: no_token: Tu ne posse accessar ti-ci págine sin venir de un email pri reiniciar li passa-parol. Si tu ha venit de un email pri reiniciar li passa-parol, ples far cert que tu usat li complet URL providet. send_instructions: Si tui email-adresse existe in nor database, tu va reciver un ligament por recuperar li passa-parol a tui email-adresse in quelc minutes. Ples vider tui spam-emails si tu ne recivet ti email. diff --git a/config/locales/doorkeeper.ie.yml b/config/locales/doorkeeper.ie.yml index 86a5de7b37ecca..b778871e3a4879 100644 --- a/config/locales/doorkeeper.ie.yml +++ b/config/locales/doorkeeper.ie.yml @@ -31,6 +31,7 @@ ie: redirect_uri: Usar un linea per URI index: application: Aplication + callback_url: URL de retrovocada delete: Deleter empty: Tu have null aplicationes. name: Nómine @@ -42,6 +43,7 @@ ie: show: actions: Actiones application_id: Clave de client + callback_urls: URLs de retrovocada secret: Secrete de client title: 'Aplication: %{name}' authorizations: @@ -51,6 +53,7 @@ ie: error: title: Alquo ha errat new: + review_permissions: Inspecter permissiones title: Autorisation besonat authorized_applications: buttons: diff --git a/config/locales/en.yml b/config/locales/en.yml index 0415465f843e8b..9339cc438daa0b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -472,6 +472,7 @@ en: view: View domain block email_domain_blocks: add_new: Add new + allow_registrations_with_approval: Allow registrations with approval attempts_over_week: one: "%{count} attempt over the last week" other: "%{count} sign-up attempts over the last week" diff --git a/config/locales/lad.yml b/config/locales/lad.yml index 5c541d9f999423..323ade8ddb0968 100644 --- a/config/locales/lad.yml +++ b/config/locales/lad.yml @@ -383,7 +383,11 @@ lad: confirm_suspension: cancel: Anula confirm: Suspende + permanent_action: Si kites la suspensyon no restoraras dingunos datos ni relasyones. + remove_all_data: Esto efasara todo el kontenido, multimedia i datos de profiles de los kuentos en este domeno de tu sirvidor. + stop_communication: Tu sirvidor deshara de komunikarse kon estos sirvidores. title: Konfirma bloko de domeno para %{domain} + undo_relationships: Esto kitara todas las relasyones de segimyento entre tu kuentos en estos sirvidores i el tu sirvidor. created_msg: El bloko de domeno esta siendo prosesado destroyed_msg: El bloko de domeno se dezizo domain: Domeno @@ -772,6 +776,8 @@ lad: type: Tipo types: major: Versyon prinsipala + minor: Versyon minora + patch: Versyon de remendo – koreksyones de yerros i trokamientos simples version: Versyon statuses: account: Autor @@ -829,8 +835,10 @@ lad: message_html: No ay dingun prosedura Sidekiq en egzekusion para la(s) kola(s) %{value}. Por favor, reviza tu konfigurasyon de Sidekiq software_version_critical_check: action: Amostra aktualizasyones desponivles + message_html: Una aktualizasyon kritika de Mastodon esta desponivle. Por favor aktualiza pishin. software_version_patch_check: action: Amostra aktualizasyones desponivles + message_html: Una aktualizasyon de Mastodon kon koreksyon de yerros esta desponivle. upload_check_privacy_error: action: Klika aki para mas enformasyon message_html: "Tu sirvidor de web es mal konfigurado. La privasita de tus utilizadores esta en riziko." @@ -945,6 +953,7 @@ lad: next_steps: Puedes achetar la apelasyon para dezazer la dechizyon de moderasyon, o ignorarla. subject: "%{username} esta apelando a una dechizyon de moderasyon en %{instance}" new_critical_software_updates: + body: Ay mueva versyon kritika de Mastodon. Es posivle ke keras aktualizar pishin! subject: Ay aktualizasyones kritikas de Mastodon desponivles para %{instance}! new_pending_account: body: Los peratim del muevo kuento estan abashos. Puedes achetar o refuzar esta aplikasyon. @@ -1045,13 +1054,17 @@ lad: accept: Acheta back: Atras preamble: Estas son establesidas i aplikadas por los moderadores de %{domain}. + preamble_invited: Antes de kontinuar, por favor reviza las reglas del sirvidor establesidas por los moderatores de %{domain}. title: Algunas reglas bazikas. title_invited: Fuites envitado. security: Sigurita set_new_password: Establese muevo kod setup: email_below_hint_html: Mira en tu kuti de spam o solisita de muevo. Si el adreso de posta elektronika ke aparese aki es yerrado, puedes trokarlo aki. + email_settings_hint_html: Klika el atadjiko ke te embimos para verifikar %{email}. Asperaremos aki. link_not_received: No risivites un atadijo? + new_confirmation_instructions_sent: Resiviras un muevo mesaj de posta elektronika kon el atadjio de konfirmasyon en unos minutos! + title: Reviza tu kuti de arivo sign_in: preamble_html: Konektate kon tus kredensiales de %{domain}. Si tu kuento esta balabayado en otruno servidor, no puedras konektarte aki. title: Konektate kon %{domain} @@ -1246,9 +1259,11 @@ lad: imports: errors: empty: Dosya CSV vaziya + incompatible_type: Inkompativle kon el tipo de importo eskojido invalid_csv_file: 'Dosya CSV no valida. Yerro: %{error}' over_rows_processing_limit: kontiene mas de %{count} filas too_large: Dosya es mas grande + failures: Yerros imported: Importado modes: merge: Une @@ -1671,6 +1686,9 @@ lad: month: "%b %Y" time: "%H:%M" with_time_zone: "%d de %b del %Y, %H:%M %Z" + translation: + errors: + too_many_requests: Ay demaziadas solisitudes de servisyo de traduksyon. two_factor_authentication: add: Adjusta disable: Inkapasita autentifikasyon en dos pasos @@ -1750,9 +1768,12 @@ lad: title: Bienvenido, %{name}! users: follow_limit_reached: No puedes segir a mas de %{limit} personas + go_to_sso_account_settings: Va a la konfigurasyon de kuento de tu prokurador de identita invalid_otp_token: Kodiche de dos pasos no valido + otp_lost_help_html: Si pedriste akseso a los dos, puedes kontaktarte kon %{email} signed_in_as: 'Konektado komo:' verification: + here_is_how: Ansina es komo verification: Verifikasyon verified_links: Tus atadijos verifikados webauthn_credentials: diff --git a/config/locales/simple_form.ie.yml b/config/locales/simple_form.ie.yml index bde52e2a783ea0..bd055b1c9eb24a 100644 --- a/config/locales/simple_form.ie.yml +++ b/config/locales/simple_form.ie.yml @@ -86,6 +86,7 @@ ie: ip_block: comment: Facultativ. Ne obliviar pro quo tu adjuntet ti-ci regul. expires_in: IP-adresses es un ressurse finit, quelcvez partit e transferet de manu a manu. Pro to, un índefinit bloccada de IP ne es recomandat. + ip: Intrar un adresse IPv4 o IPv6. Tu posse bloccar un tot intervalle de ili con li sintaxe CIDR. Atention a ne bloccar te self! severities: no_access: Bloccar accesse a omni ressurses sign_up_block: Nov registrationes ne va esser possibil @@ -93,6 +94,10 @@ ie: severity: Selecter quo va evenir con demandes ex ti-ci IP rule: text: Descrir un regul o postulation por usatores sur ti-ci servitor. Prova scrir un descrition curt e simplic + sessions: + otp: 'Intrar li 2-factor code generat del app sur tui portabile o usar un de tui codes de recuperation:' + settings: + show_application: Totvez, tu va sempre posser vider quel app ha publicat tui posta. user: role: Permissiones de usator decidet per su rol user_role: @@ -111,6 +116,7 @@ ie: name: Etiquette value: Contenete indexable: Includer public postas in resultates de sercha + unlocked: Automaticmen acceptar nov sequitores account_alias: acct: Usator-nómine del anteyan conto account_migration: @@ -158,6 +164,7 @@ ie: max_uses: Max grand númere de usas new_password: Nov passa-parol note: Biografie + otp_attempt: 2-factor code password: Passa-parol phrase: Clave-parol o frase setting_advanced_layout: Possibilisar web-interfacie avansat @@ -165,10 +172,12 @@ ie: setting_default_language: Lingue in quel postar setting_default_privacy: Privatie de postada setting_default_sensitive: Sempre marcar medie quam sensitiv + setting_display_media: Exposition de medie setting_display_media_default: Predefinitiones setting_display_media_hide_all: Celar omno setting_display_media_show_all: Monstrar omno setting_expand_spoilers: Sempre expander postas marcat con admonitiones de contenete + setting_hide_network: Celar tui grafica social setting_system_font_ui: Usar predefinit fonte de sistema setting_theme: Tema de situ setting_trends: Monstrar li hodial tendenties @@ -179,6 +188,7 @@ ie: title: Titul type: Specie de importation username: Nómine de usator + username_or_email: Usator-nómine o E-posta whole_word: Plen parol featured_tag: name: Hashtag @@ -193,11 +203,13 @@ ie: custom_css: Custom CSS profile_directory: Possibilisar profilarium registrations_mode: Qui posse registrar se + require_invite_text: Exiger un rason por adherer se show_domain_blocks: Vider bloccas de dominia show_domain_blocks_rationale: Monstrar pro quo cert dominias esset bloccat site_contact_email: Contact e-mail adresse site_contact_username: Usator-nómine de contact site_extended_description: Extendet descrition + site_short_description: Descrition del servitor site_title: Nómine de servitor theme: Predefenit tema trendable_by_default: Possibilisar tendenties sin priori inspection diff --git a/db/migrate/20170901141119_truncate_preview_cards.rb b/db/migrate/20170901141119_truncate_preview_cards.rb index 22a7731099435b..b4ba8c45ea633d 100644 --- a/db/migrate/20170901141119_truncate_preview_cards.rb +++ b/db/migrate/20170901141119_truncate_preview_cards.rb @@ -22,11 +22,9 @@ def up end def down - if ActiveRecord::Base.connection.table_exists? 'deprecated_preview_cards' - drop_table :preview_cards - rename_table :deprecated_preview_cards, :preview_cards - else - raise ActiveRecord::IrreversibleMigration, 'Previous preview cards table has already been removed' - end + raise ActiveRecord::IrreversibleMigration, 'Previous preview cards table has already been removed' unless ActiveRecord::Base.connection.table_exists? 'deprecated_preview_cards' + + drop_table :preview_cards + rename_table :deprecated_preview_cards, :preview_cards end end diff --git a/db/migrate/20181024224956_migrate_account_conversations.rb b/db/migrate/20181024224956_migrate_account_conversations.rb index 93ef5da6157f2d..d879fd88a24228 100644 --- a/db/migrate/20181024224956_migrate_account_conversations.rb +++ b/db/migrate/20181024224956_migrate_account_conversations.rb @@ -105,7 +105,7 @@ def up end end - notifications_about_direct_statuses.includes(:account, mention: { status: [:account, mentions: :account] }).find_each do |notification| + notifications_about_direct_statuses.includes(:account, mention: { status: [:account, { mentions: :account }] }).find_each do |notification| MigrationAccountConversation.add_status(notification.account, notification.target_status) migrated += 1 diff --git a/db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb b/db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb index 88dcea43600044..4597ec5ef4c08a 100644 --- a/db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb +++ b/db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb @@ -9,7 +9,7 @@ def up # on the to-be-changed default User.where(User.arel_table[:current_sign_in_at].gteq(1.month.ago)).find_each do |user| - next if Setting.unscoped.where(thing_type: 'User', thing_id: user.id, var: 'advanced_layout').exists? + next if Setting.unscoped.exists?(thing_type: 'User', thing_id: user.id, var: 'advanced_layout') user.settings.advanced_layout = true end diff --git a/db/migrate/20231222100226_add_allow_with_approval_to_email_domain_blocks.rb b/db/migrate/20231222100226_add_allow_with_approval_to_email_domain_blocks.rb new file mode 100644 index 00000000000000..01e8edfed46898 --- /dev/null +++ b/db/migrate/20231222100226_add_allow_with_approval_to_email_domain_blocks.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddAllowWithApprovalToEmailDomainBlocks < ActiveRecord::Migration[7.1] + def change + add_column :email_domain_blocks, :allow_with_approval, :boolean, default: false, null: false + end +end diff --git a/db/post_migrate/20220704024901_migrate_settings_to_user_roles.rb b/db/post_migrate/20220704024901_migrate_settings_to_user_roles.rb index 254690cc3a4c34..00afee26d0c8d5 100644 --- a/db/post_migrate/20220704024901_migrate_settings_to_user_roles.rb +++ b/db/post_migrate/20220704024901_migrate_settings_to_user_roles.rb @@ -6,36 +6,55 @@ class MigrateSettingsToUserRoles < ActiveRecord::Migration[6.1] class UserRole < ApplicationRecord; end def up - owner_role = UserRole.find_by(name: 'Owner') - admin_role = UserRole.find_by(name: 'Admin') - moderator_role = UserRole.find_by(name: 'Moderator') - everyone_role = UserRole.find_by(id: -99) - - min_invite_role = Setting.min_invite_role - show_staff_badge = Setting.show_staff_badge - - if everyone_role - everyone_role.permissions &= ~::UserRole::FLAGS[:invite_users] unless min_invite_role == 'user' - everyone_role.save - end - - if owner_role - owner_role.highlighted = show_staff_badge - owner_role.save - end - - if admin_role - admin_role.permissions |= ::UserRole::FLAGS[:invite_users] if %w(admin moderator).include?(min_invite_role) - admin_role.highlighted = show_staff_badge - admin_role.save - end - - if moderator_role - moderator_role.permissions |= ::UserRole::FLAGS[:invite_users] if %w(moderator).include?(min_invite_role) - moderator_role.highlighted = show_staff_badge - moderator_role.save - end + process_role_everyone + process_role_owner + process_role_admin + process_role_moderator end def down; end + + private + + def process_role_everyone + everyone_role = UserRole.find_by(id: -99) + return unless everyone_role + + everyone_role.permissions &= ~::UserRole::FLAGS[:invite_users] unless min_invite_role == 'user' + everyone_role.save + end + + def process_role_owner + owner_role = UserRole.find_by(name: 'Owner') + return unless owner_role + + owner_role.highlighted = show_staff_badge + owner_role.save + end + + def process_role_admin + admin_role = UserRole.find_by(name: 'Admin') + return unless admin_role + + admin_role.permissions |= ::UserRole::FLAGS[:invite_users] if %w(admin moderator).include?(min_invite_role) + admin_role.highlighted = show_staff_badge + admin_role.save + end + + def process_role_moderator + moderator_role = UserRole.find_by(name: 'Moderator') + return unless moderator_role + + moderator_role.permissions |= ::UserRole::FLAGS[:invite_users] if %w(moderator).include?(min_invite_role) + moderator_role.highlighted = show_staff_badge + moderator_role.save + end + + def min_invite_role + Setting.min_invite_role + end + + def show_staff_badge + Setting.show_staff_badge + end end diff --git a/db/post_migrate/20221101190723_backfill_admin_action_logs.rb b/db/post_migrate/20221101190723_backfill_admin_action_logs.rb index 6476f4b13a740e..1d8d983b3a4b4b 100644 --- a/db/post_migrate/20221101190723_backfill_admin_action_logs.rb +++ b/db/post_migrate/20221101190723_backfill_admin_action_logs.rb @@ -77,90 +77,135 @@ class AdminActionLog < ApplicationRecord def up safety_assured do - AdminActionLog.includes(:account).where(target_type: 'Account', human_identifier: nil).find_each do |log| - next if log.account.nil? + process_logs_for_account + process_logs_for_user + process_logs_for_report + process_logs_for_domain_block + process_logs_for_domain_allow + process_logs_for_email_domain_block + process_logs_for_unavailable_domain + process_logs_for_status + process_logs_for_account_warning + process_logs_for_announcement + process_logs_for_ip_block + process_logs_for_custom_emoji + process_logs_for_canonical_email_block + process_logs_for_appeal + end + end - log.update_attribute('human_identifier', log.account.acct) - end + def down; end - AdminActionLog.includes(user: :account).where(target_type: 'User', human_identifier: nil).find_each do |log| - next if log.user.nil? + private - log.update_attribute('human_identifier', log.user.account.acct) - log.update_attribute('route_param', log.user.account_id) - end + def process_logs_for_account + AdminActionLog.includes(:account).where(target_type: 'Account', human_identifier: nil).find_each do |log| + next if log.account.nil? - AdminActionLog.where(target_type: 'Report', human_identifier: nil).in_batches.update_all('human_identifier = target_id::text') + log.update_attribute('human_identifier', log.account.acct) + end + end - AdminActionLog.includes(:domain_block).where(target_type: 'DomainBlock').find_each do |log| - next if log.domain_block.nil? + def process_logs_for_user + AdminActionLog.includes(user: :account).where(target_type: 'User', human_identifier: nil).find_each do |log| + next if log.user.nil? - log.update_attribute('human_identifier', log.domain_block.domain) - end + log.update_attribute('human_identifier', log.user.account.acct) + log.update_attribute('route_param', log.user.account_id) + end + end - AdminActionLog.includes(:domain_allow).where(target_type: 'DomainAllow').find_each do |log| - next if log.domain_allow.nil? + def process_logs_for_report + AdminActionLog.where(target_type: 'Report', human_identifier: nil).in_batches.update_all('human_identifier = target_id::text') + end - log.update_attribute('human_identifier', log.domain_allow.domain) - end + def process_logs_for_domain_block + AdminActionLog.includes(:domain_block).where(target_type: 'DomainBlock').find_each do |log| + next if log.domain_block.nil? - AdminActionLog.includes(:email_domain_block).where(target_type: 'EmailDomainBlock').find_each do |log| - next if log.email_domain_block.nil? + log.update_attribute('human_identifier', log.domain_block.domain) + end + end - log.update_attribute('human_identifier', log.email_domain_block.domain) - end + def process_logs_for_domain_allow + AdminActionLog.includes(:domain_allow).where(target_type: 'DomainAllow').find_each do |log| + next if log.domain_allow.nil? - AdminActionLog.includes(:unavailable_domain).where(target_type: 'UnavailableDomain').find_each do |log| - next if log.unavailable_domain.nil? + log.update_attribute('human_identifier', log.domain_allow.domain) + end + end - log.update_attribute('human_identifier', log.unavailable_domain.domain) - end + def process_logs_for_email_domain_block + AdminActionLog.includes(:email_domain_block).where(target_type: 'EmailDomainBlock').find_each do |log| + next if log.email_domain_block.nil? - AdminActionLog.includes(status: :account).where(target_type: 'Status', human_identifier: nil).find_each do |log| - next if log.status.nil? + log.update_attribute('human_identifier', log.email_domain_block.domain) + end + end - log.update_attribute('human_identifier', log.status.account.acct) - log.update_attribute('permalink', log.status.uri) - end + def process_logs_for_unavailable_domain + AdminActionLog.includes(:unavailable_domain).where(target_type: 'UnavailableDomain').find_each do |log| + next if log.unavailable_domain.nil? - AdminActionLog.includes(account_warning: :account).where(target_type: 'AccountWarning', human_identifier: nil).find_each do |log| - next if log.account_warning.nil? + log.update_attribute('human_identifier', log.unavailable_domain.domain) + end + end - log.update_attribute('human_identifier', log.account_warning.account.acct) - end + def process_logs_for_status + AdminActionLog.includes(status: :account).where(target_type: 'Status', human_identifier: nil).find_each do |log| + next if log.status.nil? - AdminActionLog.includes(:announcement).where(target_type: 'Announcement', human_identifier: nil).find_each do |log| - next if log.announcement.nil? + log.update_attribute('human_identifier', log.status.account.acct) + log.update_attribute('permalink', log.status.uri) + end + end - log.update_attribute('human_identifier', log.announcement.text) - end + def process_logs_for_account_warning + AdminActionLog.includes(account_warning: :account).where(target_type: 'AccountWarning', human_identifier: nil).find_each do |log| + next if log.account_warning.nil? - AdminActionLog.includes(:ip_block).where(target_type: 'IpBlock', human_identifier: nil).find_each do |log| - next if log.ip_block.nil? + log.update_attribute('human_identifier', log.account_warning.account.acct) + end + end - log.update_attribute('human_identifier', "#{log.ip_block.ip}/#{log.ip_block.ip.prefix}") - end + def process_logs_for_announcement + AdminActionLog.includes(:announcement).where(target_type: 'Announcement', human_identifier: nil).find_each do |log| + next if log.announcement.nil? - AdminActionLog.includes(:custom_emoji).where(target_type: 'CustomEmoji', human_identifier: nil).find_each do |log| - next if log.custom_emoji.nil? + log.update_attribute('human_identifier', log.announcement.text) + end + end - log.update_attribute('human_identifier', log.custom_emoji.shortcode) - end + def process_logs_for_ip_block + AdminActionLog.includes(:ip_block).where(target_type: 'IpBlock', human_identifier: nil).find_each do |log| + next if log.ip_block.nil? - AdminActionLog.includes(:canonical_email_block).where(target_type: 'CanonicalEmailBlock', human_identifier: nil).find_each do |log| - next if log.canonical_email_block.nil? + log.update_attribute('human_identifier', "#{log.ip_block.ip}/#{log.ip_block.ip.prefix}") + end + end - log.update_attribute('human_identifier', log.canonical_email_block.canonical_email_hash) - end + def process_logs_for_custom_emoji + AdminActionLog.includes(:custom_emoji).where(target_type: 'CustomEmoji', human_identifier: nil).find_each do |log| + next if log.custom_emoji.nil? - AdminActionLog.includes(appeal: :account).where(target_type: 'Appeal', human_identifier: nil).find_each do |log| - next if log.appeal.nil? + log.update_attribute('human_identifier', log.custom_emoji.shortcode) + end + end - log.update_attribute('human_identifier', log.appeal.account.acct) - log.update_attribute('route_param', log.appeal.account_warning_id) - end + def process_logs_for_canonical_email_block + AdminActionLog.includes(:canonical_email_block).where(target_type: 'CanonicalEmailBlock', human_identifier: nil).find_each do |log| + next if log.canonical_email_block.nil? + + log.update_attribute('human_identifier', log.canonical_email_block.canonical_email_hash) end end - def down; end + def process_logs_for_appeal + AdminActionLog.includes(appeal: :account).where(target_type: 'Appeal', human_identifier: nil).find_each do |log| + next if log.appeal.nil? + + log.update_attribute('human_identifier', log.appeal.account.acct) + log.update_attribute('route_param', log.appeal.account_warning_id) + end + end end diff --git a/db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb b/db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb index 3c68470a7f3812..c080e77ecfd09a 100644 --- a/db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb +++ b/db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb @@ -77,90 +77,135 @@ class AdminActionLog < ApplicationRecord def up safety_assured do - AdminActionLog.includes(:account).where(target_type: 'Account', human_identifier: nil).find_each do |log| - next if log.account.nil? + process_logs_for_account + process_logs_for_user + process_logs_for_report + process_logs_for_domain_block + process_logs_for_domain_allow + process_logs_for_email_domain_block + process_logs_for_unavailable_domain + process_logs_for_status + process_logs_for_account_warning + process_logs_for_announcement + process_logs_for_ip_block + process_logs_for_custom_emoji + process_logs_for_canonical_email_block + process_logs_for_appeal + end + end - log.update_attribute('human_identifier', log.account.acct) - end + def down; end - AdminActionLog.includes(user: :account).where(target_type: 'User', human_identifier: nil).find_each do |log| - next if log.user.nil? + private - log.update_attribute('human_identifier', log.user.account.acct) - log.update_attribute('route_param', log.user.account_id) - end + def process_logs_for_account + AdminActionLog.includes(:account).where(target_type: 'Account', human_identifier: nil).find_each do |log| + next if log.account.nil? - AdminActionLog.where(target_type: 'Report', human_identifier: nil).in_batches.update_all('human_identifier = target_id::text') + log.update_attribute('human_identifier', log.account.acct) + end + end - AdminActionLog.includes(:domain_block).where(target_type: 'DomainBlock').find_each do |log| - next if log.domain_block.nil? + def process_logs_for_user + AdminActionLog.includes(user: :account).where(target_type: 'User', human_identifier: nil).find_each do |log| + next if log.user.nil? - log.update_attribute('human_identifier', log.domain_block.domain) - end + log.update_attribute('human_identifier', log.user.account.acct) + log.update_attribute('route_param', log.user.account_id) + end + end - AdminActionLog.includes(:domain_allow).where(target_type: 'DomainAllow').find_each do |log| - next if log.domain_allow.nil? + def process_logs_for_report + AdminActionLog.where(target_type: 'Report', human_identifier: nil).in_batches.update_all('human_identifier = target_id::text') + end - log.update_attribute('human_identifier', log.domain_allow.domain) - end + def process_logs_for_domain_block + AdminActionLog.includes(:domain_block).where(target_type: 'DomainBlock').find_each do |log| + next if log.domain_block.nil? - AdminActionLog.includes(:email_domain_block).where(target_type: 'EmailDomainBlock').find_each do |log| - next if log.email_domain_block.nil? + log.update_attribute('human_identifier', log.domain_block.domain) + end + end - log.update_attribute('human_identifier', log.email_domain_block.domain) - end + def process_logs_for_domain_allow + AdminActionLog.includes(:domain_allow).where(target_type: 'DomainAllow').find_each do |log| + next if log.domain_allow.nil? - AdminActionLog.includes(:unavailable_domain).where(target_type: 'UnavailableDomain').find_each do |log| - next if log.unavailable_domain.nil? + log.update_attribute('human_identifier', log.domain_allow.domain) + end + end - log.update_attribute('human_identifier', log.unavailable_domain.domain) - end + def process_logs_for_email_domain_block + AdminActionLog.includes(:email_domain_block).where(target_type: 'EmailDomainBlock').find_each do |log| + next if log.email_domain_block.nil? - AdminActionLog.includes(status: :account).where(target_type: 'Status', human_identifier: nil).find_each do |log| - next if log.status.nil? + log.update_attribute('human_identifier', log.email_domain_block.domain) + end + end - log.update_attribute('human_identifier', log.status.account.acct) - log.update_attribute('permalink', log.status.uri) - end + def process_logs_for_unavailable_domain + AdminActionLog.includes(:unavailable_domain).where(target_type: 'UnavailableDomain').find_each do |log| + next if log.unavailable_domain.nil? - AdminActionLog.includes(account_warning: :account).where(target_type: 'AccountWarning', human_identifier: nil).find_each do |log| - next if log.account_warning.nil? + log.update_attribute('human_identifier', log.unavailable_domain.domain) + end + end - log.update_attribute('human_identifier', log.account_warning.account.acct) - end + def process_logs_for_status + AdminActionLog.includes(status: :account).where(target_type: 'Status', human_identifier: nil).find_each do |log| + next if log.status.nil? - AdminActionLog.includes(:announcement).where(target_type: 'Announcement', human_identifier: nil).find_each do |log| - next if log.announcement.nil? + log.update_attribute('human_identifier', log.status.account.acct) + log.update_attribute('permalink', log.status.uri) + end + end - log.update_attribute('human_identifier', log.announcement.text) - end + def process_logs_for_account_warning + AdminActionLog.includes(account_warning: :account).where(target_type: 'AccountWarning', human_identifier: nil).find_each do |log| + next if log.account_warning.nil? - AdminActionLog.includes(:ip_block).where(target_type: 'IpBlock', human_identifier: nil).find_each do |log| - next if log.ip_block.nil? + log.update_attribute('human_identifier', log.account_warning.account.acct) + end + end - log.update_attribute('human_identifier', "#{log.ip_block.ip}/#{log.ip_block.ip.prefix}") - end + def process_logs_for_announcement + AdminActionLog.includes(:announcement).where(target_type: 'Announcement', human_identifier: nil).find_each do |log| + next if log.announcement.nil? - AdminActionLog.includes(:custom_emoji).where(target_type: 'CustomEmoji', human_identifier: nil).find_each do |log| - next if log.custom_emoji.nil? + log.update_attribute('human_identifier', log.announcement.text) + end + end - log.update_attribute('human_identifier', log.custom_emoji.shortcode) - end + def process_logs_for_ip_block + AdminActionLog.includes(:ip_block).where(target_type: 'IpBlock', human_identifier: nil).find_each do |log| + next if log.ip_block.nil? - AdminActionLog.includes(:canonical_email_block).where(target_type: 'CanonicalEmailBlock', human_identifier: nil).find_each do |log| - next if log.canonical_email_block.nil? + log.update_attribute('human_identifier', "#{log.ip_block.ip}/#{log.ip_block.ip.prefix}") + end + end - log.update_attribute('human_identifier', log.canonical_email_block.canonical_email_hash) - end + def process_logs_for_custom_emoji + AdminActionLog.includes(:custom_emoji).where(target_type: 'CustomEmoji', human_identifier: nil).find_each do |log| + next if log.custom_emoji.nil? - AdminActionLog.includes(appeal: :account).where(target_type: 'Appeal', human_identifier: nil).find_each do |log| - next if log.appeal.nil? + log.update_attribute('human_identifier', log.custom_emoji.shortcode) + end + end - log.update_attribute('human_identifier', log.appeal.account.acct) - log.update_attribute('route_param', log.appeal.account_warning_id) - end + def process_logs_for_canonical_email_block + AdminActionLog.includes(:canonical_email_block).where(target_type: 'CanonicalEmailBlock', human_identifier: nil).find_each do |log| + next if log.canonical_email_block.nil? + + log.update_attribute('human_identifier', log.canonical_email_block.canonical_email_hash) end end - def down; end + def process_logs_for_appeal + AdminActionLog.includes(appeal: :account).where(target_type: 'Appeal', human_identifier: nil).find_each do |log| + next if log.appeal.nil? + + log.update_attribute('human_identifier', log.appeal.account.acct) + log.update_attribute('route_param', log.appeal.account_warning_id) + end + end end diff --git a/db/schema.rb b/db/schema.rb index 9e31dcb530bc08..a22b8313a056fc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2023_12_14_225249) do +ActiveRecord::Schema[7.1].define(version: 2023_12_22_100226) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -596,6 +596,7 @@ t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false t.bigint "parent_id" + t.boolean "allow_with_approval", default: false, null: false t.index ["domain"], name: "index_email_domain_blocks_on_domain", unique: true end diff --git a/lib/mastodon/cli/base.rb b/lib/mastodon/cli/base.rb index 32aff2fcc50451..8c222bbb2b3040 100644 --- a/lib/mastodon/cli/base.rb +++ b/lib/mastodon/cli/base.rb @@ -4,6 +4,7 @@ require_relative '../../../config/environment' require 'thor' +require 'pastel' require_relative 'progress_helper' module Mastodon diff --git a/lib/mastodon/cli/federation.rb b/lib/mastodon/cli/federation.rb index 1b4cb467a571bb..8bb46ecb1ae5af 100644 --- a/lib/mastodon/cli/federation.rb +++ b/lib/mastodon/cli/federation.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'tty-prompt' - module Mastodon::CLI module Federation extend ActiveSupport::Concern @@ -30,44 +28,50 @@ module Federation LONG_DESC def self_destruct if SelfDestructHelper.self_destruct? - prompt.ok('Self-destruct mode is already enabled for this Mastodon server') + say('Self-destruct mode is already enabled for this Mastodon server', :green) pending_accounts = Account.local.without_suspended.count + Account.local.suspended.joins(:deletion_request).count sidekiq_stats = Sidekiq::Stats.new if pending_accounts.positive? - prompt.warn("#{pending_accounts} accounts are still pending deletion.") + say("#{pending_accounts} accounts are still pending deletion.", :yellow) elsif sidekiq_stats.enqueued.positive? - prompt.warn('Deletion notices are still being processed') + say('Deletion notices are still being processed', :yellow) elsif sidekiq_stats.retry_size.positive? - prompt.warn('At least one delivery attempt for each deletion notice has been made, but some have failed and are scheduled for retry') + say('At least one delivery attempt for each deletion notice has been made, but some have failed and are scheduled for retry', :yellow) else - prompt.ok('Every deletion notice has been sent! You can safely delete all data and decomission your servers!') + say('Every deletion notice has been sent! You can safely delete all data and decomission your servers!', :green) end exit(0) end - exit(1) unless prompt.ask('Type in the domain of the server to confirm:', required: true) == Rails.configuration.x.local_domain + exit(1) unless ask('Type in the domain of the server to confirm:') == Rails.configuration.x.local_domain - prompt.warn('This operation WILL NOT be reversible.') - prompt.warn('While the data won\'t be erased locally, the server will be in a BROKEN STATE afterwards.') - prompt.warn('The deletion process itself may take a long time, and will be handled by Sidekiq, so do not shut it down until it has finished (you will be able to re-run this command to see the state of the self-destruct process).') + say(<<~WARNING, :yellow) + This operation WILL NOT be reversible. + While the data won't be erased locally, the server will be in a BROKEN STATE afterwards. + The deletion process itself may take a long time, and will be handled by Sidekiq, so do not shut it down until it has finished (you will be able to re-run this command to see the state of the self-destruct process). + WARNING - exit(1) if prompt.no?('Are you sure you want to proceed?') + exit(1) if no?('Are you sure you want to proceed?') - self_destruct_value = Rails.application.message_verifier('self-destruct').generate(Rails.configuration.x.local_domain) - prompt.ok('To switch Mastodon to self-destruct mode, add the following variable to your evironment (e.g. by adding a line to your `.env.production`) and restart all Mastodon processes:') - prompt.ok(" SELF_DESTRUCT=#{self_destruct_value}") - prompt.ok("\nYou can re-run this command to see the state of the self-destruct process.") - rescue TTY::Reader::InputInterrupt + say(<<~INSTRUCTIONS, :green) + To switch Mastodon to self-destruct mode, add the following variable to your evironment (e.g. by adding a line to your `.env.production`) and restart all Mastodon processes: + SELF_DESTRUCT=#{self_destruct_value} + You can re-run this command to see the state of the self-destruct process. + INSTRUCTIONS + rescue Interrupt exit(1) end private - def prompt - @prompt ||= TTY::Prompt.new + def self_destruct_value + Rails + .application + .message_verifier('self-destruct') + .generate(Rails.configuration.x.local_domain) end end end diff --git a/lib/mastodon/cli/preview_cards.rb b/lib/mastodon/cli/preview_cards.rb index 2df3d095da0f2a..9b20a0cbb8662d 100644 --- a/lib/mastodon/cli/preview_cards.rb +++ b/lib/mastodon/cli/preview_cards.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require 'tty-prompt' require_relative 'base' module Mastodon::CLI diff --git a/package.json b/package.json index aa80000633fb04..2af4d654a8bf03 100644 --- a/package.json +++ b/package.json @@ -207,7 +207,7 @@ "prettier": "^3.0.0", "react-test-renderer": "^18.2.0", "stylelint": "^16.0.2", - "stylelint-config-standard-scss": "^12.0.0", + "stylelint-config-standard-scss": "^13.0.0", "typescript": "^5.0.4", "webpack-dev-server": "^3.11.3", "yargs": "^17.7.2" diff --git a/spec/controllers/admin/email_domain_blocks_controller_spec.rb b/spec/controllers/admin/email_domain_blocks_controller_spec.rb index 42866001449b7c..9379fe374aa3c4 100644 --- a/spec/controllers/admin/email_domain_blocks_controller_spec.rb +++ b/spec/controllers/admin/email_domain_blocks_controller_spec.rb @@ -12,13 +12,14 @@ describe 'GET #index' do around do |example| default_per_page = EmailDomainBlock.default_per_page - EmailDomainBlock.paginates_per 1 + EmailDomainBlock.paginates_per 2 example.run EmailDomainBlock.paginates_per default_per_page end it 'returns http success' do 2.times { Fabricate(:email_domain_block) } + Fabricate(:email_domain_block, allow_with_approval: true) get :index, params: { page: 2 } expect(response).to have_http_status(200) end diff --git a/spec/controllers/auth/registrations_controller_spec.rb b/spec/controllers/auth/registrations_controller_spec.rb index 37172f8d24477f..bd1c6165951ebf 100644 --- a/spec/controllers/auth/registrations_controller_spec.rb +++ b/spec/controllers/auth/registrations_controller_spec.rb @@ -135,6 +135,25 @@ end end + context 'when user has an email address requiring approval' do + subject do + Setting.registrations_mode = 'open' + Fabricate(:email_domain_block, allow_with_approval: true, domain: 'example.com') + request.headers['Accept-Language'] = accept_language + post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', agreement: 'true' } } + end + + it 'creates unapproved user and redirects to setup' do + subject + expect(response).to redirect_to auth_setup_path + + user = User.find_by(email: 'test@example.com') + expect(user).to_not be_nil + expect(user.locale).to eq(accept_language) + expect(user.approved).to be(false) + end + end + context 'with Approval-based registrations without invite' do subject do Setting.registrations_mode = 'approved' diff --git a/spec/features/admin/accounts_spec.rb b/spec/features/admin/accounts_spec.rb index ad9c51485a76db..6d7bab1844ccbf 100644 --- a/spec/features/admin/accounts_spec.rb +++ b/spec/features/admin/accounts_spec.rb @@ -22,7 +22,7 @@ context 'without selecting any accounts' do it 'displays a notice about account selection' do - click_button button_for_suspend + click_on button_for_suspend expect(page).to have_content(selection_error_text) end @@ -32,7 +32,7 @@ it 'suspends the account' do batch_checkbox_for(approved_user_account).check - click_button button_for_suspend + click_on button_for_suspend expect(approved_user_account.reload).to be_suspended end @@ -42,7 +42,7 @@ it 'approves the account user' do batch_checkbox_for(unapproved_user_account).check - click_button button_for_approve + click_on button_for_approve expect(unapproved_user_account.reload.user).to be_approved end @@ -52,7 +52,7 @@ it 'rejects and removes the account' do batch_checkbox_for(unapproved_user_account).check - click_button button_for_reject + click_on button_for_reject expect { unapproved_user_account.reload }.to raise_error(ActiveRecord::RecordNotFound) end diff --git a/spec/features/admin/custom_emojis_spec.rb b/spec/features/admin/custom_emojis_spec.rb index 3fea8f06fe6f50..8a8b6efcd119fb 100644 --- a/spec/features/admin/custom_emojis_spec.rb +++ b/spec/features/admin/custom_emojis_spec.rb @@ -16,7 +16,7 @@ context 'without selecting any records' do it 'displays a notice about selection' do - click_button button_for_enable + click_on button_for_enable expect(page).to have_content(selection_error_text) end diff --git a/spec/features/admin/domain_blocks_spec.rb b/spec/features/admin/domain_blocks_spec.rb index 4379ac91dbabb5..6a1405cdf64169 100644 --- a/spec/features/admin/domain_blocks_spec.rb +++ b/spec/features/admin/domain_blocks_spec.rb @@ -14,7 +14,7 @@ fill_in 'domain_block_domain', with: 'example.com' select I18n.t('admin.domain_blocks.new.severity.silence'), from: 'domain_block_severity' - click_button I18n.t('admin.domain_blocks.new.create') + click_on I18n.t('admin.domain_blocks.new.create') expect(DomainBlock.exists?(domain: 'example.com', severity: 'silence')).to be true expect(DomainBlockWorker).to have_received(:perform_async) @@ -27,14 +27,14 @@ fill_in 'domain_block_domain', with: 'example.com' select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity' - click_button I18n.t('admin.domain_blocks.new.create') + click_on I18n.t('admin.domain_blocks.new.create') # It doesn't immediately block but presents a confirmation screen expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com')) expect(DomainBlockWorker).to_not have_received(:perform_async) # Confirming creates a block - click_button I18n.t('admin.domain_blocks.confirm_suspension.confirm') + click_on I18n.t('admin.domain_blocks.confirm_suspension.confirm') expect(DomainBlock.exists?(domain: 'example.com', severity: 'suspend')).to be true expect(DomainBlockWorker).to have_received(:perform_async) @@ -49,14 +49,14 @@ fill_in 'domain_block_domain', with: 'example.com' select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity' - click_button I18n.t('admin.domain_blocks.new.create') + click_on I18n.t('admin.domain_blocks.new.create') # It doesn't immediately block but presents a confirmation screen expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com')) expect(DomainBlockWorker).to_not have_received(:perform_async) # Confirming updates the block - click_button I18n.t('admin.domain_blocks.confirm_suspension.confirm') + click_on I18n.t('admin.domain_blocks.confirm_suspension.confirm') expect(domain_block.reload.severity).to eq 'suspend' expect(DomainBlockWorker).to have_received(:perform_async) @@ -71,14 +71,14 @@ fill_in 'domain_block_domain', with: 'subdomain.example.com' select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity' - click_button I18n.t('admin.domain_blocks.new.create') + click_on I18n.t('admin.domain_blocks.new.create') # It doesn't immediately block but presents a confirmation screen expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'subdomain.example.com')) expect(DomainBlockWorker).to_not have_received(:perform_async) # Confirming creates the block - click_button I18n.t('admin.domain_blocks.confirm_suspension.confirm') + click_on I18n.t('admin.domain_blocks.confirm_suspension.confirm') expect(DomainBlock.where(domain: 'subdomain.example.com', severity: 'suspend')).to exist expect(DomainBlockWorker).to have_received(:perform_async) @@ -96,14 +96,14 @@ visit edit_admin_domain_block_path(domain_block) select I18n.t('admin.domain_blocks.new.severity.suspend'), from: 'domain_block_severity' - click_button I18n.t('generic.save_changes') + click_on I18n.t('generic.save_changes') # It doesn't immediately block but presents a confirmation screen expect(page).to have_title(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com')) expect(DomainBlockWorker).to_not have_received(:perform_async) # Confirming updates the block - click_button I18n.t('admin.domain_blocks.confirm_suspension.confirm') + click_on I18n.t('admin.domain_blocks.confirm_suspension.confirm') expect(DomainBlockWorker).to have_received(:perform_async) expect(domain_block.reload.severity).to eq 'suspend' diff --git a/spec/features/admin/email_domain_blocks_spec.rb b/spec/features/admin/email_domain_blocks_spec.rb index 80efe72e95c568..14959cbe74b0dd 100644 --- a/spec/features/admin/email_domain_blocks_spec.rb +++ b/spec/features/admin/email_domain_blocks_spec.rb @@ -16,7 +16,7 @@ context 'without selecting any records' do it 'displays a notice about selection' do - click_button button_for_delete + click_on button_for_delete expect(page).to have_content(selection_error_text) end diff --git a/spec/features/admin/ip_blocks_spec.rb b/spec/features/admin/ip_blocks_spec.rb index 465c8891907013..c9b16f6f78fb1d 100644 --- a/spec/features/admin/ip_blocks_spec.rb +++ b/spec/features/admin/ip_blocks_spec.rb @@ -16,7 +16,7 @@ context 'without selecting any records' do it 'displays a notice about selection' do - click_button button_for_delete + click_on button_for_delete expect(page).to have_content(selection_error_text) end diff --git a/spec/features/admin/software_updates_spec.rb b/spec/features/admin/software_updates_spec.rb index a2373d35a6080c..4a635d1a794f8f 100644 --- a/spec/features/admin/software_updates_spec.rb +++ b/spec/features/admin/software_updates_spec.rb @@ -11,13 +11,13 @@ it 'shows a link to the software updates page, which links to release notes' do visit settings_profile_path - click_link I18n.t('admin.critical_update_pending') + click_on I18n.t('admin.critical_update_pending') expect(page).to have_title(I18n.t('admin.software_updates.title')) expect(page).to have_content('99.99.99') - click_link I18n.t('admin.software_updates.release_notes') + click_on I18n.t('admin.software_updates.release_notes') expect(page).to have_current_path('https://github.com/mastodon/mastodon/releases/v99', url: true) end end diff --git a/spec/features/admin/statuses_spec.rb b/spec/features/admin/statuses_spec.rb index a21c901a921b23..531d0de9538b21 100644 --- a/spec/features/admin/statuses_spec.rb +++ b/spec/features/admin/statuses_spec.rb @@ -17,7 +17,7 @@ context 'without selecting any records' do it 'displays a notice about selection' do - click_button button_for_report + click_on button_for_report expect(page).to have_content(selection_error_text) end diff --git a/spec/features/admin/trends/links/preview_card_providers_spec.rb b/spec/features/admin/trends/links/preview_card_providers_spec.rb index cf9796abf362cd..dca89117b1079a 100644 --- a/spec/features/admin/trends/links/preview_card_providers_spec.rb +++ b/spec/features/admin/trends/links/preview_card_providers_spec.rb @@ -16,7 +16,7 @@ context 'without selecting any records' do it 'displays a notice about selection' do - click_button button_for_allow + click_on button_for_allow expect(page).to have_content(selection_error_text) end diff --git a/spec/features/admin/trends/links_spec.rb b/spec/features/admin/trends/links_spec.rb index 8b1b991a5a85cc..99638bc069ffbe 100644 --- a/spec/features/admin/trends/links_spec.rb +++ b/spec/features/admin/trends/links_spec.rb @@ -16,7 +16,7 @@ context 'without selecting any records' do it 'displays a notice about selection' do - click_button button_for_allow + click_on button_for_allow expect(page).to have_content(selection_error_text) end diff --git a/spec/features/admin/trends/statuses_spec.rb b/spec/features/admin/trends/statuses_spec.rb index a578ab05593c08..779a15d38fbec3 100644 --- a/spec/features/admin/trends/statuses_spec.rb +++ b/spec/features/admin/trends/statuses_spec.rb @@ -16,7 +16,7 @@ context 'without selecting any records' do it 'displays a notice about selection' do - click_button button_for_allow + click_on button_for_allow expect(page).to have_content(selection_error_text) end diff --git a/spec/features/admin/trends/tags_spec.rb b/spec/features/admin/trends/tags_spec.rb index 7502bc8c6f50c1..52e49c3a5d92b6 100644 --- a/spec/features/admin/trends/tags_spec.rb +++ b/spec/features/admin/trends/tags_spec.rb @@ -16,7 +16,7 @@ context 'without selecting any records' do it 'displays a notice about selection' do - click_button button_for_allow + click_on button_for_allow expect(page).to have_content(selection_error_text) end diff --git a/spec/features/captcha_spec.rb b/spec/features/captcha_spec.rb index 906aec4af977bd..15c37eb463aebb 100644 --- a/spec/features/captcha_spec.rb +++ b/spec/features/captcha_spec.rb @@ -23,7 +23,7 @@ expect(user.reload.confirmed?).to be false # It redirects to app and confirms user - click_button I18n.t('challenge.confirm') + click_on I18n.t('challenge.confirm') expect(user.reload.confirmed?).to be true expect(page).to have_current_path(/\A#{client_app.confirmation_redirect_uri}/, url: true) diff --git a/spec/features/log_in_spec.rb b/spec/features/log_in_spec.rb index 7e5196aba99564..c64e19d2b7c43f 100644 --- a/spec/features/log_in_spec.rb +++ b/spec/features/log_in_spec.rb @@ -19,7 +19,7 @@ it 'A valid email and password user is able to log in' do fill_in 'user_email', with: email fill_in 'user_password', with: password - click_button I18n.t('auth.login') + click_on I18n.t('auth.login') expect(subject).to have_css('div.app-holder') end @@ -27,7 +27,7 @@ it 'A invalid email and password user is not able to log in' do fill_in 'user_email', with: 'invalid_email' fill_in 'user_password', with: 'invalid_password' - click_button I18n.t('auth.login') + click_on I18n.t('auth.login') expect(subject).to have_css('.flash-message', text: failure_message('invalid')) end @@ -38,7 +38,7 @@ it 'A unconfirmed user is able to log in' do fill_in 'user_email', with: email fill_in 'user_password', with: password - click_button I18n.t('auth.login') + click_on I18n.t('auth.login') expect(subject).to have_css('div.admin-wrapper') end diff --git a/spec/features/oauth_spec.rb b/spec/features/oauth_spec.rb index 0e612b56a5a24f..967956cc8ea91a 100644 --- a/spec/features/oauth_spec.rb +++ b/spec/features/oauth_spec.rb @@ -20,7 +20,7 @@ expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize')) # Upon authorizing, it redirects to the apps' callback URL - click_button I18n.t('doorkeeper.authorizations.buttons.authorize') + click_on I18n.t('doorkeeper.authorizations.buttons.authorize') expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true) # It grants the app access to the account @@ -35,7 +35,7 @@ expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.deny')) # Upon denying, it redirects to the apps' callback URL - click_button I18n.t('doorkeeper.authorizations.buttons.deny') + click_on I18n.t('doorkeeper.authorizations.buttons.deny') expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true) # It does not grant the app access to the account @@ -63,17 +63,17 @@ # Failing to log-in presents the form again fill_in 'user_email', with: email fill_in 'user_password', with: 'wrong password' - click_button I18n.t('auth.login') + click_on I18n.t('auth.login') expect(page).to have_content(I18n.t('auth.login')) # Logging in redirects to an authorization page fill_in 'user_email', with: email fill_in 'user_password', with: password - click_button I18n.t('auth.login') + click_on I18n.t('auth.login') expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize')) # Upon authorizing, it redirects to the apps' callback URL - click_button I18n.t('doorkeeper.authorizations.buttons.authorize') + click_on I18n.t('doorkeeper.authorizations.buttons.authorize') expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true) # It grants the app access to the account @@ -90,17 +90,17 @@ # Failing to log-in presents the form again fill_in 'user_email', with: email fill_in 'user_password', with: 'wrong password' - click_button I18n.t('auth.login') + click_on I18n.t('auth.login') expect(page).to have_content(I18n.t('auth.login')) # Logging in redirects to an authorization page fill_in 'user_email', with: email fill_in 'user_password', with: password - click_button I18n.t('auth.login') + click_on I18n.t('auth.login') expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize')) # Upon denying, it redirects to the apps' callback URL - click_button I18n.t('doorkeeper.authorizations.buttons.deny') + click_on I18n.t('doorkeeper.authorizations.buttons.deny') expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true) # It does not grant the app access to the account @@ -120,27 +120,27 @@ # Failing to log-in presents the form again fill_in 'user_email', with: email fill_in 'user_password', with: 'wrong password' - click_button I18n.t('auth.login') + click_on I18n.t('auth.login') expect(page).to have_content(I18n.t('auth.login')) # Logging in redirects to a two-factor authentication page fill_in 'user_email', with: email fill_in 'user_password', with: password - click_button I18n.t('auth.login') + click_on I18n.t('auth.login') expect(page).to have_content(I18n.t('simple_form.hints.sessions.otp')) # Filling in an incorrect two-factor authentication code presents the form again fill_in 'user_otp_attempt', with: 'wrong' - click_button I18n.t('auth.login') + click_on I18n.t('auth.login') expect(page).to have_content(I18n.t('simple_form.hints.sessions.otp')) # Filling in the correct TOTP code redirects to an app authorization page fill_in 'user_otp_attempt', with: user.current_otp - click_button I18n.t('auth.login') + click_on I18n.t('auth.login') expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize')) # Upon authorizing, it redirects to the apps' callback URL - click_button I18n.t('doorkeeper.authorizations.buttons.authorize') + click_on I18n.t('doorkeeper.authorizations.buttons.authorize') expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true) # It grants the app access to the account @@ -157,27 +157,27 @@ # Failing to log-in presents the form again fill_in 'user_email', with: email fill_in 'user_password', with: 'wrong password' - click_button I18n.t('auth.login') + click_on I18n.t('auth.login') expect(page).to have_content(I18n.t('auth.login')) # Logging in redirects to a two-factor authentication page fill_in 'user_email', with: email fill_in 'user_password', with: password - click_button I18n.t('auth.login') + click_on I18n.t('auth.login') expect(page).to have_content(I18n.t('simple_form.hints.sessions.otp')) # Filling in an incorrect two-factor authentication code presents the form again fill_in 'user_otp_attempt', with: 'wrong' - click_button I18n.t('auth.login') + click_on I18n.t('auth.login') expect(page).to have_content(I18n.t('simple_form.hints.sessions.otp')) # Filling in the correct TOTP code redirects to an app authorization page fill_in 'user_otp_attempt', with: user.current_otp - click_button I18n.t('auth.login') + click_on I18n.t('auth.login') expect(page).to have_content(I18n.t('doorkeeper.authorizations.buttons.authorize')) # Upon denying, it redirects to the apps' callback URL - click_button I18n.t('doorkeeper.authorizations.buttons.deny') + click_on I18n.t('doorkeeper.authorizations.buttons.deny') expect(page).to have_current_path(/\A#{client_app.redirect_uri}/, url: true) # It does not grant the app access to the account diff --git a/spec/lib/mastodon/cli/main_spec.rb b/spec/lib/mastodon/cli/main_spec.rb index 59f1fc47841954..081cd2dd47df0a 100644 --- a/spec/lib/mastodon/cli/main_spec.rb +++ b/spec/lib/mastodon/cli/main_spec.rb @@ -20,4 +20,157 @@ .to output_results(Mastodon::Version.to_s) end end + + describe '#self_destruct' do + let(:action) { :self_destruct } + + context 'with self destruct mode enabled' do + before do + allow(SelfDestructHelper).to receive(:self_destruct?).and_return(true) + end + + context 'with pending accounts' do + before { Fabricate(:account) } + + it 'reports about pending accounts' do + expect { subject } + .to output_results( + 'already enabled', + 'still pending deletion' + ) + .and raise_error(SystemExit) + end + end + + context 'with sidekiq notices being processed' do + before do + Account.delete_all + stats_double = instance_double(Sidekiq::Stats, enqueued: 5) + allow(Sidekiq::Stats).to receive(:new).and_return(stats_double) + end + + it 'reports about notices' do + expect { subject } + .to output_results( + 'already enabled', + 'notices are still being' + ) + .and raise_error(SystemExit) + end + end + + context 'with sidekiq failed deliveries' do + before do + Account.delete_all + stats_double = instance_double(Sidekiq::Stats, enqueued: 0, retry_size: 10) + allow(Sidekiq::Stats).to receive(:new).and_return(stats_double) + end + + it 'reports about notices' do + expect { subject } + .to output_results( + 'already enabled', + 'some have failed and are scheduled' + ) + .and raise_error(SystemExit) + end + end + + context 'with self descruct mode ready' do + before do + Account.delete_all + stats_double = instance_double(Sidekiq::Stats, enqueued: 0, retry_size: 0) + allow(Sidekiq::Stats).to receive(:new).and_return(stats_double) + end + + it 'reports about notices' do + expect { subject } + .to output_results( + 'already enabled', + 'can safely delete all data' + ) + .and raise_error(SystemExit) + end + end + end + + context 'with self destruct mode disabled' do + before do + allow(SelfDestructHelper).to receive(:self_destruct?).and_return(false) + end + + context 'with an incorrect response to hostname' do + before do + answer_hostname_incorrectly + end + + it 'exits silently' do + expect { subject } + .to raise_error(SystemExit) + end + end + + context 'with a correct response to hostname but no to proceed' do + before do + answer_hostname_correctly + decline_proceed + end + + it 'passes first step but stops before instructions' do + expect { subject } + .to output_results('operation WILL NOT') + .and raise_error(SystemExit) + end + end + + context 'with a correct response to hostname and yes to proceed' do + before do + answer_hostname_correctly + accept_proceed + end + + it 'instructs to set the appropriate environment variable' do + expect { subject } + .to output_results( + 'operation WILL NOT', + 'the following variable' + ) + end + end + + private + + def answer_hostname_incorrectly + allow(cli.shell) + .to receive(:ask) + .with('Type in the domain of the server to confirm:') + .and_return('wrong.host') + .once + end + + def answer_hostname_correctly + allow(cli.shell) + .to receive(:ask) + .with('Type in the domain of the server to confirm:') + .and_return(Rails.configuration.x.local_domain) + .once + end + + def decline_proceed + allow(cli.shell) + .to receive(:no?) + .with('Are you sure you want to proceed?') + .and_return(true) + .once + end + + def accept_proceed + allow(cli.shell) + .to receive(:no?) + .with('Are you sure you want to proceed?') + .and_return(false) + .once + end + end + end end diff --git a/spec/lib/mastodon/cli/media_spec.rb b/spec/lib/mastodon/cli/media_spec.rb index 24e1467a3ca8c9..071bcd9e34a98f 100644 --- a/spec/lib/mastodon/cli/media_spec.rb +++ b/spec/lib/mastodon/cli/media_spec.rb @@ -184,4 +184,58 @@ end end end + + describe '#remove_orphans' do + let(:action) { :remove_orphans } + + before do + FileUtils.mkdir_p Rails.public_path.join('system') + end + + context 'without any options' do + it 'runs without error' do + expect { subject } + .to output_results('Removed', 'orphans (approx') + end + end + + context 'when in azure mode' do + before do + allow(Paperclip::Attachment).to receive(:default_options).and_return(storage: :azure) + end + + it 'warns about usage and exits' do + expect { subject } + .to output_results('azure storage driver is not supported') + .and raise_error(SystemExit) + end + end + + context 'when in fog mode' do + before do + allow(Paperclip::Attachment).to receive(:default_options).and_return(storage: :fog) + end + + it 'warns about usage and exits' do + expect { subject } + .to output_results('fog storage driver is not supported') + .and raise_error(SystemExit) + end + end + + context 'when in filesystem mode' do + before do + allow(File).to receive(:delete).and_return(true) + media_attachment.delete + end + + let(:media_attachment) { Fabricate(:media_attachment) } + + it 'removes the unlinked files' do + expect { subject } + .to output_results('Removed', 'orphans (approx') + expect(File).to have_received(:delete).with(media_attachment.file.path) + end + end + end end diff --git a/spec/lib/request_pool_spec.rb b/spec/lib/request_pool_spec.rb index f179e6ca94a079..bdb0859d764a36 100644 --- a/spec/lib/request_pool_spec.rb +++ b/spec/lib/request_pool_spec.rb @@ -33,11 +33,13 @@ subject - threads = Array.new(20) do |_i| + threads = Array.new(3) do Thread.new do - 20.times do + 2.times do subject.with('http://example.com') do |http_client| http_client.get('/').flush + # Nudge scheduler to yield and exercise the full pool + sleep(0) end end end diff --git a/spec/requests/content_security_policy_spec.rb b/spec/requests/content_security_policy_spec.rb index 7610e698cdf4cd..d4cc40bce5d976 100644 --- a/spec/requests/content_security_policy_spec.rb +++ b/spec/requests/content_security_policy_spec.rb @@ -20,7 +20,7 @@ "form-action 'self'", "child-src 'self' blob: https://cb6e6126.ngrok.io", "worker-src 'self' blob: https://cb6e6126.ngrok.io", - "connect-src 'self' data: blob: https://cb6e6126.ngrok.io ws://localhost:4000", + "connect-src 'self' data: blob: https://cb6e6126.ngrok.io ws://cb6e6126.ngrok.io:4000", "script-src 'self' https://cb6e6126.ngrok.io 'wasm-unsafe-eval'" ) end diff --git a/spec/requests/signature_verification_spec.rb b/spec/requests/signature_verification_spec.rb index b753750b840595..401828c4a3c5d1 100644 --- a/spec/requests/signature_verification_spec.rb +++ b/spec/requests/signature_verification_spec.rb @@ -94,6 +94,72 @@ end end + context 'with a valid signature on a GET request that has a query string' do + let(:signature_header) do + 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="SDMa4r/DQYMXYxVgYO2yEqGWWUXugKjVuz0I8dniQAk+aunzBaF2aPu+4grBfawAshlx1Xytl8lhb0H2MllEz16/tKY7rUrb70MK0w8ohXgpb0qs3YvQgdj4X24L1x2MnkFfKHR/J+7TBlnivq0HZqXm8EIkPWLv+eQxu8fbowLwHIVvRd/3t6FzvcfsE0UZKkoMEX02542MhwSif6cu7Ec/clsY9qgKahb9JVGOGS1op9Lvg/9y1mc8KCgD83U5IxVygYeYXaVQ6gixA9NgZiTCwEWzHM5ELm7w5hpdLFYxYOHg/3G3fiqJzpzNQAcCD4S4JxfE7hMI0IzVlNLT6A=="' # rubocop:disable Layout/LineLength + end + + it 'successfuly verifies signature', :aggregate_failures do + expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success?foo=42', { 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' }) + + get '/activitypub/success?foo=42', headers: { + 'Host' => 'www.example.com', + 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', + 'Signature' => signature_header, + } + + expect(response).to have_http_status(200) + expect(body_as_json).to match( + signed_request: true, + signature_actor_id: actor.id.to_s + ) + end + end + + context 'when the query string is missing from the signature verification (compatibility quirk)' do + let(:signature_header) do + 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="' # rubocop:disable Layout/LineLength + end + + it 'successfuly verifies signature', :aggregate_failures do + expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' }) + + get '/activitypub/success?foo=42', headers: { + 'Host' => 'www.example.com', + 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', + 'Signature' => signature_header, + } + + expect(response).to have_http_status(200) + expect(body_as_json).to match( + signed_request: true, + signature_actor_id: actor.id.to_s + ) + end + end + + context 'with mismatching query string' do + let(:signature_header) do + 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="SDMa4r/DQYMXYxVgYO2yEqGWWUXugKjVuz0I8dniQAk+aunzBaF2aPu+4grBfawAshlx1Xytl8lhb0H2MllEz16/tKY7rUrb70MK0w8ohXgpb0qs3YvQgdj4X24L1x2MnkFfKHR/J+7TBlnivq0HZqXm8EIkPWLv+eQxu8fbowLwHIVvRd/3t6FzvcfsE0UZKkoMEX02542MhwSif6cu7Ec/clsY9qgKahb9JVGOGS1op9Lvg/9y1mc8KCgD83U5IxVygYeYXaVQ6gixA9NgZiTCwEWzHM5ELm7w5hpdLFYxYOHg/3G3fiqJzpzNQAcCD4S4JxfE7hMI0IzVlNLT6A=="' # rubocop:disable Layout/LineLength + end + + it 'fails to verify signature', :aggregate_failures do + expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success?foo=42', { 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' }) + + get '/activitypub/success?foo=43', headers: { + 'Host' => 'www.example.com', + 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', + 'Signature' => signature_header, + } + + expect(body_as_json).to match( + signed_request: true, + signature_actor_id: nil, + error: anything + ) + end + end + context 'with a mismatching path' do it 'fails to verify signature', :aggregate_failures do get '/activitypub/alternative-path', headers: { diff --git a/spec/services/app_sign_up_service_spec.rb b/spec/services/app_sign_up_service_spec.rb index 0adb473f17770b..86e64dab21cdd5 100644 --- a/spec/services/app_sign_up_service_spec.rb +++ b/spec/services/app_sign_up_service_spec.rb @@ -27,6 +27,27 @@ end end + context 'when the email address requires approval' do + before do + Setting.registrations_mode = 'open' + Fabricate(:email_domain_block, allow_with_approval: true, domain: 'email.com') + end + + it 'creates an unapproved user', :aggregate_failures do + access_token = subject.call(app, remote_ip, params) + expect(access_token).to_not be_nil + expect(access_token.scopes.to_s).to eq 'read write' + + user = User.find_by(id: access_token.resource_owner_id) + expect(user).to_not be_nil + expect(user.confirmed?).to be false + expect(user.approved?).to be false + + expect(user.account).to_not be_nil + expect(user.invite_request).to be_nil + end + end + context 'when registrations are closed' do before do Setting.registrations_mode = 'none' diff --git a/spec/support/stories/profile_stories.rb b/spec/support/stories/profile_stories.rb index 82667ca080c99b..2b345ddef10b31 100644 --- a/spec/support/stories/profile_stories.rb +++ b/spec/support/stories/profile_stories.rb @@ -18,7 +18,7 @@ def as_a_logged_in_user visit new_user_session_path fill_in 'user_email', with: email fill_in 'user_password', with: password - click_button I18n.t('auth.login') + click_on I18n.t('auth.login') end def with_alice_as_local_user diff --git a/spec/system/new_statuses_spec.rb b/spec/system/new_statuses_spec.rb index 244101f4d4fd6b..02f2e283931eec 100644 --- a/spec/system/new_statuses_spec.rb +++ b/spec/system/new_statuses_spec.rb @@ -24,7 +24,7 @@ within('.compose-form') do fill_in "What's on your mind?", with: status_text - click_button 'Publish!' + click_on 'Publish!' end expect(subject).to have_css('.status__content__text', text: status_text) @@ -37,7 +37,7 @@ within('.compose-form') do fill_in "What's on your mind?", with: status_text - click_button 'Publish!' + click_on 'Publish!' end expect(subject).to have_css('.status__content__text', text: status_text) diff --git a/streaming/index.js b/streaming/index.js index aaf37534fcedf5..14a8c92c6a6008 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -5,6 +5,7 @@ const http = require('http'); const path = require('path'); const url = require('url'); +const cors = require('cors'); const dotenv = require('dotenv'); const express = require('express'); const Redis = require('ioredis'); @@ -187,6 +188,7 @@ const startServer = async () => { const pgPool = new pg.Pool(pgConfigFromEnv(process.env)); const server = http.createServer(app); + app.use(cors()); /** * @type {Object.): void>>} @@ -327,19 +329,6 @@ const startServer = async () => { } }; - /** - * @param {any} req - * @param {any} res - * @param {function(Error=): void} next - */ - const allowCrossDomain = (req, res, next) => { - res.header('Access-Control-Allow-Origin', '*'); - res.header('Access-Control-Allow-Headers', 'Authorization, Accept, Cache-Control'); - res.header('Access-Control-Allow-Methods', 'GET, OPTIONS'); - - next(); - }; - /** * @param {any} req * @param {any} res @@ -1042,7 +1031,6 @@ const startServer = async () => { api.use(setRequestId); api.use(setRemoteAddress); - api.use(allowCrossDomain); api.use(authenticationMiddleware); api.use(errorMiddleware); diff --git a/streaming/package.json b/streaming/package.json index 0cfc5b32765fc6..2d70acb829cac3 100644 --- a/streaming/package.json +++ b/streaming/package.json @@ -16,6 +16,7 @@ "check:types": "tsc --noEmit" }, "dependencies": { + "cors": "^2.8.5", "dotenv": "^16.0.3", "express": "^4.18.2", "ioredis": "^5.3.2", @@ -28,6 +29,7 @@ "ws": "^8.12.1" }, "devDependencies": { + "@types/cors": "^2.8.16", "@types/express": "^4.17.17", "@types/npmlog": "^7.0.0", "@types/pg": "^8.6.6", diff --git a/yarn.lock b/yarn.lock index 044015ee5911fd..7df8bfef9b840b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2452,7 +2452,7 @@ __metadata: stacktrace-js: "npm:^2.0.2" stringz: "npm:^2.1.0" stylelint: "npm:^16.0.2" - stylelint-config-standard-scss: "npm:^12.0.0" + stylelint-config-standard-scss: "npm:^13.0.0" substring-trie: "npm:^1.0.2" terser-webpack-plugin: "npm:^4.2.3" tesseract.js: "npm:^2.1.5" @@ -2487,12 +2487,14 @@ __metadata: version: 0.0.0-use.local resolution: "@mastodon/streaming@workspace:streaming" dependencies: + "@types/cors": "npm:^2.8.16" "@types/express": "npm:^4.17.17" "@types/npmlog": "npm:^7.0.0" "@types/pg": "npm:^8.6.6" "@types/uuid": "npm:^9.0.0" "@types/ws": "npm:^8.5.9" bufferutil: "npm:^4.0.7" + cors: "npm:^2.8.5" dotenv: "npm:^16.0.3" eslint-define-config: "npm:^2.0.0" express: "npm:^4.18.2" @@ -3046,6 +3048,15 @@ __metadata: languageName: node linkType: hard +"@types/cors@npm:^2.8.16": + version: 2.8.16 + resolution: "@types/cors@npm:2.8.16" + dependencies: + "@types/node": "npm:*" + checksum: ebcfb325b102739249bbaa4845cf1cf4830baf5490a32bcd1a85cd9b8c4d4b9eaaaea94423e454b5b7c9da77e46a64db80d2381d3bc3f940d15d13814e87b70a + languageName: node + linkType: hard + "@types/emoji-mart@npm:^3.0.9": version: 3.0.14 resolution: "@types/emoji-mart@npm:3.0.14" @@ -4697,13 +4708,13 @@ __metadata: linkType: hard "axios@npm:^1.4.0": - version: 1.6.3 - resolution: "axios@npm:1.6.3" + version: 1.6.4 + resolution: "axios@npm:1.6.4" dependencies: - follow-redirects: "npm:^1.15.0" + follow-redirects: "npm:^1.15.4" form-data: "npm:^4.0.0" proxy-from-env: "npm:^1.1.0" - checksum: dcc6d982353db33e6893ef01cdf81d0a0548dbd8fba0cb046dc4aee1a6a16226721faa4c2a13b2673d47130509629cdb93bb991b3a2bd4ef17a5ac27a8bba0da + checksum: daac697fa1ea9865cb48e9edb7eacd99e8a9214997f2d8e886cb61c380a613e5c270078bfc153ac96206680106c223f005f0e4bf2f3b2ddd88e559ecf970521f languageName: node linkType: hard @@ -5216,7 +5227,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.21.10, browserslist@npm:^4.21.4, browserslist@npm:^4.22.2": +"browserslist@npm:^4.0.0, browserslist@npm:^4.21.10, browserslist@npm:^4.22.2": version: 4.22.2 resolution: "browserslist@npm:4.22.2" dependencies: @@ -5992,6 +6003,16 @@ __metadata: languageName: node linkType: hard +"cors@npm:^2.8.5": + version: 2.8.5 + resolution: "cors@npm:2.8.5" + dependencies: + object-assign: "npm:^4" + vary: "npm:^1" + checksum: 373702b7999409922da80de4a61938aabba6929aea5b6fd9096fefb9e8342f626c0ebd7507b0e8b0b311380744cc985f27edebc0a26e0ddb784b54e1085de761 + languageName: node + linkType: hard + "cosmiconfig@npm:^7.0.0": version: 7.1.0 resolution: "cosmiconfig@npm:7.1.0" @@ -6147,7 +6168,7 @@ __metadata: languageName: node linkType: hard -"css-declaration-sorter@npm:^7.0.0": +"css-declaration-sorter@npm:^7.1.1": version: 7.1.1 resolution: "css-declaration-sorter@npm:7.1.1" peerDependencies: @@ -6235,7 +6256,7 @@ __metadata: languageName: node linkType: hard -"css-tree@npm:^2.2.1, css-tree@npm:^2.3.1": +"css-tree@npm:^2.3.1": version: 2.3.1 resolution: "css-tree@npm:2.3.1" dependencies: @@ -6285,42 +6306,42 @@ __metadata: languageName: node linkType: hard -"cssnano-preset-default@npm:^6.0.2": - version: 6.0.2 - resolution: "cssnano-preset-default@npm:6.0.2" +"cssnano-preset-default@npm:^6.0.3": + version: 6.0.3 + resolution: "cssnano-preset-default@npm:6.0.3" dependencies: - css-declaration-sorter: "npm:^7.0.0" + css-declaration-sorter: "npm:^7.1.1" cssnano-utils: "npm:^4.0.1" postcss-calc: "npm:^9.0.1" - postcss-colormin: "npm:^6.0.1" - postcss-convert-values: "npm:^6.0.1" + postcss-colormin: "npm:^6.0.2" + postcss-convert-values: "npm:^6.0.2" postcss-discard-comments: "npm:^6.0.1" postcss-discard-duplicates: "npm:^6.0.1" postcss-discard-empty: "npm:^6.0.1" postcss-discard-overridden: "npm:^6.0.1" - postcss-merge-longhand: "npm:^6.0.1" - postcss-merge-rules: "npm:^6.0.2" + postcss-merge-longhand: "npm:^6.0.2" + postcss-merge-rules: "npm:^6.0.3" postcss-minify-font-values: "npm:^6.0.1" postcss-minify-gradients: "npm:^6.0.1" - postcss-minify-params: "npm:^6.0.1" - postcss-minify-selectors: "npm:^6.0.1" + postcss-minify-params: "npm:^6.0.2" + postcss-minify-selectors: "npm:^6.0.2" postcss-normalize-charset: "npm:^6.0.1" postcss-normalize-display-values: "npm:^6.0.1" postcss-normalize-positions: "npm:^6.0.1" postcss-normalize-repeat-style: "npm:^6.0.1" postcss-normalize-string: "npm:^6.0.1" postcss-normalize-timing-functions: "npm:^6.0.1" - postcss-normalize-unicode: "npm:^6.0.1" + postcss-normalize-unicode: "npm:^6.0.2" postcss-normalize-url: "npm:^6.0.1" postcss-normalize-whitespace: "npm:^6.0.1" postcss-ordered-values: "npm:^6.0.1" - postcss-reduce-initial: "npm:^6.0.1" + postcss-reduce-initial: "npm:^6.0.2" postcss-reduce-transforms: "npm:^6.0.1" - postcss-svgo: "npm:^6.0.1" - postcss-unique-selectors: "npm:^6.0.1" + postcss-svgo: "npm:^6.0.2" + postcss-unique-selectors: "npm:^6.0.2" peerDependencies: postcss: ^8.4.31 - checksum: c6f97674704c3a2a2473440549eac38ac722feebabbd39f2d4d1b8fae7f137f8fd0dfb88929e1ff737d54008de583c39e96f9dc450f2d71f8be6fc3bac2840a3 + checksum: d100a1f8ab71adbb6df85e00f4a9e5d04ac06fc50343157eef853aded3f75dd0489dd845a5b2fb43ca701bd88c39c5aa88673f842bc1f94f4318c7b38ced1963 languageName: node linkType: hard @@ -6334,23 +6355,14 @@ __metadata: linkType: hard "cssnano@npm:^6.0.1": - version: 6.0.2 - resolution: "cssnano@npm:6.0.2" + version: 6.0.3 + resolution: "cssnano@npm:6.0.3" dependencies: - cssnano-preset-default: "npm:^6.0.2" + cssnano-preset-default: "npm:^6.0.3" lilconfig: "npm:^3.0.0" peerDependencies: postcss: ^8.4.31 - checksum: 5f4146a6c8937d24b0d1d33e3acd85db7913c7558cc80b23169f86c9a552d091a26e0af6adcc535f8355561872f797a917b9353e38fe935bbaf08ec2b66f5ff8 - languageName: node - linkType: hard - -"csso@npm:5.0.5": - version: 5.0.5 - resolution: "csso@npm:5.0.5" - dependencies: - css-tree: "npm:~2.2.0" - checksum: ab4beb1e97dd7e207c10e9925405b45f15a6cd1b4880a8686ad573aa6d476aed28b4121a666cffd26c37a26179f7b54741f7c257543003bfb244d06a62ad569b + checksum: d1669eb987fd96159bae262ef2f76c1a64fffefe8fa593918a6bda377977798b60fb4a6a871a9b9a9deb11258130ee254fdb8c3144769b3060ad9f2a95a4ed0a languageName: node linkType: hard @@ -6363,6 +6375,15 @@ __metadata: languageName: node linkType: hard +"csso@npm:^5.0.5": + version: 5.0.5 + resolution: "csso@npm:5.0.5" + dependencies: + css-tree: "npm:~2.2.0" + checksum: ab4beb1e97dd7e207c10e9925405b45f15a6cd1b4880a8686ad573aa6d476aed28b4121a666cffd26c37a26179f7b54741f7c257543003bfb244d06a62ad569b + languageName: node + linkType: hard + "cssom@npm:^0.5.0": version: 0.5.0 resolution: "cssom@npm:0.5.0" @@ -8191,13 +8212,13 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.15.0": - version: 1.15.3 - resolution: "follow-redirects@npm:1.15.3" +"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.15.4": + version: 1.15.4 + resolution: "follow-redirects@npm:1.15.4" peerDependenciesMeta: debug: optional: true - checksum: 915a2cf22e667bdf47b1a43cc6b7dce14d95039e9bbf9a24d0e739abfbdfa00077dd43c86d4a7a19efefcc7a99af144920a175eedc3888d268af5df67c272ee5 + checksum: 5f37ed9170c9eb19448c5418fdb0f2b73f644b5364834e70791a76ecc7db215246f9773bbef4852cfae4067764ffc852e047f744b661b0211532155b73556a6a languageName: node linkType: hard @@ -11981,7 +12002,7 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4.0.1, object-assign@npm:^4.1.1": +"object-assign@npm:^4, object-assign@npm:^4.0.1, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" checksum: 1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 @@ -12750,29 +12771,29 @@ __metadata: languageName: node linkType: hard -"postcss-colormin@npm:^6.0.1": - version: 6.0.1 - resolution: "postcss-colormin@npm:6.0.1" +"postcss-colormin@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-colormin@npm:6.0.2" dependencies: - browserslist: "npm:^4.21.4" + browserslist: "npm:^4.22.2" caniuse-api: "npm:^3.0.0" colord: "npm:^2.9.1" postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.4.31 - checksum: b0056812b3436b05b6b84284a1ebe68a72299f23e7eeb0b7b40a775978d06a1cbe235f3665e3f694f5de76fe7d9b93db607536d07697b31a59fd4e8705e5b64d + checksum: 229681f9b89ba0909b4c69563837b0c32cc3d1c17ed1b00c33d4abfb0a0ef455124968e4885b5f92c64482e92074cd1958018ec111ed5d118f1e24baeda19c14 languageName: node linkType: hard -"postcss-convert-values@npm:^6.0.1": - version: 6.0.1 - resolution: "postcss-convert-values@npm:6.0.1" +"postcss-convert-values@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-convert-values@npm:6.0.2" dependencies: - browserslist: "npm:^4.21.4" + browserslist: "npm:^4.22.2" postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.4.31 - checksum: 53b951d7475206969c63b8427a2dea0ccba0a7cb08122e5f05aee8d12b09c870c070b101c9f8eceda76ff4d0fd9e5fa9385e83f143d658bb729dbb6a3583b872 + checksum: 882d0b7839ef07ac8ffbf9cb48db0f610939a3496bd0321c7f23096ead676f13e09ab3d9c20ff3dbe2c887e855826051ca7dffeaffce5068cfdc9aaa573a3842 languageName: node linkType: hard @@ -12835,29 +12856,29 @@ __metadata: languageName: node linkType: hard -"postcss-merge-longhand@npm:^6.0.1": - version: 6.0.1 - resolution: "postcss-merge-longhand@npm:6.0.1" +"postcss-merge-longhand@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-merge-longhand@npm:6.0.2" dependencies: postcss-value-parser: "npm:^4.2.0" - stylehacks: "npm:^6.0.1" + stylehacks: "npm:^6.0.2" peerDependencies: postcss: ^8.4.31 - checksum: 2c0eb81b6c6d3d2af3b129c46d10317b7923f218db1cadcb4723091fb951fe4624638002b65f235151129d4ce9b4775a6ed0d5fa13419c0df580f72e15fa4ad3 + checksum: 2b3fae51bffc5962258d638bc7f415237593b515f369233e023f0eae5b13116297463c04b8c47a7b7af51cba5faaa7f517b653f6123e51935d670d4d4de5a26d languageName: node linkType: hard -"postcss-merge-rules@npm:^6.0.2": - version: 6.0.2 - resolution: "postcss-merge-rules@npm:6.0.2" +"postcss-merge-rules@npm:^6.0.3": + version: 6.0.3 + resolution: "postcss-merge-rules@npm:6.0.3" dependencies: - browserslist: "npm:^4.21.4" + browserslist: "npm:^4.22.2" caniuse-api: "npm:^3.0.0" cssnano-utils: "npm:^4.0.1" - postcss-selector-parser: "npm:^6.0.5" + postcss-selector-parser: "npm:^6.0.15" peerDependencies: postcss: ^8.4.31 - checksum: 138a9921423420116b20e5761a1139392f0bcfcf34264fe11e254917d9c3170e3c0478a1b409e227d22bb0d9820b0168a871a240215d114e9c1e218ee6c132e6 + checksum: c8355db11aa60bedcb1e6535fcd70f6ecec2dadd5c2975d3accf0eedbc92af782ac1f5e91a53866816ce332e4cbf1b94749a9425067935be066bc0c974e30fee languageName: node linkType: hard @@ -12885,27 +12906,27 @@ __metadata: languageName: node linkType: hard -"postcss-minify-params@npm:^6.0.1": - version: 6.0.1 - resolution: "postcss-minify-params@npm:6.0.1" +"postcss-minify-params@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-minify-params@npm:6.0.2" dependencies: - browserslist: "npm:^4.21.4" + browserslist: "npm:^4.22.2" cssnano-utils: "npm:^4.0.1" postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.4.31 - checksum: 0b34817f032ec9793fad4d33f3ba5551531073a36c9120d77194a3edeee860132951ed6954913494e5a6752ae8da1bc5cdb2a44fa5f428621afae8edddb0ca80 + checksum: 6638460d2be4a2eca8adee8409b70d6c6a19aff8cf93fda1b45c9da627b258b6baaa6acb48f51d26cd287704a235f9c9ae2e4744335b1fd47e163177c33896df languageName: node linkType: hard -"postcss-minify-selectors@npm:^6.0.1": - version: 6.0.1 - resolution: "postcss-minify-selectors@npm:6.0.1" +"postcss-minify-selectors@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-minify-selectors@npm:6.0.2" dependencies: - postcss-selector-parser: "npm:^6.0.5" + postcss-selector-parser: "npm:^6.0.15" peerDependencies: postcss: ^8.4.31 - checksum: ffc7ebb286beda2b2aa0ed13abafc89b5ffe232a48d57d3f2b9f69e167e354482a6f5279e9118bed753bf6e82d3cfb21228a6b07acd93d0dc9e01bbf0e7ebc75 + checksum: 5437b586c1237fc442e7e6078d4f23c987efc456366368b07a0da67332b04bd55821cedf0441e73e1209689f63139e272d930508e2963ba6e27c46561a661128 languageName: node linkType: hard @@ -13017,15 +13038,15 @@ __metadata: languageName: node linkType: hard -"postcss-normalize-unicode@npm:^6.0.1": - version: 6.0.1 - resolution: "postcss-normalize-unicode@npm:6.0.1" +"postcss-normalize-unicode@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-normalize-unicode@npm:6.0.2" dependencies: - browserslist: "npm:^4.21.4" + browserslist: "npm:^4.22.2" postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.4.31 - checksum: 8057748dade94dc2dd63a3b75a85e394c2e9a7076053886ff08aa9b7729d383f204eda52d882e5361ae1ec493036e90b2e18dcc5f8c9b3a8f1cbfada12bcc05b + checksum: ea696194f65ad31de2a9c022f1946a07c298f04070706d88a20061845e1e052e645c74b5bc785595814db87d14e435f85e968a44855dedc207d8c0b5d43b1aee languageName: node linkType: hard @@ -13063,15 +13084,15 @@ __metadata: languageName: node linkType: hard -"postcss-reduce-initial@npm:^6.0.1": - version: 6.0.1 - resolution: "postcss-reduce-initial@npm:6.0.1" +"postcss-reduce-initial@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-reduce-initial@npm:6.0.2" dependencies: - browserslist: "npm:^4.21.4" + browserslist: "npm:^4.22.2" caniuse-api: "npm:^3.0.0" peerDependencies: postcss: ^8.4.31 - checksum: 3f8f6c26ceeb79ddc285b0e01183fe30e911dd26b3abcdca56568e2bef3747f2b7f22ee3f9117e9752e1e93c10bcd88bd6a2842ca525b54336726292ebd3c3ad + checksum: d35ad6f9725cdceb390a97a461e8594df7fbed4c55497c90d07c42f8343bf80139e720eaebc580bf480bf10e92959490aa308af66d8802ba71c327bdf08c93a1 languageName: node linkType: hard @@ -13111,36 +13132,36 @@ __metadata: languageName: node linkType: hard -"postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.0.13, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.0.5": - version: 6.0.13 - resolution: "postcss-selector-parser@npm:6.0.13" +"postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.0.13, postcss-selector-parser@npm:^6.0.15, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4": + version: 6.0.15 + resolution: "postcss-selector-parser@npm:6.0.15" dependencies: cssesc: "npm:^3.0.0" util-deprecate: "npm:^1.0.2" - checksum: 51f099b27f7c7198ea1826470ef0adfa58b3bd3f59b390fda123baa0134880a5fa9720137b6009c4c1373357b144f700b0edac73335d0067422063129371444e + checksum: 48b425d6cef497bcf6b7d136f6fd95cfca43026955e07ec9290d3c15457de3a862dbf251dd36f42c07a0d5b5ab6f31e41acefeff02528995a989b955505e440b languageName: node linkType: hard -"postcss-svgo@npm:^6.0.1": - version: 6.0.1 - resolution: "postcss-svgo@npm:6.0.1" +"postcss-svgo@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-svgo@npm:6.0.2" dependencies: postcss-value-parser: "npm:^4.2.0" - svgo: "npm:^3.0.5" + svgo: "npm:^3.2.0" peerDependencies: postcss: ^8.4.31 - checksum: 021da9b0d0696fce970f407891a0d6c05e51d1908af435026e0cd5936a75cd8502a7d504cd0e6a33b6f3369fee41f01b848e5bd919aecc3e804ce6308e91a6cc + checksum: db607404d09af256c7957a0ace822d651a00a52a1796da603f93ba3f0a095ac7595e1f624b9dc53f362ab10e382845d7873f485980f9c92fcb86256833f5e835 languageName: node linkType: hard -"postcss-unique-selectors@npm:^6.0.1": - version: 6.0.1 - resolution: "postcss-unique-selectors@npm:6.0.1" +"postcss-unique-selectors@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-unique-selectors@npm:6.0.2" dependencies: - postcss-selector-parser: "npm:^6.0.5" + postcss-selector-parser: "npm:^6.0.15" peerDependencies: postcss: ^8.4.31 - checksum: 637e35775d0ee8fbcf4a81b28d3832c5076de7c0232eb7769d4fbbf783f26793e2ec95e18461ae3b9f5f5cd63c3de9db102464487ba2488d4947aad24dc8841f + checksum: a0fe112d1094f90e1bfcfd2174a74b2fd0630a24449e9942923d02956c7d64ea4add5adede53d9efb3f6d40cd388ac150d032a115f6a46b73d5f3d3d26fa1bb7 languageName: node linkType: hard @@ -13708,8 +13729,8 @@ __metadata: linkType: hard "react-redux-loading-bar@npm:^5.0.4": - version: 5.0.7 - resolution: "react-redux-loading-bar@npm:5.0.7" + version: 5.0.8 + resolution: "react-redux-loading-bar@npm:5.0.8" dependencies: prop-types: "npm:^15.7.2" react-lifecycles-compat: "npm:^3.0.4" @@ -13718,7 +13739,7 @@ __metadata: react-dom: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 react-redux: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 redux: ^3.0.0 || ^4.0.0 || ^5.0.0 - checksum: 45333093e7d28df923a657ad89ffe4673d7bd135ef57c0143fb4d868f21b57aeb9044691f553f7d2afbcc9080a1f8cd3cec5b274c80cb57faf0e87a70f7a2cce + checksum: 797c1abf8bcc947feb127380e6d363db264c12bc94e578d635f86f1d806b0ec714dc3723e54c884937448b17f9042cfc995fe7a1deaf558efc01681e43e4669c languageName: node linkType: hard @@ -15620,15 +15641,15 @@ __metadata: languageName: node linkType: hard -"stylehacks@npm:^6.0.1": - version: 6.0.1 - resolution: "stylehacks@npm:6.0.1" +"stylehacks@npm:^6.0.2": + version: 6.0.2 + resolution: "stylehacks@npm:6.0.2" dependencies: - browserslist: "npm:^4.21.4" - postcss-selector-parser: "npm:^6.0.4" + browserslist: "npm:^4.22.2" + postcss-selector-parser: "npm:^6.0.15" peerDependencies: postcss: ^8.4.31 - checksum: 0877016f5b2a06b8ceaf39382b0c33da11ea93268209444f67f29b1ce465994058f305fc3bc90dda21e8664c959561fbb06ba12b82289c3b26ba832c6979d513 + checksum: 658cac8b28edcb94d1db67808ab3aaa511cb1b9293594fc95607ee42ac4f57e742d9a1fa3ff5d5849db692971dc2a310e9ac1ed0bd4ea4bc48c80f5a6ef823fc languageName: node linkType: hard @@ -15658,30 +15679,30 @@ __metadata: languageName: node linkType: hard -"stylelint-config-standard-scss@npm:^12.0.0": - version: 12.0.0 - resolution: "stylelint-config-standard-scss@npm:12.0.0" +"stylelint-config-standard-scss@npm:^13.0.0": + version: 13.0.0 + resolution: "stylelint-config-standard-scss@npm:13.0.0" dependencies: stylelint-config-recommended-scss: "npm:^14.0.0" - stylelint-config-standard: "npm:^35.0.0" + stylelint-config-standard: "npm:^36.0.0" peerDependencies: postcss: ^8.3.3 - stylelint: ^16.0.2 + stylelint: ^16.1.0 peerDependenciesMeta: postcss: optional: true - checksum: 7f3ccfb4175f9c50b69d30ca35a97887008c5ba493dbe7d5bce0b57b1eafd21b268177b82404368e7780600077cba784f98e1046671724be3b29a00c6a7913a4 + checksum: 4abf317676184f4aaace6ce72b9fc9e2dffe051d43dd5637afc5803b062ea381e2807ae983c045dff22e96af58388a8b1fe9a8bdda9f97bc3660280cf24fb4d3 languageName: node linkType: hard -"stylelint-config-standard@npm:^35.0.0": - version: 35.0.0 - resolution: "stylelint-config-standard@npm:35.0.0" +"stylelint-config-standard@npm:^36.0.0": + version: 36.0.0 + resolution: "stylelint-config-standard@npm:36.0.0" dependencies: stylelint-config-recommended: "npm:^14.0.0" peerDependencies: - stylelint: ^16.0.0 - checksum: 791fbc26cc3029ce3c2423a643e903545b5e4cd605251b18f0ce790bac6fbaaf380469845c1ff45f4e320126af9f8a9dc1ca85d0df9274277ae60da91e81895b + stylelint: ^16.1.0 + checksum: 1fc9adddfc5cf0a1d7a443182a0731712a3950ace72a24081b4ede2b0bb6fc1eebd003c009f1d8d06c3a64ba9b31b0ed12512db2f91c8fa549238d8341580e4b languageName: node linkType: hard @@ -15852,20 +15873,20 @@ __metadata: languageName: node linkType: hard -"svgo@npm:^3.0.5": - version: 3.1.0 - resolution: "svgo@npm:3.1.0" +"svgo@npm:^3.2.0": + version: 3.2.0 + resolution: "svgo@npm:3.2.0" dependencies: "@trysound/sax": "npm:0.2.0" commander: "npm:^7.2.0" css-select: "npm:^5.1.0" - css-tree: "npm:^2.2.1" + css-tree: "npm:^2.3.1" css-what: "npm:^6.1.0" - csso: "npm:5.0.5" + csso: "npm:^5.0.5" picocolors: "npm:^1.0.0" bin: svgo: ./bin/svgo - checksum: b3f00b3319dee6ddc53f8b8ac5acef581860e1708c98b492169e096621edc1bdf46e3778099e3dffb5116bf0d4c074a686099843dbc020c73b3ccfae7b6a88f0 + checksum: 28fa9061ccbcf2e3616d48d1feb613aaa05f8f290a329beb0e585914f1864385152934a7d4d683a4609fafbae3d51666633437c359c5c5ef74fb58ad09092a7c languageName: node linkType: hard @@ -16772,7 +16793,7 @@ __metadata: languageName: node linkType: hard -"vary@npm:~1.1.2": +"vary@npm:^1, vary@npm:~1.1.2": version: 1.1.2 resolution: "vary@npm:1.1.2" checksum: f15d588d79f3675135ba783c91a4083dcd290a2a5be9fcb6514220a1634e23df116847b1cc51f66bfb0644cf9353b2abb7815ae499bab06e46dd33c1a6bf1f4f