paperclip_private 0.0.2

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.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/Gemfile +14 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +31 -0
  6. data/Rakefile +37 -0
  7. data/app/controllers/paperclip_private/attachment_controller.rb +75 -0
  8. data/bin/rails +13 -0
  9. data/config/routes.rb +3 -0
  10. data/lib/paperclip_private.rb +11 -0
  11. data/lib/paperclip_private/attachment.rb +33 -0
  12. data/lib/paperclip_private/attachment_registry.rb +13 -0
  13. data/lib/paperclip_private/engine.rb +4 -0
  14. data/lib/paperclip_private/errors.rb +18 -0
  15. data/lib/paperclip_private/has_attached_file.rb +10 -0
  16. data/lib/paperclip_private/interpolations.rb +26 -0
  17. data/lib/paperclip_private/version.rb +3 -0
  18. data/lib/tasks/paperclip_private_tasks.rake +4 -0
  19. data/paperclip_private.gemspec +24 -0
  20. data/test/dummy/Rakefile +6 -0
  21. data/test/dummy/app/assets/config/manifest.js +5 -0
  22. data/test/dummy/app/assets/images/.keep +0 -0
  23. data/test/dummy/app/assets/javascripts/application.js +13 -0
  24. data/test/dummy/app/assets/javascripts/cable.js +13 -0
  25. data/test/dummy/app/assets/javascripts/channels/.keep +0 -0
  26. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  27. data/test/dummy/app/channels/application_cable/channel.rb +4 -0
  28. data/test/dummy/app/channels/application_cable/connection.rb +4 -0
  29. data/test/dummy/app/controllers/application_controller.rb +3 -0
  30. data/test/dummy/app/controllers/concerns/.keep +0 -0
  31. data/test/dummy/app/helpers/application_helper.rb +2 -0
  32. data/test/dummy/app/jobs/application_job.rb +2 -0
  33. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  34. data/test/dummy/app/models/application_record.rb +3 -0
  35. data/test/dummy/app/models/concerns/.keep +0 -0
  36. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  37. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  38. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  39. data/test/dummy/bin/bundle +3 -0
  40. data/test/dummy/bin/rails +4 -0
  41. data/test/dummy/bin/rake +4 -0
  42. data/test/dummy/bin/setup +34 -0
  43. data/test/dummy/bin/update +29 -0
  44. data/test/dummy/config.ru +5 -0
  45. data/test/dummy/config/application.rb +15 -0
  46. data/test/dummy/config/boot.rb +5 -0
  47. data/test/dummy/config/cable.yml +9 -0
  48. data/test/dummy/config/database.yml +25 -0
  49. data/test/dummy/config/environment.rb +5 -0
  50. data/test/dummy/config/environments/development.rb +54 -0
  51. data/test/dummy/config/environments/production.rb +86 -0
  52. data/test/dummy/config/environments/test.rb +42 -0
  53. data/test/dummy/config/initializers/application_controller_renderer.rb +6 -0
  54. data/test/dummy/config/initializers/assets.rb +11 -0
  55. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  56. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  57. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  58. data/test/dummy/config/initializers/inflections.rb +16 -0
  59. data/test/dummy/config/initializers/mime_types.rb +4 -0
  60. data/test/dummy/config/initializers/new_framework_defaults.rb +24 -0
  61. data/test/dummy/config/initializers/session_store.rb +3 -0
  62. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  63. data/test/dummy/config/locales/en.yml +23 -0
  64. data/test/dummy/config/puma.rb +47 -0
  65. data/test/dummy/config/routes.rb +3 -0
  66. data/test/dummy/config/secrets.yml +22 -0
  67. data/test/dummy/config/spring.rb +6 -0
  68. data/test/dummy/lib/assets/.keep +0 -0
  69. data/test/dummy/log/.keep +0 -0
  70. data/test/dummy/public/404.html +67 -0
  71. data/test/dummy/public/422.html +67 -0
  72. data/test/dummy/public/500.html +66 -0
  73. data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
  74. data/test/dummy/public/apple-touch-icon.png +0 -0
  75. data/test/dummy/public/favicon.ico +0 -0
  76. data/test/integration/navigation_test.rb +8 -0
  77. data/test/paperclip_private_test.rb +7 -0
  78. data/test/test_helper.rb +20 -0
  79. metadata +164 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 66481098f3f82d89f0bfb3e357804c7603159aef
