fog-brightbox 1.5.0 → 1.6.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/CHANGELOG.md +22 -0
- data/lib/fog/brightbox/compute/shared.rb +26 -10
- data/lib/fog/brightbox/compute.rb +4 -0
- data/lib/fog/brightbox/config.rb +12 -0
- data/lib/fog/brightbox/oauth2.rb +47 -7
- data/lib/fog/brightbox/version.rb +1 -1
- data/spec/fog/brightbox/compute/credentials_spec.rb +41 -0
- data/spec/fog/brightbox/compute/get_access_token_spec.rb +305 -0
- data/spec/fog/brightbox/compute/two_factor_spec.rb +53 -0
- data/spec/fog/brightbox/oauth2/user_credentials_strategy_spec.rb +20 -0
- metadata +7 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ed1e1f608af748e905cfeafeb9f1a72674074f11720c854988e324de9e1fa84
|
4
|
+
data.tar.gz: c078791baaf84d13fb09c1f0d8ff17b9c1116f247cfb8c73647b529b97ce3bea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fbfd902483fc3bb101255c4915d8fec6e54c5edf2995f6aa1b0eebe510e210a0b6173b109aa19811fee8fabc1c5f05c294d36b49fbcc9b15056bedf22e1ebac2
|
7
|
+
data.tar.gz: e3b208fa57e74eca736fa8d53683cbfa78645a6aa75a15b680f59365bbd26538cbb373828f92ba4d6de7a79a867e2377500179c9ef18171c35748dd3fa759e17
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
### 1.6.0 / 2022-07-25
|
2
|
+
|
3
|
+
Changes:
|
4
|
+
|
5
|
+
* Added support to opt-in to support Brightbox 2FA within clients.
|
6
|
+
|
7
|
+
Two Factor Authentication (2FA) support:
|
8
|
+
|
9
|
+
Passing `brightbox_support_two_factor` into a configuration will raise a new
|
10
|
+
error when user authentication has failed BUT the presence of the
|
11
|
+
`X-Brightbox-OTP: required` HTTP header is found.
|
12
|
+
|
13
|
+
This new error `Fog::Brightbox::OAuth2::TwoFactorMissingError` can be handled
|
14
|
+
differently by the client and the second factor can be prompted for or
|
15
|
+
recovered from a secure keychain.
|
16
|
+
|
17
|
+
Without opting in, the previous error `Excon::Errors::Unauthorized` is raised.
|
18
|
+
|
19
|
+
To send the OTP, the `brightbox_one_time_password` can be passed into a
|
20
|
+
configuration object or one_time_password can be passed to a credentials
|
21
|
+
object.
|
22
|
+
|
1
23
|
### 1.5.0 / 2022-06-09
|
2
24
|
|
3
25
|
Changes:
|
@@ -39,21 +39,15 @@ module Fog
|
|
39
39
|
@persistent = @config.connection_persistent?
|
40
40
|
@connection = Fog::Core::Connection.new(@api_url, @persistent, @connection_options)
|
41
41
|
|
42
|
-
# Authentication options
|
43
|
-
client_id = @config.client_id
|
44
|
-
client_secret = @config.client_secret
|
45
|
-
|
46
|
-
username = @config.username
|
47
|
-
password = @config.password
|
48
42
|
@configured_account = @config.account
|
49
43
|
# Request account can be changed at anytime and changes behaviour of future requests
|
50
44
|
@scoped_account = @configured_account
|
51
45
|
|
52
|
-
|
53
|
-
@
|
46
|
+
@two_factor = @config.two_factor?
|
47
|
+
@one_time_password = @config.one_time_password
|
54
48
|
|
55
49
|
# If existing tokens have been cached, allow continued use of them in the service
|
56
|
-
|
50
|
+
credentials.update_tokens(@config.cached_access_token, @config.cached_refresh_token)
|
57
51
|
|
58
52
|
@token_management = @config.managed_tokens?
|
59
53
|
end
|
@@ -73,6 +67,12 @@ module Fog
|
|
73
67
|
@scoped_account = @configured_account
|
74
68
|
end
|
75
69
|
|
70
|
+
def credentials
|
71
|
+
client_id = @config.client_id
|
72
|
+
client_secret = @config.client_secret
|
73
|
+
@credentials ||= CredentialSet.new(client_id, client_secret, credential_options)
|
74
|
+
end
|
75
|
+
|
76
76
|
# Returns the scoped account being used for requests
|
77
77
|
#
|
78
78
|
# * For API clients this is the owning account
|
@@ -122,7 +122,7 @@ module Fog
|
|
122
122
|
def get_access_token
|
123
123
|
begin
|
124
124
|
get_access_token!
|
125
|
-
rescue Excon::Errors::Unauthorized, Excon::Errors::BadRequest
|
125
|
+
rescue Fog::Brightbox::OAuth2::TwoFactorMissingError, Excon::Errors::Unauthorized, Excon::Errors::BadRequest
|
126
126
|
@credentials.update_tokens(nil, nil)
|
127
127
|
end
|
128
128
|
@credentials.access_token
|
@@ -154,8 +154,24 @@ module Fog
|
|
154
154
|
@default_image_id ||= (@config.default_image_id || select_default_image)
|
155
155
|
end
|
156
156
|
|
157
|
+
def two_factor?
|
158
|
+
@two_factor || false
|
159
|
+
end
|
160
|
+
|
157
161
|
private
|
158
162
|
|
163
|
+
# This returns a formatted set of options for the credentials for this service
|
164
|
+
#
|
165
|
+
# @return [Hash]
|
166
|
+
def credential_options
|
167
|
+
{
|
168
|
+
username: @config.username,
|
169
|
+
password: @config.password
|
170
|
+
}.tap do |opts|
|
171
|
+
opts[:one_time_password] = @one_time_password if two_factor?
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
159
175
|
# This makes a request of the API based on the configured setting for
|
160
176
|
# token management.
|
161
177
|
#
|
@@ -21,6 +21,10 @@ module Fog
|
|
21
21
|
# Automatic token management
|
22
22
|
recognizes :brightbox_token_management
|
23
23
|
|
24
|
+
# Two Factor Authentication support (2FA)
|
25
|
+
recognizes :brightbox_support_two_factor
|
26
|
+
recognizes :brightbox_one_time_password
|
27
|
+
|
24
28
|
# Excon connection settings
|
25
29
|
recognizes :persistent
|
26
30
|
|
data/lib/fog/brightbox/config.rb
CHANGED
@@ -33,6 +33,10 @@ module Fog
|
|
33
33
|
# Set to specify the client secret to use for requests.
|
34
34
|
# @option options [String] :brightbox_token_management (true)
|
35
35
|
# Set to specify if the service should handle expired tokens or raise an error instead.
|
36
|
+
# @option options [Boolean] :brightbox_support_two_factor
|
37
|
+
# Set to enable two factor authentication (2FA) for this configuration/user
|
38
|
+
# @option options [String] :brightbox_one_time_password
|
39
|
+
# Set to pass through the current one time password when using 2FA
|
36
40
|
# @option options [String] :brightbox_username
|
37
41
|
# Set to specify your user account. Either user identifier (+usr-12345+) or email address
|
38
42
|
# may be used.
|
@@ -226,6 +230,14 @@ module Fog
|
|
226
230
|
def storage_temp_key
|
227
231
|
@options[:brightbox_temp_url_key]
|
228
232
|
end
|
233
|
+
|
234
|
+
def two_factor?
|
235
|
+
@options[:brightbox_support_two_factor] || false
|
236
|
+
end
|
237
|
+
|
238
|
+
def one_time_password
|
239
|
+
@options[:brightbox_one_time_password]
|
240
|
+
end
|
229
241
|
end
|
230
242
|
end
|
231
243
|
end
|
data/lib/fog/brightbox/oauth2.rb
CHANGED
@@ -6,6 +6,10 @@ module Fog
|
|
6
6
|
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10
|
7
7
|
#
|
8
8
|
module OAuth2
|
9
|
+
TWO_FACTOR_HEADER = "X-Brightbox-OTP".freeze
|
10
|
+
|
11
|
+
class TwoFactorMissingError < StandardError; end
|
12
|
+
|
9
13
|
# This builds the simplest form of requesting an access token
|
10
14
|
# based on the arguments passed in
|
11
15
|
#
|
@@ -13,16 +17,38 @@ module Fog
|
|
13
17
|
# @param [CredentialSet] credentials
|
14
18
|
#
|
15
19
|
# @return [Excon::Response]
|
20
|
+
# @raise [Excon::Errors::Unauthorized] if the credentials are rejected by the server
|
21
|
+
# @raise [Fog::Brightbox::OAuth2::TwoFactorMissingError] if opted in to 2FA and server reports header is required
|
16
22
|
def request_access_token(connection, credentials)
|
17
23
|
token_strategy = credentials.best_grant_strategy
|
18
24
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
25
|
+
if two_factor?
|
26
|
+
# When 2FA opt-in is set, we can expect 401 responses as well
|
27
|
+
response = connection.request(
|
28
|
+
:path => "/token",
|
29
|
+
:expects => [200, 401],
|
30
|
+
:headers => token_strategy.headers,
|
31
|
+
:method => "POST",
|
32
|
+
:body => Fog::JSON.encode(token_strategy.authorization_body_data)
|
33
|
+
)
|
34
|
+
|
35
|
+
if response.status == 401 && response.headers[Fog::Brightbox::OAuth2::TWO_FACTOR_HEADER] == "required"
|
36
|
+
raise TwoFactorMissingError
|
37
|
+
elsif response.status == 401
|
38
|
+
raise Excon::Errors.status_error({ expects: 200 }, status: 401)
|
39
|
+
else
|
40
|
+
response
|
41
|
+
end
|
42
|
+
else
|
43
|
+
# Use classic behaviour and return Excon::
|
44
|
+
connection.request(
|
45
|
+
:path => "/token",
|
46
|
+
:expects => 200,
|
47
|
+
:headers => token_strategy.headers,
|
48
|
+
:method => "POST",
|
49
|
+
:body => Fog::JSON.encode(token_strategy.authorization_body_data)
|
50
|
+
)
|
51
|
+
end
|
26
52
|
end
|
27
53
|
|
28
54
|
# Encapsulates credentials required to request access tokens from the
|
@@ -33,12 +59,14 @@ module Fog
|
|
33
59
|
class CredentialSet
|
34
60
|
attr_reader :client_id, :client_secret, :username, :password
|
35
61
|
attr_reader :access_token, :refresh_token, :expires_in
|
62
|
+
attr_reader :one_time_password
|
36
63
|
#
|
37
64
|
# @param [String] client_id
|
38
65
|
# @param [String] client_secret
|
39
66
|
# @param [Hash] options
|
40
67
|
# @option options [String] :username
|
41
68
|
# @option options [String] :password
|
69
|
+
# @option options [String] :one_time_password One Time Password for Two Factor authentication
|
42
70
|
#
|
43
71
|
def initialize(client_id, client_secret, options = {})
|
44
72
|
@client_id = client_id
|
@@ -48,6 +76,7 @@ module Fog
|
|
48
76
|
@access_token = options[:access_token]
|
49
77
|
@refresh_token = options[:refresh_token]
|
50
78
|
@expires_in = options[:expires_in]
|
79
|
+
@one_time_password = options[:one_time_password]
|
51
80
|
end
|
52
81
|
|
53
82
|
# Returns true if user details are available
|
@@ -142,6 +171,17 @@ module Fog
|
|
142
171
|
"password" => @credentials.password
|
143
172
|
}
|
144
173
|
end
|
174
|
+
|
175
|
+
def headers
|
176
|
+
{
|
177
|
+
"Authorization" => authorization_header,
|
178
|
+
"Content-Type" => "application/json"
|
179
|
+
}.tap do |headers|
|
180
|
+
if @credentials.one_time_password
|
181
|
+
headers[TWO_FACTOR_HEADER] = @credentials.one_time_password
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
145
185
|
end
|
146
186
|
|
147
187
|
# This strategy attempts to use a refresh_token gained during an earlier
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Fog::Brightbox::Compute, "#credentials" do
|
4
|
+
describe "when 2FA support is disabled" do
|
5
|
+
before do
|
6
|
+
@options = {
|
7
|
+
brightbox_client_id: "app-12345",
|
8
|
+
brightbox_secret: "1234567890",
|
9
|
+
brightbox_username: "jason.null@brightbox.com",
|
10
|
+
brightbox_password: "HR4life",
|
11
|
+
brightbox_one_time_password: "123456"
|
12
|
+
}
|
13
|
+
@service = Fog::Brightbox::Compute.new(@options)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "does not add passed OTP to credentials" do
|
17
|
+
assert_kind_of Fog::Brightbox::OAuth2::CredentialSet, @service.credentials
|
18
|
+
assert_nil @service.credentials.one_time_password
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "with 2FA support is enabled" do
|
23
|
+
before do
|
24
|
+
@expected_otp = "123456"
|
25
|
+
@options = {
|
26
|
+
brightbox_client_id: "app-12345",
|
27
|
+
brightbox_secret: "1234567890",
|
28
|
+
brightbox_username: "jason.null@brightbox.com",
|
29
|
+
brightbox_password: "HR4life",
|
30
|
+
brightbox_support_two_factor: true,
|
31
|
+
brightbox_one_time_password: @expected_otp
|
32
|
+
}
|
33
|
+
@service = Fog::Brightbox::Compute.new(@options)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "adds passed OTP to credentials" do
|
37
|
+
assert_kind_of Fog::Brightbox::OAuth2::CredentialSet, @service.credentials
|
38
|
+
assert @expected_otp, @service.credentials.one_time_password
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,305 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Fog::Brightbox::Compute, "#get_access_token" do
|
4
|
+
before do
|
5
|
+
@new_access_token = "0987654321"
|
6
|
+
@new_refresh_token = "5432167890"
|
7
|
+
|
8
|
+
@options = {
|
9
|
+
brightbox_client_id: "app-12345",
|
10
|
+
brightbox_secret: "1234567890",
|
11
|
+
brightbox_username: "jason.null@brightbox.com",
|
12
|
+
brightbox_password: "HR4life",
|
13
|
+
brightbox_support_two_factor: true,
|
14
|
+
brightbox_one_time_password: "123456"
|
15
|
+
}
|
16
|
+
@service = Fog::Brightbox::Compute.new(@options)
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "when user does not have 2FA enabled" do
|
20
|
+
describe "and authenticates correctly" do
|
21
|
+
before do
|
22
|
+
stub_authentication_request(auth_correct: true)
|
23
|
+
|
24
|
+
assert @service.two_factor?
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "without !" do
|
28
|
+
it "updates credentials" do
|
29
|
+
token = @service.get_access_token
|
30
|
+
assert_equal "0987654321", token
|
31
|
+
|
32
|
+
assert_equal token, @service.access_token
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "with !" do
|
37
|
+
it "updates credentials" do
|
38
|
+
token = @service.get_access_token!
|
39
|
+
assert_equal "0987654321", token
|
40
|
+
|
41
|
+
assert_equal token, @service.access_token
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "and authenticates incorrectly" do
|
47
|
+
before do
|
48
|
+
stub_authentication_request(auth_correct: false)
|
49
|
+
|
50
|
+
assert @service.two_factor?
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "without !" do
|
54
|
+
it "returns nil" do
|
55
|
+
assert_nil @service.get_access_token
|
56
|
+
|
57
|
+
assert_nil @service.access_token
|
58
|
+
assert_nil @service.refresh_token
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "with !" do
|
63
|
+
it "raises an error" do
|
64
|
+
begin
|
65
|
+
@service.get_access_token!
|
66
|
+
rescue Excon::Error::Unauthorized
|
67
|
+
assert_nil @service.access_token
|
68
|
+
assert_nil @service.refresh_token
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "when user does have 2FA enabled" do
|
76
|
+
describe "and authenticates correctly" do
|
77
|
+
describe "without OTP" do
|
78
|
+
before do
|
79
|
+
stub_authentication_request(auth_correct: true,
|
80
|
+
two_factor_user: true)
|
81
|
+
|
82
|
+
assert @service.two_factor?
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "without !" do
|
86
|
+
it "returns nil" do
|
87
|
+
assert_nil @service.get_access_token
|
88
|
+
|
89
|
+
assert_nil @service.access_token
|
90
|
+
assert_nil @service.refresh_token
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "with !" do
|
95
|
+
it "raises an error" do
|
96
|
+
begin
|
97
|
+
@service.get_access_token!
|
98
|
+
rescue Fog::Brightbox::OAuth2::TwoFactorMissingError
|
99
|
+
assert_nil @service.access_token
|
100
|
+
assert_nil @service.refresh_token
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "with OTP" do
|
107
|
+
before do
|
108
|
+
stub_authentication_request(auth_correct: true,
|
109
|
+
two_factor_user: true,
|
110
|
+
otp_sent: true)
|
111
|
+
|
112
|
+
assert @service.two_factor?
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "without !" do
|
116
|
+
it "updates credentials" do
|
117
|
+
token = @service.get_access_token
|
118
|
+
assert_equal "0987654321", token
|
119
|
+
|
120
|
+
assert_equal token, @service.access_token
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "with !" do
|
125
|
+
it "updates credentials" do
|
126
|
+
token = @service.get_access_token!
|
127
|
+
assert_equal "0987654321", token
|
128
|
+
|
129
|
+
assert_equal token, @service.access_token
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "and authenticates incorrectly" do
|
136
|
+
before do
|
137
|
+
stub_authentication_request(auth_correct: false,
|
138
|
+
two_factor_user: true)
|
139
|
+
|
140
|
+
assert @service.two_factor?
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "without !" do
|
144
|
+
it "returns nil" do
|
145
|
+
assert_nil @service.get_access_token
|
146
|
+
|
147
|
+
assert_nil @service.access_token
|
148
|
+
assert_nil @service.refresh_token
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "with !" do
|
153
|
+
it "raises an error" do
|
154
|
+
begin
|
155
|
+
@service.get_access_token!
|
156
|
+
rescue Fog::Brightbox::OAuth2::TwoFactorMissingError
|
157
|
+
assert_nil @service.access_token
|
158
|
+
assert_nil @service.refresh_token
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe "without 2FA support enabled" do
|
165
|
+
before do
|
166
|
+
@options = {
|
167
|
+
brightbox_client_id: "app-12345",
|
168
|
+
brightbox_secret: "1234567890",
|
169
|
+
brightbox_username: "jason.null@brightbox.com",
|
170
|
+
brightbox_password: "HR4life",
|
171
|
+
brightbox_support_two_factor: false,
|
172
|
+
brightbox_one_time_password: "123456"
|
173
|
+
}
|
174
|
+
@service = Fog::Brightbox::Compute.new(@options)
|
175
|
+
|
176
|
+
refute @service.two_factor?
|
177
|
+
end
|
178
|
+
|
179
|
+
describe "without !" do
|
180
|
+
it "returns nil" do
|
181
|
+
stub_authentication_request(auth_correct: false,
|
182
|
+
two_factor_user: true)
|
183
|
+
|
184
|
+
assert_nil @service.get_access_token
|
185
|
+
|
186
|
+
assert_nil @service.access_token
|
187
|
+
assert_nil @service.refresh_token
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe "with !" do
|
192
|
+
describe "when authentication incorrect" do
|
193
|
+
it "raises an error" do
|
194
|
+
stub_authentication_request(auth_correct: false,
|
195
|
+
two_factor_user: true,
|
196
|
+
otp_sent: true)
|
197
|
+
|
198
|
+
begin
|
199
|
+
@service.get_access_token!
|
200
|
+
rescue Excon::Error::Unauthorized
|
201
|
+
assert_nil @service.access_token
|
202
|
+
assert_nil @service.refresh_token
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
describe "with missing OTP" do
|
208
|
+
before do
|
209
|
+
@options = {
|
210
|
+
brightbox_client_id: "app-12345",
|
211
|
+
brightbox_secret: "1234567890",
|
212
|
+
brightbox_username: "jason.null@brightbox.com",
|
213
|
+
brightbox_password: "HR4life",
|
214
|
+
brightbox_support_two_factor: false
|
215
|
+
}
|
216
|
+
@service = Fog::Brightbox::Compute.new(@options)
|
217
|
+
|
218
|
+
refute @service.two_factor?
|
219
|
+
end
|
220
|
+
|
221
|
+
it "raises an error" do
|
222
|
+
stub_authentication_request(auth_correct: true,
|
223
|
+
two_factor_user: true,
|
224
|
+
otp_sent: false)
|
225
|
+
|
226
|
+
begin
|
227
|
+
@service.get_access_token!
|
228
|
+
rescue Excon::Error::Unauthorized
|
229
|
+
assert_nil @service.access_token
|
230
|
+
assert_nil @service.refresh_token
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# @param auth_correct [Boolean] Treat username/password as correct
|
239
|
+
# @param two_factor_user [Boolean] Is user protected by 2FA on server
|
240
|
+
# @param otp_sent [Boolean] Is an OTP sent in the request?
|
241
|
+
def stub_authentication_request(auth_correct:,
|
242
|
+
two_factor_user: false,
|
243
|
+
otp_sent: nil)
|
244
|
+
|
245
|
+
two_factor_supported = @service.two_factor?
|
246
|
+
|
247
|
+
request = {
|
248
|
+
headers: {
|
249
|
+
"Authorization" => "Basic YXBwLTEyMzQ1OjEyMzQ1Njc4OTA=",
|
250
|
+
"Content-Type" => "application/json",
|
251
|
+
},
|
252
|
+
body: {
|
253
|
+
grant_type: "password",
|
254
|
+
username: "jason.null@brightbox.com",
|
255
|
+
password: "HR4life"
|
256
|
+
}.to_json
|
257
|
+
}.tap do |req|
|
258
|
+
# Only expect the header if the service should send it
|
259
|
+
# Without this, we stub a request that demands an OTP but it is not sent
|
260
|
+
if two_factor_supported && otp_sent
|
261
|
+
req[:headers]["X-Brightbox-OTP"] = "123456"
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# The cases we are testing are:
|
266
|
+
#
|
267
|
+
# * User does not use 2FA and authenticate
|
268
|
+
# * User does not use 2FA and FAILS to authenticate
|
269
|
+
# * User does use 2FA and authenticate
|
270
|
+
# * User does use 2FA and FAILS to authenticate
|
271
|
+
# * User does use 2FA and FAILS to send OTP
|
272
|
+
#
|
273
|
+
response = if two_factor_user && !otp_sent
|
274
|
+
# OTP required header
|
275
|
+
{
|
276
|
+
status: 401,
|
277
|
+
headers: {
|
278
|
+
"X-Brightbox-OTP" => "required"
|
279
|
+
},
|
280
|
+
body: { error: "invalid_client" }.to_json
|
281
|
+
}
|
282
|
+
elsif !auth_correct
|
283
|
+
# No OTP header
|
284
|
+
{
|
285
|
+
status: 401,
|
286
|
+
headers: {},
|
287
|
+
body: { error: "invalid_client" }.to_json
|
288
|
+
}
|
289
|
+
else
|
290
|
+
{
|
291
|
+
status: 200,
|
292
|
+
headers: {},
|
293
|
+
body: {
|
294
|
+
access_token: @new_access_token,
|
295
|
+
refresh_token: @new_refresh_token,
|
296
|
+
expires_in: 7200
|
297
|
+
}.to_json
|
298
|
+
}
|
299
|
+
end
|
300
|
+
|
301
|
+
stub_request(:post, "https://api.gb1.brightbox.com/token")
|
302
|
+
.with(request)
|
303
|
+
.to_return(response)
|
304
|
+
end
|
305
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Fog::Brightbox::Compute, "#two_factor?" do
|
4
|
+
describe "when omitted" do
|
5
|
+
before do
|
6
|
+
@options = {
|
7
|
+
brightbox_client_id: "app-12345",
|
8
|
+
brightbox_secret: "1234567890",
|
9
|
+
brightbox_username: "jason.null@brightbox.com",
|
10
|
+
brightbox_password: "HR4life"
|
11
|
+
}
|
12
|
+
@service = Fog::Brightbox::Compute.new(@options)
|
13
|
+
end
|
14
|
+
|
15
|
+
it do
|
16
|
+
refute @service.two_factor?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "when disabled" do
|
21
|
+
before do
|
22
|
+
@options = {
|
23
|
+
brightbox_client_id: "app-12345",
|
24
|
+
brightbox_secret: "1234567890",
|
25
|
+
brightbox_username: "jason.null@brightbox.com",
|
26
|
+
brightbox_password: "HR4life",
|
27
|
+
brightbox_support_two_factor: false
|
28
|
+
}
|
29
|
+
@service = Fog::Brightbox::Compute.new(@options)
|
30
|
+
end
|
31
|
+
|
32
|
+
it do
|
33
|
+
refute @service.two_factor?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "when enabled" do
|
38
|
+
before do
|
39
|
+
@options = {
|
40
|
+
brightbox_client_id: "app-12345",
|
41
|
+
brightbox_secret: "1234567890",
|
42
|
+
brightbox_username: "jason.null@brightbox.com",
|
43
|
+
brightbox_password: "HR4life",
|
44
|
+
brightbox_support_two_factor: true
|
45
|
+
}
|
46
|
+
@service = Fog::Brightbox::Compute.new(@options)
|
47
|
+
end
|
48
|
+
|
49
|
+
it do
|
50
|
+
assert @service.two_factor?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -37,4 +37,24 @@ describe Fog::Brightbox::OAuth2::UserCredentialsStrategy do
|
|
37
37
|
assert_equal "Basic YXBwLTEyMzQ1Ol9fbWFzaGVkX2tleXNfMTIzX18=", headers["Authorization"]
|
38
38
|
assert_equal "application/json", headers["Content-Type"]
|
39
39
|
end
|
40
|
+
|
41
|
+
describe "when 2FA OTP is included" do
|
42
|
+
before do
|
43
|
+
options = {
|
44
|
+
username: @username,
|
45
|
+
password: @password,
|
46
|
+
one_time_password: "123456"
|
47
|
+
}
|
48
|
+
|
49
|
+
@credentials = Fog::Brightbox::OAuth2::CredentialSet.new(@client_id, @client_secret, options)
|
50
|
+
@strategy = Fog::Brightbox::OAuth2::UserCredentialsStrategy.new(@credentials)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "tests #headers" do
|
54
|
+
headers = @strategy.headers
|
55
|
+
assert_equal "Basic YXBwLTEyMzQ1Ol9fbWFzaGVkX2tleXNfMTIzX18=", headers["Authorization"]
|
56
|
+
assert_equal "application/json", headers["Content-Type"]
|
57
|
+
assert_equal "123456", headers["X-Brightbox-OTP"]
|
58
|
+
end
|
59
|
+
end
|
40
60
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fog-brightbox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Thornthwaite
|
@@ -381,6 +381,9 @@ files:
|
|
381
381
|
- lib/fog/brightbox/storage/not_found.rb
|
382
382
|
- lib/fog/brightbox/version.rb
|
383
383
|
- spec/fog/brightbox/compute/config_spec.rb
|
384
|
+
- spec/fog/brightbox/compute/credentials_spec.rb
|
385
|
+
- spec/fog/brightbox/compute/get_access_token_spec.rb
|
386
|
+
- spec/fog/brightbox/compute/two_factor_spec.rb
|
384
387
|
- spec/fog/brightbox/compute/wrapped_request_spec.rb
|
385
388
|
- spec/fog/brightbox/config_spec.rb
|
386
389
|
- spec/fog/brightbox/link_helper_spec.rb
|
@@ -488,6 +491,9 @@ summary: This library can be used as a module for `fog` or as standalone provide
|
|
488
491
|
to use the Brightbox Cloud in applications
|
489
492
|
test_files:
|
490
493
|
- spec/fog/brightbox/compute/config_spec.rb
|
494
|
+
- spec/fog/brightbox/compute/credentials_spec.rb
|
495
|
+
- spec/fog/brightbox/compute/get_access_token_spec.rb
|
496
|
+
- spec/fog/brightbox/compute/two_factor_spec.rb
|
491
497
|
- spec/fog/brightbox/compute/wrapped_request_spec.rb
|
492
498
|
- spec/fog/brightbox/config_spec.rb
|
493
499
|
- spec/fog/brightbox/link_helper_spec.rb
|