effective_storage 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +94 -0
- data/Rakefile +18 -0
- data/app/controllers/admin/storage_controller.rb +20 -0
- data/app/datatables/admin/effective_storage_datatable.rb +64 -0
- data/app/helpers/effective_storage_helper.rb +2 -0
- data/app/models/concerns/active_storage_attachment_extension.rb +48 -0
- data/app/models/concerns/active_storage_authorization.rb +144 -0
- data/app/models/effective/active_storage_extension.rb +31 -0
- data/app/views/admin/storage/_datatable_actions.html.haml +8 -0
- data/app/views/admin/storage/index.html.haml +8 -0
- data/config/effective_storage.rb +9 -0
- data/config/routes.rb +17 -0
- data/db/migrate/01_create_effective_storage.rb.erb +13 -0
- data/db/seeds.rb +1 -0
- data/lib/effective_storage/engine.rb +43 -0
- data/lib/effective_storage/version.rb +3 -0
- data/lib/effective_storage.rb +18 -0
- data/lib/generators/effective_storage/install_generator.rb +28 -0
- data/lib/tasks/effective_storage_tasks.rake +8 -0
- metadata +175 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f43ada6fb79a20117caa0e82a3a2853852e4db3e38c80bf6278b539b05fcdb3a
|
4
|
+
data.tar.gz: b727658f31266edf3646e8f6658b0b16c9b524439efd034caa83438e5c5a88a3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e0d0f65d241af25b65e7e326a908614e4d439126edd464a15449a4a2d93a7cf2e5539cd5929b98902f1e346ac3854ceb86e690af0a312b79a97aa19b960c90ab
|
7
|
+
data.tar.gz: d6c1f5c005c079b9a3af3347c5f4fa590ddb4c788890683220b2202b2f574e82bedfde51d2b34964ea4e4359baf105f1af6dab1e4dc85a5a8a0ba26b231223c0
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2022 Code and Effect Inc.
|
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,94 @@
|
|
1
|
+
# Effective Storage
|
2
|
+
|
3
|
+
Adds an authentication layer to the Active Storage downloads controller.
|
4
|
+
|
5
|
+
Authorizes the user downloading each file and raises an exception for unauthorized requests.
|
6
|
+
|
7
|
+
Adds an admin screen to browse Active Storage attachments and mark them as inherited or public.
|
8
|
+
|
9
|
+
## Getting Started
|
10
|
+
|
11
|
+
This requires Rails 6+ and Twitter Bootstrap 4 and just works with Devise.
|
12
|
+
|
13
|
+
Please first install the [effective_datatables](https://github.com/code-and-effect/effective_datatables) gem.
|
14
|
+
|
15
|
+
Please download and install the [Twitter Bootstrap4](http://getbootstrap.com)
|
16
|
+
|
17
|
+
Add to your Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'haml-rails' # or try using gem 'hamlit-rails'
|
21
|
+
gem 'effective_storage'
|
22
|
+
```
|
23
|
+
|
24
|
+
Run the bundle command to install it:
|
25
|
+
|
26
|
+
```console
|
27
|
+
bundle install
|
28
|
+
```
|
29
|
+
|
30
|
+
Then run the generator:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
rails generate effective_storage:install
|
34
|
+
```
|
35
|
+
|
36
|
+
The generator will install an initializer which describes all configuration options and creates a database migration.
|
37
|
+
|
38
|
+
If you want to tweak the table names, manually adjust both the configuration file and the migration now.
|
39
|
+
|
40
|
+
Then migrate the database:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
rake db:migrate
|
44
|
+
```
|
45
|
+
|
46
|
+
Add a link to the admin menu:
|
47
|
+
|
48
|
+
```haml
|
49
|
+
- if can?(:admin, :effective_storage) && can?(:index, ActiveStorage::Attachment)
|
50
|
+
= nav_link_to 'Storage', effective_storage.admin_storage_path
|
51
|
+
```
|
52
|
+
|
53
|
+
## Configuration
|
54
|
+
|
55
|
+
## Authorization
|
56
|
+
|
57
|
+
All authorization checks are handled via the effective_resources gem found in the `config/initializers/effective_resources.rb` file.
|
58
|
+
|
59
|
+
## Permissions
|
60
|
+
|
61
|
+
The permissions you actually want to define are as follows (using CanCan):
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
can(:show, ActiveStorage::Attachment) { |attachment| attachment.permission_public? }
|
65
|
+
|
66
|
+
if user.persisted?
|
67
|
+
end
|
68
|
+
|
69
|
+
if user.admin?
|
70
|
+
can :admin, :effective_storage
|
71
|
+
can :index, ActiveStorage::Attachment
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
## License
|
76
|
+
|
77
|
+
MIT License. Copyright [Code and Effect Inc.](http://www.codeandeffect.com/)
|
78
|
+
|
79
|
+
## Testing
|
80
|
+
|
81
|
+
Run tests by:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
rails test
|
85
|
+
```
|
86
|
+
|
87
|
+
## Contributing
|
88
|
+
|
89
|
+
1. Fork it
|
90
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
91
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
92
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
93
|
+
5. Bonus points for test coverage
|
94
|
+
6. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
|
3
|
+
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
4
|
+
load "rails/tasks/engine.rake"
|
5
|
+
|
6
|
+
load "rails/tasks/statistics.rake"
|
7
|
+
|
8
|
+
require "bundler/gem_tasks"
|
9
|
+
|
10
|
+
require "rake/testtask"
|
11
|
+
|
12
|
+
Rake::TestTask.new(:test) do |t|
|
13
|
+
t.libs << 'test'
|
14
|
+
t.pattern = 'test/**/*_test.rb'
|
15
|
+
t.verbose = false
|
16
|
+
end
|
17
|
+
|
18
|
+
task default: :test
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Admin
|
2
|
+
class StorageController < ApplicationController
|
3
|
+
before_action(:authenticate_user!) if defined?(Devise)
|
4
|
+
before_action { EffectiveResources.authorize!(self, :admin, :effective_storage) }
|
5
|
+
|
6
|
+
include Effective::CrudController
|
7
|
+
|
8
|
+
page_title 'Storage'
|
9
|
+
|
10
|
+
resource_scope -> { ActiveStorage::Attachment.all }
|
11
|
+
datatable -> { Admin::EffectiveStorageDatatable.new }
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def permitted_params
|
16
|
+
params.require(:active_storage_extension).permit!
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Admin
|
2
|
+
class EffectiveStorageDatatable < Effective::Datatable
|
3
|
+
datatable do
|
4
|
+
order :created_at
|
5
|
+
|
6
|
+
col :created_at, as: :date
|
7
|
+
col :updated_at, visible: false
|
8
|
+
col :id, visible: false
|
9
|
+
|
10
|
+
col :record_type, visible: false
|
11
|
+
col :record_id, label: 'Record Id', visible: false
|
12
|
+
|
13
|
+
col :related_type, visible: false do |attachment|
|
14
|
+
attachment.record.try(:record_type)
|
15
|
+
end
|
16
|
+
|
17
|
+
col :related_id, label: 'Related Id', visible: false do |attachment|
|
18
|
+
attachment.record.try(:record_id)
|
19
|
+
end
|
20
|
+
|
21
|
+
col :resource_type do |attachment|
|
22
|
+
attachment.record.try(:record_type) || attachment.record_type
|
23
|
+
end
|
24
|
+
|
25
|
+
col :resource do |attachment|
|
26
|
+
record = attachment.record
|
27
|
+
record = attachment.record.record if record.respond_to?(:record) # ActionText::RichText will
|
28
|
+
|
29
|
+
url = Effective::Resource.new(record, namespace: :admin).action_path(:edit)
|
30
|
+
link_to(record, url, target: '_blank') if url
|
31
|
+
end
|
32
|
+
|
33
|
+
col :filename, label: 'File' do |attachment|
|
34
|
+
content_tag(:div, class: 'col-resource_item') do
|
35
|
+
link_to(attachment.blob.filename, url_for(attachment.blob), target: '_blank')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
col :permission, search: Effective::ActiveStorageExtension::PERMISSIONS do |attachment|
|
40
|
+
if attachment.permission_public?
|
41
|
+
content_tag(:span, attachment.permission, class: 'badge badge-warning')
|
42
|
+
else
|
43
|
+
content_tag(:span, attachment.permission, class: 'badge badge-info')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
col :content_type do |attachment|
|
48
|
+
attachment.blob.content_type
|
49
|
+
end
|
50
|
+
|
51
|
+
col :byte_size do |attachment|
|
52
|
+
number_to_human_size(attachment.blob.byte_size)
|
53
|
+
end
|
54
|
+
|
55
|
+
actions_col partial: 'admin/storage/datatable_actions', partial_as: :attachment
|
56
|
+
end
|
57
|
+
|
58
|
+
collection do
|
59
|
+
ActiveStorage::Attachment.all.deep
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# This is included into ActiveStorage::Attachment automatically by engine.rb
|
2
|
+
module ActiveStorageAttachmentExtension
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
has_many :active_storage_extensions, class_name: 'Effective::ActiveStorageExtension', inverse_of: :attachment, dependent: :destroy
|
7
|
+
accepts_nested_attributes_for :active_storage_extensions, allow_destroy: true
|
8
|
+
|
9
|
+
scope :deep, -> { includes(:active_storage_extensions, :blob, record: :record) }
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
end
|
14
|
+
|
15
|
+
# Instance methods
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
'attachment'
|
19
|
+
end
|
20
|
+
|
21
|
+
# Find or build
|
22
|
+
def active_storage_extension
|
23
|
+
active_storage_extensions.to_a.first || active_storage_extensions.build(permission: 'inherited')
|
24
|
+
end
|
25
|
+
|
26
|
+
def permission
|
27
|
+
active_storage_extension.permission
|
28
|
+
end
|
29
|
+
|
30
|
+
def permission_inherited?
|
31
|
+
permission == 'inherited'
|
32
|
+
end
|
33
|
+
|
34
|
+
def permission_public?
|
35
|
+
permission == 'public'
|
36
|
+
end
|
37
|
+
|
38
|
+
def mark_inherited!
|
39
|
+
active_storage_extension.assign_attributes(permission: 'inherited')
|
40
|
+
save!
|
41
|
+
end
|
42
|
+
|
43
|
+
def mark_public!
|
44
|
+
active_storage_extension.assign_attributes(permission: 'public')
|
45
|
+
save!
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This authorizes all ActiveStorage downloads
|
4
|
+
# This is included automatically by the engine
|
5
|
+
# It can be disabled by setting config.authorize_active_storage = false in config/initializers/effective_storage.rb
|
6
|
+
#
|
7
|
+
# There are 3 ways to add permissions:
|
8
|
+
# 1.) can?(:show, resource)
|
9
|
+
# 2.) can?(:show, ActionText::RichText) { |text| ... }
|
10
|
+
# 3.) can?(:show, ActiveStorage::Attachment) { |attachment| ... }
|
11
|
+
#
|
12
|
+
# The :show and :edit will both work.
|
13
|
+
#
|
14
|
+
module ActiveStorageAuthorization
|
15
|
+
extend ActiveSupport::Concern
|
16
|
+
|
17
|
+
included do
|
18
|
+
rescue_from(Exception, with: :unauthorized_active_storage_request)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Authorize ActiveStorage DiskController downloads
|
22
|
+
# Used for local storage
|
23
|
+
def authorize_active_storage_download!
|
24
|
+
@blob || set_download_blob()
|
25
|
+
authorize_active_storage!
|
26
|
+
end
|
27
|
+
|
28
|
+
# Authorize ActiveStorage Blob and Representation redirects
|
29
|
+
# Used for amazon storage
|
30
|
+
def authorize_active_storage_redirect!
|
31
|
+
@blob || set_blob()
|
32
|
+
authorize_active_storage!
|
33
|
+
end
|
34
|
+
|
35
|
+
# Send an ExceptionNotification email with the unauthorized details
|
36
|
+
# This is not visible to users
|
37
|
+
def unauthorized_active_storage_request(exception)
|
38
|
+
return if request.referer.to_s.include?('.test:')
|
39
|
+
|
40
|
+
if defined?(ExceptionNotifier)
|
41
|
+
data = { 'current_user_id': current_user&.id || 'none' }.merge(@blob&.attributes || {})
|
42
|
+
ExceptionNotifier.notify_exception(exception, env: request.env, data: data)
|
43
|
+
else
|
44
|
+
raise(exception)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def set_download_blob
|
51
|
+
@blob ||= ActiveStorage::Blob.where(key: decode_verified_key().try(:dig, :key)).first
|
52
|
+
end
|
53
|
+
|
54
|
+
# Authorize the current blob and prevent it from being served if unauthorized
|
55
|
+
def authorize_active_storage!
|
56
|
+
return unless @blob.present?
|
57
|
+
|
58
|
+
# If the blob is not attached to anything, permit the blob
|
59
|
+
return true if @blob.attachments.blank? && authorize_content_download?(@blob)
|
60
|
+
|
61
|
+
# If the blob is an ActiveStorage::Variant it's been previously authorized
|
62
|
+
return true if @blob.attachments.any? { |attachment| authorized_variant_download?(attachment) }
|
63
|
+
|
64
|
+
# If we are authorized on any attached record, permit the download
|
65
|
+
return true if @blob.attachments.any? { |attachment| authorized_attachment_download?(attachment) }
|
66
|
+
|
67
|
+
# Otherwise raise a 403 Forbidden and block the download
|
68
|
+
head :forbidden
|
69
|
+
|
70
|
+
# Raise an exception to log unauthorized request
|
71
|
+
raise_exception()
|
72
|
+
end
|
73
|
+
|
74
|
+
def raise_exception
|
75
|
+
attachment = @blob.attachments.first
|
76
|
+
record = attachment.record if attachment
|
77
|
+
resource = record.record if record.respond_to?(:record)
|
78
|
+
|
79
|
+
error = [
|
80
|
+
"unauthorized active storage request for #{@blob.filename}",
|
81
|
+
("on #{record.class.name} #{record.id}" if record.present?),
|
82
|
+
("from #{resource.class.name} #{resource.id}" if resource.present?),
|
83
|
+
("with current_user #{current_user.class.name } #{current_user&.id}"),
|
84
|
+
].compact.join(' ')
|
85
|
+
|
86
|
+
resolution = "Missing can?(:show, #{(resource || record || attachment).class.name})"
|
87
|
+
|
88
|
+
raise(error + '. ' + resolution)
|
89
|
+
end
|
90
|
+
|
91
|
+
# This is a file that was drag & drop or inserted into the article editor
|
92
|
+
# I think this might only happen with article editor edit screens
|
93
|
+
def authorize_content_download?(blob)
|
94
|
+
# Allow signed out users to view images
|
95
|
+
return true if blob.image?
|
96
|
+
|
97
|
+
# Require sign in to view any attached files
|
98
|
+
current_user.present?
|
99
|
+
end
|
100
|
+
|
101
|
+
# This was included and resized in an ActionText::RichText object
|
102
|
+
# But these ones don't belong_to any record
|
103
|
+
def authorized_variant_download?(attachment)
|
104
|
+
attachment.record_type == 'ActiveStorage::VariantRecord'
|
105
|
+
end
|
106
|
+
|
107
|
+
# This is a has_one_attached or has_many_attached record
|
108
|
+
# Or an ActionText::RichText object, that belongs_to a record
|
109
|
+
def authorized_attachment_download?(attachment)
|
110
|
+
return false if attachment.record.blank?
|
111
|
+
|
112
|
+
# Associated Record
|
113
|
+
record = attachment.record
|
114
|
+
return true if authorized?(record)
|
115
|
+
|
116
|
+
# ActionText::RichText
|
117
|
+
resource = record.record if record.respond_to?(:record)
|
118
|
+
return true if authorized?(resource)
|
119
|
+
|
120
|
+
# Attachment itself
|
121
|
+
return true if authorized?(attachment)
|
122
|
+
|
123
|
+
false
|
124
|
+
end
|
125
|
+
|
126
|
+
def authorized?(record)
|
127
|
+
return false if record.blank?
|
128
|
+
|
129
|
+
# If they can show or edit the resource, they are authorized.
|
130
|
+
return true if EffectiveResources.authorized?(self, :show, record)
|
131
|
+
return true if EffectiveResources.authorized?(self, :edit, record)
|
132
|
+
|
133
|
+
false
|
134
|
+
end
|
135
|
+
|
136
|
+
def current_user
|
137
|
+
(defined?(Tenant) ? send("current_#{Tenant.current}_user") : super)
|
138
|
+
end
|
139
|
+
|
140
|
+
def current_ability
|
141
|
+
@current_ability ||= (defined?(Tenant) ? Tenant.Ability.new(current_user) : super)
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Effective
|
2
|
+
class ActiveStorageExtension < ActiveRecord::Base
|
3
|
+
belongs_to :attachment, class_name: 'ActiveStorage::Attachment'
|
4
|
+
|
5
|
+
PERMISSIONS = ['inherited', 'public']
|
6
|
+
|
7
|
+
effective_resource do
|
8
|
+
permission :string
|
9
|
+
|
10
|
+
timestamps
|
11
|
+
end
|
12
|
+
|
13
|
+
scope :deep, -> { includes(:attachment) }
|
14
|
+
scope :sorted, -> { order(:id) }
|
15
|
+
|
16
|
+
validates :permission, presence: true, inclusion: { in: PERMISSIONS }
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
permission.presence || 'active storage extension'
|
20
|
+
end
|
21
|
+
|
22
|
+
def permission_inherited?
|
23
|
+
permission == 'inherited'
|
24
|
+
end
|
25
|
+
|
26
|
+
def permission_public?
|
27
|
+
permission == 'public'
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
= dropdown(variation: :dropleft) do
|
2
|
+
- if EffectiveResources.authorized?(self, :mark_inherited, attachment)
|
3
|
+
= dropdown_link_to 'Mark as Inherited', effective_storage.mark_inherited_admin_storage_path(attachment),
|
4
|
+
data: { method: :post, remote: true, confirm: "Mark as Inherited Permission?" }
|
5
|
+
|
6
|
+
- if EffectiveResources.authorized?(self, :mark_public, attachment)
|
7
|
+
= dropdown_link_to 'Mark as Public', effective_storage.mark_public_admin_storage_path(attachment),
|
8
|
+
data: { method: :post, remote: true, confirm: "Mark as Public Permission?" }
|
@@ -0,0 +1,8 @@
|
|
1
|
+
%h1.effective-admin-heading= @page_title
|
2
|
+
|
3
|
+
= collapse('Show legend') do
|
4
|
+
%ul
|
5
|
+
%li Inherited - If the resource is public, anyone may access the file without signing in. If the resource has restricted access, only signed in users who can access the resource are able to access the file. This is the default permission.
|
6
|
+
%li Public - Anyone may access the file without signing in, regardless of the resource's access restrictions.
|
7
|
+
|
8
|
+
= render_datatable @datatable
|
@@ -0,0 +1,9 @@
|
|
1
|
+
EffectiveStorage.setup do |config|
|
2
|
+
# Layout Settings
|
3
|
+
# Configure the Layout per controller, or all at once
|
4
|
+
# config.layout = { application: 'application', admin: 'admin' }
|
5
|
+
|
6
|
+
# Perform authorization on ActiveStorage downloads
|
7
|
+
# When false any request will be permitted (the default)
|
8
|
+
config.authorize_active_storage = true
|
9
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Rails.application.routes.draw do
|
4
|
+
mount EffectiveStorage::Engine => '/', as: 'effective_storage'
|
5
|
+
end
|
6
|
+
|
7
|
+
EffectiveStorage::Engine.routes.draw do
|
8
|
+
namespace :admin do
|
9
|
+
resources :storage, only: [] do
|
10
|
+
post :mark_inherited, on: :member
|
11
|
+
post :mark_public, on: :member
|
12
|
+
end
|
13
|
+
|
14
|
+
get '/storage', to: 'storage#index', as: :storage
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/db/seeds.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
puts "Running effective_storage seeds"
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module EffectiveStorage
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
engine_name 'effective_storage'
|
4
|
+
|
5
|
+
# Set up our default configuration options.
|
6
|
+
initializer 'effective_storage.defaults', before: :load_config_initializers do |app|
|
7
|
+
eval File.read("#{config.root}/config/effective_storage.rb")
|
8
|
+
end
|
9
|
+
|
10
|
+
# Include active_storage_attachment_extension concern
|
11
|
+
initializer 'effective_storage.active_storage_attachment_extension' do |app|
|
12
|
+
app.config.to_prepare do
|
13
|
+
ActiveStorage::Attachment.include(ActiveStorageAttachmentExtension)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Adds user authorization for all active storage requests
|
18
|
+
# Please see upside/app/models/concerns/active_storage_authorization.rb
|
19
|
+
# https://github.com/rails/rails/blob/v6.1.4.1/activestorage/app/controllers/active_storage/disk_controller.rb
|
20
|
+
initializer 'effective_storage.active_storage_authorization' do |app|
|
21
|
+
if EffectiveStorage.authorize_active_storage?
|
22
|
+
app.config.to_prepare do
|
23
|
+
ActiveStorage::BaseController.class_eval do
|
24
|
+
include ActiveStorageAuthorization
|
25
|
+
end
|
26
|
+
|
27
|
+
ActiveStorage::DiskController.class_eval do
|
28
|
+
before_action :authorize_active_storage_download!, only: [:show]
|
29
|
+
end
|
30
|
+
|
31
|
+
ActiveStorage::Blobs::RedirectController.class_eval do
|
32
|
+
before_action :authorize_active_storage_redirect!, only: [:show]
|
33
|
+
end
|
34
|
+
|
35
|
+
ActiveStorage::Representations::RedirectController.class_eval do
|
36
|
+
before_action :authorize_active_storage_redirect!, only: [:show]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'effective_resources'
|
2
|
+
require 'effective_datatables'
|
3
|
+
require 'effective_storage/engine'
|
4
|
+
require 'effective_storage/version'
|
5
|
+
|
6
|
+
module EffectiveStorage
|
7
|
+
|
8
|
+
def self.config_keys
|
9
|
+
[:layout, :authorize_active_storage]
|
10
|
+
end
|
11
|
+
|
12
|
+
include EffectiveGem
|
13
|
+
|
14
|
+
def self.authorize_active_storage?
|
15
|
+
authorize_active_storage == true
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module EffectiveStorage
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
include Rails::Generators::Migration
|
5
|
+
|
6
|
+
desc 'Creates an EffectiveStorage initializer in your application.'
|
7
|
+
|
8
|
+
source_root File.expand_path('../../templates', __FILE__)
|
9
|
+
|
10
|
+
def self.next_migration_number(dirname)
|
11
|
+
if not ActiveRecord::Base.timestamped_migrations
|
12
|
+
Time.new.utc.strftime("%Y%m%d%H%M%S")
|
13
|
+
else
|
14
|
+
'%.3d' % (current_migration_number(dirname) + 1)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def copy_initializer
|
19
|
+
template ('../' * 3) + 'config/effective_storage.rb', 'config/initializers/effective_storage.rb'
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_migration_file
|
23
|
+
migration_template ('../' * 3) + 'db/migrate/01_create_effective_storage.rb.erb', 'db/migrate/create_effective_storage.rb'
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: effective_storage
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Code and Effect
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-08-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 6.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 6.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: effective_bootstrap
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: effective_datatables
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 4.0.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 4.0.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: effective_resources
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sqlite3
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: devise
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: haml-rails
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: pry-byebug
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Authorize and browse Active Storage attachments
|
126
|
+
email:
|
127
|
+
- info@codeandeffect.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- MIT-LICENSE
|
133
|
+
- README.md
|
134
|
+
- Rakefile
|
135
|
+
- app/controllers/admin/storage_controller.rb
|
136
|
+
- app/datatables/admin/effective_storage_datatable.rb
|
137
|
+
- app/helpers/effective_storage_helper.rb
|
138
|
+
- app/models/concerns/active_storage_attachment_extension.rb
|
139
|
+
- app/models/concerns/active_storage_authorization.rb
|
140
|
+
- app/models/effective/active_storage_extension.rb
|
141
|
+
- app/views/admin/storage/_datatable_actions.html.haml
|
142
|
+
- app/views/admin/storage/index.html.haml
|
143
|
+
- config/effective_storage.rb
|
144
|
+
- config/routes.rb
|
145
|
+
- db/migrate/01_create_effective_storage.rb.erb
|
146
|
+
- db/seeds.rb
|
147
|
+
- lib/effective_storage.rb
|
148
|
+
- lib/effective_storage/engine.rb
|
149
|
+
- lib/effective_storage/version.rb
|
150
|
+
- lib/generators/effective_storage/install_generator.rb
|
151
|
+
- lib/tasks/effective_storage_tasks.rake
|
152
|
+
homepage: https://github.com/code-and-effect/effective_storage
|
153
|
+
licenses:
|
154
|
+
- MIT
|
155
|
+
metadata: {}
|
156
|
+
post_install_message:
|
157
|
+
rdoc_options: []
|
158
|
+
require_paths:
|
159
|
+
- lib
|
160
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
161
|
+
requirements:
|
162
|
+
- - ">="
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
version: '0'
|
165
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
166
|
+
requirements:
|
167
|
+
- - ">="
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: '0'
|
170
|
+
requirements: []
|
171
|
+
rubygems_version: 3.1.2
|
172
|
+
signing_key:
|
173
|
+
specification_version: 4
|
174
|
+
summary: Authorize and browse Active Storage attachments
|
175
|
+
test_files: []
|