wine_bouncer 0.2.2 → 0.3.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: c16d471f3bf4c00fc8aabb999f0225071ba3e199
4
- data.tar.gz: 2e6c872a709af181da9c42d19394da0e01a0db29
3
+ metadata.gz: e08374333bc4fe68a78e7f177b74179d550ddba2
4
+ data.tar.gz: e9e635c0d5f531108e3bf39dbfb847a4eab71ddf
5
5
  SHA512:
6
- metadata.gz: aeee07c8f60e126290e75e311abc94ed2086640dd39a0f2e128775e83b6168d31d5e064e0f826f992409071cb5bb2f60624509d1ba14fb7f04b68e8947d041f8
7
- data.tar.gz: 47c5faa48f155eac4747f3f26caee1ef877157913f897e98821032311f14231b0d7634ceaa23a47da2402eaec02d9c8cae3b6ee82ae5f2437e9c2fd7ec656899
6
+ metadata.gz: 3fbecb2d1db85e84b23f059d5a73bd9313c8cb73c259797bc7e78bd06694ed80ac3dee5e295b1fc15373bcdc39587f1a470cade1e9533698ba8c261a450e680e
7
+ data.tar.gz: b3060def1967e6361ca3c8a174c97face42dacd6821288dc2206cb7b678d9a8d4aa0f5732a63a8dd913052ec9d832d395f8940f2200106bae34e7801b10db098
data/.travis.yml CHANGED
@@ -6,19 +6,16 @@ rvm:
6
6
  - 1.9.3
7
7
  - 2.0
8
8
  - 2.1
9
- - 2.2
10
9
  env:
11
- - rails=3.2.18 grape=0.8.0
12
- - rails=4.1.1 grape=0.8.0
13
- - rails=3.2.18 grape=0.9.0 doorkeeper=1.4.1
14
10
  - rails=3.2.18 grape=0.9.0
15
- - rails=4.1.1 grape=0.9.0 doorkeeper=1.4.1
16
11
  - rails=4.1.1 grape=0.9.0
12
+ - rails=3.2.18 grape=0.10.0
13
+ - rails=4.1.1 grape=0.10.0 doorkeeper=1.4.1
14
+ - rails=4.1.1 grape=0.10.0
15
+ - rails=4.1.1 grape=0.11.0 doorkeeper=2.1.1
17
16
  matrix:
18
17
  allow_failures:
19
- - env: rails=3.2.18 grape=0.8.0
20
- - env: rails=3.2.18 grape=0.9.0 doorkeeper=1.4.1
21
- - env: rails=3.2.18 grape=0.9.0
18
+ - rvm: 1.9.3
22
19
  addons:
23
20
  code_climate:
24
21
  repo_token: ab1b6ce5f973da033f80ae2e99fadbb32b2f9c37892703956d8ef954c8e8134e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  Changelog
2
2
  =========
