registrar-client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. data/LICENSE +19 -0
  2. data/Readme.md +70 -0
  3. data/Spec.md +55 -0
  4. data/examples/enom.example.yml +4 -0
  5. data/examples/purchase_com.rb +7 -0
  6. data/lib/registrar-client.rb +1 -0
  7. data/lib/registrar.rb +14 -0
  8. data/lib/registrar/client.rb +224 -0
  9. data/lib/registrar/contact.rb +31 -0
  10. data/lib/registrar/domain.rb +26 -0
  11. data/lib/registrar/extended_attribute.rb +12 -0
  12. data/lib/registrar/extended_attribute_descriptor.rb +31 -0
  13. data/lib/registrar/extended_attribute_option_descriptor.rb +7 -0
  14. data/lib/registrar/name_server.rb +16 -0
  15. data/lib/registrar/order.rb +43 -0
  16. data/lib/registrar/provider/enom.rb +430 -0
  17. data/lib/registrar/provider/enom/contact.rb +68 -0
  18. data/lib/registrar/provider/enom/extended_attribute.rb +64 -0
  19. data/lib/registrar/provider/enom/extended_attribute_be.rb +0 -0
  20. data/lib/registrar/provider/enom/extended_attribute_ca.rb +40 -0
  21. data/lib/registrar/provider/enom/extended_attribute_io.rb +18 -0
  22. data/lib/registrar/provider/enom/extended_attribute_us.rb +29 -0
  23. data/lib/registrar/provider/enom/order.rb +41 -0
  24. data/lib/registrar/provider/opensrs.rb +133 -0
  25. data/lib/registrar/provider/opensrs/contact.rb +30 -0
  26. data/lib/registrar/provider/opensrs/contact_set.rb +24 -0
  27. data/lib/registrar/provider/opensrs/name_server_list.rb +26 -0
  28. data/lib/registrar/provider/opensrs/operation.rb +42 -0
  29. data/lib/registrar/provider/opensrs/order.rb +59 -0
  30. data/lib/registrar/provider/opensrs/tld_data.rb +29 -0
  31. data/lib/registrar/provider/opensrs/tld_data_us.rb +48 -0
  32. data/lib/registrar/purchase_options.rb +29 -0
  33. data/lib/registrar/renewal_options.rb +8 -0
  34. metadata +177 -0
