be9-acl9 0.10.0 → 0.11.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.
@@ -1,3 +1,11 @@
1
+ h2. 0.11.0 (16-Sep-2009)
2
+
3
+ * :protect_global_roles
4
+ * Subject#roles renamed to Subject#role_objects
5
+ * Fix namespaced models in roles backend (thanks goes to Tomas Jogin)
6
+ * Action name override in boolean methods.
7
+ * @:query_method@ option for @access_control@.
8
+
1
9
  h2. 0.10.0 (03-May-2009)
2
10
 
3
11
  * Use context+matchy combo for testing
@@ -1,4 +1,4 @@
1
- h1. Acl9
1
+ h1. Introduction
2
2
 
3
3
  Acl9 is yet another solution for role-based authorization in Rails. It consists of two
4
4
  subsystems which can be used separately.
@@ -38,6 +38,17 @@ An example:
38
38
  end
39
39
  </code></pre>
40
40
 
41
+ h1. Contacts
42
+
43
+ Acl9 is hosted "on the GitHub":http://github.com/be9/acl9.
44
+
45
+ You may find tutorials and additional docs on the "wiki page":http://wiki.github.com/be9/acl9.
46
+
47
+ Rdocs are available "here":http://rdoc.info/projects/be9/acl9.
48
+
49
+ If you have questions, please post to the
50
+ "acl9-discuss group":http://groups.google.com/group/acl9-discuss
51
+
41
52
  h1. Installation
42
53
 
43
54
  Acl9 can be installed as a gem from "GitHub":http://github.com.
@@ -875,5 +886,3 @@ An imaginary view:
875
886
  </code></pre>
876
887
 
877
888
  Copyright (c) 2009 Oleg Dashevskii, released under the MIT license.
878
-
879
- Contact me at "#{%w(Oleg Dashevskii).join('').downcase}@gmail.com"
data/Rakefile CHANGED
@@ -27,3 +27,13 @@ Rake::TestTask.new(:test) do |test|
27
27
  test.pattern = 'test/**/*_test.rb'
28
28
  test.verbose = false
29
29
  end
30
+
31
+ begin
32
+ require 'yard'
33
+
34
+ YARD::Rake::YardocTask.new do |t|
35
+ t.files = ['lib/**/*.rb']
36
+ #t.options = ['--any', '--extra', '--opts'] # optional
37
+ end
38
+ rescue LoadError
39
+ end
@@ -1,4 +1,4 @@
1
1
  ---
2
- :minor: 10
2
+ :minor: 11
3
3
  :patch: 0
4
4
  :major: 0
@@ -3,6 +3,7 @@ module Acl9
3
3
  :default_role_class_name => 'Role',
4
4
  :default_subject_class_name => 'User',
5
5
  :default_subject_method => :current_user,
6
+ :protect_global_roles => false,
6
7
  }
7
8
 
8
9
  mattr_reader :config
@@ -46,18 +46,39 @@ module Acl9
46
46
 
47
47
  method = opts[:as_method]
48
48
 
49
+ query_method_available = true
49
50
  generator = case
50
51
  when method && filter
51
52
  Acl9::Dsl::Generators::FilterMethod.new(subject_method, method)
52
53
  when method && !filter
54
+ query_method_available = false
53
55
  Acl9::Dsl::Generators::BooleanMethod.new(subject_method, method)
54
56
  else
55
57
  Acl9::Dsl::Generators::FilterLambda.new(subject_method)
56
58
  end
57
59
 
58
60
  generator.acl_block!(&block)
59
-
61
+
60
62
  generator.install_on(self, opts)
63
+
64
+ if query_method_available && (query_method = opts.delete(:query_method))
65
+ case query_method
66
+ when true
67
+ if method
68
+ query_method = "#{method}?"
69
+ else
70
+ raise ArgumentError, "You must specify :query_method as Symbol"
71
+ end
72
+ when Symbol, String
73
+ # okay here
74
+ else
75
+ raise ArgumentError, "Invalid value for :query_method"
76
+ end
77
+
78
+ second_generator = Acl9::Dsl::Generators::BooleanMethod.new(subject_method, query_method)
79
+ second_generator.acl_block!(&block)
80
+ second_generator.install_on(self, opts)
81
+ end
61
82
  end
62
83
  end
63
84
  end
@@ -95,9 +95,13 @@ module Acl9
95
95
 
96
96
  alias action actions
97
97
 
98
+ def logged_in; false end
98
99
  def anonymous; nil end
99
100
  def all; true end
100
- def logged_in; false end
101
+
102
+ alias everyone all
103
+ alias everybody all
104
+ alias anyone all
101
105
 
