access-granted 1.0.4 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9d05f9e124c62d1c23281ac49ed7c482b8751698
4
- data.tar.gz: 7eb253b29b09538e3cef0f6f7d8f3cf217589c86
3
+ metadata.gz: 62c9307deb8d9c208a9934214e56999b093c017b
4
+ data.tar.gz: 74049e46c543d0015f565c96236ddc7b7fd7bea6
5
5
  SHA512:
6
- metadata.gz: 93bb115a1c3868245441187628a7d59fd603208f445c59b519837be4ad492ff76179e42b087af7d72dc845e9989bd133467d57dd4e8c184a1e923ca57450abdc
7
- data.tar.gz: 56d85a6e5dc4b4f71fe8755d2e635d24b36e14d5d96f7a2912d0ede6915c0f0edcfad46d0cb032ca21cc2f76706af40962871b1aec50d9f8f506c95154046222
6
+ metadata.gz: c15fffc2c35008b3e0dee3b758827c483ea12bcacc3eb0f9fabfec61004dd1929c92210846bb6a1ec0c0c66dab1b78c0d087f2d096d822f48f50519661f5a558
7
+ data.tar.gz: 70d741d7ed4606a3b1309e6a1b1a8ffdf84c0624c6040cb7e56cc6882c1dfbccbc533e65ac035b99ee695f33392059865e07fa694dc480e19913a4a25dd671d2
@@ -5,6 +5,7 @@ rvm:
5
5
  - 2.0
6
6
  - 2.1
7
7
  - 2.2
8
+ - 2.3
8
9
  - rbx-2
9
10
  - rbx
10
11
  - jruby-1
data/README.md CHANGED
@@ -3,12 +3,19 @@
3
3
  ## [![](http://i.imgur.com/ya8Wnyl.png)](https://chaps.io) proudly made by [Chaps](https://chaps.io)
4
4
 
5
5
 
6
- Multi-role and whitelist based authorization gem for Rails. And it's lightweight (~300 lines of code)!
6
+ AccessGranted is a multi-role and whitelist based authorization gem for Rails. And it's lightweight (~300 lines of code)!
7
7
 
8
8
 
9
9
  ## Installation
10
10
 
11
- gem 'access-granted'
11
+ Add the gem to your gemfile:
12
+
13
+ ```ruby
14
+ gem 'access-granted', '~> 1.0.0'
15
+ ```
16
+ Run the bundle command to install it. Then run the generator:
17
+
18
+ rails generate access_granted:policy
12
19
 
13
20
  ### Supported Ruby versions
14
21
 
@@ -20,37 +27,37 @@ AccessGranted is meant as a replacement for CanCan to solve major problems:
20
27
 
21
28
  1. Performance
22
29
 
23
- On average AccessGranted is 50-60% faster in resolving identical permissions and takes less memory.
30
+ On average AccessGranted is **20 times faster** in resolving identical permissions and takes less memory.
24
31
  See [benchmarks](https://github.com/chaps-io/access-granted/blob/master/benchmarks).
25
32
 
26
33
  2. Roles
27
34
 
28
- 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.
35
+ 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.
29
36
 
30
- 3. white-lists
37
+ 3. Whitelists
31
38
 
32
- This means that you define what the user **can** do, which results in clean, readable policies regardless of app complexity.
39
+ This means that you define what the user can do, which results in clean, readable policies regardless of application complexity.
33
40
  You don't have to worry about juggling `can`s and `cannot`s in a very convoluted way!
34
41
 
35
42
  _Note_: `cannot` is still available, but has a very specifc use. See [Usage](#usage) below.
36
43
 
37
- 4. framework agnostic
44
+ 4. Framework agnostic
38
45
 
39
46
  Permissions can work on basically any object and AccessGranted is framework-agnostic,
40
- but it has Rails support out of the box :)
41
- It **does not depend on any libraries**, pure and clean Ruby code. Guaranteed to always work,
47
+ but it has Rails support out of the box. :)
48
+ It does not depend on any libraries, pure and clean Ruby code. Guaranteed to always work,
42
49
  even when software around changes.
43
50
 
44
51
  ## Usage
45
52
 
