aclize 0.1.0 → 0.2.0

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: b2f95212250080779c4cd98f14d0b83073d25743
4
- data.tar.gz: ec841f33a6a1e6f437e26a56cb1e28c9ad4a7d5e
3
+ metadata.gz: e883bc914bc0302e8ddd301838686b681420ed98
4
+ data.tar.gz: 0110271f53cc15fac67b6800f2bbc99de266dfc8
5
5
  SHA512:
6
- metadata.gz: 023df60e150155e7a932f22b342ef5a0219b308ebcd16c2d19af882926bdeca89284a54efadc0e860019d4c391b4d0d5cbc9ebe6bcc0b1b49c29c8f636e6c71b
7
- data.tar.gz: 92fed867177543f7ce28ebd27a1cab45e1189f494d5fabbc7ea03060e928e75665835de9cc67ef07470bc77d7a8399e7228b43a0f68a9d1032be16a73a6f08df
6
+ metadata.gz: 74e415381c8b03575c405cec1d54b74b887e3a1aef3e784b6b64e566192e637bf4bdab0c5e692c8f07a7d6903034443f4caab56eb6b054b4a226fb132f874a76
7
+ data.tar.gz: 67a452e4826d6450df25ed6cd5bd9272216545a0ce9a62cbecae798676c74bc79c5eb1a5d7fe3d0d1c57d619f08782ad3c31edd8ce81ea791808ca607054611d
data/README.md CHANGED
@@ -33,32 +33,34 @@ class ApplicationController < ActionController::Base
33
33
  protected
34
34
 
35
35
  def setup_acl
36
- if current_user.admin?
37
- # setup the ACL for admin users
38
- define_acl({
39
- controllers: {
40
- "*" => { allow: ["*"] } # grant permissions to access any action of any controller
41
- }
42
- })
43
- else
44
- # setup the ACL for other users
45
- define_acl({
46
- controllers: {
47
- posts: {
48
- allow: ["index", "show"] # allow to access only #index and #show actions of PostsController
49
- }
50
- }
51
- })
36
+
37
+ # define ACL for :admin
38
+ acl_for :admin do
39
+ controllers do
40
+ permit "*" # permit to access any action of any controller
41
+ end
42
+ end
43
+
44
+ # define acl for :user
45
+ acl_for :user do
46
+ controllers do
47
+ permit :posts, only: [:index, :show] # users can access only :index and :show actions of :posts controller
48
+ permit :comments, except: [:edit, :update, :destroy] # can also access all the actions of :comments controller, except for :edit, :update and :destroy actions
49
+ end
50
+
51
+ paths do
52
+ permit "path/[a-c]", "path/[0-9]+" # permit :user to access "path/a", "path/b", "path/c" and "path/<a digit>"
53
+ deny "path/b" # deny the access to "path/b"
54
+ end
52
55
  end
53
56
 
54
- filter_access!
57
+ set_current_role(current_user.role) # assuming that current_user is returning an object representing the current user
58
+ filter_access! # apply the ACL for the current user
55
59
  end
56
60
  end
