lib/transip.rb in transip-0.3.7 vs lib/transip.rb in transip-0.4.1
- old
+ new
@@ -7,36 +7,36 @@
require 'digest/sha2'
require 'base64'
require 'ipaddr'
require File.expand_path '../transip/version', __FILE__
+require File.expand_path '../transip/client', __FILE__
+require File.expand_path '../transip/api_error', __FILE__
+
#
-# Implements the www.transip.nl API (v4.2). For more info see: https://www.transip.nl/g/api/
+# Implements the www.transip.nl API (v5.0). For more info see: https://www.transip.nl/g/api/
#
# The transip API makes use of public/private key encryption. You need to use the TransIP
# control panel to give your server access to the api, and to generate a key. You can then
# use the key together with your username to gain access to the api
# Usage:
-# transip = Transip.new(:username => 'api_username', :key => private_key, :ip => '12.34.12.3', :mode => 'readwrite') # use this in production
+# transip = Transip::DomainClient.new(:username => 'api_username', :key => private_key, :ip => '12.34.12.3', :mode => 'readwrite') # use this in production
# transip.actions # => [:check_availability, :get_whois, :get_domain_names, :get_info, :get_auth_code, :get_is_locked, :register, :cancel, :transfer_with_owner_change, :transfer_without_owner_change, :set_nameservers, :set_lock, :unset_lock, :set_dns_entries, :set_owner, :set_contacts]
# transip.request(:get_domain_names)
# transip.request(:get_info, :domain_name => 'example.com')
# transip.request(:get_whois, :domain_name => 'example.com')
# transip.request(:set_dns_entries, :domain_name => 'example.com', :dns_entries => [Transip::DnsEntry.new('test', 5.minutes, 'A', '74.125.77.147')])
# transip.request(:set_contacts, :domain_name => 'example.com', :contacts => [Transip::WhoisContact.new('type', 'first', 'middle', 'last', 'company', 'kvk', 'companyType', 'street','number','postalCode','city','phoneNumber','faxNumber','email','country')])
# transip.request(:register, Transip::Domain.new('example.com', nil, nil, [Transip::DnsEntry.new('test', 5.minutes, 'A', '74.125.77.147')]))
#
-class Transip
- SERVICE = 'DomainService'
- WSDL = 'https://api.transip.nl/wsdl/?service=DomainService'
- API_VERSION = '4.2'
+module Transip
- attr_accessor :username, :password, :ip, :mode, :hash
- attr_reader :response
-
- # Following Error needs to be catched in your code!
- class ApiError < RuntimeError
+ # Backwards compatibility with v3.x of the gem.
+ # TODO: Remove
+ def self.new(*args)
+ puts "Transip.new is deprecated. Use Transip::DomainClient.new instead!"
+ Client.new(*args)
end
# Following subclasses are actually not needed (as you can also
# do the same by just creating hashes..).
@@ -68,11 +68,11 @@
parts.join
end
# See what happens here: http://snippets.dzone.com/posts/show/302
def members_to_hash
- Hash[*members.collect {|m| [member_name_to_camel(m), self.send(m)]}.flatten]
+ Hash[*members.collect {|m| [member_name_to_camel(m), self.send(m)]}.flatten(1)]
end
def to_hash
{ self.class_name_to_sym => self.members_to_hash }
end
@@ -184,233 +184,15 @@
# registration_period_length - number
# cancel_time_frame - number
class Tld < TransipStruct.new(:name, :price, :renewal_price, :capabilities, :registration_period_length, :cancel_time_frame)
end
- # 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
- 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?
+# VPS related methods
+# Available from TransIp v5.0.
- @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!
+ class Vps < TransipStruct.new(:name, :description, :operating_system, :disk_size, :memory_size, :cpus, :status, :ip_address, :vnc_hostname, :vnc_port_number, :vnc_password, :is_blocked, :is_customer_locked)
end
- # By default we don't want to debug!
- # Changing might impact other Savon usages.
- def turn_off_debugging!
- @savon_options[:log] = false # disable logging
- @savon_options[:log_level] = :info # changing the log level
+ class VpsService < TransipStruct.new(:name, :description, :operating_system, :disk_size, :memory_size, :cpus, :status, :ip_address, :vnc_hostname, :vnc_port_number, :vnc_password, :is_blocked, :is_customer_locked)
end
-
- # Make Savon log to Rails.logger and turn_off_debugging!
- def use_with_rails!
- if Rails.env.production?
- self.turn_off_debugging!
- end
- @savon_options[:logger] = Rails.logger # using the Rails logger
- end
-
- # yes, i know, it smells bad
- def convert_array_to_hash(array)
- result = {}
- array.each_with_index do |value, index|
- result[index] = value
- end
- result
- end
-
- def urlencode(input)
- output = URI.encode_www_form_component(input)
- output.gsub!('+', '%20')
- output.gsub!('%7E', '~')
- output
- end
-
- 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
-
-
- # 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!
- 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
-
- def to_cookies(content)
- content.map do |item|
- HTTPI::Cookie.new item
- end
- end
-
-
- # Used for authentication
- 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
-
- # 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.
- def client!
- @client = Savon::Client.new(@savon_options) do
- namespaces(
- "xmlns:enc" => "http://schemas.xmlsoap.org/soap/encoding/"
- )
- end
- return @client
- end
-
- # Returns a Savon::Client object to be used in the connection.
- # This object is re-used and cached as @client.
- def client
- @client ||= client!
- end
-
- # Returns Array with all possible SOAP WSDL actions.
- def actions
- client.operations
- end
-
- # This makes sure that arrays are properly encoded as soap-arrays by Gyoku
- 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
-
-
- # converts the savon response object to something we can return to the caller
- # - A TransipStruct object
- # - An array of TransipStructs
- # - nil
- def process_response(response)
- response = response.to_hash.values.first[:return] rescue nil
- TransipStruct.from_soap(response)
-
- end
-
- # This is the main request function
- # throws ApiError
- # returns response object (can be TransipStruct or Array of TransipStruct)
- 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
-end
+end
\ No newline at end of file