4
+ data.tar.gz: 791cde5a10795fb9cd4faa0d3b1d16f843740c57
5
+ SHA512:
6
+ metadata.gz: 390dfc6fafd5108e6d1e4088fed7db870c1bf44fa482e79d61b4be2f3747270dd1309117860cfd6505411f70e35eeb2d2243a67e2213f220dbb505be42cf7499
7
+ data.tar.gz: 01c204df3132537a2e1106f14fd6936b43c12b4216ba51c34ab657f567d1a3970a06fc88e95d8529286312ea4bccd2360edafab44376aea97f30e1c8d44b7a99
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ .bundle/
2
+ log/*.log
3
+ pkg/
4
+ test/dummy/db/*.sqlite3
5
+ test/dummy/db/*.sqlite3-journal
6
+ test/dummy/log/*.log
7
+ test/dummy/tmp/
8
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Declare your gem's dependencies in paperclip_private.gemspec.
4
+ # Bundler will treat runtime dependencies like base dependencies, and
5
+ # development dependencies will be added by default to the :development group.
6
+ gemspec
7
+
8
+ # Declare any dependencies that are still in development here instead of in
9
+ # your gemspec. These might include edge Rails or gems from your path or
10
+ # Git. Remember to move these dependencies to your gemspec before releasing
11
+ # your gem to rubygems.org.
12
+
13
+ # To use a debugger
14
+ # gem 'byebug', group: [:development, :test]
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2017 jose
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,31 @@
1
+ +---
2
+ +
3
+ +Private Attachments
4
+ +-------------------
5
+ +If you want to place files behind a controller in order to perform validation you can.
6
+ +
7
+ +In your routes.rb file mount the paperclip engine with any path you'd like.
8
+ +Ex:
9
+ +```ruby
10
+ +mount PaperclipPrivate::Engine => 'paperclip/'
11
+ +```
12
+ +
13
+ +In your ApplicationController add the method `paperclip_whitelist` which returns an array of the models with attachments you would like to serve privately.
14
+ +Ex:
15
+ +```ruby
16
+ +def paperclip_whitelist
17
+ + [PrivateAttachment]
18
+ +end
19
+ +```
20
+ +
21
+ +Then in your model add `privacy: :private` to the has_attached_file options. Then add the method `can_download_attachment?`.
22
+ +The method `can_download_attachment?` gets passed the controller instance and the params and is expected to return true, false, or raise Paperclip::Errors::AccessDeniedError.
23
+ +The controller is passed so that methods like current_user can be run on it to get the user instance for validation puprposes. Duplicated params gets passed to do things like allow anyone if the style is :thumb or to only allow :original to paid users.
24
+ +Ex:
25
+ +```ruby
26
+ + has_attached_file :file, privacy: :private
27
+ +
28
+ + def can_download_attachment?(controller, params)
29
+ + params[:style] == :thumb || (!controller.current_user.nil? && created_by == controller.current_user.id)
30
+ + end
31
+ +```
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'PaperclipPrivate'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+
24
+
25
+ require 'bundler/gem_tasks'
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+
37
+ task default: :test
@@ -0,0 +1,75 @@
1
+ module PaperclipPrivate
2
+ class AttachmentController < ::ApplicationController
3
+ before_action :validate_params!
4
+ before_action :set_attachment
5
+ before_action :set_path
6
+ before_action :confirm_attachment_access!
7
+ rescue_from ActiveRecord::RecordNotFound, with: :handle_not_found!
8
+ rescue_from ::PaperclipPrivate::Errors::FileDoesNotExistError, with: :handle_not_found!
9
+ rescue_from ::PaperclipPrivate::Errors::ControllerValidationError, with: :hadle_bad_params!
10
+ rescue_from ::PaperclipPrivate::Errors::AccessDeniedError, with: :handle_access_denied!
11
+
12
+ # Download action
13
+ def download
14
+ send_file(@path)
15
+ end
16
+
17
+ protected
18
+ # Return the private arratcment registry
19
+ def paperclip_whitelist
20
+ super if self.class.superclass.instance_methods.include?(:paperclip_whitelist) # Call classes in an array so they appear in the registry.
21
+ ::PaperclipPrivate::AttachmentRegistry.registry
22
+ end
23
+
24
+ private
25
+ # Confirm class name is valid and attachment name is valid before calling constantize in set_attachment method.
26
+ def validate_params!
27
+ attachments = paperclip_whitelist[params[:class_name]]
28
+ raise ::PaperclipPrivate::Errors::ControllerValidationError if attachments.nil? || !attachments.keys.map(&:to_s).include?(params[:attachment])
29
+ @options = attachments[params[:attachment].to_sym]
30
+ end
31
+
32
+ # Sets object, attachment, and styles
33
+ def set_attachment
34
+ klass = params[:class_name].constantize
35
+ object_id = params[:id]
36
+ @object = klass.find(object_id)
37
+ @attachment = @object.send(params[:attachment])
38
+ @styles = @options[:styles] || {}
39
+ @styles = @styles.call(@attachment) if @styles.respond_to?(:call)
40
+ end
41
+
42
+ # Sets path and confirms the file exists.
43
+ def set_path
44
+ @path = @styles.keys.map(&:to_s).include?(params[:style]) ? @attachment.path(params[:style]) : @attachment.path
45
+ raise ::PaperclipPrivate::Errors::FileDoesNotExistError unless File.exist?(@path)
46
+ end
47
+
48
+ # Confirms the download is allowed.
49
+ def confirm_attachment_access!
50
+ # Passes self and params.dup to object.can_download_attachment? that is expected to return true, false, or raise AccessDeniedError.
51
+ # self is passed so that methods like current_user can be run. params.dup gets passed to do things like allow anyone if the style is :thumb but
52
+ # to only allow :original to paid users for example.
53
+ # Considering refactoring to use config options to specify the get user method and send the result of that method on self to
54
+ # object.can_download_attachment? instead of passing controller instance.
55
+ raise ::PaperclipPrivate::Errors::AccessDeniedError unless @object.respond_to?(:can_download_attachment?) && @object.can_download_attachment?(self, params.dup)
56
+ end
57
+
58
+ # Handles what should happen when the route params do not pass validation.
59
+ def hadle_bad_params!
60
+ head 400
61
+ end
62
+
63
+ # Handles what should happen when the record or file is not found.
64
+ def handle_not_found!
65
+ head 404
66
+ end
67
+
68
+ # Handles what should happen when the object's can_download_attachment? method does not return true.
69
+ def handle_access_denied!
70
+ head 401
71
+ end
72
+
73
+
74
+ end
75
+ end
data/bin/rails ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails gems
3
+ # installed from the root of your application.
4
+
5
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
6
+ ENGINE_PATH = File.expand_path('../../lib/paperclip_private/engine', __FILE__)
7
+
8
+ # Set up gems listed in the Gemfile.
9
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
10
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
11
+
12
+ require 'rails/all'
13
+ require 'rails/engine/commands'
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ PaperclipPrivate::Engine.routes.draw do
2
+ get 'private/:class_name/:id/:attachment/:style', controller: Paperclip.options[:controller] || 'paperclip_private/attachment', action: Paperclip.options[:action] || 'download'
3
+ end
@@ -0,0 +1,11 @@
1
+ require 'paperclip'
2
+ require 'paperclip_private/interpolations'
3
+ require 'paperclip_private/attachment'
4
+ require 'paperclip_private/errors'
5
+ require 'paperclip_private/has_attached_file'
6
+ require 'paperclip_private/attachment_registry'
7
+ require 'paperclip_private/engine'
8
+
9
+ Paperclip::Attachment.include PaperclipPrivate::Attachment
10
+ Paperclip::HasAttachedFile.prepend PaperclipPrivate::HasAttachedFile
11
+ Paperclip::Interpolations.extend PaperclipPrivate::Interpolations
@@ -0,0 +1,33 @@
1
+ module PaperclipPrivate
2
+ module Attachment
3
+
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ base.prepend Initializer
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def default_options
12
+ super.merge({privacy: :public})
13
+ end
14
+
15
+ end
16
+
17
+ module Initializer
18
+ def initialize(name, instance, options = {})
19
+ defaults = self.class.default_options.dup
20
+ if options[:privacy] == :private # change default options if private before merging with options.
21
+ defaults[:path] = ":rails_root/:privacy#{defaults[:url]}"
22
+ defaults[:url] = "#{Rails.application.routes.url_helpers.paperclip_private_engine_path}/private/:klass/:id/:attachment_singular/:style"
23
+ end
24
+ options = defaults.deep_merge(options)
25
+
26
+ super(name, instance, options)
27
+ end
28
+ end
29
+
30
+
31
+
32
+ end
33
+ end
@@ -0,0 +1,13 @@
1
+ module PaperclipPrivate
2
+ module AttachmentRegistry
3
+ def self.registry
4
+ @registry ||= {}
5
+ end
6
+
7
+ def self.register(klass, name, options)
8
+ class_name = klass.to_s
9
+ self.registry[class_name] ||= {}
10
+ self.registry[class_name][name] = options
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ module PaperclipPrivate
2
+ class Engine < ::Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,18 @@
1
+ module PaperclipPrivate
2
+
3
+ module Errors
4
+ # Wil be thrown if params validation fails in private controller.
5
+ class ControllerValidationError < ::Paperclip::Error
6
+ end
7
+
8
+ # Will be thrown if file does not exist on server.
9
+ class FileDoesNotExistError < ::Paperclip::Error
10
+ end
11
+
12
+ # Will be thrown if true is not returned from an attachment's owner's
13
+ # can_download_attachment? method.
14
+ class AccessDeniedError < ::Paperclip::Error
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ module PaperclipPrivate
2
+ module HasAttachedFile
3
+
4
+ def register_new_attachment
5
+ super
6
+ PaperclipPrivate::AttachmentRegistry.register(@klass, @name, @options) if @options[:privacy] == :private
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,26 @@
1
+ module PaperclipPrivate
2
+ module Interpolations
3
+
4
+ def self.extended(base)
5
+ base.instance_eval do
6
+ ::PaperclipPrivate::Interpolations.instance_methods(false).each { |m| define_method(m, self.method(m)) }
7
+ end
8
+ end
9
+
10
+ # Returns the style, or the default style if nil is supplied.
11
+ def privacy(attachment, style_name)
12
+ attachment.options[:privacy]
13
+ end
14
+
15
+ # Returns the class name without pluarlizing it.
16
+ def klass(attachment, style_name)
17
+ attachment.instance.class
18
+ end
19
+
20
+ # Returns the attachment name without pluralizing it
21
+ def attachment_singular(attachment, style_name)
22
+ attachment.name
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module PaperclipPrivate
2
+ VERSION = '0.0.2'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :paperclip_private do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,24 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+ # Maintain your gem's version:
4
+ require "paperclip_private/version"
5
+
6
+ # Describe your gem and declare its dependencies:
7
+ Gem::Specification.new do |s|
8
+ s.name = "paperclip_private"
9
+ s.version = PaperclipPrivate::VERSION
10
+ s.authors = ["jose"]
11
+ s.email = ["nextgenappsllc@gmail.com"]
12
+ s.homepage = "https://nextgenappsllc.com"
13
+ s.summary = "Allows for creation of private attachments with paperclip gem."
14
+ s.description = "Allows restrcition of access to paperclip attachments with a private option."
15
+ s.license = "MIT"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ # s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
19
+
20
+ s.add_dependency "rails", "~> 4.0"
21
+ s.add_dependency "paperclip", "~> 5.0"
22
+
23
+ s.add_development_dependency "sqlite3", "~> 1.0"
24
+ end
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require_relative 'config/application'
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,5 @@
1
+
2
+ //= link_tree ../images
3
+ //= link_directory ../javascripts .js
4
+ //= link_directory ../stylesheets .css
5
+ //= link paperclip_private_manifest.js
File without changes
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,13 @@
1
+ // Action Cable provides the framework to deal with WebSockets in Rails.
2
+ // You can generate new channels where WebSocket features live using the rails generate channel command.
3
+ //
4
+ //= require action_cable
5
+ //= require_self
6
+ //= require_tree ./channels
7
+
8
+ (function() {
9
+ this.App || (this.App = {});
10
+
11
+ App.cable = ActionCable.createConsumer();
12
+
13
+ }).call(this);
File without changes
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */