Compare commits

...
Sign in to create a new pull request.

13 commits

Author SHA1 Message Date
KMY(雪あすか)
d8256c2862
Merge pull request #607 from kmycode/kb-draft-11.2
Release: 11.2
2024-02-24 09:01:02 +09:00
KMY
51b8c5b045 Bump version to 11.2 2024-02-24 08:32:27 +09:00
Claire
3d80e706c3 Fix processing of Link objects in Image objects (#29335) 2024-02-24 08:31:57 +09:00
Claire
3f469e1adc Fix link verifications when page size exceeds 1MB (#29358) 2024-02-24 08:30:17 +09:00
Claire
38c91aeff7 Change registrations to be disabled by default for new servers (#29280) 2024-02-24 08:30:09 +09:00
Claire
69acc5ebbe Fix auto-close email being sent to users with devops permissions instead of settings permissions (#29355) 2024-02-24 08:29:10 +09:00
Claire
eebd6ba39a Automatically switch from open to approved registrations in absence of moderators (#29318) 2024-02-24 08:28:49 +09:00
KMY
2025ac1a32 Bump version to 11.1 2024-02-21 10:55:13 +09:00
KMY
9669a25d46 Fix: 公開投稿禁止中、ローカル公開以外を投稿できない問題 2024-02-21 10:54:50 +09:00
KMY(雪あすか)
ba21ed51fe
Merge pull request #594 from kmycode/kb-draft-11.0
Release: 11.0
2024-02-21 08:18:54 +09:00
KMY(雪あすか)
8a9a29cba1 Fix: 新規登録可能時間帯を空欄にすると、画面が表示できなくなる問題 (#603)
* Fix: 新規登録可能時間帯を空欄にすると、画面が表示できなくなる問題

* リファクタリング

* Fix test
2024-02-20 09:25:22 +09:00
KMY(雪あすか)
ad723b0cbd Change: ホワイトリストが空でも設定が有効であればアカウント作成を保留 (#601)
* Change: ホワイトリストが空でも設定が有効であればアカウント作成を保留

* Fix test
2024-02-19 09:21:28 +09:00
KMY
9d57a83555 Pumb version to 11.0 2024-02-18 14:33:10 +09:00
19 changed files with 198 additions and 42 deletions

View file

@ -32,20 +32,22 @@ module RegistrationLimitationHelper
end end
def registrations_in_time? def registrations_in_time?
start_hour = Setting.registrations_start_hour || 0 start_hour = Setting.registrations_start_hour
end_hour = Setting.registrations_end_hour || 24 end_hour = Setting.registrations_end_hour
secondary_start_hour = Setting.registrations_secondary_start_hour || 0 secondary_start_hour = Setting.registrations_secondary_start_hour
secondary_end_hour = Setting.registrations_secondary_end_hour || 0 secondary_end_hour = Setting.registrations_secondary_end_hour
start_hour = 0 unless start_hour.is_a?(Integer)
end_hour = 0 unless end_hour.is_a?(Integer)
secondary_start_hour = 0 unless secondary_start_hour.is_a?(Integer)
secondary_end_hour = 0 unless secondary_end_hour.is_a?(Integer)
return true if start_hour >= end_hour && secondary_start_hour >= secondary_end_hour return true if start_hour >= end_hour && secondary_start_hour >= secondary_end_hour
current_hour = Time.now.utc.hour current_hour = Time.now.utc.hour
primary_permitted = false
primary_permitted = start_hour <= current_hour && current_hour < end_hour if start_hour < end_hour && end_hour.positive?
secondary_permitted = false
secondary_permitted = secondary_start_hour <= current_hour && current_hour < secondary_end_hour if secondary_start_hour < secondary_end_hour && secondary_end_hour.positive?
primary_permitted || secondary_permitted (start_hour < end_hour && end_hour.positive? && current_hour.between?(start_hour, end_hour - 1)) ||
(secondary_start_hour < secondary_end_hour && secondary_end_hour.positive? && current_hour.between?(secondary_start_hour, secondary_end_hour - 1))
end end
def reset_registration_limit_caches! def reset_registration_limit_caches!

View file

@ -155,6 +155,10 @@ Rails.delegate(document, '#form_admin_settings_enable_bootstrap_timeline_account
const onChangeRegistrationMode = (target) => { const onChangeRegistrationMode = (target) => {
const enabled = target.value === 'approved'; const enabled = target.value === 'approved';
[].forEach.call(document.querySelectorAll('.form_admin_settings_registrations_mode .warning-hint'), (warning_hint) => {
warning_hint.style.display = target.value === 'open' ? 'inline' : 'none';
});
[].forEach.call(document.querySelectorAll('#form_admin_settings_require_invite_text'), (input) => { [].forEach.call(document.querySelectorAll('#form_admin_settings_require_invite_text'), (input) => {
input.disabled = !enabled; input.disabled = !enabled;
if (enabled) { if (enabled) {

View file

@ -69,6 +69,12 @@ class AdminMailer < ApplicationMailer
end end
end end
def auto_close_registrations
locale_for_account(@me) do
mail subject: default_i18n_subject(instance: @instance)
end
end
private private
def process_params def process_params

View file

@ -141,7 +141,6 @@ class ActivityPub::ProcessAccountService < BaseService
def blocking_new_account? def blocking_new_account?
return false unless Setting.hold_remote_new_accounts return false unless Setting.hold_remote_new_accounts
return false if permit_new_account_domains.blank?
permit_new_account_domains.exclude?(@domain) permit_new_account_domains.exclude?(@domain)
end end
@ -247,10 +246,15 @@ class ActivityPub::ProcessAccountService < BaseService
value = first_of_value(@json[key]) value = first_of_value(@json[key])
return if value.nil? return if value.nil?
return value['url'] if value.is_a?(Hash)
image = fetch_resource_without_id_validation(value) if value.is_a?(String)
image['url'] if image value = fetch_resource_without_id_validation(value)
return if value.nil?
end
value = first_of_value(value['url']) if value.is_a?(Hash) && value['type'] == 'Image'
value = value['href'] if value.is_a?(Hash)
value if value.is_a?(String)
end end
def public_key def public_key

View file

@ -70,11 +70,11 @@ class PostStatusService < BaseService
def preprocess_attributes! def preprocess_attributes!
@sensitive = (@options[:sensitive].nil? ? @account.user&.setting_default_sensitive : @options[:sensitive]) || @options[:spoiler_text].present? @sensitive = (@options[:sensitive].nil? ? @account.user&.setting_default_sensitive : @options[:sensitive]) || @options[:spoiler_text].present?
@text = @options.delete(:spoiler_text) if @text.blank? && @options[:spoiler_text].present? @text = @options.delete(:spoiler_text) if @text.blank? && @options[:spoiler_text].present?
@visibility = @options[:visibility]&.to_sym || @account.user&.setting_default_privacy&.to_sym @visibility = @options[:visibility]&.to_sym || @account.user&.setting_default_privacy&.to_sym || :public
@visibility = :limited if %w(mutual circle reply).include?(@options[:visibility]) @visibility = :limited if %w(mutual circle reply).include?(@options[:visibility])
@visibility = :unlisted if (@visibility == :public || @visibility == :public_unlisted || @visibility == :login) && @account.silenced? @visibility = :unlisted if (@visibility == :public || @visibility == :public_unlisted || @visibility == :login) && @account.silenced?
@visibility = :public_unlisted if @visibility == :public && !@options[:force_visibility] && !@options[:application]&.superapp && @account.user&.setting_public_post_to_unlisted && Setting.enable_public_unlisted_visibility @visibility = :public_unlisted if @visibility == :public && !@options[:force_visibility] && !@options[:application]&.superapp && @account.user&.setting_public_post_to_unlisted && Setting.enable_public_unlisted_visibility
@visibility = Setting.enable_public_unlisted_visibility ? :public_unlisted : :unlisted unless Setting.enable_public_visibility @visibility = Setting.enable_public_unlisted_visibility ? :public_unlisted : :unlisted if !Setting.enable_public_visibility && @visibility == :public
@limited_scope = @options[:visibility]&.to_sym if @visibility == :limited && @options[:visibility] != 'limited' @limited_scope = @options[:visibility]&.to_sym if @visibility == :limited && @options[:visibility] != 'limited'
@searchability = searchability @searchability = searchability
@searchability = :private if @account.silenced? && %i(public public_unlisted).include?(@searchability&.to_sym) @searchability = :private if @account.silenced? && %i(public public_unlisted).include?(@searchability&.to_sym)

View file

@ -19,7 +19,7 @@ class VerifyLinkService < BaseService
def perform_request! def perform_request!
@body = Request.new(:get, @url).add_headers('Accept' => 'text/html').perform do |res| @body = Request.new(:get, @url).add_headers('Accept' => 'text/html').perform do |res|
res.code == 200 ? res.body_with_limit : nil res.code == 200 ? res.truncated_body : nil
end end
end end

View file

@ -10,9 +10,11 @@
%p.lead= t('admin.settings.registrations.preamble') %p.lead= t('admin.settings.registrations.preamble')
.flash-message= t('admin.settings.registrations.moderation_recommandation')
.fields-row .fields-row
.fields-row__column.fields-row__column-6.fields-group .fields-row__column.fields-row__column-6.fields-group
= f.input :registrations_mode, collection: %w(open approved none), wrapper: :with_label, include_blank: false, label_method: ->(mode) { I18n.t("admin.settings.registrations_mode.modes.#{mode}") } = f.input :registrations_mode, collection: %w(open approved none), wrapper: :with_label, include_blank: false, label_method: ->(mode) { I18n.t("admin.settings.registrations_mode.modes.#{mode}") }, warning_hint: I18n.t('admin.settings.registrations_mode.warning_hint')
.fields-row__column.fields-row__column-6.fields-group .fields-row__column.fields-row__column-6.fields-group
= f.input :require_invite_text, as: :boolean, wrapper: :with_label, disabled: !approved_registrations? = f.input :require_invite_text, as: :boolean, wrapper: :with_label, disabled: !approved_registrations?

View file

@ -0,0 +1,3 @@
<%= raw t('admin_mailer.auto_close_registrations.body', instance: @instance) %>
<%= raw t('application_mailer.view')%> <%= admin_settings_registrations_url %>

View file

@ -0,0 +1,33 @@
# frozen_string_literal: true
class Scheduler::AutoCloseRegistrationsScheduler
include Sidekiq::Worker
include Redisable
sidekiq_options retry: 0
# Automatically switch away from open registrations if no
# moderator had any activity in that period of time
OPEN_REGISTRATIONS_MODERATOR_THRESHOLD = 1.week + UserTrackingConcern::SIGN_IN_UPDATE_FREQUENCY
def perform
return if Rails.configuration.x.email_domains_whitelist.present? || ENV['DISABLE_AUTOMATIC_SWITCHING_TO_APPROVED_REGISTRATIONS'] == 'true'
return unless Setting.registrations_mode == 'open'
switch_to_approval_mode! unless active_moderators?
end
private
def active_moderators?
User.those_who_can(:manage_reports).exists?(current_sign_in_at: OPEN_REGISTRATIONS_MODERATOR_THRESHOLD.ago...)
end
def switch_to_approval_mode!
Setting.registrations_mode = 'approved'
User.those_who_can(:manage_settings).includes(:account).find_each do |user|
AdminMailer.with(recipient: user.account).auto_close_registrations.deliver_later
end
end
end

View file

@ -661,7 +661,7 @@ en:
post_mentions_max: Mentions max for posts post_mentions_max: Mentions max for posts
post_stranger_mentions_max: 投稿に設定可能なメンションの最大数 (If the mentions include at least one person who is not a follower of yours) post_stranger_mentions_max: 投稿に設定可能なメンションの最大数 (If the mentions include at least one person who is not a follower of yours)
remote_approval_list: List of remote accounts awaiting approval remote_approval_list: List of remote accounts awaiting approval
remote_approval_hint: If you set one or more domains on the list of domains for which you want to automatically approve new users, newly recognized accounts on unspecified domains will be placed in suspend status. You can review that list and approve them if necessary. If none is specified, all remote accounts are approved immediately. remote_approval_hint: Newly recognized accounts with unspecified domains will be placed in Suspended status. You can review that list and approve them if necessary. If this setting is not enabled, all remote accounts will be approved immediately.
stranger_mention_from_local_ng: フォローしていないアカウントへのメンションのNGワードを、ローカルユーザーによる投稿にも適用する stranger_mention_from_local_ng: フォローしていないアカウントへのメンションのNGワードを、ローカルユーザーによる投稿にも適用する
stranger_mention_from_local_ng_hint: サーバーの登録が承認制でない場合、あなたのサーバーにもスパムが入り込む可能性があります stranger_mention_from_local_ng_hint: サーバーの登録が承認制でない場合、あなたのサーバーにもスパムが入り込む可能性があります
test_error: Testing is returned any errors test_error: Testing is returned any errors
@ -899,6 +899,7 @@ en:
disabled: To no one disabled: To no one
users: To logged-in local users users: To logged-in local users
registrations: registrations:
moderation_recommandation: Please make sure you have an adequate and reactive moderation team before you open registrations to everyone!
preamble: Control who can create an account on your server. preamble: Control who can create an account on your server.
title: Registrations title: Registrations
registrations_mode: registrations_mode:
@ -906,6 +907,7 @@ en:
approved: Approval required for sign up approved: Approval required for sign up
none: Nobody can sign up none: Nobody can sign up
open: Anyone can sign up open: Anyone can sign up
warning_hint: We recommend using “Approval required for sign up” unless you are confident your moderation team can handle spam and malicious registrations in a timely fashion.
security: security:
authorized_fetch: Require authentication from federated servers authorized_fetch: Require authentication from federated servers
authorized_fetch_hint: Requiring authentication from federated servers enables stricter enforcement of both user-level and server-level blocks. However, this comes at the cost of a performance penalty, reduces the reach of your replies, and may introduce compatibility issues with some federated services. In addition, this will not prevent dedicated actors from fetching your public posts and accounts. authorized_fetch_hint: Requiring authentication from federated servers enables stricter enforcement of both user-level and server-level blocks. However, this comes at the cost of a performance penalty, reduces the reach of your replies, and may introduce compatibility issues with some federated services. In addition, this will not prevent dedicated actors from fetching your public posts and accounts.
@ -1112,6 +1114,9 @@ en:
title: Webhooks title: Webhooks
webhook: Webhook webhook: Webhook
admin_mailer: admin_mailer:
auto_close_registrations:
body: Due to a lack of recent moderator activity, registrations on %{instance} have been automatically switched to requiring manual review, to prevent %{instance} from being used as a platform for potential bad actors. You can switch it back to open registrations at any time.
subject: Registrations for %{instance} have been automatically switched to requiring approval
new_appeal: new_appeal:
actions: actions:
delete_statuses: to delete their posts delete_statuses: to delete their posts

View file

@ -654,7 +654,7 @@ ja:
post_mentions_max: 投稿に設定可能なメンションの最大数 post_mentions_max: 投稿に設定可能なメンションの最大数
post_stranger_mentions_max: 投稿に設定可能なメンションの最大数 (メンション先にフォロワー以外を1人でも含む場合) post_stranger_mentions_max: 投稿に設定可能なメンションの最大数 (メンション先にフォロワー以外を1人でも含む場合)
remote_approval_list: 承認待ちのリモートアカウント一覧 remote_approval_list: 承認待ちのリモートアカウント一覧
remote_approval_hint: 新規ユーザーを自動承認するドメインリストに1つ以上のドメインを設定すると、指定されていないドメインで新しく認識されたアカウントはサスペンド状態になります。その一覧を確認し、必要であれば承認を行うことができます。何も指定しなかった場合、全てのリモートアカウントが即座に承認されます。 remote_approval_hint: 指定されていないドメインで新しく認識されたアカウントはサスペンド状態になります。その一覧を確認し、必要であれば承認を行うことができます。この設定が有効でない場合、全てのリモートアカウントが即座に承認されます。
stranger_mention_from_local_ng: フォローしていないアカウントへのメンションのNGワードを、ローカルユーザーによる投稿にも適用する stranger_mention_from_local_ng: フォローしていないアカウントへのメンションのNGワードを、ローカルユーザーによる投稿にも適用する
stranger_mention_from_local_ng_hint: サーバーの登録が承認制でない場合、あなたのサーバーにもスパムが入り込む可能性があります stranger_mention_from_local_ng_hint: サーバーの登録が承認制でない場合、あなたのサーバーにもスパムが入り込む可能性があります
test_error: NGワードのテストに失敗しました。正規表現のミスが含まれているかもしれません test_error: NGワードのテストに失敗しました。正規表現のミスが含まれているかもしれません

View file

@ -9,7 +9,7 @@ defaults: &defaults
site_terms: '' site_terms: ''
site_contact_username: '' site_contact_username: ''
site_contact_email: '' site_contact_email: ''
registrations_mode: 'open' registrations_mode: 'none'
registrations_limit: 0 registrations_limit: 0
registrations_limit_per_day: 0 registrations_limit_per_day: 0
registrations_start_hour: 0 registrations_start_hour: 0

View file

@ -64,3 +64,7 @@
interval: 30 minutes interval: 30 minutes
class: Scheduler::SoftwareUpdateCheckScheduler class: Scheduler::SoftwareUpdateCheckScheduler
queue: scheduler queue: scheduler
auto_close_registrations_scheduler:
interval: 1 hour
class: Scheduler::AutoCloseRegistrationsScheduler
queue: scheduler

View file

@ -9,13 +9,13 @@ module Mastodon
end end
def kmyblue_minor def kmyblue_minor
0 2
end end
def kmyblue_flag def kmyblue_flag
# 'LTS' # 'LTS'
'dev' # 'dev'
# nil nil
end end
def major def major

View file

@ -29,11 +29,11 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
Setting.permit_new_account_domains = permit_new_account_domains Setting.permit_new_account_domains = permit_new_account_domains
end end
it 'created account in a simple case' do it 'creates pending account in a simple case' do
expect(subject).to_not be_nil expect(subject).to_not be_nil
expect(subject.uri).to eq 'https://foo.test' expect(subject.uri).to eq 'https://foo.test'
expect(subject.suspended?).to be false expect(subject.suspended?).to be true
expect(subject.remote_pending).to be false expect(subject.remote_pending).to be true
end end
context 'when is blocked' do context 'when is blocked' do
@ -325,7 +325,7 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
end end
end end
context 'with property values' do context 'with property values, an avatar, and a profile header' do
let(:payload) do let(:payload) do
{ {
id: 'https://foo.test', id: 'https://foo.test',
@ -336,14 +336,29 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
{ type: 'PropertyValue', name: 'Occupation', value: 'Unit test' }, { type: 'PropertyValue', name: 'Occupation', value: 'Unit test' },
{ type: 'PropertyValue', name: 'non-string', value: %w(foo bar) }, { type: 'PropertyValue', name: 'non-string', value: %w(foo bar) },
], ],
image: {
type: 'Image',
mediaType: 'image/png',
url: 'https://foo.test/image.png',
},
icon: {
type: 'Image',
url: [
{
mediaType: 'image/png',
href: 'https://foo.test/icon.png',
},
],
},
}.with_indifferent_access }.with_indifferent_access
end end
before do before do
stub_request(:get, 'https://example.com/.well-known/nodeinfo').to_return(body: '{}') stub_request(:get, 'https://foo.test/image.png').to_return(request_fixture('avatar.txt'))
stub_request(:get, 'https://foo.test/icon.png').to_return(request_fixture('avatar.txt'))
end end
it 'parses out of attachment' do it 'parses property values, avatar and profile header as expected' do
account = subject.call('alice', 'example.com', payload) account = subject.call('alice', 'example.com', payload)
expect(account.fields) expect(account.fields)
@ -361,6 +376,10 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
name: eq('Occupation'), name: eq('Occupation'),
value: eq('Unit test') value: eq('Unit test')
) )
expect(account).to have_attributes(
avatar_remote_url: 'https://foo.test/icon.png',
header_remote_url: 'https://foo.test/image.png'
)
end end
end end

View file

@ -76,6 +76,20 @@ RSpec.describe VerifyLinkService, type: :service do
end end
context 'when a document is truncated but the link back is valid' do context 'when a document is truncated but the link back is valid' do
let(:html) do
"
<!doctype html>
<body>
<a rel=\"me\" href=\"#{ActivityPub::TagManager.instance.url_for(account)}\">
"
end
it 'marks the field as verified' do
expect(field.verified?).to be true
end
end
context 'when a link tag might be truncated' do
let(:html) do let(:html) do
" "
<!doctype html> <!doctype html>
@ -89,19 +103,6 @@ RSpec.describe VerifyLinkService, type: :service do
end end
end end
context 'when a link back might be truncated' do
let(:html) do
"
<!doctype html>
<body>
<a rel=\"me\" href=\"#{ActivityPub::TagManager.instance.url_for(account)}"
end
it 'does not mark the field as verified' do
expect(field.verified?).to be false
end
end
context 'when a link does not contain a link back' do context 'when a link does not contain a link back' do
let(:html) { '' } let(:html) { '' }

View file

@ -25,6 +25,12 @@ RSpec.configure do |config|
config.before :suite do config.before :suite do
Rails.application.load_seed Rails.application.load_seed
Chewy.strategy(:bypass) Chewy.strategy(:bypass)
# NOTE: we switched registrations mode to closed by default, but the specs
# very heavily rely on having it enabled by default, as it relies on users
# being approved by default except in select cases where explicitly testing
# other registration modes
Setting.registrations_mode = 'open'
end end
config.after :suite do config.after :suite do

View file

@ -102,6 +102,13 @@ RSpec.configure do |config|
self.use_transactional_tests = false self.use_transactional_tests = false
DatabaseCleaner.cleaning do DatabaseCleaner.cleaning do
# NOTE: we switched registrations mode to closed by default, but the specs
# very heavily rely on having it enabled by default, as it relies on users
# being approved by default except in select cases where explicitly testing
# other registration modes
# Also needs to be set per-example here because of the database cleaner.
Setting.registrations_mode = 'open'
example.run example.run
end end

View file

@ -0,0 +1,60 @@
# frozen_string_literal: true
require 'rails_helper'
describe Scheduler::AutoCloseRegistrationsScheduler do
subject { described_class.new }
describe '#perform' do
let(:moderator_activity_date) { Time.now.utc }
before do
Fabricate(:user, role: UserRole.find_by(name: 'Owner'), current_sign_in_at: 10.years.ago)
Fabricate(:user, role: UserRole.find_by(name: 'Moderator'), current_sign_in_at: moderator_activity_date)
end
context 'when registrations are open' do
before do
Setting.registrations_mode = 'open'
end
context 'when a moderator has logged in recently' do
let(:moderator_activity_date) { Time.now.utc }
it 'does not change registrations mode' do
expect { subject.perform }.to_not change(Setting, :registrations_mode)
end
end
context 'when a moderator has not recently signed in' do
let(:moderator_activity_date) { 1.year.ago }
it 'changes registrations mode from open to approved' do
expect { subject.perform }.to change(Setting, :registrations_mode).from('open').to('approved')
end
end
end
context 'when registrations are closed' do
before do
Setting.registrations_mode = 'none'
end
context 'when a moderator has logged in recently' do
let(:moderator_activity_date) { Time.now.utc }
it 'does not change registrations mode' do
expect { subject.perform }.to_not change(Setting, :registrations_mode)
end
end
context 'when a moderator has not recently signed in' do
let(:moderator_activity_date) { 1.year.ago }
it 'does not change registrations mode' do
expect { subject.perform }.to_not change(Setting, :registrations_mode)
end
end
end
end
end