102
106
  def _parse_and_add_rule(*args)
103
107
  options = args.extract_options!
@@ -108,9 +112,9 @@ module Acl9
108
112
 
109
113
  role_checks = args.map do |who|
110
114
  case who
111
- when nil then "#{_subject_ref}.nil?" # anonymous
112
- when false then "!#{_subject_ref}.nil?" # logged_in
113
- when true then "true" # all
115
+ when anonymous() then "#{_subject_ref}.nil?"
116
+ when logged_in() then "!#{_subject_ref}.nil?"
117
+ when all() then "true"
114
118
  else
115
119
  "!#{_subject_ref}.nil? && #{_subject_ref}.has_role?('#{who.to_s.singularize}', #{object})"
116
120
  end
@@ -1,7 +1,35 @@
1
1
  require File.join(File.dirname(__FILE__), 'dsl_base')
2
2
 
3
3
  module Acl9
4
+ ##
5
+ # This exception is raised whenever ACL block finds that the current user
6
+ # is not authorized for the controller action he wants to execute.
7
+ # @example How to catch this exception in ApplicationController
8
+ # class ApplicationController < ActionController::Base
9
+ # rescue_from 'Acl9::AccessDenied', :with => :access_denied
10
+ #
11
+ # # ...other stuff...
12
+ # private
13
+ #
14
+ # def access_denied
15
+ # if current_user
16
+ # # It's presumed you have a template with words of pity and regret
17
+ # # for unhappy user who is not authorized to do what he wanted
18
+ # render :template => 'home/access_denied'
19
+ # else
20
+ # # In this case user has not even logged in. Might be OK after login.
21
+ # flash[:notice] = 'Access denied. Try to log in first.'
22
+ # redirect_to login_path
23
+ # end
24
+ # end
25
+ # end
26
+ #
4
27
  class AccessDenied < StandardError; end
28
+
29
+ ##
30
+ # This exception is raised when acl9 has generated invalid code for the
31
+ # filtering method or block. Should never happen, and it's a bug when it
32
+ # happens.
5
33
  class FilterSyntaxError < StandardError; end
6
34
 
7
35
  module Dsl
@@ -49,7 +77,7 @@ module Acl9
49
77
  logger.debug self.to_s
50
78
  logger.debug "======"
51
79
  end
52
-
80
+
53
81
  def logger
54
82
  ActionController::Base.logger
55
83
  end
@@ -76,13 +104,15 @@ module Acl9
76
104
  end
77
105
  end
78
106
  RUBY
79
-
107
+
80
108
  self.instance_eval(code, __FILE__, __LINE__)
81
109
  rescue SyntaxError
82
110
  raise FilterSyntaxError, code
83
111
  end
84
112
  end
85
-
113
+
114
+ ################################################################
115
+
86
116
  class FilterMethod < BaseGenerator
87
117
  def initialize(subject_method, method_name)
88
118
  super
@@ -90,7 +120,7 @@ module Acl9
90
120
  @method_name = method_name
91
121
  @controller = nil
92
122
  end
93
-
123
+
94
124
  def install_on(controller_class, options)
95
125
  super
96
126
  _add_method(controller_class)
@@ -105,7 +135,7 @@ module Acl9
105
135
  rescue SyntaxError
106
136
  raise FilterSyntaxError, code
107
137
  end
108
-
138
+
109
139
  def to_method_code
110
140
  <<-RUBY
111
141
  def #{@method_name}
@@ -116,7 +146,9 @@ module Acl9
116
146
  RUBY
117
147
  end
118
148
  end
119
-
149
+
150
+ ################################################################
151
+
120
152
  class BooleanMethod < FilterMethod
121
153
  def install_on(controller_class, opts)
122
154
  debug_dump(controller_class) if opts[:debug]
@@ -128,12 +160,20 @@ module Acl9
128
160
  end
129
161
  end
130
162
 
131
- protected
132
-
163
+ protected
164
+
133
165
  def to_method_code
134
166
  <<-RUBY
135
- def #{@method_name}(options = {})
136
- #{allowance_expression}
167
+ def #{@method_name}(*args)
168
+ options = args.extract_options!
169
+
170
+ unless args.size <= 1
171
+ raise ArgumentError, "call #{@method_name} with 0, 1 or 2 arguments"
172
+ end
173
+
174
+ action_name = args.empty? ? self.action_name : args.first.to_s
175
+
176
+ return #{allowance_expression}
137
177
  end
138
178
  RUBY
139
179
  end
@@ -143,6 +183,8 @@ module Acl9
143
183
  end
144
184
  end
145
185
 
