Skip to content

Commit c2301ce

Browse files
committed
Merge pull request #106 from renier/token_auth
Add token/password authentication to ruby client
2 parents 449da16 + fc8fce0 commit c2301ce

File tree

4 files changed

+100
-46
lines changed

4 files changed

+100
-46
lines changed

CHANGELOG.textile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
*3.2*
2+
* Add password-based authentication with `SoftLayer::Client.with_password(username: '...', password: '...', ...)`.
3+
14
*3.0*
25
* Substantially rewrote the ObjectFilter class. ObjectFilters used to be hashes which made it easy to manipulate their content incorrectly. The new implementation has a strict interface that makes it harder to manipulate filters incorrectly.
36
* Added a model for Virtual Server Image Templates (SoftLayer::ImageTemplate) - VirtualServerOrder now requires an instance of this class rather than allowing you to provide the global_id of an image

lib/softlayer/Client.rb

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,32 @@ def self.default_client=(new_default)
5858
@@default_client = new_default
5959
end
6060

61+
##
62+
# This will be using your username and password to get a portal
63+
# token with which to authenticate client calls.
64+
# This is a wrapper around Client.new. You can pass it the same
65+
# parameters as with Client.new, with the exception that this will
66+
# be expecting a password in the options hash.
67+
def self.with_password(options = {})
68+
if options[:username].nil? || options[:username].empty?
69+
raise 'A username is required to create this client'
70+
end
71+
72+
if options[:password].nil? || options[:password].empty?
73+
raise 'A password is required to create this client'
74+
end
75+
76+
service = SoftLayer::Service.new('SoftLayer_User_Customer')
77+
token = service.getPortalLoginToken(
78+
options[:username], options[:password]
79+
)
80+
81+
options[:userId] = token['userId']
82+
options[:authToken] = token['hash']
83+
84+
SoftLayer::Client.new(options)
85+
end
86+
6187
##
6288
#
6389
# Clients are built with a number of settings:
@@ -76,10 +102,14 @@ def initialize(options = {})
76102
settings = Config.client_settings(options)
77103

78104
# pick up the username from the options, the global, or assume no username
79-
@username = settings[:username] || ""
105+
@username = settings[:username]
80106

81107
# do a similar thing for the api key
82-
@api_key = settings[:api_key] || ""
108+
@api_key = settings[:api_key]
109+
110+
# grab token pair
111+
@userId = settings[:userId]
112+
@authToken = settings[:authToken]
83113

84114
# and the endpoint url
85115
@endpoint_url = settings[:endpoint_url] || API_PUBLIC_ENDPOINT
@@ -90,19 +120,39 @@ def initialize(options = {})
90120
# and assign a time out if the settings offer one
91121
@network_timeout = settings[:timeout] if settings.has_key?(:timeout)
92122

93-
raise "A SoftLayer Client requires a username" if !@username || @username.empty?
94-
raise "A SoftLayer Client requires an api_key" if !@api_key || @api_key.empty?
95123
raise "A SoftLayer Client requires an endpoint URL" if !@endpoint_url || @endpoint_url.empty?
96124
end
97125

