使用设备保护活动存储
Secure active storage with devise
使用设计 gem 对应用程序的所有用户进行身份验证。
我正在尝试实施活动存储。
假设所有用户在访问应用程序后都必须进行身份验证:
class ApplicationController < ActionController::Base
before_action :authenticate_user!
...
end
如何保护 Active Storage 生成的路由?
URL 的上传文件无需先进行身份验证即可访问。未经身份验证的用户可以获得 Active Storage 生成的文件 url。
这不是一个完整的答案,而是一个起点:
要点:您需要覆盖重定向控制器。
docs for activestorage/app/controllers/active_storage/blobs_controller.rb 说:
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.
此外,如果您打算使用预览 docs for activestorage/app/models/active_storage/blob/representable.rb 说
Active Storage provides one [controller action for previews], but you may want to create your own (for
example, if you need authentication).
您还可以在 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!
更新二:
您可以在 ActiveStorage::RepresentationsController
和 ActiveStorage::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
如果要对活动存储提供的所有端点实施身份验证,可以基于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
使用设计 gem 对应用程序的所有用户进行身份验证。 我正在尝试实施活动存储。
假设所有用户在访问应用程序后都必须进行身份验证:
class ApplicationController < ActionController::Base
before_action :authenticate_user!
...
end
如何保护 Active Storage 生成的路由?
URL 的上传文件无需先进行身份验证即可访问。未经身份验证的用户可以获得 Active Storage 生成的文件 url。
这不是一个完整的答案,而是一个起点:
要点:您需要覆盖重定向控制器。
docs for activestorage/app/controllers/active_storage/blobs_controller.rb 说:
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.
此外,如果您打算使用预览 docs for activestorage/app/models/active_storage/blob/representable.rb 说
Active Storage provides one [controller action for previews], but you may want to create your own (for example, if you need authentication).
您还可以在 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!
更新二:
您可以在 ActiveStorage::RepresentationsController
和 ActiveStorage::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
如果要对活动存储提供的所有端点实施身份验证,可以基于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