commit
d8256c2862
16 changed files with 178 additions and 23 deletions
|
@ -155,6 +155,10 @@ Rails.delegate(document, '#form_admin_settings_enable_bootstrap_timeline_account
|
|||
const onChangeRegistrationMode = (target) => {
|
||||
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) => {
|
||||
input.disabled = !enabled;
|
||||
if (enabled) {
|
||||
|
|
|
@ -69,6 +69,12 @@ class AdminMailer < ApplicationMailer
|
|||
end
|
||||
end
|
||||
|
||||
def auto_close_registrations
|
||||
locale_for_account(@me) do
|
||||
mail subject: default_i18n_subject(instance: @instance)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_params
|
||||
|
|
|
@ -246,10 +246,15 @@ class ActivityPub::ProcessAccountService < BaseService
|
|||
value = first_of_value(@json[key])
|
||||
|
||||
return if value.nil?
|
||||
return value['url'] if value.is_a?(Hash)
|
||||
|
||||
image = fetch_resource_without_id_validation(value)
|
||||
image['url'] if image
|
||||
if value.is_a?(String)
|
||||
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
|
||||
|
||||
def public_key
|
||||
|
|
|
@ -19,7 +19,7 @@ class VerifyLinkService < BaseService
|
|||
|
||||
def perform_request!
|
||||
@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
|
||||
|
||||
|
|
|
@ -10,9 +10,11 @@
|
|||
|
||||
%p.lead= t('admin.settings.registrations.preamble')
|
||||
|
||||
.flash-message= t('admin.settings.registrations.moderation_recommandation')
|
||||
|
||||
.fields-row
|
||||
.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
|
||||
= f.input :require_invite_text, as: :boolean, wrapper: :with_label, disabled: !approved_registrations?
|
||||
|
|
3
app/views/admin_mailer/auto_close_registrations.text.erb
Normal file
3
app/views/admin_mailer/auto_close_registrations.text.erb
Normal file
|
@ -0,0 +1,3 @@
|
|||
<%= raw t('admin_mailer.auto_close_registrations.body', instance: @instance) %>
|
||||
|
||||
<%= raw t('application_mailer.view')%> <%= admin_settings_registrations_url %>
|
33
app/workers/scheduler/auto_close_registrations_scheduler.rb
Normal file
33
app/workers/scheduler/auto_close_registrations_scheduler.rb
Normal 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
|
|
@ -899,6 +899,7 @@ en:
|
|||
disabled: To no one
|
||||
users: To logged-in local users
|
||||
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.
|
||||
title: Registrations
|
||||
registrations_mode:
|
||||
|
@ -906,6 +907,7 @@ en:
|
|||
approved: Approval required for sign up
|
||||
none: Nobody 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:
|
||||
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.
|
||||
|
@ -1112,6 +1114,9 @@ en:
|
|||
title: Webhooks
|
||||
webhook: Webhook
|
||||
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:
|
||||
actions:
|
||||
delete_statuses: to delete their posts
|
||||
|
|
|
@ -9,7 +9,7 @@ defaults: &defaults
|
|||
site_terms: ''
|
||||
site_contact_username: ''
|
||||
site_contact_email: ''
|
||||
registrations_mode: 'open'
|
||||
registrations_mode: 'none'
|
||||
registrations_limit: 0
|
||||
registrations_limit_per_day: 0
|
||||
registrations_start_hour: 0
|
||||
|
|
|
@ -64,3 +64,7 @@
|
|||
interval: 30 minutes
|
||||
class: Scheduler::SoftwareUpdateCheckScheduler
|
||||
queue: scheduler
|
||||
auto_close_registrations_scheduler:
|
||||
interval: 1 hour
|
||||
class: Scheduler::AutoCloseRegistrationsScheduler
|
||||
queue: scheduler
|
||||
|
|
|
@ -9,7 +9,7 @@ module Mastodon
|
|||
end
|
||||
|
||||
def kmyblue_minor
|
||||
1
|
||||
2
|
||||
end
|
||||
|
||||
def kmyblue_flag
|
||||
|
|
|
@ -325,7 +325,7 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with property values' do
|
||||
context 'with property values, an avatar, and a profile header' do
|
||||
let(:payload) do
|
||||
{
|
||||
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: '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
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
it 'parses out of attachment' do
|
||||
it 'parses property values, avatar and profile header as expected' do
|
||||
account = subject.call('alice', 'example.com', payload)
|
||||
|
||||
expect(account.fields)
|
||||
|
@ -361,6 +376,10 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
|
|||
name: eq('Occupation'),
|
||||
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
|
||||
|
||||
|
|
|
@ -76,6 +76,20 @@ RSpec.describe VerifyLinkService, type: :service do
|
|||
end
|
||||
|
||||
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
|
||||
"
|
||||
<!doctype html>
|
||||
|
@ -89,19 +103,6 @@ RSpec.describe VerifyLinkService, type: :service do
|
|||
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
|
||||
let(:html) { '' }
|
||||
|
||||
|
|
|
@ -25,6 +25,12 @@ RSpec.configure do |config|
|
|||
config.before :suite do
|
||||
Rails.application.load_seed
|
||||
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
|
||||
|
||||
config.after :suite do
|
||||
|
|
|
@ -102,6 +102,13 @@ RSpec.configure do |config|
|
|||
self.use_transactional_tests = false
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
|
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue