cannie 0.0.1 → 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.
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