access-granted 1.1.2 → 1.3.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b6d400982e35b05842762be3929a3bfb9d1a9de6
4
- data.tar.gz: 3825d04d00c581e55b21339c92e6b8b3b5e90672
2
+ SHA256:
3
+ metadata.gz: 115b6ed416c4bfa4b6d94d53520388c382b65966aa8ce7c4072d9991a630d1d3
4
+ data.tar.gz: 0af1baa07da37953f292b4bb8d24680cbebdf70c1495203d7a4312d4735584bb
5
5
  SHA512:
6
- metadata.gz: f1fa9bad01415dc1642a0d7d4c6396f4db625f88f87c81f59649fb230547da4f32cbbcb01bb3362a4716cdfba702ed97719afc2eecee688a8383c05901e2f9b6
7
- data.tar.gz: bc6346e03d9d8502f4e42fce4ec0eba1939fd5e3c01846380f480c1e0feb4936af16840d2caf507f8b75e4b66f9e7b9b03841e42cac2c34200d512d409d2e738
6
+ metadata.gz: 6554c68a9ddd5f04866afef389d59d48ddbd63fc6173c6955b39075fb9f23ac5c8d1036f13cc8f7ac9fed997217d1d69b725c5f6e0dc83550a1e8eea293e6e6d
7
+ data.tar.gz: 162efc4e19ad3fa554778b00dfd46d28f951ac90a65d94c2f3037f554dc590f9822eb7db6a19c792f65eeaba78adcc77386f9956e88c7f00f9127a2a231141e9
data/.travis.yml CHANGED
@@ -1,9 +1,11 @@
1
1
  language: ruby
2
2
  sudo: false
3
3
  rvm:
4
- - 1.9.3
5
4
  - 2.0
6
5
  - 2.1
7
6
  - 2.2
7
+ - 2.3
8
+ - 2.4
9
+ - 2.5
8
10
  - jruby-1
9
11
  - jruby