46
53
  Roles are defined using blocks (or by passing custom classes to keep things tidy).
47
54
 
48
- **Order of the roles is VERY important**, because they are being traversed in the top-to-bottom order.
49
- At the top you must have an admin or other important role giving the user top permissions, and as you go down you define less-privileged roles.
55
+ **Order of the roles is VERY important**, because they are being traversed in top-to-bottom order.
56
+ At the top you must have an admin or some other important role giving the user top permissions, and as you go down you define less-privileged roles.
50
57
 
51
58
  **I recommend starting your adventure by reading my [blog post about AccessGranted](http://blog.chaps.io/2015/11/13/role-based-authorization-in-rails.html), where I demonstrate its abilities on a real life example.**
52
59
 
53
- ### 1. Defining access policy
60
+ ### Defining an access policy
54
61
 
55
62
  Let's start with a complete example of what can be achieved:
56
63
 
@@ -89,7 +96,7 @@ end
89
96
  #### Defining roles
90
97
 
91
98
  Each `role` method accepts the name of the role you're creating and an optional matcher.
92
- Matchers are used to check if user belongs to that role and if the permissions inside should be executed against it.
99
+ Matchers are used to check if the user belongs to that role and if the permissions inside should be executed against it.
93
100
 
94
101
  The simplest role can be defined as follows:
95
102
 
@@ -123,7 +130,7 @@ So, if the user has an attribute `is_admin` set to `true`, then the role will be
123
130
 
124
131
  #### Hash conditions
125
132
 
126
- Hashes can be used as matchers as a check if action is permitted.
133
+ Hashes can be used as matchers to check if an action is permitted.
127
134
  For example, we may allow users to only see published posts, like this:
128
135
 
129
136
  ```ruby
@@ -145,7 +152,7 @@ role :member do
145
152
  end
146
153
  ```
147
154
 
148
- When the given block evaluates to `true`, then user is given the permission to update the post.
155
+ When the given block evaluates to `true`, then `user` is given the permission to update the post.
149
156
 
150
157
  #### Roles in order of importance
151
158
 
@@ -168,7 +175,7 @@ As stated before: **`:admin` role takes precedence over `:member`** role, so whe
168
175
 
169
176
  That way you can keep a tidy and readable policy file which is basically human readable.
170
177
 
171
- ### Using in Rails
178
+ ### Usage with Rails
172
179
 
173
180
  AccessGranted comes with a set of helpers available in Ruby on Rails apps:
174
181
 
@@ -188,20 +195,20 @@ class PostsController
188
195
  end
189
196
  ```
190
197
 
191
- `authorize!` throws an exception when current user doesn't have a given permission.
198
+ `authorize!` throws an exception when `current_user` doesn't have a given permission.
192
199
  You can rescue from it using `rescue_from`:
193
200
 
194
201
  ```ruby
195
202
  class ApplicationController < ActionController::Base
196
203
  rescue_from "AccessGranted::AccessDenied" do |exception|
197
- redirect_to root_path, alert: "You don't have permissions to access this page."
204
+ redirect_to root_path, alert: "You don't have permission to access this page."
198
205
  end
199
206
  end
200
207
  ```
201
208
 
202
209
  #### Checking permissions in controllers
203
210
 
204
- To check if the user has a permission to perform an action, use `can?` and `cannot?` methods.
211
+ To check if the user has a permission to perform an action, use the `can?` and `cannot?` methods.
205
212
 
206
213
  **Example:**
207
214
 
@@ -244,16 +251,15 @@ By default, AccessGranted adds this method to your controllers:
244
251
  end
245
252
  ```
246
253
 
247
- If you have a different policy class or if your user is not stored in `current_user` variable, then you can override it in any controller and modify the logic as you please.
254
+ If you have a different policy class or if your user is not stored in the `current_user` variable, then you can override it in any controller and modify the logic as you please.
248
255
 
249
256
  You can even have different policies for different controllers!
250
257
 
251
- ### Using in pure Ruby
258
+ ### Usage with pure Ruby
252
259
 
253
260
  Initialize the Policy class:
254
261
 
255
262
  ```ruby
256
-
257
263
  policy = AccessPolicy.new(current_user)
258
264
  ```
259
265
 
@@ -296,7 +302,7 @@ end
296
302
 
297
303
  ```
298
304
 
299
- And roles should look like this
305
+ And roles should look like this:
300
306
 
301
307
  ```ruby
302
308
  # app/roles/member_role.rb
@@ -326,8 +332,8 @@ This gem has been created as a replacement for CanCan and therefore it requires
326
332
  The only change you have to make is to replace all `can? :manage, Class` with the exact action to check against.
327
333
  `can :manage` is still available for **defining** methods and serves as a shortcut for defining `:create`, `:read`, `:update`, `:destroy` all in one line.
328
334
 
329
- 3. Syntax for defining permissions in AccessPolicy file (Ability in CanCan) is exactly the same,
330
- with roles added on top. See [Usage](#usage) below.
335
+ 3. Syntax for defining permissions in the AccessPolicy file (Ability in CanCan) is exactly the same,
336
+ with roles added on top. See [Usage](#usage) above.
331
337
 
332
338
 
333
339
  ## Contributing
@@ -336,4 +342,4 @@ This gem has been created as a replacement for CanCan and therefore it requires
336
342
  2. Create your feature branch (`git checkout -b my-new-feature`)
337
343
  3. Commit your changes (`git commit -am 'Add some feature'`)
338
344
  4. Push to the branch (`git push origin my-new-feature`)
339
- 5. Create new Pull Request
345
+ 5. Create new pull request
@@ -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.0.4"
7
+ spec.version = "1.1.0"
8
8
  spec.authors = ["Piotrek Okoński"]
9
9
  spec.email = ["piotrek@okonski.org"]
10
10
  spec.description = %q{Role based authorization gem}
@@ -2,35 +2,45 @@ class Ability
2
2
  include CanCan::Ability
3
3
 
4
4
  def initialize(user)
5
- if user.is_admin == true
5
+ if user.is_admin
6
6
  can :destroy, String
7
+ can :foo, Integer
7
8
  end
8
9
 
9
- if user.is_moderator == true
10
+ if user.is_moderator
10
11
  can :update, String
12
+ can :bar, String
11
13
  end
12
14
 
13
15
  can :read, String
16
+ can :zoom, Integer
17
+ can :boom, Hash
18
+ can :rub, Fixnum
14
19
  end
15
20
  end
16
21
 
17
22
  class AccessPolicy
18
23
  include AccessGranted::Policy
19
24
 
20
- def configure(user)
25
+ def configure
21
26
  role :administrator, { is_admin: true } do
22
27
  can :destroy, String
28
+ can :foo, Integer
23
29
  end
24
30
 
25
31
  role :moderator, { is_moderator: true } do
26
32
  can :update, String
33
+ can :bar, String
27
34
  end
28
35
 
29
36
  role :member do
30
37
  can :read, String
38
+ can :zoom, Integer
39
+ can :boom, Hash
40
+ can :rub, Fixnum
31
41
  end
32
42
  end
33
43
  end
34
44
 
35
- class User < Struct.new(:is_admin, :is_moderator)
45
+ class User < Struct.new(:id, :is_admin, :is_moderator)
36
46
  end
@@ -3,9 +3,9 @@ require 'access-granted'
3
3
  require 'cancan'
4
4
  require_relative './config'
5
5
 
6
- admin = User.new(true, false)
7
- mod = User.new(false, true)
8
- user = User.new(false, false)
6
+ admin = User.new(1, true, false)
7
+ mod = User.new(2, false, true)
8
+ user = User.new(3, false, false)
9
9
 
10
10
  user_policy = AccessPolicy.new(user)
11
11
  admin_policy = AccessPolicy.new(admin)
@@ -16,18 +16,18 @@ admin_ability = Ability.new(admin)
16
16
  mod_ability = Ability.new(mod)
17
17
 
18
18
  Benchmark.ips do |x|
19
- x.config(time: 20, warmup: 2)
19
+ x.config(time: 5, warmup: 1)
20
20
 
21
21
  x.report("ag-admin") do
22
22
  admin_policy.can?(:read, String)
23
23
  end
24
24
 
25
25
  x.report("ag-moderator") do
26
- mod_policy.can?(:read, String)
26
+ mod_policy.can?(:bar, String)
27
27
  end
28
28
 
29
29
  x.report("ag-user") do
30
- user_policy.can?(:read, String)
30
+ user_policy.can?(:zoom, Integer)
31
31
  end
32
32
 
33
33
  x.report("cancan-admin") do
@@ -35,11 +35,10 @@ Benchmark.ips do |x|
35
35
  end
36
36
 
37
37
  x.report("cancan-moderator") do
38
- mod_ability.can?(:read, String)
38
+ mod_ability.can?(:bar, String)
39
39
  end
40
40
 
41
41
  x.report("cancan-user") do
42
- user_ability.can?(:read, String)
42
+ user_ability.can?(:zoom, Integer)
43
43
  end
44
-
45
44
  end
@@ -2,7 +2,7 @@ require "access-granted/exceptions"
2
2
  require "access-granted/policy"
3
3
  require "access-granted/permission"
4
4
  require "access-granted/role"
5
- require 'access-granted/rails/controller_methods'
5
+ require "access-granted/rails/controller_methods"
6
6
 
7
7
  module AccessGranted
8
8
 
@@ -1,10 +1,12 @@
1
1
  module AccessGranted
2
2
  module Policy
3
- attr_accessor :roles
3
+ attr_accessor :roles, :cache
4
+ attr_reader :user
4
5
 
5
- def initialize(user)
6
+ def initialize(user, cache_enabled = true)
6
7
  @user = user
7
8
  @roles = []
9
+ @cache = {}
8
10
  configure
9
11
  end
10
12
 
@@ -17,20 +19,25 @@ module AccessGranted
17
19
  raise DuplicateRole, "Role '#{name}' already defined"
18
20
  end
19
21
  r = if conditions_or_klass.is_a?(Class) && conditions_or_klass <= AccessGranted::Role
20
- conditions_or_klass.new(name, conditions, @user, block)
22
+ conditions_or_klass.new(name, conditions, user, block)
21
23
  else
22
- Role.new(name, conditions_or_klass, @user, block)
24
+ Role.new(name, conditions_or_klass, user, block)
23
25
  end
24
26
  roles << r
25
27
  r
26
28
  end
27
29
 
28
30
  def can?(action, subject = nil)
29
- roles.each do |role|
30
- next unless role.applies_to?(@user)
31
+ cache[action] ||= {}
32
+ cache[action][subject] ||= check_permission(action, subject)
33
+ end
34
+
35
+ def check_permission(action, subject)
36
+ applicable_roles.each do |role|
31
37
  permission = role.find_permission(action, subject)
32
38
  return permission.granted if permission
33
39
  end
40
+
34
41
  false
35
42
  end
36
43
 
@@ -44,5 +51,13 @@ module AccessGranted
44
51
  end
45
52
  subject
46
53
  end
54
+
55
+ private
56
+
57
+ def applicable_roles
58
+ @applicable_roles ||= roles.select do |role|
59
+ role.applies_to?(user)
60
+ end
61
+ end
47
62
  end
48
63
  end
@@ -8,7 +8,7 @@ module AccessGranted
8
8
  @conditions = conditions
9
9
  @block = block
10
10
  @permissions = []
11
- @permissions_by_action = {}
11
+
12
12
  if @block
13
13
  instance_eval(&@block)
14
14
  else
@@ -28,8 +28,10 @@ module AccessGranted
28
28
  end
29
29
 
30
30
  def find_permission(action, subject)
31
- permissions_by_action(action).detect do |permission|
32
- permission.matches_subject?(subject) && permission.matches_conditions?(subject)
31
+ permissions.detect do |permission|
32
+ permission.action == action &&
33
+ permission.matches_subject?(subject) &&
34
+ permission.matches_conditions?(subject)
33
35
  end
34
36
  end
35
37
 
@@ -52,17 +54,15 @@ module AccessGranted
52
54
 
53
55
  def add_permission(granted, action, subject, conditions, block)
54
56
  prepare_actions(action).each do |a|
55
- raise DuplicatePermission if permission_exists?(a, subject)
56
- @permissions << Permission.new(granted, a, subject, @user, conditions, block)
57
- @permissions_by_action[a] ||= []
58
- @permissions_by_action[a] << @permissions.size - 1
57
+ raise DuplicatePermission if find_permission(a, subject)
58
+ permissions << Permission.new(granted, a, subject, @user, conditions, block)
59
59
  end
60
60
  end
61
61
 
62
62
  private
63
63
 
64
64
  def permission_exists?(action, subject)
65
- permissions_by_action(action).any? do |permission|
65
+ permissions.any? do |permission|
66
66
  permission.matches_subject?(subject)
67
67
  end
68
68
  end
@@ -74,11 +74,5 @@ module AccessGranted
74
74
  actions = Array(*[action])
75
75
  end
76
76
  end
77
-
78
- def permissions_by_action(action)
79
- (@permissions_by_action[action] || []).map do |index|
80
- @permissions[index]
81
- end
82
- end
83
77
  end
84
78
  end
@@ -6,16 +6,16 @@ describe AccessGranted::Policy do
6
6
 
7
7
  describe "#configure" do
8
8
  before :each do
9
- @member = double("member", id: 1, is_moderator: false, is_admin: false, is_banned: false)
10
- @mod = double("moderator", id: 2, is_moderator: true, is_admin: false, is_banned: false)
11
- @admin = double("administrator", id: 3, is_moderator: false, is_admin: true, is_banned: false)
12
- @banned = double("banned", id: 4, is_moderator: false, is_admin: true, is_banned: true)
9
+ @member = FakeUser.new(1, false, false, false)
10
+ @mod = FakeUser.new(2, true, false, false)
11
+ @admin = FakeUser.new(3, false, true, false)
12
+ @banned = FakeUser.new(4, false, true, true)
13
13
  end
14
14
 
15
15
  it "passes user object to permission block" do
16
16
  post_owner = double(id: 123)
17
17
  other_user = double(id: 5)
18
- post = FakePost.new(post_owner.id)
18
+ post = FakePost.new(1, post_owner.id)
19
19
 
20
20
  klass = Class.new do
21
21
  include AccessGranted::Policy
@@ -63,8 +63,8 @@ describe AccessGranted::Policy do
63
63
 
64
64
  context "when multiple roles define the same permission" do
65
65
  it "checks all roles until conditions are met" do
66
- user_post = FakePost.new(@member.id)
67
- other_post = FakePost.new(66)
66
+ user_post = FakePost.new(1, @member.id)
67
+ other_post = FakePost.new(2, 66)
68
68
 
69
69
  klass = Class.new do
70
70
  include AccessGranted::Policy
@@ -120,7 +120,7 @@ describe AccessGranted::Policy do
120
120
  end
121
121
  end
122
122
  expect(klass.new(@member).can?(:create, String)).to eq(true)
123
- expect(klass.new(@banned).cannot?(:create, String)).to eq(true)
123
+ expect(klass.new(@banned).can?(:create, String)).to eq(false)
124
124
  end
125
125
  end
126
126
 
@@ -23,10 +23,12 @@ end
23
23
 
24
24
  require 'access-granted'
25
25
 
26
- class FakePost < Struct.new(:user_id)
26
+ class FakeUser < Struct.new(:id, :is_moderator, :is_admin, :is_banned)
27
+ end
28
+
29
+ class FakePost < Struct.new(:id, :user_id)
27
30
  end
28
31
 
29
32
  class AccessPolicy
30
33
  include AccessGranted::Policy
31
34
  end
32
-
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.0.4
4
+ version: 1.1.0
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: 2015-11-26 00:00:00.000000000 Z
11
+ date: 2016-07-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -89,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
89
  version: '0'
90
90
  requirements: []
91
91
  rubyforge_project:
92
- rubygems_version: 2.4.5
92
+ rubygems_version: 2.4.5.1
93
93
  signing_key:
94
94
  specification_version: 4
95
95
  summary: Elegant whitelist and role based authorization with ability to prioritize
@@ -100,4 +100,3 @@ test_files:
100
100
  - spec/policy_spec.rb
101
101
  - spec/role_spec.rb
102
102
  - spec/spec_helper.rb
103
- has_rdoc: