forked from fedi/mastodon
Add whitelist mode (#11291)
This commit is contained in:
parent
85b7b565de
commit
24552b5160
|
@ -3,6 +3,7 @@
|
||||||
class AboutController < ApplicationController
|
class AboutController < ApplicationController
|
||||||
layout 'public'
|
layout 'public'
|
||||||
|
|
||||||
|
before_action :require_open_federation!, only: [:show, :more]
|
||||||
before_action :set_body_classes, only: :show
|
before_action :set_body_classes, only: :show
|
||||||
before_action :set_instance_presenter
|
before_action :set_instance_presenter
|
||||||
before_action :set_expires_in
|
before_action :set_expires_in
|
||||||
|
@ -19,6 +20,10 @@ class AboutController < ApplicationController
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def require_open_federation!
|
||||||
|
not_found if whitelist_mode?
|
||||||
|
end
|
||||||
|
|
||||||
def new_user
|
def new_user
|
||||||
User.new.tap do |user|
|
User.new.tap do |user|
|
||||||
user.build_account
|
user.build_account
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ActivityPub::BaseController < Api::BaseController
|
class ActivityPub::BaseController < Api::BaseController
|
||||||
|
skip_before_action :require_authenticated_user!
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_cache_headers
|
def set_cache_headers
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ActivityPub::InboxesController < Api::BaseController
|
class ActivityPub::InboxesController < ActivityPub::BaseController
|
||||||
include SignatureVerification
|
include SignatureVerification
|
||||||
include JsonLdHelper
|
include JsonLdHelper
|
||||||
include AccountOwnedConcern
|
include AccountOwnedConcern
|
||||||
|
|
40
app/controllers/admin/domain_allows_controller.rb
Normal file
40
app/controllers/admin/domain_allows_controller.rb
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Admin::DomainAllowsController < Admin::BaseController
|
||||||
|
before_action :set_domain_allow, only: [:destroy]
|
||||||
|
|
||||||
|
def new
|
||||||
|
authorize :domain_allow, :create?
|
||||||
|
|
||||||
|
@domain_allow = DomainAllow.new(domain: params[:_domain])
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
authorize :domain_allow, :create?
|
||||||
|
|
||||||
|
@domain_allow = DomainAllow.new(resource_params)
|
||||||
|
|
||||||
|
if @domain_allow.save
|
||||||
|
log_action :create, @domain_allow
|
||||||
|
redirect_to admin_instances_path, notice: I18n.t('admin.domain_allows.created_msg')
|
||||||
|
else
|
||||||
|
render :new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
authorize @domain_allow, :destroy?
|
||||||
|
UnallowDomainService.new.call(@domain_allow)
|
||||||
|
redirect_to admin_instances_path, notice: I18n.t('admin.domain_allows.destroyed_msg')
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_domain_allow
|
||||||
|
@domain_allow = DomainAllow.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def resource_params
|
||||||
|
params.require(:domain_allow).permit(:domain)
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class InstancesController < BaseController
|
class InstancesController < BaseController
|
||||||
|
before_action :set_domain_block, only: :show
|
||||||
|
before_action :set_domain_allow, only: :show
|
||||||
|
before_action :set_instance, only: :show
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :instance, :index?
|
authorize :instance, :index?
|
||||||
|
|
||||||
|
@ -11,20 +15,38 @@ module Admin
|
||||||
def show
|
def show
|
||||||
authorize :instance, :show?
|
authorize :instance, :show?
|
||||||
|
|
||||||
@instance = Instance.new(Account.by_domain_accounts.find_by(domain: params[:id]) || DomainBlock.find_by!(domain: params[:id]))
|
|
||||||
@following_count = Follow.where(account: Account.where(domain: params[:id])).count
|
@following_count = Follow.where(account: Account.where(domain: params[:id])).count
|
||||||
@followers_count = Follow.where(target_account: Account.where(domain: params[:id])).count
|
@followers_count = Follow.where(target_account: Account.where(domain: params[:id])).count
|
||||||
@reports_count = Report.where(target_account: Account.where(domain: params[:id])).count
|
@reports_count = Report.where(target_account: Account.where(domain: params[:id])).count
|
||||||
@blocks_count = Block.where(target_account: Account.where(domain: params[:id])).count
|
@blocks_count = Block.where(target_account: Account.where(domain: params[:id])).count
|
||||||
@available = DeliveryFailureTracker.available?(Account.select(:shared_inbox_url).where(domain: params[:id]).first&.shared_inbox_url)
|
@available = DeliveryFailureTracker.available?(Account.select(:shared_inbox_url).where(domain: params[:id]).first&.shared_inbox_url)
|
||||||
@media_storage = MediaAttachment.where(account: Account.where(domain: params[:id])).sum(:file_file_size)
|
@media_storage = MediaAttachment.where(account: Account.where(domain: params[:id])).sum(:file_file_size)
|
||||||
@domain_block = DomainBlock.rule_for(params[:id])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def set_domain_block
|
||||||
|
@domain_block = DomainBlock.rule_for(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_domain_allow
|
||||||
|
@domain_allow = DomainAllow.rule_for(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_instance
|
||||||
|
resource = Account.by_domain_accounts.find_by(domain: params[:id])
|
||||||
|
resource ||= @domain_block
|
||||||
|
resource ||= @domain_allow
|
||||||
|
|
||||||
|
if resource
|
||||||
|
@instance = Instance.new(resource)
|
||||||
|
else
|
||||||
|
not_found
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def filtered_instances
|
def filtered_instances
|
||||||
InstanceFilter.new(filter_params).results
|
InstanceFilter.new(whitelist_mode? ? { allowed: true } : filter_params).results
|
||||||
end
|
end
|
||||||
|
|
||||||
def paginated_instances
|
def paginated_instances
|
||||||
|
|
|
@ -9,6 +9,7 @@ class Api::BaseController < ApplicationController
|
||||||
skip_before_action :store_current_location
|
skip_before_action :store_current_location
|
||||||
skip_before_action :require_functional!
|
skip_before_action :require_functional!
|
||||||
|
|
||||||
|
before_action :require_authenticated_user!, if: :disallow_unauthenticated_api_access?
|
||||||
before_action :set_cache_headers
|
before_action :set_cache_headers
|
||||||
|
|
||||||
protect_from_forgery with: :null_session
|
protect_from_forgery with: :null_session
|
||||||
|
@ -69,6 +70,10 @@ class Api::BaseController < ApplicationController
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def require_authenticated_user!
|
||||||
|
render json: { error: 'This API requires an authenticated user' }, status: 401 unless current_user
|
||||||
|
end
|
||||||
|
|
||||||
def require_user!
|
def require_user!
|
||||||
if !current_user
|
if !current_user
|
||||||
render json: { error: 'This method requires an authenticated user' }, status: 422
|
render json: { error: 'This method requires an authenticated user' }, status: 422
|
||||||
|
@ -94,4 +99,8 @@ class Api::BaseController < ApplicationController
|
||||||
def set_cache_headers
|
def set_cache_headers
|
||||||
response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
|
response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def disallow_unauthenticated_api_access?
|
||||||
|
authorized_fetch_mode?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,6 +12,8 @@ class Api::V1::AccountsController < Api::BaseController
|
||||||
before_action :check_account_suspension, only: [:show]
|
before_action :check_account_suspension, only: [:show]
|
||||||
before_action :check_enabled_registrations, only: [:create]
|
before_action :check_enabled_registrations, only: [:create]
|
||||||
|
|
||||||
|
skip_before_action :require_authenticated_user!, only: :create
|
||||||
|
|
||||||
respond_to :json
|
respond_to :json
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::AppsController < Api::BaseController
|
class Api::V1::AppsController < Api::BaseController
|
||||||
|
skip_before_action :require_authenticated_user!
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@app = Doorkeeper::Application.create!(application_options)
|
@app = Doorkeeper::Application.create!(application_options)
|
||||||
render json: @app, serializer: REST::ApplicationSerializer
|
render json: @app, serializer: REST::ApplicationSerializer
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class Api::V1::Instances::ActivityController < Api::BaseController
|
class Api::V1::Instances::ActivityController < Api::BaseController
|
||||||
before_action :require_enabled_api!
|
before_action :require_enabled_api!
|
||||||
|
|
||||||
skip_before_action :set_cache_headers
|
skip_before_action :set_cache_headers
|
||||||
|
|
||||||
respond_to :json
|
respond_to :json
|
||||||
|
@ -33,6 +34,6 @@ class Api::V1::Instances::ActivityController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def require_enabled_api!
|
def require_enabled_api!
|
||||||
head 404 unless Setting.activity_api_enabled
|
head 404 unless Setting.activity_api_enabled && !whitelist_mode?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class Api::V1::Instances::PeersController < Api::BaseController
|
class Api::V1::Instances::PeersController < Api::BaseController
|
||||||
before_action :require_enabled_api!
|
before_action :require_enabled_api!
|
||||||
|
|
||||||
skip_before_action :set_cache_headers
|
skip_before_action :set_cache_headers
|
||||||
|
|
||||||
respond_to :json
|
respond_to :json
|
||||||
|
@ -14,6 +15,6 @@ class Api::V1::Instances::PeersController < Api::BaseController
|
||||||
private
|
private
|
||||||
|
|
||||||
def require_enabled_api!
|
def require_enabled_api!
|
||||||
head 404 unless Setting.peers_api_enabled
|
head 404 unless Setting.peers_api_enabled && !whitelist_mode?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class Api::V1::InstancesController < Api::BaseController
|
class Api::V1::InstancesController < Api::BaseController
|
||||||
respond_to :json
|
respond_to :json
|
||||||
|
|
||||||
skip_before_action :set_cache_headers
|
skip_before_action :set_cache_headers
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
|
|
@ -11,12 +11,14 @@ class ApplicationController < ActionController::Base
|
||||||
include UserTrackingConcern
|
include UserTrackingConcern
|
||||||
include SessionTrackingConcern
|
include SessionTrackingConcern
|
||||||
include CacheConcern
|
include CacheConcern
|
||||||
|
include DomainControlHelper
|
||||||
|
|
||||||
helper_method :current_account
|
helper_method :current_account
|
||||||
helper_method :current_session
|
helper_method :current_session
|
||||||
helper_method :current_theme
|
helper_method :current_theme
|
||||||
helper_method :single_user_mode?
|
helper_method :single_user_mode?
|
||||||
helper_method :use_seamless_external_login?
|
helper_method :use_seamless_external_login?
|
||||||
|
helper_method :whitelist_mode?
|
||||||
|
|
||||||
rescue_from ActionController::RoutingError, with: :not_found
|
rescue_from ActionController::RoutingError, with: :not_found
|
||||||
rescue_from ActiveRecord::RecordNotFound, with: :not_found
|
rescue_from ActiveRecord::RecordNotFound, with: :not_found
|
||||||
|
@ -38,7 +40,7 @@ class ApplicationController < ActionController::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorized_fetch_mode?
|
def authorized_fetch_mode?
|
||||||
ENV['AUTHORIZED_FETCH'] == 'true'
|
ENV['AUTHORIZED_FETCH'] == 'true' || Rails.configuration.x.whitelist_mode
|
||||||
end
|
end
|
||||||
|
|
||||||
def public_fetch_mode?
|
def public_fetch_mode?
|
||||||
|
|
|
@ -4,6 +4,7 @@ module AccountOwnedConcern
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
included do
|
included do
|
||||||
|
before_action :authenticate_user!, if: -> { whitelist_mode? && request.format != :json }
|
||||||
before_action :set_account, if: :account_required?
|
before_action :set_account, if: :account_required?
|
||||||
before_action :check_account_approval, if: :account_required?
|
before_action :check_account_approval, if: :account_required?
|
||||||
before_action :check_account_suspension, if: :account_required?
|
before_action :check_account_suspension, if: :account_required?
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
class DirectoriesController < ApplicationController
|
class DirectoriesController < ApplicationController
|
||||||
layout 'public'
|
layout 'public'
|
||||||
|
|
||||||
before_action :check_enabled
|
before_action :authenticate_user!, if: :whitelist_mode?
|
||||||
|
before_action :require_enabled!
|
||||||
before_action :set_instance_presenter
|
before_action :set_instance_presenter
|
||||||
before_action :set_tag, only: :show
|
before_action :set_tag, only: :show
|
||||||
before_action :set_tags
|
before_action :set_tags
|
||||||
|
@ -19,7 +20,7 @@ class DirectoriesController < ApplicationController
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def check_enabled
|
def require_enabled!
|
||||||
return not_found unless Setting.profile_directory
|
return not_found unless Setting.profile_directory
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ class HomeController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_redirect_path
|
def default_redirect_path
|
||||||
if request.path.start_with?('/web')
|
if request.path.start_with?('/web') || whitelist_mode?
|
||||||
new_user_session_path
|
new_user_session_path
|
||||||
elsif single_user_mode?
|
elsif single_user_mode?
|
||||||
short_account_path(Account.local.without_suspended.where('id > 0').first)
|
short_account_path(Account.local.without_suspended.where('id > 0').first)
|
||||||
|
|
|
@ -5,6 +5,7 @@ class MediaController < ApplicationController
|
||||||
|
|
||||||
skip_before_action :store_current_location
|
skip_before_action :store_current_location
|
||||||
|
|
||||||
|
before_action :authenticate_user!, if: :whitelist_mode?
|
||||||
before_action :set_media_attachment
|
before_action :set_media_attachment
|
||||||
before_action :verify_permitted_status!
|
before_action :verify_permitted_status!
|
||||||
before_action :check_playable, only: :player
|
before_action :check_playable, only: :player
|
||||||
|
|
|
@ -5,6 +5,8 @@ class MediaProxyController < ApplicationController
|
||||||
|
|
||||||
skip_before_action :store_current_location
|
skip_before_action :store_current_location
|
||||||
|
|
||||||
|
before_action :authenticate_user!, if: :whitelist_mode?
|
||||||
|
|
||||||
def show
|
def show
|
||||||
RedisLock.acquire(lock_options) do |lock|
|
RedisLock.acquire(lock_options) do |lock|
|
||||||
if lock.acquired?
|
if lock.acquired?
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
class PublicTimelinesController < ApplicationController
|
class PublicTimelinesController < ApplicationController
|
||||||
layout 'public'
|
layout 'public'
|
||||||
|
|
||||||
before_action :check_enabled
|
before_action :authenticate_user!, if: :whitelist_mode?
|
||||||
|
before_action :require_enabled!
|
||||||
before_action :set_body_classes
|
before_action :set_body_classes
|
||||||
before_action :set_instance_presenter
|
before_action :set_instance_presenter
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ class PublicTimelinesController < ApplicationController
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def check_enabled
|
def require_enabled!
|
||||||
not_found unless Setting.timeline_preview
|
not_found unless Setting.timeline_preview
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ class RemoteInteractionController < ApplicationController
|
||||||
|
|
||||||
layout 'modal'
|
layout 'modal'
|
||||||
|
|
||||||
|
before_action :authenticate_user!, if: :whitelist_mode?
|
||||||
before_action :set_interaction_type
|
before_action :set_interaction_type
|
||||||
before_action :set_status
|
before_action :set_status
|
||||||
before_action :set_body_classes
|
before_action :set_body_classes
|
||||||
|
|
|
@ -8,6 +8,7 @@ class TagsController < ApplicationController
|
||||||
layout 'public'
|
layout 'public'
|
||||||
|
|
||||||
before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||||
|
before_action :authenticate_user!, if: :whitelist_mode?
|
||||||
before_action :set_tag
|
before_action :set_tag
|
||||||
before_action :set_body_classes
|
before_action :set_body_classes
|
||||||
before_action :set_instance_presenter
|
before_action :set_instance_presenter
|
||||||
|
|
|
@ -12,6 +12,14 @@ module DomainControlHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if whitelist_mode?
|
||||||
|
!DomainAllow.allowed?(domain)
|
||||||
|
else
|
||||||
DomainBlock.blocked?(domain)
|
DomainBlock.blocked?(domain)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def whitelist_mode?
|
||||||
|
Rails.configuration.x.whitelist_mode
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
33
app/models/domain_allow.rb
Normal file
33
app/models/domain_allow.rb
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: domain_allows
|
||||||
|
#
|
||||||
|
# id :bigint(8) not null, primary key
|
||||||
|
# domain :string default(""), not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
#
|
||||||
|
|
||||||
|
class DomainAllow < ApplicationRecord
|
||||||
|
include DomainNormalizable
|
||||||
|
|
||||||
|
validates :domain, presence: true, uniqueness: true
|
||||||
|
|
||||||
|
scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
|
||||||
|
|
||||||
|
class << self
|
||||||
|
def allowed?(domain)
|
||||||
|
!rule_for(domain).nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
def rule_for(domain)
|
||||||
|
return if domain.blank?
|
||||||
|
|
||||||
|
uri = Addressable::URI.new.tap { |u| u.host = domain.gsub(/[\/]/, '') }
|
||||||
|
|
||||||
|
find_by(domain: uri.normalized_host)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,8 +7,9 @@ class Instance
|
||||||
|
|
||||||
def initialize(resource)
|
def initialize(resource)
|
||||||
@domain = resource.domain
|
@domain = resource.domain
|
||||||
@accounts_count = resource.is_a?(DomainBlock) ? nil : resource.accounts_count
|
@accounts_count = resource.respond_to?(:accounts_count) ? resource.accounts_count : nil
|
||||||
@domain_block = resource.is_a?(DomainBlock) ? resource : DomainBlock.rule_for(domain)
|
@domain_block = resource.is_a?(DomainBlock) ? resource : DomainBlock.rule_for(domain)
|
||||||
|
@domain_allow = resource.is_a?(DomainAllow) ? resource : DomainAllow.rule_for(domain)
|
||||||
end
|
end
|
||||||
|
|
||||||
def countable?
|
def countable?
|
||||||
|
|
|
@ -12,6 +12,10 @@ class InstanceFilter
|
||||||
scope = DomainBlock
|
scope = DomainBlock
|
||||||
scope = scope.matches_domain(params[:by_domain]) if params[:by_domain].present?
|
scope = scope.matches_domain(params[:by_domain]) if params[:by_domain].present?
|
||||||
scope.order(id: :desc)
|
scope.order(id: :desc)
|
||||||
|
elsif params[:allowed].present?
|
||||||
|
scope = DomainAllow
|
||||||
|
scope = scope.matches_domain(params[:by_domain]) if params[:by_domain].present?
|
||||||
|
scope.order(id: :desc)
|
||||||
else
|
else
|
||||||
scope = Account.remote
|
scope = Account.remote
|
||||||
scope = scope.matches_domain(params[:by_domain]) if params[:by_domain].present?
|
scope = scope.matches_domain(params[:by_domain]) if params[:by_domain].present?
|
||||||
|
|
11
app/policies/domain_allow_policy.rb
Normal file
11
app/policies/domain_allow_policy.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class DomainAllowPolicy < ApplicationPolicy
|
||||||
|
def create?
|
||||||
|
admin?
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy?
|
||||||
|
admin?
|
||||||
|
end
|
||||||
|
end
|
|
@ -14,6 +14,6 @@ module Payloadable
|
||||||
end
|
end
|
||||||
|
|
||||||
def signing_enabled?
|
def signing_enabled?
|
||||||
ENV['AUTHORIZED_FETCH'] != 'true'
|
ENV['AUTHORIZED_FETCH'] != 'true' && !Rails.configuration.x.whitelist_mode
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
11
app/services/unallow_domain_service.rb
Normal file
11
app/services/unallow_domain_service.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class UnallowDomainService < BaseService
|
||||||
|
def call(domain_allow)
|
||||||
|
Account.where(domain: domain_allow.domain).find_each do |account|
|
||||||
|
SuspendAccountService.new.call(account, destroy: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
domain_allow.destroy
|
||||||
|
end
|
||||||
|
end
|
14
app/views/admin/domain_allows/new.html.haml
Normal file
14
app/views/admin/domain_allows/new.html.haml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
- content_for :header_tags do
|
||||||
|
= javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
|
||||||
|
|
||||||
|
- content_for :page_title do
|
||||||
|
= t('admin.domain_allows.add_new')
|
||||||
|
|
||||||
|
= simple_form_for @domain_allow, url: admin_domain_allows_path do |f|
|
||||||
|
= render 'shared/error_messages', object: @domain_allow
|
||||||
|
|
||||||
|
.fields-group
|
||||||
|
= f.input :domain, wrapper: :with_label, label: t('admin.domain_blocks.domain'), required: true
|
||||||
|
|
||||||
|
.actions
|
||||||
|
= f.button :button, t('admin.domain_allows.add_new'), type: :submit
|
|
@ -6,12 +6,18 @@
|
||||||
%strong= t('admin.instances.moderation.title')
|
%strong= t('admin.instances.moderation.title')
|
||||||
%ul
|
%ul
|
||||||
%li= filter_link_to t('admin.instances.moderation.all'), limited: nil
|
%li= filter_link_to t('admin.instances.moderation.all'), limited: nil
|
||||||
|
|
||||||
|
- unless whitelist_mode?
|
||||||
%li= filter_link_to t('admin.instances.moderation.limited'), limited: '1'
|
%li= filter_link_to t('admin.instances.moderation.limited'), limited: '1'
|
||||||
|
|
||||||
%div{ style: 'flex: 1 1 auto; text-align: right' }
|
%div{ style: 'flex: 1 1 auto; text-align: right' }
|
||||||
|
- if whitelist_mode?
|
||||||
|
= link_to t('admin.domain_allows.add_new'), new_admin_domain_allow_path, class: 'button'
|
||||||
|
- else
|
||||||
= link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path, class: 'button'
|
= link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path, class: 'button'
|
||||||
|
|
||||||
= form_tag admin_instances_url, method: 'GET', class: 'simple_form' do
|
- unless whitelist_mode?
|
||||||
|
= form_tag admin_instances_url, method: 'GET', class: 'simple_form' do
|
||||||
.fields-group
|
.fields-group
|
||||||
- Admin::FilterHelper::INSTANCES_FILTERS.each do |key|
|
- Admin::FilterHelper::INSTANCES_FILTERS.each do |key|
|
||||||
- if params[key].present?
|
- if params[key].present?
|
||||||
|
@ -47,8 +53,11 @@
|
||||||
- unless first_item
|
- unless first_item
|
||||||
•
|
•
|
||||||
= t('admin.domain_blocks.rejecting_reports')
|
= t('admin.domain_blocks.rejecting_reports')
|
||||||
|
- elsif whitelist_mode?
|
||||||
|
= t('admin.accounts.whitelisted')
|
||||||
- else
|
- else
|
||||||
= t('admin.accounts.no_limits_imposed')
|
= t('admin.accounts.no_limits_imposed')
|
||||||
- if instance.countable?
|
- if instance.countable?
|
||||||
.trends__item__current{ title: t('admin.instances.known_accounts', count: instance.accounts_count) }= number_to_human instance.accounts_count, strip_insignificant_zeros: true
|
.trends__item__current{ title: t('admin.instances.known_accounts', count: instance.accounts_count) }= number_to_human instance.accounts_count, strip_insignificant_zeros: true
|
||||||
|
|
||||||
= paginate paginated_instances
|
= paginate paginated_instances
|
||||||
|
|
|
@ -38,7 +38,9 @@
|
||||||
= link_to t('admin.accounts.title'), admin_accounts_path(remote: '1', by_domain: @instance.domain), class: 'button'
|
= link_to t('admin.accounts.title'), admin_accounts_path(remote: '1', by_domain: @instance.domain), class: 'button'
|
||||||
|
|
||||||
%div{ style: 'float: right' }
|
%div{ style: 'float: right' }
|
||||||
- if @domain_block
|
- if @domain_allow
|
||||||
|
= link_to t('admin.domain_allows.undo'), admin_domain_allow_path(@domain_allow), class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete }
|
||||||
|
- elsif @domain_block
|
||||||
= link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@domain_block), class: 'button'
|
= link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@domain_block), class: 'button'
|
||||||
- else
|
- else
|
||||||
= link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @instance.domain), class: 'button'
|
= link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @instance.domain), class: 'button'
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
|
|
||||||
%hr.spacer/
|
%hr.spacer/
|
||||||
|
|
||||||
|
- unless whitelist_mode?
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :timeline_preview, as: :boolean, wrapper: :with_label, label: t('admin.settings.timeline_preview.title'), hint: t('admin.settings.timeline_preview.desc_html')
|
= f.input :timeline_preview, as: :boolean, wrapper: :with_label, label: t('admin.settings.timeline_preview.title'), hint: t('admin.settings.timeline_preview.desc_html')
|
||||||
|
|
||||||
|
@ -54,6 +55,7 @@
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :open_deletion, as: :boolean, wrapper: :with_label, label: t('admin.settings.registrations.deletion.title'), hint: t('admin.settings.registrations.deletion.desc_html')
|
= f.input :open_deletion, as: :boolean, wrapper: :with_label, label: t('admin.settings.registrations.deletion.title'), hint: t('admin.settings.registrations.deletion.desc_html')
|
||||||
|
|
||||||
|
- unless whitelist_mode?
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :activity_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.activity_api_enabled.title'), hint: t('admin.settings.activity_api_enabled.desc_html')
|
= f.input :activity_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.activity_api_enabled.title'), hint: t('admin.settings.activity_api_enabled.desc_html')
|
||||||
|
|
||||||
|
@ -76,7 +78,7 @@
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :closed_registrations_message, as: :text, wrapper: :with_block_label, label: t('admin.settings.registrations.closed_message.title'), hint: t('admin.settings.registrations.closed_message.desc_html'), input_html: { rows: 8 }
|
= f.input :closed_registrations_message, as: :text, wrapper: :with_block_label, label: t('admin.settings.registrations.closed_message.title'), hint: t('admin.settings.registrations.closed_message.desc_html'), input_html: { rows: 8 }
|
||||||
= f.input :site_extended_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description_extended.title'), hint: t('admin.settings.site_description_extended.desc_html'), input_html: { rows: 8 }
|
= f.input :site_extended_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description_extended.title'), hint: t('admin.settings.site_description_extended.desc_html'), input_html: { rows: 8 } unless whitelist_mode?
|
||||||
= f.input :site_terms, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_terms.title'), hint: t('admin.settings.site_terms.desc_html'), input_html: { rows: 8 }
|
= f.input :site_terms, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_terms.title'), hint: t('admin.settings.site_terms.desc_html'), input_html: { rows: 8 }
|
||||||
= f.input :custom_css, wrapper: :with_block_label, as: :text, input_html: { rows: 8 }, label: t('admin.settings.custom_css.title'), hint: t('admin.settings.custom_css.desc_html')
|
= f.input :custom_css, wrapper: :with_block_label, as: :text, input_html: { rows: 8 }, label: t('admin.settings.custom_css.title'), hint: t('admin.settings.custom_css.desc_html')
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
= f.input :invite_code, as: :hidden
|
= f.input :invite_code, as: :hidden
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path)
|
= f.input :agreement, as: :boolean, wrapper: :with_label, label: whitelist_mode? ? t('auth.checkbox_agreement_without_rules_html', terms_path: terms_path) : t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path)
|
||||||
|
|
||||||
.actions
|
.actions
|
||||||
= f.button :button, @invite.present? ? t('auth.register') : sign_up_message, type: :submit
|
= f.button :button, @invite.present? ? t('auth.register') : sign_up_message, type: :submit
|
||||||
|
|
|
@ -10,10 +10,13 @@
|
||||||
= link_to root_url, class: 'brand' do
|
= link_to root_url, class: 'brand' do
|
||||||
= svg_logo_full
|
= svg_logo_full
|
||||||
|
|
||||||
|
- unless whitelist_mode?
|
||||||
= link_to t('directories.directory'), explore_path, class: 'nav-link optional' if Setting.profile_directory
|
= link_to t('directories.directory'), explore_path, class: 'nav-link optional' if Setting.profile_directory
|
||||||
= link_to t('about.about_this'), about_more_path, class: 'nav-link optional'
|
= link_to t('about.about_this'), about_more_path, class: 'nav-link optional'
|
||||||
= link_to t('about.apps'), 'https://joinmastodon.org/apps', class: 'nav-link optional'
|
= link_to t('about.apps'), 'https://joinmastodon.org/apps', class: 'nav-link optional'
|
||||||
|
|
||||||
.nav-center
|
.nav-center
|
||||||
|
|
||||||
.nav-right
|
.nav-right
|
||||||
- if user_signed_in?
|
- if user_signed_in?
|
||||||
= link_to t('settings.back'), root_url, class: 'nav-link nav-button webapp-btn'
|
= link_to t('settings.back'), root_url, class: 'nav-link nav-button webapp-btn'
|
||||||
|
|
5
config/initializers/2_whitelist_mode.rb
Normal file
5
config/initializers/2_whitelist_mode.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
Rails.application.configure do
|
||||||
|
config.x.whitelist_mode = ENV['WHITELIST_MODE'] == 'true'
|
||||||
|
end
|
|
@ -186,6 +186,7 @@ en:
|
||||||
username: Username
|
username: Username
|
||||||
warn: Warn
|
warn: Warn
|
||||||
web: Web
|
web: Web
|
||||||
|
whitelisted: Whitelisted
|
||||||
action_logs:
|
action_logs:
|
||||||
actions:
|
actions:
|
||||||
assigned_to_self_report: "%{name} assigned report %{target} to themselves"
|
assigned_to_self_report: "%{name} assigned report %{target} to themselves"
|
||||||
|
@ -269,6 +270,11 @@ en:
|
||||||
week_interactions: interactions this week
|
week_interactions: interactions this week
|
||||||
week_users_active: active this week
|
week_users_active: active this week
|
||||||
week_users_new: users this week
|
week_users_new: users this week
|
||||||
|
domain_allows:
|
||||||
|
add_new: Whitelist domain
|
||||||
|
created_msg: Domain has been successfully whitelisted
|
||||||
|
destroyed_msg: Domain has been removed from the whitelist
|
||||||
|
undo: Remove from whitelist
|
||||||
domain_blocks:
|
domain_blocks:
|
||||||
add_new: Add new domain block
|
add_new: Add new domain block
|
||||||
created_msg: Domain block is now being processed
|
created_msg: Domain block is now being processed
|
||||||
|
@ -524,6 +530,7 @@ en:
|
||||||
apply_for_account: Request an invite
|
apply_for_account: Request an invite
|
||||||
change_password: Password
|
change_password: Password
|
||||||
checkbox_agreement_html: I agree to the <a href="%{rules_path}" target="_blank">server rules</a> and <a href="%{terms_path}" target="_blank">terms of service</a>
|
checkbox_agreement_html: I agree to the <a href="%{rules_path}" target="_blank">server rules</a> and <a href="%{terms_path}" target="_blank">terms of service</a>
|
||||||
|
checkbox_agreement_without_rules_html: I agree to the <a href="%{terms_path}" target="_blank">terms of service</a>
|
||||||
delete_account: Delete account
|
delete_account: Delete account
|
||||||
delete_account_html: If you wish to delete your account, you can <a href="%{path}">proceed here</a>. You will be asked for confirmation.
|
delete_account_html: If you wish to delete your account, you can <a href="%{path}">proceed here</a>. You will be asked for confirmation.
|
||||||
didnt_get_confirmation: Didn't receive confirmation instructions?
|
didnt_get_confirmation: Didn't receive confirmation instructions?
|
||||||
|
|
|
@ -38,6 +38,8 @@ en:
|
||||||
setting_use_pending_items: Hide timeline updates behind a click instead of automatically scrolling the feed
|
setting_use_pending_items: Hide timeline updates behind a click instead of automatically scrolling the feed
|
||||||
username: Your username will be unique on %{domain}
|
username: Your username will be unique on %{domain}
|
||||||
whole_word: When the keyword or phrase is alphanumeric only, it will only be applied if it matches the whole word
|
whole_word: When the keyword or phrase is alphanumeric only, it will only be applied if it matches the whole word
|
||||||
|
domain_allow:
|
||||||
|
domain: This domain will be able to fetch data from this server and incoming data from it will be processed and stored
|
||||||
featured_tag:
|
featured_tag:
|
||||||
name: 'You might want to use one of these:'
|
name: 'You might want to use one of these:'
|
||||||
imports:
|
imports:
|
||||||
|
|
|
@ -39,7 +39,7 @@ SimpleNavigation::Configuration.run do |navigation|
|
||||||
s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_url, highlights_on: %r{/admin/accounts|/admin/pending_accounts}
|
s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_url, highlights_on: %r{/admin/accounts|/admin/pending_accounts}
|
||||||
s.item :invites, safe_join([fa_icon('user-plus fw'), t('admin.invites.title')]), admin_invites_path
|
s.item :invites, safe_join([fa_icon('user-plus fw'), t('admin.invites.title')]), admin_invites_path
|
||||||
s.item :tags, safe_join([fa_icon('tag fw'), t('admin.tags.title')]), admin_tags_path
|
s.item :tags, safe_join([fa_icon('tag fw'), t('admin.tags.title')]), admin_tags_path
|
||||||
s.item :instances, safe_join([fa_icon('cloud fw'), t('admin.instances.title')]), admin_instances_url(limited: '1'), highlights_on: %r{/admin/instances|/admin/domain_blocks}, if: -> { current_user.admin? }
|
s.item :instances, safe_join([fa_icon('cloud fw'), t('admin.instances.title')]), admin_instances_url(limited: whitelist_mode? ? nil : '1'), highlights_on: %r{/admin/instances|/admin/domain_blocks|/admin/domain_allows}, if: -> { current_user.admin? }
|
||||||
s.item :email_domain_blocks, safe_join([fa_icon('envelope fw'), t('admin.email_domain_blocks.title')]), admin_email_domain_blocks_url, highlights_on: %r{/admin/email_domain_blocks}, if: -> { current_user.admin? }
|
s.item :email_domain_blocks, safe_join([fa_icon('envelope fw'), t('admin.email_domain_blocks.title')]), admin_email_domain_blocks_url, highlights_on: %r{/admin/email_domain_blocks}, if: -> { current_user.admin? }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -154,6 +154,7 @@ Rails.application.routes.draw do
|
||||||
namespace :admin do
|
namespace :admin do
|
||||||
get '/dashboard', to: 'dashboard#index'
|
get '/dashboard', to: 'dashboard#index'
|
||||||
|
|
||||||
|
resources :domain_allows, only: [:new, :create, :show, :destroy]
|
||||||
resources :domain_blocks, only: [:new, :create, :show, :destroy]
|
resources :domain_blocks, only: [:new, :create, :show, :destroy]
|
||||||
resources :email_domain_blocks, only: [:index, :new, :create, :destroy]
|
resources :email_domain_blocks, only: [:index, :new, :create, :destroy]
|
||||||
resources :action_logs, only: [:index]
|
resources :action_logs, only: [:index]
|
||||||
|
|
9
db/migrate/20190705002136_create_domain_allows.rb
Normal file
9
db/migrate/20190705002136_create_domain_allows.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
class CreateDomainAllows < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
create_table :domain_allows do |t|
|
||||||
|
t.string :domain, default: '', null: false, index: { unique: true }
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2019_07_26_175042) do
|
ActiveRecord::Schema.define(version: 2019_07_28_084117) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -245,6 +245,13 @@ ActiveRecord::Schema.define(version: 2019_07_26_175042) do
|
||||||
t.index ["account_id"], name: "index_custom_filters_on_account_id"
|
t.index ["account_id"], name: "index_custom_filters_on_account_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "domain_allows", force: :cascade do |t|
|
||||||
|
t.string "domain", default: "", null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["domain"], name: "index_domain_allows_on_domain", unique: true
|
||||||
|
end
|
||||||
|
|
||||||
create_table "domain_blocks", force: :cascade do |t|
|
create_table "domain_blocks", force: :cascade do |t|
|
||||||
t.string "domain", default: "", null: false
|
t.string "domain", default: "", null: false
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
|
|
|
@ -12,17 +12,33 @@ module Mastodon
|
||||||
end
|
end
|
||||||
|
|
||||||
option :dry_run, type: :boolean
|
option :dry_run, type: :boolean
|
||||||
desc 'purge DOMAIN', 'Remove accounts from a DOMAIN without a trace'
|
option :whitelist_mode, type: :boolean
|
||||||
|
desc 'purge [DOMAIN]', 'Remove accounts from a DOMAIN without a trace'
|
||||||
long_desc <<-LONG_DESC
|
long_desc <<-LONG_DESC
|
||||||
Remove all accounts from a given DOMAIN without leaving behind any
|
Remove all accounts from a given DOMAIN without leaving behind any
|
||||||
records. Unlike a suspension, if the DOMAIN still exists in the wild,
|
records. Unlike a suspension, if the DOMAIN still exists in the wild,
|
||||||
it means the accounts could return if they are resolved again.
|
it means the accounts could return if they are resolved again.
|
||||||
|
|
||||||
|
When the --whitelist-mode option is given, instead of purging accounts
|
||||||
|
from a single domain, all accounts from domains that are not whitelisted
|
||||||
|
are removed from the database.
|
||||||
LONG_DESC
|
LONG_DESC
|
||||||
def purge(domain)
|
def purge(domain = nil)
|
||||||
removed = 0
|
removed = 0
|
||||||
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
|
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
|
||||||
|
|
||||||
Account.where(domain: domain).find_each do |account|
|
scope = begin
|
||||||
|
if options[:whitelist_mode]
|
||||||
|
Account.remote.where.not(domain: DomainAllow.pluck(:domain))
|
||||||
|
elsif domain.present?
|
||||||
|
Account.remote.where(domain: domain)
|
||||||
|
else
|
||||||
|
say('No domain given', :red)
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scope.find_each do |account|
|
||||||
SuspendAccountService.new.call(account, destroy: true) unless options[:dry_run]
|
SuspendAccountService.new.call(account, destroy: true) unless options[:dry_run]
|
||||||
removed += 1
|
removed += 1
|
||||||
say('.', :green, false)
|
say('.', :green, false)
|
||||||
|
|
3
spec/fabricators/domain_allow_fabricator.rb
Normal file
3
spec/fabricators/domain_allow_fabricator.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Fabricator(:domain_allow) do
|
||||||
|
domain "MyString"
|
||||||
|
end
|
5
spec/models/domain_allow_spec.rb
Normal file
5
spec/models/domain_allow_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe DomainAllow, type: :model do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
|
@ -12,6 +12,7 @@ const uuid = require('uuid');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
const env = process.env.NODE_ENV || 'development';
|
const env = process.env.NODE_ENV || 'development';
|
||||||
|
const alwaysRequireAuth = process.env.WHITELIST_MODE === 'true' || process.env.AUTHORIZED_FETCH === 'true';
|
||||||
|
|
||||||
dotenv.config({
|
dotenv.config({
|
||||||
path: env === 'production' ? '.env.production' : '.env',
|
path: env === 'production' ? '.env.production' : '.env',
|
||||||
|
@ -271,7 +272,7 @@ const startWorker = (workerId) => {
|
||||||
|
|
||||||
const wsVerifyClient = (info, cb) => {
|
const wsVerifyClient = (info, cb) => {
|
||||||
const location = url.parse(info.req.url, true);
|
const location = url.parse(info.req.url, true);
|
||||||
const authRequired = !PUBLIC_STREAMS.some(stream => stream === location.query.stream);
|
const authRequired = alwaysRequireAuth || !PUBLIC_STREAMS.some(stream => stream === location.query.stream);
|
||||||
const allowedScopes = [];
|
const allowedScopes = [];
|
||||||
|
|
||||||
if (authRequired) {
|
if (authRequired) {
|
||||||
|
@ -306,7 +307,7 @@ const startWorker = (workerId) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const authRequired = !PUBLIC_ENDPOINTS.some(endpoint => endpoint === req.path);
|
const authRequired = alwaysRequireAuth || !PUBLIC_ENDPOINTS.some(endpoint => endpoint === req.path);
|
||||||
const allowedScopes = [];
|
const allowedScopes = [];
|
||||||
|
|
||||||
if (authRequired) {
|
if (authRequired) {
|
||||||
|
|
Loading…
Reference in a new issue