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 +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
|