Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.textile
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
*3.2*
* Add password-based authentication with `SoftLayer::Client.with_password(username: '...', password: '...', ...)`.

*3.0*
* 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.
* 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
Expand Down
68 changes: 59 additions & 9 deletions lib/softlayer/Client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,32 @@ def self.default_client=(new_default)
@@default_client = new_default
end

##
# This will be using your username and password to get a portal
# token with which to authenticate client calls.
# This is a wrapper around Client.new. You can pass it the same
# parameters as with Client.new, with the exception that this will
# be expecting a password in the options hash.
def self.with_password(options = {})
if options[:username].nil? || options[:username].empty?
raise 'A username is required to create this client'
end

if options[:password].nil? || options[:password].empty?
raise 'A password is required to create this client'
end

service = SoftLayer::Service.new('SoftLayer_User_Customer')
token = service.getPortalLoginToken(
options[:username], options[:password]
)

options[:userId] = token['userId']
options[:authToken] = token['hash']

SoftLayer::Client.new(options)
end

##
#
# Clients are built with a number of settings:
Expand All @@ -76,10 +102,14 @@ def initialize(options = {})
settings = Config.client_settings(options)

# pick up the username from the options, the global, or assume no username
@username = settings[:username] || ""
@username = settings[:username]

# do a similar thing for the api key
@api_key = settings[:api_key] || ""
@api_key = settings[:api_key]

# grab token pair
@userId = settings[:userId]
@authToken = settings[:authToken]

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

raise "A SoftLayer Client requires a username" if !@username || @username.empty?
raise "A SoftLayer Client requires an api_key" if !@api_key || @api_key.empty?
raise "A SoftLayer Client requires an endpoint URL" if !@endpoint_url || @endpoint_url.empty?
end

# return whether this client is using token-based authentication
def token_based?
@userId && @authToken && !@authToken.empty?
end

# return whether this client is using api_key-based authentication
def key_based?
@username && !@username.empty? && @api_key && !@api_key.empty?
end

# return a hash of the authentication headers for the client
def authentication_headers
{
"authenticate" => {
"username" => @username,
"apiKey" => @api_key
if token_based?
{
'authenticate' => {
'complexType' => 'PortalLoginToken',
'userId' => @userId,
'authToken' => @authToken
}
}
}
elsif key_based?
{
'authenticate' => {
'username' => @username,
'apiKey' => @api_key
}
}
else
{}
end
end

# Returns a service with the given name.
Expand Down
2 changes: 1 addition & 1 deletion lib/softlayer/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
module SoftLayer
# The version number (including major, minor, and bugfix numbers)
# This should change in accordance with the concept of Semantic Versioning
VERSION = "3.1.1" # version history in the CHANGELOG.textile file at the root of the source
VERSION = "3.2.0" # version history in the CHANGELOG.textile file at the root of the source

# The base URL of the SoftLayer API available to the public internet.
API_PUBLIC_ENDPOINT = 'https://api.softlayer.com/xmlrpc/v3/'
Expand Down
73 changes: 37 additions & 36 deletions spec/Client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,42 +41,43 @@
expect(client.api_key).to eq 'fake_key'
end

it 'raises an error if passed an empty user name' do
expect do
$SL_API_USERNAME = ''
client = SoftLayer::Client.new(:api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/')
end.to raise_error

expect do
$SL_API_USERNAME = 'good_username'
$SL_API_KEY = 'sample'
client = SoftLayer::Client.new(:username => '', :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/')
end.to raise_error
end

it 'fails if the user name is nil' do
expect do
$SL_API_USERNAME = nil
client = SoftLayer::Client.new(:username => nil, :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/')
end.to raise_error
end

it 'fails if the api_key is empty' do
expect do
$SL_API_KEY = ''
client = SoftLayer::Client.new(:username => 'fake_user', :endpoint_url => 'http://fakeurl.org/')
end.to raise_error

expect do
client = SoftLayer::Client.new(:username => 'fake_user', :api_key => '', :endpoint_url => 'http://fakeurl.org/')
end.to raise_error
end

it 'fails if the api_key is nil' do
expect do
$SL_API_KEY = nil
client = SoftLayer::Client.new(:username => 'fake_user', :endpoint_url => 'http://fakeurl.org/', :api_key => nil)
end.to raise_error
it 'produces empty auth headers if the username is empty' do

$SL_API_USERNAME = ''
client = SoftLayer::Client.new(:api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/')

expect(client.authentication_headers.empty?).to be true

$SL_API_USERNAME = 'good_username'
$SL_API_KEY = 'sample'
client = SoftLayer::Client.new(:username => '', :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/')

expect(client.authentication_headers.empty?).to be true
end

it 'produces empty auth headers if the username is nil' do
$SL_API_USERNAME = nil
client = SoftLayer::Client.new(:username => nil, :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/')

expect(client.authentication_headers.empty?).to be true
end

it 'produces empty auth headers if the api_key is empty' do
$SL_API_KEY = ''
client = SoftLayer::Client.new(:username => 'fake_user', :endpoint_url => 'http://fakeurl.org/')

expect(client.authentication_headers.empty?).to be true

client = SoftLayer::Client.new(:username => 'fake_user', :api_key => '', :endpoint_url => 'http://fakeurl.org/')

expect(client.authentication_headers.empty?).to be true
end

it 'produces empty auth headers if the api_key is nil' do
$SL_API_KEY = nil
client = SoftLayer::Client.new(:username => 'fake_user', :endpoint_url => 'http://fakeurl.org/', :api_key => nil)

expect(client.authentication_headers.empty?).to be true
end

it 'initializes by default with nil as the timeout' do
Expand Down