data/CHANGELOG.md CHANGED
@@ -1,3 +1,42 @@
1
+ # 1.3.3
2
+
3
+ - Fix compatibility with Rails 6.0 and Zeitwerk ([PR #53](https://github.com/chaps-io/access-granted/pull/53)), thanks [jraqula](https://github.com/dmorehouse)!
4
+
5
+ # 1.3.2
6
+
7
+ - Expose `applicable_roles` method on the policy instance. This allows insight into what roles actually apply to a given user.
8
+
9
+ # 1.3.1
10
+
11
+ - Add information about action and subject when raising AccessDenied exception ([PR #46](https://github.com/chaps-io/access-granted/pull/46)), thanks [jraqula](https://github.com/jraqula)!
12
+
13
+ # 1.3.0
14
+
15
+ - Drop support for Ruby 1.9.3, it might still work but we are no longer testing against it.
16
+ - Start testing against Rubies 2.3-2.5 in CI
17
+ - Move Rails integration into Railties, this fixes some load order issues ([PR #45](https://github.com/chaps-io/access-granted/pull/45)), thanks [jraqula](https://github.com/jraqula)!
18
+
19
+ # 1.2.0
20
+
21
+ - Cache whole blocks of identical permissions when one of them is checked.
22
+ For example, assuming we have a given permissions set:
23
+
24
+ ```ruby
25
+ can [:update, :destroy, :archive], Post do |post, user|
26
+ post.user_id == user.id
27
+ end
28
+ ```
29
+
30
+ When resolving one of them like this:
31
+
32
+ ```ruby
33
+ can? :update, @post
34
+ ```
35
+
36
+ Access Granted will cache the result for each of the remaining actions, too.
37
+ So next time when checking permissions `:destroy` or `:archive`, AG will serve the result from cache instead of running the block again.
38
+
39
+
1
40
  # 1.1.2
2
41
 
3
42
  - Expose internal `block` instance variable in Permission class
data/README.md CHANGED
@@ -19,11 +19,13 @@ Run the bundle command to install it. Then run the generator:
19
19
 
20
20
  Add the `policies` (and `roles` if you're using it to split up your roles into files) directories to your autoload paths in `application.rb`:
21
21
 
22
- config.autoload_paths += %W(#{config.root}/app/policies #{config.root}/app/roles)
22
+ ```ruby
23
+ config.autoload_paths += %W(#{config.root}/app/policies #{config.root}/app/roles)
24
+ ```
23
25
 
24
26
  ### Supported Ruby versions
25
27
 
26
- Because it has **zero** runtime dependencies it is guaranteed to work on all major Ruby versions MRI 1.9.3-2.2, Rubinius >= 2.X and JRuby >= 1.7.
28
+ Because it has **zero** runtime dependencies it is guaranteed to work on all major Ruby versions MRI `2.0` - `2.5`, Rubinius `>= 2.X` and JRuby `>= 1.7`.
27
29
 
28
30
  ## Summary
29
31
 
@@ -31,26 +33,26 @@ AccessGranted is meant as a replacement for CanCan to solve major problems:
31
33
 
32
34
  1. Performance
33
35
 
34
- On average AccessGranted is **20 times faster** in resolving identical permissions and takes less memory.
35
- See [benchmarks](https://github.com/chaps-io/access-granted/blob/master/benchmarks).
36
+ On average AccessGranted is **20 times faster** in resolving identical permissions and takes less memory.
37
+ See [benchmarks](https://github.com/chaps-io/access-granted/blob/master/benchmarks).
36
38
 
37
39
  2. Roles
38
40
 
39
- Adds support for roles, so no more `if`s and `else`s in your Policy file. This makes it extremely easy to maintain and read the code.
41
+ Adds support for roles, so no more `if`s and `else`s in your Policy file. This makes it extremely easy to maintain and read the code.
40
42
 
41
43
  3. Whitelists
42
44
 
43
- This means that you define what the user can do, which results in clean, readable policies regardless of application complexity.
44
- You don't have to worry about juggling `can`s and `cannot`s in a very convoluted way!
45
+ This means that you define what the user can do, which results in clean, readable policies regardless of application complexity.
46
+ You don't have to worry about juggling `can`s and `cannot`s in a very convoluted way!
45
47
 
46
- _Note_: `cannot` is still available, but has a very specifc use. See [Usage](#usage) below.
48
+ _Note_: `cannot` is still available, but has a very specifc use. See [Usage](#usage) below.
47
49
 
48
50
  4. Framework agnostic
49
51
 
50
- Permissions can work on basically any object and AccessGranted is framework-agnostic,
51
- but it has Rails support out of the box. :)
52
- It does not depend on any libraries, pure and clean Ruby code. Guaranteed to always work,
53
- even when software around changes.
52
+ Permissions can work on basically any object and AccessGranted is framework-agnostic,
53
+ but it has Rails support out of the box. :)
54
+ It does not depend on any libraries, pure and clean Ruby code. Guaranteed to always work,
55
+ even when software around changes.
54
56
 
55
57
  ## Usage
56
58
 
@@ -210,6 +212,41 @@ class ApplicationController < ActionController::Base
210
212
  end
211
213
  ```
212
214
 
215
+ You can also extract the action and subject which raised the error,
216
+ if you want to handle authorization errors differently for some cases:
217
+ ```ruby
218
+ rescue_from "AccessGranted::AccessDenied" do |exception|
219
+ status = case exception.action
220
+ when :read # invocation like `authorize! :read, @something`
221
+ 403
222
+ else
223
+ 404
224
+ end
225
+
226
+ body = case exception.subject
227
+ when Post # invocation like `authorize! @some_action, Post`
228
+ "failed to access a post"
229
+ else
230
+ "failed to access something else"
231
+ end
232
+ end
233
+ ```
234
+
235
+ You can also have a custom exception message while authorizing a request.
236
+ This message will be associated with the exception object thrown.
237
+
238
+ ```ruby
239
+ class PostsController
240
+ def show
241
+ @post = Post.find(params[:id])
242
+ authorize! :read, @post, 'You do not have access to this post'
243
+ render json: { post: @post }
244
+ rescue AccessGranted::AccessDenied => e
245
+ render json: { error: e.message }, status: :forbidden
246
+ end
247
+ end
248
+ ```
249
+
213
250
  #### Checking permissions in controllers
214
251
 
215
252
  To check if the user has a permission to perform an action, use the `can?` and `cannot?` methods.
@@ -280,7 +317,7 @@ or with `cannot?`:
280
317
 
281
318
  ```ruby
282
319
  policy.cannot?(:create, Post) #=> false
283
- policy.cannot?(:update, @ost) #=> true
320
+ policy.cannot?(:update, @post) #=> true
284
321
  ```
285
322
 
286
323
  ## Common examples
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "access-granted"
7
- spec.version = "1.1.2"
7
+ spec.version = "1.3.3"
8
8
  spec.authors = ["Piotrek Okoński"]
9
9
  spec.email = ["piotrek@okonski.org"]
10
10
  spec.description = %q{Role based authorization gem}
data/benchmarks/README.md CHANGED
@@ -1,24 +1,24 @@
1
1
  # Benchmark results
2
2
 
3
- Benchmarks ran on Ubuntu 15.04 64bit, i5 2500k @ 4.4Ghz, 16 GB RAM with Ruby 2.2.
3
+ Benchmarks ran on Ubuntu 17.04 64bit, i7 6700k @ 4.0Ghz, 32 GB RAM with Ruby 2.3.
4
4
 
5
5
  ## permissions.rb
6
6
 
7
7
  This benchmark runs `can?` method for the 3 user roles for 20 seconds each, for both CanCan and AccessGranted.
8
8
 
9
9
  ```
10
+ Warming up --------------------------------------
11
+ ag-admin 158.815k i/100ms
12
+ ag-moderator 161.055k i/100ms
13
+ ag-user 161.670k i/100ms
14
+ cancan-admin 14.865k i/100ms
15
+ cancan-moderator 13.181k i/100ms
16
+ cancan-user 18.907k i/100ms
10
17
  Calculating -------------------------------------
11
- ag-admin 21.361k i/100ms
12
- cancan-admin 13.631k i/100ms
13
- ag-moderator 22.328k i/100ms
14
- cancan-moderator 11.679k i/100ms
15
- ag-user 25.860k i/100ms
16
- cancan-user 16.308k i/100ms
17
- -------------------------------------------------
18
- ag-admin 283.174k (± 1.1%) i/s - 5.682M
19
- cancan-admin 160.450k (± 1.0%) i/s - 3.217M
20
- ag-moderator 301.290k (± 1.1%) i/s - 6.029M
21
- cancan-moderator 134.591k (± 1.3%) i/s - 2.698M
22
- ag-user 353.259k (± 0.9%) i/s - 7.086M
23
- cancan-user 198.579k (± 1.6%) i/s - 3.979M
18
+ ag-admin 2.141M (± 3.9%) i/s - 10.799M in 5.052573s
19
+ ag-moderator 2.180M (± 2.1%) i/s - 10.952M in 5.025727s
20
+ ag-user 2.206M (± 0.4%) i/s - 11.155M in 5.056550s
21
+ cancan-admin 158.288k (± 2.4%) i/s - 802.710k in 5.074299s
22
+ cancan-moderator 142.573k (± 2.1%) i/s - 724.955k in 5.087277s
23
+ cancan-user 204.783k (± 2.2%) i/s - 1.040M in 5.080488s
24
24
  ```
@@ -3,19 +3,9 @@ require "access-granted/policy"
3
3
  require "access-granted/permission"
4
4
  require "access-granted/role"
5
5
  require "access-granted/rails/controller_methods"
6
+ require "access-granted/railtie" if defined?(Rails)
6
7
 
7
8
  module AccessGranted
8
9
 
9
10
  end
10
11
 
11
- if defined? ActionController::Base
12
- ActionController::Base.class_eval do
13
- include AccessGranted::Rails::ControllerMethods
14
- end
15
- end
16
-
17
- if defined? ActionController::API
18
- ActionController::API.class_eval do
19
- include AccessGranted::Rails::ControllerMethods
20
- end
21
- end
@@ -3,5 +3,12 @@ module AccessGranted
3
3
 
4
4
  class DuplicatePermission < Error; end;
5
5
  class DuplicateRole < Error; end;
6
- class AccessDenied < Error; end;
6
+ class AccessDenied < Error
7
+ attr_reader :action, :subject, :message
8
+ def initialize(action = nil, subject = nil, message = nil)
9
+ @action = action
10
+ @subject = subject
11
+ @message = message
12
+ end
13
+ end
7
14
  end
@@ -1,13 +1,14 @@
1
1
  module AccessGranted
2
2
  class Permission
3
- attr_reader :action, :subject, :granted, :conditions, :block
3
+ attr_reader :action, :subject, :granted, :conditions, :actions, :block
4
4
 
5
- def initialize(granted, action, subject, user = nil, conditions = {}, block = nil)
5
+ def initialize(granted, action, subject, user = nil, conditions = {}, actions = [], block = nil)
6
6
  @action = action
7
7
  @user = user
8
8
  @granted = granted
9
9
  @subject = subject
10
10
  @conditions = conditions
11
+ @actions = actions
11
12
  @block = block
12
13
  end
13
14
 
@@ -20,10 +21,12 @@ module AccessGranted
20
21
  end
21
22
 
22
23
  def matches_conditions?(subject)
23
- if @block && !subject.is_a?(Class)
24
+ if @block
24
25
  @block.call(subject, @user)
25
- else
26
+ elsif !@conditions.empty?
26
27
  matches_hash_conditions?(subject)
28
+ else
29
+ true
27
30
  end
28
31
  end
29
32
 
@@ -29,31 +29,40 @@ module AccessGranted
29
29
 
30
30
  def can?(action, subject = nil)
31
31
  cache[action] ||= {}
32
- cache[action][subject] ||= check_permission(action, subject)
32
+
33
+ if cache[action][subject]
34
+ cache[action][subject]
35
+ else
36
+ granted, actions = check_permission(action, subject)
37
+ actions.each do |a|
38
+ cache[a] ||= {}
39
+ cache[a][subject] ||= granted
40
+ end
41
+
42
+ granted
43
+ end
33
44
  end
34
45
 
35
46
  def check_permission(action, subject)
36
47
  applicable_roles.each do |role|
37
48
  permission = role.find_permission(action, subject)
38
- return permission.granted if permission
49
+ return [permission.granted, permission.actions] if permission
39
50
  end
40
51
 
41
- false
52
+ [false, []]
42
53
  end
43
54
 
44
55
  def cannot?(*args)
45
56
  !can?(*args)
46
57
  end
47
58
 
48
- def authorize!(action, subject)
59
+ def authorize!(action, subject, message = 'Access Denied')
49
60
  if cannot?(action, subject)
50
- raise AccessDenied
61
+ raise AccessDenied.new(action, subject, message)
51
62
  end
52
63
  subject
53
64
  end
54
65
 
55
- private
56
-
57
66
  def applicable_roles
58
67
  @applicable_roles ||= roles.select do |role|
59
68
  role.applies_to?(user)
@@ -0,0 +1,29 @@
1
+ require 'rails/railtie'
2
+
3
+ module AccessGranted
4
+ class Railtie < ::Rails::Railtie
5
+ initializer :access_granted do
6
+ if ::Rails::VERSION::MAJOR >= 6
7
+ ActiveSupport.on_load(:action_controller_base) do |base|
8
+ base.include AccessGranted::Rails::ControllerMethods
9
+ end
10
+
11
+ ActiveSupport.on_load(:action_controller_api) do |base|
12
+ base.include AccessGranted::Rails::ControllerMethods
13
+ end
14
+ else
15
+ if defined? ActionController::Base
16
+ ActionController::Base.class_eval do
17
+ include AccessGranted::Rails::ControllerMethods
18
+ end
19
+ end
20
+
21
+ if defined? ActionController::API
22
+ ActionController::API.class_eval do
23
+ include AccessGranted::Rails::ControllerMethods
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -53,9 +53,10 @@ module AccessGranted
53
53
  end
54
54
 
55
55
  def add_permission(granted, action, subject, conditions, block)
56
- prepare_actions(action).each do |a|
56
+ prepared_actions = prepare_actions(action)
57
+ prepared_actions.each do |a|
57
58
  raise DuplicatePermission, "Permission `#{a}` is already defined for #{subject} in role `#{name}`" if find_permission(a, subject)
58
- permissions << Permission.new(granted, a, subject, @user, conditions, block)
59
+ permissions << Permission.new(granted, a, subject, @user, conditions, prepared_actions, block)
59
60
  end
60
61
  end
61
62
 
@@ -68,11 +69,8 @@ module AccessGranted
68
69
  end
69
70
 
70
71
  def prepare_actions(action)
71
- if action == :manage
72
- actions = [:read, :create, :update, :destroy]
73
- else
74
- actions = Array(*[action])
75
- end
72
+ actions = Array(*[action])
73
+ actions.flat_map { |a| a == :manage ? [:create, :read, :update, :destroy ] : [a] }
76
74
  end
77
75
  end
78
76
  end
@@ -21,7 +21,11 @@ describe AccessGranted::Rails::ControllerMethods do
21
21
 
22
22
  describe "#authorize!" do
23
23
  it "raises exception when authorization fails" do
24
- expect { @controller.authorize!(:read, String) }.to raise_error(AccessGranted::AccessDenied)
24
+ expect { @controller.authorize!(:read, String) }.to raise_error do |err|
25
+ expect(err).to be_a(AccessGranted::AccessDenied)
26
+ expect(err.action).to eq(:read)
27
+ expect(err.subject).to eq(String)
28
+ end
25
29
  end
26
30
 
27
31
  it "returns subject if authorization succeeds" do
@@ -3,30 +3,23 @@ require 'spec_helper'
3
3
  describe AccessGranted::Permission do
4
4
  subject { AccessGranted::Permission }
5
5
 
6
- describe "#matches_conditions?" do
7
- it "matches when no conditions given" do
8
- perm = subject.new(true, :read, String)
9
- expect(perm.matches_conditions?(String)).to eq(true)
10
- end
6
+ describe "#matches_proc_conditions?" do
11
7
 
12
- it "matches proc conditions" do
8
+ it "matches proc conditions when true" do
13
9
  sub = double("Element", published?: true)
14
- perm = subject.new(true, :read, sub.class, nil, {}, proc {|el| el.published? })
10
+ perm = subject.new(true, :read, sub, nil, {}, [], proc {true})
15
11
  expect(perm.matches_conditions?(sub)).to eq(true)
16
12
  end
17
13
 
18
- it "does not match proc conditions when given a class instead of an instance" do
14
+ it "does not match proc conditions false" do
19
15
  sub = double("Element", published?: true)
20
- perm = subject.new(true, :read, sub.class, nil, {}, proc {|el| el.published? })
21
- expect(perm.matches_conditions?(sub.class)).to eq(true)
16
+ perm = subject.new(true, :read, sub, nil, {}, [], proc {false})
17
+ expect(perm.matches_conditions?(sub)).to eq(false)
22
18
  end
19
+
23
20
  end
24
21
 
25
22
  describe "#matches_hash_conditions?" do
26
- it "matches condition hash is empty" do
27
- perm = subject.new(true, :read, String)
28
- expect(perm.matches_hash_conditions?(String)).to eq(true)
29
- end
30
23
 
31
24
  it "matches when conditions given" do
32
25
  sub = double("Element", published: true)
@@ -39,6 +32,7 @@ describe AccessGranted::Permission do
39
32
  perm = subject.new(true, :read, sub, nil, { published: true, readable: true })
40
33
  expect(perm.matches_hash_conditions?(sub)).to eq(false)
41
34
  end
35
+
42
36
  end
43
37
 
44
38
  describe "#matches_action?" do
@@ -46,6 +40,7 @@ describe AccessGranted::Permission do
46
40
  perm = subject.new(true, :read, String)
47
41
  expect(perm.matches_action?(:read)).to_not be_nil
48
42
  end
43
+
49
44
  end
50
45
 
51
46
  describe "#matches_subject?" do
@@ -73,5 +68,15 @@ describe AccessGranted::Permission do
73
68
  perm = subject.new(true, :read, String)
74
69
  expect(perm.matches_subject? Object.new).to eq(false)
75
70
  end
71
+
76
72
  end
73
+
74
+ describe "#matches_empty_conditions?" do
75
+ it "matches when no conditions given" do
76
+ perm = subject.new(true, :read, String)
77
+ expect(perm.matches_conditions?(String)).to eq(true)
78
+ end
79
+
80
+ end
81
+
77
82
  end
data/spec/policy_spec.rb CHANGED
@@ -136,7 +136,21 @@ describe AccessGranted::Policy do
136
136
  end
137
137
 
138
138
  it "raises AccessDenied if action is not allowed" do
139
- expect { klass.new(@member).authorize!(:create, Integer) }.to raise_error AccessGranted::AccessDenied
139
+ expect { klass.new(@member).authorize!(:create, Integer) }.to raise_error do |err|
140
+ expect(err).to be_a(AccessGranted::AccessDenied)
141
+ expect(err.action).to eq(:create)
142
+ expect(err.subject).to eq(Integer)
143
+ end
144
+ end
145
+
146
+ it "raises AccessDenied with supplied message if action is not allowed" do
147
+ message = 'You are not allowed to create Integer'
148
+ expect { klass.new(@member).authorize!(:create, Integer, message) }.to raise_error do |err|
149
+ expect(err).to be_a(AccessGranted::AccessDenied)
150
+ expect(err.action).to eq(:create)
151
+ expect(err.subject).to eq(Integer)
152
+ expect(err.message).to eq(message)
153
+ end
140
154
  end
141
155
 
142
156
  it "returns the subject if allowed" do
@@ -184,8 +198,9 @@ describe AccessGranted::Policy do
184
198
  end
185
199
  end
186
200
 
187
- describe "#matching_roles" do
201
+ describe "#applicable_roles" do
188
202
  let(:user) { double("User", is_moderator: true, is_admin: true) }
203
+ subject(:policy) { klass.new(user) }
189
204
 
190
205
  before do
191
206
  policy.role(:administrator, { is_admin: true })
@@ -193,9 +208,17 @@ describe AccessGranted::Policy do
193
208
  policy.role(:member)
194
209
  end
195
210
 
196
- shared_examples 'role matcher' do
211
+ context "user matches all roles" do
197
212
  it "returns all matching roles in the order of priority" do
198
- expect(subject.map(&:name)).to eq([:administrator, :moderator, :member])
213
+ expect(policy.applicable_roles.map(&:name)).to eq([:administrator, :moderator, :member])
214
+ end
215
+ end
216
+
217
+ context "user is just an admin" do
218
+ let(:user) { double("User", is_moderator: false, is_admin: true) }
219
+
220
+ it 'returns array with admin and member roles' do
221
+ expect(policy.applicable_roles.map(&:name)).to eq([:administrator, :member])
199
222
  end
200
223
  end
201
224
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: access-granted
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotrek Okoński
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-02 00:00:00.000000000 Z
11
+ date: 2021-08-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -62,6 +62,7 @@ files:
62
62
  - lib/access-granted/permission.rb
63
63
  - lib/access-granted/policy.rb
64
64
  - lib/access-granted/rails/controller_methods.rb
65
+ - lib/access-granted/railtie.rb
65
66
  - lib/access-granted/role.rb
66
67
  - lib/generators/access_granted/policy_generator.rb
67
68
  - lib/generators/templates/access_policy.rb
@@ -89,8 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
90
  - !ruby/object:Gem::Version
90
91
  version: '0'
91
92
  requirements: []
92
- rubyforge_project:
93
- rubygems_version: 2.5.1
93
+ rubygems_version: 3.1.4
94
94
  signing_key:
95
95
  specification_version: 4
96
96
  summary: Elegant whitelist and role based authorization with ability to prioritize
@@ -101,4 +101,3 @@ test_files:
101
101
  - spec/policy_spec.rb
102
102
  - spec/role_spec.rb
103
103
  - spec/spec_helper.rb
104
- has_rdoc: