Class: Transip

Inherits:
Object
  • Object
show all
Defined in:
lib/transip.rb,
lib/transip/version.rb

Overview

transip.request(:register, Transip::Domain.new(‘example.com’, nil, nil, [Transip::DnsEntry.new(‘test’, 5.minutes, ‘A’, ‘74.125.77.147’)]))

Defined Under Namespace

Classes: ApiError, DnsEntry, Domain, DomainBranding, Nameserver, Tld, TransipStruct, WhoisContact

Constant Summary collapse

SERVICE =
'DomainService'
WSDL =
'https://api.transip.nl/wsdl/?service=DomainService'
API_VERSION =
'4.2'
VERSION =
"0.3.7"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Transip

Options:

  • username

  • ip

  • key

  • mode

Example:

transip = Transip.new(:username => 'api_username', :ip => '12.34.12.3', :key => mykey, :mode => 'readwrite') # use this in production

Raises:

  • (ArgumentError)


197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/transip.rb', line 197

def initialize(options = {})
  @key = options[:key]
  @username = options[:username]
  @ip = options[:ip]
  raise ArgumentError, "The :username, :ip and :key options are required!" if @username.nil? or @key.nil?

  @mode = options[:mode] || :readonly
  @endpoint = options[:endpoint] || 'api.transip.nl'
  if options[:password]
    @password = options[:password]
  end
  @savon_options = {
    :wsdl => WSDL
  }
  # By default we don't want to debug!
  self.turn_off_debugging!
end

Instance Attribute Details

#hashObject

Returns the value of attribute hash.



33
34
35
# File 'lib/transip.rb', line 33

def hash
  @hash
end

#ipObject

Returns the value of attribute ip.



33
34
35
# File 'lib/transip.rb', line 33

def ip
  @ip
end

#modeObject

Returns the value of attribute mode.



33
34
35
# File 'lib/transip.rb', line 33

def mode
  @mode
end

#passwordObject

Returns the value of attribute password.



33
34
35
# File 'lib/transip.rb', line 33

def password
  @password
end

#responseObject (readonly)

Returns the value of attribute response.



34
35
36
# File 'lib/transip.rb', line 34

def response
  @response
end

#usernameObject

Returns the value of attribute username.



33
34
35
# File 'lib/transip.rb', line 33

def username
  @username
end

Instance Method Details

#actionsObject

Returns Array with all possible SOAP WSDL actions.



355
356
357
# File 'lib/transip.rb', line 355

def actions
  client.operations
end

#clientObject

Returns a Savon::Client object to be used in the connection. This object is re-used and cached as @client.



350
351
352
# File 'lib/transip.rb', line 350

def client
  @client ||= client!
end

#client!Object

Same as client method but initializes a brand new fresh client. You have to use this one when you want to re-set the mode (readwrite, readonly), or authentication details of your client.



339
340
341
342
343
344
345
346
# File 'lib/transip.rb', line 339

def client!
  @client = Savon::Client.new(@savon_options) do
    namespaces(
      "xmlns:enc" => "http://schemas.xmlsoap.org/soap/encoding/"
    )
  end
  return @client
end

#convert_array_to_hash(array) ⇒ Object

yes, i know, it smells bad



232
233
234
235
236
237
238
# File 'lib/transip.rb', line 232

def convert_array_to_hash(array)
  result = {}
  array.each_with_index do |value, index|
    result[index] = value
  end
  result
end

#cookies(method, parameters) ⇒ Object

Used for authentication



320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
# File 'lib/transip.rb', line 320

def cookies(method, parameters)
  time = Time.new.to_i
  #strip out the -'s because transip requires the nonce to be between 6 and 32 chars
  nonce = SecureRandom.uuid.gsub("-", '')
  result = to_cookies [ "login=#{self.username}",
               "mode=#{self.mode}",
               "timestamp=#{time}",
               "nonce=#{nonce}",
               "clientVersion=#{API_VERSION}",
               "signature=#{signature(method, parameters, time, nonce)}"

             ]
  #puts signature(method, parameters, time, nonce)
  result
end

#fix_array_definitions(options) ⇒ Object

This makes sure that arrays are properly encoded as soap-arrays by Gyoku



360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/transip.rb', line 360

def fix_array_definitions(options)
  result = {}
  options.each do |key, value|
    if value.is_a? Array and value.size > 0
      entry_name = value.first.class.name.split(":").last
      result[key] = {
        'item' => {:content! => value, :'@xsi:type' => "tns:#{entry_name}"},
        :'@xsi:type' => "tns:ArrayOf#{entry_name}",
        :'@enc:arrayType' => "tns:#{entry_name}[#{value.size}]"
      }
    else
      result[key] = value
    end
  end
  result
end

#process_response(response) ⇒ Object

converts the savon response object to something we can return to the caller

  • A TransipStruct object

  • An array of TransipStructs

  • nil



