active_storage_authorization 0.1.0
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/CHANGELOG.md +14 -0
- data/MIT-LICENSE +20 -0
- data/README.md +109 -0
- data/SECURITY.md +7 -0
- data/lib/active_storage_authorization/attachment.rb +25 -0
- data/lib/active_storage_authorization/authorization_header_guard.rb +20 -0
- data/lib/active_storage_authorization/blob.rb +19 -0
- data/lib/active_storage_authorization/current_user.rb +13 -0
- data/lib/active_storage_authorization/direct_upload_guard.rb +34 -0
- data/lib/active_storage_authorization/railtie.rb +14 -0
- data/lib/active_storage_authorization/serving_guard.rb +38 -0
- data/lib/active_storage_authorization/version.rb +5 -0
- data/lib/active_storage_authorization.rb +65 -0
- metadata +104 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 053ec2ae3fec299ec341ae8699d65fdfc141dd7fb6f802eefd1ef680d365ce92
|
|
4
|
+
data.tar.gz: 463bd945a98c7f8ee72e7da2305c7001b03e43308a272cec985bd99fc236e625
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c1d11e3aac7c3dd8eb907d11769302dc51ccf965a9b0b0f7f180a340cd9d7ecb94e0cbc5df6cab059facf585f3c1e0ef76f226081263c2a302d99e1863d0a637
|
|
7
|
+
data.tar.gz: f8ce24cdb7cee24a9cfd4829ef0a84cbfc5c4da4f703c1ae4adc7b5163430cec7221ec99c9cb89fb6d6c4c89c29a2ad31cfe0dc6a782edfe46595b0251de6558
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2026-11-15
|
|
6
|
+
|
|
7
|
+
- Default-deny serving for blobs and representations, with records opting in
|
|
8
|
+
through `active_storage_publicly_accessible?` and
|
|
9
|
+
`active_storage_accessible_to?(user, session:)`.
|
|
10
|
+
- `Cache-Control: no-store` on private responses.
|
|
11
|
+
- Files embedded in rich text inherit the access of the owning record.
|
|
12
|
+
- Direct uploads: `:signed_in` (stamps the uploader, who alone can read the
|
|
13
|
+
blob until it is attached) or `:deny`, with a per-IP rate limit.
|
|
14
|
+
- Optional refusal of requests that carry an `Authorization` header.
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2026 Yaroslav Shmarov
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# active_storage_authorization
|
|
2
|
+
|
|
3
|
+
Default-deny file serving for Active Storage.
|
|
4
|
+
|
|
5
|
+
Out of the box, Rails serves any Active Storage file to anyone who has its URL, and URLs leak: browser history, shared links, emails, logs. This gem flips the default. A file is served only when a record it is attached to says so:
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
class Invoice < ApplicationRecord
|
|
9
|
+
has_one_attached :pdf
|
|
10
|
+
|
|
11
|
+
def active_storage_accessible_to?(user, session:)
|
|
12
|
+
user.present? && organization.members.include?(user)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class User < ApplicationRecord
|
|
17
|
+
has_one_attached :avatar
|
|
18
|
+
|
|
19
|
+
def active_storage_publicly_accessible? = true
|
|
20
|
+
end
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
A blob is served when **any** record it's attached to returns true from `active_storage_publicly_accessible?`, or from `active_storage_accessible_to?(current_user, session:)`. Otherwise the response is `403`.
|
|
24
|
+
|
|
25
|
+
What else it does:
|
|
26
|
+
|
|
27
|
+
- **Private responses are never cached.** They get `Cache-Control: no-store`, so neither the bytes (proxy mode) nor the signed storage URL (redirect mode) outlive the access check. Public files keep Rails' long-lived public caching.
|
|
28
|
+
- **Rich text follows its owner.** A file embedded in Action Text or [Lexxy](https://github.com/basecamp/lexxy) is attached to an `ActionText::RichText` row; it takes the access of the model that owns that rich text.
|
|
29
|
+
- **Direct uploads require a signed-in user**, are rate limited per IP, and record who uploaded each blob. Until the form is saved, a direct upload has no attachment, so only its uploader can read it (editors preview uploads immediately). Or turn direct uploads off entirely.
|
|
30
|
+
- **Optionally refuses requests that carry an `Authorization` header**, so an API bearer token can never ride on a browser session cookie to reach a file.
|
|
31
|
+
|
|
32
|
+
Works with Rails 7.2+ and both proxy and redirect serving.
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
# Gemfile
|
|
38
|
+
gem "active_storage_authorization"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
# config/initializers/active_storage_authorization.rb
|
|
43
|
+
ActiveStorageAuthorization.configure do |config|
|
|
44
|
+
# Required. Runs inside the Active Storage controller (cookies, session and
|
|
45
|
+
# request are available) and returns the signed-in user or nil. Active Storage
|
|
46
|
+
# controllers don't inherit from your ApplicationController, so resolve the
|
|
47
|
+
# user here the way your authentication does.
|
|
48
|
+
config.current_user = -> { Current.user || Session.find_by(id: cookies.signed[:session_id])&.user }
|
|
49
|
+
|
|
50
|
+
# :signed_in (default) or :deny.
|
|
51
|
+
# config.direct_uploads = :deny
|
|
52
|
+
|
|
53
|
+
# Per-IP limit on minting direct uploads. nil turns it off. Read at boot.
|
|
54
|
+
# Uses the controller cache store, so it needs a real cache (not :null_store).
|
|
55
|
+
# config.direct_upload_rate_limit = { to: 60, within: 10.minutes }
|
|
56
|
+
|
|
57
|
+
# config.reject_authorization_header = true
|
|
58
|
+
end
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Then opt records in. Until a record defines one of the two methods, its files are refused, which is the point, but check your public assets (avatars, logos, banners, Open Graph images) before deploying.
|
|
62
|
+
|
|
63
|
+
## The record API
|
|
64
|
+
|
|
65
|
+
| Method | Called with | Return true to |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| `active_storage_publicly_accessible?` | nothing | serve to everyone, with public caching |
|
|
68
|
+
| `active_storage_accessible_to?(user, session:)` | the result of `config.current_user` (may be nil), and the Rails session | serve to this viewer, uncached |
|
|
69
|
+
|
|
70
|
+
Define either, both, or neither. `session:` exists for access that isn't tied to a user account, such as an anonymous reporter holding a case key in their session. Use `**` if you don't need it:
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
def active_storage_accessible_to?(user, **) = user == author
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Settings
|
|
77
|
+
|
|
78
|
+
| Setting | Default | |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `current_user` | `-> {}` | Resolves the viewer. With the default, only public files are served. |
|
|
81
|
+
| `direct_uploads` | `:signed_in` | `:deny` refuses every direct upload (forms that upload with the record still work). |
|
|
82
|
+
| `direct_upload_rate_limit` | `{ to: 60, within: 10.minutes }` | `nil` turns it off. |
|
|
83
|
+
| `reject_authorization_header` | `false` | `true` refuses any Active Storage request carrying an `Authorization` header. Leave it off if HTTP basic auth protects a staging site, or every file there becomes a 403. |
|
|
84
|
+
|
|
85
|
+
## Adding your own guards
|
|
86
|
+
|
|
87
|
+
`ActiveStorageAuthorization.serving_controllers` lists the four controllers that serve or redirect to blob bytes. Add checks of your own to the same list, for example to hold back files a virus scan hasn't cleared. They run after the access check:
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
module VirusScanGuard
|
|
91
|
+
extend ActiveSupport::Concern
|
|
92
|
+
|
|
93
|
+
included { before_action -> { head :forbidden if @blob.metadata["scan"] == "pending" } }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
Rails.application.config.to_prepare do
|
|
97
|
+
ActiveStorageAuthorization.serving_controllers.each { it.include(VirusScanGuard) }
|
|
98
|
+
end
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## What it does not cover
|
|
102
|
+
|
|
103
|
+
- **Service URLs you generate yourself.** `blob.url` and `rails_storage_redirect` hand out a signed URL to the storage service; whoever holds that URL can fetch the file until it expires (`config.active_storage.service_urls_expire_in`, 5 minutes by default). The access check happens before the redirect, not after.
|
|
104
|
+
- **Public services.** On a `public: true` service the redirect still checks access, but it points at a permanent, unsigned bucket URL that anyone can reuse.
|
|
105
|
+
- **`ActiveStorage::DiskController`.** It serves signed, expiring keys that a check above already issued.
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
MIT
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
Security fixes are released for the latest version.
|
|
4
|
+
|
|
5
|
+
Please do not open a public issue for a vulnerability. Use
|
|
6
|
+
[GitHub private vulnerability reporting](https://github.com/yshmarov/active_storage_authorization/security/advisories/new)
|
|
7
|
+
with the affected version, a minimal reproduction, and the impact.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveStorageAuthorization
|
|
4
|
+
module Attachment
|
|
5
|
+
def active_storage_publicly_accessible?
|
|
6
|
+
authorizing_record.try(:active_storage_publicly_accessible?)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def active_storage_accessible_to?(user, session:)
|
|
10
|
+
authorizing_record.try(:active_storage_accessible_to?, user, session: session)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
# Files embedded in rich text are attached to the ActionText::RichText row,
|
|
16
|
+
# not to the model that owns it, so they take that model's access.
|
|
17
|
+
def authorizing_record
|
|
18
|
+
if defined?(ActionText::RichText) && record.is_a?(ActionText::RichText)
|
|
19
|
+
record.record
|
|
20
|
+
else
|
|
21
|
+
record
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveStorageAuthorization
|
|
4
|
+
module AuthorizationHeaderGuard
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
prepend_before_action :reject_authorization_header
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def reject_authorization_header
|
|
14
|
+
return unless ActiveStorageAuthorization.reject_authorization_header && request.authorization.present?
|
|
15
|
+
|
|
16
|
+
no_store
|
|
17
|
+
head :forbidden
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveStorageAuthorization
|
|
4
|
+
module Blob
|
|
5
|
+
def active_storage_publicly_accessible?
|
|
6
|
+
attachments.includes(:record).any?(&:active_storage_publicly_accessible?)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def active_storage_accessible_to?(user, session:)
|
|
10
|
+
attachments.includes(:record).any? do |attachment|
|
|
11
|
+
attachment.active_storage_accessible_to?(user, session: session)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def active_storage_uploaded_by?(user)
|
|
16
|
+
user.present? && attachments.none? && metadata[UPLOADER_METADATA_KEY].to_s == user.id.to_s
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveStorageAuthorization
|
|
4
|
+
module CurrentUser
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def active_storage_user
|
|
8
|
+
return @active_storage_user if defined?(@active_storage_user)
|
|
9
|
+
|
|
10
|
+
@active_storage_user = instance_exec(&ActiveStorageAuthorization.current_user)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveStorageAuthorization
|
|
4
|
+
module DirectUploadGuard
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
include CurrentUser
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
if (limit = ActiveStorageAuthorization.direct_upload_rate_limit)
|
|
10
|
+
rate_limit to: limit.fetch(:to), within: limit.fetch(:within), only: :create,
|
|
11
|
+
by: -> { request.remote_ip }, with: -> { head :too_many_requests }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
before_action :authorize_direct_upload, only: :create
|
|
15
|
+
prepend UploaderStamp
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def authorize_direct_upload
|
|
21
|
+
head :forbidden if ActiveStorageAuthorization.direct_uploads == :deny || active_storage_user.nil?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Prepended so it wraps the controller's own blob_args, and merged last so
|
|
25
|
+
# a client can't claim someone else as the uploader.
|
|
26
|
+
module UploaderStamp
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def blob_args
|
|
30
|
+
super.deep_merge(metadata: { UPLOADER_METADATA_KEY => active_storage_user.id })
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveStorageAuthorization
|
|
4
|
+
class Railtie < Rails::Railtie
|
|
5
|
+
initializer 'active_storage_authorization.models' do
|
|
6
|
+
ActiveSupport.on_load(:active_storage_blob) { include ActiveStorageAuthorization::Blob }
|
|
7
|
+
ActiveSupport.on_load(:active_storage_attachment) { include ActiveStorageAuthorization::Attachment }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Registered before the app's own to_prepare blocks, so these guards run
|
|
11
|
+
# ahead of any the app adds to the same controllers.
|
|
12
|
+
config.to_prepare { ActiveStorageAuthorization.install! }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveStorageAuthorization
|
|
4
|
+
module ServingGuard
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
include CurrentUser
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
before_action :authorize_active_storage_blob
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def authorize_active_storage_blob
|
|
15
|
+
return if @blob.active_storage_publicly_accessible?
|
|
16
|
+
|
|
17
|
+
# Private bytes and the signed URL a redirect carries must not outlive
|
|
18
|
+
# this access check in a browser or shared cache.
|
|
19
|
+
no_store
|
|
20
|
+
return if @blob.active_storage_accessible_to?(active_storage_user, session: session)
|
|
21
|
+
# A direct upload has no attachment until its form is saved, but editors
|
|
22
|
+
# like Lexxy preview it right away, so its uploader may read it.
|
|
23
|
+
return if @blob.active_storage_uploaded_by?(active_storage_user)
|
|
24
|
+
|
|
25
|
+
head :forbidden
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def http_cache_forever(public: false, &)
|
|
29
|
+
return yield if response.cache_control[:no_store]
|
|
30
|
+
|
|
31
|
+
super
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def expires_in(...)
|
|
35
|
+
super unless response.cache_control[:no_store]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support'
|
|
4
|
+
require 'active_support/core_ext/numeric/time'
|
|
5
|
+
require 'active_storage_authorization/version'
|
|
6
|
+
|
|
7
|
+
# Default-deny file serving for Active Storage. A blob is served only when a
|
|
8
|
+
# record it is attached to opts in, and direct uploads require a signed-in
|
|
9
|
+
# user (or are switched off). See README.md.
|
|
10
|
+
module ActiveStorageAuthorization
|
|
11
|
+
DIRECT_UPLOAD_MODES = %i[signed_in deny].freeze
|
|
12
|
+
UPLOADER_METADATA_KEY = 'uploaded_by_user_id'
|
|
13
|
+
|
|
14
|
+
autoload :Blob, 'active_storage_authorization/blob'
|
|
15
|
+
autoload :Attachment, 'active_storage_authorization/attachment'
|
|
16
|
+
autoload :CurrentUser, 'active_storage_authorization/current_user'
|
|
17
|
+
autoload :ServingGuard, 'active_storage_authorization/serving_guard'
|
|
18
|
+
autoload :DirectUploadGuard, 'active_storage_authorization/direct_upload_guard'
|
|
19
|
+
autoload :AuthorizationHeaderGuard, 'active_storage_authorization/authorization_header_guard'
|
|
20
|
+
|
|
21
|
+
# Evaluated inside the Active Storage controller (so `cookies`, `request`
|
|
22
|
+
# and `session` are available); returns the signed-in user or nil.
|
|
23
|
+
mattr_accessor :current_user, default: -> {}
|
|
24
|
+
|
|
25
|
+
# :signed_in lets signed-in users mint direct uploads and stamps them as the
|
|
26
|
+
# uploader; :deny refuses every direct upload.
|
|
27
|
+
mattr_accessor :direct_uploads, default: :signed_in
|
|
28
|
+
|
|
29
|
+
# Per-IP limit on minting direct uploads; nil turns it off. Read once at
|
|
30
|
+
# boot, unlike the other settings.
|
|
31
|
+
mattr_accessor :direct_upload_rate_limit, default: { to: 60, within: 10.minutes }
|
|
32
|
+
|
|
33
|
+
# Refuse any Active Storage request that carries an Authorization header, so
|
|
34
|
+
# an API bearer token can never fall back to a browser session cookie. Off by
|
|
35
|
+
# default: browsers send HTTP basic auth on every request to a protected
|
|
36
|
+
# staging site, which would make every file a 403.
|
|
37
|
+
mattr_accessor :reject_authorization_header, default: false
|
|
38
|
+
|
|
39
|
+
def self.configure
|
|
40
|
+
yield self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# The controllers that serve blob bytes or redirect to them. Include extra
|
|
44
|
+
# guards (virus scan status, processing state) into these.
|
|
45
|
+
def self.serving_controllers
|
|
46
|
+
[
|
|
47
|
+
ActiveStorage::Blobs::RedirectController,
|
|
48
|
+
ActiveStorage::Blobs::ProxyController,
|
|
49
|
+
ActiveStorage::Representations::RedirectController,
|
|
50
|
+
ActiveStorage::Representations::ProxyController
|
|
51
|
+
]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.install!
|
|
55
|
+
unless DIRECT_UPLOAD_MODES.include?(direct_uploads)
|
|
56
|
+
raise ArgumentError, "direct_uploads must be one of #{DIRECT_UPLOAD_MODES.inspect}, got #{direct_uploads.inspect}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
ActiveStorage::BaseController.include(AuthorizationHeaderGuard)
|
|
60
|
+
serving_controllers.each { |controller| controller.include(ServingGuard) }
|
|
61
|
+
ActiveStorage::DirectUploadsController.include(DirectUploadGuard)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
require 'active_storage_authorization/railtie' if defined?(Rails::Railtie)
|
metadata
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: active_storage_authorization
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Yaroslav Shmarov
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: activestorage
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '7.2'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '9'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '7.2'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '9'
|
|
32
|
+
- !ruby/object:Gem::Dependency
|
|
33
|
+
name: railties
|
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
|
35
|
+
requirements:
|
|
36
|
+
- - ">="
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '7.2'
|
|
39
|
+
- - "<"
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '9'
|
|
42
|
+
type: :runtime
|
|
43
|
+
prerelease: false
|
|
44
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - ">="
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: '7.2'
|
|
49
|
+
- - "<"
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '9'
|
|
52
|
+
description: |
|
|
53
|
+
Rails serves any Active Storage file to anyone holding its URL. This gem
|
|
54
|
+
flips that: a file is served only when a record it is attached to says the
|
|
55
|
+
current user may see it (or that it is public). Private responses are never
|
|
56
|
+
cached, direct uploads require a signed-in user and are rate limited, and
|
|
57
|
+
files embedded in rich text inherit their owning record's access.
|
|
58
|
+
email:
|
|
59
|
+
- yaroslav.shmarov@clickfunnels.com
|
|
60
|
+
executables: []
|
|
61
|
+
extensions: []
|
|
62
|
+
extra_rdoc_files: []
|
|
63
|
+
files:
|
|
64
|
+
- CHANGELOG.md
|
|
65
|
+
- MIT-LICENSE
|
|
66
|
+
- README.md
|
|
67
|
+
- SECURITY.md
|
|
68
|
+
- lib/active_storage_authorization.rb
|
|
69
|
+
- lib/active_storage_authorization/attachment.rb
|
|
70
|
+
- lib/active_storage_authorization/authorization_header_guard.rb
|
|
71
|
+
- lib/active_storage_authorization/blob.rb
|
|
72
|
+
- lib/active_storage_authorization/current_user.rb
|
|
73
|
+
- lib/active_storage_authorization/direct_upload_guard.rb
|
|
74
|
+
- lib/active_storage_authorization/railtie.rb
|
|
75
|
+
- lib/active_storage_authorization/serving_guard.rb
|
|
76
|
+
- lib/active_storage_authorization/version.rb
|
|
77
|
+
homepage: https://github.com/yshmarov/active_storage_authorization
|
|
78
|
+
licenses:
|
|
79
|
+
- MIT
|
|
80
|
+
metadata:
|
|
81
|
+
homepage_uri: https://github.com/yshmarov/active_storage_authorization
|
|
82
|
+
source_code_uri: https://github.com/yshmarov/active_storage_authorization
|
|
83
|
+
changelog_uri: https://github.com/yshmarov/active_storage_authorization/blob/main/CHANGELOG.md
|
|
84
|
+
bug_tracker_uri: https://github.com/yshmarov/active_storage_authorization/issues
|
|
85
|
+
rubygems_mfa_required: 'true'
|
|
86
|
+
rdoc_options: []
|
|
87
|
+
require_paths:
|
|
88
|
+
- lib
|
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
90
|
+
requirements:
|
|
91
|
+
- - ">="
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
version: '3.2'
|
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
|
+
requirements:
|
|
96
|
+
- - ">="
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
version: '0'
|
|
99
|
+
requirements: []
|
|
100
|
+
rubygems_version: 4.0.20
|
|
101
|
+
specification_version: 4
|
|
102
|
+
summary: 'Default-deny file serving for Active Storage: records opt in to who may
|
|
103
|
+
download their files.'
|
|
104
|
+
test_files: []
|