privy_wine_bouncer 1.0.4.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.rspec +2 -0
- data/.rubocop.yml +23 -0
- data/.rubocop_todo.yml +182 -0
- data/.travis.yml +124 -0
- data/CHANGELOG.md +60 -0
- data/CONTRIBUTING.md +55 -0
- data/Gemfile +21 -0
- data/LICENSE.txt +22 -0
- data/README.md +238 -0
- data/Rakefile +11 -0
- data/UPGRADING.md +62 -0
- data/lib/generators/templates/wine_bouncer.rb +9 -0
- data/lib/generators/wine_bouncer/initializer_generator.rb +14 -0
- data/lib/privy_wine_bouncer.rb +3 -0
- data/lib/wine_bouncer/auth_methods/auth_methods.rb +38 -0
- data/lib/wine_bouncer/auth_strategies/default.rb +27 -0
- data/lib/wine_bouncer/auth_strategies/protected.rb +43 -0
- data/lib/wine_bouncer/auth_strategies/swagger.rb +33 -0
- data/lib/wine_bouncer/base_strategy.rb +7 -0
- data/lib/wine_bouncer/configuration.rb +70 -0
- data/lib/wine_bouncer/errors.rb +23 -0
- data/lib/wine_bouncer/extension.rb +24 -0
- data/lib/wine_bouncer/oauth2.rb +106 -0
- data/lib/wine_bouncer/version.rb +5 -0
- data/lib/wine_bouncer.rb +14 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/api/default_api.rb +71 -0
- data/spec/dummy/app/api/protected_api.rb +66 -0
- data/spec/dummy/app/api/swagger_api.rb +61 -0
- data/spec/dummy/app/assets/config/manifest.js +1 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +7 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/helpers/application_helper.rb +4 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/models/user.rb +4 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config/application.rb +31 -0
- data/spec/dummy/config/boot.rb +7 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +7 -0
- data/spec/dummy/config/environments/development.rb +39 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +43 -0
- data/spec/dummy/config/initializers/assets.rb +10 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +9 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/spec/dummy/config/initializers/doorkeeper.rb +94 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
- data/spec/dummy/config/initializers/inflections.rb +18 -0
- data/spec/dummy/config/initializers/mime_types.rb +6 -0
- data/spec/dummy/config/initializers/secret_token.rb +6 -0
- data/spec/dummy/config/initializers/session_store.rb +5 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +16 -0
- data/spec/dummy/config/locales/doorkeeper.en.yml +71 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +8 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20140915153344_create_users.rb +11 -0
- data/spec/dummy/db/migrate/20140915160601_create_doorkeeper_tables.rb +43 -0
- data/spec/dummy/db/schema.rb +62 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/factories/access_token.rb +13 -0
- data/spec/factories/application.rb +8 -0
- data/spec/factories/user.rb +7 -0
- data/spec/intergration/oauth2_default_strategy_spec.rb +189 -0
- data/spec/intergration/oauth2_protected_strategy_spec.rb +199 -0
- data/spec/intergration/oauth2_swagger_strategy_spec.rb +156 -0
- data/spec/lib/generators/wine_bouncer/initializer_generator_spec.rb +19 -0
- data/spec/lib/wine_bouncer/auth_methods/auth_methods_spec.rb +105 -0
- data/spec/lib/wine_bouncer/auth_strategies/default_spec.rb +76 -0
- data/spec/lib/wine_bouncer/auth_strategies/swagger_spec.rb +115 -0
- data/spec/rails_helper.rb +79 -0
- data/spec/shared/orm/active_record.rb +4 -0
- data/spec/spec_helper.rb +95 -0
- data/wine_bouncer.gemspec +33 -0
- metadata +386 -0
data/README.md
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
# WineBouncer
|
2
|
+
|
3
|
+
Protect your precious Grape API with Doorkeeper.
|
4
|
+
WineBouncer uses minimal modification, to make the magic happen.
|
5
|
+
|
6
|
+
# Table of Contents
|
7
|
+
|
8
|
+
- [Requirements](#requirements)
|
9
|
+
- [Installation](#installation)
|
10
|
+
- [Upgrading](#upgrading)
|
11
|
+
- [Usage](#usage)
|
12
|
+
- [Easy DSL](#easy-dsl)
|
13
|
+
- [Authentication strategies](#authentication-strategies)
|
14
|
+
- [Default](#default)
|
15
|
+
- [Swagger](#swagger)
|
16
|
+
- [Protected](#protected)
|
17
|
+
- [Token information](#token-information)
|
18
|
+
- [Disable WineBouncer](#disable-winebouncer)
|
19
|
+
- [Exceptions and Exception handling](#exceptions-and-exception-handling)
|
20
|
+
- [Example Application](#example-application)
|
21
|
+
- [Development](#development)
|
22
|
+
- [Contributing](#contributing)
|
23
|
+
|
24
|
+
## Requirements
|
25
|
+
|
26
|
+
- Ruby > 2.1
|
27
|
+
- Doorkeeper > 1.4.0 and < 5
|
28
|
+
- Grape > 0.10 and < 1.2
|
29
|
+
|
30
|
+
Please submit pull requests and Travis env bumps for newer dependency versions.
|
31
|
+
|
32
|
+
## Installation
|
33
|
+
|
34
|
+
Add this line to your application's Gemfile:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
gem 'privy_wine_bouncer'
|
38
|
+
```
|
39
|
+
|
40
|
+
And then execute:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
bundle
|
44
|
+
```
|
45
|
+
|
46
|
+
## Upgrading
|
47
|
+
|
48
|
+
When upgrading from a previous version, see [UPGRADING](UPGRADING.md). You might also be interested at the [CHANGELOG](CHANGELOG.md).
|
49
|
+
|
50
|
+
## Usage
|
51
|
+
|
52
|
+
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.
|
53
|
+
|
54
|
+
To get started with WineBouncer, run the configuration initializer:
|
55
|
+
|
56
|
+
```shell
|
57
|
+
$ rails g wine_bouncer:initializer
|
58
|
+
```
|
59
|
+
|
60
|
+
This creates a rails initializer in your Rails app at `config/initializers/wine_bouncer.rb` with the following configuration:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
WineBouncer.configure do |config|
|
64
|
+
config.auth_strategy = :default
|
65
|
+
|
66
|
+
config.define_resource_owner do
|
67
|
+
User.find(doorkeeper_access_token.resource_owner_id) if doorkeeper_access_token
|
68
|
+
end
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
72
|
+
Then register WineBouncer as Grape middleware in your Grape API.
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
class Api < Grape::API
|
76
|
+
default_format :json
|
77
|
+
format :json
|
78
|
+
use ::WineBouncer::OAuth2
|
79
|
+
mount YourAwesomeApi
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
### Easy DSL
|
84
|
+
|
85
|
+
WineBouncer comes with an easy DSL and relies on Grape's DSL extentions to define if an endpoint method should be protected.
|
86
|
+
You can protect an endpoint by calling `oauth2` method with optional scopes in front of the endpoint definition.
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
class MyAwesomeAPI < Grape::API
|
90
|
+
desc 'protected method with required public scope'
|
91
|
+
oauth2 'public'
|
92
|
+
get '/protected' do
|
93
|
+
{ hello: 'world' }
|
94
|
+
end
|
95
|
+
|
96
|
+
oauth2 'public'
|
97
|
+
get '/with_no_description' do
|
98
|
+
{ hello: 'undescribed world' }
|
99
|
+
end
|
100
|
+
|
101
|
+
desc 'Unprotected method'
|
102
|
+
get '/unprotected' do
|
103
|
+
{ hello: 'unprotected world' }
|
104
|
+
end
|
105
|
+
|
106
|
+
desc 'This method needs the public or private scope.'
|
107
|
+
oauth2 'public', 'write'
|
108
|
+
get '/method' do
|
109
|
+
{ hello: 'public or private user.' }
|
110
|
+
end
|
111
|
+
|
112
|
+
desc 'This method uses Doorkeepers default scopes.',
|
113
|
+
oauth2
|
114
|
+
get '/protected_with_default_scope' do
|
115
|
+
{ hello: 'protected unscoped world' }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class Api < Grape::API
|
120
|
+
default_format :json
|
121
|
+
format :json
|
122
|
+
use ::WineBouncer::OAuth2
|
123
|
+
mount MyAwesomeAPI
|
124
|
+
end
|
125
|
+
```
|
126
|
+
|
127
|
+
### Authentication strategies
|
128
|
+
|
129
|
+
Behaviour of the authentication can be customized by selecting an authentication strategy. The following authentication strategies are provided in the gem.
|
130
|
+
|
131
|
+
#### Default
|
132
|
+
|
133
|
+
The default strategy only authenticates endpoints which are annotated by the `oauth2` method. Un-annotated endpoints still can be accessed without authentication.
|
134
|
+
|
135
|
+
#### Swagger
|
136
|
+
|
137
|
+
WineBouncer comes with a strategy that can be perfectly used with [grape-swagger](https://github.com/tim-vandecasteele/grape-swagger) with a syntax compliant with the [swagger spec](https://github.com/wordnik/swagger-spec/).
|
138
|
+
This might be one of the simplest methods to protect your API and serve it with documentation. You can use [swagger-ui](https://github.com/wordnik/swagger-ui) to view your documentation.
|
139
|
+
|
140
|
+
To get started ensure you also have included the `grape-swagger` gem in your gemfile.
|
141
|
+
|
142
|
+
Run `bundle` to install the missing gems.
|
143
|
+
|
144
|
+
Create a rails initializer in your Rails app at `config/initializers/wine_bouncer.rb` with the following configuration.
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
WineBouncer.configure do |config|
|
148
|
+
config.auth_strategy = :swagger
|
149
|
+
|
150
|
+
config.define_resource_owner do
|
151
|
+
User.find(doorkeeper_access_token.resource_owner_id) if doorkeeper_access_token
|
152
|
+
end
|
153
|
+
end
|
154
|
+
```
|
155
|
+
|
156
|
+
Then you can start protecting your API like the example below.
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
desc 'This method needs the public or private scope.',
|
160
|
+
success: Api::Entities::Response,
|
161
|
+
failure: [
|
162
|
+
[401, 'Unauthorized', Api::Entities::Error],
|
163
|
+
[403, 'Forbidden', Api::Entities::Error]
|
164
|
+
],
|
165
|
+
notes: <<-NOTE
|
166
|
+
Marked down notes!
|
167
|
+
NOTE
|
168
|
+
oauth2 'public', 'write'
|
169
|
+
get '/method' do
|
170
|
+
{ hello: 'public or private user.' }
|
171
|
+
end
|
172
|
+
```
|
173
|
+
|
174
|
+
The Swagger strategy uses scopes and injects them in the authorizations description for external application to be read.
|
175
|
+
|
176
|
+
#### Protected
|
177
|
+
|
178
|
+
The protected strategy is very similar to the default strategy except any public endpoint must explicitly set. To make an end point public, use `oauth2 false`.
|
179
|
+
If the authorization method is not set, the end point is assumed to be **protected with Doorkeeper's default scopes** (which is the same as `oauth2 nil `.)
|
180
|
+
To protect your endpoint with other scopes append the following method `oauth2 'first scope', 'second scope'`.
|
181
|
+
|
182
|
+
### Token information
|
183
|
+
|
184
|
+
WineBouncer comes with free extras! Methods for `resource_owner` and `doorkeeper_access_token` get included in your endpoints. You can use them to get the current resource owner, and the access_token object of doorkeeper.
|
185
|
+
|
186
|
+
### Disable WineBouncer
|
187
|
+
|
188
|
+
If you want to disable WineBouncer conditionally - e.g. in specs - you can add a block to the WineBouncer configuration. When this block evaluates to true, any request will be unprotected. For example:
|
189
|
+
|
190
|
+
```{ruby}
|
191
|
+
WineBouncer.configure do |config|
|
192
|
+
config.disable do
|
193
|
+
Rails.env.test?
|
194
|
+
end
|
195
|
+
end
|
196
|
+
```
|
197
|
+
|
198
|
+
The block is newly evaluated for every request, so you could in principle have something like:
|
199
|
+
|
200
|
+
```{ruby}
|
201
|
+
config.disable do
|
202
|
+
[true, false].sample
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
206
|
+
You probably shouldn't, though.
|
207
|
+
|
208
|
+
## Exceptions and Exception handling
|
209
|
+
|
210
|
+
This gem raises the following exceptions which can be handled in your Grape API, see [Grape documentation](https://github.com/intridea/grape#exception-handling).
|
211
|
+
|
212
|
+
- `WineBouncer::Errors::OAuthUnauthorizedError`
|
213
|
+
when the request is unauthorized.
|
214
|
+
- `WineBouncer::Errors::OAuthForbiddenError`
|
215
|
+
when the token is found but scopes do not match.
|
216
|
+
|
217
|
+
Detailed doorkeeper error response can be found in the error's `response` attribute. You could use
|
218
|
+
it to compose the actual HTTP response to API users.
|
219
|
+
|
220
|
+
## Example/Template Application
|
221
|
+
|
222
|
+
A full working sample app (or starter template) can be found at [grape-doorkeeper on github](https://github.com/sethherr/grape-doorkeeper). It has one click deploy to Heroku and [a live example](https://grape-doorkeeper.herokuapp.com/).
|
223
|
+
|
224
|
+
## Development
|
225
|
+
|
226
|
+
Since we want the gem tested against several rails versions we use the same way to prepare our development environment as Doorkeeper.
|
227
|
+
|
228
|
+
To install the development environment for rails 4.2.6, you can also specify a different rails version to test against.
|
229
|
+
|
230
|
+
`rails=4.2.6 bundle install`
|
231
|
+
|
232
|
+
To run the specs.
|
233
|
+
|
234
|
+
`rails=4.2.6 bundle exec rake`
|
235
|
+
|
236
|
+
## Contributing
|
237
|
+
|
238
|
+
For contributing to the gem see [CONTRIBUTING](CONTRIBUTING.md).
|
data/Rakefile
ADDED
data/UPGRADING.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
Upgrading WineBouncer
|
2
|
+
=====================
|
3
|
+
|
4
|
+
|
5
|
+
## Upgrading to >= 1.0
|
6
|
+
|
7
|
+
No DSL changes have been made. This is just a stability release from we can work to next releases.
|
8
|
+
|
9
|
+
|
10
|
+
## Upgrading to >= 0.5.0
|
11
|
+
|
12
|
+
WineBouncer's exceptions `OAuthUnauthorizedError` and `OAuthForbiddenError` now come with a
|
13
|
+
corresponding Doorkeeper's error response. You can access it via `response` method of the exception.
|
14
|
+
For backward compatible, you can still use `message` but the content will be provided by Doorkeeper
|
15
|
+
error response's description.
|
16
|
+
|
17
|
+
## Upgrading to >= 0.3.0
|
18
|
+
|
19
|
+
An new DSL has been introduced to WineBouncer. This DSL will become the preferred way to authorize your endpoints.
|
20
|
+
The authentication trough endpoint description will become deprecated in the future version.
|
21
|
+
|
22
|
+
The old way to authorize your endpoints:
|
23
|
+
|
24
|
+
```
|
25
|
+
desc 'protected method with required public and private scope',
|
26
|
+
auth: { scopes: ['public','private'] }
|
27
|
+
get '/protected' do
|
28
|
+
{ hello: 'world' }
|
29
|
+
end
|
30
|
+
```
|
31
|
+
|
32
|
+
You may now write:
|
33
|
+
```
|
34
|
+
desc 'protected method with required public and private scope'
|
35
|
+
oauth2 'public', 'private'
|
36
|
+
get '/protected' do
|
37
|
+
{ hello: 'world' }
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
And even remove the description.
|
42
|
+
|
43
|
+
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.
|
44
|
+
|
45
|
+
### Upgrading to >= 0.2.0
|
46
|
+
|
47
|
+
To use the `resource_owner` you now need to define how WineBouncer gets the resource owner. In most cases he needs to return the User that is logged in, but not in all cases.
|
48
|
+
You now additionally need to specify `define_resource_owner` in your configuration. Mostly this the following code will work:
|
49
|
+
|
50
|
+
``` ruby
|
51
|
+
WineBouncer.configure do |c|
|
52
|
+
.......
|
53
|
+
c.define_resource_owner do
|
54
|
+
User.find(doorkeeper_access_token.resource_owner_id) if doorkeeper_access_token
|
55
|
+
end
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
The required Grape version is now from 0.8 and above.
|
60
|
+
|
61
|
+
------
|
62
|
+
From version 0.1.0 upgrade instructions are given.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WineBouncer
|
4
|
+
module Generators
|
5
|
+
class InitializerGenerator < ::Rails::Generators::Base
|
6
|
+
source_root File.expand_path('../../templates', __FILE__)
|
7
|
+
|
8
|
+
desc 'Creates a sample WineBouncer initializer.'
|
9
|
+
def copy_initializer
|
10
|
+
copy_file 'wine_bouncer.rb', 'config/initializers/wine_bouncer.rb'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WineBouncer
|
4
|
+
module AuthMethods
|
5
|
+
|
6
|
+
def protected_endpoint=(protected)
|
7
|
+
@protected_endpoint = protected
|
8
|
+
end
|
9
|
+
|
10
|
+
def protected_endpoint?
|
11
|
+
@protected_endpoint || false
|
12
|
+
end
|
13
|
+
|
14
|
+
def resource_owner
|
15
|
+
instance_eval(&WineBouncer.configuration.defined_resource_owner)
|
16
|
+
end
|
17
|
+
|
18
|
+
def client_credential_token?
|
19
|
+
has_doorkeeper_token? && doorkeeper_access_token.resource_owner_id.nil?
|
20
|
+
end
|
21
|
+
|
22
|
+
def doorkeeper_access_token
|
23
|
+
@_doorkeeper_access_token
|
24
|
+
end
|
25
|
+
|
26
|
+
def doorkeeper_access_token=(token)
|
27
|
+
@_doorkeeper_access_token = token
|
28
|
+
end
|
29
|
+
|
30
|
+
def has_doorkeeper_token?
|
31
|
+
!@_doorkeeper_access_token.nil?
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_resource_owner?
|
35
|
+
has_doorkeeper_token? && !!doorkeeper_access_token.resource_owner_id
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WineBouncer
|
4
|
+
module AuthStrategies
|
5
|
+
class Default < ::WineBouncer::BaseStrategy
|
6
|
+
def endpoint_protected?
|
7
|
+
!!endpoint_authorizations
|
8
|
+
end
|
9
|
+
|
10
|
+
def has_auth_scopes?
|
11
|
+
!!endpoint_authorizations &&
|
12
|
+
endpoint_authorizations.key?(:scopes) &&
|
13
|
+
!endpoint_authorizations[:scopes].empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def auth_scopes
|
17
|
+
endpoint_authorizations[:scopes].map(&:to_sym)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def endpoint_authorizations
|
23
|
+
api_context.options[:route_options][:auth]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WineBouncer
|
4
|
+
module AuthStrategies
|
5
|
+
class Protected < WineBouncer::BaseStrategy
|
6
|
+
def endpoint_protected?
|
7
|
+
has_authorizations?
|
8
|
+
end
|
9
|
+
|
10
|
+
def has_auth_scopes?
|
11
|
+
endpoint_authorizations &&
|
12
|
+
endpoint_authorizations.key?(:scopes) &&
|
13
|
+
endpoint_authorizations[:scopes].any?
|
14
|
+
end
|
15
|
+
|
16
|
+
def auth_scopes
|
17
|
+
endpoint_authorizations[:scopes].map(&:to_sym)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def nil_authorizations?
|
23
|
+
endpoint_authorizations.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
# returns true if an authorization hash has been found
|
27
|
+
# First it checks for the old syntax, then for the new.
|
28
|
+
def has_authorizations?
|
29
|
+
(nil_authorizations? || !!endpoint_authorizations) && scope_keys?
|
30
|
+
end
|
31
|
+
|
32
|
+
# if false or nil scopes are entered the authorization should be skipped.
|
33
|
+
# nil_authorizations? is used to check against the legacy hash.
|
34
|
+
def scope_keys?
|
35
|
+
nil_authorizations? || endpoint_authorizations[:scopes] != [false]
|
36
|
+
end
|
37
|
+
|
38
|
+
def endpoint_authorizations
|
39
|
+
api_context.options[:route_options][:auth]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WineBouncer
|
4
|
+
module AuthStrategies
|
5
|
+
class Swagger < ::WineBouncer::BaseStrategy
|
6
|
+
def endpoint_protected?
|
7
|
+
has_authorizations? && !!authorization_type_oauth2
|
8
|
+
end
|
9
|
+
|
10
|
+
def has_auth_scopes?
|
11
|
+
endpoint_protected? && !authorization_type_oauth2.empty?
|
12
|
+
end
|
13
|
+
|
14
|
+
def auth_scopes
|
15
|
+
authorization_type_oauth2.map { |hash| hash[:scope].to_sym }
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def has_authorizations?
|
21
|
+
!!endpoint_authorizations
|
22
|
+
end
|
23
|
+
|
24
|
+
def endpoint_authorizations
|
25
|
+
api_context.options[:route_options][:authorizations]
|
26
|
+
end
|
27
|
+
|
28
|
+
def authorization_type_oauth2
|
29
|
+
endpoint_authorizations[:oauth2]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WineBouncer
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_accessor :configuration
|
7
|
+
end
|
8
|
+
|
9
|
+
class Configuration
|
10
|
+
attr_accessor :auth_strategy
|
11
|
+
attr_accessor :defined_resource_owner
|
12
|
+
attr_writer :auth_strategy
|
13
|
+
|
14
|
+
def auth_strategy
|
15
|
+
@auth_strategy || :default
|
16
|
+
end
|
17
|
+
|
18
|
+
def require_strategies
|
19
|
+
require "wine_bouncer/auth_strategies/#{auth_strategy}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def define_resource_owner &block
|
23
|
+
raise(ArgumentError, 'define_resource_owner expects a block in the configuration') unless block_given?
|
24
|
+
@defined_resource_owner = block
|
25
|
+
end
|
26
|
+
|
27
|
+
def defined_resource_owner
|
28
|
+
raise(Errors::UnconfiguredError, 'Please define define_resource_owner to configure the resource owner') unless @defined_resource_owner
|
29
|
+
@defined_resource_owner
|
30
|
+
end
|
31
|
+
|
32
|
+
# when the block evaluates to true, WineBouncer should be disabled
|
33
|
+
# if no block is provided, WineBouncer is always enabled
|
34
|
+
def disable(&block)
|
35
|
+
@disable_block = block
|
36
|
+
end
|
37
|
+
|
38
|
+
def disable_block
|
39
|
+
@disable_block || ->() { false }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.configuration
|
44
|
+
@config || raise(Errors::UnconfiguredError.new)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.configuration=(config)
|
48
|
+
@config = config
|
49
|
+
@config.require_strategies
|
50
|
+
end
|
51
|
+
|
52
|
+
###
|
53
|
+
# Configure block.
|
54
|
+
# Requires all strategy specific files.
|
55
|
+
###
|
56
|
+
def self.configure
|
57
|
+
yield(config)
|
58
|
+
config.require_strategies
|
59
|
+
config
|
60
|
+
end
|
61
|
+
|
62
|
+
private_class_method
|
63
|
+
|
64
|
+
###
|
65
|
+
# Returns a new configuration or existing one.
|
66
|
+
###
|
67
|
+
def self.config
|
68
|
+
@config ||= Configuration.new
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WineBouncer
|
4
|
+
module Errors
|
5
|
+
class UnconfiguredError < StandardError; end
|
6
|
+
|
7
|
+
class OAuthUnauthorizedError < StandardError
|
8
|
+
attr_reader :response
|
9
|
+
def initialize(response)
|
10
|
+
super(response.try(:description))
|
11
|
+
@response = response
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class OAuthForbiddenError < StandardError
|
16
|
+
attr_reader :response
|
17
|
+
def initialize(response)
|
18
|
+
super(response.try(:description))
|
19
|
+
@response = response
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WineBouncer
|
4
|
+
module Extension
|
5
|
+
def oauth2(*scopes)
|
6
|
+
scopes = Doorkeeper.configuration.default_scopes.all if scopes.all? { |x| x.nil? }
|
7
|
+
description = if respond_to?(:route_setting) # >= grape-0.10.0
|
8
|
+
route_setting(:description) || route_setting(:description, {})
|
9
|
+
else
|
10
|
+
@last_description ||= {}
|
11
|
+
end
|
12
|
+
# case WineBouncer.configuration.auth_strategy
|
13
|
+
# when :default
|
14
|
+
description[:auth] = { scopes: scopes }
|
15
|
+
# when :swagger
|
16
|
+
description[:authorizations] = { oauth2: scopes.map { |x| { scope: x } } }
|
17
|
+
# end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Grape::API::Instance is defined in grape 1.2.0 or above
|
21
|
+
grape_api = defined?(Grape::API::Instance) ? Grape::API::Instance : Grape::API
|
22
|
+
grape_api.extend self
|
23
|
+
end
|
24
|
+
end
|