186
+ ################################################################
187
+
146
188
  class HelperMethod < BooleanMethod
147
189
  def initialize(subject_method, method)
148
190
  super
@@ -8,9 +8,9 @@ module Acl9
8
8
  def access_control(method, opts = {}, &block)
9
9
  subject_method = opts.delete(:subject_method) || Acl9::config[:default_subject_method]
10
10
  raise ArgumentError, "Block must be supplied to access_control" unless block
11
-
11
+
12
12
  generator = Acl9::Dsl::Generators::HelperMethod.new(subject_method, method)
13
-
13
+
14
14
  generator.acl_block!(&block)
15
15
  generator.install_on(self, opts)
16
16
  end
@@ -2,29 +2,75 @@ require File.join(File.dirname(__FILE__), 'model_extensions', 'subject')
2
2
  require File.join(File.dirname(__FILE__), 'model_extensions', 'object')
3
3
 
4
4
  module Acl9
5
- module ModelExtensions
5
+ module ModelExtensions #:nodoc:
6
6
  def self.included(base)
7
7
  base.extend(ClassMethods)
8
8
  end
9
9
 
10
10
  module ClassMethods
11
+ # Add #has_role? and other role methods to the class.
12
+ # Makes a class a auth. subject class.
13
+ #
14
+ # @param [Hash] options the options for tuning
15
+ # @option options [String] :role_class_name (Acl9::config[:default_role_class_name])
16
+ # Class name of the role class (e.g. 'AccountRole')
17
+ # @option options [String] :join_table_name (Acl9::config[:default_join_table_name])
18
+ # Join table name (e.g. 'accounts_account_roles')
19
+ # @example
20
+ # class User < ActiveRecord::Base
21
+ # acts_as_authorization_subject
22
+ # end
23
+ #
24
+ # user = User.new
25
+ # user.roles #=> returns Role objects, associated with the user
26
+ # user.has_role!(...)
27
+ # user.has_no_role!(...)
28
+ #
29
+ # # other functions from Acl9::ModelExtensions::Subject are made available
30
+ #
31
+ # @see Acl9::ModelExtensions::Subject
32
+ #
11
33
  def acts_as_authorization_subject(options = {})
12
34
  role = options[:role_class_name] || Acl9::config[:default_role_class_name]
13
- has_and_belongs_to_many :roles, :class_name => role
35
+ join_table = options[:join_table_name] || Acl9::config[:default_join_table_name] ||
36
+ join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(role))
37
+
38
+ has_and_belongs_to_many :role_objects, :class_name => role, :join_table => join_table
14
39
 
15
40
  cattr_accessor :_auth_role_class_name
16
41
  self._auth_role_class_name = role
17
42
 
18
- include Acl9::ModelExtensions::Subject
43
+ include Acl9::ModelExtensions::Subject
19
44
  end
20
45
 
46
+ # Add role query and set methods to the class (making it an auth object class).
47
+ #
48
+ # @param [Hash] options the options for tuning
49
+ # @option options [String] :subject_class_name (Acl9::config[:default_subject_class_name])
50
+ # Subject class name (e.g. 'User', or 'Account)
51
+ # @option options [String] :role_class_name (Acl9::config[:default_role_class_name])
52
+ # Role class name (e.g. 'AccountRole')
53
+ # @example
54
+ # class Product < ActiveRecord::Base
55
+ # acts_as_authorization_object
56
+ # end
57
+ #
58
+ # product = Product.new
59
+ # product.accepted_roles #=> returns Role objects, associated with the product
60
+ # product.users #=> returns User objects, associated with the product
61
+ # product.accepts_role!(...)
62
+ # product.accepts_no_role!(...)
63
+ # # other functions from Acl9::ModelExtensions::Object are made available
64
+ #
65
+ # @see Acl9::ModelExtensions::Object
66
+ #
21
67
  def acts_as_authorization_object(options = {})
22
68
  subject = options[:subject_class_name] || Acl9::config[:default_subject_class_name]
23
- subj_table = subject.tableize
69
+ subj_table = subject.constantize.table_name
24
70
  subj_col = subject.underscore
25
71
 
26
72
  role = options[:role_class_name] || Acl9::config[:default_role_class_name]
27
- role_table = role.tableize
73
+ role_table = role.constantize.table_name
28
74
 
29
75
  sql_tables = <<-EOS
30
76
  FROM #{subj_table}
@@ -36,21 +82,49 @@ module Acl9
36
82
  WHERE authorizable_type = '#{self.class.base_class.to_s}'
37
83
  AND authorizable_id = #{id}
38
84
  EOS
39
-
85
+
40
86
  has_many :accepted_roles, :as => :authorizable, :class_name => role, :dependent => :destroy
