registrar-client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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