aptible-auth 0.11.3 → 0.11.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 93d75943842f3e5ed625c2da64dc0c7ee83ffd69
4
- data.tar.gz: 84e6d466d82a90fb85060294a5b93f1a9702925d
3
+ metadata.gz: f84b4c267178de22880242466b3cf1094be20fb6
4
+ data.tar.gz: f77b05461a7db6598902a9bdfbb8140691e493b9
5
5
  SHA512:
6
- metadata.gz: 73d27eeac4954a579d97e8148f23f1a96316825e7b95c6641870db5296b6a5bdbf789e0ff12a587fc3e7cb0536c6a4d457ca2fb8a58b15f9e7950f47657a8a86
7
- data.tar.gz: bd1bf9f3fd30a2d2196807507a754c9183d85a9fc0f6cdf424f465caf837b30958e13a6793fc7fcf94b5dc0fa28024fb98361e2f4ccac5b878624c6eb40a8412
6
+ metadata.gz: 8a373677f9e0aadfae4220f181ceb31eea72ad3b756960bdf56b2e6039ee99156470101b4f256dca089345d13dbabdc0cc9e9e0c052d8074adbbdc4be83572d5
7
+ data.tar.gz: 2a187d5ad9ded64e5d2a0689c43bf49f429230eff390425af7136507172edab58ef129b4bb3a679e96ebad851838c3fc6138b6fbecf2383418769dbd2175cc33
@@ -3,44 +3,70 @@ require 'oauth2'
3
3
  module Aptible
4
4
  module Auth
5
5
  class Token < Resource
6
- attr_accessor :access_token, :refresh_token, :expires_at
6
+ # Unlike other resources, tokens aren't created in a REST fashion.
7
+ # Instead, they're created via OAuth grants. This means we need to
8
+ # override the way HyperResource / aptible-resource normally do things
9
+ # and plug in an OAuth library.
10
+ #
11
+ # To do so, we take control of the creation arguments and feed them into
12
+ # the OAuth2 library (in Token.create!), and then feed the response back
13
+ # to HyperResource (in Token#apply_oauth_response).
14
+ belongs_to :user
15
+ belongs_to :actor
16
+
17
+ field :access_token
18
+ field :refresh_token
19
+ field :expires_at
7
20
 
8
21
  def self.create(options)
22
+ # For backwards compatibility: we used to throw in .create (which isn't
23
+ # consistent with other resources), and we probably need to continue
24
+ # doing this. We also need to continue throwing a OAuth2::Error.
9
25
  token = new
10
26
  token.process_options(options)
11
27
  token
12
28
  end
13
29
 
30
+ def self.create!(options)
31
+ Token.create(options)
32
+ rescue OAuth2::Error => e
33
+ # Rethrow OAuth2::Error as HyperResource::ResponseError for
34
+ # aptible-resource to handle
35
+ raise HyperResource::ResponseError.new(e.code, response: e.response,
36
+ cause: e)
37
+ end
38
+
14
39
  def authenticate_user(email, password, options = {})
15
40
  options[:scope] ||= 'manage'
16
- response = oauth.password.get_token(email, password, options)
17
- parse_oauth_response(response)
41
+ oauth_token = oauth.password.get_token(email, password, options)
42
+ apply_oauth_response(oauth_token)
18
43
  end
19
44
 
20
45
  def authenticate_client(id, secret, subject, options = {})
21
46
  options[:scope] ||= 'manage'
