you_shall_not_pass 0.0.3 → 0.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.
- checksums.yaml +4 -4
- data/.travis.yml +5 -0
- data/README.md +157 -5
- data/Rakefile +2 -0
- data/lib/you_shall_not_pass/authorizator.rb +33 -9
- data/lib/you_shall_not_pass/policy.rb +0 -4
- data/lib/you_shall_not_pass/version.rb +1 -1
- data/spec/you_shall_not_pass/authorizator_spec.rb +71 -44
- data/you_shall_not_pass.gemspec +3 -3
- metadata +9 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f7b94fd90514299c816fa9f4f55a4f50fe7fadb
|
4
|
+
data.tar.gz: 2c20e0ded03fde67292cb5fbed5a11a7ffa959b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7e767bf6c234ab62c6ad73c94d8ce37e90fc25bdf5c2948d6126bc715351a3367164f1fe490ed28c9ccd17e884a86629077f6d81c530b884ccf205c6d6c03d8
|
7
|
+
data.tar.gz: 8512dcc4f5df0b615619ebc360387d400a56ce7db016d1598defaedef9defe7902b1ccaff82a9f62f006e22182383929c3c9e0e01512a36d914ac73bfcdf7aac
|
data/README.md
CHANGED
@@ -1,13 +1,168 @@
|
|
1
1
|
# YouShallNotPass
|
2
|
+
[](https://travis-ci.org/iachettifederico/you_shall_not_pass)
|
3
|
+
|
4
|
+
|
5
|
+
Simple framework-agnostic authorization library.
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
You Shall Not Pass is a very minimalistic authorization framework. It doesn't really care about your system architecture.
|
10
|
+
You don't really need to set up your authentication schema in any particular way (in fact, you don't even really need authentication to make You Shall Not Pass work).
|
11
|
+
|
12
|
+
The first you need is to create an Authorizator. You can define one by extending `YouShallNotPass::Authorizator` and then define the authorization policies.
|
13
|
+
|
14
|
+
Let's write a first example:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
class MyAuthorizator < YouShallNotPass::Authorizator
|
18
|
+
def time_policies
|
19
|
+
{
|
20
|
+
morning_shift: Time.now.hour <= 12,
|
21
|
+
afternoon_shift: Time.now.hour > 12 && Time.now.hour < 20,
|
22
|
+
night_shift: Time.now.hour >= 20,
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
As you can see, there's no user, role, or any kind of authentication going on in this example.
|
29
|
+
|
30
|
+
Now we can ask if we have permission to `<insert action here>` depending on the shift:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
Time.now
|
34
|
+
# => 2015-02-09 20:17:26 -0300
|
35
|
+
|
36
|
+
Time.now.hour
|
37
|
+
# => 20
|
38
|
+
|
39
|
+
auth = MyAuthorizator.new
|
40
|
+
auth.can?(:morning_shift)
|
41
|
+
# => false
|
42
|
+
|
43
|
+
auth.can?(:afternoon_shift)
|
44
|
+
# => false
|
45
|
+
|
46
|
+
auth.can?(:night_shift)
|
47
|
+
# => true
|
48
|
+
```
|
49
|
+
|
50
|
+
In this case, the code was run at 20:17, so it corresponds to the night shift.
|
51
|
+
|
52
|
+
We can also perform an action depending on the shift. So let's get some output:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
auth.perform_for(:morning_shift) do
|
56
|
+
puts "Morning shift"
|
57
|
+
end
|
58
|
+
|
59
|
+
auth.perform_for(:afternoon_shift) do
|
60
|
+
puts "Afternoon shift"
|
61
|
+
end
|
62
|
+
|
63
|
+
auth.perform_for(:night_shift) do
|
64
|
+
puts "Night shift"
|
65
|
+
end
|
66
|
+
|
67
|
+
# >> Night shift
|
68
|
+
```
|
69
|
+
|
70
|
+
And as we are on the night shift, the only block that get's called is the one that performs for the night shift.
|
71
|
+
|
72
|
+
## *_policies
|
73
|
+
|
74
|
+
You Shall Not Pass allows you to group your permissions by providing instance methods suffixed with `_policies`.
|
75
|
+
|
76
|
+
So, if we have:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
class MyAuthorizator < YouShallNotPass::Authorizator
|
80
|
+
def time_policies
|
81
|
+
{
|
82
|
+
morning_shift: Time.now.hour <= 12,
|
83
|
+
afternoon_shift: Time.now.hour > 12 && Time.now.hour < 20,
|
84
|
+
night_shift: Time.now.hour >= 20,
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
def date_policies
|
89
|
+
{
|
90
|
+
jan: Time.now.month == 1,
|
91
|
+
feb: Time.now.month == 2,
|
92
|
+
}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
```
|
96
|
+
|
97
|
+
The available policy names are:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
auth = MyAuthorizator.new
|
101
|
+
auth.policies.keys
|
102
|
+
# => [:morning_shift, :afternoon_shift, :night_shift, :jan, :feb]
|
103
|
+
```
|
104
|
+
|
105
|
+
## Attributes
|
106
|
+
|
107
|
+
If you have an authorization schema in place, you can make You Shall Not Pass aware of it by introducing attributes.
|
108
|
+
|
109
|
+
Let's say we have a `User` object and we want our authenticator to use it. We can extend `MyAuthorizator` to accept a `user` attribute and use it on the policies.
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
class User
|
113
|
+
def initialize(admin)
|
114
|
+
@admin = admin
|
115
|
+
end
|
116
|
+
|
117
|
+
def admin?
|
118
|
+
@admin
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class MyAuthorizator < YouShallNotPass::Authorizator
|
123
|
+
attribute :user
|
124
|
+
|
125
|
+
def user_policies
|
126
|
+
{
|
127
|
+
admin: user.admin?
|
128
|
+
}
|
129
|
+
end
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
Now we need to instantiate an authorizator passing the user as a (named) parameter:
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
auth1 = MyAuthorizator.new(user: User.new(true))
|
137
|
+
auth1.perform_for(:admin) do
|
138
|
+
puts "First user"
|
139
|
+
end
|
140
|
+
|
141
|
+
auth2 = MyAuthorizator.new(user: User.new(false))
|
142
|
+
auth2.perform_for(:admin) do
|
143
|
+
puts "Second user"
|
144
|
+
end
|
145
|
+
|
146
|
+
# >> First user
|
147
|
+
```
|
148
|
+
|
149
|
+
And only the first one will print.
|
150
|
+
|
151
|
+
You can add as many attributes as you want and you will get an instance method to use it at will.
|
152
|
+
|
153
|
+
It's important to notice that the `user` in this case is just a Ruby object. You don't need to comply with any API in particular.
|
154
|
+
|
155
|
+
In a web framework, is a good idea to create the authorizator after defining the current user and then pass the current user as a parameter.
|
156
|
+
|
157
|
+
If you need to authenticate using other objects, you can keep adding attributes to the authorizator (for example pass a settings array, the environment your on - dev, prod-, etc).
|
2
158
|
|
3
|
-
TODO: Write a gem description
|
4
159
|
|
5
160
|
## Installation
|
6
161
|
|
7
162
|
Add this line to your application's Gemfile:
|
8
163
|
|
9
164
|
```ruby
|
10
|
-
gem 'you_shall_not_pass'
|
165
|
+
gem 'you_shall_not_pass'
|
11
166
|
```
|
12
167
|
|
13
168
|
And then execute:
|
@@ -18,9 +173,6 @@ Or install it yourself as:
|
|
18
173
|
|
19
174
|
$ gem install you_shall_not_pass
|
20
175
|
|
21
|
-
## Usage
|
22
|
-
|
23
|
-
TODO: Write usage instructions here
|
24
176
|
|
25
177
|
## Contributing
|
26
178
|
|
data/Rakefile
CHANGED
@@ -8,23 +8,27 @@ module YouShallNotPass
|
|
8
8
|
send attr, value
|
9
9
|
end
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def can?(permission, **args)
|
13
13
|
Array(policies.fetch(permission)).all? do |policy|
|
14
14
|
Callable(policy).call(**args) == true
|
15
15
|
end
|
16
|
-
rescue KeyError =>
|
17
|
-
|
16
|
+
rescue KeyError => exception
|
17
|
+
break_down_can(permission, exception, **args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def break_down_can(permission, exception, **args)
|
21
|
+
case permission
|
22
|
+
when /_and_/
|
18
23
|
permission.to_s.split("_and_").all? { |policy| can?(policy.to_sym, **args)}
|
19
|
-
|
24
|
+
when /_or_/
|
20
25
|
permission.to_s.split("_or_").any? { |policy| can?(policy.to_sym, **args)}
|
21
|
-
|
26
|
+
when /\Anot_/
|
22
27
|
policy = permission.to_s.gsub(/\Anot_/, "")
|
23
28
|
! can?(policy.to_sym, **args)
|
24
29
|
else
|
25
|
-
raise
|
30
|
+
raise exception
|
26
31
|
end
|
27
|
-
|
28
32
|
end
|
29
33
|
|
30
34
|
def perform_for(permission, **args)
|
@@ -32,15 +36,35 @@ module YouShallNotPass
|
|
32
36
|
end
|
33
37
|
|
34
38
|
def policies
|
35
|
-
@policies ||=
|
39
|
+
@policies ||= __set_policies__
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def __set_policies__
|
45
|
+
the_policies = methods.grep(/_policies\z/).map {|name| send(name)}.each_with_object({}) do |curr, res|
|
36
46
|
res.merge!(curr)
|
37
47
|
end
|
48
|
+
|
49
|
+
the_policies.merge!( self.class.__dsl_policies__.each_with_object({}) { |policy, h|
|
50
|
+
block = policy.last
|
51
|
+
h[policy.first] = instance_eval(&block)
|
52
|
+
})
|
53
|
+
|
54
|
+
the_policies
|
38
55
|
end
|
39
56
|
|
40
|
-
|
57
|
+
def self.__dsl_policies__
|
58
|
+
@__dsl_policies__ ||= {}
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.authorize(name, &block)
|
62
|
+
__dsl_policies__[name] = block
|
63
|
+
end
|
41
64
|
|
42
65
|
def self.attribute(attr)
|
43
66
|
fattr attr
|
44
67
|
end
|
68
|
+
private_class_method :attribute
|
45
69
|
end
|
46
70
|
end
|
@@ -24,28 +24,28 @@ scope YouShallNotPass::Authorizator do
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
class
|
27
|
+
class MyAuthorizator < YouShallNotPass::Authorizator
|
28
28
|
def policies
|
29
29
|
{
|
30
|
-
|
31
|
-
|
30
|
+
can_lambda: -> (*) { true },
|
31
|
+
cant_lambda: -> (*) { false },
|
32
32
|
|
33
|
-
|
34
|
-
|
33
|
+
can_proc: proc { true },
|
34
|
+
cant_proc: proc { false },
|
35
35
|
|
36
|
-
|
37
|
-
|
36
|
+
can_action: Action.new(true),
|
37
|
+
cant_action: Action.new(false),
|
38
38
|
|
39
|
-
|
40
|
-
|
39
|
+
can_true: true,
|
40
|
+
cant_false: false,
|
41
41
|
|
42
|
-
|
43
|
-
|
42
|
+
can_array: [ true, proc { true }, true ],
|
43
|
+
cant_array: [ true, false, true ],
|
44
44
|
}
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
let(:authorizator) {
|
48
|
+
let(:authorizator) { MyAuthorizator.new }
|
49
49
|
|
50
50
|
spec "allow lambda" do
|
51
51
|
authorizator.can?(:can_lambda)
|
@@ -90,18 +90,18 @@ scope YouShallNotPass::Authorizator do
|
|
90
90
|
end
|
91
91
|
|
92
92
|
scope "arguments" do
|
93
|
-
class
|
93
|
+
class MyAuthorizatorWithArgs < YouShallNotPass::Authorizator
|
94
94
|
def policies
|
95
95
|
{
|
96
|
-
|
97
|
-
|
96
|
+
lambda: -> (a:, b:) { a == b },
|
97
|
+
proc: proc { |a:, b:| a == b },
|
98
98
|
|
99
|
-
|
99
|
+
splat: -> (**args) { args.all? { |k, v| k == v} },
|
100
100
|
}
|
101
101
|
end
|
102
102
|
end
|
103
103
|
|
104
|
-
let(:authorizator) {
|
104
|
+
let(:authorizator) { MyAuthorizatorWithArgs.new }
|
105
105
|
|
106
106
|
spec "allow lambda" do
|
107
107
|
authorizator.can?(:lambda, a: 1, b: 1)
|
@@ -132,10 +132,10 @@ scope YouShallNotPass::Authorizator do
|
|
132
132
|
class BasicAuthorizator < YouShallNotPass::Authorizator
|
133
133
|
def policies
|
134
134
|
{
|
135
|
-
|
136
|
-
|
135
|
+
can: true,
|
136
|
+
cant: false,
|
137
137
|
|
138
|
-
|
138
|
+
use_args: -> (**args) { args.all? { |k, v| k == v } }
|
139
139
|
}
|
140
140
|
end
|
141
141
|
end
|
@@ -168,18 +168,18 @@ scope YouShallNotPass::Authorizator do
|
|
168
168
|
end
|
169
169
|
|
170
170
|
scope "conditional policies" do
|
171
|
-
class
|
171
|
+
class NumberAuthorizator < YouShallNotPass::Authorizator
|
172
172
|
def policies
|
173
173
|
{
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
174
|
+
one: true,
|
175
|
+
two: true,
|
176
|
+
three: false,
|
177
|
+
four: false,
|
178
178
|
}
|
179
179
|
end
|
180
180
|
end
|
181
181
|
|
182
|
-
let(:authorizator) {
|
182
|
+
let(:authorizator) { NumberAuthorizator.new }
|
183
183
|
|
184
184
|
spec "allow _and_" do
|
185
185
|
authorizator.can?(:one_and_two)
|
@@ -217,10 +217,10 @@ scope YouShallNotPass::Authorizator do
|
|
217
217
|
class ConditionalAuthorizator < YouShallNotPass::Authorizator
|
218
218
|
def policies
|
219
219
|
{
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
220
|
+
one: true,
|
221
|
+
two: true,
|
222
|
+
one_and_two: false,
|
223
|
+
one_or_two: false
|
224
224
|
}
|
225
225
|
end
|
226
226
|
end
|
@@ -241,22 +241,22 @@ scope YouShallNotPass::Authorizator do
|
|
241
241
|
class MergePolicies < YouShallNotPass::Authorizator
|
242
242
|
def action_policies
|
243
243
|
{
|
244
|
-
|
245
|
-
|
244
|
+
create_user: true,
|
245
|
+
update_user: true,
|
246
246
|
}
|
247
247
|
end
|
248
248
|
|
249
249
|
def role_policies
|
250
250
|
{
|
251
|
-
|
252
|
-
|
251
|
+
admin: true,
|
252
|
+
editor: true,
|
253
253
|
}
|
254
254
|
end
|
255
255
|
|
256
256
|
def feature_policies
|
257
257
|
{
|
258
|
-
|
259
|
-
|
258
|
+
avatars: true,
|
259
|
+
random_player: true,
|
260
260
|
}
|
261
261
|
end
|
262
262
|
end
|
@@ -269,13 +269,13 @@ scope YouShallNotPass::Authorizator do
|
|
269
269
|
|
270
270
|
spec "merges all the policies" do
|
271
271
|
@expected = {
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
272
|
+
create_user: true,
|
273
|
+
update_user: true,
|
274
|
+
admin: true,
|
275
|
+
editor: true,
|
276
|
+
avatars: true,
|
277
|
+
random_player: true,
|
278
|
+
}
|
279
279
|
|
280
280
|
authorizator.policies == @expected
|
281
281
|
end
|
@@ -288,7 +288,7 @@ scope YouShallNotPass::Authorizator do
|
|
288
288
|
|
289
289
|
def policies
|
290
290
|
{
|
291
|
-
|
291
|
+
something: proc { user == "Me" && role == :admin }
|
292
292
|
}
|
293
293
|
end
|
294
294
|
end
|
@@ -305,4 +305,31 @@ scope YouShallNotPass::Authorizator do
|
|
305
305
|
! authorizator.can?(:something)
|
306
306
|
end
|
307
307
|
end
|
308
|
+
|
309
|
+
scope "dsl" do
|
310
|
+
class DslAuthorizator < YouShallNotPass::Authorizator
|
311
|
+
attribute :user
|
312
|
+
attribute :pass
|
313
|
+
|
314
|
+
authorize(:true) { true }
|
315
|
+
authorize(:false) { false }
|
316
|
+
|
317
|
+
authorize(:login) { user == pass}
|
318
|
+
end
|
319
|
+
|
320
|
+
let(:authorizator) { DslAuthorizator.new(user: "fede", pass: "fede") }
|
321
|
+
|
322
|
+
spec "authorizes true" do
|
323
|
+
authorizator.can?(:true)
|
324
|
+
end
|
325
|
+
|
326
|
+
spec "rejects false" do
|
327
|
+
authorizator.can?(:false) == false
|
328
|
+
end
|
329
|
+
|
330
|
+
spec do
|
331
|
+
authorizator.can?(:login)
|
332
|
+
end
|
333
|
+
|
334
|
+
end
|
308
335
|
end
|
data/you_shall_not_pass.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["iachetti.federico@gmail.com"]
|
11
11
|
spec.summary = %q{Simple authorization library.}
|
12
12
|
spec.description = %q{Embrace authorization with this simple library.}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/iachettifederico/you_shall_not_pass"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -20,8 +20,8 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.7"
|
22
22
|
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
-
spec.add_development_dependency "matest", "~> 1.
|
23
|
+
spec.add_development_dependency "matest", "~> 1.7.1"
|
24
24
|
|
25
|
-
spec.add_dependency "callable", "~> 0.0.
|
25
|
+
spec.add_dependency "callable", "~> 0.0.5"
|
26
26
|
spec.add_dependency "fattr", "~> 2.2.2"
|
27
27
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: you_shall_not_pass
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Federico Iachetti
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-09-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -44,28 +44,28 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 1.7.1
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 1.7.1
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: callable
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.0.
|
61
|
+
version: 0.0.5
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.0.
|
68
|
+
version: 0.0.5
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: fattr
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -88,6 +88,7 @@ extensions: []
|
|
88
88
|
extra_rdoc_files: []
|
89
89
|
files:
|
90
90
|
- ".gitignore"
|
91
|
+
- ".travis.yml"
|
91
92
|
- Gemfile
|
92
93
|
- LICENSE.txt
|
93
94
|
- README.md
|
@@ -99,7 +100,7 @@ files:
|
|
99
100
|
- spec/spec_helper.rb
|
100
101
|
- spec/you_shall_not_pass/authorizator_spec.rb
|
101
102
|
- you_shall_not_pass.gemspec
|
102
|
-
homepage:
|
103
|
+
homepage: https://github.com/iachettifederico/you_shall_not_pass
|
103
104
|
licenses:
|
104
105
|
- MIT
|
105
106
|
metadata: {}
|
@@ -119,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
120
|
version: '0'
|
120
121
|
requirements: []
|
121
122
|
rubyforge_project:
|
122
|
-
rubygems_version: 2.4.
|
123
|
+
rubygems_version: 2.4.8
|
123
124
|
signing_key:
|
124
125
|
specification_version: 4
|
125
126
|
summary: Simple authorization library.
|