transip 0.3.7 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1 +1,5 @@
1
- *.gem
1
+ *.gem
2
+ .DS_Store
3
+ .bundle/
4
+ vendor/
5
+ tmp/
data/Gemfile CHANGED
@@ -1,6 +1,4 @@
1
- source "http://rubygems.org"
2
- source 'http://gemcutter.org'
3
- source 'http://gems.github.com'
1
+ source 'https://rubygems.org'
4
2
 
5
3
  gem 'savon', '>= 2.3.0'
6
4
  gem 'curb', '>= 0.8.4'
@@ -1,19 +1,17 @@
1
1
  = TransIP API
2
2
 
3
- Ruby gem to use the full TransIP API (v4.2).
4
- Uses an updated version of savon, and implements the new request signing method that the guys at TransIP have introduced into their api. So far, i have only tested the :get_domain_names, :get_info and :set_dns_entries calls.
3
+ Ruby gem to use the full TransIP (www.transip.nl) API (v5.0).
4
+ Uses an updated version of savon and implements the new request signing method that the guys at TransIP have introduced into their API.
5
5
 
6
- The transip API makes use of public/private key encryption. You need to use the TransIP
7
- control panel to give your server access to the api, and to generate a key. You can then
8
- use the key together with your username to gain access to the api
6
+ The TransIP API makes use of public/private key encryption. You need to use the TransIP
7
+ control panel to give your server access to the API and to generate a key. You can then
8
+ use the key together with your username to gain access to the API.
9
9
 
10
10
  For more info see:
11
11
 
12
12
  * <b>The origin of this code:</b> https://github.com/joost/transip
13
13
  * <b>TransIP API Docs:</b> https://www.transip.nl/g/api
14
14
 
