From 5ee10800a284e7dcab913d353920c37b7fa8b6ef Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Thu, 11 Dec 2025 11:52:38 +0100 Subject: [PATCH] Allow direct uploads via API --- config/initializers/active_storage.rb | 10 +++ .../direct_uploads_controller_test.rb | 72 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 test/controllers/active_storage/direct_uploads_controller_test.rb diff --git a/config/initializers/active_storage.rb b/config/initializers/active_storage.rb index e647a72cf..5c83b18f5 100644 --- a/config/initializers/active_storage.rb +++ b/config/initializers/active_storage.rb @@ -35,6 +35,16 @@ module ActiveStorageControllerExtensions end end +module ActiveStorageDirectUploadsControllerExtensions + extend ActiveSupport::Concern + + included do + include Authentication + skip_forgery_protection if: :authenticate_by_bearer_token + end +end + Rails.application.config.to_prepare do ActiveStorage::BaseController.include ActiveStorageControllerExtensions + ActiveStorage::DirectUploadsController.include ActiveStorageDirectUploadsControllerExtensions end diff --git a/test/controllers/active_storage/direct_uploads_controller_test.rb b/test/controllers/active_storage/direct_uploads_controller_test.rb new file mode 100644 index 000000000..cdb218be2 --- /dev/null +++ b/test/controllers/active_storage/direct_uploads_controller_test.rb @@ -0,0 +1,72 @@ +require "test_helper" + +class ActiveStorage::DirectUploadsControllerTest < ActionDispatch::IntegrationTest + setup do + @blob_params = { + blob: { + filename: "screenshot.png", + byte_size: 12345, + checksum: "GQ5SqLsM7ylnji0Wgd9wNC==", + content_type: "image/png" + } + } + end + + test "create" do + sign_in_as :david + + post rails_direct_uploads_path, + params: @blob_params, + headers: bearer_token_header(identity_access_tokens(:davids_api_token).token), + as: :json + + assert_response :success + assert_includes response.parsed_body.keys, "direct_upload" + end + + test "create with valid access token" do + post rails_direct_uploads_path, + params: @blob_params, + headers: bearer_token_header(identity_access_tokens(:davids_api_token).token), + as: :json + + assert_response :success + assert_includes response.parsed_body.keys, "direct_upload" + end + + test "create with read-only access token" do + post rails_direct_uploads_path, + params: @blob_params, + headers: bearer_token_header(identity_access_tokens(:jasons_api_token).token), + as: :json + + assert_response :unauthorized + end + + test "create with invalid access token" do + post rails_direct_uploads_path, + params: @blob_params, + headers: bearer_token_header("invalid_token"), + as: :json + + assert_response :unauthorized + end + + test "create unauthenticated" do + post rails_direct_uploads_path, + params: @blob_params.merge(authenticity_token: csrf_token), + as: :json + + assert_response :redirect + end + + private + def bearer_token_header(token) + { "Authorization" => "Bearer #{token}" } + end + + def csrf_token + get new_session_url + response.body[/name="csrf-token" content="([^"]+)"/, 1] + end +end