【问题标题】:Secure active storage with devise使用设备保护主动存储
【发布时间】:2018-09-23 08:07:33
【问题描述】:

使用 devise gem 对应用程序的所有用户进行身份验证。 我正在尝试实现 Active Storage。

假设所有用户必须在访问应用程序后立即进行身份验证:

class ApplicationController < ActionController::Base
  before_action :authenticate_user!

...
end

如何保护 Active Storage 生成的路由?

无需先验证即可访问上传文件的 URL。未经认证的用户可以获得Active Storage生成的文件url。

【问题讨论】:

  • 好问题,我什至没有想到这一点。必须有某种方法可以将 ActiveStorage URL 放在 devise_scope 下,是我的想法...
  • 你解决了这个问题吗?
  • 遗憾的是,我暂时不坚持神社。我认为它对所有项目都不够成熟。但我敢打赌,它们会在未来的版本中有所改进。

标签: ruby-on-rails rails-activestorage


【解决方案1】:

如果要对主动存储提供的所有端点实现身份验证,可以在original implementation的基础上覆盖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

【讨论】:

  • 谢谢你,工作!我刚刚在调用身份验证之前添加了include Devise::Controllers::Helpers
  • 请确保如果您要覆盖内置的 rails 控制器,则引用与当前 rails 版本关联的文件。例如:如果您使用的是 rails 5.2.6,那么您可能希望覆盖此版本的 ActiveStorage::BaseController 文件:github.com/rails/rails/blob/v5.2.6/activestorage/app/…
【解决方案2】:

这不是一个完整的答案,而是一个起点:

要点:您需要覆盖重定向控制器。

docs for activestorage/app/controllers/active_storage/blobs_controller.rb 说:

如果您需要强制执行访问保护超出 已签名的 blob 引用的安全系数, 您需要实现自己的经过身份验证的重定向 控制器。

另外,如果您打算使用预览,docs for activestorage/app/models/active_storage/blob/representable.rb

Active Storage 提供了一个 [用于预览的控制器操作],但您可能希望创建自己的(例如 例如,如果您需要身份验证)。

你也可以在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

请注意,与original code 唯一不同的是添加了第二行

before_action :authenticate_user!

更新 2:

您可以在ActiveStorage::RepresentationsControllerActiveStorage::BlobsController 中包含一个问题,以便为ActiveStorage 启用devise 身份验证

参见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

【讨论】:

  • 感谢您抽出宝贵时间提供此反馈。我投了赞成票,但它仍然没有解释如何保护 AS。您刚才提到的链接说“您需要实现自己的经过身份验证的重定向控制器”,我也相信这一点,但我们(作为用户)仍然不知道实现这一点的路径;)这就是问题所在。跨度>
  • 我用一个小代码示例更新了答案。我将来可能不得不实施这个(这就是我首先提出这个问题的方式),如果/当我这样做时,我会确保回到这里并分享完整的解决方案。
  • 感谢您的更新!我知道如果我们想要保护 AS,我们需要覆盖它的一些部分。我现在会接受答案并等待未来的更新;)稍后我还将在现有应用程序上对其进行测试,看看它的行为方式。
  • 我们必须测试的一件事是“不受保护”的旧路由/控制器不再可达。
  • 这应该没问题,因为您基本上使用与以前相同的控制器,但添加了 before_action 以确保身份验证。值得一提的三件事恕我直言:1)以同样的方式保护文件预览/变体,你必须以同样的方式覆盖app/controllers/active_storage/representations_controller.rb。 2) 上面的示例不关心检查是否应允许用户查看特定文件。 3) 控制器仍将返回公共 url(尽管快过期的)。在大多数情况下,我认为这不是问题,只是需要注意。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-31
  • 2011-04-04
  • 1970-01-01
  • 2014-08-26
  • 2014-01-14
  • 1970-01-01
相关资源
最近更新 更多