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.4"

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)


189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/transip.rb', line 189

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.



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

def hash
  @hash
end

#ipObject

Returns the value of attribute ip.



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

def ip
  @ip
end

#modeObject

Returns the value of attribute mode.



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

def mode
  @mode
end

#passwordObject

Returns the value of attribute password.



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

def password
  @password
end

#responseObject (readonly)

Returns the value of attribute response.



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

def response
  @response
end

#usernameObject

Returns the value of attribute username.



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

def username
  @username
end

Instance Method Details

#actionsObject

Returns Array with all possible SOAP WSDL actions.



341
342
343
# File 'lib/transip.rb', line 341

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.



336
337
338
# File 'lib/transip.rb', line 336

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.



325
326
327
328
329
330
331
332
# File 'lib/transip.rb', line 325

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



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

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



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/transip.rb', line 306

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



346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/transip.rb', line 346

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



368
369
370
371
372
# File 'lib/transip.rb', line 368

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)



377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# File 'lib/transip.rb', line 377

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



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/transip.rb', line 239

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!



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/transip.rb', line 273

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"
  asn = asn_header + digest
  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



298
299
300
301
302
# File 'lib/transip.rb', line 298

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.



209
210
211
212
# File 'lib/transip.rb', line 209

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

#urlencode(input) ⇒ Object



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

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!



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

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