Add confirmation when redirecting logged-out requests to permalink (#27792)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
		@@ -21,10 +21,19 @@ module WebAppControllerConcern
 | 
			
		||||
  def redirect_unauthenticated_to_permalinks!
 | 
			
		||||
    return if user_signed_in? && current_account.moved_to_account_id.nil?
 | 
			
		||||
 | 
			
		||||
    redirect_path = PermalinkRedirector.new(request.path).redirect_path
 | 
			
		||||
    return if redirect_path.blank?
 | 
			
		||||
    permalink_redirector = PermalinkRedirector.new(request.path)
 | 
			
		||||
    return if permalink_redirector.redirect_path.blank?
 | 
			
		||||
 | 
			
		||||
    expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless user_signed_in?
 | 
			
		||||
    redirect_to(redirect_path)
 | 
			
		||||
 | 
			
		||||
    respond_to do |format|
 | 
			
		||||
      format.html do
 | 
			
		||||
        redirect_to(permalink_redirector.redirect_confirmation_path, allow_other_host: false)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      format.json do
 | 
			
		||||
        redirect_to(permalink_redirector.redirect_uri, allow_other_host: true)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								app/controllers/redirect/accounts_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/controllers/redirect/accounts_controller.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Redirect::AccountsController < ApplicationController
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def set_resource
 | 
			
		||||
    @resource = Account.find(params[:id])
 | 
			
		||||
    not_found if @resource.local?
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										24
									
								
								app/controllers/redirect/base_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								app/controllers/redirect/base_controller.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Redirect::BaseController < ApplicationController
 | 
			
		||||
  vary_by 'Accept-Language'
 | 
			
		||||
 | 
			
		||||
  before_action :set_resource
 | 
			
		||||
  before_action :set_app_body_class
 | 
			
		||||
 | 
			
		||||
  def show
 | 
			
		||||
    @redirect_path = ActivityPub::TagManager.instance.url_for(@resource)
 | 
			
		||||
 | 
			
		||||
    render 'redirects/show', layout: 'application'
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def set_app_body_class
 | 
			
		||||
    @body_classes = 'app-body'
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_resource
 | 
			
		||||
    raise NotImplementedError
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										10
									
								
								app/controllers/redirect/statuses_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/controllers/redirect/statuses_controller.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Redirect::StatusesController < Redirect::BaseController
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def set_resource
 | 
			
		||||
    @resource = Status.find(params[:id])
 | 
			
		||||
    not_found if @resource.local? || !@resource.distributable?
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -104,3 +104,59 @@
 | 
			
		||||
    margin-inline-start: 10px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.redirect {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  height: 100vh;
 | 
			
		||||
  font-size: 14px;
 | 
			
		||||
  line-height: 18px;
 | 
			
		||||
 | 
			
		||||
  &__logo {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    margin-bottom: 30px;
 | 
			
		||||
 | 
			
		||||
    img {
 | 
			
		||||
      height: 48px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &__message {
 | 
			
		||||
    text-align: center;
 | 
			
		||||
 | 
			
		||||
    h1 {
 | 
			
		||||
      font-size: 17px;
 | 
			
		||||
      line-height: 22px;
 | 
			
		||||
      font-weight: 700;
 | 
			
		||||
      margin-bottom: 30px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    p {
 | 
			
		||||
      margin-bottom: 30px;
 | 
			
		||||
 | 
			
		||||
      &:last-child {
 | 
			
		||||
        margin-bottom: 0;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    a {
 | 
			
		||||
      color: $highlight-text-color;
 | 
			
		||||
      font-weight: 500;
 | 
			
		||||
      text-decoration: none;
 | 
			
		||||
 | 
			
		||||
      &:hover,
 | 
			
		||||
      &:focus,
 | 
			
		||||
      &:active {
 | 
			
		||||
        text-decoration: underline;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &__link {
 | 
			
		||||
    margin-top: 15px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,17 +5,46 @@ class PermalinkRedirector
 | 
			
		||||
 | 
			
		||||
  def initialize(path)
 | 
			
		||||
    @path = path
 | 
			
		||||
    @object = nil
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def object
 | 
			
		||||
    @object ||= begin
 | 
			
		||||
      if at_username_status_request? || statuses_status_request?
 | 
			
		||||
        status = Status.find_by(id: second_segment)
 | 
			
		||||
        status if status&.distributable? && !status&.local?
 | 
			
		||||
      elsif at_username_request?
 | 
			
		||||
        username, domain = first_segment.delete_prefix('@').split('@')
 | 
			
		||||
        domain = nil if TagManager.instance.local_domain?(domain)
 | 
			
		||||
        account = Account.find_remote(username, domain)
 | 
			
		||||
        account unless account&.local?
 | 
			
		||||
      elsif accounts_request? && record_integer_id_request?
 | 
			
		||||
        account = Account.find_by(id: second_segment)
 | 
			
		||||
        account unless account&.local?
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def redirect_path
 | 
			
		||||
    if at_username_status_request? || statuses_status_request?
 | 
			
		||||
      find_status_url_by_id(second_segment)
 | 
			
		||||
    elsif at_username_request?
 | 
			
		||||
      find_account_url_by_name(first_segment)
 | 
			
		||||
    elsif accounts_request? && record_integer_id_request?
 | 
			
		||||
      find_account_url_by_id(second_segment)
 | 
			
		||||
    elsif @path.start_with?('/deck')
 | 
			
		||||
      @path.delete_prefix('/deck')
 | 
			
		||||
    return ActivityPub::TagManager.instance.url_for(object) if object.present?
 | 
			
		||||
 | 
			
		||||
    @path.delete_prefix('/deck') if @path.start_with?('/deck')
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def redirect_uri
 | 
			
		||||
    return ActivityPub::TagManager.instance.uri_for(object) if object.present?
 | 
			
		||||
 | 
			
		||||
    @path.delete_prefix('/deck') if @path.start_with?('/deck')
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def redirect_confirmation_path
 | 
			
		||||
    case object.class.name
 | 
			
		||||
    when 'Account'
 | 
			
		||||
      redirect_account_path(object.id)
 | 
			
		||||
    when 'Status'
 | 
			
		||||
      redirect_status_path(object.id)
 | 
			
		||||
    else
 | 
			
		||||
      @path.delete_prefix('/deck') if @path.start_with?('/deck')
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -56,22 +85,4 @@ class PermalinkRedirector
 | 
			
		||||
  def path_segments
 | 
			
		||||
    @path_segments ||= @path.delete_prefix('/deck').delete_prefix('/').split('/')
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def find_status_url_by_id(id)
 | 
			
		||||
    status = Status.find_by(id: id)
 | 
			
		||||
    ActivityPub::TagManager.instance.url_for(status) if status&.distributable? && !status.account.local?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def find_account_url_by_id(id)
 | 
			
		||||
    account = Account.find_by(id: id)
 | 
			
		||||
    ActivityPub::TagManager.instance.url_for(account) if account.present? && !account.local?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def find_account_url_by_name(name)
 | 
			
		||||
    username, domain = name.gsub(/\A@/, '').split('@')
 | 
			
		||||
    domain           = nil if TagManager.instance.local_domain?(domain)
 | 
			
		||||
    account          = Account.find_remote(username, domain)
 | 
			
		||||
 | 
			
		||||
    ActivityPub::TagManager.instance.url_for(account) if account.present? && !account.local?
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								app/views/redirects/show.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/views/redirects/show.html.haml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
.redirect
 | 
			
		||||
  .redirect__logo
 | 
			
		||||
    = link_to render_logo, root_path
 | 
			
		||||
 | 
			
		||||
  .redirect__message
 | 
			
		||||
    %h1= t('redirects.title', instance: site_hostname)
 | 
			
		||||
    %p= t('redirects.prompt')
 | 
			
		||||
    %p= link_to @redirect_path, @redirect_path, rel: 'noreferrer noopener'
 | 
			
		||||
@@ -1547,6 +1547,9 @@ en:
 | 
			
		||||
    errors:
 | 
			
		||||
      limit_reached: Limit of different reactions reached
 | 
			
		||||
      unrecognized_emoji: is not a recognized emoji
 | 
			
		||||
  redirects:
 | 
			
		||||
    prompt: If you trust this link, click it to continue.
 | 
			
		||||
    title: You are leaving %{instance}.
 | 
			
		||||
  relationships:
 | 
			
		||||
    activity: Account activity
 | 
			
		||||
    confirm_follow_selected_followers: Are you sure you want to follow selected followers?
 | 
			
		||||
 
 | 
			
		||||
@@ -162,6 +162,11 @@ Rails.application.routes.draw do
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  namespace :redirect do
 | 
			
		||||
    resources :accounts, only: :show
 | 
			
		||||
    resources :statuses, only: :show
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  resources :media, only: [:show] do
 | 
			
		||||
    get :player
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user