activestorage_legacy 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.babelrc +5 -0
- data/.codeclimate.yml +7 -0
- data/.eslintrc +19 -0
- data/.github/workflows/gem-push.yml +29 -0
- data/.github/workflows/ruby-tests.yml +37 -0
- data/.gitignore +9 -0
- data/.rubocop.yml +125 -0
- data/.travis.yml +25 -0
- data/Gemfile +33 -0
- data/Gemfile.lock +271 -0
- data/MIT-LICENSE +20 -0
- data/README.md +160 -0
- data/Rakefile +12 -0
- data/activestorage.gemspec +27 -0
- data/app/assets/javascripts/activestorage.js +1 -0
- data/app/controllers/active_storage/blobs_controller.rb +22 -0
- data/app/controllers/active_storage/direct_uploads_controller.rb +21 -0
- data/app/controllers/active_storage/disk_controller.rb +52 -0
- data/app/controllers/active_storage/variants_controller.rb +28 -0
- data/app/helpers/active_storage/file_field_with_direct_upload_helper.rb +18 -0
- data/app/javascript/activestorage/blob_record.js +54 -0
- data/app/javascript/activestorage/blob_upload.js +34 -0
- data/app/javascript/activestorage/direct_upload.js +42 -0
- data/app/javascript/activestorage/direct_upload_controller.js +67 -0
- data/app/javascript/activestorage/direct_uploads_controller.js +50 -0
- data/app/javascript/activestorage/file_checksum.js +53 -0
- data/app/javascript/activestorage/helpers.js +42 -0
- data/app/javascript/activestorage/index.js +11 -0
- data/app/javascript/activestorage/ujs.js +74 -0
- data/app/jobs/active_storage/purge_attachment_worker.rb +9 -0
- data/app/jobs/active_storage/purge_blob_worker.rb +9 -0
- data/app/models/active_storage/attachment.rb +33 -0
- data/app/models/active_storage/blob.rb +198 -0
- data/app/models/active_storage/filename.rb +49 -0
- data/app/models/active_storage/variant.rb +82 -0
- data/app/models/active_storage/variation.rb +53 -0
- data/config/routes.rb +9 -0
- data/config/storage_services.yml +34 -0
- data/lib/active_storage/attached/macros.rb +86 -0
- data/lib/active_storage/attached/many.rb +51 -0
- data/lib/active_storage/attached/one.rb +56 -0
- data/lib/active_storage/attached.rb +38 -0
- data/lib/active_storage/engine.rb +81 -0
- data/lib/active_storage/gem_version.rb +15 -0
- data/lib/active_storage/log_subscriber.rb +48 -0
- data/lib/active_storage/messages_metadata.rb +64 -0
- data/lib/active_storage/migration.rb +27 -0
- data/lib/active_storage/patches/active_record.rb +19 -0
- data/lib/active_storage/patches/delegation.rb +98 -0
- data/lib/active_storage/patches/secure_random.rb +26 -0
- data/lib/active_storage/patches.rb +4 -0
- data/lib/active_storage/service/azure_service.rb +115 -0
- data/lib/active_storage/service/configurator.rb +28 -0
- data/lib/active_storage/service/disk_service.rb +124 -0
- data/lib/active_storage/service/gcs_service.rb +79 -0
- data/lib/active_storage/service/mirror_service.rb +46 -0
- data/lib/active_storage/service/s3_service.rb +96 -0
- data/lib/active_storage/service.rb +113 -0
- data/lib/active_storage/verifier.rb +113 -0
- data/lib/active_storage/version.rb +8 -0
- data/lib/active_storage.rb +34 -0
- data/lib/tasks/activestorage.rake +20 -0
- data/package.json +33 -0
- data/test/controllers/direct_uploads_controller_test.rb +123 -0
- data/test/controllers/disk_controller_test.rb +57 -0
- data/test/controllers/variants_controller_test.rb +21 -0
- data/test/database/create_users_migration.rb +7 -0
- data/test/database/setup.rb +6 -0
- data/test/dummy/Rakefile +3 -0
- data/test/dummy/app/assets/config/manifest.js +5 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/jobs/application_job.rb +2 -0
- data/test/dummy/app/models/application_record.rb +3 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/yarn +11 -0
- data/test/dummy/config/application.rb +22 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +49 -0
- data/test/dummy/config/environments/production.rb +82 -0
- data/test/dummy/config/environments/test.rb +33 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +6 -0
- data/test/dummy/config/initializers/assets.rb +14 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/secret_key.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/routes.rb +2 -0
- data/test/dummy/config/secrets.yml +32 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/config/storage_services.yml +3 -0
- data/test/dummy/config.ru +5 -0
- data/test/dummy/db/.keep +0 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/package.json +5 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/test/dummy/public/apple-touch-icon.png +0 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/filename_test.rb +36 -0
- data/test/fixtures/files/racecar.jpg +0 -0
- data/test/models/attachments_test.rb +122 -0
- data/test/models/blob_test.rb +47 -0
- data/test/models/variant_test.rb +27 -0
- data/test/service/.gitignore +1 -0
- data/test/service/azure_service_test.rb +14 -0
- data/test/service/configurations-example.yml +31 -0
- data/test/service/configurator_test.rb +14 -0
- data/test/service/disk_service_test.rb +12 -0
- data/test/service/gcs_service_test.rb +42 -0
- data/test/service/mirror_service_test.rb +62 -0
- data/test/service/s3_service_test.rb +52 -0
- data/test/service/shared_service_tests.rb +66 -0
- data/test/sidekiq/minitest_support.rb +6 -0
- data/test/support/assertions.rb +20 -0
- data/test/test_helper.rb +69 -0
- data/webpack.config.js +27 -0
- data/yarn.lock +3164 -0
- metadata +330 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'active_storage/messages_metadata'
|
|
3
|
+
|
|
4
|
+
class ActiveStorage::Verifier
|
|
5
|
+
class InvalidSignature < StandardError; end
|
|
6
|
+
|
|
7
|
+
def initialize(secret, options = {})
|
|
8
|
+
raise ArgumentError, "Secret should not be nil." unless secret
|
|
9
|
+
@secret = secret
|
|
10
|
+
@digest = options[:digest] || "SHA1"
|
|
11
|
+
@serializer = options[:serializer] || Marshal
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Checks if a signed message could have been generated by signing an object
|
|
15
|
+
# with the +MessageVerifier+'s secret.
|
|
16
|
+
#
|
|
17
|
+
# verifier = ActiveSupport::MessageVerifier.new 's3Krit'
|
|
18
|
+
# signed_message = verifier.generate 'a private message'
|
|
19
|
+
# verifier.valid_message?(signed_message) # => true
|
|
20
|
+
#
|
|
21
|
+
# tampered_message = signed_message.chop # editing the message invalidates the signature
|
|
22
|
+
# verifier.valid_message?(tampered_message) # => false
|
|
23
|
+
def valid_message?(signed_message)
|
|
24
|
+
return if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank?
|
|
25
|
+
|
|
26
|
+
data, digest = signed_message.split("--".freeze)
|
|
27
|
+
data.present? && digest.present? && security_class.secure_compare(digest, generate_digest(data))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Decodes the signed message using the +MessageVerifier+'s secret.
|
|
31
|
+
#
|
|
32
|
+
# verifier = ActiveSupport::MessageVerifier.new 's3Krit'
|
|
33
|
+
#
|
|
34
|
+
# signed_message = verifier.generate 'a private message'
|
|
35
|
+
# verifier.verified(signed_message) # => 'a private message'
|
|
36
|
+
#
|
|
37
|
+
# Returns +nil+ if the message was not signed with the same secret.
|
|
38
|
+
#
|
|
39
|
+
# other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
|
|
40
|
+
# other_verifier.verified(signed_message) # => nil
|
|
41
|
+
#
|
|
42
|
+
# Returns +nil+ if the message is not Base64-encoded.
|
|
43
|
+
#
|
|
44
|
+
# invalid_message = "f--46a0120593880c733a53b6dad75b42ddc1c8996d"
|
|
45
|
+
# verifier.verified(invalid_message) # => nil
|
|
46
|
+
#
|
|
47
|
+
# Raises any error raised while decoding the signed message.
|
|
48
|
+
#
|
|
49
|
+
# incompatible_message = "test--dad7b06c94abba8d46a15fafaef56c327665d5ff"
|
|
50
|
+
# verifier.verified(incompatible_message) # => TypeError: incompatible marshal file format
|
|
51
|
+
def verified(signed_message, purpose: nil, **)
|
|
52
|
+
if valid_message?(signed_message)
|
|
53
|
+
begin
|
|
54
|
+
data = signed_message.split("--".freeze)[0]
|
|
55
|
+
message = ActiveSupport::MessagesMetadata.verify(decode(data), purpose)
|
|
56
|
+
@serializer.load(message) if message
|
|
57
|
+
rescue ArgumentError => argument_error
|
|
58
|
+
return if argument_error.message.include?("invalid base64")
|
|
59
|
+
raise
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Decodes the signed message using the +MessageVerifier+'s secret.
|
|
65
|
+
#
|
|
66
|
+
# verifier = ActiveSupport::MessageVerifier.new 's3Krit'
|
|
67
|
+
# signed_message = verifier.generate 'a private message'
|
|
68
|
+
#
|
|
69
|
+
# verifier.verify(signed_message) # => 'a private message'
|
|
70
|
+
#
|
|
71
|
+
# Raises +InvalidSignature+ if the message was not signed with the same
|
|
72
|
+
# secret or was not Base64-encoded.
|
|
73
|
+
#
|
|
74
|
+
# other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
|
|
75
|
+
# other_verifier.verify(signed_message) # => ActiveSupport::MessageVerifier::InvalidSignature
|
|
76
|
+
def verify(*args)
|
|
77
|
+
verified(*args) || raise(InvalidSignature)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Generates a signed message for the provided value.
|
|
81
|
+
#
|
|
82
|
+
# The message is signed with the +MessageVerifier+'s secret. Without knowing
|
|
83
|
+
# the secret, the original value cannot be extracted from the message.
|
|
84
|
+
#
|
|
85
|
+
# verifier = ActiveSupport::MessageVerifier.new 's3Krit'
|
|
86
|
+
# verifier.generate 'a private message' # => "BAhJIhRwcml2YXRlLW1lc3NhZ2UGOgZFVA==--e2d724331ebdee96a10fb99b089508d1c72bd772"
|
|
87
|
+
def generate(value, expires_at: nil, expires_in: nil, purpose: nil)
|
|
88
|
+
data = encode(ActiveSupport::MessagesMetadata.wrap(@serializer.dump(value), expires_at: expires_at, expires_in: expires_in, purpose: purpose))
|
|
89
|
+
"#{data}--#{generate_digest(data)}"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
def encode(data)
|
|
94
|
+
::Base64.strict_encode64(data)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def security_class
|
|
98
|
+
if defined?(ActiveSupport::SecurityUtils)
|
|
99
|
+
ActiveSupport::SecurityUtils
|
|
100
|
+
else
|
|
101
|
+
Rack::Utils
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def decode(data)
|
|
106
|
+
::Base64.strict_decode64(data)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def generate_digest(data)
|
|
110
|
+
require "openssl" unless defined?(OpenSSL)
|
|
111
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@digest).new, @secret, data)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2017 David Heinemeier Hansson
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
5
|
+
# a copy of this software and associated documentation files (the
|
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
10
|
+
# the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be
|
|
13
|
+
# included in all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
|
+
#++
|
|
23
|
+
|
|
24
|
+
require "active_record"
|
|
25
|
+
require "active_storage/engine"
|
|
26
|
+
|
|
27
|
+
module ActiveStorage
|
|
28
|
+
extend ActiveSupport::Autoload
|
|
29
|
+
|
|
30
|
+
autoload :Attached
|
|
31
|
+
autoload :Service
|
|
32
|
+
|
|
33
|
+
mattr_accessor :verifier
|
|
34
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
|
|
3
|
+
namespace :activestorage do
|
|
4
|
+
desc "Copy over the migration needed to the application"
|
|
5
|
+
task :install do
|
|
6
|
+
FileUtils.mkdir_p Rails.root.join("storage")
|
|
7
|
+
FileUtils.mkdir_p Rails.root.join("tmp/storage")
|
|
8
|
+
puts "Made storage and tmp/storage directories for development and testing"
|
|
9
|
+
|
|
10
|
+
FileUtils.cp File.expand_path("../../../config/storage_services.yml", __FILE__), Rails.root.join("config")
|
|
11
|
+
puts "Copied default configuration to config/storage_services.yml"
|
|
12
|
+
|
|
13
|
+
migration_file_path = "db/migrate/#{Time.now.utc.strftime("%Y%m%d%H%M%S")}_active_storage_create_tables.rb"
|
|
14
|
+
FileUtils.mkdir_p Rails.root.join("db/migrate")
|
|
15
|
+
FileUtils.cp File.expand_path("../../active_storage/migration.rb", __FILE__), Rails.root.join(migration_file_path)
|
|
16
|
+
puts "Copied migration to #{migration_file_path}"
|
|
17
|
+
|
|
18
|
+
puts "Now run rails db:migrate to create the tables for Active Storage"
|
|
19
|
+
end
|
|
20
|
+
end
|
data/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "activestorage",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Attach cloud and local files in Rails applications",
|
|
5
|
+
"main": "app/assets/javascripts/activestorage.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"app/assets/javascripts/*.js"
|
|
8
|
+
],
|
|
9
|
+
"homepage": "https://github.com/rails/activestorage",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/rails/activestorage.git"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/rails/activestorage/issues"
|
|
16
|
+
},
|
|
17
|
+
"author": "Javan Makhmali <javan@javan.us>",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"babel-core": "^6.25.0",
|
|
21
|
+
"babel-loader": "^7.1.1",
|
|
22
|
+
"babel-preset-env": "^1.6.0",
|
|
23
|
+
"eslint": "^4.3.0",
|
|
24
|
+
"eslint-plugin-import": "^2.7.0",
|
|
25
|
+
"spark-md5": "^3.0.0",
|
|
26
|
+
"webpack": "^3.4.0"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"prebuild": "yarn lint",
|
|
30
|
+
"build": "webpack -p",
|
|
31
|
+
"lint": "eslint app/javascript"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
require "database/setup"
|
|
3
|
+
|
|
4
|
+
if SERVICE_CONFIGURATIONS[:s3]
|
|
5
|
+
class ActiveStorage::S3DirectUploadsControllerTest < ActionDispatch::IntegrationTest
|
|
6
|
+
setup do
|
|
7
|
+
@old_service = ActiveStorage::Blob.service
|
|
8
|
+
ActiveStorage::Blob.service = ActiveStorage::Service.configure(:s3, SERVICE_CONFIGURATIONS)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
teardown do
|
|
12
|
+
ActiveStorage::Blob.service = @old_service
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
test "creating new direct upload" do
|
|
16
|
+
checksum = Digest::MD5.base64digest("Hello")
|
|
17
|
+
|
|
18
|
+
post rails_direct_uploads_url, { blob: {
|
|
19
|
+
filename: "hello.txt", byte_size: 6, checksum: checksum, content_type: "text/plain" } }
|
|
20
|
+
|
|
21
|
+
parsed_body = JSON.parse(@response.body)
|
|
22
|
+
parsed_body.tap do |details|
|
|
23
|
+
assert_equal ActiveStorage::Blob.find(details["id"]), ActiveStorage::Blob.find_signed(details["signed_id"])
|
|
24
|
+
assert_equal "hello.txt", details["filename"]
|
|
25
|
+
assert_equal 6, details["byte_size"]
|
|
26
|
+
assert_equal checksum, details["checksum"]
|
|
27
|
+
assert_equal "text/plain", details["content_type"]
|
|
28
|
+
assert_match /#{SERVICE_CONFIGURATIONS[:s3][:bucket]}\.s3.(\S+)?amazonaws\.com/, details["direct_upload"]["url"]
|
|
29
|
+
assert_equal({ "Content-Type" => "text/plain", "Content-MD5" => checksum }, details["direct_upload"]["headers"])
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
else
|
|
34
|
+
puts "Skipping S3 Direct Upload tests because no S3 configuration was supplied"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if SERVICE_CONFIGURATIONS[:gcs]
|
|
38
|
+
class ActiveStorage::GCSDirectUploadsControllerTest < ActionDispatch::IntegrationTest
|
|
39
|
+
setup do
|
|
40
|
+
@config = SERVICE_CONFIGURATIONS[:gcs]
|
|
41
|
+
|
|
42
|
+
@old_service = ActiveStorage::Blob.service
|
|
43
|
+
ActiveStorage::Blob.service = ActiveStorage::Service.configure(:gcs, SERVICE_CONFIGURATIONS)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
teardown do
|
|
47
|
+
ActiveStorage::Blob.service = @old_service
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
test "creating new direct upload" do
|
|
51
|
+
checksum = Digest::MD5.base64digest("Hello")
|
|
52
|
+
|
|
53
|
+
post rails_direct_uploads_url, { blob: {
|
|
54
|
+
filename: "hello.txt", byte_size: 6, checksum: checksum, content_type: "text/plain" } }
|
|
55
|
+
|
|
56
|
+
parsed_body = JSON.parse(@response.body)
|
|
57
|
+
parsed_body.tap do |details|
|
|
58
|
+
assert_equal ActiveStorage::Blob.find(details["id"]), ActiveStorage::Blob.find_signed(details["signed_id"])
|
|
59
|
+
assert_equal "hello.txt", details["filename"]
|
|
60
|
+
assert_equal 6, details["byte_size"]
|
|
61
|
+
assert_equal checksum, details["checksum"]
|
|
62
|
+
assert_equal "text/plain", details["content_type"]
|
|
63
|
+
assert_match %r{storage\.googleapis\.com/#{@config[:bucket]}}, details["direct_upload"]["url"]
|
|
64
|
+
assert_equal({ "Content-Type" => "text/plain", "Content-MD5" => checksum }, details["direct_upload"]["headers"])
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
else
|
|
69
|
+
puts "Skipping GCS Direct Upload tests because no GCS configuration was supplied"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
if SERVICE_CONFIGURATIONS[:azure]
|
|
73
|
+
class ActiveStorage::AzureDirectUploadsControllerTest < ActionDispatch::IntegrationTest
|
|
74
|
+
setup do
|
|
75
|
+
@config = SERVICE_CONFIGURATIONS[:azure]
|
|
76
|
+
|
|
77
|
+
@old_service = ActiveStorage::Blob.service
|
|
78
|
+
ActiveStorage::Blob.service = ActiveStorage::Service.configure(:azure, SERVICE_CONFIGURATIONS)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
teardown do
|
|
82
|
+
ActiveStorage::Blob.service = @old_service
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
test "creating new direct upload" do
|
|
86
|
+
checksum = Digest::MD5.base64digest("Hello")
|
|
87
|
+
|
|
88
|
+
post rails_direct_uploads_url, { blob: {
|
|
89
|
+
filename: "hello.txt", byte_size: 6, checksum: checksum, content_type: "text/plain" } }
|
|
90
|
+
|
|
91
|
+
@response.parsed_body.tap do |details|
|
|
92
|
+
assert_equal ActiveStorage::Blob.find(details["id"]), ActiveStorage::Blob.find_signed(details["signed_id"])
|
|
93
|
+
assert_equal "hello.txt", details["filename"]
|
|
94
|
+
assert_equal 6, details["byte_size"]
|
|
95
|
+
assert_equal checksum, details["checksum"]
|
|
96
|
+
assert_equal "text/plain", details["content_type"]
|
|
97
|
+
assert_match %r{#{@config[:storage_account_name]}\.blob\.core\.windows\.net/#{@config[:container]}}, details["direct_upload"]["url"]
|
|
98
|
+
assert_equal({ "Content-Type" => "text/plain", "Content-MD5" => checksum, "x-ms-blob-type" => "BlockBlob" }, details["direct_upload"]["headers"])
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
else
|
|
103
|
+
puts "Skipping Azure Direct Upload tests because no Azure configuration was supplied"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
class ActiveStorage::DiskDirectUploadsControllerTest < ActionDispatch::IntegrationTest
|
|
107
|
+
test "creating new direct upload" do
|
|
108
|
+
checksum = Digest::MD5.base64digest("Hello")
|
|
109
|
+
|
|
110
|
+
post rails_direct_uploads_url, { blob: { filename: "hello.txt", byte_size: 6, checksum: checksum, content_type: "text/plain" } }
|
|
111
|
+
|
|
112
|
+
parsed_body = JSON.parse(@response.body)
|
|
113
|
+
parsed_body.tap do |details|
|
|
114
|
+
assert_equal ActiveStorage::Blob.find(details["id"]), ActiveStorage::Blob.find_signed(details["signed_id"])
|
|
115
|
+
assert_equal "hello.txt", details["filename"]
|
|
116
|
+
assert_equal 6, details["byte_size"]
|
|
117
|
+
assert_equal checksum, details["checksum"]
|
|
118
|
+
assert_equal "text/plain", details["content_type"]
|
|
119
|
+
assert_match /rails\/active_storage\/disk/, details["direct_upload"]["url"]
|
|
120
|
+
assert_equal({ "Content-Type" => "text/plain" }, details["direct_upload"]["headers"])
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
require "database/setup"
|
|
3
|
+
|
|
4
|
+
class ActiveStorage::DiskControllerTest < ActionDispatch::IntegrationTest
|
|
5
|
+
test "showing blob inline" do
|
|
6
|
+
blob = create_blob
|
|
7
|
+
|
|
8
|
+
get blob.service_url
|
|
9
|
+
assert_equal "inline; filename=\"#{blob.filename.base}\"", @response.headers["Content-Disposition"]
|
|
10
|
+
assert_equal "text/plain", @response.headers["Content-Type"]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
test "showing blob as attachment" do
|
|
14
|
+
blob = create_blob
|
|
15
|
+
|
|
16
|
+
get blob.service_url(disposition: :attachment)
|
|
17
|
+
assert_equal "attachment; filename=\"#{blob.filename.base}\"", @response.headers["Content-Disposition"]
|
|
18
|
+
assert_equal "text/plain", @response.headers["Content-Type"]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
test "directly uploading blob with integrity" do
|
|
23
|
+
data = "Something else entirely!"
|
|
24
|
+
blob = create_blob_before_direct_upload byte_size: data.size, checksum: Digest::MD5.base64digest(data)
|
|
25
|
+
|
|
26
|
+
put blob.service_url_for_direct_upload, data, { 'CONTENT_TYPE' => "text/plain" }
|
|
27
|
+
assert_response :no_content
|
|
28
|
+
assert_equal data, blob.download
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
test "directly uploading blob without integrity" do
|
|
32
|
+
data = "Something else entirely!"
|
|
33
|
+
blob = create_blob_before_direct_upload byte_size: data.size, checksum: Digest::MD5.base64digest("bad data")
|
|
34
|
+
|
|
35
|
+
put blob.service_url_for_direct_upload, data
|
|
36
|
+
assert_response :unprocessable_entity
|
|
37
|
+
assert_not blob.service.exist?(blob.key)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
test "directly uploading blob with mismatched content type" do
|
|
41
|
+
data = "Something else entirely!"
|
|
42
|
+
blob = create_blob_before_direct_upload byte_size: data.size, checksum: Digest::MD5.base64digest(data)
|
|
43
|
+
|
|
44
|
+
put blob.service_url_for_direct_upload, data, { 'CONTENT_TYPE' => "application/octet-stream" }
|
|
45
|
+
assert_response :unprocessable_entity
|
|
46
|
+
assert_not blob.service.exist?(blob.key)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
test "directly uploading blob with mismatched content length" do
|
|
50
|
+
data = "Something else entirely!"
|
|
51
|
+
blob = create_blob_before_direct_upload byte_size: data.size - 1, checksum: Digest::MD5.base64digest(data)
|
|
52
|
+
|
|
53
|
+
put blob.service_url_for_direct_upload, data, { 'CONTENT_TYPE' => "text/plain" }
|
|
54
|
+
assert_response :unprocessable_entity
|
|
55
|
+
assert_not blob.service.exist?(blob.key)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
require "database/setup"
|
|
3
|
+
|
|
4
|
+
class ActiveStorage::VariantsControllerTest < ActionDispatch::IntegrationTest
|
|
5
|
+
setup do
|
|
6
|
+
@blob = create_image_blob filename: "racecar.jpg"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
test "showing variant inline" do
|
|
10
|
+
get rails_blob_variation_url(
|
|
11
|
+
filename: @blob.filename,
|
|
12
|
+
signed_blob_id: @blob.signed_id,
|
|
13
|
+
variation_key: ActiveStorage::Variation.encode(resize: "100x100"))
|
|
14
|
+
|
|
15
|
+
assert_match /racecar\.jpg\?.*disposition=inline/, @response.redirect_url
|
|
16
|
+
|
|
17
|
+
image = read_image_variant(@blob.variant(resize: "100x100"))
|
|
18
|
+
assert_equal 100, image.width
|
|
19
|
+
assert_equal 67, image.height
|
|
20
|
+
end
|
|
21
|
+
end
|
data/test/dummy/Rakefile
ADDED
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
|
2
|
+
// listed below.
|
|
3
|
+
//
|
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
|
6
|
+
//
|
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
|
8
|
+
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
|
9
|
+
//
|
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
|
11
|
+
// about supported directives.
|
|
12
|
+
//
|
|
13
|
+
//= require_tree .
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
|
3
|
+
* listed below.
|
|
4
|
+
*
|
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
|
7
|
+
*
|
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
|
11
|
+
* It is generally better to create a new file per style scope.
|
|
12
|
+
*
|
|
13
|
+
*= require_tree .
|
|
14
|
+
*= require_self
|
|
15
|
+
*/
|
|
File without changes
|
|
File without changes
|
data/test/dummy/bin/rake
ADDED
data/test/dummy/bin/yarn
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
VENDOR_PATH = File.expand_path('..', __dir__)
|
|
3
|
+
Dir.chdir(VENDOR_PATH) do
|
|
4
|
+
begin
|
|
5
|
+
exec "yarnpkg #{ARGV.join(" ")}"
|
|
6
|
+
rescue Errno::ENOENT
|
|
7
|
+
$stderr.puts "Yarn executable was not detected in the system."
|
|
8
|
+
$stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
|
|
9
|
+
exit 1
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require_relative 'boot'
|
|
2
|
+
|
|
3
|
+
require "rails"
|
|
4
|
+
require "active_model/railtie"
|
|
5
|
+
require "active_record/railtie"
|
|
6
|
+
require "action_controller/railtie"
|
|
7
|
+
require "action_view/railtie"
|
|
8
|
+
require "sprockets/railtie"
|
|
9
|
+
#require "action_mailer/railtie"
|
|
10
|
+
#require "rails/test_unit/railtie"
|
|
11
|
+
#require "action_cable/engine"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
Bundler.require(*Rails.groups)
|
|
15
|
+
require "active_storage"
|
|
16
|
+
|
|
17
|
+
module Dummy
|
|
18
|
+
class Application < Rails::Application
|
|
19
|
+
# config.active_storage.service = :local
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# SQLite version 3.x
|
|
2
|
+
# gem install sqlite3
|
|
3
|
+
#
|
|
4
|
+
# Ensure the SQLite 3 gem is defined in your Gemfile
|
|
5
|
+
# gem 'sqlite3'
|
|
6
|
+
#
|
|
7
|
+
default: &default
|
|
8
|
+
adapter: sqlite3
|
|
9
|
+
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
|
10
|
+
timeout: 5000
|
|
11
|
+
|
|
12
|
+
development:
|
|
13
|
+
<<: *default
|
|
14
|
+
database: db/development.sqlite3
|
|
15
|
+
|
|
16
|
+
# Warning: The database defined as "test" will be erased and
|
|
17
|
+
# re-generated from your development database when you run "rake".
|
|
18
|
+
# Do not set this db to the same as development or production.
|
|
19
|
+
test:
|
|
20
|
+
<<: *default
|
|
21
|
+
database: db/test.sqlite3
|
|
22
|
+
|
|
23
|
+
production:
|
|
24
|
+
<<: *default
|
|
25
|
+
database: db/production.sqlite3
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Dummy::Application.configure do
|
|
2
|
+
# Settings specified here will take precedence over those in config/application.rb.
|
|
3
|
+
|
|
4
|
+
# In the development environment your application's code is reloaded on
|
|
5
|
+
# every request. This slows down response time but is perfect for development
|
|
6
|
+
# since you don't have to restart the web server when you make code changes.
|
|
7
|
+
config.cache_classes = false
|
|
8
|
+
|
|
9
|
+
# Do not eager load code on boot.
|
|
10
|
+
config.eager_load = false
|
|
11
|
+
|
|
12
|
+
# Show full error reports.
|
|
13
|
+
config.consider_all_requests_local = true
|
|
14
|
+
|
|
15
|
+
# Enable/disable caching. By default caching is disabled.
|
|
16
|
+
if Rails.root.join('tmp/caching-dev.txt').exist?
|
|
17
|
+
config.action_controller.perform_caching = true
|
|
18
|
+
|
|
19
|
+
config.cache_store = :memory_store
|
|
20
|
+
config.public_file_server.headers = {
|
|
21
|
+
'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}"
|
|
22
|
+
}
|
|
23
|
+
else
|
|
24
|
+
config.action_controller.perform_caching = false
|
|
25
|
+
|
|
26
|
+
config.cache_store = :null_store
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Print deprecation notices to the Rails logger.
|
|
30
|
+
config.active_support.deprecation = :log
|
|
31
|
+
|
|
32
|
+
# Raise an error on page load if there are pending migrations.
|
|
33
|
+
# config.active_record.migration_error = :page_load
|
|
34
|
+
|
|
35
|
+
# Debug mode disables concatenation and preprocessing of assets.
|
|
36
|
+
# This option may cause significant delays in view rendering with a large
|
|
37
|
+
# number of complex assets.
|
|
38
|
+
config.assets.debug = true
|
|
39
|
+
|
|
40
|
+
# Suppress logger output for asset requests.
|
|
41
|
+
config.assets.quiet = true
|
|
42
|
+
|
|
43
|
+
# Raises error for missing translations
|
|
44
|
+
# config.action_view.raise_on_missing_translations = true
|
|
45
|
+
|
|
46
|
+
# Use an evented file watcher to asynchronously detect changes in source code,
|
|
47
|
+
# routes, locales, etc. This feature depends on the listen gem.
|
|
48
|
+
# config.file_watcher = ActiveSupport::EventedFileUpdateChecker
|
|
49
|
+
end
|