g5_authenticatable_api 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +13 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +1 -0
- data/README.md +104 -1
- data/g5_authenticatable_api.gemspec +1 -1
- data/lib/g5_authenticatable_api/helpers/grape.rb +27 -3
- data/lib/g5_authenticatable_api/helpers/rails.rb +23 -2
- data/lib/g5_authenticatable_api/services/token_info.rb +40 -0
- data/lib/g5_authenticatable_api/services/token_validator.rb +54 -0
- data/lib/g5_authenticatable_api/services/user_fetcher.rb +15 -0
- data/lib/g5_authenticatable_api/version.rb +1 -1
- data/spec/dummy/config/database.yml.ci +1 -2
- data/spec/lib/g5_authenticatable_api/helpers/grape_spec.rb +156 -0
- data/spec/lib/g5_authenticatable_api/helpers/rails_spec.rb +148 -0
- data/spec/lib/g5_authenticatable_api/services/token_info_spec.rb +158 -0
- data/spec/lib/g5_authenticatable_api/{token_validator_spec.rb → services/token_validator_spec.rb} +1 -55
- data/spec/lib/g5_authenticatable_api/services/user_fetcher_spec.rb +41 -0
- data/spec/support/shared_contexts/current_auth_user.rb +33 -0
- data/spec/support/shared_contexts/valid_access_token.rb +12 -1
- data/spec/support/shared_examples/auth_user.rb +33 -0
- metadata +23 -9
- data/circle.yml +0 -4
- data/lib/g5_authenticatable_api/token_validator.rb +0 -78
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5149507d4b4decc9cea0ecbf91c3ff2ed948188
|
4
|
+
data.tar.gz: 6e2cb01e5217b1e7b3ed6991048cd5242e026147
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33415eef9d07b90e5dbad1f898691b82b85800810378a25126e6a5f47121c6e76929b5c912ec55df3f5ab45c927901c70b790d6b79148c1205e3fc2739dbfc30
|
7
|
+
data.tar.gz: 7f271c4ab903a38f76671f3a0ca7d30b5443d2c4627be86a0413c814135a0116f7ce28f01dde2ebaa592fdb218e1c8f7a03aeb738227848376fa7d81668aafd2
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.1.
|
1
|
+
2.1.6
|
data/.travis.yml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.0.0
|
4
|
+
- 2.1.6
|
5
|
+
- 2.2.2
|
6
|
+
script:
|
7
|
+
- (cd spec/dummy; RAILS_ENV=test bundle exec rake db:drop db:create db:schema:load)
|
8
|
+
- bundle exec rspec spec
|
9
|
+
before_script:
|
10
|
+
- cp spec/dummy/config/database.yml.ci spec/dummy/config/database.yml
|
11
|
+
env:
|
12
|
+
global:
|
13
|
+
- DEVISE_SECRET_KEY=foo
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
## v0.4.0 (2015-05-22)
|
2
|
+
|
3
|
+
* Add helpers for retrieving current API user as well as more detailed token
|
4
|
+
information
|
5
|
+
([#8](https://github.com/G5/g5_authenticatable_api/pull/8))
|
6
|
+
|
7
|
+
## v0.3.2 (2015-04-20)
|
8
|
+
|
9
|
+
* Fix for case-insensitive authorization request header
|
10
|
+
([#7](https://github.com/G5/g5_authenticatable_api/pull/7))
|
11
|
+
|
1
12
|
## v0.3.1 (2015-01-20)
|
2
13
|
|
3
14
|
* Disable strict token validation for session-authenticated users by
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -9,7 +9,7 @@ service using token-based authentication.
|
|
9
9
|
|
10
10
|
## Current Version
|
11
11
|
|
12
|
-
0.
|
12
|
+
0.4.0
|
13
13
|
|
14
14
|
## Requirements
|
15
15
|
|
@@ -106,6 +106,58 @@ class MyResourceController < ApplicationController
|
|
106
106
|
end
|
107
107
|
```
|
108
108
|
|
109
|
+
After authenticating an API user, you can retrieve the current token data as a
|
110
|
+
[`G5AuthenticationClient::TokenInfo`](https://github.com/G5/g5_authentication_client/blob/master/lib/g5_authentication_client/token_info.rb)
|
111
|
+
using the `token_data` helper:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
class MyResourceController < ApplicationController
|
115
|
+
before_filter :authenticate_api_user!
|
116
|
+
|
117
|
+
respond_to :json
|
118
|
+
|
119
|
+
def index
|
120
|
+
token_expiration = token_data.expires_in_seconds
|
121
|
+
# ...
|
122
|
+
end
|
123
|
+
end
|
124
|
+
```
|
125
|
+
|
126
|
+
You can retrieve the current user data using the `current_api_user` helper,
|
127
|
+
which will attempt to retrieve the data from
|
128
|
+
[warden](https://github.com/hassox/warden) if possible. Otherwise it will return
|
129
|
+
a [`G5AuthenticationClient::User`](https://github.com/G5/g5_authentication_client/blob/master/lib/g5_authentication_client/user.rb):
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
class MyResourceController < ApplicationController
|
133
|
+
before_filter :authenticate_api_user!
|
134
|
+
|
135
|
+
respond_to :json
|
136
|
+
|
137
|
+
def index
|
138
|
+
user = current_api_user
|
139
|
+
# ...
|
140
|
+
end
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
|
145
|
+
Finally, you can retrieve the value of the access token in use for this request
|
146
|
+
by using the `access_token` helper:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
class MyResourceController < ApplicationController
|
150
|
+
before_filter :authenticate_api_user!
|
151
|
+
|
152
|
+
respond_to :json
|
153
|
+
|
154
|
+
def index
|
155
|
+
token = access_token
|
156
|
+
# ...
|
157
|
+
end
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
109
161
|
### Grape
|
110
162
|
|
111
163
|
To require authentication for all endpoints exposed by your API:
|
@@ -138,6 +190,57 @@ class MyApi < Grape::API
|
|
138
190
|
end
|
139
191
|
```
|
140
192
|
|
193
|
+
After authenticating an API user, you can retrieve the current token data as a
|
194
|
+
[`G5AuthenticationClient::TokenInfo`](https://github.com/G5/g5_authentication_client/blob/master/lib/g5_authentication_client/token_info.rb)
|
195
|
+
using the `token_data` helper:
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
class MyApi < Grape::API
|
199
|
+
helpers G5AuthenticatableApi::Helpers::Grape
|
200
|
+
|
201
|
+
before { authenticate_user! }
|
202
|
+
|
203
|
+
get :index do
|
204
|
+
token_expiration = token_data.expires_in_seconds
|
205
|
+
# ...
|
206
|
+
end
|
207
|
+
end
|
208
|
+
```
|
209
|
+
|
210
|
+
You can retrieve the current user data using the `current_user` helper,
|
211
|
+
which will attempt to retrieve the data from
|
212
|
+
[warden](https://github.com/hassox/warden) if possible. Otherwise it will return
|
213
|
+
a [`G5AuthenticationClient::User`](https://github.com/G5/g5_authentication_client/blob/master/lib/g5_authentication_client/user.rb):
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
class MyApi < Grape::API
|
217
|
+
helpers G5AuthenticatableApi::Helpers::Grape
|
218
|
+
|
219
|
+
before { authenticate_user! }
|
220
|
+
|
221
|
+
get :index do
|
222
|
+
user = current_user
|
223
|
+
# ...
|
224
|
+
end
|
225
|
+
end
|
226
|
+
```
|
227
|
+
|
228
|
+
You can retrieve the value of the access token in use for this request with the
|
229
|
+
`access_token` helper:
|
230
|
+
|
231
|
+
```ruby
|
232
|
+
class MyApi < Grape::API
|
233
|
+
helpers G5AuthenticatableApi::Helpers::Grape
|
234
|
+
|
235
|
+
before { authenticate_user! }
|
236
|
+
|
237
|
+
get :index do
|
238
|
+
token = access_token
|
239
|
+
# ...
|
240
|
+
end
|
241
|
+
end
|
242
|
+
```
|
243
|
+
|
141
244
|
### Submitting a token
|
142
245
|
|
143
246
|
Authenticated requests follow the requirements described by
|
@@ -19,6 +19,6 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
21
|
spec.add_dependency 'rack'
|
22
|
-
spec.add_dependency 'g5_authentication_client', '~> 0.
|
22
|
+
spec.add_dependency 'g5_authentication_client', '~> 0.4'
|
23
23
|
spec.add_dependency 'activesupport', '>= 3.2'
|
24
24
|
end
|
@@ -1,4 +1,5 @@
|
|
1
|
-
require 'g5_authenticatable_api/token_validator'
|
1
|
+
require 'g5_authenticatable_api/services/token_validator'
|
2
|
+
require 'g5_authenticatable_api/services/user_fetcher'
|
2
3
|
|
3
4
|
module G5AuthenticatableApi
|
4
5
|
module Helpers
|
@@ -7,14 +8,37 @@ module G5AuthenticatableApi
|
|
7
8
|
raise_auth_error if !token_validator.valid?
|
8
9
|
end
|
9
10
|
|
11
|
+
def token_data
|
12
|
+
@token_data ||= token_info.token_data
|
13
|
+
end
|
14
|
+
|
15
|
+
def current_user
|
16
|
+
@current_user ||= user_fetcher.current_user
|
17
|
+
end
|
18
|
+
|
19
|
+
def access_token
|
20
|
+
@access_token ||= token_info.access_token
|
21
|
+
end
|
22
|
+
|
10
23
|
def warden
|
11
24
|
env['warden']
|
12
25
|
end
|
13
26
|
|
14
27
|
private
|
28
|
+
def request
|
29
|
+
Rack::Request.new(env)
|
30
|
+
end
|
31
|
+
|
32
|
+
def token_info
|
33
|
+
@token_info ||= Services::TokenInfo.new(request.params, headers, warden)
|
34
|
+
end
|
35
|
+
|
15
36
|
def token_validator
|
16
|
-
|
17
|
-
|
37
|
+
@token_validator ||= Services::TokenValidator.new(request.params, headers, warden)
|
38
|
+
end
|
39
|
+
|
40
|
+
def user_fetcher
|
41
|
+
@user_fetcher ||= Services::UserFetcher.new(request.params, headers, warden)
|
18
42
|
end
|
19
43
|
|
20
44
|
def raise_auth_error
|
@@ -1,4 +1,5 @@
|
|
1
|
-
require 'g5_authenticatable_api/token_validator'
|
1
|
+
require 'g5_authenticatable_api/services/token_validator'
|
2
|
+
require 'g5_authenticatable_api/services/user_fetcher'
|
2
3
|
|
3
4
|
module G5AuthenticatableApi
|
4
5
|
module Helpers
|
@@ -7,13 +8,33 @@ module G5AuthenticatableApi
|
|
7
8
|
raise_auth_error if !token_validator.valid?
|
8
9
|
end
|
9
10
|
|
11
|
+
def token_data
|
12
|
+
@token_data ||= token_info.token_data
|
13
|
+
end
|
14
|
+
|
15
|
+
def current_api_user
|
16
|
+
@current_api_user ||= user_fetcher.current_user
|
17
|
+
end
|
18
|
+
|
19
|
+
def access_token
|
20
|
+
@access_token ||= token_info.access_token
|
21
|
+
end
|
22
|
+
|
10
23
|
def warden
|
11
24
|
request.env['warden']
|
12
25
|
end
|
13
26
|
|
14
27
|
private
|
28
|
+
def token_info
|
29
|
+
@token_info ||= Services::TokenInfo.new(request.params, request.headers, warden)
|
30
|
+
end
|
31
|
+
|
15
32
|
def token_validator
|
16
|
-
@token_validator ||= TokenValidator.new(request.params, request.headers, warden)
|
33
|
+
@token_validator ||= Services::TokenValidator.new(request.params, request.headers, warden)
|
34
|
+
end
|
35
|
+
|
36
|
+
def user_fetcher
|
37
|
+
@user_fetcher ||= Services::UserFetcher.new(request.params, request.headers, warden)
|
17
38
|
end
|
18
39
|
|
19
40
|
def raise_auth_error
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module G5AuthenticatableApi
|
2
|
+
module Services
|
3
|
+
class TokenInfo
|
4
|
+
attr_reader :params, :headers, :warden
|
5
|
+
|
6
|
+
def initialize(params={},headers={},warden=nil)
|
7
|
+
@params = params || {}
|
8
|
+
@headers = headers || {}
|
9
|
+
@warden = warden
|
10
|
+
end
|
11
|
+
|
12
|
+
def access_token
|
13
|
+
@access_token ||= (extract_token_from_header ||
|
14
|
+
params['access_token'] ||
|
15
|
+
warden.try(:user).try(:g5_access_token))
|
16
|
+
end
|
17
|
+
|
18
|
+
def token_data
|
19
|
+
auth_client.token_info
|
20
|
+
end
|
21
|
+
|
22
|
+
def auth_client
|
23
|
+
@auth_client ||= G5AuthenticationClient::Client.new(allow_password_credentials: 'false',
|
24
|
+
access_token: access_token)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def extract_token_from_header
|
29
|
+
if authorization_header
|
30
|
+
parts = authorization_header.match(/Bearer (?<access_token>\S+)/)
|
31
|
+
parts['access_token']
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def authorization_header
|
36
|
+
@headers['Authorization'] || @headers['AUTHORIZATION']
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'g5_authenticatable_api/services/token_info'
|
2
|
+
|
3
|
+
module G5AuthenticatableApi
|
4
|
+
module Services
|
5
|
+
class TokenValidator < TokenInfo
|
6
|
+
attr_reader :error
|
7
|
+
|
8
|
+
def validate!
|
9
|
+
begin
|
10
|
+
token_data unless skip_validation?
|
11
|
+
rescue StandardError => @error
|
12
|
+
raise error
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def valid?
|
17
|
+
begin
|
18
|
+
validate!
|
19
|
+
true
|
20
|
+
rescue StandardError => e
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def auth_response_header
|
26
|
+
if error
|
27
|
+
auth_header = "Bearer"
|
28
|
+
|
29
|
+
if access_token
|
30
|
+
auth_header << " error=\"#{error_code}\""
|
31
|
+
auth_header << ",error_description=\"#{error_description}\"" if error_description
|
32
|
+
end
|
33
|
+
|
34
|
+
auth_header
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def error_code
|
40
|
+
error_code = error.code if error.respond_to?(:code)
|
41
|
+
error_code || 'invalid_request'
|
42
|
+
end
|
43
|
+
|
44
|
+
def error_description
|
45
|
+
error_description = error.description if error.respond_to?(:description)
|
46
|
+
error_description
|
47
|
+
end
|
48
|
+
|
49
|
+
def skip_validation?
|
50
|
+
@warden.try(:user) && !G5AuthenticatableApi.strict_token_validation
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'g5_authenticatable_api/services/token_info'
|
2
|
+
|
3
|
+
module G5AuthenticatableApi
|
4
|
+
module Services
|
5
|
+
class UserFetcher < TokenInfo
|
6
|
+
def current_user
|
7
|
+
if access_token == @warden.try(:user).try(:g5_access_token)
|
8
|
+
@warden.user
|
9
|
+
else
|
10
|
+
auth_client.me
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe G5AuthenticatableApi::Helpers::Grape do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def app
|
7
|
+
Class.new(Grape::API) do
|
8
|
+
helpers G5AuthenticatableApi::Helpers::Grape
|
9
|
+
|
10
|
+
get :authenticate do
|
11
|
+
authenticate_user!
|
12
|
+
{ hello: 'world' }
|
13
|
+
end
|
14
|
+
|
15
|
+
get :token_data do
|
16
|
+
token_data.to_json
|
17
|
+
end
|
18
|
+
|
19
|
+
get :current_user do
|
20
|
+
current_user.to_json
|
21
|
+
end
|
22
|
+
|
23
|
+
get :access_token do
|
24
|
+
[access_token]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:env) { {'warden' => warden} }
|
30
|
+
let(:warden) { double(:warden) }
|
31
|
+
|
32
|
+
let(:params) { {'access_token' => token_value} }
|
33
|
+
let(:token_value) { 'abc123' }
|
34
|
+
|
35
|
+
let(:headers) { {'Host'=>'example.org', 'Cookie'=>''} }
|
36
|
+
|
37
|
+
describe '#authenticate_user!' do
|
38
|
+
subject(:authenticate_user!) { get '/authenticate', params, env }
|
39
|
+
|
40
|
+
let(:token_validator) do
|
41
|
+
double(:token_validator, valid?: valid,
|
42
|
+
auth_response_header: auth_response_header,
|
43
|
+
access_token: token_value)
|
44
|
+
end
|
45
|
+
before do
|
46
|
+
allow(G5AuthenticatableApi::Services::TokenValidator).to receive(:new).
|
47
|
+
and_return(token_validator)
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when token is valid' do
|
51
|
+
let(:valid) { true }
|
52
|
+
let(:auth_response_header) {}
|
53
|
+
|
54
|
+
it 'initializes the token validator correctly' do
|
55
|
+
authenticate_user!
|
56
|
+
expect(G5AuthenticatableApi::Services::TokenValidator).to have_received(:new).
|
57
|
+
with(params, headers, warden)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'is successful' do
|
61
|
+
authenticate_user!
|
62
|
+
expect(last_response).to be_http_ok
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'does not set the authenticate response header' do
|
66
|
+
authenticate_user!
|
67
|
+
expect(last_response).to_not have_header('WWW-Authenticate')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'when token is invalid' do
|
72
|
+
let(:valid) { false }
|
73
|
+
let(:auth_response_header) { 'whatever' }
|
74
|
+
|
75
|
+
it 'is unauthorized' do
|
76
|
+
authenticate_user!
|
77
|
+
expect(last_response).to be_http_unauthorized
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'renders an error message' do
|
81
|
+
authenticate_user!
|
82
|
+
expect(last_response.body).to eq('Unauthorized')
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'sets the authenticate response header' do
|
86
|
+
authenticate_user!
|
87
|
+
expect(last_response).to have_header('WWW-Authenticate' => auth_response_header)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#token_data' do
|
93
|
+
subject(:token_data) { get '/token_data', params, env }
|
94
|
+
|
95
|
+
before do
|
96
|
+
allow(G5AuthenticatableApi::Services::TokenInfo).to receive(:new).
|
97
|
+
and_return(token_info)
|
98
|
+
end
|
99
|
+
let(:token_info) { double(:token_info, token_data: mock_token_data) }
|
100
|
+
let(:mock_token_data) { double(:token_data, to_json: '{result: mock_token_data_json}') }
|
101
|
+
|
102
|
+
it 'initializes the token info service correctly' do
|
103
|
+
token_data
|
104
|
+
expect(G5AuthenticatableApi::Services::TokenInfo).to have_received(:new).
|
105
|
+
with(params, headers, warden)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'returns the token info from the service' do
|
109
|
+
token_data
|
110
|
+
expect(last_response.body).to eq(mock_token_data.to_json)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe '#current_user' do
|
115
|
+
subject(:current_user) { get '/current_user', params, env }
|
116
|
+
|
117
|
+
before do
|
118
|
+
allow(G5AuthenticatableApi::Services::UserFetcher).to receive(:new).
|
119
|
+
and_return(user_fetcher)
|
120
|
+
end
|
121
|
+
let(:user_fetcher) { double(:user_fetcher, current_user: user) }
|
122
|
+
let(:user) { double(:user, to_json: '{result: mock_user_json}') }
|
123
|
+
|
124
|
+
it 'initializes the user fetcher service correctly' do
|
125
|
+
current_user
|
126
|
+
expect(G5AuthenticatableApi::Services::UserFetcher).to have_received(:new).
|
127
|
+
with(params, headers, warden)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'returns the user from the service' do
|
131
|
+
current_user
|
132
|
+
expect(last_response.body).to eq(user.to_json)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe '#access_token' do
|
137
|
+
subject(:access_token) { get '/access_token', params, env }
|
138
|
+
|
139
|
+
before do
|
140
|
+
allow(G5AuthenticatableApi::Services::TokenInfo).to receive(:new).
|
141
|
+
and_return(token_info)
|
142
|
+
end
|
143
|
+
let(:token_info) { double(:token_info, access_token: token_value) }
|
144
|
+
|
145
|
+
it 'initializes the token info service correctly' do
|
146
|
+
access_token
|
147
|
+
expect(G5AuthenticatableApi::Services::TokenInfo).to have_received(:new).
|
148
|
+
with(params, headers, warden)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'returns the access token from the service' do
|
152
|
+
access_token
|
153
|
+
expect(last_response.body).to eq([token_value].to_json)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe G5AuthenticatableApi::Helpers::Rails, type: :controller do
|
4
|
+
controller(ActionController::Base) do
|
5
|
+
before_action :authenticate_api_user!, only: :index
|
6
|
+
|
7
|
+
def index
|
8
|
+
render json: [], status: :ok
|
9
|
+
end
|
10
|
+
|
11
|
+
def new
|
12
|
+
render json: [], status: :ok
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:warden) { double(:warden) }
|
17
|
+
before { request.env['warden'] = warden }
|
18
|
+
|
19
|
+
describe '#authenticate_api_user!' do
|
20
|
+
subject(:authenticate_api_user!) { get :index, access_token: token_value }
|
21
|
+
|
22
|
+
let(:token_value) { 'abc123' }
|
23
|
+
|
24
|
+
let(:token_validator) do
|
25
|
+
double(:token_validator, valid?: valid,
|
26
|
+
auth_response_header: auth_response_header,
|
27
|
+
access_token: token_value)
|
28
|
+
end
|
29
|
+
before do
|
30
|
+
allow(G5AuthenticatableApi::Services::TokenValidator).to receive(:new).
|
31
|
+
and_return(token_validator)
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when token is valid' do
|
35
|
+
let(:valid) { true }
|
36
|
+
let(:auth_response_header) {}
|
37
|
+
|
38
|
+
it 'initializes the token validator correctly' do
|
39
|
+
authenticate_api_user!
|
40
|
+
expect(G5AuthenticatableApi::Services::TokenValidator).to have_received(:new).
|
41
|
+
with(request.params,
|
42
|
+
an_instance_of(ActionDispatch::Http::Headers),
|
43
|
+
warden)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'is successful' do
|
47
|
+
authenticate_api_user!
|
48
|
+
expect(response).to be_success
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'does not set the authenticate response header' do
|
52
|
+
authenticate_api_user!
|
53
|
+
expect(response).to_not have_header('WWW-Authenticate')
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when token is invalid' do
|
59
|
+
let(:valid) { false }
|
60
|
+
let(:auth_response_header) { 'whatever' }
|
61
|
+
|
62
|
+
it 'is unauthorized' do
|
63
|
+
authenticate_api_user!
|
64
|
+
expect(response).to be_unauthorized
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'renders an error message' do
|
68
|
+
authenticate_api_user!
|
69
|
+
expect(JSON.parse(response.body)).to eq('error' => 'Unauthorized')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '#token_data' do
|
75
|
+
subject(:token_data) { controller.token_data }
|
76
|
+
|
77
|
+
before do
|
78
|
+
allow(G5AuthenticatableApi::Services::TokenInfo).to receive(:new).
|
79
|
+
and_return(token_info)
|
80
|
+
end
|
81
|
+
let(:token_info) { double(:user_fetcher, token_data: mock_token_data) }
|
82
|
+
let(:mock_token_data) { double(:token_info) }
|
83
|
+
|
84
|
+
before { get :new, access_token: 'abc123' }
|
85
|
+
|
86
|
+
it 'initializes the token info service correctly' do
|
87
|
+
token_data
|
88
|
+
expect(G5AuthenticatableApi::Services::TokenInfo).to have_received(:new).
|
89
|
+
with(request.params,
|
90
|
+
an_instance_of(ActionDispatch::Http::Headers),
|
91
|
+
warden)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'returns the token data from the service' do
|
95
|
+
expect(token_data).to eq(mock_token_data)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#access_token' do
|
100
|
+
subject(:access_token) { controller.access_token }
|
101
|
+
|
102
|
+
before do
|
103
|
+
allow(G5AuthenticatableApi::Services::TokenInfo).to receive(:new).
|
104
|
+
and_return(token_info)
|
105
|
+
end
|
106
|
+
let(:token_info) { double(:token_info, access_token: token_value) }
|
107
|
+
let(:token_value) { 'abc123' }
|
108
|
+
|
109
|
+
before { get :new, access_token: token_value }
|
110
|
+
|
111
|
+
it 'initializes the token info service correctly' do
|
112
|
+
access_token
|
113
|
+
expect(G5AuthenticatableApi::Services::TokenInfo).to have_received(:new).
|
114
|
+
with(request.params,
|
115
|
+
an_instance_of(ActionDispatch::Http::Headers),
|
116
|
+
warden)
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'returns the access token from the service' do
|
120
|
+
expect(access_token).to eq(token_value)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe '#current_api_user' do
|
125
|
+
subject(:current_api_user) { controller.current_api_user }
|
126
|
+
|
127
|
+
before do
|
128
|
+
allow(G5AuthenticatableApi::Services::UserFetcher).to receive(:new).
|
129
|
+
and_return(user_fetcher)
|
130
|
+
end
|
131
|
+
let(:user_fetcher) { double(:user_fetcher, current_user: user) }
|
132
|
+
let(:user) { double(:user) }
|
133
|
+
|
134
|
+
before { get :new, access_token: 'abc123' }
|
135
|
+
|
136
|
+
it 'initializes the user fetcher service correctly' do
|
137
|
+
current_api_user
|
138
|
+
expect(G5AuthenticatableApi::Services::UserFetcher).to have_received(:new).
|
139
|
+
with(request.params,
|
140
|
+
an_instance_of(ActionDispatch::Http::Headers),
|
141
|
+
warden)
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'returns the user from the service' do
|
145
|
+
expect(current_api_user).to eq(user)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe G5AuthenticatableApi::Services::TokenInfo do
|
4
|
+
subject(:token_info) { described_class.new(params, headers, warden) }
|
5
|
+
let(:params) { {'access_token' => token_value} }
|
6
|
+
let(:headers) { Hash.new }
|
7
|
+
let(:warden) {}
|
8
|
+
|
9
|
+
let(:token_value) { 'abc123' }
|
10
|
+
|
11
|
+
describe '#initialize' do
|
12
|
+
let(:params) { {'foo' => 'bar'} }
|
13
|
+
let(:headers) { {'Content-Type' => 'application/json'} }
|
14
|
+
|
15
|
+
context 'with warden' do
|
16
|
+
let(:warden) { double(:warden) }
|
17
|
+
|
18
|
+
it 'sets the params' do
|
19
|
+
expect(token_info.params).to eq(params)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'sets the headers' do
|
23
|
+
expect(token_info.headers).to eq(headers)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'sets warden' do
|
27
|
+
expect(token_info.warden).to eq(warden)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'without warden' do
|
32
|
+
let(:token_info) { described_class.new(params, headers) }
|
33
|
+
|
34
|
+
it 'sets the params' do
|
35
|
+
expect(token_info.params).to eq(params)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'sets the headers' do
|
39
|
+
expect(token_info.headers).to eq(headers)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'defaults warden to nil' do
|
43
|
+
expect(token_info.warden).to be_nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#access_token' do
|
49
|
+
subject(:access_token) { token_info.access_token }
|
50
|
+
|
51
|
+
context 'with auth header' do
|
52
|
+
let(:headers) { {'Authorization' => "Bearer #{token_value}"} }
|
53
|
+
let(:params) {}
|
54
|
+
|
55
|
+
it 'should extract the token value from the header' do
|
56
|
+
expect(access_token).to eq(token_value)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'with all caps authorization key' do
|
61
|
+
let(:headers) { {'AUTHORIZATION' => "Bearer #{token_value}"} }
|
62
|
+
let(:params) {}
|
63
|
+
|
64
|
+
it 'should extract the token value from the header' do
|
65
|
+
expect(access_token).to eq(token_value)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'with auth param' do
|
70
|
+
let(:params) { {'access_token' => token_value} }
|
71
|
+
let(:headers) {}
|
72
|
+
|
73
|
+
it 'should extract the token value from the access_token parameter' do
|
74
|
+
expect(access_token).to eq(token_value)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'with warden user' do
|
79
|
+
let(:warden) { double(:warden, user: user) }
|
80
|
+
let(:user) { FactoryGirl.build_stubbed(:user) }
|
81
|
+
|
82
|
+
context 'without token on request' do
|
83
|
+
let(:params) {}
|
84
|
+
let(:headers) {}
|
85
|
+
|
86
|
+
it 'should extract the token value from the session user' do
|
87
|
+
expect(access_token).to eq(user.g5_access_token)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'with auth param' do
|
92
|
+
let(:params) { {'access_token' => token_value} }
|
93
|
+
let(:headers) {}
|
94
|
+
|
95
|
+
it 'should give precedence to the token on the request' do
|
96
|
+
expect(access_token).to eq(token_value)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe '#token_data' do
|
103
|
+
subject(:token_data) { token_info.token_data }
|
104
|
+
|
105
|
+
context 'when token is valid' do
|
106
|
+
include_context 'valid access token'
|
107
|
+
|
108
|
+
it 'includes the resource_owner_id' do
|
109
|
+
expect(token_data.resource_owner_id).to eq(raw_token_info['resource_owner_id'])
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'includes the expires_in_seconds' do
|
113
|
+
expect(token_data.expires_in_seconds).to eq(raw_token_info['expires_in_seconds'])
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'includes the application_uid' do
|
117
|
+
expect(token_data.application_uid).to eq(raw_token_info['application']['uid'])
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'includes the scopes' do
|
121
|
+
expect(token_data.scopes). to eq(raw_token_info['scopes'])
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'when token is invalid' do
|
126
|
+
include_context 'invalid access token'
|
127
|
+
|
128
|
+
it 'raises an error' do
|
129
|
+
expect { token_data }.to raise_error
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe '#auth_client' do
|
135
|
+
subject(:auth_client) { token_info.auth_client }
|
136
|
+
|
137
|
+
let(:client) { double(:auth_client) }
|
138
|
+
before do
|
139
|
+
allow(G5AuthenticationClient::Client).to receive(:new).and_return(client)
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'returns the initialized client' do
|
143
|
+
expect(auth_client).to eq(client)
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'initializes the client with the token' do
|
147
|
+
auth_client
|
148
|
+
expect(G5AuthenticationClient::Client).to have_received(:new).
|
149
|
+
with(hash_including(access_token: token_value))
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'disables password access on the client' do
|
153
|
+
auth_client
|
154
|
+
expect(G5AuthenticationClient::Client).to have_received(:new).
|
155
|
+
with(hash_including(allow_password_credentials: 'false'))
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
data/spec/lib/g5_authenticatable_api/{token_validator_spec.rb → services/token_validator_spec.rb}
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe G5AuthenticatableApi::TokenValidator do
|
3
|
+
describe G5AuthenticatableApi::Services::TokenValidator do
|
4
4
|
subject { validator }
|
5
5
|
|
6
6
|
let(:validator) { described_class.new(params, headers, warden) }
|
@@ -10,60 +10,6 @@ describe G5AuthenticatableApi::TokenValidator do
|
|
10
10
|
let(:token_value) { 'abc123' }
|
11
11
|
let(:warden) {}
|
12
12
|
|
13
|
-
describe '#access_token' do
|
14
|
-
subject(:access_token) { validator.access_token }
|
15
|
-
|
16
|
-
context 'with auth header' do
|
17
|
-
let(:headers) { {'Authorization' => "Bearer #{token_value}"} }
|
18
|
-
let(:params) {}
|
19
|
-
|
20
|
-
it 'should extract the token value from the header' do
|
21
|
-
expect(access_token).to eq(token_value)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
context 'with all caps authorization key' do
|
26
|
-
let(:headers) { {'AUTHORIZATION' => "Bearer #{token_value}"} }
|
27
|
-
let(:params) {}
|
28
|
-
|
29
|
-
it 'should extract the token value from the header' do
|
30
|
-
expect(access_token).to eq(token_value)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
context 'with auth param' do
|
35
|
-
let(:params) { {'access_token' => token_value} }
|
36
|
-
let(:headers) {}
|
37
|
-
|
38
|
-
it 'should extract the token value from the access_token parameter' do
|
39
|
-
expect(access_token).to eq(token_value)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
context 'with warden user' do
|
44
|
-
let(:warden) { double(:warden, user: user) }
|
45
|
-
let(:user) { FactoryGirl.build_stubbed(:user) }
|
46
|
-
|
47
|
-
context 'without token on request' do
|
48
|
-
let(:params) {}
|
49
|
-
let(:headers) {}
|
50
|
-
|
51
|
-
it 'should extract the token value from the session user' do
|
52
|
-
expect(access_token).to eq(user.g5_access_token)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
context 'with auth param' do
|
57
|
-
let(:params) { {'access_token' => token_value} }
|
58
|
-
let(:headers) {}
|
59
|
-
|
60
|
-
it 'should give precedence to the token on the request' do
|
61
|
-
expect(access_token).to eq(token_value)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
13
|
describe '#validate!' do
|
68
14
|
subject(:validate!) { validator.validate! }
|
69
15
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe G5AuthenticatableApi::Services::UserFetcher do
|
4
|
+
subject(:user_fetcher) { described_class.new(params, headers, warden) }
|
5
|
+
let(:params) { {'access_token' => token_value} }
|
6
|
+
let(:token_value) { 'abc123' }
|
7
|
+
let(:headers) {}
|
8
|
+
let(:warden) {}
|
9
|
+
|
10
|
+
describe '#current_user' do
|
11
|
+
include_context 'current auth user'
|
12
|
+
|
13
|
+
subject(:current_user) { user_fetcher.current_user }
|
14
|
+
|
15
|
+
context 'when there is no warden user' do
|
16
|
+
it_behaves_like 'an auth user' do
|
17
|
+
let(:user) { current_user }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when there is a warden user' do
|
22
|
+
let(:warden) { double(:warden, user: warden_user) }
|
23
|
+
|
24
|
+
context 'when the access_token is for the warden user' do
|
25
|
+
let(:warden_user) { double(:user, g5_access_token: token_value) }
|
26
|
+
|
27
|
+
it 'returns the warden user' do
|
28
|
+
expect(current_user).to eq(warden_user)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'when the access token is for a different user' do
|
33
|
+
let(:warden_user) { double(:user, g5_access_token: "#{token_value}42") }
|
34
|
+
|
35
|
+
it_behaves_like 'an auth user' do
|
36
|
+
let(:user) { current_user }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
shared_context 'current auth user' do
|
2
|
+
before do
|
3
|
+
stub_request(:get, 'auth.g5search.com/v1/me').
|
4
|
+
with(headers: {'Authorization'=>"Bearer #{token_value}"}).
|
5
|
+
to_return(status: 200,
|
6
|
+
body: raw_user_info.to_json,
|
7
|
+
headers: {'Content-Type' => 'application/json'})
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:raw_user_info) do
|
11
|
+
{
|
12
|
+
'id' => 42,
|
13
|
+
'email' => 'fred.rogers@thehood.net',
|
14
|
+
'first_name' => 'Fred',
|
15
|
+
'last_name' => 'Rogers',
|
16
|
+
'phone_number' => '(555) 555-1212',
|
17
|
+
'organization_name' => 'The Neighborhood',
|
18
|
+
'title' => 'Head Cardigan Wearer',
|
19
|
+
'roles' => [
|
20
|
+
{
|
21
|
+
'name' => 'viewer',
|
22
|
+
'type' => 'GLOBAL',
|
23
|
+
'urn' => nil
|
24
|
+
},
|
25
|
+
{
|
26
|
+
'name' => 'admin',
|
27
|
+
'type' => 'G5Updatable::Client',
|
28
|
+
'urn' => 'g5-c-some-randomly-generated-string'
|
29
|
+
}
|
30
|
+
]
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
@@ -2,6 +2,17 @@ shared_context 'valid access token' do
|
|
2
2
|
before do
|
3
3
|
stub_request(:get, 'auth.g5search.com/oauth/token/info').
|
4
4
|
with(headers: {'Authorization'=>"Bearer #{token_value}"}).
|
5
|
-
|
5
|
+
to_return(status: 200,
|
6
|
+
body: raw_token_info.to_json,
|
7
|
+
headers: {'Content-Type' => 'application/json'})
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:raw_token_info) do
|
11
|
+
{
|
12
|
+
'resource_owner_id' => '42',
|
13
|
+
'scopes' => [],
|
14
|
+
'expires_in_seconds' => 120,
|
15
|
+
'application' => {'uid' => 'abcdefg112358'}
|
16
|
+
}
|
6
17
|
end
|
7
18
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
shared_examples_for 'an auth user' do
|
2
|
+
it 'has the correct id' do
|
3
|
+
expect(user.id).to eq(raw_user_info['id'])
|
4
|
+
end
|
5
|
+
|
6
|
+
it 'has the correct email' do
|
7
|
+
expect(user.email).to eq(raw_user_info['email'])
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'has the correct first_name' do
|
11
|
+
expect(user.first_name).to eq(raw_user_info['first_name'])
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'has the correct last_name' do
|
15
|
+
expect(user.last_name).to eq(raw_user_info['last_name'])
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'has the correct phone_number' do
|
19
|
+
expect(user.phone_number).to eq(raw_user_info['phone_number'])
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'has the correct title' do
|
23
|
+
expect(user.title).to eq(raw_user_info['title'])
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'has the correct organization_name' do
|
27
|
+
expect(user.organization_name).to eq(raw_user_info['organization_name'])
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'has the correct number of roles' do
|
31
|
+
expect(user.roles.count).to eq(raw_user_info['roles'].count)
|
32
|
+
end
|
33
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: g5_authenticatable_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maeve Revels
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-05-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0.
|
33
|
+
version: '0.4'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '0.
|
40
|
+
version: '0.4'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: activesupport
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -62,18 +62,20 @@ files:
|
|
62
62
|
- ".gitignore"
|
63
63
|
- ".rspec"
|
64
64
|
- ".ruby-version"
|
65
|
+
- ".travis.yml"
|
65
66
|
- CHANGELOG.md
|
66
67
|
- Gemfile
|
67
68
|
- LICENSE.txt
|
68
69
|
- README.md
|
69
70
|
- Rakefile
|
70
|
-
- circle.yml
|
71
71
|
- g5_authenticatable_api.gemspec
|
72
72
|
- lib/g5_authenticatable_api.rb
|
73
73
|
- lib/g5_authenticatable_api/helpers/grape.rb
|
74
74
|
- lib/g5_authenticatable_api/helpers/rails.rb
|
75
75
|
- lib/g5_authenticatable_api/railtie.rb
|
76
|
-
- lib/g5_authenticatable_api/
|
76
|
+
- lib/g5_authenticatable_api/services/token_info.rb
|
77
|
+
- lib/g5_authenticatable_api/services/token_validator.rb
|
78
|
+
- lib/g5_authenticatable_api/services/user_fetcher.rb
|
77
79
|
- lib/g5_authenticatable_api/version.rb
|
78
80
|
- spec/dummy/README.rdoc
|
79
81
|
- spec/dummy/Rakefile
|
@@ -132,14 +134,20 @@ files:
|
|
132
134
|
- spec/dummy/vendor/assets/javascripts/.keep
|
133
135
|
- spec/dummy/vendor/assets/stylesheets/.keep
|
134
136
|
- spec/factories/user.rb
|
135
|
-
- spec/lib/g5_authenticatable_api/
|
137
|
+
- spec/lib/g5_authenticatable_api/helpers/grape_spec.rb
|
138
|
+
- spec/lib/g5_authenticatable_api/helpers/rails_spec.rb
|
139
|
+
- spec/lib/g5_authenticatable_api/services/token_info_spec.rb
|
140
|
+
- spec/lib/g5_authenticatable_api/services/token_validator_spec.rb
|
141
|
+
- spec/lib/g5_authenticatable_api/services/user_fetcher_spec.rb
|
136
142
|
- spec/lib/g5_authenticatable_api/version_spec.rb
|
137
143
|
- spec/requests/grape_api_spec.rb
|
138
144
|
- spec/requests/rails_api_spec.rb
|
139
145
|
- spec/spec_helper.rb
|
140
146
|
- spec/support/factory_girl.rb
|
147
|
+
- spec/support/shared_contexts/current_auth_user.rb
|
141
148
|
- spec/support/shared_contexts/invalid_access_token.rb
|
142
149
|
- spec/support/shared_contexts/valid_access_token.rb
|
150
|
+
- spec/support/shared_examples/auth_user.rb
|
143
151
|
- spec/support/shared_examples/token_authenticatable_api.rb
|
144
152
|
- spec/support/shared_examples/token_validation.rb
|
145
153
|
- spec/support/shared_examples/warden_authenticatable_api.rb
|
@@ -164,7 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
164
172
|
version: '0'
|
165
173
|
requirements: []
|
166
174
|
rubyforge_project:
|
167
|
-
rubygems_version: 2.2.
|
175
|
+
rubygems_version: 2.2.3
|
168
176
|
signing_key:
|
169
177
|
specification_version: 4
|
170
178
|
summary: Helpers for securing APIs with G5
|
@@ -226,14 +234,20 @@ test_files:
|
|
226
234
|
- spec/dummy/vendor/assets/javascripts/.keep
|
227
235
|
- spec/dummy/vendor/assets/stylesheets/.keep
|
228
236
|
- spec/factories/user.rb
|
229
|
-
- spec/lib/g5_authenticatable_api/
|
237
|
+
- spec/lib/g5_authenticatable_api/helpers/grape_spec.rb
|
238
|
+
- spec/lib/g5_authenticatable_api/helpers/rails_spec.rb
|
239
|
+
- spec/lib/g5_authenticatable_api/services/token_info_spec.rb
|
240
|
+
- spec/lib/g5_authenticatable_api/services/token_validator_spec.rb
|
241
|
+
- spec/lib/g5_authenticatable_api/services/user_fetcher_spec.rb
|
230
242
|
- spec/lib/g5_authenticatable_api/version_spec.rb
|
231
243
|
- spec/requests/grape_api_spec.rb
|
232
244
|
- spec/requests/rails_api_spec.rb
|
233
245
|
- spec/spec_helper.rb
|
234
246
|
- spec/support/factory_girl.rb
|
247
|
+
- spec/support/shared_contexts/current_auth_user.rb
|
235
248
|
- spec/support/shared_contexts/invalid_access_token.rb
|
236
249
|
- spec/support/shared_contexts/valid_access_token.rb
|
250
|
+
- spec/support/shared_examples/auth_user.rb
|
237
251
|
- spec/support/shared_examples/token_authenticatable_api.rb
|
238
252
|
- spec/support/shared_examples/token_validation.rb
|
239
253
|
- spec/support/shared_examples/warden_authenticatable_api.rb
|
data/circle.yml
DELETED
@@ -1,78 +0,0 @@
|
|
1
|
-
module G5AuthenticatableApi
|
2
|
-
class TokenValidator
|
3
|
-
attr_reader :error
|
4
|
-
|
5
|
-
def initialize(params={},headers={},warden=nil)
|
6
|
-
@params = params || {}
|
7
|
-
@headers = headers || {}
|
8
|
-
@warden = warden
|
9
|
-
end
|
10
|
-
|
11
|
-
def validate!
|
12
|
-
begin
|
13
|
-
auth_client.token_info unless skip_validation?
|
14
|
-
rescue StandardError => @error
|
15
|
-
raise error
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def valid?
|
20
|
-
begin
|
21
|
-
validate!
|
22
|
-
true
|
23
|
-
rescue StandardError => e
|
24
|
-
false
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def access_token
|
29
|
-
@access_token ||= (extract_token_from_header ||
|
30
|
-
@params['access_token'] ||
|
31
|
-
@warden.try(:user).try(:g5_access_token))
|
32
|
-
end
|
33
|
-
|
34
|
-
def auth_response_header
|
35
|
-
if error
|
36
|
-
auth_header = "Bearer"
|
37
|
-
|
38
|
-
if access_token
|
39
|
-
auth_header << " error=\"#{error_code}\""
|
40
|
-
auth_header << ",error_description=\"#{error_description}\"" if error_description
|
41
|
-
end
|
42
|
-
|
43
|
-
auth_header
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def auth_client
|
48
|
-
@auth_client ||= G5AuthenticationClient::Client.new(allow_password_credentials: 'false',
|
49
|
-
access_token: access_token)
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
def error_code
|
54
|
-
error_code = error.code if error.respond_to?(:code)
|
55
|
-
error_code || 'invalid_request'
|
56
|
-
end
|
57
|
-
|
58
|
-
def error_description
|
59
|
-
error_description = error.description if error.respond_to?(:description)
|
60
|
-
error_description
|
61
|
-
end
|
62
|
-
|
63
|
-
def extract_token_from_header
|
64
|
-
if authorization_header
|
65
|
-
parts = authorization_header.match(/Bearer (?<access_token>\S+)/)
|
66
|
-
parts['access_token']
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def skip_validation?
|
71
|
-
@warden.try(:user) && !G5AuthenticatableApi.strict_token_validation
|
72
|
-
end
|
73
|
-
|
74
|
-
def authorization_header
|
75
|
-
@headers['Authorization'] || @headers['AUTHORIZATION']
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|