Merge remote-tracking branch 'upstream/main'
All checks were successful
continuous-integration/drone Build is passing
All checks were successful
continuous-integration/drone Build is passing
This commit is contained in:
commit
3dd4a94c57
71 changed files with 415 additions and 309 deletions
|
@ -26,7 +26,7 @@ Lint/NonLocalExitFromIterator:
|
|||
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
||||
Metrics/AbcSize:
|
||||
Max: 144
|
||||
Max: 125
|
||||
|
||||
# Configuration parameters: CountBlocks, Max.
|
||||
Metrics/BlockNesting:
|
||||
|
|
|
@ -247,7 +247,9 @@ RUN \
|
|||
RUN \
|
||||
# Pre-create and chown system volume to Mastodon user
|
||||
mkdir -p /opt/mastodon/public/system; \
|
||||
chown mastodon:mastodon /opt/mastodon/public/system;
|
||||
chown mastodon:mastodon /opt/mastodon/public/system; \
|
||||
# Set Mastodon user as owner of tmp folder
|
||||
chown -R mastodon:mastodon /opt/mastodon/tmp;
|
||||
|
||||
# Set the running user for resulting container
|
||||
USER mastodon
|
||||
|
|
|
@ -50,7 +50,7 @@ class AccountsController < ApplicationController
|
|||
end
|
||||
|
||||
def only_media_scope
|
||||
Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id)
|
||||
Status.joins(:media_attachments).merge(@account.media_attachments).group(:id)
|
||||
end
|
||||
|
||||
def no_replies_scope
|
||||
|
|
|
@ -4,7 +4,7 @@ require 'csv'
|
|||
|
||||
module Admin
|
||||
class ExportDomainAllowsController < BaseController
|
||||
include AdminExportControllerConcern
|
||||
include Admin::ExportControllerConcern
|
||||
|
||||
before_action :set_dummy_import!, only: [:new]
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ require 'csv'
|
|||
|
||||
module Admin
|
||||
class ExportDomainBlocksController < BaseController
|
||||
include AdminExportControllerConcern
|
||||
include Admin::ExportControllerConcern
|
||||
|
||||
before_action :set_dummy_import!, only: [:new]
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@ class Api::BaseController < ApplicationController
|
|||
DEFAULT_STATUSES_LIMIT = 20
|
||||
DEFAULT_ACCOUNTS_LIMIT = 40
|
||||
|
||||
include RateLimitHeaders
|
||||
include AccessTokenTrackingConcern
|
||||
include ApiCachingConcern
|
||||
include Api::RateLimitHeaders
|
||||
include Api::AccessTokenTrackingConcern
|
||||
include Api::CachingConcern
|
||||
include Api::ContentSecurityPolicy
|
||||
|
||||
skip_before_action :require_functional!, unless: :limited_federation_mode?
|
||||
|
@ -105,7 +105,7 @@ class Api::BaseController < ApplicationController
|
|||
end
|
||||
|
||||
def require_not_suspended!
|
||||
render json: { error: 'Your login is currently disabled' }, status: 403 if current_user&.account&.suspended?
|
||||
render json: { error: 'Your login is currently disabled' }, status: 403 if current_user&.account&.unavailable?
|
||||
end
|
||||
|
||||
def require_user!
|
||||
|
|
|
@ -26,7 +26,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def hide_results?
|
||||
@account.suspended? || (@account.hides_followers? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
|
||||
@account.unavailable? || (@account.hides_followers? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
|
|
|
@ -26,7 +26,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def hide_results?
|
||||
@account.suspended? || (@account.hides_following? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
|
||||
@account.unavailable? || (@account.hides_following? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
|
|
|
@ -19,7 +19,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
|||
end
|
||||
|
||||
def load_statuses
|
||||
@account.suspended? ? [] : cached_account_statuses
|
||||
@account.unavailable? ? [] : cached_account_statuses
|
||||
end
|
||||
|
||||
def cached_account_statuses
|
||||
|
|
|
@ -8,6 +8,11 @@ class Api::V2::SearchController < Api::BaseController
|
|||
before_action -> { authorize_if_got_token! :read, :'read:search' }
|
||||
before_action :validate_search_params!
|
||||
|
||||
with_options unless: :user_signed_in? do
|
||||
before_action :query_pagination_error, if: :pagination_requested?
|
||||
before_action :remote_resolve_error, if: :remote_resolve_requested?
|
||||
end
|
||||
|
||||
def index
|
||||
@search = Search.new(search_results)
|
||||
render json: @search, serializer: REST::SearchSerializer
|
||||
|
@ -21,12 +26,22 @@ class Api::V2::SearchController < Api::BaseController
|
|||
|
||||
def validate_search_params!
|
||||
params.require(:q)
|
||||
end
|
||||
|
||||
return if user_signed_in?
|
||||
def query_pagination_error
|
||||
render json: { error: 'Search queries pagination is not supported without authentication' }, status: 401
|
||||
end
|
||||
|
||||
return render json: { error: 'Search queries pagination is not supported without authentication' }, status: 401 if params[:offset].present?
|
||||
def remote_resolve_error
|
||||
render json: { error: 'Search queries that resolve remote resources are not supported without authentication' }, status: 401
|
||||
end
|
||||
|
||||
render json: { error: 'Search queries that resolve remote resources are not supported without authentication' }, status: 401 if truthy_param?(:resolve)
|
||||
def remote_resolve_requested?
|
||||
truthy_param?(:resolve)
|
||||
end
|
||||
|
||||
def pagination_requested?
|
||||
params[:offset].present?
|
||||
end
|
||||
|
||||
def search_results
|
||||
|
@ -34,7 +49,15 @@ class Api::V2::SearchController < Api::BaseController
|
|||
params[:q],
|
||||
current_account,
|
||||
limit_param(RESULTS_LIMIT),
|
||||
search_params.merge(resolve: truthy_param?(:resolve), exclude_unreviewed: truthy_param?(:exclude_unreviewed), following: truthy_param?(:following))
|
||||
combined_search_params
|
||||
)
|
||||
end
|
||||
|
||||
def combined_search_params
|
||||
search_params.merge(
|
||||
resolve: truthy_param?(:resolve),
|
||||
exclude_unreviewed: truthy_param?(:exclude_unreviewed),
|
||||
following: truthy_param?(:following)
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Auth::ConfirmationsController < Devise::ConfirmationsController
|
||||
include CaptchaConcern
|
||||
include Auth::CaptchaConcern
|
||||
|
||||
layout 'auth'
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class Auth::RegistrationsController < Devise::RegistrationsController
|
||||
include RegistrationHelper
|
||||
include RegistrationSpamConcern
|
||||
include Auth::RegistrationSpamConcern
|
||||
|
||||
layout :determine_layout
|
||||
|
||||
|
@ -120,7 +120,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
|||
end
|
||||
|
||||
def require_not_suspended!
|
||||
forbidden if current_account.suspended?
|
||||
forbidden if current_account.unavailable?
|
||||
end
|
||||
|
||||
def set_rules
|
||||
|
|
|
@ -10,7 +10,7 @@ class Auth::SessionsController < Devise::SessionsController
|
|||
|
||||
prepend_before_action :check_suspicious!, only: [:create]
|
||||
|
||||
include TwoFactorAuthenticationConcern
|
||||
include Auth::TwoFactorAuthenticationConcern
|
||||
|
||||
before_action :set_body_classes
|
||||
|
||||
|
|
|
@ -34,8 +34,8 @@ module AccountOwnedConcern
|
|||
end
|
||||
|
||||
def check_account_suspension
|
||||
if @account.suspended_permanently?
|
||||
permanent_suspension_response
|
||||
if @account.permanently_unavailable?
|
||||
permanent_unavailability_response
|
||||
elsif @account.suspended? && !skip_temporary_suspension_response?
|
||||
temporary_suspension_response
|
||||
end
|
||||
|
@ -45,7 +45,7 @@ module AccountOwnedConcern
|
|||
false
|
||||
end
|
||||
|
||||
def permanent_suspension_response
|
||||
def permanent_unavailability_response
|
||||
expires_in(3.minutes, public: true)
|
||||
gone
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AdminExportControllerConcern
|
||||
module Admin::ExportControllerConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
private
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AccessTokenTrackingConcern
|
||||
module Api::AccessTokenTrackingConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
ACCESS_TOKEN_UPDATE_FREQUENCY = 24.hours.freeze
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ApiCachingConcern
|
||||
module Api::CachingConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def cache_if_unauthenticated!
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RateLimitHeaders
|
||||
module Api::RateLimitHeaders
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module CaptchaConcern
|
||||
module Auth::CaptchaConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include Hcaptcha::Adapters::ViewMethods
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RegistrationSpamConcern
|
||||
module Auth::RegistrationSpamConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def set_registration_form_time
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module TwoFactorAuthenticationConcern
|
||||
module Auth::TwoFactorAuthenticationConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ExportControllerConcern
|
||||
module Settings::ExportControllerConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
|
@ -31,7 +31,7 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
|
|||
end
|
||||
|
||||
def require_not_suspended!
|
||||
forbidden if current_account.suspended?
|
||||
forbidden if current_account.unavailable?
|
||||
end
|
||||
|
||||
def set_cache_headers
|
||||
|
|
|
@ -18,6 +18,6 @@ class Settings::BaseController < ApplicationController
|
|||
end
|
||||
|
||||
def require_not_suspended!
|
||||
forbidden if current_account.suspended?
|
||||
forbidden if current_account.unavailable?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,7 +25,7 @@ class Settings::DeletesController < Settings::BaseController
|
|||
end
|
||||
|
||||
def require_not_suspended!
|
||||
forbidden if current_account.suspended?
|
||||
forbidden if current_account.unavailable?
|
||||
end
|
||||
|
||||
def challenge_passed?
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module Settings
|
||||
module Exports
|
||||
class BlockedAccountsController < BaseController
|
||||
include ExportControllerConcern
|
||||
include Settings::ExportControllerConcern
|
||||
|
||||
def index
|
||||
send_export_file
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module Settings
|
||||
module Exports
|
||||
class BlockedDomainsController < BaseController
|
||||
include ExportControllerConcern
|
||||
include Settings::ExportControllerConcern
|
||||
|
||||
def index
|
||||
send_export_file
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module Settings
|
||||
module Exports
|
||||
class BookmarksController < BaseController
|
||||
include ExportControllerConcern
|
||||
include Settings::ExportControllerConcern
|
||||
|
||||
def index
|
||||
send_export_file
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module Settings
|
||||
module Exports
|
||||
class FollowingAccountsController < BaseController
|
||||
include ExportControllerConcern
|
||||
include Settings::ExportControllerConcern
|
||||
|
||||
def index
|
||||
send_export_file
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module Settings
|
||||
module Exports
|
||||
class ListsController < BaseController
|
||||
include ExportControllerConcern
|
||||
include Settings::ExportControllerConcern
|
||||
|
||||
def index
|
||||
send_export_file
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module Settings
|
||||
module Exports
|
||||
class MutedAccountsController < BaseController
|
||||
include ExportControllerConcern
|
||||
include Settings::ExportControllerConcern
|
||||
|
||||
def index
|
||||
send_export_file
|
||||
|
|
|
@ -42,7 +42,7 @@ module WellKnown
|
|||
end
|
||||
|
||||
def check_account_suspension
|
||||
gone if @account.suspended_permanently?
|
||||
gone if @account.permanently_unavailable?
|
||||
end
|
||||
|
||||
def gone
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-call,
|
||||
@typescript-eslint/no-unsafe-return,
|
||||
@typescript-eslint/no-unsafe-assignment,
|
||||
@typescript-eslint/no-unsafe-member-access
|
||||
-- the settings store is not yet typed */
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useCallback, useState, useEffect } from 'react';
|
||||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
|
||||
import { changeSetting } from 'mastodon/actions/settings';
|
||||
import { bannerSettings } from 'mastodon/settings';
|
||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||
|
||||
import { IconButton } from './icon_button';
|
||||
|
||||
|
@ -21,13 +28,25 @@ export const DismissableBanner: React.FC<PropsWithChildren<Props>> = ({
|
|||
id,
|
||||
children,
|
||||
}) => {
|
||||
const [visible, setVisible] = useState(!bannerSettings.get(id));
|
||||
const dismissed = useAppSelector((state) =>
|
||||
state.settings.getIn(['dismissed_banners', id], false),
|
||||
);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const [visible, setVisible] = useState(!bannerSettings.get(id) && !dismissed);
|
||||
const intl = useIntl();
|
||||
|
||||
const handleDismiss = useCallback(() => {
|
||||
setVisible(false);
|
||||
bannerSettings.set(id, true);
|
||||
}, [id]);
|
||||
dispatch(changeSetting(['dismissed_banners', id], true));
|
||||
}, [id, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!visible && !dismissed) {
|
||||
dispatch(changeSetting(['dismissed_banners', id], true));
|
||||
}
|
||||
}, [id, dispatch, visible, dismissed]);
|
||||
|
||||
if (!visible) {
|
||||
return null;
|
||||
|
|
|
@ -100,6 +100,15 @@ const initialState = ImmutableMap({
|
|||
body: '',
|
||||
}),
|
||||
}),
|
||||
|
||||
dismissed_banners: ImmutableMap({
|
||||
'public_timeline': false,
|
||||
'community_timeline': false,
|
||||
'home.explore_prompt': false,
|
||||
'explore/links': false,
|
||||
'explore/statuses': false,
|
||||
'explore/tags': false,
|
||||
}),
|
||||
});
|
||||
|
||||
const defaultColumns = fromJS([
|
||||
|
|
|
@ -2732,22 +2732,16 @@ $ui-header-height: 55px;
|
|||
&__description {
|
||||
flex: 1 1 auto;
|
||||
line-height: 20px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
||||
h6 {
|
||||
color: $highlight-text-color;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
p {
|
||||
color: $darker-text-color;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ class AccountStatusesFilter
|
|||
private
|
||||
|
||||
def initial_scope
|
||||
return Status.none if suspended?
|
||||
return Status.none if account.unavailable?
|
||||
|
||||
if anonymous?
|
||||
account.statuses.where(visibility: %i(public unlisted))
|
||||
|
@ -70,7 +70,7 @@ class AccountStatusesFilter
|
|||
end
|
||||
|
||||
def only_media_scope
|
||||
Status.joins(:media_attachments).merge(account.media_attachments.reorder(nil)).group(Status.arel_table[:id])
|
||||
Status.joins(:media_attachments).merge(account.media_attachments).group(Status.arel_table[:id])
|
||||
end
|
||||
|
||||
def no_replies_scope
|
||||
|
@ -95,10 +95,6 @@ class AccountStatusesFilter
|
|||
end
|
||||
end
|
||||
|
||||
def suspended?
|
||||
account.suspended?
|
||||
end
|
||||
|
||||
def anonymous?
|
||||
current_account.nil?
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ class ActivityPub::Activity::Move < ActivityPub::Activity
|
|||
|
||||
target_account = ActivityPub::FetchRemoteAccountService.new.call(target_uri)
|
||||
|
||||
if target_account.nil? || target_account.suspended? || !target_account.also_known_as.include?(origin_account.uri)
|
||||
if target_account.nil? || target_account.unavailable? || !target_account.also_known_as.include?(origin_account.uri)
|
||||
unmark_as_processing!
|
||||
return
|
||||
end
|
||||
|
|
|
@ -9,8 +9,8 @@ class ContentSecurityPolicy
|
|||
url_from_configured_asset_host || url_from_base_host
|
||||
end
|
||||
|
||||
def media_host
|
||||
cdn_host_value || assets_host
|
||||
def media_hosts
|
||||
[assets_host, cdn_host_value].compact
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -27,11 +27,11 @@ class Vacuum::MediaAttachmentsVacuum
|
|||
end
|
||||
|
||||
def media_attachments_past_retention_period
|
||||
MediaAttachment.unscoped.remote.cached.where(MediaAttachment.arel_table[:created_at].lt(@retention_period.ago)).where(MediaAttachment.arel_table[:updated_at].lt(@retention_period.ago))
|
||||
MediaAttachment.remote.cached.where(MediaAttachment.arel_table[:created_at].lt(@retention_period.ago)).where(MediaAttachment.arel_table[:updated_at].lt(@retention_period.ago))
|
||||
end
|
||||
|
||||
def orphaned_media_attachments
|
||||
MediaAttachment.unscoped.unattached.where(MediaAttachment.arel_table[:created_at].lt(TTL.ago))
|
||||
MediaAttachment.unattached.where(MediaAttachment.arel_table[:created_at].lt(TTL.ago))
|
||||
end
|
||||
|
||||
def retention_period?
|
||||
|
|
|
@ -246,6 +246,9 @@ class Account < ApplicationRecord
|
|||
suspended? && deletion_request.present?
|
||||
end
|
||||
|
||||
alias unavailable? suspended?
|
||||
alias permanently_unavailable? suspended_permanently?
|
||||
|
||||
def suspend!(date: Time.now.utc, origin: :local, block_email: true)
|
||||
transaction do
|
||||
create_deletion_request!
|
||||
|
|
|
@ -32,7 +32,7 @@ class Admin::StatusFilter
|
|||
def scope_for(key, _value)
|
||||
case key.to_s
|
||||
when 'media'
|
||||
Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id).reorder('statuses.id desc')
|
||||
Status.joins(:media_attachments).merge(@account.media_attachments).group(:id).reorder('statuses.id desc')
|
||||
else
|
||||
raise Mastodon::InvalidParameterError, "Unknown filter: #{key}"
|
||||
end
|
||||
|
|
|
@ -205,12 +205,11 @@ class MediaAttachment < ApplicationRecord
|
|||
validates :thumbnail, absence: true, if: -> { local? && !audio_or_video? }
|
||||
|
||||
scope :attached, -> { where.not(status_id: nil).or(where.not(scheduled_status_id: nil)) }
|
||||
scope :unattached, -> { where(status_id: nil, scheduled_status_id: nil) }
|
||||
scope :local, -> { where(remote_url: '') }
|
||||
scope :remote, -> { where.not(remote_url: '') }
|
||||
scope :cached, -> { remote.where.not(file_file_name: nil) }
|
||||
|
||||
default_scope { order(id: :asc) }
|
||||
scope :local, -> { where(remote_url: '') }
|
||||
scope :ordered, -> { order(id: :asc) }
|
||||
scope :remote, -> { where.not(remote_url: '') }
|
||||
scope :unattached, -> { where(status_id: nil, scheduled_status_id: nil) }
|
||||
|
||||
attr_accessor :skip_download
|
||||
|
||||
|
|
|
@ -250,7 +250,7 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
def functional_or_moved?
|
||||
confirmed? && approved? && !disabled? && !account.suspended? && !account.memorial?
|
||||
confirmed? && approved? && !disabled? && !account.unavailable? && !account.memorial?
|
||||
end
|
||||
|
||||
def unconfirmed?
|
||||
|
|
|
@ -8,7 +8,7 @@ class StatusPolicy < ApplicationPolicy
|
|||
end
|
||||
|
||||
def show?
|
||||
return false if author.suspended?
|
||||
return false if author.unavailable?
|
||||
|
||||
if requires_mention?
|
||||
owned? || mention_exists?
|
||||
|
|
|
@ -96,19 +96,19 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
|
|||
end
|
||||
|
||||
def discoverable
|
||||
object.suspended? ? false : (object.discoverable || false)
|
||||
object.unavailable? ? false : (object.discoverable || false)
|
||||
end
|
||||
|
||||
def indexable
|
||||
object.suspended? ? false : (object.indexable || false)
|
||||
object.unavailable? ? false : (object.indexable || false)
|
||||
end
|
||||
|
||||
def name
|
||||
object.suspended? ? object.username : (object.display_name.presence || object.username)
|
||||
object.unavailable? ? object.username : (object.display_name.presence || object.username)
|
||||
end
|
||||
|
||||
def summary
|
||||
object.suspended? ? '' : account_bio_format(object)
|
||||
object.unavailable? ? '' : account_bio_format(object)
|
||||
end
|
||||
|
||||
def icon
|
||||
|
@ -132,23 +132,23 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
|
|||
end
|
||||
|
||||
def avatar_exists?
|
||||
!object.suspended? && object.avatar?
|
||||
!object.unavailable? && object.avatar?
|
||||
end
|
||||
|
||||
def header_exists?
|
||||
!object.suspended? && object.header?
|
||||
!object.unavailable? && object.header?
|
||||
end
|
||||
|
||||
def manually_approves_followers
|
||||
object.suspended? ? false : object.locked
|
||||
object.unavailable? ? false : object.locked
|
||||
end
|
||||
|
||||
def virtual_tags
|
||||
object.suspended? ? [] : (object.emojis + object.tags)
|
||||
object.unavailable? ? [] : (object.emojis + object.tags)
|
||||
end
|
||||
|
||||
def virtual_attachments
|
||||
object.suspended? ? [] : object.fields
|
||||
object.unavailable? ? [] : object.fields
|
||||
end
|
||||
|
||||
def moved_to
|
||||
|
@ -156,11 +156,11 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
|
|||
end
|
||||
|
||||
def moved?
|
||||
!object.suspended? && object.moved?
|
||||
!object.unavailable? && object.moved?
|
||||
end
|
||||
|
||||
def also_known_as?
|
||||
!object.suspended? && !object.also_known_as.empty?
|
||||
!object.unavailable? && !object.also_known_as.empty?
|
||||
end
|
||||
|
||||
def published
|
||||
|
|
|
@ -39,18 +39,18 @@ class InitialStateSerializer < ActiveModel::Serializer
|
|||
|
||||
if object.current_account
|
||||
store[:me] = object.current_account.id.to_s
|
||||
store[:unfollow_modal] = object.current_account.user.setting_unfollow_modal
|
||||
store[:boost_modal] = object.current_account.user.setting_boost_modal
|
||||
store[:delete_modal] = object.current_account.user.setting_delete_modal
|
||||
store[:auto_play_gif] = object.current_account.user.setting_auto_play_gif
|
||||
store[:display_media] = object.current_account.user.setting_display_media
|
||||
store[:expand_spoilers] = object.current_account.user.setting_expand_spoilers
|
||||
store[:reduce_motion] = object.current_account.user.setting_reduce_motion
|
||||
store[:disable_swiping] = object.current_account.user.setting_disable_swiping
|
||||
store[:advanced_layout] = object.current_account.user.setting_advanced_layout
|
||||
store[:use_blurhash] = object.current_account.user.setting_use_blurhash
|
||||
store[:use_pending_items] = object.current_account.user.setting_use_pending_items
|
||||
store[:show_trends] = Setting.trends && object.current_account.user.setting_trends
|
||||
store[:unfollow_modal] = object_account_user.setting_unfollow_modal
|
||||
store[:boost_modal] = object_account_user.setting_boost_modal
|
||||
store[:delete_modal] = object_account_user.setting_delete_modal
|
||||
store[:auto_play_gif] = object_account_user.setting_auto_play_gif
|
||||
store[:display_media] = object_account_user.setting_display_media
|
||||
store[:expand_spoilers] = object_account_user.setting_expand_spoilers
|
||||
store[:reduce_motion] = object_account_user.setting_reduce_motion
|
||||
store[:disable_swiping] = object_account_user.setting_disable_swiping
|
||||
store[:advanced_layout] = object_account_user.setting_advanced_layout
|
||||
store[:use_blurhash] = object_account_user.setting_use_blurhash
|
||||
store[:use_pending_items] = object_account_user.setting_use_pending_items
|
||||
store[:show_trends] = Setting.trends && object_account_user.setting_trends
|
||||
else
|
||||
store[:auto_play_gif] = Setting.auto_play_gif
|
||||
store[:display_media] = Setting.display_media
|
||||
|
@ -71,9 +71,9 @@ class InitialStateSerializer < ActiveModel::Serializer
|
|||
|
||||
if object.current_account
|
||||
store[:me] = object.current_account.id.to_s
|
||||
store[:default_privacy] = object.visibility || object.current_account.user.setting_default_privacy
|
||||
store[:default_sensitive] = object.current_account.user.setting_default_sensitive
|
||||
store[:default_language] = object.current_account.user.preferred_posting_language
|
||||
store[:default_privacy] = object.visibility || object_account_user.setting_default_privacy
|
||||
store[:default_sensitive] = object_account_user.setting_default_sensitive
|
||||
store[:default_language] = object_account_user.preferred_posting_language
|
||||
end
|
||||
|
||||
store[:text] = object.text if object.text
|
||||
|
@ -89,11 +89,11 @@ class InitialStateSerializer < ActiveModel::Serializer
|
|||
associations: [:account_stat, :user, { moved_to_account: [:account_stat, :user] }]
|
||||
)
|
||||
|
||||
store[object.current_account.id.to_s] = ActiveModelSerializers::SerializableResource.new(object.current_account, serializer: REST::AccountSerializer) if object.current_account
|
||||
store[object.admin.id.to_s] = ActiveModelSerializers::SerializableResource.new(object.admin, serializer: REST::AccountSerializer) if object.admin
|
||||
store[object.owner.id.to_s] = ActiveModelSerializers::SerializableResource.new(object.owner, serializer: REST::AccountSerializer) if object.owner
|
||||
store[object.disabled_account.id.to_s] = ActiveModelSerializers::SerializableResource.new(object.disabled_account, serializer: REST::AccountSerializer) if object.disabled_account
|
||||
store[object.moved_to_account.id.to_s] = ActiveModelSerializers::SerializableResource.new(object.moved_to_account, serializer: REST::AccountSerializer) if object.moved_to_account
|
||||
store[object.current_account.id.to_s] = serialized_account(object.current_account) if object.current_account
|
||||
store[object.admin.id.to_s] = serialized_account(object.admin) if object.admin
|
||||
store[object.owner.id.to_s] = serialized_account(object.owner) if object.owner
|
||||
store[object.disabled_account.id.to_s] = serialized_account(object.disabled_account) if object.disabled_account
|
||||
store[object.moved_to_account.id.to_s] = serialized_account(object.moved_to_account) if object.moved_to_account
|
||||
|
||||
store
|
||||
end
|
||||
|
@ -108,6 +108,14 @@ class InitialStateSerializer < ActiveModel::Serializer
|
|||
|
||||
private
|
||||
|
||||
def object_account_user
|
||||
object.current_account.user
|
||||
end
|
||||
|
||||
def serialized_account(account)
|
||||
ActiveModelSerializers::SerializableResource.new(account, serializer: REST::AccountSerializer)
|
||||
end
|
||||
|
||||
def instance_presenter
|
||||
@instance_presenter ||= InstancePresenter.new
|
||||
end
|
||||
|
|
|
@ -61,7 +61,7 @@ class REST::AccountSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def note
|
||||
object.suspended? ? '' : account_bio_format(object)
|
||||
object.unavailable? ? '' : account_bio_format(object)
|
||||
end
|
||||
|
||||
def url
|
||||
|
@ -73,19 +73,19 @@ class REST::AccountSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def avatar
|
||||
full_asset_url(object.suspended? ? object.avatar.default_url : object.avatar_original_url)
|
||||
full_asset_url(object.unavailable? ? object.avatar.default_url : object.avatar_original_url)
|
||||
end
|
||||
|
||||
def avatar_static
|
||||
full_asset_url(object.suspended? ? object.avatar.default_url : object.avatar_static_url)
|
||||
full_asset_url(object.unavailable? ? object.avatar.default_url : object.avatar_static_url)
|
||||
end
|
||||
|
||||
def header
|
||||
full_asset_url(object.suspended? ? object.header.default_url : object.header_original_url)
|
||||
full_asset_url(object.unavailable? ? object.header.default_url : object.header_original_url)
|
||||
end
|
||||
|
||||
def header_static
|
||||
full_asset_url(object.suspended? ? object.header.default_url : object.header_static_url)
|
||||
full_asset_url(object.unavailable? ? object.header.default_url : object.header_static_url)
|
||||
end
|
||||
|
||||
def created_at
|
||||
|
@ -97,39 +97,39 @@ class REST::AccountSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def display_name
|
||||
object.suspended? ? '' : object.display_name
|
||||
object.unavailable? ? '' : object.display_name
|
||||
end
|
||||
|
||||
def locked
|
||||
object.suspended? ? false : object.locked
|
||||
object.unavailable? ? false : object.locked
|
||||
end
|
||||
|
||||
def bot
|
||||
object.suspended? ? false : object.bot
|
||||
object.unavailable? ? false : object.bot
|
||||
end
|
||||
|
||||
def discoverable
|
||||
object.suspended? ? false : object.discoverable
|
||||
object.unavailable? ? false : object.discoverable
|
||||
end
|
||||
|
||||
def indexable
|
||||
object.suspended? ? false : object.indexable
|
||||
object.unavailable? ? false : object.indexable
|
||||
end
|
||||
|
||||
def moved_to_account
|
||||
object.suspended? ? nil : AccountDecorator.new(object.moved_to_account)
|
||||
object.unavailable? ? nil : AccountDecorator.new(object.moved_to_account)
|
||||
end
|
||||
|
||||
def emojis
|
||||
object.suspended? ? [] : object.emojis
|
||||
object.unavailable? ? [] : object.emojis
|
||||
end
|
||||
|
||||
def fields
|
||||
object.suspended? ? [] : object.fields
|
||||
object.unavailable? ? [] : object.fields
|
||||
end
|
||||
|
||||
def suspended
|
||||
object.suspended?
|
||||
object.unavailable?
|
||||
end
|
||||
|
||||
def silenced
|
||||
|
@ -141,7 +141,7 @@ class REST::AccountSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def roles
|
||||
if object.suspended? || object.user.nil?
|
||||
if object.unavailable? || object.user.nil?
|
||||
[]
|
||||
else
|
||||
[object.user.role].compact.filter(&:highlighted?)
|
||||
|
|
|
@ -72,7 +72,7 @@ class BackupService < BaseService
|
|||
end
|
||||
|
||||
def dump_media_attachments!(zipfile)
|
||||
MediaAttachment.attached.where(account: account).reorder(nil).find_in_batches do |media_attachments|
|
||||
MediaAttachment.attached.where(account: account).find_in_batches do |media_attachments|
|
||||
media_attachments.each do |m|
|
||||
path = m.file&.path
|
||||
next unless path
|
||||
|
|
|
@ -43,7 +43,7 @@ class ClearDomainMediaService < BaseService
|
|||
end
|
||||
|
||||
def media_from_blocked_domain
|
||||
MediaAttachment.joins(:account).merge(blocked_domain_accounts).reorder(nil)
|
||||
MediaAttachment.joins(:account).merge(blocked_domain_accounts)
|
||||
end
|
||||
|
||||
def emojis_from_blocked_domains
|
||||
|
|
|
@ -165,7 +165,7 @@ class DeleteAccountService < BaseService
|
|||
end
|
||||
|
||||
def purge_media_attachments!
|
||||
@account.media_attachments.reorder(nil).find_each do |media_attachment|
|
||||
@account.media_attachments.find_each do |media_attachment|
|
||||
next if keep_account_record? && reported_status_ids.include?(media_attachment.status_id)
|
||||
|
||||
media_attachment.destroy
|
||||
|
|
|
@ -50,7 +50,7 @@ class FollowService < BaseService
|
|||
end
|
||||
|
||||
def following_not_possible?
|
||||
@target_account.nil? || @target_account.id == @source_account.id || @target_account.suspended?
|
||||
@target_account.nil? || @target_account.id == @source_account.id || @target_account.unavailable?
|
||||
end
|
||||
|
||||
def following_not_allowed?
|
||||
|
|
|
@ -108,7 +108,7 @@ class NotifyService < BaseService
|
|||
end
|
||||
|
||||
def blocked?
|
||||
blocked = @recipient.suspended?
|
||||
blocked = @recipient.unavailable?
|
||||
blocked ||= from_self? && @notification.type != :poll
|
||||
|
||||
return blocked if message? && from_staff?
|
||||
|
|
|
@ -51,7 +51,7 @@ class ProcessMentionsService < BaseService
|
|||
|
||||
# If after resolving it still isn't found or isn't the right
|
||||
# protocol, then give up
|
||||
next match if mention_undeliverable?(mentioned_account) || mentioned_account&.suspended?
|
||||
next match if mention_undeliverable?(mentioned_account) || mentioned_account&.unavailable?
|
||||
|
||||
mention = @previous_mentions.find { |x| x.account_id == mentioned_account.id }
|
||||
mention ||= @current_mentions.find { |x| x.account_id == mentioned_account.id }
|
||||
|
|
|
@ -12,7 +12,7 @@ class ReportService < BaseService
|
|||
@rule_ids = options.delete(:rule_ids).presence
|
||||
@options = options
|
||||
|
||||
raise ActiveRecord::RecordNotFound if @target_account.suspended?
|
||||
raise ActiveRecord::RecordNotFound if @target_account.unavailable?
|
||||
|
||||
create_report!
|
||||
notify_staff!
|
||||
|
|
|
@ -65,7 +65,7 @@ class SuspendAccountService < BaseService
|
|||
def privatize_media_attachments!
|
||||
attachment_names = MediaAttachment.attachment_definitions.keys
|
||||
|
||||
@account.media_attachments.reorder(nil).find_each do |media_attachment|
|
||||
@account.media_attachments.find_each do |media_attachment|
|
||||
attachment_names.each do |attachment_name|
|
||||
attachment = media_attachment.public_send(attachment_name)
|
||||
styles = MediaAttachment::DEFAULT_STYLES | attachment.styles.keys
|
||||
|
|
|
@ -61,7 +61,7 @@ class UnsuspendAccountService < BaseService
|
|||
def publish_media_attachments!
|
||||
attachment_names = MediaAttachment.attachment_definitions.keys
|
||||
|
||||
@account.media_attachments.reorder(nil).find_each do |media_attachment|
|
||||
@account.media_attachments.find_each do |media_attachment|
|
||||
attachment_names.each do |attachment_name|
|
||||
attachment = media_attachment.public_send(attachment_name)
|
||||
styles = MediaAttachment::DEFAULT_STYLES | attachment.styles.keys
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.batch-table__row{ class: [!account.suspended? && account.user_pending? && 'batch-table__row--attention', (account.suspended? || account.user_unconfirmed?) && 'batch-table__row--muted'] }
|
||||
.batch-table__row{ class: [!account.unavailable? && account.user_pending? && 'batch-table__row--attention', (account.unavailable? || account.user_unconfirmed?) && 'batch-table__row--muted'] }
|
||||
%label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox
|
||||
= f.check_box :account_ids, { multiple: true, include_hidden: false }, account.id
|
||||
.batch-table__row__content.batch-table__row__content--unpadded
|
||||
|
@ -8,13 +8,13 @@
|
|||
%td
|
||||
= account_link_to account, path: admin_account_path(account.id)
|
||||
%td.accounts-table__count.optional
|
||||
- if account.suspended? || account.user_pending?
|
||||
- if account.unavailable? || account.user_pending?
|
||||
\-
|
||||
- else
|
||||
= friendly_number_to_human account.statuses_count
|
||||
%small= t('accounts.posts', count: account.statuses_count).downcase
|
||||
%td.accounts-table__count.optional
|
||||
- if account.suspended? || account.user_pending?
|
||||
- if account.unavailable? || account.user_pending?
|
||||
\-
|
||||
- else
|
||||
= friendly_number_to_human account.followers_count
|
||||
|
@ -30,6 +30,6 @@
|
|||
\-
|
||||
%br/
|
||||
%samp.ellipsized-ip= relevant_account_ip(account, params[:ip])
|
||||
- if !account.suspended? && account.user_pending? && account.user&.invite_request&.text.present?
|
||||
- if !account.unavailable? && account.user_pending? && account.user&.invite_request&.text.present?
|
||||
.batch-table__row__content__quote
|
||||
%p= account.user&.invite_request&.text
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
= t('doorkeeper.authorized_applications.index.authorized_at', date: l(application.created_at.to_date))
|
||||
|
||||
- unless application.superapp? || current_account.suspended?
|
||||
- unless application.superapp? || current_account.unavailable?
|
||||
%div
|
||||
= table_link_to 'times', t('doorkeeper.authorized_applications.buttons.revoke'), oauth_authorized_application_path(application), method: :delete, data: { confirm: t('doorkeeper.authorized_applications.confirmations.revoke') }
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ class AccountDeletionWorker
|
|||
|
||||
def perform(account_id, options = {})
|
||||
account = Account.find(account_id)
|
||||
return unless account.suspended?
|
||||
return unless account.unavailable?
|
||||
|
||||
reserve_username = options.with_indifferent_access.fetch(:reserve_username, true)
|
||||
skip_activitypub = options.with_indifferent_access.fetch(:skip_activitypub, false)
|
||||
|
|
|
@ -21,12 +21,12 @@ class Scheduler::SuspendedUserCleanupScheduler
|
|||
def perform
|
||||
return if Sidekiq::Queue.new('pull').size > MAX_PULL_SIZE
|
||||
|
||||
clean_suspended_accounts!
|
||||
process_deletion_requests!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def clean_suspended_accounts!
|
||||
def process_deletion_requests!
|
||||
# This should be fine because we only process a small amount of deletion requests at once and
|
||||
# `id` and `created_at` should follow the same order.
|
||||
AccountDeletionRequest.reorder(id: :asc).take(MAX_DELETIONS_PER_JOB).each do |deletion_request|
|
||||
|
|
|
@ -10,7 +10,7 @@ require_relative '../../app/lib/content_security_policy'
|
|||
|
||||
policy = ContentSecurityPolicy.new
|
||||
assets_host = policy.assets_host
|
||||
media_host = policy.media_host
|
||||
media_hosts = policy.media_hosts
|
||||
|
||||
def sso_host
|
||||
return unless ENV['ONE_CLICK_SSO_LOGIN'] == 'true'
|
||||
|
@ -35,9 +35,9 @@ Rails.application.config.content_security_policy do |p|
|
|||
p.default_src :none
|
||||
p.frame_ancestors :none
|
||||
p.font_src :self, assets_host
|
||||
p.img_src :self, :https, :data, :blob, assets_host
|
||||
p.img_src :self, :data, :blob, *media_hosts
|
||||
p.style_src :self, assets_host
|
||||
p.media_src :self, :https, :data, assets_host
|
||||
p.media_src :self, :data, *media_hosts
|
||||
p.frame_src :self, :https
|
||||
p.manifest_src :self, assets_host
|
||||
|
||||
|
@ -54,10 +54,10 @@ Rails.application.config.content_security_policy do |p|
|
|||
webpacker_public_host = ENV.fetch('WEBPACKER_DEV_SERVER_PUBLIC', Webpacker.config.dev_server[:public])
|
||||
webpacker_urls = %w(ws http).map { |protocol| "#{protocol}#{Webpacker.dev_server.https? ? 's' : ''}://#{webpacker_public_host}" }
|
||||
|
||||
p.connect_src :self, :data, :blob, assets_host, media_host, Rails.configuration.x.streaming_api_base_url, *webpacker_urls
|
||||
p.connect_src :self, :data, :blob, *media_hosts, Rails.configuration.x.streaming_api_base_url, *webpacker_urls
|
||||
p.script_src :self, :unsafe_inline, :unsafe_eval, assets_host
|
||||
else
|
||||
p.connect_src :self, :data, :blob, assets_host, media_host, Rails.configuration.x.streaming_api_base_url
|
||||
p.connect_src :self, :data, :blob, *media_hosts, Rails.configuration.x.streaming_api_base_url
|
||||
p.script_src :self, assets_host, "'wasm-unsafe-eval'"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,21 +13,22 @@
|
|||
# end
|
||||
|
||||
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
||||
inflect.acronym 'StatsD'
|
||||
inflect.acronym 'OEmbed'
|
||||
inflect.acronym 'OStatus'
|
||||
inflect.acronym 'ActivityPub'
|
||||
inflect.acronym 'PubSubHubbub'
|
||||
inflect.acronym 'ActivityStreams'
|
||||
inflect.acronym 'JsonLd'
|
||||
inflect.acronym 'Ed25519'
|
||||
inflect.acronym 'TOC'
|
||||
inflect.acronym 'RSS'
|
||||
inflect.acronym 'REST'
|
||||
inflect.acronym 'URL'
|
||||
inflect.acronym 'ASCII'
|
||||
inflect.acronym 'CLI'
|
||||
inflect.acronym 'DeepL'
|
||||
inflect.acronym 'DSL'
|
||||
inflect.acronym 'Ed25519'
|
||||
inflect.acronym 'JsonLd'
|
||||
inflect.acronym 'OEmbed'
|
||||
inflect.acronym 'OStatus'
|
||||
inflect.acronym 'PubSubHubbub'
|
||||
inflect.acronym 'REST'
|
||||
inflect.acronym 'RSS'
|
||||
inflect.acronym 'StatsD'
|
||||
inflect.acronym 'TOC'
|
||||
inflect.acronym 'URL'
|
||||
|
||||
inflect.singular 'data', 'data'
|
||||
end
|
||||
|
|
|
@ -34,6 +34,26 @@ RSpec.describe Api::V2::SearchController do
|
|||
expect(body_as_json[:accounts].pluck(:id)).to contain_exactly(bob.id.to_s, ana.id.to_s, tom.id.to_s)
|
||||
end
|
||||
|
||||
context 'with truthy `resolve`' do
|
||||
let(:params) { { q: 'test1', resolve: '1' } }
|
||||
|
||||
it 'returns http unauthorized' do
|
||||
get :index, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with `offset`' do
|
||||
let(:params) { { q: 'test1', offset: 1 } }
|
||||
|
||||
it 'returns http unauthorized' do
|
||||
get :index, params: params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with following=true' do
|
||||
let(:params) { { q: 'test', type: 'accounts', following: 'true' } }
|
||||
|
||||
|
@ -48,6 +68,26 @@ RSpec.describe Api::V2::SearchController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when search raises syntax error' do
|
||||
before { allow(Search).to receive(:new).and_raise(Mastodon::SyntaxError) }
|
||||
|
||||
it 'returns http unprocessable_entity' do
|
||||
get :index, params: params
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when search raises not found error' do
|
||||
before { allow(Search).to receive(:new).and_raise(ActiveRecord::RecordNotFound) }
|
||||
|
||||
it 'returns http not_found' do
|
||||
get :index, params: params
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -59,6 +99,12 @@ RSpec.describe Api::V2::SearchController do
|
|||
get :index, params: search_params
|
||||
end
|
||||
|
||||
context 'without a `q` param' do
|
||||
it 'returns http bad_request' do
|
||||
expect(response).to have_http_status(400)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a `q` shorter than 5 characters' do
|
||||
let(:search_params) { { q: 'test' } }
|
||||
|
||||
|
@ -79,6 +125,7 @@ RSpec.describe Api::V2::SearchController do
|
|||
|
||||
it 'returns http unauthorized' do
|
||||
expect(response).to have_http_status(401)
|
||||
expect(response.body).to match('resolve remote resources')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -87,6 +134,7 @@ RSpec.describe Api::V2::SearchController do
|
|||
|
||||
it 'returns http unauthorized' do
|
||||
expect(response).to have_http_status(401)
|
||||
expect(response.body).to match('pagination is not supported')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
require 'rails_helper'
|
||||
|
||||
describe RateLimitHeaders do
|
||||
describe Api::RateLimitHeaders do
|
||||
controller(ApplicationController) do
|
||||
include RateLimitHeaders
|
||||
include Api::RateLimitHeaders
|
||||
|
||||
def show
|
||||
head 200
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
require 'rails_helper'
|
||||
|
||||
describe ExportControllerConcern do
|
||||
describe Settings::ExportControllerConcern do
|
||||
controller(ApplicationController) do
|
||||
include ExportControllerConcern
|
||||
include Settings::ExportControllerConcern
|
||||
|
||||
def index
|
||||
send_export_file
|
|
@ -20,37 +20,30 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do
|
|||
[true, false].each do |with_otp_secret|
|
||||
let(:user) { Fabricate(:user, email: 'local-part@domain', otp_secret: with_otp_secret ? 'oldotpsecret' : nil) }
|
||||
|
||||
describe 'GET #new' do
|
||||
context 'when signed in and a new otp secret has been set in the session' do
|
||||
subject do
|
||||
sign_in user, scope: :user
|
||||
get :new, session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' }
|
||||
context 'when signed in' do
|
||||
before { sign_in user, scope: :user }
|
||||
|
||||
describe 'GET #new' do
|
||||
context 'when a new otp secret has been set in the session' do
|
||||
subject do
|
||||
get :new, session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' }
|
||||
end
|
||||
|
||||
include_examples 'renders :new'
|
||||
end
|
||||
|
||||
include_examples 'renders :new'
|
||||
end
|
||||
it 'redirects if a new otp_secret has not been set in the session' do
|
||||
get :new, session: { challenge_passed_at: Time.now.utc }
|
||||
|
||||
it 'redirects if not signed in' do
|
||||
get :new
|
||||
expect(response).to redirect_to('/auth/sign_in')
|
||||
end
|
||||
|
||||
it 'redirects if a new otp_secret has not been set in the session' do
|
||||
sign_in user, scope: :user
|
||||
get :new, session: { challenge_passed_at: Time.now.utc }
|
||||
expect(response).to redirect_to('/settings/otp_authentication')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
context 'when signed in' do
|
||||
before do
|
||||
sign_in user, scope: :user
|
||||
expect(response).to redirect_to('/settings/otp_authentication')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
describe 'when form_two_factor_confirmation parameter is not provided' do
|
||||
it 'raises ActionController::ParameterMissing' do
|
||||
post :create, params: {}, session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' }
|
||||
|
||||
expect(response).to have_http_status(400)
|
||||
end
|
||||
end
|
||||
|
@ -58,69 +51,78 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do
|
|||
describe 'when creation succeeds' do
|
||||
let!(:otp_backup_codes) { user.generate_otp_backup_codes! }
|
||||
|
||||
it 'renders page with success' do
|
||||
before do
|
||||
prepare_user_otp_generation
|
||||
prepare_user_otp_consumption
|
||||
prepare_user_otp_consumption_response(true)
|
||||
allow(controller).to receive(:current_user).and_return(user)
|
||||
end
|
||||
|
||||
expect do
|
||||
post :create,
|
||||
params: { form_two_factor_confirmation: { otp_attempt: '123456' } },
|
||||
session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' }
|
||||
end.to change { user.reload.otp_secret }.to 'thisisasecretforthespecofnewview'
|
||||
it 'renders page with success' do
|
||||
expect { post_create_with_options }
|
||||
.to change { user.reload.otp_secret }.to 'thisisasecretforthespecofnewview'
|
||||
|
||||
expect(assigns(:recovery_codes)).to eq otp_backup_codes
|
||||
expect(flash[:notice]).to eq 'Two-factor authentication successfully enabled'
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response).to render_template('settings/two_factor_authentication/recovery_codes/index')
|
||||
end
|
||||
|
||||
def prepare_user_otp_generation
|
||||
allow(user)
|
||||
.to receive(:generate_otp_backup_codes!)
|
||||
.and_return(otp_backup_codes)
|
||||
end
|
||||
|
||||
def prepare_user_otp_consumption
|
||||
options = { otp_secret: 'thisisasecretforthespecofnewview' }
|
||||
allow(user)
|
||||
.to receive(:validate_and_consume_otp!)
|
||||
.with('123456', options)
|
||||
.and_return(true)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when creation fails' do
|
||||
subject do
|
||||
options = { otp_secret: 'thisisasecretforthespecofnewview' }
|
||||
allow(user)
|
||||
.to receive(:validate_and_consume_otp!)
|
||||
.with('123456', options)
|
||||
.and_return(false)
|
||||
allow(controller).to receive(:current_user).and_return(user)
|
||||
|
||||
expect do
|
||||
post :create,
|
||||
params: { form_two_factor_confirmation: { otp_attempt: '123456' } },
|
||||
session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' }
|
||||
end.to(not_change { user.reload.otp_secret })
|
||||
expect { post_create_with_options }
|
||||
.to(not_change { user.reload.otp_secret })
|
||||
end
|
||||
|
||||
it 'renders the new view' do
|
||||
before do
|
||||
prepare_user_otp_consumption_response(false)
|
||||
allow(controller).to receive(:current_user).and_return(user)
|
||||
end
|
||||
|
||||
it 'renders page with error message' do
|
||||
subject
|
||||
expect(response.body).to include 'The entered code was invalid! Are server time and device time correct?'
|
||||
end
|
||||
|
||||
include_examples 'renders :new'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not signed in' do
|
||||
it 'redirects if not signed in' do
|
||||
post :create, params: { form_two_factor_confirmation: { otp_attempt: '123456' } }
|
||||
expect(response).to redirect_to('/auth/sign_in')
|
||||
private
|
||||
|
||||
def post_create_with_options
|
||||
post :create,
|
||||
params: { form_two_factor_confirmation: { otp_attempt: '123456' } },
|
||||
session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' }
|
||||
end
|
||||
|
||||
def prepare_user_otp_generation
|
||||
allow(user)
|
||||
.to receive(:generate_otp_backup_codes!)
|
||||
.and_return(otp_backup_codes)
|
||||
end
|
||||
|
||||
def prepare_user_otp_consumption_response(result)
|
||||
options = { otp_secret: 'thisisasecretforthespecofnewview' }
|
||||
allow(user)
|
||||
.to receive(:validate_and_consume_otp!)
|
||||
.with('123456', options)
|
||||
.and_return(result)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not signed in' do
|
||||
it 'redirects on POST to create' do
|
||||
post :create, params: { form_two_factor_confirmation: { otp_attempt: '123456' } }
|
||||
|
||||
expect(response).to redirect_to('/auth/sign_in')
|
||||
end
|
||||
|
||||
it 'redirects on GET to new' do
|
||||
get :new
|
||||
|
||||
expect(response).to redirect_to('/auth/sign_in')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -59,10 +59,10 @@ describe ContentSecurityPolicy do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#media_host' do
|
||||
describe '#media_hosts' do
|
||||
context 'when there is no configured CDN' do
|
||||
it 'defaults to using the assets_host value' do
|
||||
expect(subject.media_host).to eq(subject.assets_host)
|
||||
expect(subject.media_hosts).to contain_exactly(subject.assets_host)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -74,7 +74,7 @@ describe ContentSecurityPolicy do
|
|||
end
|
||||
|
||||
it 'uses the s3 alias host value' do
|
||||
expect(subject.media_host).to eq 'https://asset-host.s3-alias.example'
|
||||
expect(subject.media_hosts).to contain_exactly(subject.assets_host, 'https://asset-host.s3-alias.example')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -86,7 +86,7 @@ describe ContentSecurityPolicy do
|
|||
end
|
||||
|
||||
it 'uses the s3 alias host value and preserves the path' do
|
||||
expect(subject.media_host).to eq 'https://asset-host.s3-alias.example/pathname/'
|
||||
expect(subject.media_hosts).to contain_exactly(subject.assets_host, 'https://asset-host.s3-alias.example/pathname/')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -98,7 +98,7 @@ describe ContentSecurityPolicy do
|
|||
end
|
||||
|
||||
it 'uses the s3 cloudfront host value' do
|
||||
expect(subject.media_host).to eq 'https://asset-host.s3-cloudfront.example'
|
||||
expect(subject.media_hosts).to contain_exactly(subject.assets_host, 'https://asset-host.s3-cloudfront.example')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -110,7 +110,7 @@ describe ContentSecurityPolicy do
|
|||
end
|
||||
|
||||
it 'uses the azure alias host value' do
|
||||
expect(subject.media_host).to eq 'https://asset-host.azure-alias.example'
|
||||
expect(subject.media_hosts).to contain_exactly(subject.assets_host, 'https://asset-host.azure-alias.example')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -122,7 +122,7 @@ describe ContentSecurityPolicy do
|
|||
end
|
||||
|
||||
it 'uses the s3 hostname host value' do
|
||||
expect(subject.media_host).to eq 'https://asset-host.s3.example'
|
||||
expect(subject.media_hosts).to contain_exactly(subject.assets_host, 'https://asset-host.s3.example')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,23 +2,22 @@
|
|||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AccountsController do
|
||||
render_views
|
||||
|
||||
describe 'Accounts show response' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
describe 'unapproved account check' do
|
||||
context 'with an unapproved account' do
|
||||
before { account.user.update(approved: false) }
|
||||
|
||||
it 'returns http not found' do
|
||||
%w(html json rss).each do |format|
|
||||
get :show, params: { username: account.username, format: format }
|
||||
get short_account_path(username: account.username), as: format
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'permanently suspended account check' do
|
||||
context 'with a permanently suspended account' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
|
@ -26,25 +25,26 @@ RSpec.describe AccountsController do
|
|||
|
||||
it 'returns http gone' do
|
||||
%w(html json rss).each do |format|
|
||||
get :show, params: { username: account.username, format: format }
|
||||
get short_account_path(username: account.username), as: format
|
||||
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'temporarily suspended account check' do
|
||||
context 'with a temporarily suspended account' do
|
||||
before { account.suspend! }
|
||||
|
||||
it 'returns appropriate http response code' do
|
||||
{ html: 403, json: 200, rss: 403 }.each do |format, code|
|
||||
get :show, params: { username: account.username, format: format }
|
||||
get short_account_path(username: account.username), as: format
|
||||
|
||||
expect(response).to have_http_status(code)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
describe 'GET to short username paths' do
|
||||
context 'with existing statuses' do
|
||||
let!(:status) { Fabricate(:status, account: account) }
|
||||
let!(:status_reply) { Fabricate(:status, account: account, thread: Fabricate(:status)) }
|
||||
|
@ -66,17 +66,17 @@ RSpec.describe AccountsController do
|
|||
|
||||
shared_examples 'common HTML response' do
|
||||
it 'returns a standard HTML response', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and render_template(:show)
|
||||
|
||||
expect(response.headers['Link'].to_s).to include ActivityPub::TagManager.instance.uri_for(account)
|
||||
|
||||
expect(response).to render_template(:show)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a normal account in an HTML request' do
|
||||
before do
|
||||
get :show, params: { username: account.username, format: format }
|
||||
get short_account_path(username: account.username), as: format
|
||||
end
|
||||
|
||||
it_behaves_like 'common HTML response'
|
||||
|
@ -84,8 +84,7 @@ RSpec.describe AccountsController do
|
|||
|
||||
context 'with replies' do
|
||||
before do
|
||||
allow(controller).to receive(:replies_requested?).and_return(true)
|
||||
get :show, params: { username: account.username, format: format }
|
||||
get short_account_with_replies_path(username: account.username), as: format
|
||||
end
|
||||
|
||||
it_behaves_like 'common HTML response'
|
||||
|
@ -93,8 +92,7 @@ RSpec.describe AccountsController do
|
|||
|
||||
context 'with media' do
|
||||
before do
|
||||
allow(controller).to receive(:media_requested?).and_return(true)
|
||||
get :show, params: { username: account.username, format: format }
|
||||
get short_account_media_path(username: account.username), as: format
|
||||
end
|
||||
|
||||
it_behaves_like 'common HTML response'
|
||||
|
@ -106,9 +104,8 @@ RSpec.describe AccountsController do
|
|||
let!(:status_tag) { Fabricate(:status, account: account) }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:tag_requested?).and_return(true)
|
||||
status_tag.tags << tag
|
||||
get :show, params: { username: account.username, format: format, tag: tag.to_param }
|
||||
get short_account_tag_path(username: account.username, tag: tag), as: format
|
||||
end
|
||||
|
||||
it_behaves_like 'common HTML response'
|
||||
|
@ -117,21 +114,25 @@ RSpec.describe AccountsController do
|
|||
|
||||
context 'with JSON' do
|
||||
let(:authorized_fetch_mode) { false }
|
||||
let(:format) { 'json' }
|
||||
let(:headers) { { 'ACCEPT' => 'application/json' } }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:authorized_fetch_mode?).and_return(authorized_fetch_mode)
|
||||
around do |example|
|
||||
ClimateControl.modify AUTHORIZED_FETCH: authorized_fetch_mode.to_s do
|
||||
example.run
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a normal account in a JSON request' do
|
||||
before do
|
||||
get :show, params: { username: account.username, format: format }
|
||||
get short_account_path(username: account.username), headers: headers
|
||||
end
|
||||
|
||||
it 'returns a JSON version of the account', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_attributes(
|
||||
media_type: eq('application/activity+json')
|
||||
)
|
||||
|
||||
expect(body_as_json).to include(:id, :type, :preferredUsername, :inbox, :publicKey, :name, :summary)
|
||||
end
|
||||
|
@ -152,13 +153,15 @@ RSpec.describe AccountsController do
|
|||
|
||||
before do
|
||||
sign_in(user)
|
||||
get :show, params: { username: account.username, format: format }
|
||||
get short_account_path(username: account.username), headers: headers.merge({ 'Cookie' => '123' })
|
||||
end
|
||||
|
||||
it 'returns a private JSON version of the account', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_attributes(
|
||||
media_type: eq('application/activity+json')
|
||||
)
|
||||
|
||||
expect(response.headers['Cache-Control']).to include 'private'
|
||||
|
||||
|
@ -170,14 +173,15 @@ RSpec.describe AccountsController do
|
|||
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:signed_request_actor).and_return(remote_account)
|
||||
get :show, params: { username: account.username, format: format }
|
||||
get short_account_path(username: account.username), headers: headers, sign_with: remote_account
|
||||
end
|
||||
|
||||
it 'returns a JSON version of the account', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_attributes(
|
||||
media_type: eq('application/activity+json')
|
||||
)
|
||||
|
||||
expect(body_as_json).to include(:id, :type, :preferredUsername, :inbox, :publicKey, :name, :summary)
|
||||
end
|
||||
|
@ -188,12 +192,13 @@ RSpec.describe AccountsController do
|
|||
let(:authorized_fetch_mode) { true }
|
||||
|
||||
it 'returns a private signature JSON version of the account', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_attributes(
|
||||
media_type: eq('application/activity+json')
|
||||
)
|
||||
|
||||
expect(response.headers['Cache-Control']).to include 'private'
|
||||
|
||||
expect(response.headers['Vary']).to include 'Signature'
|
||||
|
||||
expect(body_as_json).to include(:id, :type, :preferredUsername, :inbox, :publicKey, :name, :summary)
|
||||
|
@ -207,60 +212,58 @@ RSpec.describe AccountsController do
|
|||
|
||||
context 'with a normal account in an RSS request' do
|
||||
before do
|
||||
get :show, params: { username: account.username, format: format }
|
||||
get short_account_path(username: account.username, format: format)
|
||||
end
|
||||
|
||||
it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie'
|
||||
|
||||
it 'responds with correct statuses', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.body).to include_status_tag(status_media)
|
||||
expect(response.body).to include_status_tag(status_self_reply)
|
||||
expect(response.body).to include_status_tag(status)
|
||||
expect(response.body).to_not include_status_tag(status_direct)
|
||||
expect(response.body).to_not include_status_tag(status_private)
|
||||
expect(response.body).to_not include_status_tag(status_reblog.reblog)
|
||||
expect(response.body).to_not include_status_tag(status_reply)
|
||||
expect(response.body).to include(status_tag_for(status_media))
|
||||
expect(response.body).to include(status_tag_for(status_self_reply))
|
||||
expect(response.body).to include(status_tag_for(status))
|
||||
expect(response.body).to_not include(status_tag_for(status_direct))
|
||||
expect(response.body).to_not include(status_tag_for(status_private))
|
||||
expect(response.body).to_not include(status_tag_for(status_reblog.reblog))
|
||||
expect(response.body).to_not include(status_tag_for(status_reply))
|
||||
end
|
||||
end
|
||||
|
||||
context 'with replies' do
|
||||
before do
|
||||
allow(controller).to receive(:replies_requested?).and_return(true)
|
||||
get :show, params: { username: account.username, format: format }
|
||||
get short_account_with_replies_path(username: account.username, format: format)
|
||||
end
|
||||
|
||||
it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie'
|
||||
|
||||
it 'responds with correct statuses with replies', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.body).to include_status_tag(status_media)
|
||||
expect(response.body).to include_status_tag(status_reply)
|
||||
expect(response.body).to include_status_tag(status_self_reply)
|
||||
expect(response.body).to include_status_tag(status)
|
||||
expect(response.body).to_not include_status_tag(status_direct)
|
||||
expect(response.body).to_not include_status_tag(status_private)
|
||||
expect(response.body).to_not include_status_tag(status_reblog.reblog)
|
||||
expect(response.body).to include(status_tag_for(status_media))
|
||||
expect(response.body).to include(status_tag_for(status_reply))
|
||||
expect(response.body).to include(status_tag_for(status_self_reply))
|
||||
expect(response.body).to include(status_tag_for(status))
|
||||
expect(response.body).to_not include(status_tag_for(status_direct))
|
||||
expect(response.body).to_not include(status_tag_for(status_private))
|
||||
expect(response.body).to_not include(status_tag_for(status_reblog.reblog))
|
||||
end
|
||||
end
|
||||
|
||||
context 'with media' do
|
||||
before do
|
||||
allow(controller).to receive(:media_requested?).and_return(true)
|
||||
get :show, params: { username: account.username, format: format }
|
||||
get short_account_media_path(username: account.username, format: format)
|
||||
end
|
||||
|
||||
it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie'
|
||||
|
||||
it 'responds with correct statuses with media', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.body).to include_status_tag(status_media)
|
||||
expect(response.body).to_not include_status_tag(status_direct)
|
||||
expect(response.body).to_not include_status_tag(status_private)
|
||||
expect(response.body).to_not include_status_tag(status_reblog.reblog)
|
||||
expect(response.body).to_not include_status_tag(status_reply)
|
||||
expect(response.body).to_not include_status_tag(status_self_reply)
|
||||
expect(response.body).to_not include_status_tag(status)
|
||||
expect(response.body).to include(status_tag_for(status_media))
|
||||
expect(response.body).to_not include(status_tag_for(status_direct))
|
||||
expect(response.body).to_not include(status_tag_for(status_private))
|
||||
expect(response.body).to_not include(status_tag_for(status_reblog.reblog))
|
||||
expect(response.body).to_not include(status_tag_for(status_reply))
|
||||
expect(response.body).to_not include(status_tag_for(status_self_reply))
|
||||
expect(response.body).to_not include(status_tag_for(status))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -270,30 +273,29 @@ RSpec.describe AccountsController do
|
|||
let!(:status_tag) { Fabricate(:status, account: account) }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:tag_requested?).and_return(true)
|
||||
status_tag.tags << tag
|
||||
get :show, params: { username: account.username, format: format, tag: tag.to_param }
|
||||
get short_account_tag_path(username: account.username, tag: tag, format: format)
|
||||
end
|
||||
|
||||
it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie'
|
||||
|
||||
it 'responds with correct statuses with a tag', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.body).to include_status_tag(status_tag)
|
||||
expect(response.body).to_not include_status_tag(status_direct)
|
||||
expect(response.body).to_not include_status_tag(status_media)
|
||||
expect(response.body).to_not include_status_tag(status_private)
|
||||
expect(response.body).to_not include_status_tag(status_reblog.reblog)
|
||||
expect(response.body).to_not include_status_tag(status_reply)
|
||||
expect(response.body).to_not include_status_tag(status_self_reply)
|
||||
expect(response.body).to_not include_status_tag(status)
|
||||
expect(response.body).to include(status_tag_for(status_tag))
|
||||
expect(response.body).to_not include(status_tag_for(status_direct))
|
||||
expect(response.body).to_not include(status_tag_for(status_media))
|
||||
expect(response.body).to_not include(status_tag_for(status_private))
|
||||
expect(response.body).to_not include(status_tag_for(status_reblog.reblog))
|
||||
expect(response.body).to_not include(status_tag_for(status_reply))
|
||||
expect(response.body).to_not include(status_tag_for(status_self_reply))
|
||||
expect(response.body).to_not include(status_tag_for(status))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def include_status_tag(status)
|
||||
include ActivityPub::TagManager.instance.url_for(status)
|
||||
def status_tag_for(status)
|
||||
ActivityPub::TagManager.instance.url_for(status)
|
||||
end
|
||||
end
|
|
@ -12,15 +12,15 @@ describe 'Content-Security-Policy' do
|
|||
"default-src 'none'",
|
||||
"frame-ancestors 'none'",
|
||||
"font-src 'self' https://cb6e6126.ngrok.io",
|
||||
"img-src 'self' https: data: blob: https://cb6e6126.ngrok.io",
|
||||
"img-src 'self' data: blob: https://cb6e6126.ngrok.io",
|
||||
"style-src 'self' https://cb6e6126.ngrok.io 'nonce-ZbA+JmE7+bK8F5qvADZHuQ=='",
|
||||
"media-src 'self' https: data: https://cb6e6126.ngrok.io",
|
||||
"media-src 'self' data: https://cb6e6126.ngrok.io",
|
||||
"frame-src 'self' https:",
|
||||
"manifest-src 'self' https://cb6e6126.ngrok.io",
|
||||
"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 https://cb6e6126.ngrok.io ws://localhost:4000",
|
||||
"connect-src 'self' data: blob: https://cb6e6126.ngrok.io ws://localhost:4000",
|
||||
"script-src 'self' https://cb6e6126.ngrok.io 'wasm-unsafe-eval'"
|
||||
)
|
||||
end
|
||||
|
|
|
@ -384,7 +384,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do
|
|||
end
|
||||
|
||||
it 'updates the existing media attachment in-place' do
|
||||
media_attachment = status.media_attachments.reload.first
|
||||
media_attachment = status.media_attachments.ordered.reload.first
|
||||
|
||||
expect(media_attachment).to_not be_nil
|
||||
expect(media_attachment.remote_url).to eq 'https://example.com/foo.png'
|
||||
|
|
14
yarn.lock
14
yarn.lock
|
@ -1483,11 +1483,11 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.3, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
|
||||
version: 7.23.4
|
||||
resolution: "@babel/runtime@npm:7.23.4"
|
||||
version: 7.23.5
|
||||
resolution: "@babel/runtime@npm:7.23.5"
|
||||
dependencies:
|
||||
regenerator-runtime: "npm:^0.14.0"
|
||||
checksum: db2bf183cd0119599b903ca51ca0aeea8e0ab478a16be1aae10dd90473ed614159d3e5adfdd8f8f3d840402428ce0d90b5c01aae95da9e45a2dd83e02d85ca27
|
||||
checksum: ca679cc91bb7e424bc2db87bb58cc3b06ade916b9adb21fbbdc43e54cdaacb3eea201ceba2a0464b11d2eb65b9fe6a6ffcf4d7521fa52994f19be96f1af14788
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -10640,8 +10640,8 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"jsdom@npm:^23.0.0":
|
||||
version: 23.0.0
|
||||
resolution: "jsdom@npm:23.0.0"
|
||||
version: 23.0.1
|
||||
resolution: "jsdom@npm:23.0.1"
|
||||
dependencies:
|
||||
cssstyle: "npm:^3.0.0"
|
||||
data-urls: "npm:^5.0.0"
|
||||
|
@ -10665,11 +10665,11 @@ __metadata:
|
|||
ws: "npm:^8.14.2"
|
||||
xml-name-validator: "npm:^5.0.0"
|
||||
peerDependencies:
|
||||
canvas: ^3.0.0
|
||||
canvas: ^2.11.2
|
||||
peerDependenciesMeta:
|
||||
canvas:
|
||||
optional: true
|
||||
checksum: 2c876a02de49e0ed6b667a4eb9b08b8e76ac189a5571ff97791cc9564e713259314deea6d657cc7f59fc30af41b900e7d833c95017e576dfcaf25f32565722af
|
||||
checksum: 13b2b3693ccb40215d1cce77bac7a295414ee4c0a06e30167f8087c9867145ba23dbd592bd95a801cadd7b3698bfd20b9c3f2c26fd8422607f22609ed2e404ef
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
Loading…
Reference in a new issue