permitters 0.0.1

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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MmY3NGJmNTczNzExZjRkYjQ3MDRiZWJiNGM3NDhjOWI5ZTYyZThlZQ==
5
+ data.tar.gz: !binary |-
6
+ NmZmYTk1MTExNDg2YmQ4OGQyM2I5YjE2OTUzNjA2MGVhZjRlMGY4YQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NmQ5ZGYxY2I4NTcwZDY1OTBkOTg0M2VmYzIwYWM4OTlhZjBjMjJiNGU1YzU4
10
+ ZmU5OTY5MTVhNTFjZGMwZGFhN2MyNWFhN2Y5ODQ4ZGIzY2I2ZWM5ZmRjMDNj
11
+ NzUxYTQzNWZiNjRmYTBmZjdiZGViMTdlMmY3NzI1YzJiZThmMDE=
12
+ data.tar.gz: !binary |-
13
+ OTcxMzUxMjBmNGFiNDg4MzliNDhmMTg1MzA2MTg5NTIzNjhkNWVmMzk2MTRk
14
+ NzUxZTczN2M5MTFlMTE0MzQwNGZmOGZlNDZhM2MwMjBiZjczNmVlZDUzMTgx
15
+ NGE2NGI1ZDRjZmVmMjg1YWVkMDQzNDIyNDNjOWI2NWM4NDkyMzA=
data/README.md ADDED
@@ -0,0 +1,296 @@
1
+ [![Build Status](https://secure.travis-ci.org/permitters/permitters.png?branch=master)](http://travis-ci.org/permitters/permitters) [![Gem Version](https://badge.fury.io/rb/permitters.png)](http://badge.fury.io/rb/permitters)
2
+ # Permitters
3
+
4
+ Permitters are an object-oriented way of defining what request parameters are permitted using [Strong Parameters][strong_parameters]. It is to Strong Parameters what ActiveModel::Serializers are to as_json/to_json.
5
+
6
+ Permitters also allow additional parameter authorization using an authorizing class, such as CanCan's Ability class or a custom authorizer.
7
+
8
+ The original version of the Permitters framework that this was based on was provided in a [post][post] by Adam Hawkins.
9
+
10
+ ### Installation
11
+
12
+ In your Rails app's `Gemfile`:
13
+
14
+ ```ruby
15
+ gem 'permitters', '~> 0.0.1'
16
+ ```
17
+
18
+ Then:
19
+
20
+ ```
21
+ bundle install
22
+ ```
23
+
24
+ #### Strong Parameters
25
+
26
+ If you are using Rails 4.x, Strong Parameters is included and everything should be setup, so you can skip this section.
27
+
28
+ If you are using Rails 3.x, you'll need [Strong Parameters][strong_parameters]:
29
+
30
+ ```ruby
31
+ gem 'strong_parameters'
32
+ ```
33
+
34
+ Also in Rails 3.x, you probably will want to change this to false in config:
35
+
36
+ ```ruby
37
+ config.active_record.whitelist_attributes = false
38
+ ```
39
+
40
+ and either put this in each model you want to use Strong Parameters with:
41
+
42
+ ```ruby
43
+ include ActiveModel::ForbiddenAttributesProtection
44
+ ```
45
+
46
+ Or if you'd rather use Strong Parameters with all models, just put this at the bottom of your `config/environment.rb` or an initializer:
47
+
48
+ ```ruby
49
+ ActiveRecord::Base.send :include, ActiveModel::ForbiddenAttributesProtection
50
+ ```
51
+
52
+ ### Usage
53
+
54
+ First, either put this in each controller you want to use Strong Parameters with:
55
+
56
+ ```ruby
57
+ include ActionController::Permittance
58
+ ```
59
+
60
+ Or if you'd rather use Permitters with all controllers, just put this at the bottom of your `config/environment.rb` or an initializer:
61
+
62
+ ```ruby
63
+ ActionController::Base.send :include, ActionController::Permittance
64
+ ```
65
+
66
+ Then in your controller:
67
+
68
+ ```ruby
69
+ def create
70
+ @deal = Deal.new permitted_params
71
+ # ...
72
+ end
73
+ ```
74
+
75
+ Next, add a permitter for each controller that uses `ActionController::Permittance` in `/app/permitters/`.
76
+
77
+ For `/app/controllers/deals_controller.rb`, you would add a `/app/permitters/deal_permitter.rb`:
78
+
79
+ ```ruby
80
+ class DealPermitter < ActionController::Permitter
81
+ # No premissions required to set this
82
+ permit :name, :description, :close_by, :state
83
+
84
+ # can pass `:authorize` with a permission:
85
+ # This line allows user_id if the user can read the user specified
86
+ # by the user_id. This only happens if it's present
87
+ permit :user_id, :authorize => :read
88
+
89
+ # same thing but automatically handles arrays of ids as well.
90
+ # This line allows the attachment_ids if the user can manage all
91
+ # the specified attachments
92
+ permit :attachment_ids, :authorize => :manage
93
+
94
+ # same thing as before but scopes this it to the
95
+ # hash inside the line_items_attributes array
96
+ #
97
+ # line_items_attributes is permitted if every item in the array
98
+ # is allowed.
99
+ #
100
+ # This also only allows line items if the user can manage the parent
101
+ scope :line_items_attributes, :manage => true do |line_item|
102
+ # So you cannot manipulate line items outside the parent
103
+ line_item.permit :id, :authorize => :manage
104
+
105
+ line_item.permit :name, :quantity, :price, :currency, :notes
106
+ line_item.permit :product_id, :authorize => :read
107
+ end
108
+ end
109
+ ```
110
+
111
+ When you call `permitted_params`, this happens:
112
+
113
+ ```ruby
114
+ params.require(:deal).permit(:name, :description, :close_by, :state, :line_items_attributes => [:id, :name, :quantity, :price, :currency, :notes, product_id])
115
+ ```
116
+
117
+ If the controller is namespaced, the permitter should have the same namespace, e.g. `A:B:DealsController` defined in `app/controllers/a/b/deals_controller.rb` requires `A:B:DealPermitter` defined in `/app/a/b/permitters/deal_permitter.rb`.
118
+
119
+ If you need to override the argument(s) to pass into the require, use `resource` in the permitter:
120
+
121
+ ```ruby
122
+ class DealPermitter < ActionController::Permitter
123
+ resource :deal
124
+ # ...
125
+ end
126
+ ```
127
+
128
+ To specify a different Permitter to use with a Controller, either provide a `permitter_class` method:
129
+
130
+ ```ruby
131
+ def permitter_class
132
+ PersonPermitter
133
+ end
134
+ ```
135
+
136
+ Or to specify the permitter, use `permitted_params_using(PermitterClass)`, e.g.:
137
+
138
+ ```ruby
139
+ def make_cotton_candy
140
+ @cotton_candy = CottonCandy.new(permitted_params_using(A::B::CottonCandyPermitter))
141
+ # ...
142
+ end
143
+ ```
144
+
145
+ ### Authorizers
146
+
147
+ Permitters also allow additional parameter authorization using CanCan or a custom authorizer.
148
+
149
+ The authorizer class must implement `initialize(user)` and `authorize!(permission, record)` (like CanCan's Ability class).
150
+
151
+ The controller class can implement a method to return an instance of the authorizer class.
152
+
153
+ So, now let's add the `authorize!`:
154
+
155
+ ```ruby
156
+ def create
157
+ authorize! :create, Deal
158
+ @deal = Deal.new permitted_params
159
+ # ...
160
+ end
161
+ ```
162
+
163
+ When you call `authorize!(:some_permission, YourModelClass)` method, that method will raise an error if `current_user` doesn't have the appropriate permissions for those attributes for which `:authorize` is specified.
164
+
165
+ #### Adding a Custom Authorizer
166
+
167
+ To set this up, you'll need to add one of these into your app config:
168
+
169
+ ```ruby
170
+ config.action_controller.authorizer = 'SomeAuthorizer'
171
+ ```
172
+
173
+ or:
174
+
175
+ ```ruby
176
+ config.action_controller.current_authorizer_method = 'current_authorizer'
177
+ ```
178
+
179
+ If neither is specified, then if the controller calls `authorize!(permission, record)`, nothing happens.
180
+
181
+ ##### Without a Controller Method
182
+
183
+ Put this into your app config:
184
+
185
+ ```ruby
186
+ config.action_controller.authorizer = 'SomeAuthorizer'
187
+ ```
188
+
189
+ Create a class `lib/some_authorizer.rb` that has an `initialize(user)` and `authorize!(permission, record)` methods:
190
+
191
+ ```ruby
192
+ class SomeAuthorizer
193
+
194
+ def initialize(user)
195
+ @user = user
196
+ end
197
+
198
+ def authorize!(permission, record)
199
+ raise "You must login to create deals" if permission == :create && record == Deal && @user.name == 'guest'
200
+ end
201
+ end
202
+ ```
203
+
204
+ ##### With a Controller Method
205
+
206
+ Put this into your app config:
207
+
208
+ ```ruby
209
+ config.action_controller.current_authorizer_method = 'current_authorizer'
210
+ ```
211
+
212
+ and in your controller or ApplicationController return an instance of an authorizer from that method:
213
+
214
+ ```ruby
215
+ def current_authorizer
216
+ @current_ability ||= ::SomeAuthorizer.new
217
+ end
218
+ ```
219
+
220
+ Create a class `lib/some_authorizer.rb` that raises an error from `authorize!(permission, record)` if unauthorized:
221
+
222
+ ```ruby
223
+ class SomeAuthorizer
224
+ def authorize!(permission, record)
225
+ raise "Deals can only be created from 8-5pm" if permission == :create && record == Deal && (Time.new.hour < 8 || Time.new.hour >= 17)
226
+ end
227
+ end
228
+ ```
229
+
230
+ ##### CanCan
231
+
232
+ [CanCan][cancan] is able to integrate with the Permitters framework as an authorizer. To use CanCan, put this into your app config:
233
+
234
+ ```ruby
235
+ config.action_controller.authorizer = 'Ability'
236
+ config.action_controller.current_authorizer_method = 'current_ability'
237
+ ```
238
+
239
+ (Note: You could just define either. If you don't set current_authorizer_method, it will just try creating an instance of the authorizer using the current user. If neither are specified, nothing will happen when `authorize!(permission, record)` is called.)
240
+
241
+ CanCan can integrate with [Authlogic][authlogic], [Devise][devise], etc. to return a proper logged-in user, or you can return it however you wish from the `current_user` method in your controller. Just to provide a simple example, we'll pretend the user was logged-in and return a new User instance (which means you will need a User model):
242
+
243
+ ```ruby
244
+ class ApplicationController < ActionController::Base
245
+ protect_from_forgery
246
+
247
+ def current_user
248
+ User.new
249
+ end
250
+ end
251
+ ```
252
+
253
+ Again for simplicity, we'll write an "allow-everything" Ability in `app/models/ability.rb`:
254
+
255
+ ```ruby
256
+ class Ability
257
+ include CanCan::Ability
258
+
259
+ def initialize(user)
260
+ can :manage, :all
261
+ end
262
+ end
263
+ ```
264
+
265
+ Then in each model you want to use CanCan with, add this into the class:
266
+
267
+ ```ruby
268
+ include CanCan::ModelAdditions
269
+ ```
270
+
271
+ If you'd rather use CanCan with all models, just put this at the bottom of your `config/environment.rb` or an initializer:
272
+
273
+ ```ruby
274
+ ActiveRecord::Base.send :include, CanCan::ModelAdditions
275
+ ```
276
+
277
+ ### Release Notes
278
+
279
+ See the [changelog][changelog].
280
+
281
+ ### Contributors
282
+
283
+ * Adam Hawkins (https://github.com/twinturbo)
284
+ * Gary Weaver (https://github.com/garysweaver)
285
+
286
+ ### License
287
+
288
+ Copyright (c) 2013 Adam Hawkins and Gary S. Weaver, released under the [MIT license][lic].
289
+
290
+ [post]: http://broadcastingadam.com/2012/07/parameter_authorization_in_rails_apis/
291
+ [cancan]: https://github.com/ryanb/cancan
292
+ [strong_parameters]: https://github.com/rails/strong_parameters
293
+ [authlogic]: https://github.com/binarylogic/authlogic
294
+ [devise]: https://github.com/plataformatec/devise
295
+ [changelog]: https://github.com/permitters/permitters/blob/master/CHANGELOG.md
296
+ [lic]: http://github.com/permitters/permitters/blob/master/LICENSE
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require 'bundler/setup'
2
+ require 'bundler/gem_tasks'
3
+ require 'appraisal'
4
+
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default do |t|
9
+ if ENV['BUNDLE_GEMFILE'] =~ /gemfiles/
10
+ exec 'rake spec'
11
+ else
12
+ exec 'rake appraise'
13
+ end
14
+ end
15
+
16
+ task :appraise => ['appraisal:install'] do |t|
17
+ exec 'rake appraisal'
18
+ end
@@ -0,0 +1,41 @@
1
+ module ActionController
2
+ module Permittance
3
+ extend ActiveSupport::Concern
4
+
5
+ def permitted_params
6
+ get_permitted_params_using(permitter)
7
+ end
8
+
9
+ def permitted_params_using(pclass)
10
+ get_permitted_params_using(permitter(pclass))
11
+ end
12
+
13
+ # Returns a new instance of the permitter by initializing it with params, current_user, current_ability.
14
+ def permitter(pclass = permitter_class)
15
+ pinstance = (@permitter_class_to_permitter ||= {})[pclass]
16
+ return pinstance if pinstance
17
+ current_authorizer_method = ActionController::Permitter.current_authorizer_method ? ActionController::Permitter.current_authorizer_method.to_sym : nil
18
+ @permitter_class_to_permitter[pclass] = pclass.new(params, current_user, current_authorizer_method && defined?(current_authorizer_method) ? __send__(current_authorizer_method) : nil)
19
+ end
20
+
21
+ # Returns the permitter class corresponding to the controller by matching everything in the controller class name
22
+ # other than "Controller" and singularizing the part after any namespace before tacking on Permitter to the name.
23
+ #
24
+ # e.g. if self.class.name is "A:B:StatusesController", it would return A::B::StatusPermitter
25
+ def permitter_class
26
+ # Permitters should be in the same namespace as the controller, like ActiveModel::Serializers are in the same namespace as the model.
27
+ name = self.class.name
28
+ # in Rails 3.2+ could do:
29
+ # namespace = name.deconstantize; "#{namespace}#{namespace.blank? ? '' : '::'}#{name.demodulize.chomp('Controller').singularize}Permitter".constantize
30
+ # Rails < 3.2
31
+ last_index = name.rindex('::')
32
+ "#{last_index ? "#{name[0...(last_index || 0)]}::" : ''}#{(last_index ? name[(last_index+2)..-1] : name).chomp('Controller').singularize}Permitter".constantize
33
+ end
34
+
35
+ private
36
+
37
+ def get_permitted_params_using(pinstance)
38
+ (@permitter_to_permitted_params ||= {})[pinstance] ||= pinstance.permitted_params
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,101 @@
1
+ module ActionController
2
+ class PermitterAttribute < Struct.new(:name, :options); end
3
+ class Permitter
4
+
5
+ # Application-wide configuration
6
+ cattr_accessor :authorizer, :instance_accessor => false
7
+ cattr_accessor :current_authorizer_method, :instance_accessor => false
8
+
9
+ class << self
10
+ # When Permitter is inherited, it sets the resource (the symbol for params.require(some_sym)) to the unnamespaced model class that corresponds
11
+ # to the Permitter's classname, e.g. by default A::B::ApplesController will use A::B::ApplePermitter which will do params.permit(:apple).
12
+ # To change this value, use the `resource` class method.
13
+ def inherited(subclass)
14
+ subclass.class_eval do
15
+ class_attribute :permitted_attributes
16
+ class_attribute :resource_name
17
+
18
+ self.permitted_attributes = []
19
+ name = self.name
20
+
21
+ # in Rails 3.2+ could do:
22
+ # name.demodulize.chomp('Permitter').underscore.to_sym
23
+ # Rails < 3.2
24
+ last_index = name.rindex('::')
25
+ self.resource_name = (last_index ? name[(last_index+2)..-1] : name).chomp('Permitter').underscore.to_sym
26
+ end
27
+ end
28
+
29
+ def permit(*args)
30
+ options = args.extract_options!
31
+
32
+ args.each do |name|
33
+ self.permitted_attributes += [ActionController::PermitterAttribute.new(name, options)]
34
+ end
35
+ end
36
+
37
+ def scope(name)
38
+ with_options :scope => name do |nested|
39
+ yield nested
40
+ end
41
+ end
42
+
43
+ def resource(name)
44
+ self.resource_name = name
45
+ end
46
+ end
47
+
48
+ def initialize(params, user, authorizer = nil)
49
+ @params, @user, @authorizer = params, user, authorizer
50
+ end
51
+
52
+ def permitted_params
53
+ scopes = {}
54
+ unscoped_attributes = []
55
+
56
+ permitted_attributes.each do |attribute|
57
+ scope_name = attribute.options[:scope]
58
+ (scope_name ? (scopes[scope_name] ||= []) : unscoped_attributes) << attribute.name
59
+ end
60
+
61
+ # class_attribute creates an instance method called resource_name, which we'll allow overriding of in the permitter definition, if desired for some odd reason.
62
+ @filtered_params ||= params.require(resource_name).permit(*unscoped_attributes, scopes)
63
+
64
+ permitted_attributes.select {|a| a.options[:authorize]}.each do |attribute|
65
+ scope_name = attribute.options[:scope]
66
+ values = scope_name ? Array.wrap(@filtered_params[scope_name]).collect {|hash| hash[attribute.name]}.compact : Array.wrap(@filtered_params[attribute.name])
67
+ klass = (attribute.options[:as].try(:to_s) || attribute.name.to_s.split(/(.+)_ids?/)[1]).classify.constantize
68
+
69
+ values.each do |record_id|
70
+ record = klass.find record_id
71
+ permission = attribute.options[:authorize].to_sym || :read
72
+ authorize! permission, record
73
+ end
74
+ end
75
+
76
+ @filtered_params
77
+ end
78
+
79
+ def authorize!(*args, &block)
80
+ # implementing here is clearer than doing a delegate :authorize!, :to => :authorizer, imo.
81
+ authorizer ? authorizer.__send__(:authorize!, *args, &block) : nil
82
+ end
83
+
84
+
85
+ private
86
+
87
+ def params
88
+ @params
89
+ end
90
+
91
+ def user
92
+ @user
93
+ end
94
+
95
+ def authorizer
96
+ # e.g. if ActionController::Permitter.authorizer = Ability
97
+ # then this returns Ability.new(user)
98
+ @authorizer ||= (ActionController::Permitter.authorizer.is_a?(String) ? ActionController::Permitter.authorizer.constantize : ActionController::Permitter.authorizer).try(:new, user)
99
+ end
100
+ end
101
+ end
data/lib/permitters.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'permitters/version'
2
+ require 'action_controller/permittance'
3
+ require 'action_controller/permitter'
4
+ require 'permitters/railtie' if defined? ::Rails::Railtie
@@ -0,0 +1,15 @@
1
+ require 'rails/railtie'
2
+
3
+ module Permitters
4
+ class Railtie < ::Rails::Railtie
5
+ initializer "permitters.config", :before => "action_controller.set_configs" do |app|
6
+ ActionController::Permitter.authorizer = app.config.action_controller.delete(:authorizer)
7
+ ActionController::Permitter.current_authorizer_method = app.config.action_controller.delete(:current_authorizer_method)
8
+ end
9
+
10
+ initializer "permitters.set_autoload_path", after: :load_config_initializers do
11
+ permitters_path = "#{Rails.root}/app/permitters"
12
+ ActiveSupport::Dependencies.autoload_paths << permitters_path unless ActiveSupport::Dependencies.autoload_paths.include?(permitters_path)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module Permitters
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: permitters
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Adam Hawkins
8
+ - Gary S. Weaver
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-27 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Permitters are an object-oriented way of defining what request parameters
15
+ are permitted. using Strong Parameters. It is to Strong Parameters what ActiveModel::Serializers
16
+ are to as_json/to_json, but supports CanCan and similar authorization frameworks.
17
+ email:
18
+ - me@broadcastingadam.com
19
+ - garysweaver@gmail.com
20
+ executables: []
21
+ extensions: []
22
+ extra_rdoc_files: []
23
+ files:
24
+ - lib/action_controller/permittance.rb
25
+ - lib/action_controller/permitter.rb
26
+ - lib/permitters/railtie.rb
27
+ - lib/permitters/version.rb
28
+ - lib/permitters.rb
29
+ - Rakefile
30
+ - README.md
31
+ homepage: https://github.com/permitters/permitters
32
+ licenses:
33
+ - MIT
34
+ metadata: {}
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project:
51
+ rubygems_version: 2.0.3
52
+ signing_key:
53
+ specification_version: 4
54
+ summary: Object-oriented parameter authorization with Strong Parameters
55
+ test_files: []