mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-10-28 00:16:48 +09:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0141eae018 | ||
|
|
c49c41a0d7 | ||
|
|
49f06d0b22 | ||
|
|
ed5a172871 |
No files matched your search
@@ -2,4 +2,30 @@ ActiveSupport.on_load(:active_storage_blob) do
|
||||
ActiveStorage::DiskController.after_action only: :show do
|
||||
response.set_header("Cache-Control", "max-age=3600, public")
|
||||
end
|
||||
|
||||
# Gate the ActiveStorage write path behind app authentication. These endpoints
|
||||
# ship unauthenticated by Rails default; Campfire never uses direct upload for
|
||||
# legit attachments (those go through MessagesController#create, and Trix file
|
||||
# drops are disabled in the composer). Requiring a session on the write actions
|
||||
# blocks anonymous blob writes and disk-fill while leaving blob serving public.
|
||||
#
|
||||
# ActiveStorage controllers live in ActiveStorage::Engine, so they see the
|
||||
# engine's url helpers, not the main app's. Include the application helpers
|
||||
# first so Authentication#request_authentication can redirect to new_session_url.
|
||||
ActiveStorage::DirectUploadsController.include Rails.application.routes.url_helpers
|
||||
ActiveStorage::DirectUploadsController.include Authentication # only action: #create
|
||||
|
||||
ActiveStorage::DiskController.include Rails.application.routes.url_helpers
|
||||
ActiveStorage::DiskController.include Authentication
|
||||
|
||||
# Blob serving (#show) stays public so signed-token attachment URLs keep
|
||||
# resolving for unauthenticated and bot clients alike.
|
||||
ActiveStorage::DiskController.allow_unauthenticated_access only: :show
|
||||
ActiveStorage::DiskController.allow_bot_access only: :show
|
||||
|
||||
# Including Authentication re-adds protect_from_forgery, but Active Storage's
|
||||
# direct-upload service PUT (#update) carries only signed service headers and
|
||||
# no authenticity token. Re-exempt it from CSRF so authenticated uploads can
|
||||
# still store bytes; the signed URL token and the session check remain.
|
||||
ActiveStorage::DiskController.skip_forgery_protection only: :update
|
||||
end
|
||||
@@ -0,0 +1,83 @@
|
||||
require "test_helper"
|
||||
|
||||
class ActiveStorage::DirectUploadsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
host! "once.campfire.test"
|
||||
end
|
||||
|
||||
test "create requires authentication" do
|
||||
assert_no_difference -> { ActiveStorage::Blob.count } do
|
||||
post rails_direct_uploads_url, params: { blob: blob_params }
|
||||
end
|
||||
|
||||
assert_redirected_to new_session_url
|
||||
end
|
||||
|
||||
test "create succeeds for an authenticated session" do
|
||||
sign_in :david
|
||||
|
||||
assert_difference -> { ActiveStorage::Blob.count }, 1 do
|
||||
post rails_direct_uploads_url, params: { blob: blob_params }
|
||||
end
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "disk show stays reachable without authentication" do
|
||||
blob = ActiveStorage::Blob.create_and_upload! \
|
||||
io: StringIO.new("hello"), filename: "hello.txt", content_type: "text/plain"
|
||||
|
||||
ActiveStorage::Current.set(url_options: { host: "once.campfire.test", protocol: "http" }) do
|
||||
get blob.url
|
||||
end
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "disk update requires authentication" do
|
||||
_blob, url = direct_upload_url_for("hello")
|
||||
|
||||
put url, params: "hello", headers: { "Content-Type" => "text/plain" }
|
||||
|
||||
assert_redirected_to new_session_url
|
||||
end
|
||||
|
||||
test "disk update stores bytes for an authenticated session without a CSRF token" do
|
||||
sign_in :david
|
||||
blob, url = direct_upload_url_for("hello")
|
||||
|
||||
# Forgery protection is off in test by default; turn it on so this proves the
|
||||
# signed-token service PUT stays CSRF-exempt even when the concern re-arms it.
|
||||
with_forgery_protection do
|
||||
put url, params: "hello", headers: { "Content-Type" => "text/plain" }
|
||||
end
|
||||
|
||||
assert_response :no_content
|
||||
assert_equal "hello", blob.download
|
||||
end
|
||||
|
||||
private
|
||||
def blob_params
|
||||
content = "hello"
|
||||
{ filename: "hello.txt", byte_size: content.bytesize, content_type: "text/plain", \
|
||||
checksum: Digest::MD5.base64digest(content) }
|
||||
end
|
||||
|
||||
def direct_upload_url_for(content)
|
||||
blob = ActiveStorage::Blob.create_before_direct_upload! \
|
||||
filename: "hello.txt", byte_size: content.bytesize, \
|
||||
checksum: Digest::MD5.base64digest(content), content_type: "text/plain"
|
||||
url = ActiveStorage::Current.set(url_options: { host: "once.campfire.test", protocol: "http" }) do
|
||||
blob.service_url_for_direct_upload
|
||||
end
|
||||
[ blob, url ]
|
||||
end
|
||||
|
||||
def with_forgery_protection
|
||||
original = ActionController::Base.allow_forgery_protection
|
||||
ActionController::Base.allow_forgery_protection = true
|
||||
yield
|
||||
ensure
|
||||
ActionController::Base.allow_forgery_protection = original
|
||||
end
|
||||
end
|
||||
Reference in new issue
Block a user