382
383
384
385
386
# File 'lib/transip.rb', line 382

def process_response(response)
  response = response.to_hash.values.first[:return] rescue nil
  TransipStruct.from_soap(response)

end

#request(action, options = nil) ⇒ Object

This is the main request function throws ApiError returns response object (can be TransipStruct or Array of TransipStruct)



391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# File 'lib/transip.rb', line 391

def request(action, options = nil)
  formatted_action = action.to_s.lower_camelcase
  parameters = {
    # for some reason, the transip server wants the body root tag to be
    # the name of the action.
    :message_tag => formatted_action
  }
  options = options.to_hash  if options.is_a? Transip::TransipStruct

  if options.is_a? Hash
    xml_options = fix_array_definitions(options)
  elsif options.nil?
    xml_options = nil
  else
    raise "Invalid parameter format (should be nil, hash or TransipStruct"
  end
  parameters[:message] = xml_options
  parameters[:cookies] = cookies(action, options)
  #puts parameters.inspect
  response = client.call(action, parameters)

  process_response(response)
rescue Savon::SOAPFault => e
  raise ApiError.new(e), e.message.sub(/^\(\d+\)\s+/,'') # We raise our own error (FIXME: Correct?).
end

#serialize_parameters(parameters, key_prefix = nil) ⇒ Object



247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/transip.rb', line 247

def serialize_parameters(parameters, key_prefix=nil)
  parameters = parameters.to_hash.values.first if parameters.is_a? TransipStruct
  parameters = convert_array_to_hash(parameters) if parameters.is_a? Array
  if not parameters.is_a? Hash
    return urlencode(parameters)
  end

  encoded_parameters = []
  parameters.each do |key, value|
    next if key.to_s == '@xsi:type'
    encoded_key = (key_prefix.nil?) ? urlencode(key) : "#{key_prefix}[#{urlencode(key)}]"
    if value.is_a? Hash or value.is_a? Array or value.is_a? TransipStruct
      encoded_parameters << serialize_parameters(value, encoded_key)
    else
      encoded_value = urlencode(value)
      encoded_parameters << "#{encoded_key}=#{encoded_value}"
    end
  end

  encoded_parameters = encoded_parameters.join("&")
  #puts encoded_parameters.split('&').join("\n")
  encoded_parameters
end

#signature(method, parameters, time, nonce) ⇒ Object

does all the techy stuff to calculate transip’s sick authentication scheme: a hash with all the request information is subsequently: serialized like a www form SHA512 digested asn1 header added private key encrypted Base64 encoded URL encoded I think the guys at transip were trying to use their entire crypto-toolbox!



281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/transip.rb', line 281

def signature(method, parameters, time, nonce)
  formatted_method = method.to_s.lower_camelcase
  parameters ||= {}
  input = convert_array_to_hash(parameters.values)
  options = {
    '__method' => formatted_method,
    '__service' => SERVICE,
    '__hostname' => @endpoint,
    '__timestamp' => time,
    '__nonce' => nonce

  }
  input.merge!(options)
  raise "Invalid RSA key" unless @key =~ /-----BEGIN (RSA )?PRIVATE KEY-----(.*)-----END (RSA )?PRIVATE KEY-----/sim
  serialized_input = serialize_parameters(input)

  digest = Digest::SHA512.new.digest(serialized_input)
  asn_header = "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40"

  # convert asn_header literal to ASCII-8BIT
  if RUBY_VERSION.split('.')[0] == "2"
  	asn = asn_header.b + digest
  else
  	asn = asn_header + digest
  end
  private_key = OpenSSL::PKey::RSA.new(@key)
  encrypted_asn = private_key.private_encrypt(asn)
  readable_encrypted_asn = Base64.encode64(encrypted_asn)
  urlencode(readable_encrypted_asn)
end

#to_cookies(content) ⇒ Object



312
313
314
315
316
# File 'lib/transip.rb', line 312

def to_cookies(content)
  content.map do |item|
    HTTPI::Cookie.new item
  end
end

#turn_off_debugging!Object

By default we don’t want to debug! Changing might impact other Savon usages.



217
218
219
220
# File 'lib/transip.rb', line 217

def turn_off_debugging!
    @savon_options[:log] = false            # disable logging
    @savon_options[:log_level] = :info      # changing the log level
end

#urlencode(input) ⇒ Object



240
241
242
243
244
245
# File 'lib/transip.rb', line 240

def urlencode(input)
  output = URI.encode_www_form_component(input)
  output.gsub!('+', '%20')
  output.gsub!('%7E', '~')
  output
end

#use_with_rails!Object

Make Savon log to Rails.logger and turn_off_debugging!



224
225
226
227
228
229
# File 'lib/transip.rb', line 224

def use_with_rails!
  if Rails.env.production?
    self.turn_off_debugging!
  end
  @savon_options[:logger] = Rails.logger  # using the Rails logger
end