aegis 2.0.4 → 2.1.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.
data/README.rdoc CHANGED
@@ -69,19 +69,25 @@ There is an awesome {documentation wiki}[http://wiki.github.com/makandra/aegis/]
69
69
 
70
70
  == Installation
71
71
 
72
- Add the following to your <tt>Initializer.run</tt> block in your <tt>environment.rb</tt>:
72
+ Aegis is a gem, which you can install with
73
+ sudo gem install aegis
74
+
75
+ In Rails 2, add the following to your <tt>environment.rb</tt>:
73
76
  config.gem 'aegis'
74
- Then do a
75
- sudo rake gems:install
76
77
 
77
- Alternatively, use
78
- sudo gem install aegis
78
+ In Rails 3, add the following to your <tt>Gemfile</tt>:
79
+ gem 'aegis'
80
+
81
+
82
+ == Rails 3 compatibility
83
+
84
+ We cannot guarantee Rails 3 compatibility at this point, but we will upgrade the gem when Rails 3 is released.
79
85
 
80
86
 
81
87
  == Credits
82
88
 
83
89
  Henning Koch, Tobias Kraze
84
90
 
85
- {gem-session.com}[http://gem-session.com/]
91
+ {makandra.com}[http://makandra.com/]
86
92
 
87
- {www.makandra.de}[http://www.makandra.de/]
93
+ {gem-session.com}[http://gem-session.com/]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.4
1
+ 2.1.0
data/aegis.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{aegis}
8
- s.version = "2.0.4"
8
+ s.version = "2.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Henning Koch", "Tobias Kraze"]
12
- s.date = %q{2010-05-29}
12
+ s.date = %q{2010-07-25}
13
13
  s.description = %q{Aegis is an authorization solution for Ruby on Rails that supports roles and a RESTish, resource-style declaration of permission rules.}
14
14
  s.email = %q{henning.koch@makandra.de}
15
15
  s.extra_rdoc_files = [
@@ -92,10 +92,10 @@ Gem::Specification.new do |s|
92
92
  "spec/app_root/lib/console_with_fixtures.rb",
93
93
  "spec/action_controller_spec.rb",
94
94
  "spec/controllers/reviews_controller_spec.rb",
95
- "spec/has_role_spec.rb",
96
- "spec/permissions_spec.rb",
97
95
  "spec/spec_helper.rb",
98
96
  "spec/loader_spec.rb",
97
+ "spec/has_role_spec.rb",
98
+ "spec/permissions_spec.rb",
99
99
  "spec/sieve_spec.rb"
100
100
  ]
101
101
 
data/lib/aegis/action.rb CHANGED
@@ -24,12 +24,9 @@ module Aegis
24
24
 
25
25
  def may?(user, *args)
26
26
  context = extract_context(user, args)
27
- may = user.role.may_by_default?
28
- for sieve in sieves
29
- opinion = sieve.may?(context, *args)
30
- may = opinion unless opinion.nil?
27
+ user.roles.any? do |role|
28
+ may_as_role?(role, context, *args)
31
29
  end
32
- may
33
30
  end
34
31
 
35
32
  def may!(user, *args)
@@ -82,6 +79,16 @@ module Aegis
82
79
 
83
80
  private
84
81
 
82
+ def may_as_role?(role, context, *args)
83
+ context.role = role
84
+ may = role.may_by_default?
85
+ for sieve in sieves
86
+ opinion = sieve.may?(context, *args)
87
+ may = opinion unless opinion.nil?
88
+ end
89
+ may
90
+ end
91
+
85
92
  # not *args so we can change the array reference
86
93
  def extract_context(user, args)
87
94
  context = {}
@@ -3,35 +3,35 @@ module Aegis
3
3
 
4
4
  def has_role(options = {})
5
5
 
6
- if options[:accessor]
7
- options[:reader] = "#{options[:accessor]}"
8
- options[:writer] = "#{options[:accessor]}="
9
- options.delete(:accessor)
10
- end
11
-
12
- get_role_name = (options[:reader] || "role_name").to_sym
13
- set_role_name = (options[:writer] || "role_name=").to_sym
14
-
15
6
  permissions = lambda { Aegis::Permissions.app_permissions(options[:permissions]) }
16
7
 
17
8
  may_pattern = /^may_(.+?)([\!\?])$/
18
9
 
10
+ send :define_method, :role_names do
11
+ (role_name || '').split(/\s*,\s*/)
12
+ end
13
+
14
+ send :define_method, :role_names= do |role_names|
15
+ self.role_name = role_names.join(',')
16
+ end
17
+
19
18
  send :define_method, :role do
20
- permissions.call.find_role_by_name(send(get_role_name))
19
+ roles.first
21
20
  end
22
21
 
23
- send :define_method, :role= do |role|
24
- send(set_role_name, role.name)
22
+ send :define_method, :roles do
23
+ role_names.collect do |role_name|
24
+ permissions.call.find_role_by_name(role_name)
25
+ end.compact
25
26
  end
26
27
 
27
28
  metaclass.send :define_method, :validates_role do |*validate_options|
28
29
  validate_options = validate_options[0] || {}
29
30
 
30
31
  send :define_method, :validate_role do
31
- role = permissions.call.find_role_by_name(send(get_role_name))
32
- unless role
32
+ unless role_names.size > 0 && role_names.size == roles.size
33
33
  message = validate_options[:message] || I18n.translate('activerecord.errors.messages.inclusion')
34
- errors.add get_role_name, message
34
+ errors.add :role_name, message
35
35
  end
36
36
  end
37
37
 
@@ -46,8 +46,8 @@ module Aegis
46
46
  end
47
47
 
48
48
  send :define_method, :set_default_role_name do
49
- if new_record? && send(get_role_name).blank?
50
- send(set_role_name, options[:default])
49
+ if new_record? && role_name.blank?
50
+ self.role_name = options[:default]
51
51
  end
52
52
  end
53
53
 
data/lib/aegis/sieve.rb CHANGED
@@ -9,7 +9,7 @@ module Aegis
9
9
  end
10
10
 
11
11
  def may?(context, *args)
12
- matches_role = @role_name == 'everyone' || @role_name == context.user.role.name
12
+ matches_role = @role_name == 'everyone' || @role_name == context.role.name
13
13
  if matches_role
14
14
  if @block
15
15
  block_result = context.instance_exec(*args, &@block)
@@ -21,79 +21,85 @@ describe Aegis::HasRole do
21
21
  it "should define accessors for the role association" do
22
22
  user = @user_class.new
23
23
  user.should respond_to(:role)
24
- user.should respond_to(:role=)
24
+ user.should respond_to(:roles)
25
+ end
26
+
27
+ it "should allow a default for new records" do
28
+ permissions_class = @permissions_class
29
+ @user_class.class_eval { has_role :permissions => permissions_class, :default => "admin" }
30
+ user = @user_class.new
31
+ user.role_name.should == 'admin'
25
32
  end
26
33
 
27
34
  end
28
35
 
29
36
  describe 'role' do
30
37
 
31
- it "should be nil by default" do
38
+ it "should return the first role" do
32
39
  user = @user_class.new
33
- user.role.should be_nil
40
+ user.should_receive(:roles).and_return(['first role', 'second role'])
41
+ user.role.should == 'first role'
34
42
  end
35
43
 
36
- it "should return the role corresponding to the role_name" do
37
- user = @user_class.new(:role_name => 'admin')
38
- user.role.name.should == 'admin'
39
- end
40
-
41
- it "should be nil if the role_name doesn't match a known role" do
42
- user = @user_class.new(:role_name => 'nonexisting_role_name')
44
+ it "should be nil if no roles are associated" do
45
+ user = @user_class.new
46
+ user.should_receive(:roles).and_return([])
43
47
  user.role.should be_nil
44
48
  end
45
49
 
46
- it "should read the role name from a custom reader" do
47
- permissions_class = @permissions_class
48
- @user_class.class_eval { has_role :reader => "role_handle", :permissions => permissions_class }
49
- user = @user_class.new
50
- user.should_receive(:role_handle).and_return('admin')
51
- user.role
52
- end
50
+ end
53
51
 
54
- it "should read the role name from a custom accessor" do
55
- permissions_class = @permissions_class
56
- @user_class.class_eval { has_role :accessor => "role_handle", :permissions => permissions_class }
52
+ describe 'roles' do
53
+
54
+ it "should return the corresponding role for each role name" do
57
55
  user = @user_class.new
58
- user.should_receive(:role_handle).and_return('admin')
59
- user.role
56
+ user.should_receive(:role_names).and_return(['admin', 'user'])
57
+ user.roles.collect(&:name).should == ['admin', 'user']
60
58
  end
61
59
 
62
- it "should take a default role" do
63
- permissions_class = @permissions_class
64
- @user_class.class_eval { has_role :default => "admin", :permissions => permissions_class }
60
+ it "should ignore unknown role names that doesn't match a known role" do
65
61
  user = @user_class.new
66
- user.role.name.should == 'admin'
62
+ user.should_receive(:role_names).and_return(['unknown role', 'user'])
63
+ user.roles.collect(&:name).should == ['user']
67
64
  end
68
65
 
69
66
  end
70
67
 
71
- describe 'role=' do
68
+ describe 'role_names' do
72
69
 
73
- before :each do
74
- @admin_role = @permissions_class.find_role_by_name('admin')
70
+ it "should be empty if the role name is blank" do
71
+ user = @user_class.new(:role_name => '')
72
+ user.role_names.should be_empty
75
73
  end
76
74
 
77
- it "should write the role name to the role_name attribute" do
78
- user = @user_class.new
79
- user.should_receive(:role_name=).with('admin')
80
- user.role = @admin_role
75
+ it "should be empty if the role_name is nil" do
76
+ user = @user_class.new(:role_name => nil)
77
+ user.role_names.should be_empty
81
78
  end
82
79
 
83
- it "should write the role name to a custom writer" do
84
- permissions_class = @permissions_class
85
- @user_class.class_eval { has_role :writer => "role_handle=", :permissions => permissions_class }
86
- user = @user_class.new
87
- user.should_receive(:role_handle=).with('admin')
88
- user.role = @admin_role
80
+ it "should deserialize a single role name into an array with a single element" do
81
+ user = @user_class.new(:role_name => 'admin')
82
+ user.role_names.should == ['admin']
89
83
  end
90
84
 
91
- it "should write the role name to a custom accessor" do
92
- permissions_class = @permissions_class
93
- @user_class.class_eval { has_role :accessor => "role_handle", :permissions => permissions_class }
85
+ it "should deserialize multiple, comma-separated role names into an array" do
86
+ user = @user_class.new(:role_name => 'admin,user')
87
+ user.role_names.should == ['admin', 'user']
88
+ end
89
+
90
+ it "should ignore whitespace around the comma-separator" do
91
+ user = @user_class.new(:role_name => 'admin , user')
92
+ user.role_names.should == ['admin', 'user']
93
+ end
94
+
95
+ end
96
+
97
+ describe 'role_names=' do
98
+
99
+ it "should serialize the given array into a comma-separated string and store it into #role_name" do
94
100
  user = @user_class.new
95
- user.should_receive(:role_handle=).with('admin')
96
- user.role = @admin_role
101
+ user.should_receive(:role_name=).with("first,second")
102
+ user.role_names = ['first', 'second']
97
103
  end
98
104
 
99
105
  end
@@ -147,20 +153,26 @@ describe Aegis::HasRole do
147
153
  user.run_callbacks(:validate)
148
154
  end
149
155
 
150
- it "should add an inclusion error to the role name if the role name is blank" do
156
+ it "should add an inclusion error to the role name if the role name is nil" do
157
+ user = @user_class.new(:role_name => nil)
158
+ user.errors.should_receive(:add).with(:role_name, I18n.translate('activerecord.errors.messages.inclusion'))
159
+ user.send(:validate_role)
160
+ end
161
+
162
+ it "should add an inclusion error to the role name if the role name is an empty string" do
151
163
  user = @user_class.new(:role_name => '')
152
164
  user.errors.should_receive(:add).with(:role_name, I18n.translate('activerecord.errors.messages.inclusion'))
153
165
  user.send(:validate_role)
154
166
  end
155
167
 
156
- it "should add an inclusion error to the role name if the role name doesn't match a role" do
157
- user = @user_class.new(:role_name => 'nonexisting_role')
168
+ it "should add an inclusion error to the role name if a role name doesn't match a role" do
169
+ user = @user_class.new(:role_name => 'user,nonexisting_role')
158
170
  user.errors.should_receive(:add).with(:role_name, I18n.translate('activerecord.errors.messages.inclusion'))
159
171
  user.send(:validate_role)
160
172
  end
161
173
 
162
- it "should add no error if the role name matches a role" do
163
- user = @user_class.new(:role_name => 'admin')
174
+ it "should add no error if all role names matches a role" do
175
+ user = @user_class.new(:role_name => 'admin,user')
164
176
  user.errors.should_not_receive(:add)
165
177
  user.send(:validate_role)
166
178
  end
@@ -5,6 +5,7 @@ describe Aegis::Permissions do
5
5
  before(:each) do
6
6
 
7
7
  permissions = @permissions = Class.new(Aegis::Permissions) do
8
+ role :guest
8
9
  role :user
9
10
  role :moderator
10
11
  role :admin, :default_permission => :allow
@@ -117,6 +118,40 @@ describe Aegis::Permissions do
117
118
 
118
119
  end
119
120
 
121
+ describe 'multiple roles' do
122
+
123
+ before(:each) do
124
+
125
+ @permissions.class_eval do
126
+ action :update_news do
127
+ allow :moderator
128
+ end
129
+ end
130
+
131
+ end
132
+
133
+ it "should allow a user with multiple roles access if at least one role passes, even if other roles don't" do
134
+ person = @user_class.new(:role_names => ['user', 'moderator'])
135
+ @permissions.may?(person, 'update_news').should be_true
136
+ end
137
+
138
+ it "should deny a user with multiple roles access if no role passes" do
139
+ person = @user_class.new(:role_names => ['user', 'guest'])
140
+ @permissions.may?(person, 'update_news').should be_false
141
+ end
142
+
143
+ it "should deny a user with no roles access" do
144
+ person = @user_class.new(:role_names => [])
145
+ @permissions.may?(person, 'update_news').should be_false
146
+ end
147
+
148
+ it "should honor default permissions" do
149
+ person = @user_class.new(:role_names => ['admin'])
150
+ @permissions.may?(person, 'update_news').should be_true
151
+ end
152
+
153
+ end
154
+
120
155
  it "should run sieves in a sequence, the result being the last matching sieve" do
121
156
 
122
157
  @permissions.class_eval do
data/spec/sieve_spec.rb CHANGED
@@ -4,8 +4,8 @@ describe Aegis::Sieve do
4
4
 
5
5
  before(:each) do
6
6
  @role = stub('role', :name => 'user')
7
- @user = stub('user', :role => @role)
8
- @context = OpenStruct.new(:user => @user)
7
+ # user = stub('user', :role => role)
8
+ @context = OpenStruct.new(:role => @role)
9
9
  end
10
10
 
11
11
  describe 'may? 'do
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 2
7
+ - 1
7
8
  - 0
8
- - 4
9
- version: 2.0.4
9
+ version: 2.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Henning Koch
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-05-29 00:00:00 +02:00
18
+ date: 2010-07-25 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -128,8 +128,8 @@ test_files:
128
128
  - spec/app_root/lib/console_with_fixtures.rb
129
129
  - spec/action_controller_spec.rb
130
130
  - spec/controllers/reviews_controller_spec.rb
131
- - spec/has_role_spec.rb
132
- - spec/permissions_spec.rb
133
131
  - spec/spec_helper.rb
134
132
  - spec/loader_spec.rb
133
+ - spec/has_role_spec.rb
134
+ - spec/permissions_spec.rb
135
135
  - spec/sieve_spec.rb