accessly 0.0.1 → 1.0.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: 60f014b72282ae3ebb27bac60f28bea7c596b1b9
4
- data.tar.gz: 2ef2178dd5a914519839fad848c5aa6526c6a1ab
3
+ metadata.gz: 9abbf504e2ae963dde890f7175961694b521462b
4
+ data.tar.gz: a470d71b034a205dc6479c2e015f47117fd097f9
5
5
  SHA512:
6
- metadata.gz: 33ea523a967256119180e2b4a49557c5951d31db882d8c46fa823613d8226ad8896c5f44cb6f27cc767145208ada43e740086caadc853d35fdda00e9f23c0e39
7
- data.tar.gz: 01f0e9af0c17a8159e0ccbc8d8c34f5ac48e267dde25e819dbfae52eb132832639feedcb2bb92b6c47ba6a107780a4121b15050a67640d6211167c8fd348ae52
6
+ metadata.gz: 93b7e81d5ec169e79882348b900c48a4906e7b5006891cf3a42df334261ce9be085b604a6393dd8e23ce4493489a0c79a8f5766bf96dd363b234a0eec9924874
7
+ data.tar.gz: e728bcecd705c55b36c21cd325855fabdd7a19e513eb6bf2a808bd2d8889eb97337c2e120e556cf2625341ff2d5a6720a903585e977fd58a5cf5def062c7bbe4
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- accessly (0.0.1)
4
+ accessly (1.0.0)
5
5
  activerecord (~> 5.0)
6
6
 
7
7
  GEM
@@ -115,11 +115,11 @@ PLATFORMS
115
115
  DEPENDENCIES
116
116
  accessly!
117
117
  bundler (~> 1.16)
118
- database_cleaner
118
+ database_cleaner (~> 1.5)
119
119
  minitest (~> 5.0)
120
120
  rails (~> 5.0)
121
121
  rake (~> 10.0)
122
- sqlite3
122
+ sqlite3 (~> 1.3)
123
123
 
124
124
  RUBY VERSION
125
125
  ruby 2.4.2p198
data/README.md CHANGED
@@ -1,8 +1,27 @@
1
+ <img width="268" src="https://raw.githubusercontent.com/lessonly/accessly/master/logo/logo.png" alt="Accessly Logo">
2
+
1
3
  # Accessly
