kant 0.0.3 → 0.0.4
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 +4 -4
- data/README.md +1 -1
- data/lib/kant/policy_access.rb +2 -2
- data/lib/kant/version.rb +1 -1
- data/spec/kant/policy_access_spec.rb +90 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4405afa9ea78fb2fcee60f09c4f31521626ae1b1
|
4
|
+
data.tar.gz: b337033ed3ecc3119602b260a0c9aff1c14013ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6fed5522437eab76c2d21f814ebbeeaa472acdc11605ae95ab109e756a3d31d1f52330d63577d0baa6506ca3b08a8da853458a39d9e20d555c1fe6ef2e850b31
|
7
|
+
data.tar.gz: 1440cbd44f47d426e31832e65eaa21a7cbf439042ea17328808273b9a5bc97fed20a46b4a6e9425195541ec1e0c549a3173ed2c52c5293f2e2484ccf4b05a4b8
|
data/README.md
CHANGED
data/lib/kant/policy_access.rb
CHANGED
@@ -37,12 +37,12 @@ module Kant
|
|
37
37
|
#
|
38
38
|
# ability.accessible(:read, Content)
|
39
39
|
# # => a Content scope
|
40
|
-
def accessible(action, scope)
|
40
|
+
def accessible(action, scope, *rest)
|
41
41
|
abilities = resolve_scope(scope)
|
42
42
|
_scope_method = scope_method(abilities, action)
|
43
43
|
|
44
44
|
if _scope_method
|
45
|
-
abilities.send(_scope_method, scope, user)
|
45
|
+
abilities.send(_scope_method, scope, user, *rest)
|
46
46
|
else
|
47
47
|
scope.none
|
48
48
|
end
|
data/lib/kant/version.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'kant/policy_access'
|
3
|
+
require 'kant/access_denied'
|
3
4
|
|
4
5
|
describe Kant::PolicyAccess do
|
5
6
|
setup_models
|
@@ -99,6 +100,7 @@ describe Kant::PolicyAccess do
|
|
99
100
|
|
100
101
|
describe "#accessible" do
|
101
102
|
let(:user) { User.create!(email: 'foo@bar.com') }
|
103
|
+
let(:another_user) { User.create!(email: 'foo@baz.com') }
|
102
104
|
subject(:policy_access) { Kant::PolicyAccess.new(user) }
|
103
105
|
|
104
106
|
it "delegates to a Policy module" do
|
@@ -130,6 +132,94 @@ describe Kant::PolicyAccess do
|
|
130
132
|
|
131
133
|
expect(policy_access.accessible(:read, Todo)).to eq(Todo.none)
|
132
134
|
end
|
135
|
+
|
136
|
+
# The motivation here is that, when performing authorization for "index"
|
137
|
+
# actions, this often involves some context. For example, the user might be
|
138
|
+
# listing all the todos under a particular project (GET /projects/1/todos).
|
139
|
+
# In a lot of cases it might be possible to define an ActiveRecord or SQL
|
140
|
+
# query that describes what records the user can access regardless of
|
141
|
+
# context, but in some cases this might result in a very complicated query.
|
142
|
+
# (Unfortunately, the example below really doesn't really do the motivation
|
143
|
+
# justice.)
|
144
|
+
#
|
145
|
+
# Imagine instead that a user can normally only access their own User
|
146
|
+
# record (the check being `user == me`). However, when the user is part of
|
147
|
+
# a project they can also see other users in the company that the project
|
148
|
+
# is a part of. (For example, so that they can send a DM to a user.)
|
149
|
+
#
|
150
|
+
# - Company
|
151
|
+
# - has many Projects
|
152
|
+
# - has many Users
|
153
|
+
#
|
154
|
+
# Imagine what the query would look like to return all the users I can
|
155
|
+
# message...
|
156
|
+
#
|
157
|
+
# def self.readable_for_messaging(users, me)
|
158
|
+
# my_companies = Company.joins(:projects).merge(me.projects)
|
159
|
+
# my_companies_projects = Project.joins(:company).merge(my_companies)
|
160
|
+
# my_companies_users = User.joins(:projects).merge(my_companies_projects)
|
161
|
+
# users.merge(my_companies_users)
|
162
|
+
# end
|
163
|
+
#
|
164
|
+
# Some notes here:
|
165
|
+
#
|
166
|
+
# 1. This honestly isn't such a gross example, in production this logic can
|
167
|
+
# get much, much worse.
|
168
|
+
# 2. Even so, you can see it gets pretty complicated.
|
169
|
+
# 3. In practice what we have to do after all this is query the resulting
|
170
|
+
# scope for users in a particular company... which means more gross queries.
|
171
|
+
#
|
172
|
+
# Instead, by having extra params in the *able function, we can specify a
|
173
|
+
# context (typically this will probably match the shape of the endpoint, so
|
174
|
+
# if your endpoint is /companies/1/users then your *able function will
|
175
|
+
# probably take an extra `companies:` param).
|
176
|
+
#
|
177
|
+
# def self.readable_for_messaging(users, me, company:)
|
178
|
+
# my_companies = Company.joins(:projects).merge(me.projects)
|
179
|
+
# if !my_companies.where(id: company.id).any?
|
180
|
+
# fail Kant::AccessDenied, "no access"
|
181
|
+
# end
|
182
|
+
#
|
183
|
+
# users.joins(:projects).where(projects: { company_id: company.id })
|
184
|
+
# end
|
185
|
+
it "passes along other params to the *able method" do
|
186
|
+
# module TodoPolicy
|
187
|
+
# def self.readable_as_owner(todos, user, project:)
|
188
|
+
# if project.owner_id == user.id
|
189
|
+
# todos.where(project_id: project_id, completed: true)
|
190
|
+
# else
|
191
|
+
# fail Kant::AccessDenied, "user does not have access to this project"
|
192
|
+
# end
|
193
|
+
# end
|
194
|
+
# end
|
195
|
+
todo_policy = Class.new do
|
196
|
+
define_singleton_method(:readable_as_owner) do |todos, user, project:|
|
197
|
+
if project.owner_id == user.id
|
198
|
+
todos.where(project_id: project.id, completed: true)
|
199
|
+
else
|
200
|
+
fail Kant::AccessDenied, "user does not have access to this project"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
stub_const("TodoPolicy", todo_policy)
|
206
|
+
|
207
|
+
my_project = Project.create!(owner: user)
|
208
|
+
another_project = Project.create!(owner: another_user)
|
209
|
+
my_complete_todo = Todo.create!(project: my_project, completed: true)
|
210
|
+
my_incomplete_todo = Todo.create!(project: my_project, completed: false)
|
211
|
+
another_complete_todo = Todo.create!(project: another_project, completed: true)
|
212
|
+
|
213
|
+
expect{
|
214
|
+
policy_access.accessible(:read_as_owner, Todo)
|
215
|
+
}.to raise_error(ArgumentError)
|
216
|
+
|
217
|
+
expect(policy_access.accessible(:read_as_owner, Todo, project: my_project)).to eq([my_complete_todo])
|
218
|
+
|
219
|
+
expect{
|
220
|
+
policy_access.accessible(:read_as_owner, Todo, project: another_project)
|
221
|
+
}.to raise_error(Kant::AccessDenied)
|
222
|
+
end
|
133
223
|
end
|
134
224
|
|
135
225
|
describe "the policies_module option in initializer" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Przepiora
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-05-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|