thelinuxlich-aegis 1.1.7

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.
Files changed (46) hide show
  1. data/.gitignore +3 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +195 -0
  4. data/Rakefile +37 -0
  5. data/VERSION +1 -0
  6. data/aegis.gemspec +107 -0
  7. data/lib/aegis.rb +10 -0
  8. data/lib/aegis/constants.rb +7 -0
  9. data/lib/aegis/has_role.rb +110 -0
  10. data/lib/aegis/normalization.rb +26 -0
  11. data/lib/aegis/permission_error.rb +5 -0
  12. data/lib/aegis/permission_evaluator.rb +34 -0
  13. data/lib/aegis/permissions.rb +107 -0
  14. data/lib/aegis/role.rb +55 -0
  15. data/lib/rails/active_record.rb +5 -0
  16. data/test/app_root/app/controllers/application_controller.rb +2 -0
  17. data/test/app_root/app/models/old_soldier.rb +6 -0
  18. data/test/app_root/app/models/permissions.rb +49 -0
  19. data/test/app_root/app/models/soldier.rb +5 -0
  20. data/test/app_root/app/models/trust_fund_kid.rb +5 -0
  21. data/test/app_root/app/models/user.rb +6 -0
  22. data/test/app_root/app/models/user_subclass.rb +2 -0
  23. data/test/app_root/app/models/veteran_soldier.rb +6 -0
  24. data/test/app_root/config/boot.rb +114 -0
  25. data/test/app_root/config/database.yml +21 -0
  26. data/test/app_root/config/environment.rb +14 -0
  27. data/test/app_root/config/environments/in_memory.rb +0 -0
  28. data/test/app_root/config/environments/mysql.rb +0 -0
  29. data/test/app_root/config/environments/postgresql.rb +0 -0
  30. data/test/app_root/config/environments/sqlite.rb +0 -0
  31. data/test/app_root/config/environments/sqlite3.rb +0 -0
  32. data/test/app_root/config/routes.rb +4 -0
  33. data/test/app_root/db/migrate/20090408115228_create_users.rb +14 -0
  34. data/test/app_root/db/migrate/20090429075648_create_soldiers.rb +14 -0
  35. data/test/app_root/db/migrate/20091110075648_create_veteran_soldiers.rb +14 -0
  36. data/test/app_root/db/migrate/20091110075649_create_trust_fund_kids.rb +15 -0
  37. data/test/app_root/lib/console_with_fixtures.rb +4 -0
  38. data/test/app_root/log/.gitignore +1 -0
  39. data/test/app_root/script/console +7 -0
  40. data/test/has_role_options_test.rb +64 -0
  41. data/test/has_role_test.rb +54 -0
  42. data/test/permissions_test.rb +109 -0
  43. data/test/test_helper.rb +23 -0
  44. data/test/validation_test.rb +55 -0
  45. data/thelinuxlich-aegis.gemspec +109 -0
  46. metadata +131 -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,195 @@
