rolify 3.0.0 → 3.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/CHANGELOG.rdoc +13 -1
- data/{README.rdoc → README.md} +69 -41
- data/lib/generators/rolify/role/role_generator.rb +2 -2
- data/lib/generators/rolify/role/templates/initializer.rb +3 -3
- data/lib/generators/rolify/role/templates/role-mongoid.rb +9 -0
- data/lib/rolify.rb +10 -7
- data/lib/rolify/adapters/active_record/resource_adapter.rb +17 -0
- data/lib/rolify/adapters/{active_record.rb → active_record/role_adapter.rb} +12 -21
- data/lib/rolify/adapters/base.rb +24 -22
- data/lib/rolify/adapters/mongoid/resource_adapter.rb +23 -0
- data/lib/rolify/adapters/{mongoid.rb → mongoid/role_adapter.rb} +11 -31
- data/lib/rolify/configure.rb +5 -4
- data/lib/rolify/dynamic.rb +1 -1
- data/lib/rolify/role.rb +11 -7
- data/lib/rolify/utils.rb +10 -0
- data/lib/rolify/version.rb +1 -1
- data/rolify.gemspec +1 -1
- data/spec/generators/rolify/role/role_generator_spec.rb +41 -19
- data/spec/rolify/config_spec.rb +13 -11
- data/spec/rolify/resource_spec.rb +28 -29
- data/spec/rolify/shared_contexts.rb +17 -17
- data/spec/rolify/shared_examples/{shared_examples_for_has_role_setter.rb → shared_examples_for_add_role.rb} +16 -16
- data/spec/rolify/shared_examples/shared_examples_for_dynamic.rb +6 -6
- data/spec/rolify/shared_examples/shared_examples_for_has_any_role.rb +6 -6
- data/spec/rolify/shared_examples/{shared_examples_for_has_role_getter.rb → shared_examples_for_has_role.rb} +0 -0
- data/spec/rolify/shared_examples/{shared_examples_for_has_no_role.rb → shared_examples_for_remove_role.rb} +37 -13
- data/spec/rolify/shared_examples/shared_examples_for_roles.rb +18 -20
- data/spec/spec_helper.rb +0 -4
- data/spec/support/adapters/active_record.rb +2 -0
- data/spec/support/adapters/mongoid.rb +2 -0
- metadata +33 -31
data/CHANGELOG.rdoc
CHANGED
@@ -1,4 +1,16 @@
|
|
1
|
-
= 3.
|
1
|
+
= 3.1 (Apr 6, 2012)
|
2
|
+
* Mongoid adapter optimization
|
3
|
+
* adapter code refactoring
|
4
|
+
* generator now adds the role class name to the rolify method injected in the user class
|
5
|
+
* fixed a bug on the generator when using a 2 words Camel case for the Role class name
|
6
|
+
* <b>DEPRECATION NOTICE:</b> <tt>has_role</tt> and <tt>has_no_role</tt> have been depecrated. They are replaced by <tt>add_role</tt> and <tt>remove_role</tt>
|
7
|
+
* some internals cleanup (backward compatible)
|
8
|
+
* stop requiring <tt>active_record</tt> in <tt>rolify.rb</tt> to prevent other gems ORM detection issue
|
9
|
+
* fixed a bug when removing a role to the user using Mongoid adapter
|
10
|
+
* added indexes to generator for mongoid (thanks to @stigi)
|
11
|
+
* fixed a bug regarding **with_role** method on resource classes (thanks to @nfo)
|
12
|
+
|
13
|
+
= 3.0 (Apr 2, 2012)
|
2
14
|
* support for Mongoid
|
3
15
|
* roles search on resources on instance level (e.g. <tt>Forum.first.roles</tt>) and class level (e.g. <tt>Forum.with_role("admin", user)</tt>)
|
4
16
|
* heavy lifting and redesign of the library, code and specs refactoring
|
data/{README.rdoc → README.md}
RENAMED
@@ -1,138 +1,166 @@
|
|
1
|
-
|
1
|
+
# rolify [](http://travis-ci.org/EppO/rolify) [](https://gemnasium.com/EppO/rolify)
|
2
2
|
|
3
3
|
Very simple Roles library without any authorization enforcement supporting scope on resource object.
|
4
4
|
|
5
|
-
Let's see an example:
|
5
|
+
Let's see an example:
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
```ruby
|
8
|
+
user.has_role?(:moderator, Forum.first)
|
9
|
+
=> false # if user is moderator of another Forum
|
10
|
+
```
|
9
11
|
|
10
|
-
This library was intended to be used with CanCan
|
12
|
+
This library was intended to be used with [CanCan](https://github.com/ryanb/cancan) and [devise](https://github.com/plataformatec/devise) but should be generic enough to be used by any other authentication/authorization solutions.
|
11
13
|
|
12
|
-
|
14
|
+
## Requirements
|
13
15
|
|
14
16
|
* >= Rails 3.1
|
15
17
|
* ActiveRecord ORM <b>or</b> Mongoid
|
16
18
|
* supports ruby 1.8/1.9, REE, JRuby and Rubinius
|
17
19
|
|
18
|
-
|
20
|
+
## Installation
|
19
21
|
|
20
22
|
In <b>Rails 3</b>, add this to your Gemfile and run the +bundle+ command.
|
21
23
|
|
24
|
+
```ruby
|
22
25
|
gem "rolify"
|
26
|
+
```
|
23
27
|
|
24
28
|
Alternatively, you can install it as a plugin.
|
25
29
|
|
30
|
+
```
|
26
31
|
rails plugin install git://github.com/EppO/rolify.git
|
32
|
+
```
|
27
33
|
|
28
|
-
|
34
|
+
## Getting Started
|
29
35
|
|
30
|
-
|
36
|
+
### 1. Generate Role Model
|
31
37
|
|
32
38
|
First, create your Role model and migration file using this generator:
|
33
39
|
|
40
|
+
```
|
34
41
|
rails g rolify:role Role User
|
42
|
+
```
|
35
43
|
|
36
44
|
Role and User classes are the default. You can specify any Role class name you want. This is completly a new file so any name can do the job.
|
37
45
|
For the User class name, you would probably use the one provided by your authentication solution. rolify just adds some class methods in an existing User class.
|
38
46
|
|
39
|
-
If you want to use Mongoid instead of ActiveRecord, follow these instructions
|
47
|
+
If you want to use Mongoid instead of ActiveRecord, follow these [instructions](https://github.com/EppO/rolify/wiki/Configuration), and skip to step #3
|
40
48
|
|
41
|
-
|
49
|
+
### 2. Run the migration (only required when using ActiveRecord)
|
42
50
|
|
43
51
|
Let's migrate !
|
44
52
|
|
53
|
+
```
|
45
54
|
rake db:migrate
|
55
|
+
```
|
46
56
|
|
47
|
-
|
57
|
+
### 3. Add a role to a user
|
48
58
|
|
49
59
|
To define a global role:
|
50
60
|
|
61
|
+
```ruby
|
51
62
|
user = User.find(1)
|
52
|
-
user.
|
63
|
+
user.add_role :admin
|
64
|
+
```
|
53
65
|
|
54
66
|
To define a role scoped to a resource instance
|
55
67
|
|
68
|
+
```ruby
|
56
69
|
user = User.find(2)
|
57
|
-
user.
|
70
|
+
user.add_role :moderator, Forum.first
|
71
|
+
```
|
58
72
|
|
59
73
|
To define a role scoped to a resource class
|
60
74
|
|
75
|
+
```ruby
|
61
76
|
user = User.find(3)
|
62
|
-
user.
|
77
|
+
user.add_role :moderator, Forum
|
78
|
+
```
|
63
79
|
|
64
80
|
That's it !
|
65
81
|
|
66
|
-
|
82
|
+
### 4. Check roles
|
67
83
|
|
68
84
|
To check if a user has a global role:
|
69
85
|
|
86
|
+
```ruby
|
70
87
|
user = User.find(1)
|
71
|
-
user.
|
72
|
-
user.has_role?
|
88
|
+
user.add_role :admin # sets a global role
|
89
|
+
user.has_role? :admin
|
73
90
|
=> true
|
91
|
+
```
|
74
92
|
|
75
93
|
To check if a user has a role scoped to a resource instance:
|
76
94
|
|
95
|
+
```ruby
|
77
96
|
user = User.find(2)
|
78
|
-
user.
|
79
|
-
user.has_role?
|
97
|
+
user.add_role :moderator, Forum.first # sets a role scoped to a resource instance
|
98
|
+
user.has_role? :moderator, Forum.first
|
80
99
|
=> true
|
81
|
-
user.has_role?
|
100
|
+
user.has_role? :moderator, Forum.last
|
82
101
|
=> false
|
102
|
+
```
|
83
103
|
|
84
104
|
To check if a user has a role scoped to a resource class:
|
85
105
|
|
106
|
+
```ruby
|
86
107
|
user = User.find(3)
|
87
|
-
user.
|
88
|
-
user.has_role?
|
108
|
+
user.add_role :moderator, Forum # sets a role scoped to a resource class
|
109
|
+
user.has_role? :moderator, Forum
|
89
110
|
=> true
|
90
|
-
user.has_role?
|
111
|
+
user.has_role? :moderator, Forum.first
|
91
112
|
=> true
|
92
|
-
user.has_role?
|
113
|
+
user.has_role? :moderator, Forum.last
|
93
114
|
=> true
|
115
|
+
```
|
94
116
|
|
95
117
|
A global role overrides resource role request:
|
96
118
|
|
119
|
+
```ruby
|
97
120
|
user = User.find(4)
|
98
|
-
user.
|
99
|
-
user.has_role?
|
121
|
+
user.add_role :moderator # sets a global role
|
122
|
+
user.has_role? :moderator, Forum.first
|
100
123
|
=> true
|
101
|
-
user.has_role?
|
124
|
+
user.has_role? :moderator, Forum.last
|
102
125
|
=> true
|
126
|
+
```
|
103
127
|
|
104
|
-
|
128
|
+
### 5. Resource roles querying
|
105
129
|
|
106
130
|
Starting from rolify 3.0, you can search roles on instance level or class level resources.
|
107
131
|
|
108
|
-
|
132
|
+
#### Instance level
|
109
133
|
|
134
|
+
```ruby
|
110
135
|
forum = Forum.first
|
111
136
|
forum.roles
|
112
137
|
# => [ list of roles that are only binded to forum instance ]
|
113
138
|
forum.applied_roles
|
114
139
|
# => [ list of roles binded to forum instance and to the Forum class ]
|
140
|
+
```
|
115
141
|
|
116
|
-
|
142
|
+
#### Class level
|
117
143
|
|
118
|
-
|
144
|
+
```ruby
|
145
|
+
Forum.with_role(:admin)
|
119
146
|
# => [ list of Forum instances that has role "admin" binded to it ]
|
120
|
-
Forum.with_role(
|
147
|
+
Forum.with_role(:admin, current_user)
|
121
148
|
# => [ list of Forum instances that has role "admin" binded to it and belongs to current_user roles ]
|
122
149
|
|
123
150
|
Forum.find_roles
|
124
151
|
# => [ list of roles that binded to any Forum instance or to the Forum class ]
|
125
|
-
Forum.find_roles(
|
152
|
+
Forum.find_roles(:admin)
|
126
153
|
# => [ list of roles that binded to any Forum instance or to the Forum class with "admin" as a role name ]
|
127
|
-
Forum.find_roles(
|
154
|
+
Forum.find_roles(:admin, current_user)
|
128
155
|
# => [ list of roles that binded to any Forum instance or to the Forum class with "admin" as a role name and belongs to current_user roles ]
|
156
|
+
```
|
129
157
|
|
130
|
-
|
158
|
+
## Resources
|
131
159
|
|
132
|
-
*
|
133
|
-
*
|
134
|
-
*
|
160
|
+
* [Wiki](https://github.com/EppO/rolify/wiki)
|
161
|
+
* [Usage](https://github.com/EppO/rolify/wiki/Usage): all the available commands
|
162
|
+
* [Tutorial](https://github.com/EppO/rolify/wiki/Tutorial): how to use [rolify](http://eppo.github.com/rolify) with [Devise](https://github.com/plataformatec/devise) and [CanCan](https://github.com/ryanb/cancan).
|
135
163
|
|
136
|
-
|
164
|
+
## Questions or Problems?
|
137
165
|
|
138
|
-
If you have any issue or feature request with/for rolify, please add an
|
166
|
+
If you have any issue or feature request with/for rolify, please add an [issue on GitHub](https://github.com/EppO/rolify/issues) or fork the project and send a pull request.
|
@@ -14,9 +14,9 @@ module Rolify
|
|
14
14
|
desc "Generates a model with the given NAME and a migration file."
|
15
15
|
|
16
16
|
def generate_role
|
17
|
-
template "role-#{orm_adapter}.rb", "app/models/#{role_cname.
|
17
|
+
template "role-#{orm_adapter}.rb", "app/models/#{role_cname.underscore}.rb"
|
18
18
|
inject_into_class(model_path, user_cname.camelize) do
|
19
|
-
"\trolify\n"
|
19
|
+
"\trolify" + (role_cname == "Role" ? "" : ":role_cname => '#{role_cname.camelize}'") + "\n"
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
@@ -1,7 +1,7 @@
|
|
1
|
-
Rolify.configure do |
|
1
|
+
Rolify.configure do |config|
|
2
2
|
# By default ORM adapter is ActiveRecord. uncomment to use mongoid
|
3
|
-
<%= "# " if orm_adapter == "active_record" %>
|
3
|
+
<%= "# " if orm_adapter == "active_record" %>config.use_mongoid
|
4
4
|
|
5
5
|
# Dynamic shortcuts for User class (user.is_admin? like methods). Default is: false
|
6
|
-
<%= "# " if !options[:dynamic_shortcuts] %>
|
6
|
+
<%= "# " if !options[:dynamic_shortcuts] %>config.use_dynamic_shortcuts
|
7
7
|
end
|
@@ -5,4 +5,13 @@ class <%= role_cname.camelize %>
|
|
5
5
|
belongs_to :resource, :polymorphic => true
|
6
6
|
|
7
7
|
field :name, :type => String
|
8
|
+
index :name, unique: true
|
9
|
+
index(
|
10
|
+
[
|
11
|
+
[:name, Mongo::ASCENDING],
|
12
|
+
[:resource_type, Mongo::ASCENDING],
|
13
|
+
[:resource_id, Mongo::ASCENDING]
|
14
|
+
],
|
15
|
+
unique: true
|
16
|
+
)
|
8
17
|
end
|
data/lib/rolify.rb
CHANGED
@@ -1,36 +1,39 @@
|
|
1
|
-
require 'active_record'
|
2
|
-
|
3
|
-
require 'rolify/adapters/active_record' if defined?(ActiveRecord)
|
4
|
-
require 'rolify/adapters/mongoid' if defined?(Mongoid)
|
5
1
|
require 'rolify/railtie' if defined?(Rails)
|
2
|
+
require 'rolify/utils'
|
6
3
|
require 'rolify/role'
|
7
4
|
require 'rolify/configure'
|
8
5
|
require 'rolify/dynamic'
|
9
6
|
require 'rolify/resource'
|
7
|
+
require 'rolify/adapters/base'
|
10
8
|
|
11
9
|
module Rolify
|
12
10
|
extend Configure
|
11
|
+
|
13
12
|
attr_accessor :role_cname, :adapter
|
14
13
|
|
15
14
|
def rolify(options = { :role_cname => 'Role' })
|
16
15
|
include Role
|
17
16
|
extend Dynamic if Rolify.dynamic_shortcuts
|
17
|
+
|
18
18
|
rolify_options = { :class_name => options[:role_cname].camelize }
|
19
19
|
rolify_options.merge!({ :join_table => "#{self.to_s.tableize}_#{options[:role_cname].tableize}" }) if Rolify.orm == "active_record"
|
20
20
|
has_and_belongs_to_many :roles, rolify_options
|
21
21
|
|
22
|
-
|
22
|
+
self.adapter = Rolify::Adapter::Base.create("role_adapter", options[:role_cname], self.name)
|
23
23
|
self.role_cname = options[:role_cname]
|
24
|
-
|
24
|
+
|
25
|
+
load_dynamic_methods if Rolify.dynamic_shortcuts
|
25
26
|
end
|
26
27
|
|
27
28
|
def resourcify(options = { :role_cname => 'Role' })
|
28
29
|
include Resource
|
30
|
+
|
29
31
|
resourcify_options = { :class_name => options[:role_cname].camelize }
|
30
32
|
resourcify_options.merge!({ :as => :resource })
|
31
33
|
has_many :roles, resourcify_options
|
34
|
+
|
35
|
+
self.adapter = Rolify::Adapter::Base.create("resource_adapter", options[:role_cname], self.name)
|
32
36
|
self.role_cname = options[:role_cname]
|
33
|
-
self.adapter = Rolify::Adapter.const_get(Rolify.orm.camelize).new(options[:role_cname])
|
34
37
|
end
|
35
38
|
|
36
39
|
def role_class
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rolify/adapters/base'
|
2
|
+
|
3
|
+
module Rolify
|
4
|
+
module Adapter
|
5
|
+
class ResourceAdapter < ResourceAdapterBase
|
6
|
+
def resources_find(roles_table, relation, role_name)
|
7
|
+
resources = relation.joins("INNER JOIN \"#{roles_table}\" ON \"#{roles_table}\".\"resource_type\" = '#{relation.to_s}'")
|
8
|
+
resources = resources.where("#{roles_table}.name = ? AND #{roles_table}.resource_type = ?", role_name, relation.to_s)
|
9
|
+
resources
|
10
|
+
end
|
11
|
+
|
12
|
+
def in(relation, roles)
|
13
|
+
relation.where("#{role_class.to_s.tableize}.id IN (?) AND ((resource_id = #{relation.table_name}.id) OR (resource_id IS NULL))", roles)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -2,13 +2,8 @@ require 'rolify/adapters/base'
|
|
2
2
|
|
3
3
|
module Rolify
|
4
4
|
module Adapter
|
5
|
-
class
|
6
|
-
def
|
7
|
-
query, values = build_query(role_name, resource)
|
8
|
-
relation.where(query, *values)
|
9
|
-
end
|
10
|
-
|
11
|
-
def where(relation, args)
|
5
|
+
class RoleAdapter < RoleAdapterBase
|
6
|
+
def where(relation, *args)
|
12
7
|
conditions, values = build_conditions(relation, args)
|
13
8
|
relation.where(conditions, *values)
|
14
9
|
end
|
@@ -22,20 +17,16 @@ module Rolify
|
|
22
17
|
end
|
23
18
|
|
24
19
|
def remove(relation, role_name, resource = nil)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
36
|
-
|
37
|
-
def in(relation, roles)
|
38
|
-
relation.where("#{role_class.to_s.tableize}.id IN (?) AND ((resource_id = #{relation.table_name}.id) OR (resource_id IS NULL))", roles)
|
20
|
+
roles = relation.roles.where(:name => role_name)
|
21
|
+
roles = roles.where(:resource_type => (resource.is_a?(Class) ? resource.to_s : resource.class.name)) if resource
|
22
|
+
roles = roles.where(:resource_id => resource.id) if resource && !resource.is_a?(Class)
|
23
|
+
if roles
|
24
|
+
relation.roles.delete(roles)
|
25
|
+
roles.each do |role|
|
26
|
+
role.destroy if role.send(user_class.table_name.to_sym).empty?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
roles
|
39
30
|
end
|
40
31
|
|
41
32
|
def exists?(relation, column)
|
data/lib/rolify/adapters/base.rb
CHANGED
@@ -1,52 +1,54 @@
|
|
1
1
|
module Rolify
|
2
2
|
module Adapter
|
3
3
|
class Base
|
4
|
-
def initialize(role_cname)
|
4
|
+
def initialize(role_cname, user_cname)
|
5
5
|
@role_cname = role_cname
|
6
|
+
@user_cname = user_cname
|
6
7
|
end
|
7
|
-
|
8
|
+
|
8
9
|
def role_class
|
9
10
|
@role_cname.constantize
|
10
11
|
end
|
11
12
|
|
12
|
-
def
|
13
|
-
|
13
|
+
def user_class
|
14
|
+
@user_cname.constantize
|
14
15
|
end
|
15
16
|
|
17
|
+
def self.create(adapter, role_cname, user_cname)
|
18
|
+
load "rolify/adapters/#{Rolify.orm}/#{adapter}.rb"
|
19
|
+
Rolify::Adapter.const_get(adapter.camelize.to_sym).new(role_cname, user_cname)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class RoleAdapterBase < Adapter::Base
|
16
24
|
def where(relation, args)
|
17
25
|
raise NotImplementedError.new("You must implement where")
|
18
26
|
end
|
19
|
-
|
27
|
+
|
20
28
|
def find_or_create_by(role_name, resource_type = nil, resource_id = nil)
|
21
29
|
raise NotImplementedError.new("You must implement find_or_create_by")
|
22
30
|
end
|
23
|
-
|
31
|
+
|
24
32
|
def add(relation, role_name, resource = nil)
|
25
33
|
raise NotImplementedError.new("You must implement add")
|
26
34
|
end
|
27
|
-
|
35
|
+
|
28
36
|
def remove(relation, role_name, resource = nil)
|
29
37
|
raise NotImplementedError.new("You must implement delete")
|
30
38
|
end
|
31
|
-
|
32
|
-
def resources_find(roles_table, relation, role_name)
|
33
|
-
raise NotImplementedError.new("You must implement resources_find")
|
34
|
-
end
|
35
|
-
|
36
|
-
def in(resources, roles)
|
37
|
-
raise NotImplementedError.new("You must implement in")
|
38
|
-
end
|
39
|
-
|
39
|
+
|
40
40
|
def exists?(relation, column)
|
41
41
|
raise NotImplementedError.new("You must implement exists?")
|
42
42
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
end
|
44
|
+
|
45
|
+
class ResourceAdapterBase < Adapter::Base
|
46
|
+
def resources_find(roles_table, relation, role_name)
|
47
|
+
raise NotImplementedError.new("You must implement resources_find")
|
46
48
|
end
|
47
|
-
|
48
|
-
def
|
49
|
-
raise NotImplementedError.new("You must implement
|
49
|
+
|
50
|
+
def in(resources, roles)
|
51
|
+
raise NotImplementedError.new("You must implement in")
|
50
52
|
end
|
51
53
|
end
|
52
54
|
end
|