@@ -0,0 +1,68 @@
1
+ module Registrar
2
+ module Provider
3
+ # :nodoc:
4
+ class Enom
5
+ # Contact object that wraps a generic contact and can be used to produce and parse
6
+ # wire-level representations of Enom contacts.
7
+ class Contact
8
+ attr_reader :contact
9
+
10
+ def initialize(contact)
11
+ raise ArgumentError, "Contact is required" unless contact
12
+ @contact = contact
13
+ end
14
+
15
+ def identifier
16
+ contact.identifier
17
+ end
18
+
19
+ def identifier=(identifier)
20
+ contact.identifier = identifier
21
+ end
22
+
23
+ # Returns a Hash that can be merged into a query.
24
+ # Type should be one of the following: Registrant, AuxBilling, Tech, Admin
25
+ def to_query(type)
26
+ {
27
+ "#{type}Address1" => contact.address_1,
28
+ "#{type}Address2" => contact.address_2,
29
+ "#{type}City" => contact.city,
30
+ "#{type}Country" => contact.country,
31
+ "#{type}EmailAddress" => contact.email,
32
+ "#{type}Fax" => contact.fax,
33
+ "#{type}FirstName" => contact.first_name,
34
+ "#{type}LastName" => contact.last_name,
35
+ "#{type}JobTitle" => contact.job_title,
36
+ "#{type}OrganizationName" => contact.organization_name,
37
+ "#{type}Phone" => contact.phone,
38
+ "#{type}PhoneExt" => contact.phone_ext,
39
+ "#{type}PostalCode" => contact.postal_code,
40
+ "#{type}StateProvince" => contact.state_province,
41
+ "#{type}StateProvinceChoice" => contact.state_province_choice
42
+ }
43
+ end
44
+
45
+ def self.from_response(type, attributes)
46
+ contact = Registrar::Contact.new
47
+ contact.identifier = attributes["#{type}PartyID"]
48
+ contact.address_1 = attributes["#{type}Address1"]
49
+ contact.address_2 = attributes["#{type}Address2"]
50
+ contact.city = attributes["#{type}City"]
51
+ contact.country = attributes["#{type}Country"]
52
+ contact.email = attributes["#{type}EmailAddress"]
53
+ contact.fax = attributes["#{type}Fax"]
54
+ contact.first_name = attributes["#{type}FirstName"]
55
+ contact.last_name = attributes["#{type}LastName"]
56
+ contact.job_title = attributes["#{type}JobTitle"]
57
+ contact.organization_name = attributes["#{type}OrganizationName"]
58
+ contact.phone = attributes["#{type}Phone"]
59
+ contact.phone_ext = attributes["#{type}PhoneExt"]
60
+ contact.postal_code = attributes["#{type}PostalCode"]
61
+ contact.state_province = attributes["#{type}StateProvince"]
62
+ contact.state_province_choice = attributes["#{type}StateProvinceChoice"]
63
+ self.new(contact)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,64 @@
1
+ require 'registrar/provider/enom/extended_attribute_us'
2
+ require 'registrar/provider/enom/extended_attribute_ca'
3
+ require 'registrar/provider/enom/extended_attribute_io'
4
+
5
+ module Registrar
6
+ module Provider
7
+ class Enom
8
+ # Wrapper around the generic extended attribute that resolves symbolic values
9
+ # to their Enom-specific values.
10
+ class ExtendedAttribute
11
+ include ExtendedAttributeUS
12
+ include ExtendedAttributeCA
13
+ include ExtendedAttributeIO
14
+
15
+ attr_reader :extended_attribute
16
+ def initialize(extended_attribute)
17
+ @extended_attribute = extended_attribute
18
+ end
19
+
20
+ def name
21
+ resolve_name(extended_attribute.tld, extended_attribute.name)
22
+ end
23
+ def value
24
+ resolve_value(extended_attribute.tld, extended_attribute.value)
25
+ end
26
+
27
+ private
28
+ def resolve_name(tld, name)
29
+ case name
30
+ when Symbol then
31
+ names(tld)[name]
32
+ else
33
+ name
34
+ end
35
+ end
36
+
37
+ def names(tld)
38
+ {
39
+ 'us' => us_names,
40
+ 'ca' => ca_names,
41
+ 'io' => io_names,
42
+ }[tld]
43
+ end
44
+
45
+ def resolve_value(tld, value)
46
+ case value
47
+ when Symbol then
48
+ values(tld)[value]
49
+ else
50
+ value
51
+ end
52
+ end
53
+
54
+ def values(tld)
55
+ {
56
+ 'us' => us_values,
57
+ 'ca' => ca_values,
58
+ 'io' => io_values,
59
+ }[tld]
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,40 @@
1
+ module Registrar
2
+ module Provider
3
+ class Enom
4
+ module ExtendedAttributeCA
5
+ protected
6
+ def ca_names
7
+ {
8
+ :"Legal Type" => 'cira_legal_type',
9
+ :"Agreement Version" => 'cira_agreement_version',
10
+ :"Agreement Value" => 'cira_agreement_value',
11
+ }
12
+ end
13
+ def ca_values
14
+ {
15
+ :"Aboriginal Peoples" => "ABO",
16
+ :"Canadian Citizen" => "CCT",
17
+ :"Canadian Resident" => "RES",
18
+ :"Corporation" => "CCO",
19
+ :"Educational" => "EDU",
20
+ :"Government Entity" => "GOV",
21
+ :"Hospital" => "HOP",
22
+ :"Indian Band" => "INB",
23
+ :"Legal Representative" => "LGR",
24
+ :"Library, Archive or Museum" => "LAM",
25
+ :"Official Mark" => "OMK",
26
+ :"Partnership" => "PRT",
27
+ :"Political Party" => "PLT",
28
+ :"The Queen" => "MAJ",
29
+ :"Trade Union" => "TRD",
30
+ :"Trade-mark" => "TDM",
31
+ :"Trust" => "TRS",
32
+ :"Unincorporated Association" => "ASS",
33
+ :"Yes" => "Y",
34
+ :"No" => "N",
35
+ }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,18 @@
1
+ module Registrar
2
+ module Provider
3
+ class Enom
4
+ module ExtendedAttributeIO
5
+ def io_names
6
+ {
7
+ :"Renewal Agreement" => 'io_agreedelete'
8
+ }
9
+ end
10
+ def io_values
11
+ {
12
+ :"Yes" => 'YES'
13
+ }
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,29 @@
1
+ module Registrar
2
+ module Provider
3
+ class Enom
4
+ module ExtendedAttributeUS
5
+ def us_names
6
+ {
7
+ :Nexus => 'us_nexus',
8
+ :Purpose => 'us_purpose',
9
+ :Country => 'global_cc_us'
10
+ }
11
+ end
12
+ def us_values
13
+ {
14
+ :"US Citizen" => 'C11',
15
+ :"Business Entity" => 'C21',
16
+ :"Foreign Entity" => 'C31',
17
+ :"Permanent Resident" => 'C12',
18
+ :"US Based Office" => 'C22',
19
+ :"For Profit" => 'P1',
20
+ :"Non Profit" => 'P2',
21
+ :Personal => 'P3',
22
+ :Educational => 'P4',
23
+ :Government => 'P5',
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,41 @@
1
+ module Registrar
2
+ module Provider
3
+ class Enom
4
+ # An Enom order.
5
+ class Order
6
+ attr_accessor :id
7
+ attr_accessor :order_status
8
+ attr_accessor :order_date
9
+ attr_accessor :status
10
+
11
+ def initialize(id)
12
+ @id = id
13
+ end
14
+
15
+ def status
16
+ @status || 'unknown'
17
+ end
18
+
19
+ def order_status
20
+ @order_status || 'unknown'
21
+ end
22
+
23
+ # Get a generic Registrar::Order object to use.
24
+ def to_order
25
+ order = Registrar::Order.new(id)
26
+ order.successful = status.downcase == "successful"
27
+
28
+ order.status = case order_status.downcase
29
+ when 'open' then :open
30
+ when 'closed' then :closed
31
+ else
32
+ order_status.downcase.to_sym
33
+ end
34
+
35
+ order.date = order_date
36
+ order
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,133 @@
1
+ require 'digest/md5'
2
+ require 'builder'
3
+ require 'nokogiri'
4
+ require 'registrar/provider/opensrs/order'
5
+ require 'registrar/provider/opensrs/operation'
6
+ require 'registrar/provider/opensrs/contact'
7
+ require 'registrar/provider/opensrs/contact_set'
8
+
9
+ module Registrar
10
+ module Provider
11
+ # Implementation of a registrar provider for OpenSRS (http://www.opensrs.com/).
12
+ class OpenSRS
13
+ include HTTParty
14
+ format :xml
15
+ debug_output $stderr
16
+
17
+ attr_accessor :url, :username, :private_key
18
+
19
+ def initialize(url, username, private_key)
20
+ @url = url
21
+ @username = username
22
+ @private_key = private_key
23
+ end
24
+
25
+ def available?(name)
26
+ response = execute(lookup_operation(name).to_xml)
27
+
28
+ items = response['OPS_envelope']['body']['data_block']['dt_assoc']['item']
29
+ items = items.find { |item| item['dt_assoc'] }['dt_assoc']
30
+ items['item'][0] == 'available'
31
+ end
32
+
33
+ def purchase(name, registrant, purchase_options=nil)
34
+ purchase_options ||= Registrar::PurchaseOptions.new
35
+ response = execute(registration_operation(name, registrant,
36
+ purchase_options).to_xml)
37
+ order = check_order(response.body)
38
+ order.add_domain(name, registrant)
39
+ order.to_order
40
+ end
41
+
42
+ def renew(name, renewal_options)
43
+
44
+ end
45
+
46
+ def name_servers(name)
47
+ operation = Operation.new(:get, {
48
+ :domain => name,
49
+ :type => "nameservers"
50
+ })
51
+ nameserver_list_from(execute(operation.to_xml).body)
52
+ end
53
+ alias :nameservers :name_servers
54
+
55
+ private
56
+ def nameserver_list_from(xml)
57
+ Nokogiri::XML(xml).xpath('//dt_array/item/dt_assoc/item[@key="name"]').map do |item|
58
+ Registrar::NameServer.new(item.content)
59
+ end
60
+ end
61
+
62
+ def check_order(xml)
63
+ order_info = execute(order_info_operation(order_id_from(xml)).to_xml)
64
+ Registrar::Provider::OpenSRS::Order.new(order_info.to_s)
65
+ end
66
+
67
+ def order_id_from(xml)
68
+ Nokogiri::XML(xml).xpath(
69
+ "//dt_assoc/item[@key='attributes']/dt_assoc/item[@key='id']").inner_text
70
+ end
71
+
72
+ def order_info_operation(id)
73
+ operation = Operation.new(:get_order_info, {
74
+ :order_id => id})
75
+ end
76
+
77
+ def lookup_operation(name)
78
+ operation = Operation.new(:lookup, {
79
+ :domain => name,
80
+ :no_cache => "1"
81
+ })
82
+ end
83
+
84
+ def registration_operation(name, registrant, purchase_options)
85
+ operation = Operation.new(:sw_register, {
86
+ :domain => name,
87
+ :period => purchase_options.number_of_years,
88
+ :reg_type => "new",
89
+ :handle => 'process',
90
+ :reg_username => 'dnsimple',
91
+ :reg_password => 'password',
92
+ :custom_tech_contact => '1',
93
+ :custom_nameservers => '1',
94
+ :nameserver_list => nameserver_list(purchase_options),
95
+ :contact_set => contact_set(registrant),
96
+ :tld_data => tld_data(purchase_options)
97
+ })
98
+ end
99
+
100
+ def nameserver_list(purchase_options)
101
+ NameServerList.new(purchase_options) if purchase_options
102
+ end
103
+
104
+ def tld_data(purchase_options)
105
+ TldData.build_with(purchase_options) if purchase_options && purchase_options.extended_attributes != []
106
+ end
107
+
108
+ def contact_set(registrant)
109
+ ContactSet.new({
110
+ 'owner' => OpenSRS::Contact.new(registrant),
111
+ 'admin' => OpenSRS::Contact.new(registrant),
112
+ 'billing' => OpenSRS::Contact.new(registrant),
113
+ 'tech' => OpenSRS::Contact.new(registrant)
114
+ })
115
+ end
116
+
117
+ def execute(body)
118
+ self.class.headers(
119
+ "Content-Type" => "text/xml",
120
+ "X-Username" => username,
121
+ "X-Signature" => signature(body),
122
+ "Content-Length" => body.length.to_s
123
+ )
124
+ self.class.post(url, {:body => body})
125
+ end
126
+
127
+ def signature(body)
128
+ step1 = Digest::MD5.hexdigest("#{body}#{private_key}")
129
+ Digest::MD5.hexdigest("#{step1}#{private_key}")
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,30 @@
1
+ module Registrar
2
+ module Provider
3
+ # :nodoc:
4
+ class OpenSRS
5
+ class Contact
6
+ attr_reader :contact
7
+
8
+ def initialize(contact)
9
+ raise ArgumentError, "Contact is required" unless contact
10
+ @contact = contact
11
+ end
12
+
13
+ def to_xml(builder)
14
+ builder.item(contact.first_name, :key => "first_name")
15
+ builder.item(contact.last_name, :key => "last_name")
16
+ builder.item(contact.phone, :key => "phone")
17
+ builder.item(contact.fax, :key => "fax")
18
+ builder.item(contact.email, :key => "email")
19
+ builder.item(contact.organization_name, :key => "org_name")
20
+ builder.item(contact.address_1, :key => "address1")
21
+ builder.item(contact.address_2, :key => "address2")
22
+ builder.item(contact.city, :key => "city")
23
+ builder.item(contact.state_province, :key => "state")
24
+ builder.item(contact.country, :key => "country")
25
+ builder.item(contact.postal_code, :key => "postal_code")
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ module Registrar
2
+ module Provider
3
+ class OpenSRS
4
+ class ContactSet
5
+ attr_reader :contacts
6
+ def initialize(contacts)
7
+ @contacts = contacts
8
+ end
9
+
10
+ def to_xml(builder)
11
+ builder.dt_assoc { |b|
12
+ contacts.each do |key, contact|
13
+ b.item(:key => key.to_s) { |b|
14
+ b.dt_assoc { |b|
15
+ contact.to_xml(b)
16
+ }
17
+ }
18
+ end
19
+ }
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end