cjbottaro-aegis 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.gitignore +3 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +182 -0
  4. data/Rakefile +37 -0
  5. data/VERSION +1 -0
  6. data/aegis.gemspec +117 -0
  7. data/lib/aegis.rb +11 -0
  8. data/lib/aegis/constants.rb +6 -0
  9. data/lib/aegis/has_role.rb +156 -0
  10. data/lib/aegis/meta_class.rb +13 -0
  11. data/lib/aegis/normalization.rb +26 -0
  12. data/lib/aegis/permission_error.rb +5 -0
  13. data/lib/aegis/permission_evaluator.rb +41 -0
  14. data/lib/aegis/permissions.rb +108 -0
  15. data/lib/aegis/role.rb +57 -0
  16. data/lib/aegis/role_assignments.rb +10 -0
  17. data/lib/rails/active_record.rb +5 -0
  18. data/test/app_root/app/controllers/application_controller.rb +2 -0
  19. data/test/app_root/app/models/account.rb +3 -0
  20. data/test/app_root/app/models/forum.rb +8 -0
  21. data/test/app_root/app/models/permissions.rb +70 -0
  22. data/test/app_root/app/models/post.rb +7 -0
  23. data/test/app_root/app/models/soldier.rb +5 -0
  24. data/test/app_root/app/models/user.rb +7 -0
  25. data/test/app_root/config/boot.rb +114 -0
  26. data/test/app_root/config/database.yml +21 -0
  27. data/test/app_root/config/environment.rb +14 -0
  28. data/test/app_root/config/environments/in_memory.rb +0 -0
  29. data/test/app_root/config/environments/mysql.rb +0 -0
  30. data/test/app_root/config/environments/postgresql.rb +0 -0
  31. data/test/app_root/config/environments/sqlite.rb +0 -0
  32. data/test/app_root/config/environments/sqlite3.rb +0 -0
  33. data/test/app_root/config/routes.rb +4 -0
  34. data/test/app_root/db/migrate/20090408115228_create_users.rb +15 -0
  35. data/test/app_root/db/migrate/20090429075648_create_soldiers.rb +16 -0
  36. data/test/app_root/db/migrate/20090903234709_create_role_assignments.rb +16 -0
  37. data/test/app_root/db/migrate/20090903234759_create_accounts.rb +11 -0
  38. data/test/app_root/db/migrate/20090903234821_create_forums.rb +12 -0
  39. data/test/app_root/db/migrate/20090903234828_create_posts.rb +12 -0
  40. data/test/app_root/lib/console_with_fixtures.rb +4 -0
  41. data/test/app_root/log/.gitignore +1 -0
  42. data/test/app_root/script/console +7 -0
  43. data/test/fixtures/accounts.yml +5 -0
  44. data/test/fixtures/forums.yml +11 -0
  45. data/test/fixtures/posts.yml +23 -0
  46. data/test/fixtures/role_assignments.yml +0 -0
  47. data/test/fixtures/users.yml +2 -0
  48. data/test/has_role_options_test.rb +33 -0
  49. data/test/has_role_test.rb +88 -0
  50. data/test/permissions_test.rb +117 -0
  51. data/test/test_helper.rb +44 -0
  52. data/test/validation_test.rb +49 -0
  53. metadata +130 -0
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ doc
2
+ pkg
3
+ *.gem
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Henning Koch
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,182 @@
1
+ = Abstract
2
+
3
+ Aegis modified to support multiple roles per actors based on context and your data model hierarchy.
4
+
5
+ This file will only describe the new functionality I added to Aegis. See the original Aegis README.rdoc here: http://github.com/makandra/aegis/tree/master
6
+
7
+ == Description (aka longer abstract)
8
+
9
+ Aegis is a pretty simple permission system: a user belongs to a single role, and a role has many permissions associated with it.
10
+
11
+ My work requires a user to have multiple roles. Which one is chosen at any given time is based on a context. For example, we want <tt>user:123</tt> to have role <tt>:writer</tt> in <tt>forum:abc</tt>, but role <tt>:reader</tt> in <tt>forum:xyz</tt>.
12
+
13
+ Furthermore, we want to have a role hierarchy that follows our object model. If no role is defined for a user for a given context, then we ask if that context has a parent and if a role is defined there (again for that user). For example, does <tt>user:123</tt> have a role for <tt>forum:ijk</tt>? No. Does <tt>forum:ijk</tt> have a parent? Yes, it is <tt>account:1</tt>. Does <tt>user:123</tt> have a role in <tt>account:1</tt>? Yes, it is <tt>:writer</tt>.
14
+
15
+ Note that all previous Aegis functionality still exists and works (none of the existing unit tests were changed at all).
16
+
17
+ == Installation
18
+
19
+ Add the following to your <tt>Initializer.run</tt> block in your <tt>environment.rb</tt>:
20
+ config.gem 'cjbottaro-aegis', :lib => 'aegis', :source => 'http://gems.github.com'
21
+ Then do a
22
+ sudo rake gems:install
23
+
24
+ Alternatively, use
25
+ sudo gem sources -a http://gems.github.com
26
+ sudo gem install cjbottaro-aegis
27
+
28
+ == Database Migration
29
+
30
+ You need to create a table to keep track of which actors are assigned which roles for each context.
31
+
32
+ class CreateRoleAssignments < ActiveRecord::Migration
33
+ def self.up
34
+ create_table :role_assignments do |t|
35
+ t.string :actor_type, :null => false
36
+ t.integer :actor_id, :null => false
37
+ t.string :role_name, :null => false
38
+ t.string :context_type, :null => false
39
+ t.integer :context_id, :null => false
40
+ t.timestamps
41
+ end
42
+ end
43
+
44
+ def self.down
45
+ drop_table :role_assignments
46
+ end
47
+ end
48
+
49
+ == Example
50
+
51
+ First, let's define a data model for our example. An Account has many Users and Forums. A Forum has many Posts.
52
+
53
+ Account
54
+ |-> User
55
+ |-> Forum
56
+ |-> Post
57
+
58
+ Now let's assign some roles for our user.
59
+
60
+ user = User.find_by_name("chris")
61
+ forum = Forum.find_by_name("coping with metrosexuality")
62
+ RoleAssignment.create! :actor => user,
63
+ :role_name => :admin,
64
+ :context => forum
65
+ post = forum.posts.find_by_title("acceptance")
66
+ RoleAssignment.create! :actor => user,
67
+ :role_name => :reader,
68
+ :context => post
69
+
70
+ Now we can query permissions.
71
+
72
+ user.may_create_posts_in?(forum) # user has role of :admin in this forum.
73
+ => true
74
+
75
+ user.may_edit_content_for?(post) # user has role of :reader for this post.
76
+ => false
77
+
78
+ post = forum.posts.find_by_title("denial")
79
+ user.may_edit_content_for?(post) # user does not have role for this post, so we look at that
80
+ # post's parent and see he is a an :admin there.
81
+ => true
82
+
83
+ The syntax is very similar to normal Aegis except you can suffix permission queries with <tt>_in?</tt> or <tt>_for?</tt> and the last argument to the query is the context. So permission queries that accept parameters work like normal, you just pass in the context as the last parameter.
84
+
85
+ == How does Aegis know the object model hierarchy?
86
+
87
+ First way... you specify the hierarchy as a hash where the key is a class name and the value is an association name that invokes the parent.
88
+
89
+ class User < ActiveRecord::Base
90
+ has_role :hierarchy => { "Post" => "forum",
91
+ "Forum" => "account" }
92
+ end
93
+
94
+ Second way... you specify a single association name that will always return a model's parent.
95
+
96
+ class User < ActiveRecord::Base
97
+ has_role :hierarchy_accessor => "parent"
98
+ end
99
+
100
+ class Post < ActiveRecord::Base
101
+ belongs_to :forum
102
+ def parent
103
+ forum
104
+ end
105
+ end
106
+
107
+ class Forum < ActiveRecord::Base
108
+ belongs_to :account
109
+ def parent
110
+ account
111
+ end
112
+ end
113
+
114
+ == Forcing certain roles under specific conditions
115
+
116
+ My work had an additional requirement that if User#is_admin? is true, then that user should always have the role of <tt>:superuser</tt>, regardless of the context.
117
+
118
+ class User < ActiveRecord::Base
119
+ has_role :force_superuser_if => Proc.new{ |user| user.is_admin? }
120
+ end
121
+
122
+ user = User.first(:conditions => { :is_admin => true })
123
+ user.role_in(anything)
124
+ => :superuser
125
+
126
+ user.may_edit_content_for?(anything)
127
+ => true
128
+
129
+ The format of the key is special. It must be of the form <tt>force_<role_name>_if</tt>. There can be multiple keys of this form. The role is determined by the first one who's Proc returns true. They are evaluated in random order (thus is the nature of Hashes).
130
+
131
+ == Optimizations
132
+
133
+ We don't want to read the role assignments from the database every time we do a permission check. The solution to this is to read them once when the user logs in, store it in the session, then load from the session on each request.
134
+
135
+ In your login controller/action...
136
+
137
+ if successful_login?
138
+ session[:permissions] = current_user.role_assignments_hash
139
+ else
140
+ ...
141
+ end
142
+
143
+ In your controller/action that checks if a user is logged in...
144
+
145
+ if logged_in?
146
+ current_user.role_assignments_override = session[:permissions]
147
+ else
148
+ ...
149
+ end
150
+
151
+ This way the role assignments are only read when a user logs in.
152
+
153
+ == Associating permissions to contexts
154
+
155
+ Now that we have contexts, one might think we should be able to say only certain permissions make sense in given contexts.
156
+
157
+ Permissions < Aegis::Permissions
158
+ permission :delete_forum do
159
+ context "Account"
160
+ allow :admin
161
+ end
162
+ end
163
+
164
+ user = User.find(...)
165
+ post = Post.find(...)
166
+
167
+ user.may_delete_forum(post)
168
+ => Exception, "permission :delete_forum does not make sense for Post"
169
+
170
+ Note that you can specify multiple contexts...
171
+
172
+ Permissions < Aegis::Permissions
173
+ permission :some_permission do
174
+ contexts "Classname1", "Classname2"
175
+ allow :admin
176
+ end
177
+ end
178
+
179
+ === Author
180
+
181
+ Christopher J. Bottaro
182
+ http://github.com/cjbottaro
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the aegis gem.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc 'Generate documentation for the aegis plugin.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'Aegis'
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README.rdoc')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
23
+
24
+ begin
25
+ require 'jeweler'
26
+ Jeweler::Tasks.new do |gemspec|
27
+ gemspec.name = "aegis"
28
+ gemspec.summary = "Role-based permissions for your user models."
29
+ gemspec.email = "github@makandra.de"
30
+ gemspec.homepage = "http://github.com/makandra/aegis"
31
+ gemspec.description = "Aegis is a role-based permission system, where all users are given a role. It is possible to define detailed and complex permissions for each role very easily."
32
+ gemspec.authors = ["Henning Koch"]
33
+ end
34
+ rescue LoadError
35
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
36
+ end
37
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.3.0
data/aegis.gemspec ADDED
@@ -0,0 +1,117 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{aegis}
8
+ s.version = "1.3.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Henning Koch"]
12
+ s.date = %q{2009-09-07}
13
+ s.description = %q{Aegis is a role-based permission system, where all users are given a role. It is possible to define detailed and complex permissions for each role very easily.}
14
+ s.email = %q{github@makandra.de}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "MIT-LICENSE",
21
+ "README.rdoc",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "aegis.gemspec",
25
+ "lib/aegis.rb",
26
+ "lib/aegis/constants.rb",
27
+ "lib/aegis/has_role.rb",
28
+ "lib/aegis/meta_class.rb",
29
+ "lib/aegis/normalization.rb",
30
+ "lib/aegis/permission_error.rb",
31
+ "lib/aegis/permission_evaluator.rb",
32
+ "lib/aegis/permissions.rb",
33
+ "lib/aegis/role.rb",
34
+ "lib/aegis/role_assignments.rb",
35
+ "lib/rails/active_record.rb",
36
+ "test/app_root/app/controllers/application_controller.rb",
37
+ "test/app_root/app/models/account.rb",
38
+ "test/app_root/app/models/forum.rb",
39
+ "test/app_root/app/models/permissions.rb",
40
+ "test/app_root/app/models/post.rb",
41
+ "test/app_root/app/models/soldier.rb",
42
+ "test/app_root/app/models/user.rb",
43
+ "test/app_root/config/boot.rb",
44
+ "test/app_root/config/database.yml",
45
+ "test/app_root/config/environment.rb",
46
+ "test/app_root/config/environments/in_memory.rb",
47
+ "test/app_root/config/environments/mysql.rb",
48
+ "test/app_root/config/environments/postgresql.rb",
49
+ "test/app_root/config/environments/sqlite.rb",
50
+ "test/app_root/config/environments/sqlite3.rb",
51
+ "test/app_root/config/routes.rb",
52
+ "test/app_root/db/migrate/20090408115228_create_users.rb",
53
+ "test/app_root/db/migrate/20090429075648_create_soldiers.rb",
54
+ "test/app_root/db/migrate/20090903234709_create_role_assignments.rb",
55
+ "test/app_root/db/migrate/20090903234759_create_accounts.rb",
56
+ "test/app_root/db/migrate/20090903234821_create_forums.rb",
57
+ "test/app_root/db/migrate/20090903234828_create_posts.rb",
58
+ "test/app_root/lib/console_with_fixtures.rb",
59
+ "test/app_root/log/.gitignore",
60
+ "test/app_root/script/console",
61
+ "test/fixtures/accounts.yml",
62
+ "test/fixtures/forums.yml",
63
+ "test/fixtures/posts.yml",
64
+ "test/fixtures/role_assignments.yml",
65
+ "test/fixtures/users.yml",
66
+ "test/has_role_options_test.rb",
67
+ "test/has_role_test.rb",
68
+ "test/permissions_test.rb",
69
+ "test/test_helper.rb",
70
+ "test/validation_test.rb"
71
+ ]
72
+ s.has_rdoc = true
73
+ s.homepage = %q{http://github.com/makandra/aegis}
74
+ s.rdoc_options = ["--charset=UTF-8"]
75
+ s.require_paths = ["lib"]
76
+ s.rubygems_version = %q{1.3.2}
77
+ s.summary = %q{Role-based permissions for your user models.}
78
+ s.test_files = [
79
+ "test/app_root/app/controllers/application_controller.rb",
80
+ "test/app_root/app/models/account.rb",
81
+ "test/app_root/app/models/forum.rb",
82
+ "test/app_root/app/models/permissions.rb",
83
+ "test/app_root/app/models/post.rb",
84
+ "test/app_root/app/models/soldier.rb",
85
+ "test/app_root/app/models/user.rb",
86
+ "test/app_root/config/boot.rb",
87
+ "test/app_root/config/environment.rb",
88
+ "test/app_root/config/environments/in_memory.rb",
89
+ "test/app_root/config/environments/mysql.rb",
90
+ "test/app_root/config/environments/postgresql.rb",
91
+ "test/app_root/config/environments/sqlite.rb",
92
+ "test/app_root/config/environments/sqlite3.rb",
93
+ "test/app_root/config/routes.rb",
94
+ "test/app_root/db/migrate/20090408115228_create_users.rb",
95
+ "test/app_root/db/migrate/20090429075648_create_soldiers.rb",
96
+ "test/app_root/db/migrate/20090903234709_create_role_assignments.rb",
97
+ "test/app_root/db/migrate/20090903234759_create_accounts.rb",
98
+ "test/app_root/db/migrate/20090903234821_create_forums.rb",
99
+ "test/app_root/db/migrate/20090903234828_create_posts.rb",
100
+ "test/app_root/lib/console_with_fixtures.rb",
101
+ "test/has_role_options_test.rb",
102
+ "test/has_role_test.rb",
103
+ "test/permissions_test.rb",
104
+ "test/test_helper.rb",
105
+ "test/validation_test.rb"
106
+ ]
107
+
108
+ if s.respond_to? :specification_version then
109
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
110
+ s.specification_version = 3
111
+
112
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
113
+ else
114
+ end
115
+ else
116
+ end
117
+ end
data/lib/aegis.rb ADDED
@@ -0,0 +1,11 @@
1
+ # Include hook code here
2
+ require 'aegis/meta_class'
3
+ require 'aegis/constants'
4
+ require 'aegis/normalization'
5
+ require 'aegis/permission_error'
6
+ require 'aegis/role'
7
+ require 'aegis/role_assignments'
8
+ require 'aegis/permissions'
9
+ require 'aegis/has_role'
10
+ require 'rails/active_record'
11
+
@@ -0,0 +1,6 @@
1
+ module Aegis
2
+ module Constants
3
+ EVERYONE_ROLE_NAME = :everyone
4
+ CRUD_VERBS = ["create", "read", "update", "destroy"]
5
+ end
6
+ end
@@ -0,0 +1,156 @@
1
+ module Aegis
2
+ module HasRole
3
+
4
+ def self.extended(mod)
5
+ mod.send(:extend, ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+
10
+ def validates_role_name(options = {})
11
+ validates_each :role_name do |record, attr, value|
12
+ options[:message] ||= ActiveRecord::Errors.default_error_messages[:inclusion]
13
+ role = ::Permissions.find_role_by_name(value)
14
+ record.errors.add attr, options[:message] if role.nil?
15
+ end
16
+ end
17
+
18
+ alias_method :validates_role, :validates_role_name
19
+
20
+ def has_role(options = {})
21
+
22
+ if options[:name_accessor]
23
+ options[:name_reader] = "#{options[:name_accessor]}"
24
+ options[:name_writer] = "#{options[:name_accessor]}="
25
+ options.delete(:name_accessor)
26
+ end
27
+
28
+ @aegis_role_name_reader = (options[:name_reader] || "role_name").to_sym
29
+ @aegis_role_name_writer = (options[:name_writer] || "role_name=").to_sym
30
+ @aegis_role_hierarchy = (options[:hierarchy] || {})
31
+ @aegis_role_hierarchy_accessor = (options[:hierarchy_accessor])
32
+ @aegis_forced_roles = options.inject({}) do |memo, (k, v)|
33
+ (m = k.to_s.match(/force_(.+)_if/)) and (memo[v] = m[1])
34
+ memo
35
+ end
36
+
37
+ meta_eval do
38
+ attr_reader :aegis_role_name_reader
39
+ attr_reader :aegis_role_name_writer
40
+ attr_reader :aegis_role_hierarchy
41
+ attr_reader :aegis_role_hierarchy_accessor
42
+ attr_reader :aegis_forced_roles
43
+ end
44
+
45
+ has_many :role_assignments, :class_name => "Aegis::RoleAssignment",
46
+ :as => "actor"
47
+
48
+ attr_writer :role_assignments_override
49
+
50
+ include(InstanceMethods)
51
+
52
+ alias_method_chain :method_missing, :aegis_permissions
53
+ end
54
+
55
+ alias_method :has_roles, :has_role
56
+
57
+ end # module ClassMethods
58
+
59
+ module InstanceMethods
60
+
61
+ def aegis_role_name_reader
62
+ self.class.aegis_role_name_reader
63
+ end
64
+
65
+ def aegis_role_name_writer
66
+ self.class.aegis_role_name_writer
67
+ end
68
+
69
+ def aegis_role_hierarchy
70
+ self.class.aegis_role_hierarchy
71
+ end
72
+
73
+ def aegis_role_hierarchy_accessor
74
+ self.class.aegis_role_hierarchy_accessor
75
+ end
76
+
77
+ def aegis_forced_roles
78
+ self.class.aegis_forced_roles
79
+ end
80
+
81
+ def aegis_role_name
82
+ send(aegis_role_name_reader)
83
+ end
84
+
85
+ def aegis_role_name=(value)
86
+ send(aegis_role_name_writer, value)
87
+ end
88
+
89
+ def role
90
+ (forced_role = role_forced) and return forced_role
91
+ ::Permissions.find_role_by_name!(aegis_role_name)
92
+ end
93
+
94
+ def role=(role_or_name)
95
+ self.aegis_role_name = if role_or_name.is_a?(Aegis::Role)
96
+ role_or_name.name
97
+ else
98
+ role_or_name.to_s
99
+ end
100
+ end
101
+
102
+ def role_in(context, tried_contexts = [])
103
+ (forced_role = role_forced) and return forced_role
104
+ context_key = "#{context.class}:#{context.id}"
105
+ tried_contexts << context_key
106
+ roles_by_context = role_assignments_hash
107
+ if roles_by_context.has_key?(context_key)
108
+ ::Permissions.find_role_by_name!(roles_by_context[context_key])
109
+ elsif !aegis_role_hierarchy.blank? and (parent_class_accessor = aegis_role_hierarchy[context.class.name])
110
+ role_in(context.send(parent_class_accessor), tried_contexts)
111
+ elsif aegis_role_hierarchy_accessor and context.respond_to?(aegis_role_hierarchy_accessor)
112
+ role_in(context.send(aegis_role_hierarchy_accessor), tried_contexts)
113
+ else
114
+ actor_key = "#{self.class}:#{self.id}"
115
+ tried_contexts = tried_contexts.join(", ")
116
+ raise Aegis::PermissionError, "cannot find role for #{actor_key} in #{tried_contexts}"
117
+ end
118
+ end
119
+
120
+ def role_assignments_hash(*args)
121
+ return @role_assignments_override unless @role_assignments_override.blank?
122
+ role_assignments.inject({}) do |memo, role_assignment|
123
+ memo[role_assignment.context_key] = role_assignment.role_name
124
+ memo
125
+ end
126
+ end
127
+
128
+ def role_forced
129
+ aegis_forced_roles.each do |proc, role_name|
130
+ return role_name if proc.call(self)
131
+ end
132
+ nil
133
+ end
134
+
135
+ private
136
+
137
+ # Delegate may_...? and may_...! methods to the user's role.
138
+ def method_missing_with_aegis_permissions(symb, *args)
139
+ method_name = symb.to_s
140
+ if method_name =~ /^may_(.+?)_in[\!\?]$/
141
+ #method_name = method_name[0...-4] + method_name[-1, 1] # may_edit_post_in? => may_edit_post?
142
+ role_in(args.first).send(method_name, self, *args)
143
+ elsif method_name =~ /^may_(.+?)[\!\?]$/
144
+ role.send(symb, self, *args)
145
+ elsif method_name =~ /^(.*?)\?$/ && queried_role = ::Permissions.find_role_by_name($1)
146
+ role == queried_role
147
+ else
148
+ method_missing_without_aegis_permissions(symb, *args)
149
+ end
150
+ end
151
+
152
+ end # InstanceMethods
153
+
154
+ end # module HasRole
155
+
156
+ end # module Aegis