effective_storage 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,2 @@
1
+ module EffectiveStorageHelper
2
+ 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
@@ -0,0 +1,13 @@
1
+ class CreateEffectiveStorage < ActiveRecord::Migration[6.0]
2
+ def change
3
+
4
+ create_table :active_storage_extensions do |t|
5
+ t.integer :attachment_id
6
+ t.string :permission
7
+
8
+ t.datetime :updated_at
9
+ t.datetime :created_at
10
+ end
11
+
12
+ end
13
+ 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,3 @@
1
+ module EffectiveStorage
2
+ VERSION = '0.1.0'.freeze
3
+ 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
@@ -0,0 +1,8 @@
1
+ namespace :effective_storage do
2
+
3
+ # bundle exec rake effective_storage:seed
4
+ task seed: :environment do
5
+ load "#{__dir__}/../../db/seeds.rb"
6
+ end
7
+
8
+ 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: []