action_permission 1.0.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
+ SHA1:
3
+ metadata.gz: 2d598921d73d8ca53edf5154616e27108e9b9ef7
4
+ data.tar.gz: baaba6a7c5792c1f22433c768ab391cdec09cc0a
5
+ SHA512:
6
+ metadata.gz: 9de568d75f1b7871776526e385f35b5fa06e613cc78f745fcf7682b21f63e8ecab21639795572293c9a45f0a65429f1ebfb237da25231e55f6e9c2bdef9d9a89
7
+ data.tar.gz: 2cc23db2ada4942dde384df738c93168c69c4255b12e8a174dc4434bbef6048f59d3dbac791606a303387077402716718fb8ea43168faafe0bc039e9aecd1c99
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in action_permission.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Matt Duffy
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,165 @@
1
+ __NOTICE: The gem as it stands is not production-ready.__
2
+ _See [issues](https://github.com/mttdffy/action_permission/issues) for details_
3
+
4
+ ----
5
+
6
+ # ActionPermission
7
+
8
+ A permission structure for defining both action-based and attribute-based permissions for rails 3+ applications.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'action_permission'
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ ```sh
21
+ $ bundle
22
+ $ rails generate action_permission:install
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ActionPermission assumes you have the concept of user roles. This can be any field of any name. It's core action is to load permissions for the controller handling the request, determine the user's access level, and call a method on the permission object that corresponds to that level. A permission file might look like this:
28
+
29
+ ```ruby
30
+ class BookPermission < ApplicationPermission
31
+
32
+ def params
33
+ [:name, :author, :isbn, :page_count, :price]
34
+ end
35
+
36
+ def guest
37
+ allow [:index, :show]
38
+ end
39
+
40
+ def user
41
+ allow [:index, :show, :new]
42
+ allow [:create, :edit, :update, :destroy] do |user|
43
+ @membership.id == user.id
44
+ end
45
+ allow_params except: [:price]
46
+ end
47
+
48
+ def admin
49
+ allow_rest_actions
50
+ allow_params
51
+ end
52
+
53
+ end
54
+ ```
55
+
56
+ - the `params` method can be used to define attributes allowed to be modified by that user level in addition to their allowed actions.
57
+ - the `@membership` attribute is set on initialization based on the method handed to `authorize_with` in your `ApplicationController` (See 'Setup' below)
58
+
59
+
60
+ ## Setup
61
+
62
+ ```sh
63
+ $ rails generate action_permission:install
64
+ ```
65
+
66
+ This generator will creating the `app/permissions` directory along with a `application_perimission.rb` file.
67
+
68
+ Permissions should be placed in the `app/permissions` directory. Each permission will typically extend from `ApplicationPermission`, allowing you to set default permissions for each role.
69
+
70
+ Additionally, the install generator will add some boilerplate code into your `ApplicationController` for setting up your application to work properly with ActionPermission.
71
+
72
+ ```ruby
73
+ #app/controllers/application_controller.rb
74
+
75
+ authorize_with :current_user
76
+ before_action :check_permission
77
+
78
+ def current_user
79
+ @current_user ||= session[:user_id] ? User.find(session[:user_id]) : User.new
80
+ end
81
+
82
+ def check_permission
83
+ unless authorized?
84
+ #do something when user does not have permission to access page
85
+ # Flash[:warn] = "You do not have permission to access this page."
86
+ # redirect_to root_url
87
+ end
88
+ end
89
+
90
+ ```
91
+
92
+ This is a basic implementation that you can change and modify to work with your application's user role structure.
93
+
94
+ Ultimately, ActionPermission looks to receive a string representing the name of the role/level of current user. It requires you to define a method on your `ApplicationController` to call when loading permissions. This method should return an object that can repond to a `#identify` method. `identify` method should return a string value of the current user's role
95
+
96
+ ```ruby
97
+ # app/models/user.rb
98
+ class User < ActiveRecord::Base
99
+
100
+ # assume User#role exists as a string representation
101
+ # of what role that user is
102
+
103
+ def identify
104
+ role || "guest"
105
+ end
106
+ end
107
+
108
+ ```
109
+
110
+ Alternatively, you can overwrite `ActionPermission::Base#load` in the `ApplicationPermission` class to define your own way of determining a method to call.
111
+
112
+ ```ruby
113
+ class ApplicationPermission < ActionPermission::Base
114
+
115
+ def load(user)
116
+ @membership = user
117
+ send @membership.role
118
+ end
119
+
120
+ end
121
+
122
+ ```
123
+
124
+ Once you have it setup, your controller has access to an `authorized?` method which will tell you if the current user has permission to access the current action
125
+
126
+ ```ruby
127
+ # app/controllers/application_controller.rb
128
+ ApplicationController < ActionController::Base
129
+
130
+ before_action :check_permissions
131
+
132
+ def check_permissions
133
+ unless authorized?
134
+ flash[:warn] = "You do not have permission to access this page"
135
+ rediect_to root_url
136
+ end
137
+ end
138
+ end
139
+ ```
140
+
141
+ ## Generators
142
+
143
+ rails g action_permission:install
144
+
145
+ This will add the base application_permission.rb file as well as add some boilerplate code to the application controller
146
+
147
+ rails g action_permission:permission CONTROLLER [attribute, attribute, ...]
148
+
149
+ rails g action_permission:permission users username name email role password_digest
150
+
151
+ This will generate a permission file for the supplied controller. YOu can pass in attributes to auto populate the params method for that permission object. In the future this will be added onto the scaffolding generator so you don't have to run this seperately
152
+
153
+ ## Contributors
154
+
155
+ - [Matt Duffy](https://github.com/mttdffy)
156
+ - [Brian McElaney](https://github.com/mcelaney)
157
+ - [Mark Platt](https://github.com/mrkplt)
158
+
159
+ ## Contributing
160
+
161
+ 1. Fork it
162
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
163
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
164
+ 4. Push to the branch (`git push origin my-new-feature`)
165
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'action_permission/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "action_permission"
8
+ spec.version = ActionPermission::VERSION
9
+ spec.authors = ["Matt Duffy", "Brian McElaney", "Mark Platt"]
10
+ spec.email = ["matt@mttdffy.com", "", ""]
11
+ spec.summary = "Controller-based action and attribute permissions"
12
+ spec.homepage = "https://github.com/mttdffy/action_permission"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "rails", "~> 4"
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec", "~> 2"
24
+ spec.add_development_dependency "activerecord-nulldb-adapter"
25
+ end
@@ -0,0 +1,71 @@
1
+ module ActionPermission
2
+
3
+ class Base
4
+
5
+ class << self
6
+
7
+ def match_with source,to
8
+ Array(to).each do |role|
9
+ alias_method role, source
10
+ end
11
+ end
12
+
13
+ end
14
+
15
+ attr_accessor :membership
16
+ attr_reader :allowed_params, :allowed_actions
17
+
18
+ def initialize(membership)
19
+ load membership
20
+ end
21
+
22
+ def load(membership)
23
+ @membership = membership
24
+ role = @membership.identify
25
+ if role && respond_to?(role)
26
+ send(role)
27
+ end
28
+ end
29
+
30
+ def allow?(action, resource = nil)
31
+ allowed = @allowed_actions[action.to_s] if @allowed_actions
32
+ allowed && (allowed == true || resource && allowed.call(resource))
33
+ end
34
+
35
+ def allow(actions, &block)
36
+ @allowed_actions ||= {}
37
+ Array(actions).each do |action|
38
+ @allowed_actions[action.to_s] = block || true
39
+ end
40
+ end
41
+
42
+ def allow_rest_actions(&block)
43
+ allow [:index, :new, :create, :show, :edit, :update, :destroy], &block
44
+ end
45
+
46
+ def params
47
+ []
48
+ end
49
+
50
+ def allow_params options=nil
51
+ @allowed_params ||= []
52
+
53
+ if options
54
+ @allowed_params = allow_params_with_options options
55
+ else
56
+ @allowed_params = Array(params)
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def allow_params_with_options options
63
+ alt_params = params
64
+ alt_params = Array(options[:only]) if options[:only]
65
+ Array(options[:except]).each {|e| alt_params.delete e } if options[:except]
66
+ alt_params
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -0,0 +1,56 @@
1
+ require 'active_support/core_ext/object'
2
+ require 'active_support/dependencies'
3
+ require 'abstract_controller/helpers'
4
+
5
+ module ActionPermission
6
+
7
+ module Controller
8
+ extend ActiveSupport::Concern
9
+ include AbstractController::Helpers
10
+
11
+ included do
12
+ delegate :allow?, to: :current_permission
13
+ delegate :allow_param?, to: :current_permission
14
+ delegate :allowed_params_for, to: :current_permission
15
+ helper_method :allow?
16
+ helper_method :allow_param?
17
+ helper_method :current_permission
18
+ end
19
+
20
+ module ClassMethods
21
+
22
+ def authorize_with authorizer
23
+ helper_method authorizer
24
+ @@permission_authorizer = authorizer
25
+ end
26
+
27
+ def permission_authorizer
28
+ @@permission_authorizer
29
+ end
30
+
31
+ end
32
+
33
+ def current_resource
34
+ nil
35
+ end
36
+
37
+ def current_permission
38
+ @current_permission ||= ActionPermission::Dispatch.new(permission_authorizer)
39
+ end
40
+
41
+ def authorized?
42
+ current_permission.allow?(params[:controller], params[:action], current_resource)
43
+ end
44
+
45
+ private
46
+
47
+ def permission_authorizer
48
+ send self.class.permission_authorizer
49
+ end
50
+
51
+
52
+ end
53
+
54
+ end
55
+
56
+ ActionController::Base.send :include, ActionPermission::Controller if ENV['RAILS']
@@ -0,0 +1,96 @@
1
+ require 'active_support/hash_with_indifferent_access'
2
+ require 'active_support/inflector'
3
+
4
+ module ActionPermission
5
+
6
+ class Dispatch
7
+ attr_accessor :membership, :permissions
8
+
9
+ def initialize(membership)
10
+ @membership = membership
11
+ @permissions = HashWithIndifferentAccess.new
12
+ end
13
+
14
+ def allow?(controller, action, resource = nil)
15
+ current_permission = load_permission controller
16
+
17
+ if resource
18
+ current_permission.allow?(action, resource)
19
+ else !resource
20
+ current_permission.allow?(action)
21
+ end
22
+ end
23
+
24
+ # 'book/review', params #=> controller == 'books/reviews'
25
+ # 'book/review', params, 'reviews' #=> controller == 'reviews'
26
+ def allowed_params_for(resource, params, controller=nil)
27
+ controller = set_controller(resource, controller)
28
+ resource = set_resource(resource)
29
+
30
+ current_permission = load_permission(controller)
31
+
32
+ if current_permission && current_permission.allowed_params
33
+ params.require(resource).permit *current_permission.allowed_params
34
+ end
35
+ end
36
+
37
+ def allow_param?(resource, attribute)
38
+ current_permission = load_permission resource.to_s.pluralize
39
+
40
+ if current_permission && current_permission.allowed_params
41
+ current_permission.allowed_params.include? attribute
42
+ else
43
+ false
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def set_controller(resource, controller)
50
+ if controller
51
+ case controller
52
+ when String, Symbol
53
+ controller.to_s.pluralize
54
+ else
55
+ (controller.is_a?(Class) ? controller : controller.class).
56
+ name.underscore.gsub('_controller', '')
57
+ end
58
+ else
59
+ case resource
60
+ when String, Symbol
61
+ resource.to_s
62
+ else
63
+ (resource.is_a?(Class) ? resource : resource.class).
64
+ name.underscore
65
+ end.
66
+ split('/').map(&:pluralize).join('/')
67
+ end
68
+ end
69
+
70
+ def set_resource(resource)
71
+ case resource
72
+ when String, Symbol
73
+ resource.to_s.parameterize("_")
74
+ else
75
+ (resource.is_a?(Class) ? resource : resource.class).model_name.param_key
76
+ end
77
+ end
78
+
79
+ def load_permission(controller)
80
+ klass = get_class_name(controller)
81
+ # grab the permissions for this controller from cache or register it if missing
82
+ @permissions[controller] ||= register_permission(controller, klass)
83
+ end
84
+
85
+ def get_class_name(controller)
86
+ controller = controller.to_s
87
+ return (controller.pluralize + "_permissions").classify.constantize
88
+ end
89
+
90
+ def register_permission(controller, klass)
91
+ @permissions[controller] = klass.new(@membership)
92
+ end
93
+
94
+ end
95
+
96
+ end
@@ -0,0 +1,21 @@
1
+ require 'rails/railtie'
2
+
3
+ module ActionPermission
4
+ class Railtie < ::Rails::Railtie
5
+ if config.respond_to?(:app_generators)
6
+ config.app_generators.scaffold_controller = :action_permission_controller
7
+ else
8
+ config.generators.scaffold_controller = :action_permission_controller
9
+ end
10
+
11
+ # initializer 'activeservice.autoload', :before => :set_autoload_paths do |app|
12
+ # app.config.autoload_paths << (app.config.root + '/app/permissions')
13
+ # end
14
+
15
+ # initializer "strong_parameters.config", :before => "action_controller.set_configs" do |app|
16
+ # ActionController::Parameters.action_on_unpermitted_parameters = app.config.action_controller.delete(:action_on_unpermitted_parameters) do
17
+ # (Rails.env.test? || Rails.env.development?) ? :log : false
18
+ # end
19
+ # end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module ActionPermission
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,4 @@
1
+ require "action_permission/version"
2
+ require "action_permission/dispatch"
3
+ require "action_permission/base"
4
+ require "action_permission/controller"
@@ -0,0 +1,3 @@
1
+ Description:
2
+ Creates the app/permissions directory and stubs out application_permission.rb,
3
+ a base permission file used to extend controller-specific permissions for
@@ -0,0 +1,37 @@
1
+ require 'rails/generators/base'
2
+
3
+ module ActionPermission
4
+ module Generators
5
+
6
+ class InstallGenerator < Rails::Generators::Base
7
+ p "creating permissions directory"
8
+ source_root File.expand_path("../templates", __FILE__)
9
+
10
+ def copy_application_file
11
+ copy_file "application.rb", "app/permissions/application_permission.rb"
12
+ end
13
+
14
+ def add_controller_setup
15
+ line = /class ApplicationController \< ActionController\:\:Base\n/
16
+ inject_into_file 'app/controllers/application_controller.rb', after: line do <<-'RUBY'
17
+ authorize_with :current_user
18
+ before_action :check_permission
19
+
20
+ def current_user
21
+ @current_user ||= session[:user_id] ? User.find(session[:user_id]) : User.new
22
+ end
23
+
24
+ def check_permission
25
+ unless authorized?
26
+ #do something when user does not have permission to access page
27
+ # Flash[:warn] = "You do not have permission to access this page."
28
+ # redirect_to root_url
29
+ end
30
+ end
31
+ RUBY
32
+ end
33
+ end
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,32 @@
1
+ class ApplicationPermission < ActionPermission::Base
2
+
3
+ # Your base permission file
4
+ # You can use this file to set default permissions
5
+ # or overwrite the #load method to manually determine which
6
+ # methods of a permission file are called for each user role
7
+
8
+ # example overwrite method
9
+ # def load(user)
10
+ # @membership = user
11
+ # send (@membership.role) || "guest"
12
+ # end
13
+
14
+ # default permissions
15
+
16
+ # def guest
17
+ # allow [:show]
18
+ # end
19
+
20
+ # def user
21
+ # allow_rest_actions do |user|
22
+ # @membership.id == user.id
23
+ # end
24
+ # allow_params
25
+ # end
26
+
27
+ # def admin
28
+ # allow_rest_actions
29
+ # allow_params
30
+ # end
31
+
32
+ end
@@ -0,0 +1,2 @@
1
+ Description:
2
+ Creates a permission file with supplied file name
@@ -0,0 +1,14 @@
1
+ require 'rails/generators/named_base'
2
+
3
+ module ActionPermission
4
+ module Generators
5
+ class PermissionGenerator < Rails::Generators::NamedBase
6
+ argument :attributes, type: :array, default: [], banner: "field field"
7
+ source_root File.expand_path("../templates", __FILE__)
8
+
9
+ def create_permission_file
10
+ template "permission.rb", "app/permissions/#{file_name}_permission.rb"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,45 @@
1
+ <% module_namespacing do -%>
2
+ class <%= class_name %>Permission < ApplicationPermission
3
+
4
+ # defines parameters for requests coming to associated object/controlelr
5
+ # typically defines all attributes and uses except option to exclude
6
+ # params on a per-role basis
7
+ def params
8
+ [<%= attributes.map {|a| ":#{a.name}" }.sort.join(', ') %>]
9
+ end
10
+
11
+ # define methods for each of your user roles here
12
+
13
+ # allow [:actions]
14
+ # defines routes allowed for that role
15
+ # an optional block can be passed to allow to check
16
+ # things like ownership.
17
+
18
+ # allow_params
19
+ # options: [:except, :only]
20
+ # define params user role can change
21
+ # no options gives access to all of #params
22
+ # except excludes any listed
23
+ # only overwrites params array
24
+
25
+ # @membership is available as the object returned from
26
+ # method passed to ActionPermission::Controller#authorize_with
27
+
28
+ def guest
29
+ allow [:show]
30
+ end
31
+
32
+ def user
33
+ allow_rest_actions do |user|
34
+ @membership.id == user.id
35
+ end
36
+ allow_params except: [:id]
37
+ end
38
+
39
+ def admin
40
+ allow_rest_actions
41
+ allow_params
42
+ end
43
+
44
+ end
45
+ <% end -%>
@@ -0,0 +1,16 @@
1
+ Description:
2
+ Stubs out a scaffolded controller and its views. Different from rails
3
+ scaffold_controller, it uses strong_parameters to whitelist permissible
4
+ attributes in a private method.
5
+ Pass the model name, either CamelCased or under_scored. The controller
6
+ name is retrieved as a pluralized version of the model name.
7
+
8
+ With ActionPermission, it replaces the default strong_parameters
9
+ call with a call to allowed_params_for that loads the permission file
10
+ to determined whitelisted parameters
11
+
12
+ To create a controller within a module, specify the model name as a
13
+ path like 'parent_module/controller_name'.
14
+
15
+ This generates a controller class in app/controllers and invokes helper,
16
+ template engine and test framework generators.
@@ -0,0 +1,17 @@
1
+ require 'rails/version'
2
+ require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
3
+
4
+ module Rails
5
+ module Generators
6
+ class ActionPermissionControllerGenerator < ScaffoldControllerGenerator
7
+ argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
8
+ source_root File.expand_path("../templates", __FILE__)
9
+
10
+ if ::Rails::VERSION::STRING < '3.1'
11
+ def module_namespacing
12
+ yield if block_given?
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end