Skip to content

Commit

Permalink
Add support for ActiveStorage expiring URLs
Browse files Browse the repository at this point in the history
  • Loading branch information
aki77 committed Jun 14, 2021
1 parent 051b0fc commit 209a79a
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 4 deletions.
13 changes: 13 additions & 0 deletions activestorage/CHANGELOG.md
@@ -1,3 +1,16 @@

* Add support for ActiveStorage expiring URLs.

```ruby
rails_blob_path(user.avatar, disposition: "attachment", expires_in: 30.minutes)

<%= image_tag rails_blob_path(user.avatar.variant(resize: "100x100"), expires_in: 30.minutes) %>
```
If you want to set default expiration time for ActiveStorage URLs throughout your application, set `config.active_storage.urls_expire_in`.
*aki77*
* Allow to purge an attachment when record is not persisted for `has_many_attached`
*Jacopo Beschi*
Expand Down
12 changes: 8 additions & 4 deletions activestorage/config/routes.rb
Expand Up @@ -31,15 +31,17 @@
resolve("ActiveStorage::Attachment") { |attachment, options| route_for(ActiveStorage.resolve_model_to_route, attachment.blob, options) }

direct :rails_storage_proxy do |model, options|
expires_in = options.delete(:expires_in) { ActiveStorage.urls_expire_in }

if model.respond_to?(:signed_id)
route_for(
:rails_service_blob_proxy,
model.signed_id,
model.signed_id(expires_in: expires_in),
model.filename,
options
)
else
signed_blob_id = model.blob.signed_id
signed_blob_id = model.blob.signed_id(expires_in: expires_in)
variation_key = model.variation.key
filename = model.blob.filename

Expand All @@ -54,15 +56,17 @@
end

direct :rails_storage_redirect do |model, options|
expires_in = options.delete(:expires_in) { ActiveStorage.urls_expire_in }

if model.respond_to?(:signed_id)
route_for(
:rails_service_blob,
model.signed_id,
model.signed_id(expires_in: expires_in),
model.filename,
options
)
else
signed_blob_id = model.blob.signed_id
signed_blob_id = model.blob.signed_id(expires_in: expires_in)
variation_key = model.variation.key
filename = model.blob.filename

Expand Down
1 change: 1 addition & 0 deletions activestorage/lib/active_storage.rb
Expand Up @@ -60,6 +60,7 @@ module ActiveStorage
mattr_accessor :content_types_allowed_inline, default: []

mattr_accessor :service_urls_expire_in, default: 5.minutes
mattr_accessor :urls_expire_in

mattr_accessor :routes_prefix, default: "/rails/active_storage"
mattr_accessor :draw_routes, default: true
Expand Down
1 change: 1 addition & 0 deletions activestorage/lib/active_storage/engine.rb
Expand Up @@ -91,6 +91,7 @@ class Engine < Rails::Engine # :nodoc:
ActiveStorage.web_image_content_types = app.config.active_storage.web_image_content_types || []
ActiveStorage.content_types_to_serve_as_binary = app.config.active_storage.content_types_to_serve_as_binary || []
ActiveStorage.service_urls_expire_in = app.config.active_storage.service_urls_expire_in || 5.minutes
ActiveStorage.urls_expire_in = app.config.active_storage.urls_expire_in
ActiveStorage.content_types_allowed_inline = app.config.active_storage.content_types_allowed_inline || []
ActiveStorage.binary_content_type = app.config.active_storage.binary_content_type || "application/octet-stream"

Expand Down
35 changes: 35 additions & 0 deletions activestorage/test/controllers/blobs/proxy_controller_test.rb
Expand Up @@ -24,4 +24,39 @@ class ActiveStorage::Blobs::ProxyControllerTest < ActionDispatch::IntegrationTes
get rails_storage_proxy_url(create_blob(content_type: "application/zip"))
assert_match(/^attachment; /, response.headers["Content-Disposition"])
end

test "signed ID within expiration date" do
get rails_storage_proxy_url(create_file_blob(filename: "racecar.jpg"), expires_in: 1.minute)
assert_response :success
end

test "Expired signed ID" do
url = rails_storage_proxy_url(create_file_blob(filename: "racecar.jpg"), expires_in: 1.minute)
travel 2.minutes
get url
assert_response :not_found
end
end

class ActiveStorage::Blobs::ExpiringProxyControllerTest < ActionDispatch::IntegrationTest
setup do
@old_urls_expire_in = ActiveStorage.urls_expire_in
ActiveStorage.urls_expire_in = 1.minutes
end

teardown do
ActiveStorage.urls_expire_in = @old_urls_expire_in
end

test "signed ID within expiration date" do
get rails_storage_proxy_url(create_file_blob(filename: "racecar.jpg"))
assert_response :success
end

test "Expired signed ID" do
url = rails_storage_proxy_url(create_file_blob(filename: "racecar.jpg"))
travel 2.minutes
get url
assert_response :not_found
end
end
36 changes: 36 additions & 0 deletions activestorage/test/controllers/blobs/redirect_controller_test.rb
Expand Up @@ -18,4 +18,40 @@ class ActiveStorage::Blobs::RedirectControllerTest < ActionDispatch::Integration
assert_redirected_to(/racecar\.jpg/)
assert_equal "max-age=300, private", response.headers["Cache-Control"]
end

test "signed ID within expiration date" do
get rails_storage_redirect_url(@blob, expires_in: 1.minute)
assert_redirected_to(/racecar\.jpg/)
end

test "Expired signed ID" do
url = rails_storage_redirect_url(@blob, expires_in: 1.minute)
travel 2.minutes
get url
assert_response :not_found
end
end

class ActiveStorage::Blobs::ExpiringRedirectControllerTest < ActionDispatch::IntegrationTest
setup do
@blob = create_file_blob filename: "racecar.jpg"
@old_urls_expire_in = ActiveStorage.urls_expire_in
ActiveStorage.urls_expire_in = 1.minutes
end

teardown do
ActiveStorage.urls_expire_in = @old_urls_expire_in
end

test "signed ID within expiration date" do
get rails_storage_redirect_url(@blob)
assert_redirected_to(/racecar\.jpg/)
end

test "Expired signed ID" do
url = rails_storage_redirect_url(@blob)
travel 2.minutes
get url
assert_response :not_found
end
end
2 changes: 2 additions & 0 deletions guides/source/configuring.md
Expand Up @@ -1041,6 +1041,8 @@ text/javascript image/svg+xml application/postscript application/x-shockwave-fla

The default is 5 minutes.

* `config.active_storage.urls_expire_in` determines the default expiry of URLs in the Rails application generated by Active Storage. The default is nil.

* `config.active_storage.routes_prefix` can be used to set the route prefix for the routes served by Active Storage. Accepts a string that will be prepended to the generated routes.

```ruby
Expand Down

0 comments on commit 209a79a

Please sign in to comment.