roadblock 0.0.1 → 0.1.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: 25323c08d6361c7df9cb6a2a0c07b7e805e30aad
4
- data.tar.gz: ab5ad7a071020810f11a5073a5f58186fc983502
3
+ metadata.gz: af1f328bbe876f53f82f57c5b51f6b432fa0bb5e
4
+ data.tar.gz: d649ced8b0f46d0ca810fe558c8ce508252f4474
5
5
  SHA512:
6
- metadata.gz: cf74d0bf321e337b64fbb510ca89e5d3e3e8a1be7cd32a57d48098d3f702291cdd5df91d4ab23fd283fe3fdec5c5b256e82171b228ee39f468d1dd5778c71f0f
7
- data.tar.gz: 92d8951045847ba6e55d309380361334df9c65ff29d6f01fe0cb283a9943a603f0f8618ba3f1ba6821bfd7224d309eb0482de72d9877bdabec968acc6b05a671
6
+ metadata.gz: bda6816c0d10e740b186e6358b7fd080ec520aa17e6b82a15a2d46f524a98ca79659c1da98ac9a624945e978dd6cd89be5cfb510736c5b4ed13f9b56d85b8319
7
+ data.tar.gz: 1e614fe8b7d11bdf763a36919f95edfaca225d61fab8cb4e4698b6ac9b06a337aae26b4b2cde04fd5f0b045732a88f6c9a9b3f08bd2ddda3853916327e46364c
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ .ruby-version
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # Roadblock
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/roadblock.png)](http://badge.fury.io/rb/roadblock)
3
4
  [![Semaphore](https://semaphoreapp.com/api/v1/projects/f1ccf0c3ff7565f975caef0fdfcf649f24f033fb/118939/shields_badge.png)](https://semaphoreapp.com/minter/roadblock)
4
5
  [![Code Climate](https://codeclimate.com/github/teamsnap/roadblock.png)](https://codeclimate.com/github/teamsnap/roadblock)
5
6
  [![Coverage Status](https://coveralls.io/repos/teamsnap/roadblock/badge.png?branch=master)](https://coveralls.io/r/teamsnap/roadblock?branch=master)
@@ -12,7 +13,7 @@ A simple authorization library.
12
13
 
13
14
  Roadblock provides a simple interface for checking if a ruby object has the authority to interact with another object. The most obvious example being if the current user in your rails controller can read/write the object they're attempting to access.
14
15
 
15
- Nearly all authorization libraries require heavy weight configuration and tight integration with Rails. This library was created to provide the simplest solution to the problem without requiring any external dependencies. It doesn't require Rails or any of it's subcomponents and weighs in at less than 10 LOC for the actual implementation. The library also optionally understands OAUTH scopes, something other authorization libraries do not.
16
+ Nearly all authorization libraries require heavy weight configuration and tight integration with Rails. This library was created to provide the simplest solution to the problem without requiring any external dependencies. It doesn't require Rails or any of it's subcomponents and weighs in at less than 100 LOC for the actual implementation (less than 25 LOC if you don't care about authorizer stacks).
16
17
 
17
18
  ## Installation
18
19
 
@@ -30,38 +31,86 @@ Or install it yourself as:
30
31
 
31
32
  ## Usage
32
33
 
33
- require "roadblock"
34
+ ```ruby
35
+ require "roadblock"
34
36
 
35
- class TeamAuthorizer
36
- include Roadblock.authorizer
37
+ class TeamAuthorizer
38
+ include Roadblock.authorizer
37
39
 
38
- def can_read?(team)
39
- scopes.include?("read") &&
40
- user.teams.include?(team)
41
- end
40
+ def can_read?(team)
41
+ scopes.include?("read") &&
42
+ user.teams.include?(team)
43
+ end
42
44
 
43
- def can_write?(team)
44
- scopes.include?("write_teams") && (
45
- user.managed_teams.include?(team) ||
46
- user.owned_teams.include?(team)
47
- )
48
- end
45
+ def can_write?(team)
46
+ scopes.include?("write_teams") && (
47
+ user.managed_teams.include?(team) ||
48
+ user.owned_teams.include?(team)
49
+ )
50
+ end
51
+ end
52
+
53
+ scopes = ["read", "write_teams"] # Optional oauth scopes
54
+ auth = TeamAuthorizer.new(current_user, :scopes => scopes)
55
+ team = Team.find(params[:id])
56
+
57
+ # Scopes are optional, just don't pass any in if you don't want them.
58
+
59
+ auth.can?(:read, team) # or auth.can_read?(team)
60
+ auth.can?(:write, team) # or auth.can_write?(team)
61
+
62
+ # When using the #can? syntax, you can pass in an enumerable
63
+ # #can? will then tell you if the user is able to perform the
64
+ # action on all of the objects. `true` they can, `false` they
65
+ # cannot.
66
+
67
+ teams = Team.where(:sport => :hockey)
68
+
69
+ auth.can?(:read, teams)
70
+ auth.can?(:write, teams)
71
+ ```
72
+
73
+ ### Middleware
74
+
75
+ ```ruby
76
+ require "roadblock"
77
+
78
+ class TeamAuthorizer
79
+ include Roadblock.authorizer
80
+
81
+ def can_read?(team)
82
+ user.teams.include?(team)
83
+ end
84
+ end
85
+
86
+ class AdminAuthorizer
87
+ include Roadblock.authorizer
88
+
89
+ def can?(action, object)
90
+ if user.is_admin?
91
+ true
92
+ else
93
+ yield(object)
49
94
  end
95
+ end
96
+ end
97
+
98
+ stack = Roadblock::Stack.new(current_user, :scopes => scopes)
99
+ stack.add(AdminAuthorizer, TeamAuthorizer)
100
+
101
+ # Then use stack just as you would a standalone authorizer
50
102
 
51
- scopes = ["read", "write_teams"] # Optional oauth scopes
52
- auth = TeamAuthorizer.new(current_user, :scopes => scopes)
53
- team = Team.find(params[:id])
103
+ stack.can?(:read, team) # or stack.can_read?(team)
104
+ stack.can?(:read, teams) # or stack.can_read?(teams)
105
+ ```
54
106
 
55
- auth.can?(:read, team)
56
- auth.can?(:write, team)
57
-
58
107
  ## Roadmap
59
108
 
60
109
  - Add optional faliure messages
61
110
 
62
111
  ## Contributing
63
112
 
64
- 1. Fork it
113
+ 1. Fork it ( http://github.com/teamsnap/roadblock/fork )
65
114
  2. Create your feature branch (`git checkout -b my-new-feature`)
66
115
  3. Commit your changes (`git commit -am 'Add some feature'`)
67
116
  4. Push to the branch (`git push origin my-new-feature`)
@@ -1,4 +1,5 @@
1
1
  require_relative "roadblock/authorizer"
2
+ require_relative "roadblock/stack"
2
3
  require_relative "roadblock/version"
3
4
 
4
5
  module Roadblock
@@ -5,11 +5,15 @@ module Roadblock
5
5
  self.scopes = scopes
6
6
  end
7
7
 
8
- def can?(action, objects)
9
- objects = [*objects]
10
- objects
11
- .map { |object| send("can_#{action}?", object) }
12
- .all?
8
+ def can?(action, object)
9
+ if block_given?
10
+ yield(object)
11
+ else
12
+ objects = [*object]
13
+ objects
14
+ .map { |obj| send("can_#{action}?", obj) }
15
+ .all?
16
+ end
13
17
  end
14
18
 
15
19
  private
@@ -0,0 +1,51 @@
1
+ module Roadblock
2
+ class Stack
3
+ NULL_AUTHORIZER_PROC = lambda { |object| false }
4
+
5
+ def initialize(user, scopes: [])
6
+ self.user = user
7
+ self.scopes = scopes
8
+ self.authorizers = []
9
+ end
10
+
11
+ def add(*auths)
12
+ self.authorizers = authorizers + auths
13
+ end
14
+
15
+ def can?(action, objects)
16
+ objects = [*objects]
17
+
18
+ stack = authorizers.reverse.inject(NULL_AUTHORIZER_PROC) do |authorizer_proc, authorizer_klass|
19
+ lambda { |obj|
20
+ authorizer = authorizer_klass.new(user, scopes: scopes)
21
+
22
+ authorizer.can?(action, obj) do |inner_obj|
23
+ authorizer.send("can_#{action}?", inner_obj, &authorizer_proc)
24
+ end
25
+ }
26
+ end
27
+
28
+ objects
29
+ .map { |object| stack.call(object) }
30
+ .all?
31
+ end
32
+
33
+ def respond_to?(method)
34
+ /can_(.*?)\?/.match(method) ? true : false
35
+ end
36
+
37
+ def method_missing(method, *args)
38
+ match = /can_(.*?)\?/.match(method)
39
+
40
+ if match
41
+ can?(match[1], *args)
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ attr_accessor :user, :scopes, :authorizers
50
+ end
51
+ end
@@ -1,3 +1,3 @@
1
1
  module Roadblock
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -6,7 +6,7 @@ require 'roadblock/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "roadblock"
8
8
  spec.version = Roadblock::VERSION
9
- spec.authors = ["Shane Emmons"]
9
+ spec.authors = ["Shane Emmons", "Emily Dobervich"]
10
10
  spec.email = ["oss@teamsnap.com"]
11
11
  spec.description = <<DESC
12
12
  Roadblock provides a simple interface for checking if a ruby object has the
@@ -14,7 +14,23 @@ class TestAuthorizer
14
14
  end
15
15
  end
16
16
 
17
- describe Roadblock do
17
+ class ForwardingAuthorizer
18
+ include Roadblock.authorizer
19
+
20
+ def can_peek?(object)
21
+ yield(object)
22
+ end
23
+ end
24
+
25
+ class ReturningMiddleware
26
+ include Roadblock.authorizer
27
+
28
+ def can_peek?(object)
29
+ false
30
+ end
31
+ end
32
+
33
+ describe Roadblock::Authorizer do
18
34
  subject { TestAuthorizer }
19
35
 
20
36
  describe "#can?" do
@@ -23,7 +39,7 @@ describe Roadblock do
23
39
  scopes = ["peekable"]
24
40
  auth = subject.new(user, :scopes => scopes)
25
41
 
26
- expect(auth.can?(:peek, user)).to eq(true)
42
+ expect(auth.can?(:peek, user)).to be true
27
43
  end
28
44
 
29
45
  it "accepts multiple objects" do
@@ -31,7 +47,7 @@ describe Roadblock do
31
47
  scopes = ["peekable"]
32
48
  auth = subject.new(user, :scopes => scopes)
33
49
 
34
- expect(auth.can?(:peek, [user, user])).to eq(true)
50
+ expect(auth.can?(:peek, [user, user])).to be true
35
51
  end
36
52
 
37
53
  it "requires all objects to pass authorization" do
@@ -50,3 +66,88 @@ describe Roadblock do
50
66
  end
51
67
  end
52
68
  end
69
+
70
+ describe Roadblock::Stack do
71
+ describe "#can?" do
72
+ it "correctly forwards call" do
73
+ user = double
74
+ scopes = ["peekable"]
75
+
76
+ stack = Roadblock::Stack.new(user, :scopes => scopes)
77
+ stack.add(TestAuthorizer)
78
+
79
+ expect(stack.can?(:peek, user)).to be(true)
80
+ end
81
+
82
+ it "correctly forwards call with alternate call style" do
83
+ user = double
84
+ scopes = ["peekable"]
85
+
86
+ stack = Roadblock::Stack.new(user, :scopes => scopes)
87
+ stack.add(TestAuthorizer)
88
+
89
+ expect(stack.can_peek?(user)).to be(true)
90
+ end
91
+
92
+ it "correctly forwards call through middleware" do
93
+ user = double
94
+ scopes = ["peekable"]
95
+
96
+ stack = Roadblock::Stack.new(user, :scopes => scopes)
97
+ stack.add(ForwardingAuthorizer)
98
+ stack.add(TestAuthorizer)
99
+
100
+ expect(stack.can?(:peek, user)).to be(true)
101
+ end
102
+
103
+ it "correctly handles middleware returning early" do
104
+ user = double
105
+ scopes = ["peekable"]
106
+
107
+ stack = Roadblock::Stack.new(user, :scopes => scopes)
108
+ stack.add(ReturningMiddleware)
109
+ stack.add(TestAuthorizer)
110
+
111
+ expect(stack.can?(:peek, user)).to be(false)
112
+ end
113
+
114
+ it "accepts multiple objects" do
115
+ user = double
116
+ scopes = ["peekable"]
117
+
118
+ stack = Roadblock::Stack.new(user, :scopes => scopes)
119
+ stack.add(TestAuthorizer)
120
+
121
+ expect(stack.can?(:peek, [user, user])).to be true
122
+ end
123
+
124
+ it "accepts multiple objects with alternate call style" do
125
+ user = double
126
+ scopes = ["peekable"]
127
+
128
+ stack = Roadblock::Stack.new(user, :scopes => scopes)
129
+ stack.add(TestAuthorizer)
130
+
131
+ expect(stack.can_peek?([user, user])).to be true
132
+ end
133
+
134
+ it "requires all objects to pass authorization" do
135
+ user = double
136
+ scopes = ["peekable"]
137
+
138
+ stack = Roadblock::Stack.new(user, :scopes => scopes)
139
+ stack.add(TestAuthorizer)
140
+
141
+ expect(stack.can?(:peek, [user, nil])).to eq(false)
142
+ end
143
+
144
+ it "doesn't require scopes to be used" do
145
+ user = double
146
+
147
+ stack = Roadblock::Stack.new(user)
148
+ stack.add(TestAuthorizer)
149
+
150
+ expect(stack.can?(:wink, user)).to be(true)
151
+ end
152
+ end
153
+ end
metadata CHANGED
@@ -1,55 +1,56 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roadblock
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane Emmons
8
+ - Emily Dobervich
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-12-22 00:00:00.000000000 Z
12
+ date: 2014-02-14 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
- - - ~>
18
+ - - "~>"
18
19
  - !ruby/object:Gem::Version
19
20
  version: '1.3'
20
21
  type: :development
21
22
  prerelease: false
22
23
  version_requirements: !ruby/object:Gem::Requirement
23
24
  requirements:
24
- - - ~>
25
+ - - "~>"
25
26
  - !ruby/object:Gem::Version
26
27
  version: '1.3'
27
28
  - !ruby/object:Gem::Dependency
28
29
  name: rake
29
30
  requirement: !ruby/object:Gem::Requirement
30
31
  requirements:
31
- - - '>='
32
+ - - ">="
32
33
  - !ruby/object:Gem::Version
33
34
  version: '0'
34
35
  type: :development
35
36
  prerelease: false
36
37
  version_requirements: !ruby/object:Gem::Requirement
37
38
  requirements:
38
- - - '>='
39
+ - - ">="
39
40
  - !ruby/object:Gem::Version
40
41
  version: '0'
41
42
  - !ruby/object:Gem::Dependency
42
43
  name: rspec
43
44
  requirement: !ruby/object:Gem::Requirement
44
45
  requirements:
45
- - - ~>
46
+ - - "~>"
46
47
  - !ruby/object:Gem::Version
47
48
  version: 3.0.0.beta1
48
49
  type: :development
49
50
  prerelease: false
50
51
  version_requirements: !ruby/object:Gem::Requirement
51
52
  requirements:
52
- - - ~>
53
+ - - "~>"
53
54
  - !ruby/object:Gem::Version
54
55
  version: 3.0.0.beta1
55
56
  description: |
@@ -63,13 +64,14 @@ executables: []
63
64
  extensions: []
64
65
  extra_rdoc_files: []
65
66
  files:
66
- - .gitignore
67
+ - ".gitignore"
67
68
  - Gemfile
68
69
  - LICENSE.txt
69
70
  - README.md
70
71
  - Rakefile
71
72
  - lib/roadblock.rb
72
73
  - lib/roadblock/authorizer.rb
74
+ - lib/roadblock/stack.rb
73
75
  - lib/roadblock/version.rb
74
76
  - roadblock.gemspec
75
77
  - spec/roadblock_spec.rb
@@ -84,20 +86,21 @@ require_paths:
84
86
  - lib
85
87
  required_ruby_version: !ruby/object:Gem::Requirement
86
88
  requirements:
87
- - - '>='
89
+ - - ">="
88
90
  - !ruby/object:Gem::Version
89
91
  version: '0'
90
92
  required_rubygems_version: !ruby/object:Gem::Requirement
91
93
  requirements:
92
- - - '>='
94
+ - - ">="
93
95
  - !ruby/object:Gem::Version
94
96
  version: '0'
95
97
  requirements: []
96
98
  rubyforge_project:
97
- rubygems_version: 2.0.14
99
+ rubygems_version: 2.2.0
98
100
  signing_key:
99
101
  specification_version: 4
100
102
  summary: A simple authorization library.
101
103
  test_files:
102
104
  - spec/roadblock_spec.rb
103
105
  - spec/spec_helper.rb
106
+ has_rdoc: