cannie 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4767806af908d3fbb42922daeb46aa9793c43f09
4
- data.tar.gz: d1e4a3df2a8055cfa9e80645b63ceb62a66ea88c
3
+ metadata.gz: ec9ec75cbf4049751bdc12e6a3724880a39d729b
4
+ data.tar.gz: 0f5750f17a62373de640dfe3765c59bb640778d9
5
5
  SHA512:
6
- metadata.gz: 28cae6c32b9616c3d9af5983b0044cd1b34044b552f55856a680adc3560548d6b82e064ca6a9356ef09bbc37cf4672e81293a35ffd4152786435f77111700b3c
7
- data.tar.gz: a03394d05eaea44c85b0e7d2572f911b24f10700c0d5fd3e78c578919af0ebffaa6114cb234100fb936ce67718f4193f6e78261a44f9979e65e310324861bc2c
6
+ metadata.gz: 083fc7fa69ee1b74fb976edc60be681aea8d1f57e85b8d75d82ca1fa985b775e3f8bfd668d14f7da4dc1bc3b6d4d308579ad14fe24c864f1d589c3266ece6015
7
+ data.tar.gz: 92e63adbc1ad697c13bb14c460f4f516a008a8a98748cb8e5b4870d040c52e3fd58420f5a317b7941bb6fe650bb414910b20beeaaf1774073538eddb433048e3
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ .idea
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/Gemfile CHANGED
@@ -1,6 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'rails', '~> 4.0'
3
+ gem 'activesupport', '>= 4.0'
4
+ gem 'actionpack', '>= 4.0'
4
5
 
5
6
  group :test do
6
7
  gem 'rspec'
data/README.md CHANGED
@@ -65,14 +65,14 @@ To skip checking permissions for controller, add `skip_check_permissions` method
65
65
 
66
66
  Checking of permissions on per-action basis is done by calling `permit!` method inside of controller's actions:
67
67
 
68
- class PostsController < ApplicationController
69
- check_permissions
70
-
71
- def index
72
- @posts = Posts.all
73
- permit! :read, on: posts # checks whether user able to read fetched posts
74
- end
75
- end
68
+ class PostsController < ApplicationController
69
+ check_permissions
70
+
71
+ def index
72
+ @posts = Posts.all
73
+ permit! :read, on: posts # checks whether user able to read fetched posts
74
+ end
75
+ end
76
76
 
77
77
  ### Handling of unpermitted access
78
78
 
data/cannie.gemspec CHANGED
@@ -19,4 +19,6 @@ Gem::Specification.new do |spec|
19
19
 
20
20
  spec.add_development_dependency "bundler", "~> 1.3"
21
21
  spec.add_development_dependency "rake"
22
+ spec.add_runtime_dependency "actionpack", ">= 4.0"
23
+ spec.add_runtime_dependency "activesupport", ">= 4.0"
22
24
  end
@@ -0,0 +1,92 @@
1
+ module Cannie
2
+ module ControllerExtensions
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ extend ClassMethods
7
+ helper_method :can?, :current_permissions
8
+ end
9
+
10
+ module ClassMethods
11
+ # Method is used to be sure, that permissions checking is handled for each action inside controller.
12
+ #
13
+ # class PostsController < ApplicationController
14
+ # check_permissions
15
+ #
16
+ # #...
17
+ # end
18
+ #
19
+ def check_permissions(options={})
20
+ after_action(options.slice(:only, :except)) do |controller|
21
+ next if controller.permitted?
22
+ next if options[:if] && !controller.instance_eval(options[:if])
23
+ next if options[:unless] && controller.instance_eval(options[:unless])
24
+ raise CheckPermissionsNotPerformed, 'Action failed the check_permissions because it does not calls permit! method. Add skip_check_permissions to bypass this check.'
25
+ end
26
+ end
27
+
28
+ # Skip handling of permissions checking, that was defined by `check_permissions` method
29
+ #
30
+ # class PostsController < ApplicationController
31
+ # skip_check_permissions
32
+ #
33
+ # #...
34
+ # end
35
+ def skip_check_permissions(*args)
36
+ before_action(*args) do |controller|
37
+ controller.instance_variable_set(:@_permitted, true)
38
+ end
39
+ end
40
+ end
41
+
42
+ # Checks whether passed action is permitted for passed subject
43
+ #
44
+ # can? :read, on: @posts
45
+ #
46
+ # or
47
+ #
48
+ # can? :read, on: Post
49
+ #
50
+ # @param [Symbol] action
51
+ # @param [Object] subject
52
+ # @return [Boolean] result of checking permission
53
+ #
54
+ def can?(action, on: nil)
55
+ raise Cannie::SubjectNotSetError, 'Subject should be specified' unless on
56
+ current_permissions.can?(action, on: on)
57
+ end
58
+
59
+ # Define permissions, that should be checked inside controller's action
60
+ #
61
+ # def index
62
+ # permit! :read, on: Post
63
+ # @posts = Post.all
64
+ # end
65
+ #
66
+ # @param [Symbol] action
67
+ # @param [Object] subject
68
+ #
69
+ def permit!(action, on: nil)
70
+ raise Cannie::SubjectNotSetError, 'Subject should be specified' unless on
71
+ current_permissions.permit!(action, on: on)
72
+ @_permitted = true
73
+ end
74
+
75
+ def permitted?
76
+ !!@_permitted
77
+ end
78
+
79
+ def current_permissions
80
+ @current_permissions ||= ::Permissions.new(current_user)
81
+ end
82
+
83
+ private
84
+ attr_reader :_permitted
85
+ end
86
+ end
87
+
88
+ if defined? ActionController::Base
89
+ ActionController::Base.class_eval do
90
+ include Cannie::ControllerExtensions
91
+ end
92
+ end
@@ -0,0 +1,7 @@
1
+ module Cannie
2
+ class SubjectNotSetError < StandardError; end
3
+
4
+ class CheckPermissionsNotPerformed < StandardError; end
5
+
6
+ class ActionForbidden < StandardError; end
7
+ end
@@ -0,0 +1,71 @@
1
+ module Cannie
2
+ # This module provides possibility to define permissions using "allow" method
3
+ #
4
+ # class Permissions
5
+ # include Cannie::Permissions
6
+ #
7
+ # def initialize(user)
8
+ # allow :read, on: Model
9
+ # allow :manage, on: Model do |*entries|
10
+ # entries.all?{|v| v.user == user}
11
+ # end
12
+ # end
13
+ # end
14
+ #
15
+ module Permissions
16
+ # Define rules for further permissions checking
17
+ #
18
+ # allow :read, on: Model
19
+ #
20
+ # @param actions
21
+ # @param on
22
+ # @return array of rules
23
+ def allow(*actions, on: nil, &block)
24
+ rules << Rule.new(*actions, on, &block)
25
+ end
26
+
27
+ # Check permission by given action on subject, that is passed in 'on' parameter
28
+ #
29
+ # can? :read, on: Model
30
+ #
31
+ # or
32
+ #
33
+ # can? :read, on: models
34
+ #
35
+ # @param action
36
+ # @param on
37
+ # @return result of permissions check
38
+ #
39
+ def can?(action, on: nil)
40
+ rules_for(action, on).all? do |rule|
41
+ rule.permits?(*on)
42
+ end
43
+ end
44
+
45
+ # Permit access for action on a subject
46
+ #
47
+ # permit! :read, on: Model
48
+ #
49
+ # or
50
+ #
51
+ # permit! :manage, on: models
52
+ #
53
+ # @param [Symbol] Action
54
+ #
55
+ def permit!(action, on: nil)
56
+ raise Cannie::ActionForbidden unless can?(action, on: on)
57
+ end
58
+
59
+ private
60
+ def rules
61
+ @rules ||= []
62
+ end
63
+
64
+ def rules_for(action, subject)
65
+ klass = subject.is_a?(Class) ? subject : subject.class
66
+ rules.select do |r|
67
+ r.actions.include?(action) && (subject == :all || klass <= r.subject)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,19 @@
1
+ module Cannie
2
+ class Rule
3
+ attr_reader :actions, :subject, :condition
4
+
5
+ def initialize(*actions, subject, &block)
6
+ @actions = actions
7
+ @subject = subject
8
+ @condition = block
9
+ end
10
+
11
+ def permits?(*args)
12
+ if condition
13
+ !!condition.call(*args)
14
+ else
15
+ true
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,3 +1,3 @@
1
1
  module Cannie
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/cannie.rb CHANGED
@@ -1,8 +1,8 @@
1
+ require 'cannie/version'
1
2
  require 'cannie/exceptions'
2
3
  require 'cannie/rule'
3
4
  require 'cannie/permissions'
4
5
  require 'cannie/controller_extensions'
5
- require 'cannie/version'
6
6
 
7
7
  module Cannie