15
- Credits for full rewrite to work with new TransIP API version go to Richard Bronkhorst (https://github.com/richmans).
16
-
17
15
  == Install
18
16
 
19
17
  Install from rubygems (rubygems.org/gems/transip):
@@ -26,7 +24,7 @@ or add in your Bundle Gemfile:
26
24
 
27
25
  For the latest version: Download / clone the repository. Bundle install the needed gems and require the lib.
28
26
 
29
- git clone git://github.com/joost/transip.git
27
+ git clone https://github.com/joost/transip.git
30
28
  cd transip
31
29
  bundle install
32
30
  irb # and require './lib/transip'
@@ -46,19 +44,47 @@ For the most up-to-date documentation see the source files.
46
44
  Setup the API client:
47
45
 
48
46
  # use this in production
49
- transip = Transip.new(username: 'your_username', key: 'your_private_rsa_key', ip: '12.34.12.3', mode: :readwrite)
47
+ transip = Transip::DomainClient.new(username: 'your_username', key: 'your_private_rsa_key', ip: '12.34.12.3', mode: :readwrite)
48
+
49
+ You can use Transip::DomainClient, Transip::VpsClient, Transip::ColocationClient, Transip::WebhostingClient and Transip::ForwardClient.
50
50
 
51
51
  In development you can leave out the ip. To test request use :readonly mode.
52
52
 
53
+ If you store your private key in a seperate file you can do:
54
+
55
+ transip = Transip::DomainClient.new(username: 'your_username', key_file: 'path_to_your_private_key_file', ip: '12.34.12.3', mode: :readwrite)
56
+
57
+ === DomainClient
58
+
59
+ transip = Transip::DomainClient.new(username: 'your_username', key: 'your_private_rsa_key', ip: '12.34.12.3', mode: :readwrite)
60
+
53
61
  transip.actions # => [:batch_check_availability, :check_availability, :get_whois, :get_domain_names, :get_info, :batch_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, :get_all_tld_infos, :get_tld_info, :get_current_domain_action, :retry_current_domain_action_with_new_data, :retry_transfer_with_different_auth_code, :cancel_domain_action]
54
62
 
55
63
  transip.request(:get_domain_names)
56
64
  transip.request(:get_info, :domain_name => 'example.com')
57
65
  transip.request(:get_whois, :domain_name => 'example.com')
58
66
  transip.request(:set_dns_entries, :domain_name => 'example.com', :dns_entries => [Transip::DnsEntry.new('test', 5.inutes, 'A', '74.125.77.147')])
59
- transip.request(:register, Transip::Domain.new('example.com', nil, nil, [Transip::DnsEntry.new('test', 5.minutes, 'A', '74.125.77.147')]))
67
+ transip.request(:register, Transip::Domain.new('example.com', nil, nil, [Transip::DnsEntry.new('test', 300, 'A', '74.125.77.147')]))
60
68
  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')])
61
69
 
70
+ === VpsClient
71
+
72
+ transip_vps = Transip::VpsClient.new(username: 'your_username', key: 'your_private_rsa_key', ip: '12.34.12.3', mode: :readwrite)
73
+
74
+ transip_vps.actions # => [:get_available_products, :get_available_addons, :get_active_addons_for_vps, :get_available_upgrades, :get_available_addons_for_vps, :get_cancellable_addons_for_vps, :order_vps, :order_addon, :order_private_network, :upgrade_vps, :cancel_vps, :cancel_addon, :cancel_private_network, :get_private_networks_by_vps, :get_all_private_networks, :add_vps_to_private_network, :remove_vps_from_private_network, :get_amount_of_traffic_used, :start, :stop, :reset, :create_snapshot, :revert_snapshot, :remove_snapshot, :get_vps, :get_vpses, :get_snapshots_by_vps, :get_operating_systems, :install_operating_system, :get_ips_for_vps, :get_all_ips, :add_ipv6_to_vps, :update_ptr_record]
75
+
76
+ transip_vps.request(:get_available_products)
77
+ transip_vps.request(:get_operating_systems)
78
+ transip_vps.request(:order_vps, product_name: 'vps-bladevps-x1', addons: nil, operating_system_name: 'ubuntu-12.04.1-transip', hostname: 'my.hostname.com')
79
+
80
+ === ColocationClient, WebhostingClient and ForwardClient
81
+
82
+ Same as above.
83
+
84
+ Find out full methods and parameters by going throught the PHP sources of the TransIP API. You can find them via www.transip.nl.
85
+
86
+ == Contribute and licence
87
+
62
88
  Please feel free to contribute and send me a pull request via Github!
63
89
 
64
- Copyright (c) 2013 Richard Bronkhorst, released under the MIT license
90
+ (C)opyright 2014 Joost Hietbrink / Richard Bronkhorst, released under the MIT license.
@@ -9,14 +9,17 @@ require 'base64'
9
9
  require 'ipaddr'
10
10
 
11
11
  require File.expand_path '../transip/version', __FILE__
12
+ require File.expand_path '../transip/client', __FILE__
13
+ require File.expand_path '../transip/api_error', __FILE__
14
+
12
15
  #
13
- # Implements the www.transip.nl API (v4.2). For more info see: https://www.transip.nl/g/api/
16
+ # Implements the www.transip.nl API (v5.0). For more info see: https://www.transip.nl/g/api/
14
17
  #
15
18
  # The transip API makes use of public/private key encryption. You need to use the TransIP
16
19
  # control panel to give your server access to the api, and to generate a key. You can then
17
20
  # use the key together with your username to gain access to the api
18
21
  # Usage:
19
- # transip = Transip.new(:username => 'api_username', :key => private_key, :ip => '12.34.12.3', :mode => 'readwrite') # use this in production
22
+ # transip = Transip::DomainClient.new(:username => 'api_username', :key => private_key, :ip => '12.34.12.3', :mode => 'readwrite') # use this in production
20
23
  # 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]
21
24
  # transip.request(:get_domain_names)
22
25
  # transip.request(:get_info, :domain_name => 'example.com')
@@ -25,16 +28,13 @@ require File.expand_path '../transip/version', __FILE__
25
28
  # 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')])
26
29
  # transip.request(:register, Transip::Domain.new('example.com', nil, nil, [Transip::DnsEntry.new('test', 5.minutes, 'A', '74.125.77.147')]))
27
30
  #
28
- class Transip
29
- SERVICE = 'DomainService'
30
- WSDL = 'https://api.transip.nl/wsdl/?service=DomainService'
31
- API_VERSION = '4.2'
32
-
33
- attr_accessor :username, :password, :ip, :mode, :hash
34
- attr_reader :response
31
+ module Transip
35
32
 
36
- # Following Error needs to be catched in your code!
37
- class ApiError < RuntimeError
33
+ # Backwards compatibility with v3.x of the gem.
34
+ # TODO: Remove
35
+ def self.new(*args)
36
+ puts "Transip.new is deprecated. Use Transip::DomainClient.new instead!"
37
+ Client.new(*args)
38
38
  end
39
39
 
40
40
  # Following subclasses are actually not needed (as you can also
@@ -70,7 +70,7 @@ class Transip
70
70
 
71
71
  # See what happens here: http://snippets.dzone.com/posts/show/302
72
72
  def members_to_hash
73
- Hash[*members.collect {|m| [member_name_to_camel(m), self.send(m)]}.flatten]
73
+ Hash[*members.collect {|m| [member_name_to_camel(m), self.send(m)]}.flatten(1)]
74
74
  end
75
75
 
76
76
  def to_hash
@@ -186,231 +186,13 @@ class Transip
186
186
  class Tld < TransipStruct.new(:name, :price, :renewal_price, :capabilities, :registration_period_length, :cancel_time_frame)
187
187
  end
188
188
 
189
- # Options:
190
- # * username
191
- # * ip
192
- # * key
193
- # * mode
194
- #
195
- # Example:
196
- # transip = Transip.new(:username => 'api_username', :ip => '12.34.12.3', :key => mykey, :mode => 'readwrite') # use this in production
197
- def initialize(options = {})
198
- @key = options[:key]
199
- @username = options[:username]
200
- @ip = options[:ip]
201
- raise ArgumentError, "The :username, :ip and :key options are required!" if @username.nil? or @key.nil?
202
-
203
- @mode = options[:mode] || :readonly
204
- @endpoint = options[:endpoint] || 'api.transip.nl'
205
- if options[:password]
206
- @password = options[:password]
207
- end
208
- @savon_options = {
209
- :wsdl => WSDL
210
- }
211
- # By default we don't want to debug!
212
- self.turn_off_debugging!
213
- end
214
-
215
- # By default we don't want to debug!
216
- # Changing might impact other Savon usages.
217
- def turn_off_debugging!
218
- @savon_options[:log] = false # disable logging
219
- @savon_options[:log_level] = :info # changing the log level
220
- end
221
-
222
-
223
- # Make Savon log to Rails.logger and turn_off_debugging!
224
- def use_with_rails!
225
- if Rails.env.production?
226
- self.turn_off_debugging!
227
- end
228
- @savon_options[:logger] = Rails.logger # using the Rails logger
229
- end
230
-
231
- # yes, i know, it smells bad
232
- def convert_array_to_hash(array)
233
- result = {}
234
- array.each_with_index do |value, index|
235
- result[index] = value
236
- end
237
- result
238
- end
239
-
240
- def urlencode(input)
241
- output = URI.encode_www_form_component(input)
242
- output.gsub!('+', '%20')
243
- output.gsub!('%7E', '~')
244
- output
245
- end
246
-
247
- def serialize_parameters(parameters, key_prefix=nil)
248
- parameters = parameters.to_hash.values.first if parameters.is_a? TransipStruct
249
- parameters = convert_array_to_hash(parameters) if parameters.is_a? Array
250
- if not parameters.is_a? Hash
251
- return urlencode(parameters)
252
- end
253
-
254
- encoded_parameters = []
255
- parameters.each do |key, value|
256
- next if key.to_s == '@xsi:type'
257
- encoded_key = (key_prefix.nil?) ? urlencode(key) : "#{key_prefix}[#{urlencode(key)}]"
258
- if value.is_a? Hash or value.is_a? Array or value.is_a? TransipStruct
259
- encoded_parameters << serialize_parameters(value, encoded_key)
260
- else
261
- encoded_value = urlencode(value)
262
- encoded_parameters << "#{encoded_key}=#{encoded_value}"
263
- end
264
- end
265
-
266
- encoded_parameters = encoded_parameters.join("&")
267
- #puts encoded_parameters.split('&').join("\n")
268
- encoded_parameters
269
- end
270
-
271
-
272
- # does all the techy stuff to calculate transip's sick authentication scheme:
273
- # a hash with all the request information is subsequently:
274
- # serialized like a www form
275
- # SHA512 digested
276
- # asn1 header added
277
- # private key encrypted
278
- # Base64 encoded
279
- # URL encoded
280
- # I think the guys at transip were trying to use their entire crypto-toolbox!
281
- def signature(method, parameters, time, nonce)
282
- formatted_method = method.to_s.lower_camelcase
283
- parameters ||= {}
284
- input = convert_array_to_hash(parameters.values)
285
- options = {
286
- '__method' => formatted_method,
287
- '__service' => SERVICE,
288
- '__hostname' => @endpoint,
289
- '__timestamp' => time,
290
- '__nonce' => nonce
291
-
292
- }
293
- input.merge!(options)
294
- raise "Invalid RSA key" unless @key =~ /-----BEGIN (RSA )?PRIVATE KEY-----(.*)-----END (RSA )?PRIVATE KEY-----/sim
295
- serialized_input = serialize_parameters(input)
296
-
297
- digest = Digest::SHA512.new.digest(serialized_input)
298
- asn_header = "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40"
299
-
300
- # convert asn_header literal to ASCII-8BIT
301
- if RUBY_VERSION.split('.')[0] == "2"
302
- asn = asn_header.b + digest
303
- else
304
- asn = asn_header + digest
305
- end
306
- private_key = OpenSSL::PKey::RSA.new(@key)
307
- encrypted_asn = private_key.private_encrypt(asn)
308
- readable_encrypted_asn = Base64.encode64(encrypted_asn)
309
- urlencode(readable_encrypted_asn)
310
- end
311
-
312
- def to_cookies(content)
313
- content.map do |item|
314
- HTTPI::Cookie.new item
315
- end
316
- end
317
-
318
-
319
- # Used for authentication
320
- def cookies(method, parameters)
321
- time = Time.new.to_i
322
- #strip out the -'s because transip requires the nonce to be between 6 and 32 chars
323
- nonce = SecureRandom.uuid.gsub("-", '')
324
- result = to_cookies [ "login=#{self.username}",
325
- "mode=#{self.mode}",
326
- "timestamp=#{time}",
327
- "nonce=#{nonce}",
328
- "clientVersion=#{API_VERSION}",
329
- "signature=#{signature(method, parameters, time, nonce)}"
330
-
331
- ]
332
- #puts signature(method, parameters, time, nonce)
333
- result
334
- end
335
-
336
- # Same as client method but initializes a brand new fresh client.
337
- # You have to use this one when you want to re-set the mode (readwrite, readonly),
338
- # or authentication details of your client.
339
- def client!
340
- @client = Savon::Client.new(@savon_options) do
341
- namespaces(
342
- "xmlns:enc" => "http://schemas.xmlsoap.org/soap/encoding/"
343
- )
344
- end
345
- return @client
346
- end
189
+ # VPS related methods
190
+ # Available from TransIp v5.0.
347
191
 
348
- # Returns a Savon::Client object to be used in the connection.
349
- # This object is re-used and cached as @client.
350
- def client
351
- @client ||= client!
192
+ 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)
352
193
  end