2
4
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/accessly`. To experiment with that code, run `bin/console` for an interactive prompt.
5
+ Accessly exists out of our need to answer the following questions:
6
+
7
+ 1. What can a user do or see?
8
+ 2. Can a user do an arbitrary action on another object?
9
+
10
+ We were not satisfied with the available resources to answer the questions so we created Accessly!
11
+
12
+ Accessly is our opinion of access control that can broadly grant permissions to 'actors' (modeled as `users`, `groups`, `organizations`, etc)
13
+
14
+ ```
15
+ Can actor1 view the content resource (/content)?
16
+ ```
17
+
18
+ Our `actors` can also have permissions on other models in our application
19
+
20
+ ```
21
+ Can actor1 edit a Post with id 1?
22
+ ```
4
23
 
5
- TODO: Delete this and the text above, and describe your gem
24
+ If you have a similar need to implement a permission scheme in your Rails app please continue reading!
6
25
 
7
26
  ## Installation
8
27
 
@@ -20,15 +39,280 @@ Or install it yourself as:
20
39
 
21
40
  $ gem install accessly
22
41
 
42
+ Add the ActiveRecord Migrations:
43
+
44
+ $ rails g accessly:install
45
+
23
46
  ## Usage
24
47
 
25
- TODO: Write usage instructions here
48
+ You can use the Accessly gem directly to grant | revoke | check permissions. We recommend the use of 'Policies' covered in this README.
49
+ Checkout our [API docs](http://www.rubydoc.info/gems/accessly) for more info on using the API directly
50
+
51
+ We use Accessly with policies in mind to capture everything we want to know about a specific permission set. Let's take a look at some examples:
52
+
53
+ ### Basic Action Policy
54
+
55
+ ```ruby
56
+ class ApplicationFeaturePolicy < Accessly::Policy::Base
57
+
58
+ actions(
59
+ view_dashboard: 1,
60
+ view_super_secret_page: 2,
61
+ view_double_secret_probation_page: 3
62
+ )
63
+
64
+ end
65
+ ```
66
+
67
+ With this policy we can `grant` permissions to a user
68
+
69
+ ```ruby
70
+ ApplicationFeaturePolicy.new(user).grant!(:view_super_secret_page)
71
+ ```
72
+
73
+ In our `SuperSecretPageController`, we can check whether the user has permission to view that page with
74
+
75
+ ```ruby
76
+ ApplicationFeaturePolicy.new(user).view_super_secret_page?
77
+ # or
78
+ ApplicationFeaturePolicy.new(user).can?(:view_super_secret_page)
79
+ ```
80
+
81
+ At any point in time we can revoke permissions with
82
+
83
+ ```ruby
84
+ ApplicationFeaturePolicy.new(user).revoke!(:view_super_secret_page)
85
+ ```
86
+
87
+ ### Basic Action on Object Policy
88
+
89
+ We can grant permissions to `actors` on other `objects` in our application with a policy like:
90
+
91
+ ```ruby
92
+ class UserPolicy < Accessly::Policy::Base
93
+
94
+ actions_on_objects(
95
+ view: 1,
96
+ edit: 2,
97
+ destroy: 3
98
+ )
99
+
100
+ def self.namespace
101
+ User.name
102
+ end
103
+
104
+ def self.model_scope
105
+ User.all
106
+ end
107
+ end
108
+ ```
109
+
110
+ We differentiate permissions by a `namespace` which by default is the name of your policy class. However,
111
+ it may be necessary to override the default behavior represented in the above example.
112
+
113
+ Accessly can return a relation of ids on an object for a given actor's permission grants. `Accessly::Policy::Base` requires
114
+ that you implement `self.model_scope` with an `ActiveRecord` scope so the `list` api can return an `ActiveRecord::Relation`
115
+
116
+ With this policy we can `grant` permissions for a user to do an action on another user object.
117
+
118
+ ```ruby
119
+ UserPolicy.new(user).grant!(:edit, other_user)
120
+ ```
121
+
122
+ In our `EditUserController`, we can check permissions
123
+
124
+ ```ruby
125
+ UserPolicy.new(user).edit?(other_user)
126
+ # or
127
+ UserPolicy.new(user).can?(:edit, other_user)
128
+ ```
129
+
130
+ We can list all of the users available to edit with
131
+
132
+ ```ruby
133
+ UserPolicy.new(user).edit
134
+ # or
135
+ UserPolicy.new(user).list(:edit)
136
+ ```
137
+
138
+ At any point in time we can revoke permissions with
139
+
140
+ ```ruby
141
+ UserPolicy.new(user).revoke!(:edit, other_user)
142
+ ```
143
+
144
+ ### Intermediate Action Policy
145
+
146
+ Let's look at a policy with a combined configuration and more customization
147
+
148
+ ```ruby
149
+ class UserPolicy < Accessly::Policy::Base
150
+
151
+ actions(
152
+ view: 1,
153
+ edit_basic_info: 2,
154
+ change_role: 3,
155
+ email: 4
156
+ )
157
+
158
+ actions_on_objects(
159
+ view: 1,
160
+ edit: 2,
161
+ destroy: 3,
162
+ )
163
+
164
+ def self.namespace
165
+ User.name
166
+ end
167
+
168
+ def self.model_scope
169
+ User.all
170
+ end
171
+
172
+ def segment_id
173
+ actor.organization_id
174
+ end
175
+
176
+ def unrestricted?
177
+ actor.admin?
178
+ end
179
+ end
180
+ ```
181
+
182
+ This policy combines `actions` and `actions_on_objects`, introduces Accessly's support for `segment_id`, and overrides `unrestricted?`
183
+
184
+ #### combined actions and actions_on_objects
185
+
186
+ Accessly policies can extend support for combined use of `actions` and `actions_on_objects.` You may want to broadly grant `edit_basic_info` permissions to some users. The same policy can support a limited scope of permissions where the `actor` and `object` must be defined.
187
+
188
+ #### segment_id
189
+
190
+ `segment_id` allows you to scope permission grants to a specific object id that you define. In our example the `actor` belongs to an Organization model, and we set the organization_id on each permission granted for any actor using the policy.
191
+
192
+ It provides additional efficiency on query execution, and we can broadly remove permissions if the organization is no longer in the application.
193
+
194
+ #### unrestricted?
195
+
196
+ Accessly uses `unrestricted?` to bypass permission checks. This policy shows that the actor has an `admin` designation which we do not want to model in permissions. The business logic implemented here would bypass any permission check if `unrestricted?` returns `true`. When `unrestricted?` returns `true`, then `can?` and the other permission check methods (like `edit_basic_info?` in this example) automatically return `true`, and `list` and the other list methods (like `edit` in this example) returns the `ActiveRecord::Relation` given by `self.model_scope`
197
+
198
+ ### Advanced Action Policy
199
+
200
+ Let's look at a policy that overrides `action?` and `list` APIs
201
+
202
+ ```ruby
203
+ class UserPolicy < Accessly::Policy::Base
204
+
205
+ actions(
206
+ view: 1,
207
+ edit_basic_info: 2,
208
+ change_role: 3,
209
+ email: 4
210
+ )
211
+
212
+ actions_on_objects(
213
+ view: 1,
214
+ edit: 2,
215
+ destroy: 3
216
+ )
217
+
218
+ def self.namespace
219
+ User.name
220
+ end
221
+
222
+ def self.model_scope
223
+ User.all
224
+ end
225
+
226
+ # Override the destroy permission check for an "Action on Object"
227
+ def destroy?(object)
228
+ if actor.name == "Alice"
229
+ true
230
+ else
231
+ super
232
+ end
233
+ end
234
+
235
+ # Override the view permission check for both Action only and "Action on Object"
236
+ def view?(object = nil)
237
+ if object.nil?
238
+ if actor.name == "Bob"
239
+ false
240
+ else
241
+ super
242
+ end
243
+ elsif actor.name == "Alice" && object.name == "Bob"
244
+ true
245
+ else
246
+ super
247
+ end
248
+ end
249
+
250
+ # Override the change_role check for Action only
251
+ def change_role?
252
+ false
253
+ end
254
+
255
+ # Override the list method for view permissions
256
+ def view
257
+ if actor.name == "Alice"
258
+ User.all
259
+ else
260
+ super
261
+ end
262
+ end
263
+ end
264
+ ```
265
+ #### Overriding defaults
266
+
267
+ Here we provide some examples of the `Accessly::Policy::Base` overrides you can make in an application. You can override the function completely or fallback to the `Base` method. The implementation strategy is up to you!
268
+
269
+ Any call to the following functions will run the given example in the policy:
270
+
271
+ #### destroy?(object)
272
+
273
+ ```ruby
274
+ # Action on Object queries
275
+ UserPolicy.new(user).destroy?(other_user)
276
+ # or
277
+ UserPolicy.new(user).can?(:destroy, other_user)
278
+ ```
279
+
280
+ #### view?(object = nil)
281
+
282
+ ```ruby
283
+ # Action queries
284
+ UserPolicy.new(user).view?
285
+ # or
286
+ UserPolicy.new(user).can?(:view)
287
+
288
+ # Action on Object queries
289
+ UserPolicy.new(user).view?(other_user)
290
+ # or
291
+ UserPolicy.new(user).can?(:view, other_user)
292
+ ```
293
+
294
+ #### change_role?
295
+
296
+ ```ruby
297
+ # Action queries
298
+ UserPolicy.new(user).change_role?
299
+ # or
300
+ UserPolicy.new(user).can?(:change_role)
301
+ ```
302
+
303
+ #### view
304
+
305
+ ```ruby
306
+ # List queries
307
+ UserPolicy.new(user).view
308
+ # or
309
+ UserPolicy.new(user).list(:view)
310
+ ```
26
311
 
27
- ## Development
312
+ ## Caching
28
313
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
314
+ Accessly implements some internal caching to increase the performance of permission queries. If you use the same Policy object for the same lookup twice, then the second one will lookup based on the cached result. Be mindful of caching when using `revoke!` or `grant!` calls with subsequent permission queries on the same Policy object.
30
315
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
316
 
33
317
  ## Contributing
34
318
 
data/lib/accessly.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "accessly/version"
2
2
  require "accessly/query"
3
+ require "accessly/policy/base"
3
4
  require "accessly/permission/grant"
4
5
  require "accessly/permission/revoke"
5
6
  require "accessly/models/permitted_action"
@@ -38,6 +38,14 @@ module Accessly
38
38
  self.class.model_scope
39
39
  end
40
40
 
41
+ # Specifies all the actors used in permission lookups.
42
+ # Override this method in child policy classes to specify
43
+ # other actors that the actor given in the initializer may
44
+ # inherit permissions from.
45
+ def actors
46
+ actor
47
+ end
48
+
41
49
  def unrestricted?
42
50
  false
43
51
  end
@@ -46,33 +54,33 @@ module Accessly
46
54
  nil
47
55
  end
48
56
 
49
- def grant(action, object = nil)
50
- object_id = _get_object_id(object)
51
-
52
- action_id = if object_id.nil?
53
- _get_general_action_id!(action)
57
+ def can?(action, object = nil)
58
+ if object.nil?
59
+ send("#{action}?")
54
60
  else
55
- _get_action_on_object_id!(action)
61
+ send("#{action}?", object)
56
62
  end
63
+ end
57
64
 
58
- grant_object.grant!(action_id, namespace, object_id)
65
+ def list(action)
66
+ send(action)
59
67
  end
60
68
 
61
- def revoke(action, object = nil)
69
+ def grant!(action, object = nil)
62
70
  object_id = _get_object_id(object)
71
+ action_id = _get_action_id(action, object_id)
72
+ grant_object.grant!(action_id, namespace, object_id)
73
+ end
63
74
 
64
- action_id = if object_id.nil?
65
- _get_general_action_id!(action)
66
- else
67
- _get_action_on_object_id!(action)
68
- end
69
-
75
+ def revoke!(action, object = nil)
76
+ object_id = _get_object_id(object)
77
+ action_id = _get_action_id(action, object_id)
70
78
  revoke_object.revoke!(action_id, namespace, object_id)
71
79
  end
72
80
 
73
81
  def accessly_query
74
82
  @_accessly_query ||= begin
75
- query = Accessly::Query.new(actor)
83
+ query = Accessly::Query.new(actors)
76
84
  query.on_segment(segment_id) unless segment_id.nil?
77
85
  query
78
86
  end
@@ -94,6 +102,14 @@ module Accessly
94
102
 
95
103
  private
96
104
 
105
+ def _get_action_id(action, object_id = nil)
106
+ if object_id.nil?
107
+ _get_general_action_id!(action)
108
+ else
109
+ _get_action_on_object_id!(action)
110
+ end
111
+ end
112
+
97
113
  # Determines whether the caller is trying to call an action method
98
114
  # in the format `action_name?`. If so, this calls that method with
99
115
  # the given arguments.
@@ -1,3 +1,3 @@
1
1
  module Accessly
2
- VERSION = "0.0.1"
2
+ VERSION = "1.0.0"
3
3
  end
data/logo/logo.png ADDED
Binary file
data/logo/logo.svg ADDED
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0"?>
2
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 268 60">
3
+ <g fill="none" fill-rule="evenodd">
4
+ <path fill="#292933" d="M62 59c-10.6 0-19-7.5-19-18 0-11.5 8.4-19 19-19 9 0 13.6 4.9 16 10l-8 3a9.3 9.3 0 0 0-8-5c-5.5.2-9.6 4.7-10 11 .4 5.3 4.5 9.8 10 10a9.3 9.3 0 0 0 8-5l8 3c-2.3 5-7 10-16 10zm37 0c-10.6 0-19-7.5-19-18 0-11.5 8.4-19 19-19 9 0 13.6 4.9 16 10l-8 3a9.3 9.3 0 0 0-8-5c-5.5.2-9.6 4.7-10 11 .4 5.3 4.5 9.8 10 10a9.3 9.3 0 0 0 8-5l8 3c-2.3 5-7 10-16 10zm45 0h-26V23h26v8h-17v6h17v8h-17v6h17v8zm18 0c-6.6 0-11.5-2.3-15-6l5-7c2.3 3 6.1 5.1 11 5 3 .1 4.9-1.1 5-3-.1-4.7-19.9-.8-20-15 .1-5.6 5-11 14-11a20 20 0 0 1 14 5l-5 7a13.2 13.2 0 0 0-10-4c-2.6-.1-3.7 1-4 2 .3 5 20 1.7 20 15 0 7.2-5.2 12-15 12zm32 0c-6.6 0-11.5-2.3-15-6l5-7c2.3 3 6.1 5.1 11 5 3 .1 4.9-1.1 5-3-.1-4.7-19.9-.8-20-15 .1-5.6 5-11 14-11a20 20 0 0 1 14 5l-5 7a13.2 13.2 0 0 0-10-4c-2.6-.1-3.7 1-4 2 .3 5 20 1.7 20 15 0 7.2-5.2 12-15 12zm43-1h-23V23h9v27h14v8zm16 0h-8V44.4L231 23h10l8 13.6 8-13.6h10l-14 21.4V58z"/>
5
+ <path fill="#F6BF35" d="M20 60a19 19 0 1 1 0-38 19 19 0 0 1 0 38zm-5-23.3c0 1.7 1.1 3.5 3 4.8l-1.5 7a2 2 0 0 0 2 2.5h4a2 2 0 0 0 2-2.4L23 41.5c1.9-1.3 3-3 3-4.8 0-3.3-2.5-5.7-5.5-5.7S15 33.4 15 36.7z"/>
6
+ <path stroke="#F6BF35" stroke-linecap="round" stroke-width="8" d="M9 16.8C9 10.3 14.1 5 20.5 5S32 10.3 32 16.8V43"/>
7
+ </g>
8
+ </svg>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: accessly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Milam
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2018-03-28 00:00:00.000000000 Z
13
+ date: 2018-04-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -149,6 +149,8 @@ files:
149
149
  - lib/generators/accessly/install/install_generator.rb
150
150
  - lib/generators/accessly/install/templates/db/migrate/create_permitted_action_on_objects.rb
151
151
  - lib/generators/accessly/install/templates/db/migrate/create_permitted_actions.rb
152
+ - logo/logo.png
153
+ - logo/logo.svg
152
154
  homepage: https://github.com/lessonly/accessly
153
155
  licenses:
154
156
  - MIT