activestorage 0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activestorage might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +235 -0
- data/MIT-LICENSE +20 -0
- data/README.md +76 -0
- data/Rakefile +11 -0
- data/activestorage.gemspec +21 -0
- data/lib/active_storage.rb +9 -0
- data/lib/active_storage/attached.rb +34 -0
- data/lib/active_storage/attached/macros.rb +23 -0
- data/lib/active_storage/attached/many.rb +31 -0
- data/lib/active_storage/attached/one.rb +29 -0
- data/lib/active_storage/attachment.rb +30 -0
- data/lib/active_storage/blob.rb +80 -0
- data/lib/active_storage/disk_controller.rb +28 -0
- data/lib/active_storage/download.rb +90 -0
- data/lib/active_storage/filename.rb +31 -0
- data/lib/active_storage/migration.rb +28 -0
- data/lib/active_storage/purge_job.rb +10 -0
- data/lib/active_storage/railtie.rb +56 -0
- data/lib/active_storage/service.rb +34 -0
- data/lib/active_storage/service/disk_service.rb +70 -0
- data/lib/active_storage/service/gcs_service.rb +41 -0
- data/lib/active_storage/service/mirror_service.rb +34 -0
- data/lib/active_storage/service/s3_service.rb +55 -0
- data/lib/active_storage/storage_services.yml +27 -0
- data/lib/active_storage/verified_key_with_expiration.rb +24 -0
- data/lib/tasks/activestorage.rake +19 -0
- data/test/attachments_test.rb +95 -0
- data/test/blob_test.rb +28 -0
- data/test/database/create_users_migration.rb +7 -0
- data/test/database/setup.rb +6 -0
- data/test/disk_controller_test.rb +34 -0
- data/test/filename_test.rb +36 -0
- data/test/service/.gitignore +1 -0
- data/test/service/configurations-example.yml +11 -0
- data/test/service/disk_service_test.rb +8 -0
- data/test/service/gcs_service_test.rb +20 -0
- data/test/service/mirror_service_test.rb +50 -0
- data/test/service/s3_service_test.rb +11 -0
- data/test/service/shared_service_tests.rb +68 -0
- data/test/test_helper.rb +28 -0
- data/test/verified_key_with_expiration_test.rb +19 -0
- metadata +171 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ActiveStorage::FilenameTest < ActiveSupport::TestCase
|
4
|
+
test "sanitize" do
|
5
|
+
"%$|:;/\t\r\n\\".each_char do |character|
|
6
|
+
filename = ActiveStorage::Filename.new("foo#{character}bar.pdf")
|
7
|
+
assert_equal 'foo-bar.pdf', filename.sanitized
|
8
|
+
assert_equal 'foo-bar.pdf', filename.to_s
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
test "sanitize transcodes to valid UTF-8" do
|
13
|
+
{ "\xF6".force_encoding(Encoding::ISO8859_1) => "ö",
|
14
|
+
"\xC3".force_encoding(Encoding::ISO8859_1) => "Ã",
|
15
|
+
"\xAD" => "�",
|
16
|
+
"\xCF" => "�",
|
17
|
+
"\x00" => "",
|
18
|
+
}.each do |actual, expected|
|
19
|
+
assert_equal expected, ActiveStorage::Filename.new(actual).sanitized
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
test "strips RTL override chars used to spoof unsafe executables as docs" do
|
24
|
+
# Would be displayed in Windows as "evilexe.pdf" due to the right-to-left
|
25
|
+
# (RTL) override char!
|
26
|
+
assert_equal 'evil-fdp.exe', ActiveStorage::Filename.new("evil\u{202E}fdp.exe").sanitized
|
27
|
+
end
|
28
|
+
|
29
|
+
test "compare case-insensitively" do
|
30
|
+
assert_operator ActiveStorage::Filename.new('foobar.pdf'), :==, ActiveStorage::Filename.new('FooBar.PDF')
|
31
|
+
end
|
32
|
+
|
33
|
+
test "compare sanitized" do
|
34
|
+
assert_operator ActiveStorage::Filename.new('foo-bar.pdf'), :==, ActiveStorage::Filename.new("foo\tbar.pdf")
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
configurations.yml
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require "tmpdir"
|
2
|
+
require "service/shared_service_tests"
|
3
|
+
|
4
|
+
class ActiveStorage::Service::DiskServiceTest < ActiveSupport::TestCase
|
5
|
+
SERVICE = ActiveStorage::Service.configure(:Disk, root: File.join(Dir.tmpdir, "active_storage"))
|
6
|
+
|
7
|
+
include ActiveStorage::Service::SharedServiceTests
|
8
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "service/shared_service_tests"
|
2
|
+
|
3
|
+
if SERVICE_CONFIGURATIONS[:gcs]
|
4
|
+
class ActiveStorage::Service::GCSServiceTest < ActiveSupport::TestCase
|
5
|
+
SERVICE = ActiveStorage::Service.configure(:GCS, SERVICE_CONFIGURATIONS[:gcs])
|
6
|
+
|
7
|
+
include ActiveStorage::Service::SharedServiceTests
|
8
|
+
|
9
|
+
test "signed URL generation" do
|
10
|
+
travel_to Time.now do
|
11
|
+
url = SERVICE.bucket.signed_url(FIXTURE_KEY, expires: 120) +
|
12
|
+
"&response-content-disposition=inline%3B+filename%3D%22test.txt%22"
|
13
|
+
|
14
|
+
assert_equal url, @service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
else
|
19
|
+
puts "Skipping GCS Service tests because no GCS configuration was supplied"
|
20
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "tmpdir"
|
2
|
+
require "service/shared_service_tests"
|
3
|
+
|
4
|
+
class ActiveStorage::Service::MirrorServiceTest < ActiveSupport::TestCase
|
5
|
+
PRIMARY_DISK_SERVICE = ActiveStorage::Service.configure(:Disk, root: File.join(Dir.tmpdir, "active_storage"))
|
6
|
+
SECONDARY_DISK_SERVICE = ActiveStorage::Service.configure(:Disk, root: File.join(Dir.tmpdir, "active_storage_mirror"))
|
7
|
+
|
8
|
+
SERVICE = ActiveStorage::Service.configure :Mirror, services: [ PRIMARY_DISK_SERVICE, SECONDARY_DISK_SERVICE ]
|
9
|
+
|
10
|
+
include ActiveStorage::Service::SharedServiceTests
|
11
|
+
|
12
|
+
test "uploading to all services" do
|
13
|
+
begin
|
14
|
+
data = "Something else entirely!"
|
15
|
+
key = upload(data, to: @service)
|
16
|
+
|
17
|
+
assert_equal data, PRIMARY_DISK_SERVICE.download(key)
|
18
|
+
assert_equal data, SECONDARY_DISK_SERVICE.download(key)
|
19
|
+
ensure
|
20
|
+
@service.delete key
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
test "downloading from primary service" do
|
25
|
+
data = "Something else entirely!"
|
26
|
+
key = upload(data, to: PRIMARY_DISK_SERVICE)
|
27
|
+
|
28
|
+
assert_equal data, @service.download(key)
|
29
|
+
end
|
30
|
+
|
31
|
+
test "deleting from all services" do
|
32
|
+
@service.delete FIXTURE_KEY
|
33
|
+
assert_not PRIMARY_DISK_SERVICE.exist?(FIXTURE_KEY)
|
34
|
+
assert_not SECONDARY_DISK_SERVICE.exist?(FIXTURE_KEY)
|
35
|
+
end
|
36
|
+
|
37
|
+
test "URL generation in primary service" do
|
38
|
+
travel_to Time.now do
|
39
|
+
assert_equal PRIMARY_DISK_SERVICE.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt"),
|
40
|
+
@service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def upload(data, to:)
|
46
|
+
SecureRandom.base58(24).tap do |key|
|
47
|
+
@service.upload key, StringIO.new(data), checksum: Digest::MD5.base64digest(data)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require "service/shared_service_tests"
|
2
|
+
|
3
|
+
if SERVICE_CONFIGURATIONS[:s3]
|
4
|
+
class ActiveStorage::Service::S3ServiceTest < ActiveSupport::TestCase
|
5
|
+
SERVICE = ActiveStorage::Service.configure(:S3, SERVICE_CONFIGURATIONS[:s3])
|
6
|
+
|
7
|
+
include ActiveStorage::Service::SharedServiceTests
|
8
|
+
end
|
9
|
+
else
|
10
|
+
puts "Skipping S3 Service tests because no S3 configuration was supplied"
|
11
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "active_support/core_ext/securerandom"
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
SERVICE_CONFIGURATIONS = begin
|
6
|
+
YAML.load_file(File.expand_path("../configurations.yml", __FILE__)).deep_symbolize_keys
|
7
|
+
rescue Errno::ENOENT
|
8
|
+
puts "Missing service configuration file in test/services/configurations.yml"
|
9
|
+
end
|
10
|
+
|
11
|
+
module ActiveStorage::Service::SharedServiceTests
|
12
|
+
extend ActiveSupport::Concern
|
13
|
+
|
14
|
+
FIXTURE_KEY = SecureRandom.base58(24)
|
15
|
+
FIXTURE_FILE = StringIO.new("Hello world!")
|
16
|
+
|
17
|
+
included do
|
18
|
+
setup do
|
19
|
+
@service = self.class.const_get(:SERVICE)
|
20
|
+
@service.upload FIXTURE_KEY, FIXTURE_FILE
|
21
|
+
FIXTURE_FILE.rewind
|
22
|
+
end
|
23
|
+
|
24
|
+
teardown do
|
25
|
+
@service.delete FIXTURE_KEY
|
26
|
+
FIXTURE_FILE.rewind
|
27
|
+
end
|
28
|
+
|
29
|
+
test "uploading with integrity" do
|
30
|
+
begin
|
31
|
+
key = SecureRandom.base58(24)
|
32
|
+
data = "Something else entirely!"
|
33
|
+
@service.upload(key, StringIO.new(data), checksum: Digest::MD5.base64digest(data))
|
34
|
+
|
35
|
+
assert_equal data, @service.download(key)
|
36
|
+
ensure
|
37
|
+
@service.delete key
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
test "upload without integrity" do
|
42
|
+
begin
|
43
|
+
key = SecureRandom.base58(24)
|
44
|
+
data = "Something else entirely!"
|
45
|
+
|
46
|
+
assert_raises(ActiveStorage::IntegrityError) do
|
47
|
+
@service.upload(key, StringIO.new(data), checksum: "BAD_CHECKSUM")
|
48
|
+
end
|
49
|
+
ensure
|
50
|
+
@service.delete key
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
test "downloading" do
|
55
|
+
assert_equal FIXTURE_FILE.read, @service.download(FIXTURE_KEY)
|
56
|
+
end
|
57
|
+
|
58
|
+
test "existing" do
|
59
|
+
assert @service.exist?(FIXTURE_KEY)
|
60
|
+
assert_not @service.exist?(FIXTURE_KEY + "nonsense")
|
61
|
+
end
|
62
|
+
|
63
|
+
test "deleting" do
|
64
|
+
@service.delete FIXTURE_KEY
|
65
|
+
assert_not @service.exist?(FIXTURE_KEY)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
require "active_support"
|
3
|
+
require "active_support/test_case"
|
4
|
+
require "active_support/testing/autorun"
|
5
|
+
require "byebug"
|
6
|
+
|
7
|
+
require "active_storage"
|
8
|
+
|
9
|
+
require "active_storage/service"
|
10
|
+
ActiveStorage::Blob.service = ActiveStorage::Service.configure(:Disk, root: File.join(Dir.tmpdir, "active_storage"))
|
11
|
+
|
12
|
+
require "active_storage/verified_key_with_expiration"
|
13
|
+
ActiveStorage::VerifiedKeyWithExpiration.verifier = ActiveSupport::MessageVerifier.new("Testing")
|
14
|
+
|
15
|
+
class ActiveSupport::TestCase
|
16
|
+
private
|
17
|
+
def create_blob(data: "Hello world!", filename: "hello.txt", content_type: "text/plain")
|
18
|
+
ActiveStorage::Blob.create_after_upload! io: StringIO.new(data), filename: filename, content_type: content_type
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
require "active_storage/attached"
|
24
|
+
ActiveRecord::Base.send :extend, ActiveStorage::Attached::Macros
|
25
|
+
|
26
|
+
require "global_id"
|
27
|
+
GlobalID.app = "ActiveStorageExampleApp"
|
28
|
+
ActiveRecord::Base.send :include, GlobalID::Identification
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "active_support/core_ext/securerandom"
|
3
|
+
|
4
|
+
class ActiveStorage::VerifiedKeyWithExpirationTest < ActiveSupport::TestCase
|
5
|
+
FIXTURE_KEY = SecureRandom.base58(24)
|
6
|
+
|
7
|
+
test "without expiration" do
|
8
|
+
encoded_key = ActiveStorage::VerifiedKeyWithExpiration.encode(FIXTURE_KEY)
|
9
|
+
assert_equal FIXTURE_KEY, ActiveStorage::VerifiedKeyWithExpiration.decode(encoded_key)
|
10
|
+
end
|
11
|
+
|
12
|
+
test "with expiration" do
|
13
|
+
encoded_key = ActiveStorage::VerifiedKeyWithExpiration.encode(FIXTURE_KEY, expires_in: 1.minute)
|
14
|
+
assert_equal FIXTURE_KEY, ActiveStorage::VerifiedKeyWithExpiration.decode(encoded_key)
|
15
|
+
|
16
|
+
travel 2.minutes
|
17
|
+
assert_nil ActiveStorage::VerifiedKeyWithExpiration.decode(encoded_key)
|
18
|
+
end
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activestorage
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Heinemeier Hansson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-07-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activerecord
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: actionpack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activejob
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.15'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.15'
|
83
|
+
description:
|
84
|
+
email: david@basecamp.com
|
85
|
+
executables: []
|
86
|
+
extensions: []
|
87
|
+
extra_rdoc_files: []
|
88
|
+
files:
|
89
|
+
- ".gitignore"
|
90
|
+
- Gemfile
|
91
|
+
- Gemfile.lock
|
92
|
+
- MIT-LICENSE
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- activestorage.gemspec
|
96
|
+
- lib/active_storage.rb
|
97
|
+
- lib/active_storage/attached.rb
|
98
|
+
- lib/active_storage/attached/macros.rb
|
99
|
+
- lib/active_storage/attached/many.rb
|
100
|
+
- lib/active_storage/attached/one.rb
|
101
|
+
- lib/active_storage/attachment.rb
|
102
|
+
- lib/active_storage/blob.rb
|
103
|
+
- lib/active_storage/disk_controller.rb
|
104
|
+
- lib/active_storage/download.rb
|
105
|
+
- lib/active_storage/filename.rb
|
106
|
+
- lib/active_storage/migration.rb
|
107
|
+
- lib/active_storage/purge_job.rb
|
108
|
+
- lib/active_storage/railtie.rb
|
109
|
+
- lib/active_storage/service.rb
|
110
|
+
- lib/active_storage/service/disk_service.rb
|
111
|
+
- lib/active_storage/service/gcs_service.rb
|
112
|
+
- lib/active_storage/service/mirror_service.rb
|
113
|
+
- lib/active_storage/service/s3_service.rb
|
114
|
+
- lib/active_storage/storage_services.yml
|
115
|
+
- lib/active_storage/verified_key_with_expiration.rb
|
116
|
+
- lib/tasks/activestorage.rake
|
117
|
+
- test/attachments_test.rb
|
118
|
+
- test/blob_test.rb
|
119
|
+
- test/database/create_users_migration.rb
|
120
|
+
- test/database/setup.rb
|
121
|
+
- test/disk_controller_test.rb
|
122
|
+
- test/filename_test.rb
|
123
|
+
- test/service/.gitignore
|
124
|
+
- test/service/configurations-example.yml
|
125
|
+
- test/service/disk_service_test.rb
|
126
|
+
- test/service/gcs_service_test.rb
|
127
|
+
- test/service/mirror_service_test.rb
|
128
|
+
- test/service/s3_service_test.rb
|
129
|
+
- test/service/shared_service_tests.rb
|
130
|
+
- test/test_helper.rb
|
131
|
+
- test/verified_key_with_expiration_test.rb
|
132
|
+
homepage: https://github.com/rails/activestorage
|
133
|
+
licenses:
|
134
|
+
- MIT
|
135
|
+
metadata: {}
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: 2.3.0
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubyforge_project:
|
152
|
+
rubygems_version: 2.6.11
|
153
|
+
signing_key:
|
154
|
+
specification_version: 4
|
155
|
+
summary: Attach cloud and local files in Rails applications
|
156
|
+
test_files:
|
157
|
+
- test/attachments_test.rb
|
158
|
+
- test/blob_test.rb
|
159
|
+
- test/database/create_users_migration.rb
|
160
|
+
- test/database/setup.rb
|
161
|
+
- test/disk_controller_test.rb
|
162
|
+
- test/filename_test.rb
|
163
|
+
- test/service/.gitignore
|
164
|
+
- test/service/configurations-example.yml
|
165
|
+
- test/service/disk_service_test.rb
|
166
|
+
- test/service/gcs_service_test.rb
|
167
|
+
- test/service/mirror_service_test.rb
|
168
|
+
- test/service/s3_service_test.rb
|
169
|
+
- test/service/shared_service_tests.rb
|
170
|
+
- test/test_helper.rb
|
171
|
+
- test/verified_key_with_expiration_test.rb
|