41
87
 
42
88
  has_many :"#{subj_table}",
43
89
  :finder_sql => ("SELECT DISTINCT #{subj_table}.*" + sql_tables + sql_where),
44
90
  :counter_sql => ("SELECT COUNT(DISTINCT #{subj_table}.id)" + sql_tables + sql_where),
45
91
  :readonly => true
46
-
92
+
47
93
  include Acl9::ModelExtensions::Object
48
94
  end
49
95
 
96
+ # Make a class an auth role class.
97
+ #
98
+ # You'll probably never create or use objects of this class directly.
99
+ # Various auth. subject and object methods will do that for you
100
+ # internally.
101
+ #
102
+ # @param [Hash] options the options for tuning
103
+ # @option options [String] :subject_class_name (Acl9::config[:default_subject_class_name])
104
+ # Subject class name (e.g. 'User', or 'Account)
105
+ # @option options [String] :join_table_name (Acl9::config[:default_join_table_name])
106
+ # Join table name (e.g. 'accounts_account_roles')
107
+ #
108
+ # @example
109
+ # class Role < ActiveRecord::Base
110
+ # acts_as_authorization_role
111
+ # end
112
+ #
113
+ # @see Acl9::ModelExtensions::Subject#has_role!
114
+ # @see Acl9::ModelExtensions::Subject#has_role?
115
+ # @see Acl9::ModelExtensions::Subject#has_no_role!
116
+ # @see Acl9::ModelExtensions::Object#accepts_role!
117
+ # @see Acl9::ModelExtensions::Object#accepts_role?
118
+ # @see Acl9::ModelExtensions::Object#accepts_no_role!
50
119
  def acts_as_authorization_role(options = {})
51
120
  subject = options[:subject_class_name] || Acl9::config[:default_subject_class_name]
121
+ join_table = options[:join_table_name] || Acl9::config[:default_join_table_name] ||
122
+ join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(subject))
123
+
124
+ has_and_belongs_to_many subject.demodulize.tableize.to_sym,
125
+ :class_name => subject,
126
+ :join_table => join_table
52
127
 
53
- has_and_belongs_to_many subject.tableize.to_sym
54
128
  belongs_to :authorizable, :polymorphic => true
55
129
  end
56
130
  end
@@ -1,24 +1,56 @@
1
1
  module Acl9
2
2
  module ModelExtensions
3
3
  module Object
4
+ ##
5
+ # Role check.
6
+ #
7
+ # @return [Boolean] Returns true if +subject+ has a role +role_name+ on this object.
8
+ #
9
+ # @param [Symbol,String] role_name Role name
10
+ # @param [Subject] subject Subject to add role for
11
+ # @see Acl9::ModelExtensions::Subject#has_role?
4
12
  def accepts_role?(role_name, subject)
5
13
  subject.has_role? role_name, self
6
14
  end
7
15
 
16
+ ##
17
+ # Add role on the object to specified subject.
18
+ #
19
+ # @param [Symbol,String] role_name Role name
20
+ # @param [Subject] subject Subject to add role for
21
+ # @see Acl9::ModelExtensions::Subject#has_role!
8
22
  def accepts_role!(role_name, subject)
9
23
  subject.has_role! role_name, self
10
24
  end
11
25
 
26
+ ##
27
+ # Free specified subject of a role on this object.
28
+ #
29
+ # @param [Symbol,String] role_name Role name
30
+ # @param [Subject] subject Subject to remove role from
31
+ # @see Acl9::ModelExtensions::Subject#has_no_role!
12
32
  def accepts_no_role!(role_name, subject)
13
33
  subject.has_no_role! role_name, self
14
34
  end
15
35
 
36
+ ##
37
+ # Are there any roles for the specified +subject+ on this object?
38
+ #
39
+ # @param [Subject] subject Subject to query roles
40
+ # @return [Boolean] Returns true if +subject+ has any roles on this object.
41
+ # @see Acl9::ModelExtensions::Subject#has_roles_for?
16
42
  def accepts_roles_by?(subject)
17
43
  subject.has_roles_for? self
18
44
  end
19
45
 
20
46
  alias :accepts_role_by? :accepts_roles_by?
21
47
 
48
+ ##
49
+ # Which roles does +subject+ have on this object?
50
+ #
51
+ # @return [Array<Role>] Role instances, associated both with +subject+ and +object+
52
+ # @param [Subject] subject Subject to query roles
53
+ # @see Acl9::ModelExtensions::Subject#roles_for
22
54
  def accepted_roles_by(subject)
23
55
  subject.roles_for self
24
56
  end