353
194
 
354
- # Returns Array with all possible SOAP WSDL actions.
355
- def actions
356
- client.operations
195
+ 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)
357
196
  end
358
197
 
359
- # This makes sure that arrays are properly encoded as soap-arrays by Gyoku
360
- def fix_array_definitions(options)
361
- result = {}
362
- options.each do |key, value|
363
- if value.is_a? Array and value.size > 0
364
- entry_name = value.first.class.name.split(":").last
365
- result[key] = {
366
- 'item' => {:content! => value, :'@xsi:type' => "tns:#{entry_name}"},
367
- :'@xsi:type' => "tns:ArrayOf#{entry_name}",
368
- :'@enc:arrayType' => "tns:#{entry_name}[#{value.size}]"
369
- }
370
- else
371
- result[key] = value
372
- end
373
- end
374
- result
375
- end
376
-
377
-
378
- # converts the savon response object to something we can return to the caller
379
- # - A TransipStruct object
380
- # - An array of TransipStructs
381
- # - nil
382
- def process_response(response)
383
- response = response.to_hash.values.first[:return] rescue nil
384
- TransipStruct.from_soap(response)
385
-
386
- end
387
-
388
- # This is the main request function
389
- # throws ApiError
390
- # returns response object (can be TransipStruct or Array of TransipStruct)
391
- def request(action, options = nil)
392
- formatted_action = action.to_s.lower_camelcase
393
- parameters = {
394
- # for some reason, the transip server wants the body root tag to be
395
- # the name of the action.
396
- :message_tag => formatted_action
397
- }
398
- options = options.to_hash if options.is_a? Transip::TransipStruct
399
-
400
- if options.is_a? Hash
401
- xml_options = fix_array_definitions(options)
402
- elsif options.nil?
403
- xml_options = nil
404
- else
405
- raise "Invalid parameter format (should be nil, hash or TransipStruct"
406
- end
407
- parameters[:message] = xml_options
408
- parameters[:cookies] = cookies(action, options)
409
- #puts parameters.inspect
410
- response = client.call(action, parameters)
411
-
412
- process_response(response)
413
- rescue Savon::SOAPFault => e
414
- raise ApiError.new(e), e.message.sub(/^\(\d+\)\s+/,'') # We raise our own error (FIXME: Correct?).
415
- end
416
- end
198
+ end
@@ -0,0 +1,5 @@
1
+ module Transip
2
+ # Following Error needs to be catched in your code!
3
+ class ApiError < RuntimeError
4
+ end
5
+ end
@@ -0,0 +1,285 @@
1
+ module Transip
2
+
3
+ class Client
4
+
5
+ API_VERSION = '5.0'
6
+ API_SERVICE = 'DomainService'
7
+
8
+ attr_accessor :username, :password, :ip, :mode, :hash
9
+ attr_reader :response
10
+
11
+ def api_version
12
+ # We use self.class:: here to not use parentclass constant.
13
+ @api_version || self.class::API_VERSION
14
+ end
15
+
16
+ def api_service
17
+ @api_service || self.class::API_SERVICE
18
+ end
19
+
20
+ def wsdl
21
+ "https://api.transip.nl/wsdl/?service=#{api_service}"
22
+ end
23
+
24
+ attr_accessor :debug
25
+
26
+ # Options:
27
+ # * username - Your login name on the TransIP website.
28
+ # * ip - needed in production
29
+ # * key / key_file - key is one of your private keys (these can be requested via your Controlpanel). key_file is path to file containing key.
30
+ # * mode - :readonly, :readwrite
31
+ #
32
+ # Example:
33
+ # transip = Transip.new(:username => 'api_username', :ip => '12.34.12.3', :key => mykey, :mode => 'readwrite') # use this in production
34
+ def initialize(options = {})
35
+ @key = options[:key] || (options[:key_file] && File.read(options[:key_file]))
36
+ @username = options[:username]
37
+ @ip = options[:ip]
38
+ @api_version = options[:api_version]
39
+ @api_service = options[:api_service]
40
+ raise ArgumentError, "The :username, :ip and :key options are required!" if @username.nil? or @key.nil?
41
+
42
+ @mode = options[:mode] || :readonly
43
+ @endpoint = options[:endpoint] || 'api.transip.nl'
44
+ if options[:password]
45
+ @password = options[:password]
46
+ end
47
+ @savon_options = {
48
+ :wsdl => wsdl
49
+ }
50
+ # By default we don't want to debug!
51
+ self.turn_off_debugging!
52
+ end
53
+
54
+ # By default we don't want to debug!
55
+ # Changing might impact other Savon usages.
56
+ def turn_off_debugging!
57
+ @savon_options[:log] = false # disable logging
58
+ @savon_options[:log_level] = :info # changing the log level
59
+ end
60
+
61
+ # Make Savon log to Rails.logger and turn_off_debugging!
62
+ def use_with_rails!
63
+ if Rails.env.production?
64
+ self.turn_off_debugging!
65
+ end
66
+ @savon_options[:logger] = Rails.logger # using the Rails logger
67
+ end
68
+
69
+ # yes, i know, it smells bad
70
+ def convert_array_to_hash(array)
71
+ result = {}
72
+ array.each_with_index do |value, index|
73
+ result[index] = value
74
+ end
75
+ result
76
+ end
77
+
78
+ def urlencode(input)
79
+ output = URI.encode_www_form_component(input)
80
+ output.gsub!('+', '%20')
81
+ output.gsub!('%7E', '~')
82
+ output
83
+ end
84
+
85
+ def serialize_parameters(parameters, key_prefix=nil)
86
+ debug_log("serialize_parameters(#{parameters.inspect}, #{key_prefix.inspect}")
87
+
88
+ parameters = parameters.to_hash.values.first if parameters.is_a? TransipStruct
89
+ parameters = convert_array_to_hash(parameters) if parameters.is_a? Array
90
+ if not parameters.is_a? Hash
91
+ return urlencode(parameters)
92
+ end
93
+ return "#{key_prefix}=" if parameters.empty?
94
+
95
+ encoded_parameters = []
96
+ parameters.each do |key, value|
97
+ next if key.to_s == '@xsi:type'
98
+ encoded_key = (key_prefix.nil?) ? urlencode(key) : "#{key_prefix}[#{urlencode(key)}]"
99
+ if value.is_a?(Hash) or value.is_a?(Array) or value.is_a?(TransipStruct)
100
+ encoded_parameters << serialize_parameters(value, encoded_key)
101
+ else
102
+ encoded_value = urlencode(value)
103
+ encoded_parameters << "#{encoded_key}=#{encoded_value}"
104
+ end
105
+ end
106
+
107
+ encoded_parameters = encoded_parameters.join("&")
108
+ debug_log("encoded_parameters:\n#{encoded_parameters.split('&').join("\n")}")
109
+ encoded_parameters
110
+ end
111
+
112
+ # does all the techy stuff to calculate transip's sick authentication scheme:
113
+ # a hash with all the request information is subsequently:
114
+ # serialized like a www form
115
+ # SHA512 digested
116
+ # asn1 header added
117
+ # private key encrypted
118
+ # Base64 encoded
119
+ # URL encoded
120
+ # I think the guys at transip were trying to use their entire crypto-toolbox!
121
+ def signature(method, parameters, time, nonce)
122
+ formatted_method = method.to_s.lower_camelcase
123
+ parameters ||= {}
124
+ input = convert_array_to_hash(parameters.values)
125
+ options = {
126
+ '__method' => formatted_method,
127
+ '__service' => api_service,
128
+ '__hostname' => @endpoint,
129
+ '__timestamp' => time,
130
+ '__nonce' => nonce
131
+
132
+ }
133
+ input.merge!(options)
134
+ raise "Invalid RSA key" unless @key =~ /-----BEGIN (RSA )?PRIVATE KEY-----(.*)-----END (RSA )?PRIVATE KEY-----/sim
135
+ serialized_input = serialize_parameters(input)
136
+
137
+ digest = Digest::SHA512.new.digest(serialized_input)
138
+ asn_header = "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40"
139
+
140
+ # convert asn_header literal to ASCII-8BIT
141
+ if RUBY_VERSION.split('.')[0] == "2"
142
+ asn = asn_header.b + digest
143
+ else
144
+ asn = asn_header + digest
145
+ end
146
+ private_key = OpenSSL::PKey::RSA.new(@key)
147
+ encrypted_asn = private_key.private_encrypt(asn)
148
+ readable_encrypted_asn = Base64.encode64(encrypted_asn)
149
+ urlencode(readable_encrypted_asn)
150
+ end
151
+
152
+ def to_cookies(content)
153
+ content.map do |item|
154
+ HTTPI::Cookie.new item
155
+ end
156
+ end
157
+
158
+ # Used for authentication
159
+ def cookies(method, parameters)
160
+ time = Time.new.to_i
161
+ #strip out the -'s because transip requires the nonce to be between 6 and 32 chars
162
+ nonce = SecureRandom.uuid.gsub("-", '')
163
+ result = to_cookies [ "login=#{self.username}",
164
+ "mode=#{self.mode}",
165
+ "timestamp=#{time}",
166
+ "nonce=#{nonce}",
167
+ "clientVersion=#{api_version}",
168
+ "signature=#{signature(method, parameters, time, nonce)}"
169
+
170
+ ]
171
+ debug_log("signature:\n#{signature(method, parameters, time, nonce)}")
172
+ result
173
+ end
174
+
175
+ # Same as client method but initializes a brand new fresh client.
176
+ # You have to use this one when you want to re-set the mode (readwrite, readonly),
177
+ # or authentication details of your client.
178
+ def client!
179
+ @client = Savon::Client.new(@savon_options) do
180
+ namespaces(
181
+ "xmlns:enc" => "http://schemas.xmlsoap.org/soap/encoding/"
182
+ )
183
+ end
184
+ return @client
185
+ end
186
+
187
+ # Returns a Savon::Client object to be used in the connection.
188
+ # This object is re-used and cached as @client.
189
+ def client
190
+ @client ||= client!
191
+ end
192
+
193
+ # Returns Array with all possible SOAP WSDL actions.
194
+ def actions
195
+ client.operations
196
+ end
197
+
198
+ # This makes sure that arrays are properly encoded as soap-arrays by Gyoku
199
+ def fix_array_definitions(options)
200
+ result = {}
201
+ options.each do |key, value|
202
+ if value.is_a?(Array) and (value.size > 0)
203
+ entry_name = value.first.class.name.split(":").last
204
+ result[key] = {
205
+ 'item' => {:content! => value, :'@xsi:type' => "tns:#{entry_name}"},
206
+ :'@xsi:type' => "tns:ArrayOf#{entry_name}",
207
+ :'@enc:arrayType' => "tns:#{entry_name}[#{value.size}]"
208
+ }
209
+ elsif value.is_a?(Hash)
210
+ result[key] = fix_array_definitions(value)
211
+ else
212
+ result[key] = value
213
+ end
214
+ end
215
+ result
216
+ end
217
+
218
+ # converts the savon response object to something we can return to the caller
219
+ # - A TransipStruct object
220
+ # - An array of TransipStructs
221
+ # - nil
222
+ def process_response(response)
223
+ response = response.to_hash.values.first[:return] rescue nil
224
+ TransipStruct.from_soap(response)
225
+ end
226
+
227
+ # This is the main request function
228
+ # throws ApiError
229
+ # returns response object (can be TransipStruct or Array of TransipStruct)
230
+ def request(action, options = nil)
231
+ formatted_action = action.to_s.lower_camelcase
232
+ parameters = {
233
+ # for some reason, the transip server wants the body root tag to be
234
+ # the name of the action.
235
+ :message_tag => formatted_action
236
+ }
237
+ options = options.to_hash if options.is_a?(Transip::TransipStruct)
238
+
239
+ if options.is_a?(Hash)
240
+ xml_options = fix_array_definitions(options)
241
+ elsif options.nil?
242
+ xml_options = nil
243
+ else
244
+ raise "Invalid parameter format (should be nil, hash or TransipStruct)"
245
+ end
246
+ parameters[:message] = xml_options
247
+ parameters[:cookies] = cookies(action, options)
248
+ debug_log("parameters:\n#{parameters.inspect}")
249
+ response = client.call(action, parameters)
250
+
251
+ process_response(response)
252
+ rescue Savon::SOAPFault => e
253
+ raise ApiError.new(e), e.message.sub(/^\(\d+\)\s+/,'') # We raise our own error (FIXME: Correct?).
254
+ end
255
+
256
+ private
257
+
258
+ def debug_log(msg)
259
+ puts msg if @debug
260
+ end
261
+
262
+ end
263
+
264
+ # 'Aliased' by Transip::Client.
265
+ class DomainClient < Client;end
266
+
267
+ # We name it VpsClient instead of VpsService since the latter is already in use by
268
+ # the TransipStruct.
269
+ class VpsClient < Client
270
+ API_SERVICE = 'VpsService'
271
+ end
272
+
273
+ class ColocationClient < Client
274
+ API_SERVICE = 'ColocationService'
275
+ end
276
+
277
+ class WebhostingClient < Client
278
+ API_SERVICE = 'WebhostingService'
279
+ end
280
+
281
+ class ForwardClient < Client
282
+ API_SERVICE = 'ForwardService'
283
+ end
284
+
285
+ end
@@ -1,3 +1,3 @@
1
- class Transip
2
- VERSION = "0.3.7"
1
+ module Transip
2
+ VERSION = "0.4.1"
3
3
  end
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Transip::VERSION
9
9
  spec.authors = ["Joost Hietbrink", "Richard Bronkhorst"]
