aclize 0.1.0 → 0.2.0

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