Class: Transip

Inherits:
Object
  • Object
show all
Defined in:
lib/transip.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'

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)


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

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.



30
31
32
# File 'lib/transip.rb', line 30

def hash
  @hash
end

#ipObject

Returns the value of attribute ip.



30
31
32
# File 'lib/transip.rb', line 30

def ip
  @ip
end

#modeObject

Returns the value of attribute mode.



30
31
32
# File 'lib/transip.rb', line 30

def mode
  @mode
end

#passwordObject

Returns the value of attribute password.



30
31
32
# File 'lib/transip.rb', line 30

def password
  @password
end

#responseObject (readonly)

Returns the value of attribute response.



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

def response
  @response
end

#usernameObject

Returns the value of attribute username.



30
31
32
# File 'lib/transip.rb', line 30

def username
  @username
end

Instance Method Details

#actionsObject

Returns Array with all possible SOAP WSDL actions.



339
340
341
# File 'lib/transip.rb', line 339

def actions
  client.wsdl.soap_actions
end

#clientObject

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



334
335
336
# File 'lib/transip.rb', line 334

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.



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

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



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

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



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

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



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

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



366
367
368
369
370
# File 'lib/transip.rb', line 366

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)



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

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



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

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!



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

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



296
297
298
299
300
# File 'lib/transip.rb', line 296

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.



207
208
209
210
# File 'lib/transip.rb', line 207

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

#urlencode(input) ⇒ Object



230
231
232
233
234
235
# File 'lib/transip.rb', line 230

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!



214
215
216
217
218
219
# File 'lib/transip.rb', line 214

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