Secure active storage with devise

后端 未结 2 1783
遥遥无期
遥遥无期 2020-12-28 18:44

Using devise gem to authenticate all users of an application. I\'m trying to implement Active Storage.

Let\'s say that all users must be authenticated as soon as t

相关标签:
2条回答
  • 2020-12-28 19:24

    If you want to implement authentication for all endpoints provided by active storage, you can override the ActiveStorage::BaseController based on the original implementation:

    # 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
    
    0 讨论(0)
  • 2020-12-28 19:26

    This is not a full answer but a starting point:

    The gist: You would need to override the redirect controller.

    The docs for activestorage/app/controllers/active_storage/blobs_controller.rb say:

    If you need to enforce access protection beyond the security-through-obscurity factor of the signed blob references, you'll need to implement your own authenticated redirection controller.

    Also if you plan to use previews the docs for activestorage/app/models/active_storage/blob/representable.rb say

    Active Storage provides one [controller action for previews], but you may want to create your own (for example, if you need authentication).

    Also you might find some relevant information in this rails github issue

    Update: Here is a minimal example that "should" work for preventing unauthorised access to the redirects when using the devise gem.

    How the url, that the user will be redirected to if logged, is then secured is still another story I guess. By default they expire after 5 minutes but this could be set to a shorter period like 10 seconds (if you replace line 6 in example below with expires_in 10.seconds)

    Create a file app/controllers/active_storage/blobs_controller.rb with the following code:

    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
    

    Please note that the only thing that changed from the original code is that the second line is added

    before_action :authenticate_user!
    

    Update 2:

    Here is a concern that you can include in ActiveStorage::RepresentationsController and ActiveStorage::BlobsController to enable devise authentication for ActiveStorage

    See gist is at https://gist.github.com/dommmel/4e41b204b97238e9aaf35939ae8e1666 also included here:

    # 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
    
    0 讨论(0)
提交回复
热议问题