diff --git a/config/initializers/active_storage.rb b/config/initializers/active_storage.rb index e647a72cf5..5c83b18f5e 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/docs/API.md b/docs/API.md index 3710e582f0..318e7a1ddf 100644 --- a/docs/API.md +++ b/docs/API.md @@ -178,10 +178,11 @@ curl -X POST \ "content_type": "image/png" } }' \ - https://app.fizzy.do/rails/active_storage/direct_uploads + https://app.fizzy.do/123456/rails/active_storage/direct_uploads ``` The `checksum` is a Base64-encoded MD5 hash of the file content. +The direct upload endpoint is scoped to your account (replace `/123456` with your account slug). __Response:__ 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 0000000000..cdb218be20 --- /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