57
61
  ```
58
62
 
59
- In the example above we asume that the user passed the authentication, so that we know the type of account the user has.
60
-
61
- __N.B:__ When you define the ACL with `define_acl(...)` you're defining it only for the current user.
63
+ __IMPORTANT:__ you have to tell __Aclize__ what is the role of the current user by calling `set_current_role(<ROLE>)` method, because if you don't specify any role, the default role `:all` will be used.
62
64
 
63
65
  Once you've defined the ACL, __Aclize__ will automatically manage the access control and will render the `403 Forbidden` page when the user doesn't have enough permissions to access it.
64
66
 
@@ -0,0 +1,93 @@
1
+ # The policy adopted by Aclize is:
2
+ # 1. By default all the controllers and paths are denied
3
+ # 2. On rules conflict, the more restrictive rule will be used
4
+ # 3. When permit and deny rules have the same restriction,
5
+ # deny rule will be used
6
+
7
+
8
+ module Aclize
9
+ class Acl::ControllersRegistry
10
+
11
+ attr_reader :permitted, :denied
12
+
13
+ def initialize
14
+ @permitted = {"*" => []}.nested_under_indifferent_access
15
+ @denied = {"*" => []}.nested_under_indifferent_access
16
+ end
17
+
18
+ # add a new permit rule to controllers registry
19
+ def permit(controller, only: nil, except: nil)
20
+ @permitted[controller] ||= []
21
+ @denied[controller] ||= []
22
+
23
+ raise ArgumentError.new("#permit cannot accept both :only and :except. At most one of them can be specified!") if only && except
24
+
25
+ if except
26
+ @permitted[controller] = ["*"]
27
+ @denied[controller] = normalize(except)
28
+ elsif only
29
+ @denied[controller] = []
30
+ @permitted[controller] = normalize(only)
31
+ else
32
+ @permitted[controller] = ["*"]
33
+ @denied[controller] = []
34
+ end
35
+ end
36
+
37
+ # check if each action in the list is allowed for the specified controller
38
+ def permitted?(controller, *args)
39
+ @permitted[controller] ||= []
40
+ @denied[controller] ||= []
41
+ actions = normalize(args)
42
+
43
+ if actions.empty?
44
+ return controller_permitted?(controller)
45
+ elsif controller_permitted?(controller)
46
+ # we know the there's at least one permitted action for this controller,
47
+ # so return false if there's at least one denied action in the list of actions to check
48
+ return false unless (actions & @denied[controller]).empty?
49
+
50
+ # we know that the actions aren't denied at controller level, so we could
51
+ # return true if all the actions are also permitted at controller level
52
+ return true if @permitted[controller].include?("*") || (actions & @permitted[controller]) == actions
53
+
54
+ # the actions aren't permitted at controller level, so if any of them is
55
+ # denied at global level, we will return false
56
+ return false unless (actions & @denied["*"]).empty?
57
+
58
+ # the actions aren't denied at global level, so we have to check if them
59
+ # are allowed at global level and return true if so
60
+ return true if @permitted["*"].include?("*") || (actions & @permitted["*"]) == actions
61
+ end
62
+
63
+ return false
64
+ end
65
+
66
+ protected
67
+
68
+ # check if the controller is permitted (at least one action in the controller should be permitted)
69
+ def controller_permitted?(controller)
70
+ # the simplies case is when there's a permission for at least one action for this controller
71
+ if @permitted[controller].empty?
72
+ # check if there're wildcard permissions
73
+ if @permitted["*"].empty?
74
+ # there isn't any global permission for the user
75
+ return false
76
+ else
77
+ # we have global permissions (for all the controllers), so we
78
+ # have to check if those actions weren't denied
79
+ denied_actions = @denied[controller] + @denied["*"]
80
+ return !(@permitted["*"] - denied_actions).empty?
81
+ end
82
+ else
83
+ # we are sure there's at least one permission for this controller
84
+ return true
85
+ end
86
+ end
87
+
88
+ def normalize(items)
89
+ result = items.nil? ? [] : items.is_a?(Array) ? items.flatten : [items]
90
+ return result.map { |x| x.to_s }
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,77 @@
1
+ # The policy for paths ACLs is slightly different from the controllers policy, because
2
+ # on rules conflicts, the deny rule always wins. Here is a brief description of the policy:
3
+ #
4
+ # 1. By default all the paths are not permitted
5
+ # 2. On rule conflict, the deny rule always wins
6
+ # 3. A path is permitted only if there's an explicit permit rule
7
+
8
+ module Aclize
9
+ class Acl::PathsRegistry
10
+
11
+ attr_reader :permitted, :denied
12
+
13
+ def initialize
14
+ @permitted = []
15
+ @denied = []
16
+ end
17
+
18
+
19
+ # permit a new path
20
+ def permit(*paths)
21
+ @permitted += normalize(paths)
22
+ @permitted.uniq!
23
+ end
24
+
25
+
26
+ # deny a path
27
+ def deny(*paths)
28
+ @denied += normalize(paths)
29
+ @denied.uniq!
30
+ end
31
+
32
+
33
+ # Check if the paths are permitted. This method should return true
34
+ # only if each path passed as argument is permitted (isn't denied and
35
+ # have an explicit permission).
36
+ def permitted?(*args)
37
+ permitted = false
38
+
39
+ return false if denied?(args)
40
+
41
+ # each path should have an explicit permission in order to return true
42
+ args.flatten.each do |path|
43
+ # we assume that the path isn't permitted
44
+ permitted = false
45
+
46
+ # iterate over permitted paths and check if any of them matches the current one
47
+ @permitted.each do |permitted_path|
48
+ permitted ||= !!path.match(/^#{permitted_path}$/)
49
+ # stop iteration if the path is permitted
50
+ break if permitted
51
+ end
52
+ #return false if the path isn't permitted
53
+ return false unless permitted
54
+ end
55
+
56
+ return permitted
57
+ end
58
+
59
+
60
+ # Check if any of the paths is explicitly denied
61
+ def denied?(*args)
62
+ @denied.each do |denied_path|
63
+ args.flatten.each do |path|
64
+ return true if path.match(/^#{denied_path}$/)
65
+ end
66
+ end
67
+
68
+ return false
69
+ end
70
+
71
+ protected
72
+
73
+ def normalize(items)
74
+ return items.nil? ? [] : items.is_a?(Array) ? items.flatten : [items]
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,29 @@
1
+
2
+ module Aclize
3
+ class Acl::Role
4
+ require "aclize/acl/controllers_registry"
5
+ require "aclize/acl/paths_registry"
6
+
7
+ def initialize(name)
8
+ @name = name.to_s
9
+ @controllers = Aclize::Acl::ControllersRegistry.new
10
+ @paths = Aclize::Acl::PathsRegistry.new
11
+ end
12
+
13
+ def controllers(&block)
14
+ if block_given?
15
+ @controllers.instance_eval(&block)
16
+ else
17
+ return @controllers
18
+ end
19
+ end
20
+
21
+ def paths(&block)
22
+ if block_given?
23
+ @paths.instance_eval(&block)
24
+ else
25
+ return @paths
26
+ end
27
+ end
28
+ end
29
+ end
data/lib/aclize/acl.rb ADDED
@@ -0,0 +1,23 @@
1
+
2
+ module Aclize
3
+ class Acl
4
+ require "aclize/acl/role"
5
+
6
+ attr_reader :roles
7
+
8
+ def initialize
9
+ @roles = {
10
+ all: Aclize::Acl::Role.new(:all)
11
+ }.nested_under_indifferent_access
12
+ end
13
+
14
+ def get_acl_for(role)
15
+ return @roles[role] || @roles[:all]
16
+ end
17
+
18
+ def setup(role = :all, &block)
19
+ @roles[role] ||= Aclize::Acl::Role.new(role)
20
+ @roles[role].instance_eval(&block)
21
+ end
22
+ end
23
+ end
data/lib/aclize/helper.rb CHANGED
@@ -7,55 +7,28 @@ module Aclize
7
7
 
8
8
  # Check if the user have permission to access the action
9
9
  def action_allowed?(controller, action)
10
- actions_allowed?(controller, [action], :all)
10
+ actions_allowed?(controller, [action])
11
11
  end
12
12
 
13
13
 
14
- # Returns a boolean that indicates if the current used have enought permissions to access the
15
- # specified list of actions. The policy argument indicates the type of verification. By default,
16
- # its value is :all, that means the all the actions passed as argument have to be allowed. If the
17
- # policy if :any, is sufficient that at least one of the specified actions to be allowed.
18
- def actions_allowed?(controller, actions = [], policy = :all)
19
- acl = @_aclize_acl[:controllers]
20
- # If there's an entry for this controller in @acl, use that rule for permissions check.
21
- # Otherwise, check if there's an '*' entry if @acl and use that rules.
22
- methods = ( acl[controller.to_s] || acl['*'] || {} )
23
- allow = methods["allow"] || []
24
- deny = methods["deny"] || []
25
-
26
- # If the array of methods is empty, the controller isn't allowed
27
- return false if allow.empty?
28
-
29
- # Force the list of actions to be an Array of strings
30
- normalized_actions = (actions.is_a?(Array) ? actions : [actions]).map{|action| action.to_s }
31
-
32
- # If all the methods of the current controller are allowed or the list of actions to check is empty, return true
33
- return true if (allow.include?("*") && (deny & normalized_actions).empty?) || normalized_actions.empty?
34
-
35
- case policy.to_sym
36
- when :all then return (deny & normalized_actions).empty? && (allow & normalized_actions == normalized_actions) # all the actions have to be allowed
37
- when :any then return !((allow & normalized_actions) - deny).empty? # at least one action have to be allowed
38
- else
39
- logger.warn "Invalid policy: #{policy}."
40
- return false
41
- end
14
+ # Returns a boolean that indicates if the current user have enought permissions to access the
15
+ # specified list of actions.
16
+ def actions_allowed?(controller, actions = [])
17
+ acl = @_aclize_acl.get_acl_for(get_current_role)
18
+ return acl.controllers.permitted?(controller, actions)
42
19
  end
43
20
 
44
21
 
45
22
  # Verify if the path could be accessed by the user. Returns true when
46
23
  # the path is accessible
47
24
  def path_allowed?(path)
48
- paths = @_aclize_acl[:paths]
49
-
50
- (paths[:deny] || []).each do |filter|
51
- return false if !path.match(Regexp(filter)).nil?
52
- end
25
+ acl = @_aclize_acl.get_acl_for(get_current_role)
26
+ return acl.paths.permitted?(path)
27
+ end
53
28
 
54
- (paths[:allow] || []).each do |filter|
55
- return true if !path.match(Regexp(filter)).nil?
56
- end
57
29
 
58
- return false
30
+ def get_current_role
31
+ return @_aclize_current_role || :all
59
32
  end
60
33
  end
61
34
  end
@@ -1,3 +1,3 @@
1
1
  module Aclize
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/aclize.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "aclize/version"
2
+ require "aclize/acl"
2
3
  require "aclize/helper"
3
4
  require "i18n"
4
5
  require "action_controller"
@@ -31,33 +32,55 @@ module Aclize
31
32
  # The Initializer module will be used to initialize instance variables and to setup defaults.
32
33
  module Initializer
33
34
  def initialize
34
- @_aclize_acl = {controllers: {}, paths: {} }.nested_under_indifferent_access
35
+ @_aclize_acl ||= Aclize::Acl.new
36
+ @_aclize_current_role = nil
35
37
  super
36
38
  end
37
39
  end
38
40
 
39
41
  protected
40
42
 
41
- # Returns the ACL definition as a Hash
43
+ # Returns the ACL definition
42
44
  def get_acl_definition
43
45
  return @_aclize_acl
44
46
  end
45
47
 
46
- # Defines the structure of ACL for the current user
47
- # TODO: implement a better or an alternative way for ACL definition
48
- def define_acl(acl)
49
- raise "Invalid ACL definition type: (expected: Hash, got: #{acl.class})" unless acl.is_a? Hash
50
48
 
51
- if acl.has_key?(:controllers) && acl[:controllers].is_a?(Hash)
52
- @_aclize_acl[:controllers] = acl[:controllers]
53
- end
49
+ def set_current_role(role)
50
+ @_aclize_current_role = role
51
+ end
52
+
53
+ def get_current_role
54
+ return @_aclize_current_role || :all
55
+ end
56
+
54
57
 
55
- if acl.has_key?(:paths) && acl[:paths].is_a?(Hash)
56
- @_aclize_acl[:paths] = acl[:paths]
58
+ # setup the ACL for a role
59
+ def acl_for(role = :all, &block)
60
+ @_aclize_acl.setup(role, &block)
61
+ end
62
+
63
+
64
+ # apply the ACL for a specific role and unauthorize if the user is not permitted
65
+ # to access controller action or the path
66
+ def treat_as(role)
67
+ acl = @_aclize_acl.get_acl_for(role)
68
+ unauthorize! unless acl
69
+
70
+ if acl.controllers.permitted?(controller_name, action_name)
71
+ unauthorize! if acl.paths.denied?(request.path_info)
72
+ else
73
+ unauthorize! unless acl.paths.permitted?(request.path_info)
57
74
  end
58
75
  end
59
76
 
60
77
 
78
+ # use the current_role value to apply ACL
79
+ def filter_access!
80
+ treat_as get_current_role
81
+ end
82
+
83
+
61
84
  # In no callbacks were defined for unauthorized access, Aclize will render a
62
85
  # default 403 Forbidden page. Otherwise, the control will be passed to the callback.
63
86
  def unauthorize!
@@ -74,59 +97,6 @@ module Aclize
74
97
  end
75
98
  end
76
99
 
77
-
78
- # Check if the current user have enough permissions to access the current controller/path
79
- def filter_access!
80
- unauthorize! if acl_action_denied? || acl_path_denied? || !(acl_action_allowed? || acl_path_allowed?)
81
- end
82
-
83
-
84
- # check if the current action is denied
85
- def acl_action_denied?
86
- actions = (@_aclize_acl[:controllers][controller_name] || @_aclize_acl[:controllers]["*"] || {})[:deny] || []
87
- actions.map!{|action| action.to_s }
88
-
89
- return actions.include?("*") || actions.include?(action_name)
90
- end
91
-
92
-
93
- # check if the current action is allowed
94
- def acl_action_allowed?
95
- actions = (@_aclize_acl[:controllers][controller_name] || @_aclize_acl[:controllers]["*"] || {})[:allow] || []
96
- actions.map!{|action| action.to_s }
97
-
98
- return actions.include?("*") || actions.include?(action_name)
99
- end
100
-
101
-
102
- # check if the current path is denied
103
- def acl_path_denied?
104
- paths = @_aclize_acl[:paths][:deny] || []
105
- denied = false
106
-
107
- paths.each do |path|
108
- denied ||= !request.path_info.match(Regexp.new("^#{path}$")).nil?
109
- break if denied
110
- end
111
-
112
- return denied
113
- end
114
-
115
-
116
- # check if the current path is allowed
117
- def acl_path_allowed?
118
- paths = @_aclize_acl[:paths][:allow] || []
119
- allowed = false
120
-
121
- paths.each do |path|
122
- allowed ||= !request.path_info.match(Regexp.new("^#{path}$")).nil?
123
- break if allowed
124
- end
125
-
126
- return allowed
127
- end
128
-
129
-
130
100
  # register a callback to call when the user is not authorized to access the page
131
101
  def register_callback(&block)
132
102
  @_aclize_callback = block
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aclize
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Groza Sergiu
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-11-19 00:00:00.000000000 Z
11
+ date: 2015-11-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -76,6 +76,10 @@ files:
76
76
  - config/locales/en.yml
77
77
  - config/locales/it.yml
78
78
  - lib/aclize.rb
79
+ - lib/aclize/acl.rb
80
+ - lib/aclize/acl/controllers_registry.rb
81
+ - lib/aclize/acl/paths_registry.rb
82
+ - lib/aclize/acl/role.rb
79
83
  - lib/aclize/helper.rb
80
84
  - lib/aclize/version.rb
81
85
  homepage: https://github.com/serioja90/aclize