M .rubocop_todo.yml => .rubocop_todo.yml +0 -3
@@ 1224,9 1224,6 @@ Rails/ActiveRecordCallbacksOrder:
Rails/ApplicationController:
Exclude:
- 'app/controllers/health_controller.rb'
- - 'app/controllers/well_known/host_meta_controller.rb'
- - 'app/controllers/well_known/nodeinfo_controller.rb'
- - 'app/controllers/well_known/webfinger_controller.rb'
# Configuration parameters: Database, Include.
# SupportedDatabases: mysql, postgresql
M app/controllers/accounts_controller.rb => app/controllers/accounts_controller.rb +2 -1
@@ 7,8 7,9 @@ class AccountsController < ApplicationController
include AccountControllerConcern
include SignatureAuthentication
+ vary_by -> { public_fetch_mode? ? 'Accept' : 'Accept, Signature' }
+
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
- before_action :set_cache_headers
skip_around_action :set_locale, if: -> { [:json, :rss].include?(request.format&.to_sym) }
skip_before_action :require_functional!, unless: :whitelist_mode?
M app/controllers/activitypub/base_controller.rb => app/controllers/activitypub/base_controller.rb +0 -4
@@ 7,10 7,6 @@ class ActivityPub::BaseController < Api::BaseController
private
- def set_cache_headers
- response.headers['Vary'] = 'Signature' if authorized_fetch_mode?
- end
-
def skip_temporary_suspension_response?
false
end
M app/controllers/activitypub/collections_controller.rb => app/controllers/activitypub/collections_controller.rb +2 -1
@@ 4,11 4,12 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
include SignatureVerification
include AccountOwnedConcern
+ vary_by -> { 'Signature' if authorized_fetch_mode? }
+
before_action :require_account_signature!, if: :authorized_fetch_mode?
before_action :set_items
before_action :set_size
before_action :set_type
- before_action :set_cache_headers
def show
expires_in 3.minutes, public: public_fetch_mode?
M app/controllers/activitypub/followers_synchronizations_controller.rb => app/controllers/activitypub/followers_synchronizations_controller.rb +2 -1
@@ 4,9 4,10 @@ class ActivityPub::FollowersSynchronizationsController < ActivityPub::BaseContro
include SignatureVerification
include AccountOwnedConcern
+ vary_by -> { 'Signature' if authorized_fetch_mode? }
+
before_action :require_account_signature!
before_action :set_items
- before_action :set_cache_headers
def show
expires_in 0, public: false
M app/controllers/activitypub/outboxes_controller.rb => app/controllers/activitypub/outboxes_controller.rb +3 -5
@@ 6,9 6,10 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
include SignatureVerification
include AccountOwnedConcern
+ vary_by -> { 'Signature' if authorized_fetch_mode? || page_requested? }
+
before_action :require_account_signature!, if: :authorized_fetch_mode?
before_action :set_statuses
- before_action :set_cache_headers
def show
if page_requested?
@@ 16,6 17,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
else
expires_in(3.minutes, public: public_fetch_mode?)
end
+
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
end
@@ 80,8 82,4 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
def set_account
@account = params[:account_username].present? ? Account.find_local!(username_param) : Account.representative
end
-
- def set_cache_headers
- response.headers['Vary'] = 'Signature' if authorized_fetch_mode? || page_requested?
- end
end
M app/controllers/activitypub/replies_controller.rb => app/controllers/activitypub/replies_controller.rb +2 -1
@@ 7,9 7,10 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
DESCENDANTS_LIMIT = 60
+ vary_by -> { 'Signature' if authorized_fetch_mode? }
+
before_action :require_account_signature!, if: :authorized_fetch_mode?
before_action :set_status
- before_action :set_cache_headers
before_action :set_replies
def index
M app/controllers/admin/base_controller.rb => app/controllers/admin/base_controller.rb +6 -0
@@ 8,6 8,8 @@ module Admin
layout 'admin'
before_action :set_body_classes
+ before_action :set_cache_headers
+
after_action :verify_authorized
private
@@ 16,6 18,10 @@ module Admin
@body_classes = 'admin'
end
+ def set_cache_headers
+ response.cache_control.replace(private: true, no_store: true)
+ end
+
def set_user
@user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
end
M app/controllers/api/base_controller.rb => app/controllers/api/base_controller.rb +3 -3
@@ 12,7 12,7 @@ class Api::BaseController < ApplicationController
before_action :require_authenticated_user!, if: :disallow_unauthenticated_api_access?
before_action :require_not_suspended!
- before_action :set_cache_headers
+ before_action :set_cache_control_defaults
protect_from_forgery with: :null_session
@@ 148,8 148,8 @@ class Api::BaseController < ApplicationController
doorkeeper_authorize!(*scopes) if doorkeeper_token
end
- def set_cache_headers
- response.headers['Cache-Control'] = 'private, no-store'
+ def set_cache_control_defaults
+ response.cache_control.replace(private: true, no_store: true)
end
def disallow_unauthenticated_api_access?
M app/controllers/api/v1/custom_emojis_controller.rb => app/controllers/api/v1/custom_emojis_controller.rb +0 -2
@@ 1,8 1,6 @@
# frozen_string_literal: true
class Api::V1::CustomEmojisController < Api::BaseController
- skip_before_action :set_cache_headers
-
def index
expires_in 3.minutes, public: true
render_with_cache(each_serializer: REST::CustomEmojiSerializer) { CustomEmoji.listed.includes(:category) }
M app/controllers/api/v1/instances/activity_controller.rb => app/controllers/api/v1/instances/activity_controller.rb +0 -1
@@ 3,7 3,6 @@
class Api::V1::Instances::ActivityController < Api::BaseController
before_action :require_enabled_api!
- skip_before_action :set_cache_headers
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
def show
M app/controllers/api/v1/instances/peers_controller.rb => app/controllers/api/v1/instances/peers_controller.rb +0 -1
@@ 3,7 3,6 @@
class Api::V1::Instances::PeersController < Api::BaseController
before_action :require_enabled_api!
- skip_before_action :set_cache_headers
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
def index
M app/controllers/api/v1/instances_controller.rb => app/controllers/api/v1/instances_controller.rb +0 -1
@@ 1,7 1,6 @@
# frozen_string_literal: true
class Api::V1::InstancesController < Api::BaseController
- skip_before_action :set_cache_headers
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
def show
M app/controllers/auth/registrations_controller.rb => app/controllers/auth/registrations_controller.rb +1 -1
@@ 152,6 152,6 @@ class Auth::RegistrationsController < Devise::RegistrationsController
end
def set_cache_headers
- response.headers['Cache-Control'] = 'private, no-store'
+ response.cache_control.replace(private: true, no_store: true)
end
end
M app/controllers/concerns/cache_concern.rb => app/controllers/concerns/cache_concern.rb +9 -5
@@ 155,8 155,16 @@ module CacheConcern
end
end
+ class_methods do
+ def vary_by(value)
+ before_action do |controller|
+ response.headers['Vary'] = value.respond_to?(:call) ? controller.instance_exec(&value) : value
+ end
+ end
+ end
+
def render_with_cache(**options)
- raise ArgumentError, 'only JSON render calls are supported' unless options.key?(:json) || block_given?
+ raise ArgumentError, 'Only JSON render calls are supported' unless options.key?(:json) || block_given?
key = options.delete(:key) || [[params[:controller], params[:action]].join('/'), options[:json].respond_to?(:cache_key) ? options[:json].cache_key : nil, options[:fields].nil? ? nil : options[:fields].join(',')].compact.join(':')
expires_in = options.delete(:expires_in) || 3.minutes
@@ 176,10 184,6 @@ module CacheConcern
end
end
- def set_cache_headers
- response.headers['Vary'] = public_fetch_mode? ? 'Accept' : 'Accept, Signature'
- end
-
def cache_collection(raw, klass)
return raw unless klass.respond_to?(:with_includes)
M app/controllers/custom_css_controller.rb => app/controllers/custom_css_controller.rb +1 -11
@@ 1,18 1,8 @@
# frozen_string_literal: true
-class CustomCssController < ApplicationController
- skip_before_action :store_current_location
- skip_before_action :require_functional!
- skip_before_action :update_user_sign_in
- skip_before_action :set_session_activity
-
- skip_around_action :set_locale
-
- before_action :set_cache_headers
-
+class CustomCssController < ActionController::Base # rubocop:disable Rails/ApplicationController
def show
expires_in 3.minutes, public: true
- request.session_options[:skip] = true
render content_type: 'text/css'
end
end
M app/controllers/disputes/base_controller.rb => app/controllers/disputes/base_controller.rb +5 -0
@@ 9,10 9,15 @@ class Disputes::BaseController < ApplicationController
before_action :set_body_classes
before_action :authenticate_user!
+ before_action :set_cache_headers
private
def set_body_classes
@body_classes = 'admin'
end
+
+ def set_cache_headers
+ response.cache_control.replace(private: true, no_store: true)
+ end
end
M app/controllers/emojis_controller.rb => app/controllers/emojis_controller.rb +4 -7
@@ 2,15 2,12 @@
class EmojisController < ApplicationController
before_action :set_emoji
- before_action :set_cache_headers
+
+ vary_by -> { 'Signature' if authorized_fetch_mode? }
def show
- respond_to do |format|
- format.json do
- expires_in 3.minutes, public: true
- render_with_cache json: @emoji, content_type: 'application/activity+json', serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter
- end
- end
+ expires_in 3.minutes, public: true
+ render_with_cache json: @emoji, content_type: 'application/activity+json', serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter
end
private
M app/controllers/filters/statuses_controller.rb => app/controllers/filters/statuses_controller.rb +5 -0
@@ 7,6 7,7 @@ class Filters::StatusesController < ApplicationController
before_action :set_filter
before_action :set_status_filters
before_action :set_body_classes
+ before_action :set_cache_headers
PER_PAGE = 20
@@ 44,4 45,8 @@ class Filters::StatusesController < ApplicationController
def set_body_classes
@body_classes = 'admin'
end
+
+ def set_cache_headers
+ response.cache_control.replace(private: true, no_store: true)
+ end
end
M app/controllers/filters_controller.rb => app/controllers/filters_controller.rb +5 -0
@@ 6,6 6,7 @@ class FiltersController < ApplicationController
before_action :authenticate_user!
before_action :set_filter, only: [:edit, :update, :destroy]
before_action :set_body_classes
+ before_action :set_cache_headers
def index
@filters = current_account.custom_filters.includes(:keywords, :statuses).order(:phrase)
@@ 54,4 55,8 @@ class FiltersController < ApplicationController
def set_body_classes
@body_classes = 'admin'
end
+
+ def set_cache_headers
+ response.cache_control.replace(private: true, no_store: true)
+ end
end
M app/controllers/follower_accounts_controller.rb => app/controllers/follower_accounts_controller.rb +2 -1
@@ 5,8 5,9 @@ class FollowerAccountsController < ApplicationController
include SignatureVerification
include WebAppControllerConcern
+ vary_by -> { public_fetch_mode? ? 'Accept' : 'Accept, Signature' }
+
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
- before_action :set_cache_headers
skip_around_action :set_locale, if: -> { request.format == :json }
skip_before_action :require_functional!, unless: :whitelist_mode?
M app/controllers/following_accounts_controller.rb => app/controllers/following_accounts_controller.rb +2 -1
@@ 5,8 5,9 @@ class FollowingAccountsController < ApplicationController
include SignatureVerification
include WebAppControllerConcern
+ vary_by -> { public_fetch_mode? ? 'Accept' : 'Accept, Signature' }
+
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
- before_action :set_cache_headers
skip_around_action :set_locale, if: -> { request.format == :json }
skip_before_action :require_functional!, unless: :whitelist_mode?
M app/controllers/invites_controller.rb => app/controllers/invites_controller.rb +5 -0
@@ 7,6 7,7 @@ class InvitesController < ApplicationController
before_action :authenticate_user!
before_action :set_body_classes
+ before_action :set_cache_headers
def index
authorize :invite, :create?
@@ 49,4 50,8 @@ class InvitesController < ApplicationController
def set_body_classes
@body_classes = 'admin'
end
+
+ def set_cache_headers
+ response.cache_control.replace(private: true, no_store: true)
+ end
end
M app/controllers/manifests_controller.rb => app/controllers/manifests_controller.rb +1 -4
@@ 1,9 1,6 @@
# frozen_string_literal: true
-class ManifestsController < ApplicationController
- skip_before_action :store_current_location
- skip_before_action :require_functional!
-
+class ManifestsController < ActionController::Base # rubocop:disable Rails/ApplicationController
def show
expires_in 3.minutes, public: true
render json: InstancePresenter.new, serializer: ManifestSerializer, root: 'instance'
M app/controllers/oauth/authorizations_controller.rb => app/controllers/oauth/authorizations_controller.rb +1 -1
@@ 34,6 34,6 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
end
def set_cache_headers
- response.headers['Cache-Control'] = 'private, no-store'
+ response.cache_control.replace(private: true, no_store: true)
end
end
M app/controllers/oauth/authorized_applications_controller.rb => app/controllers/oauth/authorized_applications_controller.rb +5 -0
@@ 7,6 7,7 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
before_action :authenticate_resource_owner!
before_action :require_not_suspended!, only: :destroy
before_action :set_body_classes
+ before_action :set_cache_headers
skip_before_action :require_functional!
@@ 30,4 31,8 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
def require_not_suspended!
forbidden if current_account.suspended?
end
+
+ def set_cache_headers
+ response.cache_control.replace(private: true, no_store: true)
+ end
end
M app/controllers/relationships_controller.rb => app/controllers/relationships_controller.rb +5 -0
@@ 7,6 7,7 @@ class RelationshipsController < ApplicationController
before_action :set_accounts, only: :show
before_action :set_relationships, only: :show
before_action :set_body_classes
+ before_action :set_cache_headers
helper_method :following_relationship?, :followed_by_relationship?, :mutual_relationship?
@@ 70,4 71,8 @@ class RelationshipsController < ApplicationController
def set_body_classes
@body_classes = 'admin'
end
+
+ def set_cache_headers
+ response.cache_control.replace(private: true, no_store: true)
+ end
end
M app/controllers/settings/base_controller.rb => app/controllers/settings/base_controller.rb +1 -1
@@ 14,7 14,7 @@ class Settings::BaseController < ApplicationController
end
def set_cache_headers
- response.headers['Cache-Control'] = 'private, no-store'
+ response.cache_control.replace(private: true, no_store: true)
end
def require_not_suspended!
M app/controllers/statuses_cleanup_controller.rb => app/controllers/statuses_cleanup_controller.rb +5 -0
@@ 6,6 6,7 @@ class StatusesCleanupController < ApplicationController
before_action :authenticate_user!
before_action :set_policy
before_action :set_body_classes
+ before_action :set_cache_headers
def show; end
@@ 36,4 37,8 @@ class StatusesCleanupController < ApplicationController
def set_body_classes
@body_classes = 'admin'
end
+
+ def set_cache_headers
+ response.cache_control.replace(private: true, no_store: true)
+ end
end
M app/controllers/statuses_controller.rb => app/controllers/statuses_controller.rb +2 -1
@@ 6,11 6,12 @@ class StatusesController < ApplicationController
include Authorization
include AccountOwnedConcern
+ vary_by -> { public_fetch_mode? ? 'Accept' : 'Accept, Signature' }
+
before_action :require_account_signature!, only: [:show, :activity], if: -> { request.format == :json && authorized_fetch_mode? }
before_action :set_status
before_action :set_instance_presenter
before_action :redirect_to_original, only: :show
- before_action :set_cache_headers
before_action :set_body_classes, only: :embed
after_action :set_link_headers
M app/controllers/tags_controller.rb => app/controllers/tags_controller.rb +2 -0
@@ 7,6 7,8 @@ class TagsController < ApplicationController
PAGE_SIZE = 20
PAGE_SIZE_MAX = 200
+ vary_by -> { public_fetch_mode? ? 'Accept' : 'Accept, Signature' }
+
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
before_action :authenticate_user!, if: :whitelist_mode?
before_action :set_local
M app/controllers/well_known/host_meta_controller.rb => app/controllers/well_known/host_meta_controller.rb +1 -3
@@ 1,11 1,9 @@
# frozen_string_literal: true
module WellKnown
- class HostMetaController < ActionController::Base
+ class HostMetaController < ActionController::Base # rubocop:disable Rails/ApplicationController
include RoutingHelper
- before_action { response.headers['Vary'] = 'Accept' }
-
def show
@webfinger_template = "#{webfinger_url}?resource={uri}"
expires_in 3.days, public: true
M app/controllers/well_known/nodeinfo_controller.rb => app/controllers/well_known/nodeinfo_controller.rb +1 -3
@@ 1,11 1,9 @@
# frozen_string_literal: true
module WellKnown
- class NodeInfoController < ActionController::Base
+ class NodeInfoController < ActionController::Base # rubocop:disable Rails/ApplicationController
include CacheConcern
- before_action { response.headers['Vary'] = 'Accept' }
-
def index
expires_in 3.days, public: true
render_with_cache json: {}, serializer: NodeInfo::DiscoverySerializer, adapter: NodeInfo::Adapter, expires_in: 3.days, root: 'nodeinfo'
M app/controllers/well_known/webfinger_controller.rb => app/controllers/well_known/webfinger_controller.rb +7 -6
@@ 1,7 1,7 @@
# frozen_string_literal: true
module WellKnown
- class WebfingerController < ActionController::Base
+ class WebfingerController < ActionController::Base # rubocop:disable Rails/ApplicationController
include RoutingHelper
before_action :set_account
@@ 34,7 34,12 @@ module WellKnown
end
def check_account_suspension
- expires_in(3.minutes, public: true) && gone if @account.suspended_permanently?
+ gone if @account.suspended_permanently?
+ end
+
+ def gone
+ expires_in(3.minutes, public: true)
+ head 410
end
def bad_request
@@ 46,9 51,5 @@ module WellKnown
expires_in(3.minutes, public: true)
head 404
end
-
- def gone
- head 410
- end
end
end
M config/application.rb => config/application.rb +1 -0
@@ 43,6 43,7 @@ require_relative '../lib/chewy/strategy/bypass_with_warning'
require_relative '../lib/webpacker/manifest_extensions'
require_relative '../lib/webpacker/helper_extensions'
require_relative '../lib/rails/engine_extensions'
+require_relative '../lib/action_controller/conditional_get_extensions'
require_relative '../lib/active_record/database_tasks_extensions'
require_relative '../lib/active_record/batches'
require_relative '../lib/simple_navigation/item_extensions'
A lib/action_controller/conditional_get_extensions.rb => lib/action_controller/conditional_get_extensions.rb +15 -0
@@ 0,0 1,15 @@
+# frozen_string_literal: true
+
+module ActionController
+ module ConditionalGetExtensions
+ def expires_in(*)
+ # This backports a fix from Rails 7 so that a more private Cache-Control
+ # can be overriden by calling expires_in on a specific controller action
+ response.cache_control.delete(:no_store)
+
+ super
+ end
+ end
+end
+
+ActionController::ConditionalGet.prepend(ActionController::ConditionalGetExtensions)
M spec/controllers/accounts_controller_spec.rb => spec/controllers/accounts_controller_spec.rb +4 -0
@@ 17,6 17,10 @@ RSpec.describe AccountsController, type: :controller do
expect(session).to be_empty
end
+ it 'returns Vary header' do
+ expect(response.headers['Vary']).to include 'Accept'
+ end
+
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
M spec/controllers/admin/base_controller_spec.rb => spec/controllers/admin/base_controller_spec.rb +8 -0
@@ 18,6 18,14 @@ describe Admin::BaseController, type: :controller do
expect(response).to have_http_status(403)
end
+ it 'returns private cache control headers' do
+ routes.draw { get 'success' => 'admin/base#success' }
+ sign_in(Fabricate(:user, role: UserRole.find_by(name: 'Moderator')))
+ get :success
+
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
+
it 'renders admin layout as a moderator' do
routes.draw { get 'success' => 'admin/base#success' }
sign_in(Fabricate(:user, role: UserRole.find_by(name: 'Moderator')))
M spec/controllers/api/base_controller_spec.rb => spec/controllers/api/base_controller_spec.rb +6 -0
@@ 15,6 15,12 @@ describe Api::BaseController do
end
end
+ it 'returns private cache control headers by default' do
+ routes.draw { get 'success' => 'api/base#success' }
+ get :success
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
+
describe 'forgery protection' do
before do
routes.draw { post 'success' => 'api/base#success' }
M spec/controllers/api/oembed_controller_spec.rb => spec/controllers/api/oembed_controller_spec.rb +4 -0
@@ 17,5 17,9 @@ RSpec.describe Api::OEmbedController, type: :controller do
it 'returns http success' do
expect(response).to have_http_status(200)
end
+
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
end
M spec/controllers/auth/registrations_controller_spec.rb => spec/controllers/auth/registrations_controller_spec.rb +21 -6
@@ 33,27 33,42 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
end
describe 'GET #edit' do
- it 'returns http success' do
+ before do
request.env['devise.mapping'] = Devise.mappings[:user]
sign_in(Fabricate(:user))
get :edit
+ end
+
+ it 'returns http success' do
expect(response).to have_http_status(200)
end
+
+ it 'returns private cache control header' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
describe 'GET #update' do
- it 'returns http success' do
+ let(:user) { Fabricate(:user) }
+
+ before do
request.env['devise.mapping'] = Devise.mappings[:user]
- sign_in(Fabricate(:user), scope: :user)
+ sign_in(user, scope: :user)
post :update
+ end
+
+ it 'returns http success' do
expect(response).to have_http_status(200)
end
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
+
context 'when suspended' do
+ let(:user) { Fabricate(:user, account_attributes: { username: 'test', suspended_at: Time.now.utc }) }
+
it 'returns http forbidden' do
- request.env['devise.mapping'] = Devise.mappings[:user]
- sign_in(Fabricate(:user, account_attributes: { username: 'test', suspended_at: Time.now.utc }), scope: :user)
- post :update
expect(response).to have_http_status(403)
end
end
M spec/controllers/custom_css_controller_spec.rb => spec/controllers/custom_css_controller_spec.rb +17 -1
@@ 6,9 6,25 @@ describe CustomCssController do
render_views
describe 'GET #show' do
- it 'returns http success' do
+ before do
get :show
+ end
+
+ it 'returns http success' do
expect(response).to have_http_status(200)
end
+
+ it 'returns public cache control header' do
+ expect(response.headers['Cache-Control']).to include('public')
+ end
+
+ it 'does not set cookies' do
+ expect(response.cookies).to be_empty
+ expect(response.headers['Set-Cookies']).to be_nil
+ end
+
+ it 'does not set sessions' do
+ expect(session).to be_empty
+ end
end
end
M spec/controllers/filters/statuses_controller_spec.rb => spec/controllers/filters/statuses_controller_spec.rb +12 -6
@@ 18,21 18,27 @@ describe Filters::StatusesController do
context 'with a signed in user' do
context 'with the filter user signed in' do
- before { sign_in(filter.account.user) }
-
- it 'returns http success' do
+ before do
+ sign_in(filter.account.user)
get :index, params: { filter_id: filter }
+ end
+ it 'returns http success' do
expect(response).to have_http_status(200)
end
+
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
context 'with another user signed in' do
- before { sign_in(Fabricate(:user)) }
-
- it 'returns http not found' do
+ before do
+ sign_in(Fabricate(:user))
get :index, params: { filter_id: filter }
+ end
+ it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
M spec/controllers/filters_controller_spec.rb => spec/controllers/filters_controller_spec.rb +11 -4
@@ 7,21 7,28 @@ describe FiltersController do
describe 'GET #index' do
context 'with signed out user' do
- it 'redirects' do
+ before do
get :index
+ end
+ it 'redirects' do
expect(response).to be_redirect
end
end
context 'with a signed in user' do
- before { sign_in(Fabricate(:user)) }
-
- it 'returns http success' do
+ before do
+ sign_in(Fabricate(:user))
get :index
+ end
+ it 'returns http success' do
expect(response).to have_http_status(200)
end
+
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
end
end
M spec/controllers/invites_controller_spec.rb => spec/controllers/invites_controller_spec.rb +25 -20
@@ 5,35 5,40 @@ require 'rails_helper'
describe InvitesController do
render_views
+ let(:user) { Fabricate(:user) }
+
before do
sign_in user
end
describe 'GET #index' do
- subject { get :index }
-
- let(:user) { Fabricate(:user) }
- let!(:invite) { Fabricate(:invite, user: user) }
+ before do
+ Fabricate(:invite, user: user)
+ end
context 'when everyone can invite' do
before do
UserRole.everyone.update(permissions: UserRole.everyone.permissions | UserRole::FLAGS[:invite_users])
+ get :index
+ end
+
+ it 'returns http success' do
+ expect(response).to have_http_status(:success)
end
- it 'renders index page' do
- expect(subject).to render_template :index
- expect(assigns(:invites)).to include invite
- expect(assigns(:invites).count).to eq 1
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
context 'when not everyone can invite' do
before do
UserRole.everyone.update(permissions: UserRole.everyone.permissions & ~UserRole::FLAGS[:invite_users])
+ get :index
end
- it 'returns 403' do
- expect(subject).to have_http_status 403
+ it 'returns http forbidden' do
+ expect(response).to have_http_status(403)
end
end
end
@@ 42,8 47,6 @@ describe InvitesController do
subject { post :create, params: { invite: { max_uses: '10', expires_in: 1800 } } }
context 'when everyone can invite' do
- let(:user) { Fabricate(:user) }
-
before do
UserRole.everyone.update(permissions: UserRole.everyone.permissions | UserRole::FLAGS[:invite_users])
end
@@ 56,26 59,28 @@ describe InvitesController do
end
context 'when not everyone can invite' do
- let(:user) { Fabricate(:user) }
-
before do
UserRole.everyone.update(permissions: UserRole.everyone.permissions & ~UserRole::FLAGS[:invite_users])
end
- it 'returns 403' do
- expect(subject).to have_http_status 403
+ it 'returns http forbidden' do
+ expect(subject).to have_http_status(403)
end
end
end
describe 'DELETE #create' do
- subject { delete :destroy, params: { id: invite.id } }
+ let(:invite) { Fabricate(:invite, user: user, expires_at: nil) }
- let(:user) { Fabricate(:user) }
- let!(:invite) { Fabricate(:invite, user: user, expires_at: nil) }
+ before do
+ delete :destroy, params: { id: invite.id }
+ end
+
+ it 'redirects' do
+ expect(response).to redirect_to invites_path
+ end
it 'expires invite' do
- expect(subject).to redirect_to invites_path
expect(invite.reload).to be_expired
end
end
M spec/controllers/manifests_controller_spec.rb => spec/controllers/manifests_controller_spec.rb +14 -1
@@ 7,11 7,24 @@ describe ManifestsController do
describe 'GET #show' do
before do
- get :show, format: :json
+ get :show
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
+
+ it 'returns public cache control header' do
+ expect(response.headers['Cache-Control']).to include('public')
+ end
+
+ it 'does not set cookies' do
+ expect(response.cookies).to be_empty
+ expect(response.headers['Set-Cookies']).to be_nil
+ end
+
+ it 'does not set sessions' do
+ expect(session).to be_empty
+ end
end
end
M spec/controllers/oauth/authorizations_controller_spec.rb => spec/controllers/oauth/authorizations_controller_spec.rb +5 -0
@@ 31,6 31,11 @@ RSpec.describe Oauth::AuthorizationsController, type: :controller do
expect(response).to have_http_status(200)
end
+ it 'returns private cache control headers' do
+ subject
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
+
it 'gives options to authorize and deny' do
subject
expect(response.body).to match(/Authorize/)
M spec/controllers/oauth/authorized_applications_controller_spec.rb => spec/controllers/oauth/authorized_applications_controller_spec.rb +5 -0
@@ 27,6 27,11 @@ describe Oauth::AuthorizedApplicationsController do
expect(response).to have_http_status(200)
end
+ it 'returns private cache control headers' do
+ subject
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
+
include_examples 'stores location for user'
end
M spec/controllers/relationships_controller_spec.rb => spec/controllers/relationships_controller_spec.rb +36 -30
@@ 7,42 7,39 @@ describe RelationshipsController do
let(:user) { Fabricate(:user) }
- shared_examples 'authenticate user' do
- it 'redirects when not signed in' do
- expect(subject).to redirect_to '/auth/sign_in'
- end
- end
-
describe 'GET #show' do
- subject { get :show, params: { page: 2, relationship: 'followed_by' } }
-
- it 'assigns @accounts' do
- Fabricate(:account, domain: 'old').follow!(user.account)
- Fabricate(:account, domain: 'recent').follow!(user.account)
+ context 'when signed in' do
+ before do
+ sign_in user, scope: :user
+ get :show, params: { page: 2, relationship: 'followed_by' }
+ end
- sign_in user, scope: :user
- subject
+ it 'returns http success' do
+ expect(response).to have_http_status(200)
+ end
- assigned = assigns(:accounts).per(1).to_a
- expect(assigned.size).to eq 1
- expect(assigned[0].domain).to eq 'old'
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
- it 'returns http success' do
- sign_in user, scope: :user
- subject
- expect(response).to have_http_status(200)
- end
+ context 'when not signed in' do
+ before do
+ get :show, params: { page: 2, relationship: 'followed_by' }
+ end
- include_examples 'authenticate user'
+ it 'redirects when not signed in' do
+ expect(response).to redirect_to '/auth/sign_in'
+ end
+ end
end
describe 'PATCH #update' do
- let(:poopfeast) { Fabricate(:account, username: 'poopfeast', domain: 'example.com') }
+ let(:alice) { Fabricate(:account, username: 'alice', domain: 'example.com') }
shared_examples 'redirects back to followers page' do
it 'redirects back to followers page' do
- poopfeast.follow!(user.account)
+ alice.follow!(user.account)
sign_in user, scope: :user
subject
@@ 58,27 55,36 @@ describe RelationshipsController do
end
context 'when select parameter is provided' do
- subject { patch :update, params: { form_account_batch: { account_ids: [poopfeast.id] }, remove_domains_from_followers: '' } }
+ subject { patch :update, params: { form_account_batch: { account_ids: [alice.id] }, remove_domains_from_followers: '' } }
it 'soft-blocks followers from selected domains' do
- poopfeast.follow!(user.account)
+ alice.follow!(user.account)
sign_in user, scope: :user
subject
- expect(poopfeast.following?(user.account)).to be false
+ expect(alice.following?(user.account)).to be false
end
it 'does not unfollow users from selected domains' do
- user.account.follow!(poopfeast)
+ user.account.follow!(alice)
sign_in user, scope: :user
subject
- expect(user.account.following?(poopfeast)).to be true
+ expect(user.account.following?(alice)).to be true
+ end
+
+ context 'when not signed in' do
+ before do
+ subject
+ end
+
+ it 'redirects when not signed in' do
+ expect(response).to redirect_to '/auth/sign_in'
+ end
end
- include_examples 'authenticate user'
include_examples 'redirects back to followers page'
end
end
M spec/controllers/settings/aliases_controller_spec.rb => spec/controllers/settings/aliases_controller_spec.rb +8 -1
@@ 13,10 13,17 @@ describe Settings::AliasesController do
end
describe 'GET #index' do
- it 'returns http success' do
+ before do
get :index
+ end
+
+ it 'returns http success' do
expect(response).to have_http_status(200)
end
+
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
describe 'POST #create' do
M spec/controllers/settings/applications_controller_spec.rb => spec/controllers/settings/applications_controller_spec.rb +9 -5
@@ 13,13 13,17 @@ describe Settings::ApplicationsController do
end
describe 'GET #index' do
- let!(:other_app) { Fabricate(:application) }
-
- it 'shows apps' do
+ before do
+ Fabricate(:application)
get :index
+ end
+
+ it 'returns http success' do
expect(response).to have_http_status(200)
- expect(assigns(:applications)).to include(app)
- expect(assigns(:applications)).to_not include(other_app)
+ end
+
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
end
end
M spec/controllers/settings/deletes_controller_spec.rb => spec/controllers/settings/deletes_controller_spec.rb +9 -2
@@ 11,20 11,27 @@ describe Settings::DeletesController do
before do
sign_in user, scope: :user
+ get :show
end
it 'renders confirmation page' do
- get :show
expect(response).to have_http_status(200)
end
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
+
context 'when suspended' do
let(:user) { Fabricate(:user, account_attributes: { suspended_at: Time.now.utc }) }
it 'returns http forbidden' do
- get :show
expect(response).to have_http_status(403)
end
+
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
end
M spec/controllers/settings/exports_controller_spec.rb => spec/controllers/settings/exports_controller_spec.rb +6 -6
@@ 11,16 11,16 @@ describe Settings::ExportsController do
before do
sign_in user, scope: :user
- end
-
- it 'renders export' do
get :show
+ end
- export = assigns(:export)
- expect(export).to be_instance_of Export
- expect(export.account).to eq user.account
+ it 'returns http success' do
expect(response).to have_http_status(200)
end
+
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
context 'when not signed in' do
M spec/controllers/settings/imports_controller_spec.rb => spec/controllers/settings/imports_controller_spec.rb +8 -1
@@ 10,10 10,17 @@ RSpec.describe Settings::ImportsController, type: :controller do
end
describe 'GET #show' do
- it 'returns http success' do
+ before do
get :show
+ end
+
+ it 'returns http success' do
expect(response).to have_http_status(200)
end
+
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
describe 'POST #create' do
M spec/controllers/settings/login_activities_controller_spec.rb => spec/controllers/settings/login_activities_controller_spec.rb +8 -1
@@ 12,9 12,16 @@ describe Settings::LoginActivitiesController do
end
describe 'GET #index' do
- it 'returns http success' do
+ before do
get :index
+ end
+
+ it 'returns http success' do
expect(response).to have_http_status(200)
end
+
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
end
M spec/controllers/settings/migration/redirects_controller_spec.rb => spec/controllers/settings/migration/redirects_controller_spec.rb +8 -1
@@ 12,10 12,17 @@ describe Settings::Migration::RedirectsController do
end
describe 'GET #new' do
- it 'returns http success' do
+ before do
get :new
+ end
+
+ it 'returns http success' do
expect(response).to have_http_status(200)
end
+
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
describe 'POST #create' do
M spec/controllers/settings/preferences/appearance_controller_spec.rb => spec/controllers/settings/preferences/appearance_controller_spec.rb +7 -1
@@ 12,11 12,17 @@ describe Settings::Preferences::AppearanceController do
end
describe 'GET #show' do
- it 'returns http success' do
+ before do
get :show
+ end
+ it 'returns http success' do
expect(response).to have_http_status(200)
end
+
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
describe 'PUT #update' do
M spec/controllers/settings/preferences/notifications_controller_spec.rb => spec/controllers/settings/preferences/notifications_controller_spec.rb +8 -1
@@ 12,10 12,17 @@ describe Settings::Preferences::NotificationsController do
end
describe 'GET #show' do
- it 'returns http success' do
+ before do
get :show
+ end
+
+ it 'returns http success' do
expect(response).to have_http_status(200)
end
+
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
describe 'PUT #update' do
M spec/controllers/settings/preferences/other_controller_spec.rb => spec/controllers/settings/preferences/other_controller_spec.rb +8 -1
@@ 12,10 12,17 @@ describe Settings::Preferences::OtherController do
end
describe 'GET #show' do
- it 'returns http success' do
+ before do
get :show
+ end
+
+ it 'returns http success' do
expect(response).to have_http_status(200)
end
+
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
describe 'PUT #update' do
M spec/controllers/settings/profiles_controller_spec.rb => spec/controllers/settings/profiles_controller_spec.rb +8 -1
@@ 13,10 13,17 @@ RSpec.describe Settings::ProfilesController, type: :controller do
end
describe 'GET #show' do
- it 'returns http success' do
+ before do
get :show
+ end
+
+ it 'returns http success' do
expect(response).to have_http_status(200)
end
+
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
describe 'PUT #update' do
M spec/controllers/settings/two_factor_authentication_methods_controller_spec.rb => spec/controllers/settings/two_factor_authentication_methods_controller_spec.rb +6 -4
@@ 26,23 26,25 @@ describe Settings::TwoFactorAuthenticationMethodsController do
describe 'when user has enabled otp' do
before do
user.update(otp_required_for_login: true)
+ get :index
end
it 'returns http success' do
- get :index
-
expect(response).to have_http_status(200)
end
+
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
describe 'when user has not enabled otp' do
before do
user.update(otp_required_for_login: false)
+ get :index
end
it 'redirects to enable otp' do
- get :index
-
expect(response).to redirect_to(settings_otp_authentication_path)
end
end
M spec/controllers/statuses_cleanup_controller_spec.rb => spec/controllers/statuses_cleanup_controller_spec.rb +16 -3
@@ 11,19 11,32 @@ RSpec.describe StatusesCleanupController, type: :controller do
end
describe 'GET #show' do
- it 'returns http success' do
+ before do
get :show
+ end
+
+ it 'returns http success' do
expect(response).to have_http_status(200)
end
+
+ it 'returns private cache control headers' do
+ expect(response.headers['Cache-Control']).to include('private, no-store')
+ end
end
describe 'PUT #update' do
- it 'updates the account status cleanup policy' do
+ before do
put :update, params: { account_statuses_cleanup_policy: { enabled: true, min_status_age: 2.weeks.seconds, keep_direct: false, keep_polls: true } }
- expect(response).to redirect_to(statuses_cleanup_path)
+ end
+
+ it 'updates the account status cleanup policy' do
expect(@user.account.statuses_cleanup_policy.enabled).to be true
expect(@user.account.statuses_cleanup_policy.keep_direct).to be false
expect(@user.account.statuses_cleanup_policy.keep_polls).to be true
end
+
+ it 'redirects' do
+ expect(response).to redirect_to(statuses_cleanup_path)
+ end
end
end
M spec/controllers/statuses_controller_spec.rb => spec/controllers/statuses_controller_spec.rb +4 -0
@@ 15,6 15,10 @@ describe StatusesController do
expect(session).to be_empty
end
+ it 'returns Vary header' do
+ expect(response.headers['Vary']).to include 'Accept'
+ end
+
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
M spec/controllers/tags_controller_spec.rb => spec/controllers/tags_controller_spec.rb +37 -8
@@ 6,21 6,50 @@ RSpec.describe TagsController, type: :controller do
render_views
describe 'GET #show' do
- let!(:tag) { Fabricate(:tag, name: 'test') }
- let!(:local) { Fabricate(:status, tags: [tag], text: 'local #test') }
- let!(:remote) { Fabricate(:status, tags: [tag], text: 'remote #test', account: Fabricate(:account, domain: 'remote')) }
- let!(:late) { Fabricate(:status, tags: [tag], text: 'late #test') }
+ let(:format) { 'html' }
+ let(:tag) { Fabricate(:tag, name: 'test') }
+ let(:tag_name) { tag&.name }
+
+ before do
+ get :show, params: { id: tag_name, format: format }
+ end
context 'when tag exists' do
- it 'returns http success' do
- get :show, params: { id: 'test', max_id: late.id }
- expect(response).to have_http_status(200)
+ context 'when requested as HTML' do
+ it 'returns http success' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'returns Vary header' do
+ expect(response.headers['Vary']).to eq 'Accept'
+ end
+
+ it 'returns public Cache-Control header' do
+ expect(response.headers['Cache-Control']).to include 'public'
+ end
+ end
+
+ context 'when requested as JSON' do
+ let(:format) { 'json' }
+
+ it 'returns http success' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'returns Vary header' do
+ expect(response.headers['Vary']).to eq 'Accept'
+ end
+
+ it 'returns public Cache-Control header' do
+ expect(response.headers['Cache-Control']).to include 'public'
+ end
end
end
context 'when tag does not exist' do
+ let(:tag_name) { 'hoge' }
+
it 'returns http not found' do
- get :show, params: { id: 'none' }
expect(response).to have_http_status(404)
end
end