8
8
  end
@@ -0,0 +1,3 @@
1
+ Description:
2
+ The cannie:permissions generator creates an Permissions class in the models
3
+ directory.
@@ -0,0 +1,11 @@
1
+ module Cannie
2
+ module Generators
3
+ class PermissionsGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ def generate_permissions
7
+ copy_file 'permissions.rb', 'app/models/permissions.rb'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ class Permissions
2
+ include Cannie::Permissions
3
+
4
+ def initialize(user)
5
+ # Define abilities for the passed user:
6
+ #
7
+ # user ||= User.new
8
+ # if user.admin?
9
+ # allow :manage, on: Model
10
+ # else
11
+ # can :read, on: Model
12
+ # end
13
+ #
14
+ end
15
+ end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ class TestController < ActionController::Base
4
+ check_permissions
5
+
6
+ def action
7
+ end
8
+ end
9
+
10
+ describe Cannie::ControllerExtensions do
11
+ subject { TestController.new }
12
+
13
+ let(:before_filters) do
14
+ subject.class._process_action_callbacks.select{|f| f.kind == :before}.map(&:raw_filter)
15
+ end
16
+
17
+ let(:after_filters) do
18
+ subject.class._process_action_callbacks.select{|f| f.kind == :after}.map(&:raw_filter)
19
+ end
20
+
21
+ let(:permissions) do
22
+ Class.new do
23
+ include Cannie::Permissions
24
+
25
+ def initialize
26
+ allow :update, on: Array do |*attrs|
27
+ attrs.all?{|v| v % 3 == 0}
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ describe '.check_permissions' do
34
+ it 'raises exception if controller.permitted? evaluates to false' do
35
+ expect { after_filters.first.call(subject) }.to raise_error(Cannie::CheckPermissionsNotPerformed)
36
+ end
37
+
38
+ it 'does not raise exception if controller.permitted? evaluates to true' do
39
+ subject.stub(:permitted?).and_return(true)
40
+ expect { after_filters.first.call(subject) }.not_to raise_error
41
+ end
42
+
43
+ it 'raises exception if :if block executed in controller scope returns false' do
44
+ pending
45
+ end
46
+
47
+ it 'raises exception if :if block executed in controller scope returns true' do
48
+ pending
49
+ end
50
+ end
51
+
52
+ describe '.skip_check_permissions' do
53
+ it 'sets @_permitted to true to bypass permissions checking' do
54
+ subject.class.instance_eval do
55
+ skip_check_permissions
56
+ end
57
+
58
+ before_filters.first.call(subject)
59
+ expect(subject.permitted?).to be_true
60
+ end
61
+ end
62
+
63
+ describe '#can?' do
64
+ it 'raises SubjectNotSetError if value of :on param is nil' do
65
+ expect { subject.can? :action }.to raise_error(Cannie::SubjectNotSetError)
66
+ end
67
+
68
+ it 'returns true if action allowed on subject' do
69
+ subject.stub(:current_permissions).and_return permissions.new
70
+ expect(subject.can? :update, on: [3,6,9]).to be_true
71
+ end
72
+
73
+ it 'returns false if action not allowed on subject' do
74
+ subject.stub(:current_permissions).and_return permissions.new
75
+ expect(subject.can? :update, on: [3,7,9]).to be_false
76
+ end
77
+ end
78
+
79
+ describe '#permit!' do
80
+ it 'raises SubjectNotSetError if value of :on param is nil' do
81
+ expect { subject.permit! :action }.to raise_error(Cannie::SubjectNotSetError)
82
+ end
83
+
84
+ it 'assigns @_permitted to true if action is allowed on subject' do
85
+ subject.stub(:current_permissions).and_return permissions.new
86
+ subject.permit! :update, on: [3,6,9]
87
+ expect(subject.permitted?).to be_true
88
+ end
89
+
90
+ it 'raises AccessDenied error if action is not allowed on subject' do
91
+ subject.stub(:current_permissions).and_return permissions.new
92
+ expect { subject.permit! :update, on: [3,6,11] }.to raise_error(Cannie::ActionForbidden)
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cannie::Permissions do
4
+ subject { Class.new { include Cannie::Permissions } }
5
+
6
+ describe '#allow' do
7
+ before do
8
+ subject.class_eval do
9
+ def initialize
10
+ allow :read, :update, on: Array
11
+ end
12
+ end
13
+ end
14
+
15
+ let(:rules) { subject.new.send(:rules) }
16
+
17
+ it 'creates only one rule for each call of allow method' do
18
+ expect(rules.size).to eq(1)
19
+ end
20
+
21
+ it 'creates and stores Rule object with passed actions' do
22
+ expect(rules.first.actions).to eq([:read, :update])
23
+ end
24
+
25
+ it 'creates and stores Rule object with passed subject' do
26
+ expect(rules.first.subject).to eq(Array)
27
+ end
28
+
29
+ it 'creates and stores Rule object with passed condition block' do
30
+ expect(rules.first.condition).to be_nil
31
+ end
32
+ end
33
+
34
+ describe '#can?' do
35
+ before do
36
+ subject.class_eval do
37
+ def initialize
38
+ allow :read, on: Array
39
+
40
+ allow(:read, on: Array) do |*args|
41
+ args.all?{|v| v % 2 == 0}
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ let(:permissions) { subject.new }
48
+
49
+ it 'returns true if all rules for action & subject are permitted' do
50
+ expect(permissions.can? :read, on: [2, 4, 8]).to be_true
51
+ end
52
+
53
+ it 'returns false if not all rules for action & subject are permitted' do
54
+ expect(permissions.can? :read, on: [1, 2, 3]).to be_false
55
+ end
56
+ end
57
+
58
+ describe '#permit!' do
59
+ before do
60
+ subject.class_eval do
61
+ def initialize
62
+ allow :read, on: Array do |*args|
63
+ args.all?{|v| v % 2 == 0}
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ let(:permissions) { subject.new }
70
+
71
+ it 'raises AccessDenied error if permission checking failed' do
72
+ expect { permissions.permit! :read, on: [1, 2, 3] }.to raise_error
73
+ end
74
+
75
+ it 'does not raise AccessDenied error if permission checking was successfull' do
76
+ expect { permissions.permit! :read, on: [2, 4, 6] }.not_to raise_error
77
+ end
78
+ end
79
+
80
+ end
@@ -0,0 +1,15 @@
1
+ require 'simplecov'
2
+
3
+ SimpleCov.start
4
+
5
+ require 'rubygems'
6
+
7
+ require 'active_support/concern'
8
+ require 'action_controller/railtie'
9
+ require 'cannie'
10
+
11
+ RSpec.configure do |config|
12
+ config.treat_symbols_as_metadata_keys_with_true_values = true
13
+ config.filter_run focus: true
14
+ config.run_all_when_everything_filtered = true
15
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cannie
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - hck
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-09-07 00:00:00.000000000 Z
11
+ date: 2013-09-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: actionpack
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '4.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'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '4.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '4.0'
41
69
  description: Cannie is a gem for authorization/permissions checking on per-controller/per-action
