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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c2a0082fe593f22ba8a1ccc446061ed132bb3248
4
- data.tar.gz: f9be21782946d49cd83fe2b694d29ca6a153e07c
3
+ metadata.gz: 2f7b94fd90514299c816fa9f4f55a4f50fe7fadb
4
+ data.tar.gz: 2c20e0ded03fde67292cb5fbed5a11a7ffa959b3
5
5
  SHA512:
6
- metadata.gz: 225b91dba9bc231f7ff0207523bb1f8d360506aa473e9f399de64b44a8482f9d6b50712066b1bd994a03e90e19b42c273594027430642aa63dc6804383fbd791
7
- data.tar.gz: 19679f956b3282381acbca246d940be8cabf690f1e520f22d6cb0c7258ef52d8fd9d6ace5ead2bda13740af9ef723f0bc2b741717ca08aa3c70303d807a10dfe
6
+ metadata.gz: e7e767bf6c234ab62c6ad73c94d8ce37e90fc25bdf5c2948d6126bc715351a3367164f1fe490ed28c9ccd17e884a86629077f6d81c530b884ccf205c6d6c03d8
7
+ data.tar.gz: 8512dcc4f5df0b615619ebc360387d400a56ce7db016d1598defaedef9defe7902b1ccaff82a9f62f006e22182383929c3c9e0e01512a36d914ac73bfcdf7aac
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - 2.0.0
3
+ - 2.1.1
4
+ - 2.2.0
5
+ - ruby-head
data/README.md CHANGED
@@ -1,13 +1,168 @@
1
1
  # YouShallNotPass
2
+ [![Build Status](https://travis-ci.org/iachettifederico/you_shall_not_pass.png?branch=master)](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
@@ -1,3 +1,5 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
3
  require "matest/spec_tasks"
4
+
5
+ task default: :spec
@@ -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 => e
17
- if permission =~ /_and_/
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
- elsif permission =~ /_or_/
24
+ when /_or_/
20
25
  permission.to_s.split("_or_").any? { |policy| can?(policy.to_sym, **args)}
21
- elsif permission =~ /\Anot_/
26
+ when /\Anot_/
22
27
  policy = permission.to_s.gsub(/\Anot_/, "")
23
28
  ! can?(policy.to_sym, **args)
24
29
  else
25
- raise e
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 ||= methods.grep(/_policies\Z/).map {|name| send(name)}.each_with_object({}) do |curr, res|
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
- private
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
@@ -1,11 +1,7 @@
1
1
  module YouShallNotPass
2
2
  class Policy
3
- include Callable
4
- attr_reader :user
5
-
6
3
  def initialize(authorizator)
7
4
  @authorizator = authorizator
8
- @user = authorizator.user
9
5
  end
10
6
 
11
7
  def call(**options)
@@ -1,3 +1,3 @@
1
1
  module YouShallNotPass
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -24,28 +24,28 @@ scope YouShallNotPass::Authorizator do
24
24
  end
25
25
  end
26
26
 
27
- class MyAuthenticator < YouShallNotPass::Authorizator
27
+ class MyAuthorizator < YouShallNotPass::Authorizator
28
28
  def policies
29
29
  {
30
- can_lambda: -> (*) { true },
31
- cant_lambda: -> (*) { false },
30
+ can_lambda: -> (*) { true },
31
+ cant_lambda: -> (*) { false },
32
32
 
33
- can_proc: proc { true },
34
- cant_proc: proc { false },
33
+ can_proc: proc { true },
34
+ cant_proc: proc { false },
35
35
 
36
- can_action: Action.new(true),
37
- cant_action: Action.new(false),
36
+ can_action: Action.new(true),
37
+ cant_action: Action.new(false),
38
38
 
39
- can_true: true,
40
- cant_false: false,
39
+ can_true: true,
40
+ cant_false: false,
41
41
 
42
- can_array: [ true, proc { true }, true ],
43
- cant_array: [ true, false, true ],
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) { MyAuthenticator.new }
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 MyAuthenticatorWithArgs < YouShallNotPass::Authorizator
93
+ class MyAuthorizatorWithArgs < YouShallNotPass::Authorizator
94
94
  def policies
95
95
  {
96
- lambda: -> (a:, b:) { a == b },
97
- proc: proc { |a:, b:| a == b },
96
+ lambda: -> (a:, b:) { a == b },
97
+ proc: proc { |a:, b:| a == b },
98
98
 
99
- splat: -> (**args) { args.all? { |k, v| k == v} },
99
+ splat: -> (**args) { args.all? { |k, v| k == v} },
100
100
  }
101
101
  end
102
102
  end
103
103
 
104
- let(:authorizator) { MyAuthenticatorWithArgs.new }
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
- can: true,
136
- cant: false,
135
+ can: true,
136
+ cant: false,
137
137
 
138
- use_args: -> (**args) { args.all? { |k, v| k == v } }
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 NumberAuthenticator < YouShallNotPass::Authorizator
171
+ class NumberAuthorizator < YouShallNotPass::Authorizator
172
172
  def policies
173
173
  {
174
- one: true,
175
- two: true,
176
- three: false,
177
- four: false,
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) { NumberAuthenticator.new }
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
- one: true,
221
- two: true,
222
- one_and_two: false,
223
- one_or_two: false
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
- create_user: true,
245
- update_user: true,
244
+ create_user: true,
245
+ update_user: true,
246
246
  }
247
247
  end
248
248
 
249
249
  def role_policies
250
250
  {
251
- admin: true,
252
- editor: true,
251
+ admin: true,
252
+ editor: true,
253
253
  }
254
254
  end
255
255
 
256
256
  def feature_policies
257
257
  {
258
- avatars: true,
259
- random_player: true,
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
- create_user: true,
273
- update_user: true,
274
- admin: true,
275
- editor: true,
276
- avatars: true,
277
- random_player: true,
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
- something: proc { user == "Me" && role == :admin }
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
@@ -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.5"
23
+ spec.add_development_dependency "matest", "~> 1.7.1"
24
24
 
25
- spec.add_dependency "callable", "~> 0.0.3"
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.3
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-02-03 00:00:00.000000000 Z
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: '1.5'
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: '1.5'
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.3
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.3
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.5
123
+ rubygems_version: 2.4.8
123
124
  signing_key:
124
125
  specification_version: 4
125
126
  summary: Simple authorization library.