registrar-client 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +19 -0
- data/Readme.md +70 -0
- data/Spec.md +55 -0
- data/examples/enom.example.yml +4 -0
- data/examples/purchase_com.rb +7 -0
- data/lib/registrar-client.rb +1 -0
- data/lib/registrar.rb +14 -0
- data/lib/registrar/client.rb +224 -0
- data/lib/registrar/contact.rb +31 -0
- data/lib/registrar/domain.rb +26 -0
- data/lib/registrar/extended_attribute.rb +12 -0
- data/lib/registrar/extended_attribute_descriptor.rb +31 -0
- data/lib/registrar/extended_attribute_option_descriptor.rb +7 -0
- data/lib/registrar/name_server.rb +16 -0
- data/lib/registrar/order.rb +43 -0
- data/lib/registrar/provider/enom.rb +430 -0
- data/lib/registrar/provider/enom/contact.rb +68 -0
- data/lib/registrar/provider/enom/extended_attribute.rb +64 -0
- data/lib/registrar/provider/enom/extended_attribute_be.rb +0 -0
- data/lib/registrar/provider/enom/extended_attribute_ca.rb +40 -0
- data/lib/registrar/provider/enom/extended_attribute_io.rb +18 -0
- data/lib/registrar/provider/enom/extended_attribute_us.rb +29 -0
- data/lib/registrar/provider/enom/order.rb +41 -0
- data/lib/registrar/provider/opensrs.rb +133 -0
- data/lib/registrar/provider/opensrs/contact.rb +30 -0
- data/lib/registrar/provider/opensrs/contact_set.rb +24 -0
- data/lib/registrar/provider/opensrs/name_server_list.rb +26 -0
- data/lib/registrar/provider/opensrs/operation.rb +42 -0
- data/lib/registrar/provider/opensrs/order.rb +59 -0
- data/lib/registrar/provider/opensrs/tld_data.rb +29 -0
- data/lib/registrar/provider/opensrs/tld_data_us.rb +48 -0
- data/lib/registrar/purchase_options.rb +29 -0
- data/lib/registrar/renewal_options.rb +8 -0
- 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
|
File without changes
|
@@ -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,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
|