web-dev-qa-db-ja.com

deviseでアクティブストレージを保護

Devise gemを使用して、アプリケーションのすべてのユーザーを認証します。 Active Storageを実装しようとしています。

アプリに到達したらすぐに、すべてのユーザーを認証する必要があるとします。

class ApplicationController < ActionController::Base
  before_action :authenticate_user!

...
end

Active Storageが生成したルートを保護する方法は?

アップロードされたファイルのURLは、最初に認証することなくアクセスできます。認証されていないユーザーは、Active Storageによって生成されたファイルのURLを取得できます。

15
coding addicted

これは完全な答えではなく、出発点です。

要点:リダイレクトコントローラをオーバーライドする必要があります。

activestorage/app/controllers/active_storage/blobs_controller.rbのドキュメント と言う:

署名されたblob参照のsecurity-through-obscurity要素を超えてアクセス保護を実施する必要がある場合は、独自の認証済みリダイレクトコントローラーを実装する必要があります。

また、プレビューを使用する予定がある場合 activestorage/app/models/active_storage/blob/representable.rbのドキュメント 言う

Active Storageは1つの[プレビュー用のコントローラーアクション]を提供しますが、独自に作成することもできます(たとえば、認証が必要な場合)。

また、関連情報が this Rails github issue にある場合もあります。

更新:これは、devise gemを使用するときにリダイレクトへの不正アクセスを防止するために「すべき」最小限の例です。

ログに記録された場合にユーザーがリダイレクトされるURLがどのように保護されるかは、さらに別の話だと思います。デフォルトでは5分後に有効期限が切れますが、これは10秒などの短い期間に設定できます(以下の例の6行目をexpires_in 10.secondsで置き換えた場合)

次のコードでファイルapp/controllers/active_storage/blobs_controller.rbを作成します。

class ActiveStorage::BlobsController < ActiveStorage::BaseController
  before_action :authenticate_user!
  include ActiveStorage::SetBlob

  def show
    expires_in ActiveStorage::Blob.service.url_expires_in
    redirect_to @blob.service_url(disposition: params[:disposition])
  end
end

元のコード から変更された唯一の点は、2行目が追加されていることです。

before_action :authenticate_user!

更新2:

devise _ActiveStorageの認証を有効にするためにActiveStorage::RepresentationsControllerおよびActiveStorage::BlobsControllerに含めることができる懸念事項を次に示します

Gist is at https://Gist.github.com/dommmel/4e41b204b97238e9aaf35939ae8e1666 もここに含まれています:

# Rails controller concern to enable Devise authentication for ActiveStorage.
# Put it in +app/controllers/concerns/blob_authenticatable.rb+ and include it when overriding
# +ActiveStorage::BlobsController+ and +ActiveStorage::RepresentationsController+.
# 
# Optional configuration:
# 
# Set the model that includes devise's database_authenticatable.
# Defaults to Devise.default_scope which defaults to the first
# devise role declared in your routes (usually :user)
#
#   blob_authenticatable resource: :admin
#   
# To specify how to determine if the current_user is allowed to access the 
# blob, override the can_access_blob? method
#   
# Minimal example:
# 
#   class ActiveStorage::BlobsController < ActiveStorage::BaseController
#     include ActiveStorage::SetBlob
#     include AdminOrUserAuthenticatable
#     
#     def show
#       expires_in ActiveStorage::Blob.service.url_expires_in
#       redirect_to @blob.service_url(disposition: params[:disposition])
#     end
#   end
# 
# Complete example:
# 
#   class ActiveStorage::RepresentationsController < ActiveStorage::BaseController
#     include ActiveStorage::SetBlob
#     include AdminOrUserAuthenticatable
# 
#     blob_authenticatable resource: :admin
#
#     def show
#       expires_in ActiveStorage::Blob.service.url_expires_in
#       redirect_to @blob.representation(params[:variation_key]).processed.service_url(disposition: params[:disposition])
#     end
#     
#     private
#
#       def can_access_blob?(current_user)
#         @blob.attachments.map(&:record).all? { |record| record.user == current_user }
#       end
#   end

module BlobAuthenticatable
  extend ActiveSupport::Concern

  included do
    around_action :wrap_in_authentication
  end

  module ClassMethods
    def auth_resource
      @auth_resource || Devise.default_scope
    end

    private

      def blob_authenticatable(resource:)
        @auth_resource = resource
      end
  end

  private

    def wrap_in_authentication
      is_signed_in_and_authorized = send("#{self.class.auth_resource}_signed_in?") \
        & can_access_blob?(send("current_#{self.class.auth_resource}"))

      if is_signed_in_and_authorized
        yield
      else
        head :unauthorized
      end
    end

    def can_access_blob?(_user)
      true
    end
end
15
Domi

アクティブストレージによって提供されるすべてのエンドポイントに認証を実装する場合は、 元の実装 に基づいてActiveStorage::BaseControllerをオーバーライドできます。

# app/controllers/active_storage/base_controller.rb

# frozen_string_literal: true

# The base class for all Active Storage controllers.
class ActiveStorage::BaseController < ActionController::Base
  before_action :authenticate_user!
  include ActiveStorage::SetCurrent

  protect_from_forgery with: :exception
end
3
AbM