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 +4 -4
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/Gemfile +2 -1
- data/README.md +8 -8
- data/cannie.gemspec +2 -0
- data/lib/cannie/controller_extensions.rb +92 -0
- data/lib/cannie/exceptions.rb +7 -0
- data/lib/cannie/permissions.rb +71 -0
- data/lib/cannie/rule.rb +19 -0
- data/lib/cannie/version.rb +1 -1
- data/lib/cannie.rb +1 -1
- data/lib/generators/cannie/permissions/USAGE +3 -0
- data/lib/generators/cannie/permissions/permissions_generator.rb +11 -0
- data/lib/generators/cannie/permissions/templates/permissions.rb +15 -0
- data/spec/cannie/controller_extensions_spec.rb +95 -0
- data/spec/cannie/permissions_spec.rb +80 -0
- data/spec/spec_helper.rb +15 -0
- metadata +45 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec9ec75cbf4049751bdc12e6a3724880a39d729b
|
4
|
+
data.tar.gz: 0f5750f17a62373de640dfe3765c59bb640778d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 083fc7fa69ee1b74fb976edc60be681aea8d1f57e85b8d75d82ca1fa985b775e3f8bfd668d14f7da4dc1bc3b6d4d308579ad14fe24c864f1d589c3266ece6015
|
7
|
+
data.tar.gz: 92e63adbc1ad697c13bb14c460f4f516a008a8a98748cb8e5b4870d040c52e3fd58420f5a317b7941bb6fe650bb414910b20beeaaf1774073538eddb433048e3
|
data/.gitignore
CHANGED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/Gemfile
CHANGED
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
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
@@ -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,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
|
data/lib/cannie/rule.rb
ADDED
@@ -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
|
data/lib/cannie/version.rb
CHANGED
data/lib/cannie.rb
CHANGED
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -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.
|
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-
|
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.
|
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
|