1
+ = Aegis - role-based permissions for your user models
2
+
3
+ Aegis allows you to manage fine-grained, complex permission for user accounts in a central place.
4
+
5
+ == Installation
6
+
7
+ Add the following to your <tt>Initializer.run</tt> block in your <tt>environment.rb</tt>:
8
+ config.gem 'aegis', :source => 'http://gemcutter.org'
9
+ Then do a
10
+ sudo rake gems:install
11
+
12
+ Alternatively, use
13
+ sudo gem sources -a http://gemcutter.org
14
+ sudo gem install aegis
15
+
16
+ == Example
17
+
18
+ First, let's define some roles:
19
+
20
+ # app/models/permissions.rb
21
+ class Permissions < Aegis::Permissions
22
+
23
+ role :guest
24
+ role :registered_user
25
+ role :moderator
26
+ role :administrator, :default_permission => :allow
27
+
28
+ permission :edit_post do |user, post|
29
+ allow :registered_user do
30
+ post.creator == user # a registered_user can only edit his own posts
31
+ end
32
+ allow :moderator
33
+ end
34
+
35
+ permission :read_post do |post|
36
+ allow :everyone
37
+ deny :guest do
38
+ post.private? # guests may not read private posts
39
+ end
40
+ end
41
+
42
+ end
43
+
44
+
45
+ Now we assign roles to users. For this, the users table needs to have a string
46
+ column +role_name+.
47
+
48
+ # app/models/user.rb
49
+ class User
50
+ has_role
51
+ end
52
+
53
+
54
+ These permissions may be used in views and controllers:
55
+
56
+ # app/views/posts/index.html.erb
57
+ @posts.each do |post|
58
+ <% if current_user.may_read_post? post %>
59
+ <%= render post %>
60
+ <% if current_user.may_edit_post? post %>
61
+ <%= link_to 'Edit', edit_post_path(post) %>
62
+ <% end %>
63
+ <% end %>
64
+ <% end %>
65
+
66
+
67
+ # app/controllers/posts_controller.rb
68
+ class PostsController
69
+ # ...
70
+
71
+ def update
72
+ @post = Post.find(params[:id])
73
+ current_user.may_edit_post! @post # raises an Aegis::PermissionError for unauthorized access
74
+ # ...
75
+ end
76
+
77
+ end
78
+
79
+ == Details
80
+
81
+ === Roles
82
+
83
+ To equip a (user) model with any permissions, you simply call *has_role* within
84
+ the model:
85
+ class User < ActiveRecord::Base
86
+ has_role
87
+ end
88
+ Aegis assumes that the corresponding database table has a string-valued column
89
+ called +role_name+. You may override the name with the <tt>:name_accessor =>
90
+ :my_role_column</tt> option.
91
+
92
+ You can define a default role for a model by saying
93
+ class User < ActiveRecord::Base
94
+ has_role :default => :admin
95
+ end
96
+ All this will do, is initialize the +role_name+ with the given default when
97
+ <tt>User.new</tt> is called.
98
+
99
+ The roles and permissions themselves are defined in a class inheriting from
100
+ <b>Aegis::Permissions</b>. To define roles you create a model <tt>permissions.rb</tt>
101
+ and use the *role* method:
102
+ class Permissions < Aegis::Permissions
103
+ role 'role_name'
104
+ end
105
+
106
+ By default, users belonging to this role are not permitted anything. You may
107
+ override this with <tt>:default_permission => :allow</tt>, e.g.
108
+ role 'admin', :default_permission => :allow
109
+
110
+ === Permissions
111
+
112
+ Permissions are specified with the *permission* method and *allow* and *deny*
113
+ permission :do_something do
114
+ allow :role_a, :role_b
115
+ deny :role_c
116
+ end
117
+
118
+ Your user model just received two methods called <b>User#may_do_something?</b>
119
+ and <b>User#may_do_something!</b>. The first one with the ? returns true for users with
120
+ +role_a+ and +role_b+, and false for users with +role_c+. The second one with the ! raises an
121
+ Aegis::PermissionError for +role_c+.
122
+
123
+ === Normalization
124
+
125
+ Aegis will perform some normalization. For example, the permissions
126
+ +edit_something+ and +update_something+ will be the same, each granting both
127
+ <tt>may_edit_something?</tt> and <tt>may_update_something?</tt>. The following normalizations
128
+ are active:
129
+ * edit = update
130
+ * show = list = view = read
131
+ * delete = remove = destroy
132
+
133
+ === Complex permissions (with parameters)
134
+
135
+ *allow* and *deny* can also take a block that may return +true+ or +false+
136
+ indicating if this really applies. So
137
+ permission :pull_april_fools_prank do
138
+ allow :everyone do
139
+ Date.today.month == 4 and Date.today.day == 1
140
+ end
141
+ end
142
+ will generate a <tt>may_pull_april_fools_prank?</tt> method that only returns true on
143
+ April 1.
144
+
145
+ This becomes more useful if you pass parameters to a <tt>may_...?</tt> method, which
146
+ are passed through to the permission block (together with the user object). This
147
+ way you can define more complex permissions like
148
+ permission :edit_post do |current_user, post|
149
+ allow :registered_user do
150
+ post.owner == current_user
151
+ end
152
+ allow :admin
153
+ end
154
+ which will permit admins and post owners to edit posts.
155
+
156
+ === For your convenience
157
+
158
+ As a convenience, if you create a permission ending in a plural 's', this
159
+ automatically includes the singular form. That is, after
160
+ permission :read_posts do
161
+ allow :everyone
162
+ end
163
+ <tt>.may_read_post? @post</tt> will return true, as well.
164
+
165
+ If you want to grant +create_something+, +read_something+, +update_something+
166
+ and +destroy_something+ permissions all at once, just use
167
+ permission :crud_something do
168
+ allow :admin
169
+ end
170
+
171
+ If several permission blocks (or several allow and denies) apply to a certain
172
+ role, the later one always wins. That is
173
+ permission :do_something do
174
+ deny :everyone
175
+ allow :admin
176
+ end
177
+ will work as expected.
178
+
179
+ === Our stance on multiple roles per user
180
+
181
+ We believe that you should only distinguish roles that have different ways of resolving their permissions. A typical set of roles would be
182
+
183
+ * anonymous guest (has access to nothing with some exceptions)
184
+ * signed up user (has access to some things depending on its attributes and associations)
185
+ * administrator (has access to everything)
186
+
187
+ We don't do multiple, parametrized roles like "leader for project #2" and "author of post #7".
188
+ That would be reinventing associations. Just use a single :user role and let your permission block
189
+ query regular associations and attributes.
190
+
191
+ === Credits
192
+
193
+ Henning Koch, Tobias Kraze
194
+
195
+ {link www.makandra.de}[http://www.makandra.de/]
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')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
23
+
24
+ begin
25
+ require 'jeweler'
26
+ Jeweler::Tasks.new do |gemspec|
27
+ gemspec.name = "thelinuxlich-aegis"
28
+ gemspec.summary = "Role-based permissions for your user models."
29
+ gemspec.email = "thelinuxlich@gmail.com"
30
+ gemspec.homepage = "http://github.com/thelinuxlich/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 = ["thelinuxlich"]
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.1.7
data/aegis.gemspec ADDED
@@ -0,0 +1,107 @@
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.1.6"
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-11-11}
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/normalization.rb",
29
+ "lib/aegis/permission_error.rb",
30
+ "lib/aegis/permission_evaluator.rb",
31
+ "lib/aegis/permissions.rb",
32
+ "lib/aegis/role.rb",
33
+ "lib/rails/active_record.rb",
34
+ "test/app_root/app/controllers/application_controller.rb",
35
+ "test/app_root/app/models/old_soldier.rb",
36
+ "test/app_root/app/models/permissions.rb",
37
+ "test/app_root/app/models/soldier.rb",
38
+ "test/app_root/app/models/trust_fund_kid.rb",
39
+ "test/app_root/app/models/user.rb",
40
+ "test/app_root/app/models/user_subclass.rb",
41
+ "test/app_root/app/models/veteran_soldier.rb",
42
+ "test/app_root/config/boot.rb",
43
+ "test/app_root/config/database.yml",
44
+ "test/app_root/config/environment.rb",
45
+ "test/app_root/config/environments/in_memory.rb",
46
+ "test/app_root/config/environments/mysql.rb",
47
+ "test/app_root/config/environments/postgresql.rb",
48
+ "test/app_root/config/environments/sqlite.rb",
49
+ "test/app_root/config/environments/sqlite3.rb",
50
+ "test/app_root/config/routes.rb",
51
+ "test/app_root/db/migrate/20090408115228_create_users.rb",
52
+ "test/app_root/db/migrate/20090429075648_create_soldiers.rb",
53
+ "test/app_root/db/migrate/20091110075648_create_veteran_soldiers.rb",
54
+ "test/app_root/db/migrate/20091110075649_create_trust_fund_kids.rb",
55
+ "test/app_root/lib/console_with_fixtures.rb",
56
+ "test/app_root/log/.gitignore",
57
+ "test/app_root/script/console",
58
+ "test/has_role_options_test.rb",
59
+ "test/has_role_test.rb",
60
+ "test/permissions_test.rb",
61
+ "test/test_helper.rb",
62
+ "test/validation_test.rb"
63
+ ]
64
+ s.homepage = %q{http://github.com/makandra/aegis}
65
+ s.rdoc_options = ["--charset=UTF-8"]
66
+ s.require_paths = ["lib"]
67
+ s.rubygems_version = %q{1.3.5}
68
+ s.summary = %q{Role-based permissions for your user models.}
69
+ s.test_files = [
70
+ "test/app_root/app/models/trust_fund_kid.rb",
71
+ "test/app_root/app/models/veteran_soldier.rb",
72
+ "test/app_root/app/models/permissions.rb",
73
+ "test/app_root/app/models/soldier.rb",
74
+ "test/app_root/app/models/user_subclass.rb",
75
+ "test/app_root/app/models/old_soldier.rb",
76
+ "test/app_root/app/models/user.rb",
77
+ "test/app_root/app/controllers/application_controller.rb",
78
+ "test/app_root/config/environment.rb",
79
+ "test/app_root/config/environments/mysql.rb",
80
+ "test/app_root/config/environments/postgresql.rb",
81
+ "test/app_root/config/environments/sqlite3.rb",
82
+ "test/app_root/config/environments/in_memory.rb",
83
+ "test/app_root/config/environments/sqlite.rb",
84
+ "test/app_root/config/boot.rb",
85
+ "test/app_root/config/routes.rb",
86
+ "test/app_root/db/migrate/20090429075648_create_soldiers.rb",
87
+ "test/app_root/db/migrate/20090408115228_create_users.rb",
88
+ "test/app_root/db/migrate/20091110075649_create_trust_fund_kids.rb",
89
+ "test/app_root/db/migrate/20091110075648_create_veteran_soldiers.rb",
90
+ "test/app_root/lib/console_with_fixtures.rb",
91
+ "test/validation_test.rb",
92
+ "test/test_helper.rb",
93
+ "test/has_role_options_test.rb",
94
+ "test/has_role_test.rb",
95
+ "test/permissions_test.rb"
96
+ ]
97
+
98
+ if s.respond_to? :specification_version then
99
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
100
+ s.specification_version = 3
101
+
102
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
103
+ else
104
+ end
105
+ else
106
+ end
107
+ end
data/lib/aegis.rb ADDED
@@ -0,0 +1,10 @@
1
+ # Include hook code here
2
+ require 'aegis/constants'
3
+ require 'aegis/has_role'
4
+ require 'aegis/normalization'
5
+ require 'aegis/permission_error'
6
+ require 'aegis/permission_evaluator'
7
+ require 'aegis/permissions'
8
+ require 'aegis/role'
9
+ require 'rails/active_record'
10
+
@@ -0,0 +1,7 @@
1
+ module Aegis
2
+ module Constants
3
+ EVERYONE_ROLE_NAME = :everyone
4
+ PERMISSION_PREFIX = I18n.translate('aegis.permission_prefix',:default => 'may')
5
+ CRUD_VERBS = ["create", "read", "update", "destroy"]
6
+ end
7
+ end
@@ -0,0 +1,110 @@
1
+ module Aegis
2
+ module HasRole
3
+
4
+ def validates_role_name(options = {})
5
+ validates_each :role_name do |record, attr, value|
6
+ options[:message] ||= I18n.translate('activerecord.errors.messages.inclusion')
7
+ role = ::Permissions.find_role_by_name(value)
8
+ record.errors.add attr, options[:message] if role.nil?
9
+ end
10
+ end
11
+
12
+ alias_method :validates_role, :validates_role_name
13
+
14
+ def has_role(options = {})
15
+
16
+ # Legacy parameter names
17
+ options[:accessor] ||= options.delete(:name_accessor)
18
+ options[:reader] ||= options.delete(:name_reader)
19
+ options[:writer] ||= options.delete(:name_writer)
20
+
21
+ if options[:accessor]
22
+ options[:reader] = "#{options[:accessor]}"
23
+ options[:writer] = "#{options[:accessor]}="
24
+ options.delete(:accessor)
25
+ end
26
+
27
+ self.class_eval do
28
+
29
+ class_inheritable_accessor :aegis_role_name_reader, :aegis_role_name_writer, :aegis_default_role_name
30
+
31
+ unless method_defined?(:after_initialize)
32
+ def after_initialize
33
+ end
34
+ end
35
+
36
+ if options[:default]
37
+ self.aegis_default_role_name = options[:default].to_s
38
+ after_initialize :set_default_aegis_role_name
39
+ end
40
+
41
+ self.aegis_role_name_reader = (options[:reader] || "role_name").to_sym
42
+ self.aegis_role_name_writer = (options[:writer] || "role_name=").to_sym
43
+
44
+ def aegis_role_name_reader
45
+ self.class.class_eval{ aegis_role_name_reader }
46
+ end
47
+
48
+ def aegis_role_name_writer
49
+ self.class.class_eval{ aegis_role_name_writer }
50
+ end
51
+
52
+ def aegis_role_name
53
+ send(aegis_role_name_reader)
54
+ end
55
+
56
+ def aegis_role_name=(value)
57
+ send(aegis_role_name_writer, value)
58
+ end
59
+
60
+ def role
61
+ ::Permissions.find_role_by_name!(aegis_role_name)
62
+ end
63
+
64
+ def role=(role_or_name)
65
+ self.aegis_role_name = if role_or_name.is_a?(Aegis::Role)
66
+ role_or_name.name
67
+ else
68
+ role_or_name.to_s
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ # Delegate may_...? and may_...! methods to the user's role.
75
+ def method_missing_with_aegis_permissions(symb, *args)
76
+ method_name = symb.to_s
77
+ if method_name =~ /^#{Aegis::Constants::PERMISSION_PREFIX}_(.+?)[\!\?]$/
78
+ role.send(symb, self, *args)
79
+ elsif method_name =~ /^(.*?)\?$/ && queried_role = ::Permissions.find_role_by_name($1)
80
+ role == queried_role
81
+ else
82
+ method_missing_without_aegis_permissions(symb, *args)
83
+ end
84
+ end
85
+
86
+ alias_method_chain :method_missing, :aegis_permissions
87
+
88
+ def respond_to_with_aegis_permissions?(symb, include_private = false)
89
+ if symb.to_s =~ /^#{Aegis::Constants::PERMISSION_PREFIX}_(.+?)[\!\?]$/
90
+ true
91
+ else
92
+ respond_to_without_aegis_permissions?(symb, include_private)
93
+ end
94
+ end
95
+
96
+ alias_method_chain :respond_to?, :aegis_permissions
97
+
98
+ def set_default_aegis_role_name
99
+ if new_record? && self.aegis_role_name.blank?
100
+ self.aegis_role_name = self.class.aegis_default_role_name
101
+ end
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+
108
+ end
109
+
110
+ end