three 1.0.0 → 1.0.1

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: 67be0ba30ea3e93a427626a755b1b7d68c91237f
4
- data.tar.gz: c596b7c49d054654ca1fca1601b4de14d7b791b9
3
+ metadata.gz: db0908414cedec93c7f8f5cae166fad7b1b1ef9b
4
+ data.tar.gz: 7cd6687d38c7ccc4d9ffef7daaf1d719f7b26aed
5
5
  SHA512:
6
- metadata.gz: 13ec53cbf10eb8da40fbd537951a4978989bed0e899bc27f83a8d5f5efccecac9236b515e2442be805d1cb2da8b9ceb3100f9dc050457d2e0a24be3255619969
7
- data.tar.gz: c2432f75e309ca8fc2a1095e24da1667458d7e083107eff9635b9eadb55e4f4b7bc605db2fc2ca9845b9b0155eac7d9bf5f4107e9e607ce21abfc490d8efab09
6
+ metadata.gz: aa8cbc2fc7e2289a322eadefa7dca02afe0e2cf8d16edeef25087693d0f7e6ea0a793b6ea1d4085d6ac24c08b21010525a357dcacdc532d1cdca3067f50888bf
7
+ data.tar.gz: 3a55795d388bf63dedb2403708a07dcc60ca1d0b4b16f1b4846fb80425b51ae0e59bee59d4797614571b29d8bf5f34d6e101cdc7a3db560361a4a65e1dcee1d1
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .rvmrc
2
+ .Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - "1.9.3"
3
+ - "2.0.0"
4
+ - "2.1.0"
5
+ - jruby-19mode
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,20 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ three (1.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ metaclass (0.0.4)
10
+ mocha (1.1.0)
11
+ metaclass (~> 0.0.1)
12
+ rake (10.3.2)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ mocha
19
+ rake
20
+ three!
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2014 Darren Cauthon
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/ORIGINAL_LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Dmitriy Zaporozhets
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,153 @@
1
+ ## Three - an even smaller, tinier simple authorization gem for ruby
2
+
3
+ [![Build Status](https://travis-ci.org/darrencauthon/three.png)](https://travis-ci.org/darrencauthon/three)
4
+ [![Code Climate](https://codeclimate.com/github/darrencauthon/three.png)](https://codeclimate.com/github/darrencauthon/three)
5
+
6
+ This gem started as a minor fork of [six](https://github.com/randx/six), a neat, tiny authorization gem. I used six and liked it, but as small was it was I found that I needed maybe half of its code and features. So here is *three*.
7
+
8
+ **three** is a small authentication library, focused on only a few needs:
9
+
10
+ 1. Provide an open/closed method of constructing rules,
11
+ 2. Provide a way to remove permissions, and
12
+ 3. Do it as simply as possible.
13
+
14
+ ### Installation
15
+
16
+ ```ruby
17
+ gem install three
18
+ ```
19
+
20
+ ### Usage
21
+
22
+ Here's the simplest working example... a set of rules that apply for all
23
+
24
+ ```ruby
25
+ # make an object with an "allowed" method that returns an array of permissions
26
+ module Rules
27
+ def self.allowed _, _
28
+ [:edit, :delete]
29
+ end
30
+ end
31
+
32
+ # create an evaluator that can be used to evaluate rules
33
+ evaluator = Three.evaluator_for Rules
34
+
35
+ # use the evaluator to determine what's allowed or not
36
+
37
+ evaluator.allowed? nil, :edit # true
38
+ evaluator.allowed? nil, :close # false
39
+ evaluator.allowed? nil, :delete # true
40
+ ```
41
+
42
+ Unfortunately, that's not a very realistic example. We'll almost always want to evaluate the rules based on some sort of subject:
43
+
44
+ ```ruby
45
+ module AdminRules
46
+ def self.allowed user, _
47
+ return [] unless user.admin?
48
+ [:edit, :delete]
49
+ end
50
+ end
51
+
52
+ evaluator = Three.evaluator_for AdminRules
53
+
54
+ admin_user = User.new(admin: true)
55
+ not_an_admin = User.new(admin: false)
56
+
57
+ evaluator.allowed? admin_user, :edit # true
58
+ evaluator.allowed? not_an_admin_user, :edit # false
59
+ ```
60
+
61
+ See? The array of permissions returned by the "allowed" method are used to determine if a user can do something.
62
+
63
+ The rules can be compounded, like so:
64
+
65
+
66
+ ```ruby
67
+ module AdminRules
68
+ def self.allowed user, _
69
+ return [] unless user.admin?
70
+ [:edit, :delete]
71
+ end
72
+ end
73
+
74
+ module UserRules
75
+ def self.allowed user, _
76
+ return [] if user.admin?
77
+ [:view_my_account]
78
+ end
79
+ end
80
+
81
+ evaluator = Three.evaluator_for(AdminRules, UserRules)
82
+
83
+ admin_user = User.new(admin: true)
84
+ not_an_admin = User.new(admin: false)
85
+
86
+ evaluator.allowed? admin_user, :edit # true
87
+ evaluator.allowed? not_an_admin_user, :edit # false
88
+
89
+ evaluator.allowed? admin_user, :view_my_account # false
90
+ evaluator.allowed? not_an_admin_user, :view_my_account # true
91
+ ```
92
+
93
+ But what about that trailing "_" variable? That's used as an optional target, which you can use to return permissions based on the relationship between the two arguments:
94
+
95
+ ```ruby
96
+ module MovieRules
97
+ def self.allowed user, movie
98
+ if user.is_a_minor? and movie.is_rated_r
99
+ []
100
+ else
101
+ [:can_buy_the_ticket]
102
+ end
103
+ end
104
+ end
105
+
106
+ evaluator = Three.evaluator_for MovieRules
107
+
108
+ minor = User.new(minor: true)
109
+ not_a_minor = User.new(minor: false)
110
+
111
+ scary_movie = Movie.new(rating: 'R')
112
+ kids_movie = Movie.new(rating: 'PG')
113
+
114
+ evaluator.allowed? minor, :can_buy_the_ticket, scary_movie # false
115
+ evaluator.allowed? not_a_minor, :can_buy_the_ticket, scary_movie # true
116
+
117
+ evaluator.allowed? minor, :can_buy_the_ticket, kids_movie # true
118
+ evaluator.allowed? not_a_minor, :can_buy_the_ticket, kids_movie # true
119
+ ```
120
+
121
+ Only one more special thing... what if we want to right a rule that prevents something?
122
+
123
+ ```ruby
124
+ module DefaultLibraryRules
125
+ def self.allowed user, book
126
+ [:reserve_the_book]
127
+ end
128
+ end
129
+
130
+ module FinesOwedRules
131
+ def self.prevented user, _
132
+ if user.owes_fines?
133
+ [:reserve_the_book]
134
+ else
135
+ []
136
+ end
137
+ end
138
+ end
139
+
140
+ evaluator = Three.evaluator_for(DefaultLibraryRules, FinesOwedRules)
141
+
142
+ deadbeat = User.new(fines: 3.0)
143
+ responsible_citizen = User.new(fines: 0)
144
+
145
+ evaluator.allowed? deadbeat, :reserve_the_book # false
146
+ evaluator.allowed? responsible_citizen, :reserve_the_book # true
147
+
148
+ ```
149
+
150
+ The "prevented" method works just like "allowed," except that it will remove the permission from any other rule's "allowed" method.
151
+
152
+ The "prevented" method is the only only feature added with six.
153
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ task :default => [:test]
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs.push "lib"
8
+ t.test_files = FileList['spec/three/*_spec.rb', 'spec/*_spec.rb']
9
+ t.verbose = true
10
+ end
@@ -0,0 +1,71 @@
1
+ module Three
2
+
3
+ class Evaluator
4
+
5
+ def initialize(rules)
6
+ @rules = a_single_array_was_provided?(rules) ? rules[0] : rules
7
+ end
8
+
9
+ def allowed? subject, permissions_to_check, target = nil
10
+ permissions = convert_to_an_array_if_its_not permissions_to_check
11
+ these_permissions_are_allowed_for? permissions, subject, target
12
+ end
13
+
14
+ private
15
+
16
+ def rules
17
+ @rules
18
+ end
19
+
20
+ def convert_to_an_array_if_its_not potential_array
21
+ this_is_an_array?(potential_array) ? potential_array : [potential_array]
22
+ end
23
+
24
+ def these_permissions_are_allowed_for? permissions, subject, target
25
+ permissions.all? { |p| permission_included_between? p, subject, target }
26
+ end
27
+
28
+ def a_single_array_was_provided? rules
29
+ rules.count == 1 && this_is_an_array?(rules[0])
30
+ end
31
+
32
+ def this_is_an_array? thing
33
+ thing.respond_to? :each
34
+ end
35
+
36
+ def permission_included_between? permission_to_check, subject, target
37
+ allowed_permissions_for(subject, target).include? permission_to_check.to_s
38
+ end
39
+
40
+ def allowed_permissions_for subject, target
41
+ all_permissions = all_permissions_for subject, target
42
+ permissions_to_reject = permissions_to_reject_for subject, target
43
+
44
+ all_permissions - permissions_to_reject
45
+ end
46
+
47
+ def all_permissions_for subject, target
48
+ permissions = rules.map { |r| execute_rule r, :allowed, subject, target }
49
+ flatten_permissions permissions
50
+ end
51
+
52
+ def permissions_to_reject_for subject, target
53
+ permissions = rules.map { |r| execute_rule r, :prevented, subject, target }
54
+ flatten_permissions permissions
55
+ end
56
+
57
+ def execute_rule rule, method, subject, target
58
+ begin
59
+ rule.send(method, subject, target)
60
+ rescue
61
+ []
62
+ end
63
+ end
64
+
65
+ def flatten_permissions permissions
66
+ permissions.flatten.map { |a| a.to_s }
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -0,0 +1,3 @@
1
+ module Three
2
+ VERSION = "1.0.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ require_relative '../lib/three.rb'
2
+ require 'minitest/autorun'
3
+ require 'minitest/spec'
4
+ require 'mocha/setup'
@@ -0,0 +1,237 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe Three do
4
+
5
+ describe "allowed?" do
6
+
7
+ describe "there are no rules" do
8
+
9
+ let(:abilities) { Three::Evaluator.new([]) }
10
+
11
+ it "should return false" do
12
+ abilities.allowed?(nil, :does_not_matter).must_equal false
13
+ abilities.allowed?(Object.new, :something_else).must_equal false
14
+ end
15
+
16
+ end
17
+
18
+ [:one, :two].each do |permission|
19
+
20
+ describe "one rule that returns one permission" do
21
+
22
+ let(:abilities) { Three.evaluator_for([rule]) }
23
+
24
+ let(:subject) { Object.new }
25
+
26
+ let(:rule) do
27
+ o = Object.new
28
+ o.stubs(:allowed).with(subject, nil).returns [permission]
29
+ o
30
+ end
31
+
32
+ it "should return true for the allowed permission" do
33
+ abilities.allowed?(subject, permission).must_equal true
34
+ end
35
+
36
+ it "should return false for other permissions" do
37
+ abilities.allowed?(subject, :something_else).must_equal false
38
+ abilities.allowed?(subject, :another_thing).must_equal false
39
+ end
40
+
41
+ describe "with a target" do
42
+
43
+ let(:target) { Object.new }
44
+
45
+ before do
46
+ rule.stubs(:allowed).with(subject, target).returns [permission]
47
+ end
48
+
49
+ it "should return true for the allowed permission" do
50
+ abilities.allowed?(subject, permission).must_equal true
51
+ end
52
+
53
+ it "should return true if asked for the permission in an array" do
54
+ abilities.allowed?(subject, [permission]).must_equal true
55
+ end
56
+
57
+ it "should return false for other permissions" do
58
+ abilities.allowed?(subject, :something).must_equal false
59
+ abilities.allowed?(subject, :another).must_equal false
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
68
+ describe "one rule that returns two permissions" do
69
+
70
+ let(:abilities) { Three.evaluator_for([rule]) }
71
+
72
+ let(:subject) { Object.new }
73
+
74
+ let(:rule) do
75
+ o = Object.new
76
+ o.stubs(:allowed).with(subject, nil).returns [:orange, :banana]
77
+ o
78
+ end
79
+
80
+ it "should return true if asked for either, alone" do
81
+ abilities.allowed?(subject, :orange).must_equal true
82
+ abilities.allowed?(subject, :banana).must_equal true
83
+ end
84
+
85
+ it "should return true if asked for both at the same time" do
86
+ abilities.allowed?(subject, [:orange, :banana]).must_equal true
87
+ end
88
+
89
+ it "should return true if asked for one and another that does not match" do
90
+ abilities.allowed?(subject, [:orange, :apple]).must_equal false
91
+ abilities.allowed?(subject, [:pear, :banana]).must_equal false
92
+ end
93
+
94
+ it "should return false for other permissions" do
95
+ abilities.allowed?(subject, :apple).must_equal false
96
+ abilities.allowed?(subject, :pear).must_equal false
97
+ end
98
+
99
+ end
100
+
101
+ describe "two rules that return one permission each" do
102
+
103
+ let(:abilities) { Three.evaluator_for([rule1, rule2]) }
104
+
105
+ let(:subject) { Object.new }
106
+
107
+ let(:rule1) do
108
+ o = Object.new
109
+ o.stubs(:allowed).with(subject, nil).returns [:orange]
110
+ o
111
+ end
112
+
113
+ let(:rule2) do
114
+ o = Object.new
115
+ o.stubs(:allowed).with(subject, nil).returns [:banana]
116
+ o
117
+ end
118
+
119
+ it "should return true if asked for either, alone" do
120
+ abilities.allowed?(subject, :orange).must_equal true
121
+ abilities.allowed?(subject, :banana).must_equal true
122
+ end
123
+
124
+ it "should return true if asked for both at the same time" do
125
+ abilities.allowed?(subject, [:orange, :banana]).must_equal true
126
+ end
127
+
128
+ it "should return true if asked for one and another that does not match" do
129
+ abilities.allowed?(subject, [:orange, :apple]).must_equal false
130
+ abilities.allowed?(subject, [:pear, :banana]).must_equal false
131
+ end
132
+
133
+ it "should return false for other permissions" do
134
+ abilities.allowed?(subject, :apple).must_equal false
135
+ abilities.allowed?(subject, :pear).must_equal false
136
+ end
137
+
138
+ end
139
+
140
+ describe "rejecting permissions" do
141
+
142
+ let(:abilities) { Three.evaluator_for([rule1, rule2]) }
143
+
144
+ let(:subject) { Object.new }
145
+
146
+ let(:rule1) do
147
+ o = Object.new
148
+ o.stubs(:allowed).with(subject, nil).returns [:orange, :banana]
149
+ o.stubs(:prevented).with(subject, nil).returns [:apple]
150
+ o
151
+ end
152
+
153
+ let(:rule2) do
154
+ o = Object.new
155
+ o.stubs(:allowed).with(subject, nil).returns [:apple, :pear]
156
+ o.stubs(:prevented).with(subject, nil).returns [:banana]
157
+ o
158
+ end
159
+
160
+ it "should return false for the permissions that are prevented" do
161
+ abilities.allowed?(subject, :banana).must_equal false
162
+ abilities.allowed?(subject, :apple).must_equal false
163
+ end
164
+
165
+ it "should return true for the permissions that are not prevented" do
166
+ abilities.allowed?(subject, :pear).must_equal true
167
+ abilities.allowed?(subject, :orange).must_equal true
168
+ end
169
+
170
+ describe "with a target" do
171
+
172
+ let(:target) { Object.new }
173
+
174
+ before do
175
+ rule1.stubs(:allowed).with(subject, target).returns [:orange, :banana]
176
+ rule1.stubs(:prevented).with(subject, target).returns [:apple]
177
+
178
+ rule2.stubs(:allowed).with(subject, target).returns [:apple, :pear]
179
+ rule2.stubs(:prevented).with(subject, target).returns [:banana]
180
+ end
181
+
182
+ it "should return false for the permissions that are prevented" do
183
+ abilities.allowed?(subject, :banana, target).must_equal false
184
+ abilities.allowed?(subject, :apple, target).must_equal false
185
+ end
186
+
187
+ it "should return true for the permissions that are not prevented" do
188
+ abilities.allowed?(subject, :pear, target).must_equal true
189
+ abilities.allowed?(subject, :orange, target).must_equal true
190
+ end
191
+ end
192
+
193
+ end
194
+
195
+ describe "alternate constructor" do
196
+
197
+ let(:abilities) { Three.evaluator_for(rule1, rule2) }
198
+
199
+ let(:subject) { Object.new }
200
+
201
+ let(:rule1) do
202
+ o = Object.new
203
+ o.stubs(:allowed).with(subject, nil).returns [:orange, :banana]
204
+ o.stubs(:prevented).with(subject, nil).returns [:apple]
205
+ o
206
+ end
207
+
208
+ let(:rule2) do
209
+ o = Object.new
210
+ o.stubs(:allowed).with(subject, nil).returns [:apple, :pear]
211
+ o.stubs(:prevented).with(subject, nil).returns [:banana]
212
+ o
213
+ end
214
+
215
+ it "should return false for the permissions that are prevented" do
216
+ abilities.allowed?(subject, :banana).must_equal false
217
+ abilities.allowed?(subject, :apple).must_equal false
218
+ end
219
+
220
+ it "should return true for the permissions that are not prevented" do
221
+ abilities.allowed?(subject, :pear).must_equal true
222
+ abilities.allowed?(subject, :orange).must_equal true
223
+ end
224
+
225
+ end
226
+
227
+ describe "no rules provided" do
228
+ it "should return false for everything" do
229
+ abilities = Three::Evaluator.new([])
230
+ abilities.allowed?(Object.new, :anything).must_equal false
231
+ abilities.allowed?(Object.new, :anything, Object.new).must_equal false
232
+ end
233
+ end
234
+
235
+ end
236
+
237
+ end
data/three.gemspec ADDED
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/three/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'three'
6
+ s.version = Three::VERSION
7
+ s.date = '2014-06-16'
8
+ s.summary = "three"
9
+ s.description = "Even simpler authorization gem"
10
+ s.authors = ["Darren Cauthon"]
11
+ s.email = 'darren@cauthon.com'
12
+ s.files = `git ls-files`.split($/)
13
+ s.homepage = 'https://github.com/darrencauthon/three'
14
+
15
+ s.add_development_dependency 'mocha'
16
+ s.add_development_dependency 'rake'
17
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: three
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Darren Cauthon
@@ -44,7 +44,21 @@ executables: []
44
44
  extensions: []
45
45
  extra_rdoc_files: []
46
46
  files:
47
+ - .gitignore
48
+ - .rspec
49
+ - .travis.yml
50
+ - Gemfile
51
+ - Gemfile.lock
52
+ - LICENSE
53
+ - ORIGINAL_LICENSE
54
+ - README.markdown
55
+ - Rakefile
47
56
  - lib/three.rb
57
+ - lib/three/evaluator.rb
58
+ - lib/three/version.rb
59
+ - spec/spec_helper.rb
60
+ - spec/three_spec.rb
61
+ - three.gemspec
48
62
  homepage: https://github.com/darrencauthon/three
49
63
  licenses: []
50
64
  metadata: {}
@@ -64,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
64
78
  version: '0'
65
79
  requirements: []
66
80
  rubyforge_project:
67
- rubygems_version: 2.2.2
81
+ rubygems_version: 2.0.14
68
82
  signing_key:
69
83
  specification_version: 4
70
84
  summary: three