aegis 2.0.4 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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