diff options
Diffstat (limited to 'activestorage/app/controllers/active_storage')
5 files changed, 125 insertions, 0 deletions
diff --git a/activestorage/app/controllers/active_storage/base_controller.rb b/activestorage/app/controllers/active_storage/base_controller.rb new file mode 100644 index 0000000000..b27d2bd8aa --- /dev/null +++ b/activestorage/app/controllers/active_storage/base_controller.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# The base class for all Active Storage controllers. +class ActiveStorage::BaseController < ActionController::Base + include ActiveStorage::SetCurrent + + protect_from_forgery with: :exception +end diff --git a/activestorage/app/controllers/active_storage/blobs_controller.rb b/activestorage/app/controllers/active_storage/blobs_controller.rb new file mode 100644 index 0000000000..4fc3fbe824 --- /dev/null +++ b/activestorage/app/controllers/active_storage/blobs_controller.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# Take a signed permanent reference for a blob and turn it into an expiring service URL for download. +# Note: These URLs are publicly accessible. 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. +class ActiveStorage::BlobsController < ActiveStorage::BaseController + include ActiveStorage::SetBlob + + def show + expires_in ActiveStorage.service_urls_expire_in + redirect_to @blob.service_url(disposition: params[:disposition]) + end +end diff --git a/activestorage/app/controllers/active_storage/direct_uploads_controller.rb b/activestorage/app/controllers/active_storage/direct_uploads_controller.rb new file mode 100644 index 0000000000..78b43fc94c --- /dev/null +++ b/activestorage/app/controllers/active_storage/direct_uploads_controller.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# Creates a new blob on the server side in anticipation of a direct-to-service upload from the client side. +# When the client-side upload is completed, the signed_blob_id can be submitted as part of the form to reference +# the blob that was created up front. +class ActiveStorage::DirectUploadsController < ActiveStorage::BaseController + def create + blob = ActiveStorage::Blob.create_before_direct_upload!(blob_args) + render json: direct_upload_json(blob) + end + + private + def blob_args + params.require(:blob).permit(:filename, :byte_size, :checksum, :content_type, :metadata).to_h.symbolize_keys + end + + def direct_upload_json(blob) + blob.as_json(root: false, methods: :signed_id).merge(direct_upload: { + url: blob.service_url_for_direct_upload, + headers: blob.service_headers_for_direct_upload + }) + end +end diff --git a/activestorage/app/controllers/active_storage/disk_controller.rb b/activestorage/app/controllers/active_storage/disk_controller.rb new file mode 100644 index 0000000000..7bd641ab9a --- /dev/null +++ b/activestorage/app/controllers/active_storage/disk_controller.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +# Serves files stored with the disk service in the same way that the cloud services do. +# This means using expiring, signed URLs that are meant for immediate access, not permanent linking. +# Always go through the BlobsController, or your own authenticated controller, rather than directly +# to the service url. +class ActiveStorage::DiskController < ActiveStorage::BaseController + skip_forgery_protection + + def show + if key = decode_verified_key + serve_file disk_service.path_for(key), content_type: params[:content_type], disposition: params[:disposition] + else + head :not_found + end + rescue Errno::ENOENT + head :not_found + end + + def update + if token = decode_verified_token + if acceptable_content?(token) + disk_service.upload token[:key], request.body, checksum: token[:checksum] + else + head :unprocessable_entity + end + else + head :not_found + end + rescue ActiveStorage::IntegrityError + head :unprocessable_entity + end + + private + def disk_service + ActiveStorage::Blob.service + end + + + def decode_verified_key + ActiveStorage.verifier.verified(params[:encoded_key], purpose: :blob_key) + end + + def serve_file(path, content_type:, disposition:) + Rack::File.new(nil).serving(request, path).tap do |(status, headers, body)| + self.status = status + self.response_body = body + + headers.each do |name, value| + response.headers[name] = value + end + + response.headers["Content-Type"] = content_type || DEFAULT_SEND_FILE_TYPE + response.headers["Content-Disposition"] = disposition || DEFAULT_SEND_FILE_DISPOSITION + end + end + + + def decode_verified_token + ActiveStorage.verifier.verified(params[:encoded_token], purpose: :blob_token) + end + + def acceptable_content?(token) + token[:content_type] == request.content_type && token[:content_length] == request.content_length + end +end diff --git a/activestorage/app/controllers/active_storage/representations_controller.rb b/activestorage/app/controllers/active_storage/representations_controller.rb new file mode 100644 index 0000000000..98e11e5dbb --- /dev/null +++ b/activestorage/app/controllers/active_storage/representations_controller.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# Take a signed permanent reference for a blob representation and turn it into an expiring service URL for download. +# Note: These URLs are publicly accessible. If you need to enforce access protection beyond the +# security-through-obscurity factor of the signed blob and variation reference, you'll need to implement your own +# authenticated redirection controller. +class ActiveStorage::RepresentationsController < ActiveStorage::BaseController + include ActiveStorage::SetBlob + + def show + expires_in ActiveStorage.service_urls_expire_in + redirect_to @blob.representation(params[:variation_key]).processed.service_url(disposition: params[:disposition]) + end +end |