activestorage_legacy 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|