42
70
  basis.
43
71
  email:
@@ -46,14 +74,25 @@ extensions: []
46
74
  extra_rdoc_files: []
47
75
  files:
48
76
  - .gitignore
77
+ - .rspec
49
78
  - Gemfile
50
79
  - LICENSE.txt
51
80
  - README.md
52
81
  - Rakefile
53
82
  - cannie.gemspec
54
83
  - lib/cannie.rb
84
+ - lib/cannie/controller_extensions.rb
85
+ - lib/cannie/exceptions.rb
86
+ - lib/cannie/permissions.rb
87
+ - lib/cannie/rule.rb
55
88
  - lib/cannie/version.rb
89
+ - lib/generators/cannie/permissions/USAGE
90
+ - lib/generators/cannie/permissions/permissions_generator.rb
91
+ - lib/generators/cannie/permissions/templates/permissions.rb
92
+ - spec/cannie/controller_extensions_spec.rb
93
+ - spec/cannie/permissions_spec.rb
56
94
  - spec/cannie/rule_spec.rb
95
+ - spec/spec_helper.rb
57
96
  homepage: http://guthub.com/hck/cannie
58
97
  licenses:
59
98
  - MIT
@@ -74,9 +113,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
74
113
  version: '0'
75
114
  requirements: []
76
115
  rubyforge_project:
77
- rubygems_version: 2.0.3
116
+ rubygems_version: 2.1.1
78
117
  signing_key:
79
118
  specification_version: 4
80
119
  summary: Simple gem for checking permissions on per-action basis
81
120
  test_files:
121
+ - spec/cannie/controller_extensions_spec.rb
122
+ - spec/cannie/permissions_spec.rb
82
123
  - spec/cannie/rule_spec.rb
124
+ - spec/spec_helper.rb