126+
# return whether this client is using token-based authentication
127+
def token_based?
128+
@userId && @authToken && !@authToken.empty?
129+
end
130+
131+
# return whether this client is using api_key-based authentication
132+
def key_based?
133+
@username && !@username.empty? && @api_key && !@api_key.empty?
134+
end
135+
98136
# return a hash of the authentication headers for the client
99137
def authentication_headers
100-
{
101-
"authenticate" => {
102-
"username" => @username,
103-
"apiKey" => @api_key
138+
if token_based?
139+
{
140+
'authenticate' => {
141+
'complexType' => 'PortalLoginToken',
142+
'userId' => @userId,
143+
'authToken' => @authToken
144+
}
104145
}
105-
}
146+
elsif key_based?
147+
{
148+
'authenticate' => {
149+
'username' => @username,
150+
'apiKey' => @api_key
151+
}
152+
}
153+
else
154+
{}
155+
end
106156
end
107157

108158
# Returns a service with the given name.

lib/softlayer/base.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
module SoftLayer
1313
# The version number (including major, minor, and bugfix numbers)
1414
# This should change in accordance with the concept of Semantic Versioning
15-
VERSION = "3.1.1" # version history in the CHANGELOG.textile file at the root of the source
15+
VERSION = "3.2.0" # version history in the CHANGELOG.textile file at the root of the source
1616

1717
# The base URL of the SoftLayer API available to the public internet.
1818
API_PUBLIC_ENDPOINT = 'https://api.softlayer.com/xmlrpc/v3/'

spec/Client_spec.rb

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -41,42 +41,43 @@
4141
expect(client.api_key).to eq 'fake_key'
4242
end
4343

44-
it 'raises an error if passed an empty user name' do
45-
expect do
46-
$SL_API_USERNAME = ''
47-
client = SoftLayer::Client.new(:api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/')
48-
end.to raise_error
49-
50-
expect do
51-
$SL_API_USERNAME = 'good_username'
52-
$SL_API_KEY = 'sample'
53-
client = SoftLayer::Client.new(:username => '', :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/')
54-
end.to raise_error
55-
end
56-
57-
it 'fails if the user name is nil' do
58-
expect do
59-
$SL_API_USERNAME = nil
60-
client = SoftLayer::Client.new(:username => nil, :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/')
61-
end.to raise_error
62-
end
63-
64-
it 'fails if the api_key is empty' do
65-
expect do
66-
$SL_API_KEY = ''
67-
client = SoftLayer::Client.new(:username => 'fake_user', :endpoint_url => 'http://fakeurl.org/')
68-
end.to raise_error
69-
70-
expect do
71-
client = SoftLayer::Client.new(:username => 'fake_user', :api_key => '', :endpoint_url => 'http://fakeurl.org/')
72-
end.to raise_error
73-
end
74-
75-
it 'fails if the api_key is nil' do
76-
expect do
77-
$SL_API_KEY = nil
78-
client = SoftLayer::Client.new(:username => 'fake_user', :endpoint_url => 'http://fakeurl.org/', :api_key => nil)
79-
end.to raise_error
44+
it 'produces empty auth headers if the username is empty' do
45+
46+
$SL_API_USERNAME = ''
47+
client = SoftLayer::Client.new(:api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/')
48+
49+
expect(client.authentication_headers.empty?).to be true
50+
51+
$SL_API_USERNAME = 'good_username'
52+
$SL_API_KEY = 'sample'
53+
client = SoftLayer::Client.new(:username => '', :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/')
54+
55+
expect(client.authentication_headers.empty?).to be true
56+
end
57+
58+
it 'produces empty auth headers if the username is nil' do
59+
$SL_API_USERNAME = nil
60+
client = SoftLayer::Client.new(:username => nil, :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/')
61+
62+
expect(client.authentication_headers.empty?).to be true
63+
end
64+
65+
it 'produces empty auth headers if the api_key is empty' do
66+
$SL_API_KEY = ''
67+
client = SoftLayer::Client.new(:username => 'fake_user', :endpoint_url => 'http://fakeurl.org/')
68+
69+
expect(client.authentication_headers.empty?).to be true
70+
71+
client = SoftLayer::Client.new(:username => 'fake_user', :api_key => '', :endpoint_url => 'http://fakeurl.org/')
72+
73+
expect(client.authentication_headers.empty?).to be true
74+
end
75+
76+
it 'produces empty auth headers if the api_key is nil' do
77+
$SL_API_KEY = nil
78+
client = SoftLayer::Client.new(:username => 'fake_user', :endpoint_url => 'http://fakeurl.org/', :api_key => nil)
79+
80+
expect(client.authentication_headers.empty?).to be true
8081
end
8182

8283
it 'initializes by default with nil as the timeout' do

0 commit comments

Comments
 (0)