10
10
  spec.email = ["joost@joopp.com"]
11
- spec.description = %q{Ruby gem to use the full TransIP API (v4.2).}
12
- spec.summary = %q{Ruby gem to use the full TransIP API (v4.2).}
11
+ spec.description = %q{Ruby gem to use the full TransIP API (v5.0).}
12
+ spec.summary = %q{Ruby gem to use the full TransIP API (v5.0).}
13
13
  spec.homepage = %q{http://github.com/joost/transip}
14
14
  spec.license = "MIT"
15
15
 
metadata CHANGED
@@ -1,7 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: transip
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.7
4
+ version: 0.4.1
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Joost Hietbrink
@@ -9,11 +10,12 @@ authors:
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2013-12-16 00:00:00.000000000 Z
13
+ date: 2014-02-25 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: savon
16
17
  requirement: !ruby/object:Gem::Requirement
18
+ none: false
17
19
  requirements:
18
20
  - - ! '>='
19
21
  - !ruby/object:Gem::Version
@@ -21,6 +23,7 @@ dependencies:
21
23
  type: :runtime
22
24
  prerelease: false
23
25
  version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
24
27
  requirements:
25
28
  - - ! '>='
26
29
  - !ruby/object:Gem::Version
@@ -28,6 +31,7 @@ dependencies:
28
31
  - !ruby/object:Gem::Dependency
29
32
  name: curb
30
33
  requirement: !ruby/object:Gem::Requirement
34
+ none: false
31
35
  requirements:
32
36
  - - ! '>='
33
37
  - !ruby/object:Gem::Version
@@ -35,6 +39,7 @@ dependencies:
35
39
  type: :runtime
36
40
  prerelease: false
37
41
  version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
38
43
  requirements:
39
44
  - - ! '>='
40
45
  - !ruby/object:Gem::Version
@@ -42,6 +47,7 @@ dependencies:
42
47
  - !ruby/object:Gem::Dependency
43
48
  name: facets
44
49
  requirement: !ruby/object:Gem::Requirement
50
+ none: false
45
51
  requirements:
46
52
  - - ! '>='
47
53
  - !ruby/object:Gem::Version
@@ -49,11 +55,12 @@ dependencies:
49
55
  type: :runtime
50
56
  prerelease: false
51
57
  version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
52
59
  requirements:
53
60
  - - ! '>='
54
61
  - !ruby/object:Gem::Version
55
62
  version: 2.9.3
56
- description: Ruby gem to use the full TransIP API (v4.2).
63
+ description: Ruby gem to use the full TransIP API (v5.0).
57
64
  email:
58
65
  - joost@joopp.com
59
66
  executables: []
@@ -68,31 +75,34 @@ files:
68
75
  - MIT-LICENSE
69
76
  - README.rdoc
70
77
  - lib/transip.rb
78
+ - lib/transip/api_error.rb
79
+ - lib/transip/client.rb
71
80
  - lib/transip/version.rb
72
81
  - transip.gemspec
73
82
  homepage: http://github.com/joost/transip
74
83
  licenses:
75
84
  - MIT
76
- metadata: {}
77
85
  post_install_message:
78
86
  rdoc_options:
79
87
  - --charset=UTF-8
80
88
  require_paths:
81
89
  - lib
82
90
  required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
83
92
  requirements:
84
93
  - - ! '>='
85
94
  - !ruby/object:Gem::Version
86
95
  version: '0'
87
96
  required_rubygems_version: !ruby/object:Gem::Requirement
97
+ none: false
88
98
  requirements:
89
99
  - - ! '>='
90
100
  - !ruby/object:Gem::Version
91
101
  version: '0'
92
102
  requirements: []
93
103
  rubyforge_project:
94
- rubygems_version: 2.1.1
104
+ rubygems_version: 1.8.23
95
105
  signing_key:
96
- specification_version: 4
97
- summary: Ruby gem to use the full TransIP API (v4.2).
106
+ specification_version: 3
107
+ summary: Ruby gem to use the full TransIP API (v5.0).
98
108
  test_files: []
checksums.yaml DELETED
@@ -1,15 +0,0 @@
1
- ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- NjcxZDVlYWExZTQ0MjA2NGZmYWNjM2QzOTkwNjg0OTgzODgzNGRiNg==
5
- data.tar.gz: !binary |-
6
- NzM0ZjJlMzBjNDExYmVmMzE1ZDA1MjA0NTk1NGVkZTIwNDZmOTBlNQ==
7
- SHA512:
8
- metadata.gz: !binary |-
9
- OGNhMDg5ZjBiZTg2ZmZjZGUzZjZiYTkxZWI4MGMxMzYwZWFhNDM5OGIxZWVl
10
- ZDY1ZmY0YWYxZjZhYWIzZGU4NzM1YTYyZGZlOTBjMGYxMWIzN2FiNWUxMjBl
11
- NzA1OWE1Nzk4NTg0ZDBlNDM1MGFmZWQ2NzdjYzMyMTFiNTU3MzA=
12
- data.tar.gz: !binary |-
13
- NGFkYTQ4NWY5Y2U5ZDg4YTQ2ODdjYjY4ZGRiNzA0NmRiZGRiMWVlYzg2YjIx
14
- YzRlNTNlODM3Yjg5NThlNzBkZTRjMmU0MWU1NDVjZDUyNDZhMzVmY2RjMThh
15
- MmFkMGVmYWNhMmM1YWY1MzE4YjA4YWZiMDc4ZWNjODNlN2FhYzA=