22
- response = oauth.assertion.get_token({
47
+ oauth_token = oauth.assertion.get_token({
23
48
  iss: id,
24
49
  sub: subject
25
50
  }.merge(signing_params_from_secret(secret).merge(options)))
26
- parse_oauth_response(response)
51
+ apply_oauth_response(oauth_token)
27
52
  end
28
53
 
29
54
  def authenticate_impersonate(subject_token, subject_token_type, options)
30
55
  # TODO: This duplicates aptible-resource, is it worth extracting?
31
- token = case token = options.delete(:token)
32
- when Aptible::Resource::Base then token.access_token
33
- when Fridge::AccessToken then token.to_s
34
- when String then token
35
- else bearer_token
36
- end
56
+ actor_token = \
57
+ case actor_token = options.delete(:token)
58
+ when Aptible::Resource::Base then actor_token.access_token
59
+ when Fridge::AccessToken then actor_token.to_s
60
+ when String then actor_token
61
+ else bearer_token
62
+ end
37
63
 
38
64
  # TODO: Do we want to check whether the token is non-nil at this stage?
39
65
  options[:scope] ||= 'manage'
40
- response = oauth.token_exchange.get_token(
41
- token, 'urn:ietf:params:oauth:token-type:jwt',
66
+ oauth_token = oauth.token_exchange.get_token(
67
+ actor_token, 'urn:ietf:params:oauth:token-type:jwt',
42
68
  subject_token, subject_token_type, options)
43
- parse_oauth_response(response)
69
+ apply_oauth_response(oauth_token)
44
70
  end
45
71
 
46
72
  def oauth
@@ -68,12 +94,34 @@ module Aptible
68
94
  end
69
95
  end
70
96
 
97
+ def token
98
+ # If the user set an arbitrary token, then we'll return that one,
99
+ # otherwise we'll fall back to the Token itself, which makes it
100
+ # possible to create a token and immediately access it #user or #actor
101
+ # methods.
102
+ # NOTE: Setting the token after the fact probably doesn't work anyway,
103
+ # since the Authorization header won't be updated.
104
+ @token || access_token
105
+ end
106
+
107
+ def expires_at
108
+ # The Auth API returns the expiry as a timestamp (i.e. an Integer), but
109
+ # our API client knows only to handle times as strings. This overrides
110
+ # the field method for expires_at to return a Time despite the
111
+ # underlying API field being an Integer.
112
+ Time.at(attributes[:expires_at])
113
+ end
114
+
71
115
  private
72
116
 
73
- def parse_oauth_response(response)
74
- @access_token = response.token
75
- @refresh_token = response.refresh_token
76
- @expires_at = Time.at(response.expires_at)
117
+ def apply_oauth_response(oauth_token)
118
+ # apply() + loaded is what HyperResource normally does after
119
+ # deserializing a response back from the API. On top of that, we need
120
+ # to set the Authorization header so that the token can be used to make
121
+ # further API requests (e.g. accessing token#user or token#actor).
122
+ adapter.apply(oauth_token.to_hash, self)
123
+ self.loaded = true
124
+ headers['Authorization'] = "Bearer #{bearer_token}"
77
125
  self
78
126
  end
79
127
 
@@ -1,5 +1,5 @@
1
1
  module Aptible
2
2
  module Auth
3
- VERSION = '0.11.3'
3
+ VERSION = '0.11.5'
4
4
  end
5
5
  end
@@ -5,9 +5,16 @@ describe Aptible::Auth::Token do
5
5
  let(:response) { double OAuth2::AccessToken }
6
6
 
7
7
  before { subject.stub(:oauth) { oauth } }
8
- before { response.stub(:token) }
9
- before { response.stub(:refresh_token) }
10
- before { response.stub(:expires_at) { Time.now.to_i } }
8
+ let(:expires_at) { Time.now - Random.rand(1000) }
9
+ before do
10
+ response.stub(:to_hash) do
11
+ {
12
+ access_token: 'access_token',
13
+ refresh_token: nil,
14
+ expires_at: expires_at.to_i
15
+ }
16
+ end
17
+ end
11
18
 
12
19
  describe '.create' do
13
20
  it 'should call #authenticate_user if passed :email and :password' do
@@ -60,10 +67,20 @@ describe Aptible::Auth::Token do
60
67
  end
61
68
 
62
69
  it 'should set the access_token' do
63
- oauth.stub_chain(:password, :get_token, :token) { 'access_token' }
64
70
  subject.authenticate_user(*args)
65
71
  expect(subject.access_token).to eq 'access_token'
66
72
  end
73
+
74
+ it 'should set the Authorization header' do
75
+ subject.authenticate_user(*args)
76
+ expect(subject.headers['Authorization']).to eq 'Bearer access_token'
77
+ end
78
+
79
+ it 'should set the expires_at property' do
80
+ subject.authenticate_user(*args)
81
+ expect(subject.expires_at).to be_a Time
82
+ expect(subject.expires_at.to_i).to eq expires_at.to_i
83
+ end
67
84
  end
68
85
 
69
86
  describe '#authenticate_client' do
@@ -96,10 +113,29 @@ describe Aptible::Auth::Token do
96
113
  end
97
114
 
98
115
  it 'should set the access_token' do
99
- oauth.stub_chain(:assertion, :get_token, :token) { 'access_token' }
100
116
  subject.authenticate_client(*args)
101
117
  expect(subject.access_token).to eq 'access_token'
102
118
  end
119
+
120
+ it 'should set the Authorization header' do
121
+ subject.authenticate_client(*args)
122
+ expect(subject.headers['Authorization']).to eq 'Bearer access_token'
123
+ end
124
+ end
125
+
126
+ describe '#authenticate_impersonate' do
127
+ let(:args) { ['foo@bar.com', 'aptible:user:email', {}] }
128
+ before { oauth.stub_chain(:token_exchange, :get_token) { response } }
129
+
130
+ it 'should set the access_token' do
131
+ subject.authenticate_impersonate(*args)
132
+ expect(subject.access_token).to eq 'access_token'
133
+ end
134
+
135
+ it 'should set the Authorization header' do
136
+ subject.authenticate_impersonate(*args)
137
+ expect(subject.headers['Authorization']).to eq 'Bearer access_token'
138
+ end
103
139
  end
104
140
 
105
141
  describe '#signing_params_from_secret' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aptible-auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.3
4
+ version: 0.11.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Frank Macreery
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-06 00:00:00.000000000 Z
11
+ date: 2016-06-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aptible-billing