3
+
4
+ ## 0.3.0
5
+ [#21](https://github.com/antek-drzewiecki/wine_bouncer/pull/21): Added an Easy DSL for WineBouncer. Thanks @masarakki .
6
+ [#23](https://github.com/antek-drzewiecki/wine_bouncer/pull/23): Added support for Doorkeeper 2.1.1 and refactored strategies.
7
+
3
8
  ## 0.2.2
4
9
  *[#17](https://github.com/antek-drzewiecki/wine_bouncer/pull/17): Added a new protected strategy. Thanks @whatasunnyday .
5
10
 
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- ENV['grape'] ||= '0.9.0'
3
+ ENV['grape'] ||= '0.10.0'
4
4
  ENV['rails'] ||= '4.1.1'
5
5
  ENV['doorkeeper'] ||= '2.0.1'
6
6
 
data/README.md CHANGED
@@ -25,8 +25,8 @@ Table of Contents
25
25
 
26
26
  ## Requirements
27
27
  - Ruby > 1.9.3
28
- - Doorkeeper > 1.4.0 and =< 2.0.1
29
- - Grape > 0.8
28
+ - Doorkeeper > 1.4.0 and =< 2.1.1
29
+ - Grape > 0.8 and =< 0.11.0
30
30
 
31
31
  ## Installation
32
32
 
@@ -44,6 +44,7 @@ bundle
44
44
 
45
45
  ## Upgrading
46
46
  When upgrading from a previous version, see [UPGRADING](UPGRADING.md). You might also be interested at the [CHANGELOG](CHANGELOG.md).
47
+ Note this is the last version that will support Grape 0.8 and 0.9. Grape 0.10 will be the next minimum Grape version.
47
48
 
48
49
  ## Usage
49
50
  WineBouncer is a custom Grape Middleware used for Authentication and Authorization. We assume you have a Grape API mounted in your Rails application together with Doorkeeper.
@@ -54,6 +55,7 @@ To get started with WineBouncer, run the configuration initializer:
54
55
  $ rails g wine_bouncer:initializer
55
56
  ```
56
57
 
58
+
57
59
  This creates a rails initializer in your Rails app at `config/initializers/wine_bouncer.rb` with the following configuration:
58
60
 
59
61
  ``` ruby
@@ -77,39 +79,37 @@ class Api < Grape::API
77
79
  end
78
80
  ```
79
81
 
80
- WineBouncer relies on Grape's endpoint method description to define if an endpoint method should be protected.
81
- It comes with authorization strategies that allow a custom format for your authorization definition. Pick an authentication strategy to get started.
82
- Currently the following strategies are included:
83
-
84
- ### Authentication strategies
85
-
86
- #### Default
87
- The default strategy uses the `auth:` key in the description options hash to define API method authentication. It accepts an hash with options. Currently the only option is for scopes which is an array of scopes for authorization.
88
- WineBouncer uses the default Doorkeeper behaviour for scopes.
82
+ ### Easy DSL
89
83
 
90
- Example:
84
+ WineBouncer comes with an easy DSL and relies on Grape's DSL extentions to define if an endpoint method should be protected.
85
+ You can protect an endpoint by calling `oauth2` method with optional scopes in front of the endpoint definition.
91
86
 
92
87
  ``` ruby
93
88
  class MyAwesomeAPI < Grape::API
94
- desc 'protected method with required public scope',
95
- auth: { scopes: ['public'] }
89
+ desc 'protected method with required public scope'
90
+ oauth2 'public'
96
91
  get '/protected' do
97
92
  { hello: 'world' }
98
93
  end
99
94
 
95
+ oauth2 'public'
96
+ get '/with_no_description' do
97
+ { hello: 'undescribed world' }
98
+ end
99
+
100
100
  desc 'Unprotected method'
101
101
  get '/unprotected' do
102
102
  { hello: 'unprotected world' }
103
103
  end
104
104
 
105
- desc 'This method needs the public or private scope.',
106
- auth: { scopes: [ 'public', 'private' ] }
105
+ desc 'This method needs the public or private scope.'
106
+ oauth2 'public', 'write'
107
107
  get '/method' do
108
108
  { hello: 'public or private user.' }
109
109
  end
110
110
 
111
111
  desc 'This method uses Doorkeepers default scopes.',
112
- auth: { scopes: [] }
112
+ oauth2
113
113
  get '/protected_with_default_scope' do
114
114
  { hello: 'protected unscoped world' }
115
115
  end
@@ -120,10 +120,15 @@ Example:
120
120
  format :json
121
121
  use ::WineBouncer::OAuth2
122
122
  mount MyAwesomeAPI
123
- add_swagger_documentation
124
123
  end
125
124
  ```
126
125
 
126
+ ### Authentication strategies
127
+
128
+ Behaviour of the authentication can be customized by selecting an authentication strategy. The following authentication strategies are provided in the gem.
129
+
130
+ #### Default
131
+ The default strategy only authenticates endpoints which are annotated by the `oauth2` method. Un-annotated endpoints still can be accessed without authentication.
127
132
 
128
133
  #### Swagger
129
134
 
@@ -146,98 +151,32 @@ WineBouncer.configure do |config|
146
151
  end
147
152
  ```
148
153
 
149
- Then you can start documenting and protecting your API like the example below.
154
+ Then you can start protecting your API like the example below.
150
155
 
151
156
  ``` ruby
152
- class MyAwesomeAPI < Grape::API
153
- desc 'protected method with required public scope',
154
- authorizations: { oauth2: [{ scope: 'public' }] }
155
- get '/protected' do
156
- { hello: 'world' }
157
- end
158
-
159
- desc 'Unprotected method'
160
- get '/unprotected' do
161
- { hello: 'unprotected world' }
162
- end
163
-
164
- desc 'This method uses Doorkeepers default scopes.',
165
- authorizations: { oauth2: [] }
166
- get '/protected_with_default_scope' do
167
- { hello: 'protected unscoped world' }
168
- end
169
-
170
- desc 'It even works with other options!',
171
- authorizations: { oauth2: [] },
172
- :entity => Api::Entities::Response,
173
- http_codes: [
174
- [200, 'OK', Api::Entities::Response],
175
- [401, 'Unauthorized', Api::Entities::Error],
176
- [403, 'Forbidden', Api::Entities::Error]
177
- ],
178
- :notes => <<-NOTE
179
- Marked down notes!
180
- NOTE
181
- get '/extended_api' do
182
- { hello: 'Awesome world' }
183
- end
184
- end
185
-
186
- class Api < Grape::API
187
- default_format :json
188
- format :json
189
- use ::WineBouncer::OAuth2
190
- mount MyAwesomeAPI
191
- add_swagger_documentation
157
+ desc 'This method needs the public or private scope.',
158
+ :entity => Api::Entities::Response,
159
+ http_codes: [
160
+ [200, 'OK', Api::Entities::Response],
161
+ [401, 'Unauthorized', Api::Entities::Error],
162
+ [403, 'Forbidden', Api::Entities::Error]
163
+ ],
164
+ :notes => <<-NOTE
165
+ Marked down notes!
166
+ NOTE
167
+ oauth2 'public', 'write'
168
+ get '/method' do
169
+ { hello: 'public or private user.' }
192
170
  end
193
171
  ```
194
172
 
195
- The Swagger strategy uses the `authorizations: { oauth2: [] }` in the method description syntax to define API method authentication and authorization.
196
- It defaults assumes when no description is given that no authorization should be used like the `/unprotected` method.
197
- When the authentication syntax is mentioned in the method description, the method will be protected.
198
- You can use the default scopes of Doorkeeper by just adding `authorizations: { oauth2: [] }` or state your own scopes with `authorizations: { oauth2: [ { scope: 'scope1' }, { scope: 'scope2' }, ... ] }`.
173
+ The Swagger strategy uses scopes and injects them in the authorizations description for external application to be read.
199
174
 
200
175
  #### Protected
201
176
 
202
- The protected strategy is very similiar to the default strategy except any public end point must explicitly set. To make an end point public, use `auth: false`.
177
+ The protected strategy is very similar to the default strategy except any public end point must explicitly set. To make an end point public, use `oauth2: false`.
203
178
  If the authorization is not set, the end point is assumed to be protected and Doorkeeper's default scopes are used.
204
179
 
205
- Example:
206
-
207
- ``` ruby
208
- class MyAwesomeAPI < Grape::API
209
- desc 'protected method with required public scope',
210
- auth: { scopes: ['public'] }
211
- get '/protected' do
212
- { hello: 'world' }
213
- end
214
-
215
- desc 'Unprotected method'
216
- get '/unprotected', auth: false do
217
- { hello: 'unprotected world' }
218
- end
219
-
220
- desc 'This method needs the public or private scope.',
221
- auth: [:public, :private]
222
- # Doorkeeper's default scopes are [:public, :private] so auth can be omitted. See next example.
223
- get '/method' do
224
- { hello: 'public or private user.' }
225
- end
226
-
227
- desc 'This method uses Doorkeepers default scopes.'
228
- get '/protected_with_default_scope' do
229
- { hello: 'protected unscoped world' }
230
- end
231
- end
232
-
233
- class Api < Grape::API
234
- default_format :json
235
- format :json
236
- use ::WineBouncer::OAuth2
237
- mount MyAwesomeAPI
238
- add_swagger_documentation
239
- end
240
- ```
241
180
 
242
181
  ### Token information
243
182
 
data/UPGRADING.md CHANGED
@@ -1,6 +1,33 @@
1
1
  Upgrading WineBouncer
2
2
  =====================
3
3
 
4
+ ## Upgrading to >= 0.3.0
5
+
6
+ An new DSL has been introduced to WineBouncer. This DSL will become the preferred way to authorize your endpoints.
7
+ The authentication trough endpoint description will become deprecated in the future version.
8
+
9
+ The old way to authorize your endpoints:
10
+
11
+ ```
12
+ desc 'protected method with required public and private scope',
13
+ auth: { scopes: ['public','private'] }
14
+ get '/protected' do
15
+ { hello: 'world' }
16
+ end
17
+ ```
18
+
19
+ You may now write:
20
+ ```
21
+ desc 'protected method with required public and private scope'
22
+ oauth2 'public', 'private'
23
+ get '/protected' do
24
+ { hello: 'world' }
25
+ end
26
+ ```
27
+
28
+ And even remove the description.
29
+
30
+ Note this is the last version that will support Grape 0.8 and 0.9. Grape 0.10 will be the next minimum Grape version.
4
31
 
5
32
  ### Upgrading to >= 0.2.0
6
33
 
@@ -19,4 +46,4 @@ end
19
46
  The required Grape version is now from 0.8 and above.
20
47
 
21
48
  ------
22
- From version 0.1.0 upgrade instructions are given.
49
+ From version 0.1.0 upgrade instructions are given.
data/lib/wine_bouncer.rb CHANGED
@@ -4,7 +4,9 @@ require 'wine_bouncer/version'
4
4
  require 'wine_bouncer/errors'
5
5
  require 'wine_bouncer/configuration'
6
6
  require 'wine_bouncer/oauth2'
7
+ require 'wine_bouncer/base_strategy'
7
8
  require 'wine_bouncer/auth_methods/auth_methods'
9
+ require 'wine_bouncer/extension'
8
10
 
9
11
  module WineBouncer
10
12
  end
@@ -1,27 +1,25 @@
1
1
  module WineBouncer
2
2
  module AuthStrategies
3
- class Default
3
+ class Default < ::WineBouncer::BaseStrategy
4
4
 
5
- def endpoint_protected?(context)
6
- has_authorizations?(context)
5
+ def endpoint_protected?
6
+ !!endpoint_authorizations
7
7
  end
8
8
 
9
- def has_auth_scopes?(context)
10
- has_authorizations?(context) && endpoint_authorizations(context).has_key?(:scopes) && !endpoint_authorizations(context)[:scopes].empty?
9
+ def has_auth_scopes?
10
+ !!endpoint_authorizations &&
11
+ endpoint_authorizations.has_key?(:scopes) &&
12
+ !endpoint_authorizations[:scopes].empty?
11
13
  end
12
14
 
13
- def auth_scopes(context)
14
- endpoint_authorizations(context)[:scopes].map(&:to_sym)
15
+ def auth_scopes
16
+ endpoint_authorizations[:scopes].map(&:to_sym)
15
17
  end
16
18
 
17
19
  private
18
20
 
19
- def has_authorizations?(context)
20
- !!endpoint_authorizations(context)
21
- end
22
-
23
- def endpoint_authorizations(context)
24
- context[:auth]
21
+ def endpoint_authorizations
22
+ api_context.options[:route_options][:auth]
25
23
  end
26
24
  end
27
25
  end
@@ -1,34 +1,34 @@
1
1
  module WineBouncer
2
2
  module AuthStrategies
3
- class Protected
3
+ class Protected < WineBouncer::BaseStrategy
4
4
 
5
- def endpoint_protected?(context)
6
- has_authorizations?(context)
5
+ def endpoint_protected?
6
+ has_authorizations?
7
7
  end
8
8
 
9
- def has_auth_scopes?(context)
10
- endpoint_authorizations(context) &&
11
- endpoint_authorizations(context).has_key?(:scopes) &&
12
- endpoint_authorizations(context)[:scopes].any?
9
+ def has_auth_scopes?
10
+ endpoint_authorizations &&
11
+ endpoint_authorizations.has_key?(:scopes) &&
12
+ endpoint_authorizations[:scopes].any?
13
13
  end
14
14
 
15
- def auth_scopes(context)
16
- endpoint_authorizations(context)[:scopes].map(&:to_sym)
15
+ def auth_scopes
16
+ endpoint_authorizations[:scopes].map(&:to_sym)
17
17
  end
18
18
 
19
19
  private
20
20
 
21
- def nil_authorizations?(context)
22
- endpoint_authorizations(context).nil?
21
+ def nil_authorizations?
22
+ endpoint_authorizations.nil?
23
23
  end
24
24
 
25
25
 
26
- def has_authorizations?(context)
27
- nil_authorizations?(context) || endpoint_authorizations(context)
26
+ def has_authorizations?
27
+ nil_authorizations? || endpoint_authorizations
28
28
  end
29
29
 
30
- def endpoint_authorizations(context)
31
- context[:auth]
30
+ def endpoint_authorizations
31
+ api_context.options[:route_options][:auth]
32
32
  end
33
33
  end
34
34
  end
@@ -1,33 +1,32 @@
1
1
  module WineBouncer
2
2
  module AuthStrategies
3
- class Swagger
3
+ class Swagger < ::WineBouncer::BaseStrategy
4
4
 
5
- def endpoint_protected?(context)
6
- has_authorizations?(context) && !!authorization_type_oauth2(context)
5
+ def endpoint_protected?
6
+ has_authorizations? && !!authorization_type_oauth2
7
7
  end
8
8
 
9
- def has_auth_scopes?(context)
10
- endpoint_protected?(context) && !authorization_type_oauth2(context).empty?
9
+ def has_auth_scopes?
10
+ endpoint_protected? && !authorization_type_oauth2.empty?
11
11
  end
12
12
 
13
- def auth_scopes(context)
14
- authorization_type_oauth2(context).map{ |hash| hash[:scope].to_sym }
13
+ def auth_scopes
14
+ authorization_type_oauth2.map { |hash| hash[:scope].to_sym }
15
15
  end
16
16
 
17
17
  private
18
18
 
19
- def has_authorizations?(context)
20
- !!endpoint_authorizations(context)
19
+ def has_authorizations?
20
+ !!endpoint_authorizations
21
21
  end
22
22
 
23
- def endpoint_authorizations(context)
24
- context[:authorizations]
23
+ def endpoint_authorizations
24
+ api_context.options[:route_options][:authorizations]
25
25
  end
26
26
 
27
- def authorization_type_oauth2(context)
28
- endpoint_authorizations(context)[:oauth2]
27
+ def authorization_type_oauth2
28
+ endpoint_authorizations[:oauth2]
29
29
  end
30
-
31
30
  end
32
31
  end
33
32
  end
@@ -0,0 +1,5 @@
1
+ module WineBouncer
2
+ class BaseStrategy
3
+ attr_accessor :api_context
4
+ end
5
+ end
@@ -0,0 +1,20 @@
1
+ module WineBouncer
2
+ module Extension
3
+ def oauth2(*scopes)
4
+ scopes = Array(scopes)
5
+ description = if respond_to?(:route_setting) # >= grape-0.10.0
6
+ route_setting(:description)
7
+ else
8
+ @last_description ||= {}
9
+ end
10
+ # case WineBouncer.configuration.auth_strategy
11
+ # when :default
12
+ description[:auth] = { scope: scopes }
13
+ # when :swagger
14
+ description[:authorizations] = { oauth2: scopes.map{|x| {scope: x}} }
15
+ # end
16
+ end
17
+
18
+ Grape::API.extend self
19
+ end
20
+ end
@@ -44,22 +44,11 @@ module WineBouncer
44
44
  # Authorization control.
45
45
  ############
46
46
 
47
- ###
48
- # Returns true if the Api endpoint, method is configured as an protected method, false otherwise.
49
- ###
50
- def valid_route_context?
51
- context && context.options && context.options[:route_options]
52
- end
53
-
54
- def route_context
55
- context.options[:route_options]
56
- end
57
-
58
47
  ###
59
48
  # returns true if the endpoint is protected, otherwise false
60
49
  ###
61
50
  def endpoint_protected?
62
- auth_strategy.endpoint_protected?(route_context)
51
+ auth_strategy.endpoint_protected?
63
52
  end
64
53
 
65
54
  ###
@@ -67,8 +56,8 @@ module WineBouncer
67
56
  # [ nil ] if none, otherwise an array of [ :scopes ]
68
57
  ###
69
58
  def auth_scopes
70
- return *nil unless auth_strategy.has_auth_scopes?(route_context)
71
- auth_strategy.auth_scopes(route_context)
59
+ return *nil unless auth_strategy.has_auth_scopes?
60
+ auth_strategy.auth_scopes
72
61
  end
73
62
 
74
63
  ###
@@ -98,6 +87,7 @@ module WineBouncer
98
87
  ###
99
88
  def before
100
89
  set_auth_strategy(WineBouncer.configuration.auth_strategy)
90
+ auth_strategy.api_context = context
101
91
  #extend the context with auth methods.
102
92
  context.extend(WineBouncer::AuthMethods)
103
93
  context.protected_endpoint = endpoint_protected?
@@ -1,3 +1,3 @@
1
1
  module WineBouncer
2
- VERSION = '0.2.2'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -24,10 +24,21 @@ module Api
24
24
  { hello: resource_owner.name }
25
25
  end
26
26
 
27
- desc 'This method uses Doorkeepers default scopes', auth: { }
27
+ desc 'This method uses Doorkeepers default scopes', auth: {}
28
28
  get '/protected_without_scope' do
29
29
  { hello: 'protected unscoped world' }
30
30
  end
31
+
32
+ desc 'oauth2_dsl'
33
+ oauth2 'public'
34
+ get '/oauth2_dsl' do
35
+ { hello: 'oauth2_dsl' }
36
+ end
37
+
38
+ get '/not_described_world' do
39
+ { hello: 'non described world' }
40
+ end
41
+
31
42
  end
32
43
 
33
44
  class DefaultApiUnderTest < Grape::API
@@ -36,5 +47,5 @@ module Api
36
47
  use ::WineBouncer::OAuth2
37
48
  mount MountedDefaultApiUnderTest
38
49
  end
39
-
40
50
  end
51
+
@@ -28,6 +28,16 @@ module Api
28
28
  get '/protected_without_scope' do
29
29
  { hello: 'protected unscoped world' }
30
30
  end
31
+
32
+ desc 'oauth2_dsl'
33
+ oauth2 'public'
34
+ get '/oauth2_dsl' do
35
+ { hello: 'oauth2_dsl' }
36
+ end
37
+
38
+ get '/not_described_world' do
39
+ { hello: 'non described world' }
40
+ end
31
41
  end
32
42
 
33
43
  class ProtectedApiUnderTest < Grape::API
@@ -28,6 +28,16 @@ module Api
28
28
  get '/protected_without_scope' do
29
29
  { hello: 'protected unscoped world' }
30
30
  end
31
+
32
+ desc 'oauth2 dsl'
33
+ oauth2 'public'
34
+ get '/oauth2_dsl' do
35
+ { hello: 'oauth2_dsl' }
36
+ end
37
+
38
+ get '/not_described_world' do
39
+ { hello: 'non described world' }
40
+ end
31
41
  end
32
42
 
33
43
  class SwaggerApiUnderTest < Grape::API
@@ -80,6 +80,33 @@ describe Api::MountedDefaultApiUnderTest, type: :api do
80
80
  end
81
81
  end
82
82
 
83
+ context 'oauth2_dsl' do
84
+ it 'allows to call an protected endpoint without scopes' do
85
+ get '/default_api/oauth2_dsl', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
86
+
87
+ expect(last_response.status).to eq(200)
88
+ json = JSON.parse(last_response.body)
89
+ expect(json).to have_key('hello')
90
+ expect(json['hello']).to eq('oauth2_dsl')
91
+
92
+ end
93
+
94
+ it 'raises an error when an protected endpoint without scopes is called without token ' do
95
+ expect { get '/default_api/oauth2_dsl' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
96
+ end
97
+ end
98
+
99
+
100
+ context 'not_described_world' do
101
+ it 'allows to call an endpoint without description' do
102
+ get '/default_api/not_described_world'
103
+ expect(last_response.status).to eq(200)
104
+ json = JSON.parse(last_response.body)
105
+ expect(json).to have_key('hello')
106
+ expect(json['hello']).to eq('non described world')
107
+ end
108
+ end
109
+
83
110
  context 'resource_owner' do
84
111
  it 'is available in the endpoint' do
85
112
  get '/default_api/protected_user', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
@@ -91,5 +118,4 @@ describe Api::MountedDefaultApiUnderTest, type: :api do
91
118
  expect(json['hello']).to eq(user.name)
92
119
  end
93
120
  end
94
-
95
121
  end
@@ -83,6 +83,36 @@ describe Api::MountedProtectedApiUnderTest, type: :api do
83
83
  end
84
84
  end
85
85
 
86
+ context 'oauth2_dsl' do
87
+ it 'allows to call an protected endpoint without scopes' do
88
+ get '/protected_api/oauth2_dsl', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
89
+
90
+ expect(last_response.status).to eq(200)
91
+ json = JSON.parse(last_response.body)
92
+ expect(json).to have_key('hello')
93
+ expect(json['hello']).to eq('oauth2_dsl')
94
+
95
+ end
96
+
97
+ it 'raises an error when an protected endpoint without scopes is called without token ' do
98
+ expect { get '/protected_api/oauth2_dsl' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
99
+ end
100
+ end
101
+
102
+ context 'not_described_world' do
103
+ it 'authentication is required for a non described endpoint' do
104
+ get '/protected_api/not_described_world', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
105
+ expect(last_response.status).to eq(200)
106
+ json = JSON.parse(last_response.body)
107
+ expect(json).to have_key('hello')
108
+ expect(json['hello']).to eq('non described world')
109
+ end
110
+
111
+ it 'raises an error when an protected endpoint without scopes is called without token ' do
112
+ expect { get '/protected_api/not_described_world' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
113
+ end
114
+ end
115
+
86
116
  context 'resource_owner' do
87
117
  it 'is available in the endpoint' do
88
118
  get '/protected_api/protected_user', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
@@ -82,6 +82,32 @@ describe Api::MountedSwaggerApiUnderTest, type: :api do
82
82
  end
83
83
  end
84
84
 
85
+ context 'oauth2_dsl' do
86
+ it 'allows to call an protected endpoint without scopes' do
87
+ get '/swagger_api/oauth2_dsl', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
88
+
89
+ expect(last_response.status).to eq(200)
90
+ json = JSON.parse(last_response.body)
91
+ expect(json).to have_key('hello')
92
+ expect(json['hello']).to eq('oauth2_dsl')
93
+
94
+ end
95
+
96
+ it 'raises an error when an protected endpoint without scopes is called without token ' do
97
+ expect { get '/swagger_api/oauth2_dsl' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
98
+ end
99
+ end
100
+
101
+ context 'not_described_world' do
102
+ it 'allows to call an endpoint without description' do
103
+ get '/swagger_api/not_described_world'
104
+ expect(last_response.status).to eq(200)
105
+ json = JSON.parse(last_response.body)
106
+ expect(json).to have_key('hello')
107
+ expect(json['hello']).to eq('non described world')
108
+ end
109
+ end
110
+
85
111
  context 'resource_owner' do
86
112
  it 'is available in the endpoint' do
87
113
  get '/swagger_api/protected_user', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
@@ -6,61 +6,71 @@ describe ::WineBouncer::AuthStrategies::Default do
6
6
 
7
7
  let(:scopes) { %w(public private) }
8
8
  let(:scopes_hash) { { scopes: scopes } }
9
- let(:auth_context) { { auth: scopes_hash } }
9
+ let(:auth_context) { { route_options: { auth: scopes_hash } } }
10
10
 
11
11
 
12
12
  context 'endpoint_authorizations' do
13
13
  it 'returns the auth key of the authentication hash.' do
14
- expect(klass.send(:endpoint_authorizations, auth_context)).to eq(scopes_hash)
14
+ context_double = double()
15
+ allow(context_double).to receive(:options) { auth_context }
16
+ klass.api_context = context_double
17
+ expect(klass.send :endpoint_authorizations).to eq(scopes_hash)
15
18
  end
16
19
 
17
20
  it 'returns nil when the authentication key has no hash key.' do
18
- invalid_hash = { some: scopes_hash }
19
- expect(klass.send(:endpoint_authorizations, invalid_hash ) ).to eq(nil)
20
- end
21
- end
22
-
23
- context 'has_authorizations?' do
24
- it 'returns true when the context has the auth key.' do
25
- expect(klass.send(:has_authorizations?, auth_context)).to eq(true)
26
- end
27
-
28
- it 'returns false when the context has no auth key.' do
29
- invalid_hash = { some: scopes_hash }
30
- expect(klass.send(:has_authorizations?, invalid_hash ) ).to eq(false)
21
+ context_double = double()
22
+ allow(context_double).to receive(:options) { { route_options: { some: scopes_hash } } }
23
+ klass.api_context = context_double
24
+ expect(klass.send :endpoint_authorizations).to eq(nil)
31
25
  end
32
26
  end
33
27
 
34
28
  context 'endpoint_protected?' do
35
29
  it 'returns true when the context has the auth key.' do
36
- expect(klass.endpoint_protected?(auth_context)).to eq(true)
30
+ context_double = double()
31
+ allow(context_double).to receive(:options) { auth_context }
32
+ klass.api_context = context_double
33
+ expect(klass.endpoint_protected?).to eq(true)
37
34
  end
38
35
 
39
36
  it 'returns false when the context has no auth key.' do
40
- invalid_hash = { some: scopes }
41
- expect(klass.endpoint_protected?( invalid_hash ) ).to eq(false)
37
+ context_double = double()
38
+ allow(context_double).to receive(:options) { { route_options: { some: scopes_hash } } }
39
+ klass.api_context = context_double
40
+ expect(klass.endpoint_protected?).to eq(false)
42
41
  end
43
42
  end
44
43
 
45
44
  context 'has_auth_scopes?' do
46
45
  it 'returns true when the context has the auth key.' do
47
- expect(klass.has_auth_scopes?(auth_context)).to eq(true)
46
+ context_double = double()
47
+ allow(context_double).to receive(:options) { auth_context }
48
+ klass.api_context = context_double
49
+ expect(klass.has_auth_scopes?).to eq(true)
48
50
  end
49
51
 
50
52
  it 'returns false when the context has no auth key.' do
51
- invalid_hash = { some: scopes_hash }
52
- expect(klass.has_auth_scopes?(invalid_hash) ).to eq(false)
53
+ context_double = double()
54
+ allow(context_double).to receive(:options) { { route_options: { some: scopes_hash } } }
55
+ klass.api_context = context_double
56
+ expect(klass.has_auth_scopes?).to eq(false)
53
57
  end
54
58
 
55
59
  it 'returns false when the auth scopes contain a blank scope array' do
56
- blank_scopes = { auth: { scopes: [] } }
57
- expect(klass.has_auth_scopes?(blank_scopes) ).to eq(false)
60
+
61
+ context_double = double()
62
+ allow(context_double).to receive(:options) { { route_options: { auth: { scopes: [] } } } }
63
+ klass.api_context = context_double
64
+ expect(klass.has_auth_scopes?).to eq(false)
58
65
  end
59
66
  end
60
67
 
61
68
  context 'auth_scopes' do
62
69
  it 'returns an array of scopes' do
63
- expect(klass.auth_scopes(auth_context)).to eq([:public, :private])
70
+ context_double = double()
71
+ allow(context_double).to receive(:options) { auth_context }
72
+ klass.api_context = context_double
73
+ expect(klass.auth_scopes).to eq([:public, :private])
64
74
  end
65
75
  end
66
76
  end
@@ -0,0 +1,114 @@
1
+ require 'rails_helper'
2
+ require 'wine_bouncer/auth_strategies/swagger'
3
+
4
+ describe ::WineBouncer::AuthStrategies::Swagger do
5
+ subject(:klass) { ::WineBouncer::AuthStrategies::Swagger.new }
6
+
7
+ let(:scopes) { [{ scope: 'private', description: 'anything' },{ scope: 'public', description: 'anything' }] }
8
+ let(:scopes_map) { { oauth2: scopes } }
9
+ let(:auth_context) { { route_options: { authorizations: scopes_map } } }
10
+
11
+
12
+ context 'endpoint_authorizations' do
13
+ it 'returns the auth key of the authentication hash.' do
14
+ context_double = double()
15
+ allow(context_double).to receive(:options) { auth_context }
16
+ klass.api_context = context_double
17
+ expect(klass.send :endpoint_authorizations).to eq(scopes_map)
18
+ end
19
+
20
+ it 'returns nil when the authentication key has no hash key.' do
21
+ context_double = double()
22
+ allow(context_double).to receive(:options) { { route_options: { some: scopes_map } } }
23
+ klass.api_context = context_double
24
+ expect(klass.send :endpoint_authorizations).to eq(nil)
25
+ end
26
+ end
27
+
28
+ context 'has_authorizations?' do
29
+ it 'returns true when it has an authentication hash.' do
30
+ context_double = double()
31
+ allow(context_double).to receive(:options) { auth_context }
32
+ klass.api_context = context_double
33
+ expect(klass.send :has_authorizations?).to eq(true)
34
+ end
35
+
36
+ it 'returns false when there is no authentication key.' do
37
+ context_double = double()
38
+ allow(context_double).to receive(:options) { { route_options: { some: scopes_map } } }
39
+ klass.api_context = context_double
40
+ expect(klass.send :has_authorizations?).to eq(false)
41
+ end
42
+ end
43
+
44
+ context 'authorization_type_oauth2' do
45
+ it 'returns the scopes.' do
46
+ context_double = double()
47
+ allow(context_double).to receive(:options) { auth_context }
48
+ klass.api_context = context_double
49
+ expect(klass.send :authorization_type_oauth2).to eq(scopes)
50
+ end
51
+
52
+ it 'returns nil when there is no oauth2 key.' do
53
+ context_double = double()
54
+ allow(context_double).to receive(:options) { { route_options: { authorizations: { no_oauth: scopes } } } }
55
+ klass.api_context = context_double
56
+ expect(klass.send :authorization_type_oauth2).to eq(nil)
57
+ end
58
+ end
59
+
60
+ context 'endpoint_protected?' do
61
+ it 'returns true when the context has the auth key.' do
62
+ context_double = double()
63
+ allow(context_double).to receive(:options) { auth_context }
64
+ klass.api_context = context_double
65
+ expect(klass.endpoint_protected?).to eq(true)
66
+ end
67
+
68
+ it 'returns false when the context has no auth key.' do
69
+ context_double = double()
70
+ allow(context_double).to receive(:options) { { route_options: { some: scopes_map } } }
71
+ klass.api_context = context_double
72
+ expect(klass.endpoint_protected?).to eq(false)
73
+ end
74
+ end
75
+
76
+ context 'has_auth_scopes?' do
77
+ it 'returns true when the context has the auth key.' do
78
+ context_double = double()
79
+ allow(context_double).to receive(:options) { auth_context }
80
+ klass.api_context = context_double
81
+ expect(klass.has_auth_scopes?).to eq(true)
82
+ end
83
+
84
+ it 'returns false when the context has no authorizations key.' do
85
+ context_double = double()
86
+ allow(context_double).to receive(:options) { { route_options: { some: scopes_map } } }
87
+ klass.api_context = context_double
88
+ expect(klass.has_auth_scopes?).to eq(false)
89
+ end
90
+
91
+ it 'returns false when the context has no oauth2 key.' do
92
+ context_double = double()
93
+ allow(context_double).to receive(:options) { { route_options: { authorizations: { no_oauth: scopes } } } }
94
+ klass.api_context = context_double
95
+ expect(klass.has_auth_scopes?).to eq(false)
96
+ end
97
+
98
+ it 'returns false when the auth scopes contain a blank scope array' do
99
+ context_double = double()
100
+ allow(context_double).to receive(:options) { { route_options: { authorizations: { oauth2: [] } } } }
101
+ klass.api_context = context_double
102
+ expect(klass.has_auth_scopes?).to eq(false)
103
+ end
104
+ end
105
+
106
+ context 'auth_scopes' do
107
+ it 'returns an array of scopes' do
108
+ context_double = double()
109
+ allow(context_double).to receive(:options) { auth_context }
110
+ klass.api_context = context_double
111
+ expect(klass.auth_scopes).to eq([:private, :public])
112
+ end
113
+ end
114
+ end
data/wine_bouncer.gemspec CHANGED
@@ -17,8 +17,8 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
19
 
20
- spec.add_runtime_dependency 'grape', '~> 0.8'
21
- spec.add_runtime_dependency 'doorkeeper', '>= 1.4', '<= 2.0.1'
20
+ spec.add_runtime_dependency 'grape', '~> 0.8', '<= 0.11.0'
21
+ spec.add_runtime_dependency 'doorkeeper', '>= 1.4', '<= 2.1.1'
22
22
 
23
23
  spec.add_development_dependency "railties"
24
24
  spec.add_development_dependency "bundler", "~> 1.7"
metadata CHANGED
@@ -1,159 +1,165 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wine_bouncer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Antek Drzewiecki
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-05 00:00:00.000000000 Z
11
+ date: 2015-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: grape
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0.8'
20
+ - - <=
21
+ - !ruby/object:Gem::Version
22
+ version: 0.11.0
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ~>
25
28
  - !ruby/object:Gem::Version
26
29
  version: '0.8'
30
+ - - <=
31
+ - !ruby/object:Gem::Version
32
+ version: 0.11.0
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: doorkeeper
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
- - - ">="
37
+ - - '>='
32
38
  - !ruby/object:Gem::Version
33
39
  version: '1.4'
34
- - - "<="
40
+ - - <=
35
41
  - !ruby/object:Gem::Version
36
- version: 2.0.1
42
+ version: 2.1.1
37
43
  type: :runtime
38
44
  prerelease: false
39
45
  version_requirements: !ruby/object:Gem::Requirement
40
46
  requirements:
41
- - - ">="
47
+ - - '>='
42
48
  - !ruby/object:Gem::Version
43
49
  version: '1.4'
44
- - - "<="
50
+ - - <=
45
51
  - !ruby/object:Gem::Version
46
- version: 2.0.1
52
+ version: 2.1.1
47
53
  - !ruby/object:Gem::Dependency
48
54
  name: railties
49
55
  requirement: !ruby/object:Gem::Requirement
50
56
  requirements:
51
- - - ">="
57
+ - - '>='
52
58
  - !ruby/object:Gem::Version
53
59
  version: '0'
54
60
  type: :development
55
61
  prerelease: false
56
62
  version_requirements: !ruby/object:Gem::Requirement
57
63
  requirements:
58
- - - ">="
64
+ - - '>='
59
65
  - !ruby/object:Gem::Version
60
66
  version: '0'
61
67
  - !ruby/object:Gem::Dependency
62
68
  name: bundler
63
69
  requirement: !ruby/object:Gem::Requirement
64
70
  requirements:
65
- - - "~>"
71
+ - - ~>
66
72
  - !ruby/object:Gem::Version
67
73
  version: '1.7'
68
74
  type: :development
69
75
  prerelease: false
70
76
  version_requirements: !ruby/object:Gem::Requirement
71
77
  requirements:
72
- - - "~>"
78
+ - - ~>
73
79
  - !ruby/object:Gem::Version
74
80
  version: '1.7'
75
81
  - !ruby/object:Gem::Dependency
76
82
  name: rake
77
83
  requirement: !ruby/object:Gem::Requirement
78
84
  requirements:
79
- - - "~>"
85
+ - - ~>
80
86
  - !ruby/object:Gem::Version
81
87
  version: '10.0'
82
88
  type: :development
83
89
  prerelease: false
84
90
  version_requirements: !ruby/object:Gem::Requirement
85
91
  requirements:
86
- - - "~>"
92
+ - - ~>
87
93
  - !ruby/object:Gem::Version
88
94
  version: '10.0'
89
95
  - !ruby/object:Gem::Dependency
90
96
  name: rspec-rails
91
97
  requirement: !ruby/object:Gem::Requirement
92
98
  requirements:
93
- - - "~>"
99
+ - - ~>
94
100
  - !ruby/object:Gem::Version
95
101
  version: 3.2.0
96
102
  type: :development
97
103
  prerelease: false
98
104
  version_requirements: !ruby/object:Gem::Requirement
99
105
  requirements:
100
- - - "~>"
106
+ - - ~>
101
107
  - !ruby/object:Gem::Version
102
108
  version: 3.2.0
103
109
  - !ruby/object:Gem::Dependency
104
110
  name: factory_girl
105
111
  requirement: !ruby/object:Gem::Requirement
106
112
  requirements:
107
- - - "~>"
113
+ - - ~>
108
114
  - !ruby/object:Gem::Version
109
115
  version: 4.4.0
110
116
  type: :development
111
117
  prerelease: false
112
118
  version_requirements: !ruby/object:Gem::Requirement
113
119
  requirements:
114
- - - "~>"
120
+ - - ~>
115
121
  - !ruby/object:Gem::Version
116
122
  version: 4.4.0
117
123
  - !ruby/object:Gem::Dependency
118
124
  name: generator_spec
119
125
  requirement: !ruby/object:Gem::Requirement
120
126
  requirements:
121
- - - "~>"
127
+ - - ~>
122
128
  - !ruby/object:Gem::Version
123
129
  version: 0.9.0
124
130
  type: :development
125
131
  prerelease: false
126
132
  version_requirements: !ruby/object:Gem::Requirement
127
133
  requirements:
128
- - - "~>"
134
+ - - ~>
129
135
  - !ruby/object:Gem::Version
130
136
  version: 0.9.0
131
137
  - !ruby/object:Gem::Dependency
132
138
  name: sqlite3
133
139
  requirement: !ruby/object:Gem::Requirement
134
140
  requirements:
135
- - - ">="
141
+ - - '>='
136
142
  - !ruby/object:Gem::Version
137
143
  version: '0'
138
144
  type: :development
139
145
  prerelease: false
140
146
  version_requirements: !ruby/object:Gem::Requirement
141
147
  requirements:
142
- - - ">="
148
+ - - '>='
143
149
  - !ruby/object:Gem::Version
144
150
  version: '0'
145
151
  - !ruby/object:Gem::Dependency
146
152
  name: database_cleaner
147
153
  requirement: !ruby/object:Gem::Requirement
148
154
  requirements:
149
- - - "~>"
155
+ - - ~>
150
156
  - !ruby/object:Gem::Version
151
157
  version: 1.3.0
152
158
  type: :development
153
159
  prerelease: false
154
160
  version_requirements: !ruby/object:Gem::Requirement
155
161
  requirements:
156
- - - "~>"
162
+ - - ~>
157
163
  - !ruby/object:Gem::Version
158
164
  version: 1.3.0
159
165
  description:
@@ -163,9 +169,9 @@ executables: []
163
169
  extensions: []
164
170
  extra_rdoc_files: []
165
171
  files:
166
- - ".gitignore"
167
- - ".rspec"
168
- - ".travis.yml"
172
+ - .gitignore
173
+ - .rspec
174
+ - .travis.yml
169
175
  - CHANGELOG.md
170
176
  - CONTRIBUTING.md
171
177
  - Gemfile
@@ -180,8 +186,10 @@ files:
180
186
  - lib/wine_bouncer/auth_strategies/default.rb
181
187
  - lib/wine_bouncer/auth_strategies/protected.rb
182
188
  - lib/wine_bouncer/auth_strategies/swagger.rb
189
+ - lib/wine_bouncer/base_strategy.rb
183
190
  - lib/wine_bouncer/configuration.rb
184
191
  - lib/wine_bouncer/errors.rb
192
+ - lib/wine_bouncer/extension.rb
185
193
  - lib/wine_bouncer/oauth2.rb
186
194
  - lib/wine_bouncer/version.rb
187
195
  - spec/dummy/README.rdoc
@@ -243,6 +251,7 @@ files:
243
251
  - spec/lib/generators/wine_bouncer/initializer_generator_spec.rb
244
252
  - spec/lib/wine_bouncer/auth_methods/auth_methods_spec.rb
245
253
  - spec/lib/wine_bouncer/auth_strategies/default_spec.rb
254
+ - spec/lib/wine_bouncer/auth_strategies/swagger_spec.rb
246
255
  - spec/rails_helper.rb
247
256
  - spec/shared/orm/active_record.rb
248
257
  - spec/spec_helper.rb
@@ -257,17 +266,17 @@ require_paths:
257
266
  - lib
258
267
  required_ruby_version: !ruby/object:Gem::Requirement
259
268
  requirements:
260
- - - ">="
269
+ - - '>='
261
270
  - !ruby/object:Gem::Version
262
271
  version: '0'
263
272
  required_rubygems_version: !ruby/object:Gem::Requirement
264
273
  requirements:
265
- - - ">="
274
+ - - '>='
266
275
  - !ruby/object:Gem::Version
267
276
  version: '0'
268
277
  requirements: []
269
278
  rubyforge_project:
270
- rubygems_version: 2.4.5
279
+ rubygems_version: 2.2.2
271
280
  signing_key:
272
281
  specification_version: 4
273
282
  summary: A Ruby gem that allows Oauth2 protection with Doorkeeper for Grape Api's
@@ -331,6 +340,7 @@ test_files:
331
340
  - spec/lib/generators/wine_bouncer/initializer_generator_spec.rb
332
341
  - spec/lib/wine_bouncer/auth_methods/auth_methods_spec.rb
333
342
  - spec/lib/wine_bouncer/auth_strategies/default_spec.rb
343
+ - spec/lib/wine_bouncer/auth_strategies/swagger_spec.rb
334
344
  - spec/rails_helper.rb
335
345
  - spec/shared/orm/active_record.rb
336
346
  - spec/spec_helper.rb