Skip to content

Commit

Permalink
Merge pull request softlayer#106 from renier/token_auth
Browse files Browse the repository at this point in the history
Add token/password authentication to ruby client
  • Loading branch information
SLsthompson committed Nov 19, 2015
2 parents 449da16 + fc8fce0 commit c2301ce
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 46 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.textile
@@ -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
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
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
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

0 comments on commit c2301ce

Please sign in to comment.