dnsimple-ruby 1.5.5 → 1.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +65 -31
- data/bin/dnsimple.rb +1 -1
- data/lib/dnsimple/client.rb +27 -47
- data/lib/dnsimple/error.rb +12 -1
- data/lib/dnsimple/user.rb +25 -0
- data/lib/dnsimple/version.rb +1 -1
- data/spec/dnsimple/client_spec.rb +41 -5
- data/spec/dnsimple/domain_spec.rb +6 -6
- data/spec/dnsimple/user_spec.rb +39 -0
- data/spec/files/2fa/error-badtoken.http +22 -0
- data/spec/files/2fa/error-required.http +23 -0
- data/spec/files/2fa/exchange-token.http +25 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c40f147d4747a607ca8ae3cfb8bb1f99830513d9
|
4
|
+
data.tar.gz: 5d72dcfc3d7332887eec729b7d9122b51cc3fafb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 268b16e76b613c51ba2da2f51c5955d54f3ca4437acfef0232a5b4718d69b805420a5dc9851ca4d72fff1e594c3c3c4d91abab15aea138ffbbafdab9c37e34be
|
7
|
+
data.tar.gz: eb197c1b3b13de5a44075bba53585a2ed2c9cfb884cb63ba916b0cb7f52e14ed41aec1d1022b28a0c8c565ff5ff0178760fbe2102ab71934bbc39315c3e09538
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -11,6 +11,71 @@ your domain registered and set up with a minimal amount of effort.
|
|
11
11
|
|
12
12
|
$ gem install dnsimple-ruby
|
13
13
|
|
14
|
+
## DNSimple Client
|
15
|
+
|
16
|
+
This library provides a Ruby DNSimple client you can use to interact with the [DNSimple API](http://developer.dnsimple.com/). Here's a short example.
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
require 'dnsimple'
|
20
|
+
|
21
|
+
DNSimple::Client.username = 'YOUR_USERNAME'
|
22
|
+
DNSimple::Client.password = 'YOUR_PASSWORD'
|
23
|
+
|
24
|
+
user = DNSimple::User.me
|
25
|
+
puts "#{user.domain_count} domains"
|
26
|
+
|
27
|
+
puts "Domains..."
|
28
|
+
DNSimple::Domain.all.each do |domain|
|
29
|
+
puts " #{domain.name}"
|
30
|
+
end
|
31
|
+
|
32
|
+
domain = DNSimple::Domain.find("example.com")
|
33
|
+
domain.apply("template") # applies a standard or custom template to the domain
|
34
|
+
|
35
|
+
domain = DNSimple::Domain.create("newdomain.com")
|
36
|
+
puts "Added #{domain.name}"
|
37
|
+
domain.delete # removes from DNSimple
|
38
|
+
```
|
39
|
+
|
40
|
+
For the full API documentation visit http://rubydoc.info/gems/dnsimple-ruby
|
41
|
+
|
42
|
+
### Authentication
|
43
|
+
|
44
|
+
This client supports both the HTTP Basic and API Token authentication mechanism.
|
45
|
+
|
46
|
+
#### HTTP Basic
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
DNSimple::Client.username = 'YOUR_USERNAME'
|
50
|
+
DNSimple::Client.password = 'YOUR_PASSWORD'
|
51
|
+
|
52
|
+
user = DNSimple::User.me
|
53
|
+
```
|
54
|
+
|
55
|
+
#### HTTP Basic with two-factor authentication enabled
|
56
|
+
|
57
|
+
See the [2FA API documentation](http://developer.dnsimple.com/authentication/#twofa).
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
# Request the 2FA exchange token
|
61
|
+
DNSimple::Client.username = 'YOUR_USERNAME'
|
62
|
+
DNSimple::Client.password = 'YOUR_PASSWORD'
|
63
|
+
token = DNSimple::User.two_factor_exchange_token('otp-token')
|
64
|
+
|
65
|
+
# Authenticate with the exchange token
|
66
|
+
DNSimple::Client.exchange_token = token
|
67
|
+
user = DNSimple::User.me
|
68
|
+
```
|
69
|
+
|
70
|
+
#### API Token
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
DNSimple::Client.api_token = 'the-token'
|
74
|
+
|
75
|
+
user = DNSimple::User.me
|
76
|
+
```
|
77
|
+
|
78
|
+
|
14
79
|
## Credentials
|
15
80
|
|
16
81
|
Create a file in your home directory called `.dnsimple`.
|
@@ -91,34 +156,3 @@ The contact name/value pairs are:
|
|
91
156
|
- phone
|
92
157
|
- phone_ext (optional)
|
93
158
|
- fax (optional)
|
94
|
-
|
95
|
-
## Wrapper Classes
|
96
|
-
|
97
|
-
In addition to the command line utility you may also use the included Ruby
|
98
|
-
classes directly in your Ruby applications.
|
99
|
-
|
100
|
-
Here's a short example.
|
101
|
-
|
102
|
-
require 'rubygems'
|
103
|
-
require 'dnsimple'
|
104
|
-
|
105
|
-
DNSimple::Client.username = 'YOUR_USERNAME'
|
106
|
-
DNSimple::Client.password = 'YOUR_PASSWORD'
|
107
|
-
DNSimple::Client.http_proxy = {}
|
108
|
-
|
109
|
-
user = DNSimple::User.me
|
110
|
-
puts "#{user.domain_count} domains"
|
111
|
-
|
112
|
-
puts "Domains..."
|
113
|
-
DNSimple::Domain.all.each do |domain|
|
114
|
-
puts " #{domain.name}"
|
115
|
-
end
|
116
|
-
|
117
|
-
domain = DNSimple::Domain.find("example.com")
|
118
|
-
domain.apply("template") # applies a standard or custom template to the domain
|
119
|
-
|
120
|
-
domain = DNSimple::Domain.create("newdomain.com")
|
121
|
-
puts "Added #{domain.name}"
|
122
|
-
domain.delete # removes from DNSimple
|
123
|
-
|
124
|
-
For the full API documentation visit http://rubydoc.info/gems/dnsimple-ruby
|
data/bin/dnsimple.rb
CHANGED
@@ -12,7 +12,7 @@ def usage
|
|
12
12
|
$stderr.puts <<EOF
|
13
13
|
|
14
14
|
NOTICE: The CLI is moving out into its own repository and its own gem! Visit
|
15
|
-
https://
|
15
|
+
https://github.com/aetrion/dnsimple-cli
|
16
16
|
|
17
17
|
This is a command line tool for DNSimple. More information about DNSimple can
|
18
18
|
be found at http://dnsimple.com/
|
data/lib/dnsimple/client.rb
CHANGED
@@ -4,39 +4,18 @@ require 'yaml'
|
|
4
4
|
module DNSimple
|
5
5
|
class Client
|
6
6
|
|
7
|
-
DEFAULT_BASE_URI
|
7
|
+
DEFAULT_BASE_URI = "https://api.dnsimple.com/"
|
8
|
+
HEADER_2FA_STRICT = "X-DNSimple-2FA-Strict"
|
9
|
+
HEADER_API_TOKEN = "X-DNSimple-Token"
|
10
|
+
HEADER_OTP_TOKEN = "X-DNSimple-OTP"
|
11
|
+
HEADER_EXCHANGE_TOKEN = "X-DNSimple-OTP-Token"
|
8
12
|
|
13
|
+
class << self
|
14
|
+
# @return [Boolean] if the debug mode is enabled.
|
15
|
+
# Defaults to false.
|
16
|
+
attr_accessor :debug
|
9
17
|
|
10
|
-
|
11
|
-
@debug
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.debug=(debug)
|
15
|
-
@debug = debug
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.username
|
19
|
-
@username
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.username=(username)
|
23
|
-
@username = username
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.password
|
27
|
-
@password
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.password=(password)
|
31
|
-
@password = password
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.api_token
|
35
|
-
@api_token
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.api_token=(api_token)
|
39
|
-
@api_token = api_token
|
18
|
+
attr_accessor :username, :password, :exchange_token, :api_token
|
40
19
|
end
|
41
20
|
|
42
21
|
# Gets the qualified API base uri.
|
@@ -57,10 +36,6 @@ module DNSimple
|
|
57
36
|
@http_proxy
|
58
37
|
end
|
59
38
|
|
60
|
-
def self.http_proxy=(http_proxy)
|
61
|
-
@http_proxy = http_proxy
|
62
|
-
end
|
63
|
-
|
64
39
|
def self.load_credentials_if_necessary
|
65
40
|
load_credentials unless credentials_loaded?
|
66
41
|
end
|
@@ -72,12 +47,13 @@ module DNSimple
|
|
72
47
|
def self.load_credentials(path = config_path)
|
73
48
|
begin
|
74
49
|
credentials = YAML.load_file(File.expand_path(path))
|
75
|
-
self.username
|
76
|
-
self.password
|
77
|
-
self.
|
78
|
-
self.
|
79
|
-
self.base_uri
|
80
|
-
self.
|
50
|
+
self.username = credentials['username']
|
51
|
+
self.password = credentials['password']
|
52
|
+
self.exchange_token = credentials['exchange_token']
|
53
|
+
self.api_token = credentials['api_token']
|
54
|
+
self.base_uri = credentials['site'] if credentials['site']
|
55
|
+
self.base_uri = credentials['base_uri'] if credentials['base_uri']
|
56
|
+
@http_proxy = { :addr => credentials['proxy_addr'], :port => credentials['proxy_port'] } if credentials['proxy_addr'] || credentials['proxy_port']
|
81
57
|
@credentials_loaded = true
|
82
58
|
puts "Credentials loaded from #{path}"
|
83
59
|
rescue => error
|
@@ -98,15 +74,17 @@ module DNSimple
|
|
98
74
|
|
99
75
|
if http_proxy
|
100
76
|
options.merge!(
|
101
|
-
:http_proxyaddr =>
|
102
|
-
:http_proxyport =>
|
77
|
+
:http_proxyaddr => http_proxy[:addr],
|
78
|
+
:http_proxyport => http_proxy[:port]
|
103
79
|
)
|
104
80
|
end
|
105
81
|
|
106
|
-
if
|
82
|
+
if exchange_token
|
83
|
+
options[:basic_auth] = { :username => exchange_token, :password => "x-2fa-basic" }
|
84
|
+
elsif password
|
107
85
|
options[:basic_auth] = { :username => username, :password => password }
|
108
86
|
elsif api_token
|
109
|
-
options[:headers][
|
87
|
+
options[:headers][HEADER_API_TOKEN] = "#{username}:#{api_token}"
|
110
88
|
else
|
111
89
|
raise Error, 'A password or API token is required for all API requests.'
|
112
90
|
end
|
@@ -133,8 +111,10 @@ module DNSimple
|
|
133
111
|
def self.request(method, path, options)
|
134
112
|
response = HTTParty.send(method, "#{base_uri}#{path}", base_options.merge(options))
|
135
113
|
|
136
|
-
if response.code == 401
|
137
|
-
raise
|
114
|
+
if response.code == 401 && response.headers[HEADER_OTP_TOKEN] == "required"
|
115
|
+
raise TwoFactorAuthenticationRequired, response["message"]
|
116
|
+
elsif response.code == 401
|
117
|
+
raise AuthenticationFailed, response["message"]
|
138
118
|
end
|
139
119
|
|
140
120
|
response
|
data/lib/dnsimple/error.rb
CHANGED
@@ -9,13 +9,24 @@ module DNSimple
|
|
9
9
|
class RecordNotFound < Error
|
10
10
|
end
|
11
11
|
|
12
|
+
# An exception that is raised if a method is called with missing or invalid parameter values.
|
13
|
+
class ValidationError < Error
|
14
|
+
end
|
15
|
+
|
12
16
|
class RequestError < Error
|
13
17
|
def initialize(description, response)
|
14
18
|
super("#{description}: #{response["error"]}")
|
15
19
|
end
|
16
20
|
end
|
17
21
|
|
18
|
-
class
|
22
|
+
class AuthenticationError < Error
|
23
|
+
end
|
24
|
+
|
25
|
+
class AuthenticationFailed < AuthenticationError
|
26
|
+
end
|
27
|
+
|
28
|
+
# An exception that is raised if a request is executed for an account that requires two-factor authentication.
|
29
|
+
class TwoFactorAuthenticationRequired < AuthenticationError
|
19
30
|
end
|
20
31
|
|
21
32
|
end
|
data/lib/dnsimple/user.rb
CHANGED
@@ -11,6 +11,10 @@ module DNSimple
|
|
11
11
|
attr_accessor :updated_at
|
12
12
|
|
13
13
|
|
14
|
+
# Gets the information about the authenticated user.
|
15
|
+
#
|
16
|
+
# @return [User] the authenticated user
|
17
|
+
# @raise [RequestError] if the user doesn't exist
|
14
18
|
def self.me
|
15
19
|
response = DNSimple::Client.get("/v1/user")
|
16
20
|
|
@@ -22,5 +26,26 @@ module DNSimple
|
|
22
26
|
end
|
23
27
|
end
|
24
28
|
|
29
|
+
# Requests a new two-factor authentication exchange token.
|
30
|
+
#
|
31
|
+
# The exchange-token is required to validate API requests
|
32
|
+
# using HTTP Basic Authentication when the account has two-factor authentication enabled.
|
33
|
+
#
|
34
|
+
# @see http://developer.dnsimple.com/authentication/#twofa
|
35
|
+
#
|
36
|
+
# @example Request an Exchange Token
|
37
|
+
#
|
38
|
+
# DNSimple::User.two_factor_exchange_token('0000000')
|
39
|
+
# # => "cda038832591e34f5df642ce2b61dc78"
|
40
|
+
#
|
41
|
+
# @param [String] otp_token the two-factor one time (OTP) token
|
42
|
+
#
|
43
|
+
# @return [String] the two-factor API exchange token
|
44
|
+
# @raise [AuthenticationFailed] if the provided OTP token is invalid
|
45
|
+
def self.two_factor_exchange_token(otp_token)
|
46
|
+
response = DNSimple::Client.get("/v1/user", :headers => { DNSimple::Client::HEADER_2FA_STRICT => "1", DNSimple::Client::HEADER_OTP_TOKEN => otp_token })
|
47
|
+
response.headers[DNSimple::Client::HEADER_EXCHANGE_TOKEN]
|
48
|
+
end
|
49
|
+
|
25
50
|
end
|
26
51
|
end
|
data/lib/dnsimple/version.rb
CHANGED
@@ -29,10 +29,15 @@ describe DNSimple::Client do
|
|
29
29
|
end
|
30
30
|
|
31
31
|
describe ".request" do
|
32
|
+
before do
|
33
|
+
[:username, :password, :exchange_token, :api_token].each do |attribute|
|
34
|
+
described_class.send("#{attribute}=", nil)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
32
38
|
it "uses HTTP authentication if there's a password provided" do
|
33
39
|
described_class.username = 'user'
|
34
40
|
described_class.password = 'pass'
|
35
|
-
described_class.api_token = nil
|
36
41
|
described_class.base_uri = 'https://api.example.com/'
|
37
42
|
|
38
43
|
HTTParty.expects(:get).
|
@@ -44,7 +49,6 @@ describe DNSimple::Client do
|
|
44
49
|
|
45
50
|
it "uses header authentication if there's an api token provided" do
|
46
51
|
described_class.username = 'user'
|
47
|
-
described_class.password = nil
|
48
52
|
described_class.api_token = 'token'
|
49
53
|
described_class.base_uri = 'https://api.example.com/'
|
50
54
|
|
@@ -55,10 +59,21 @@ describe DNSimple::Client do
|
|
55
59
|
described_class.request(:get, '/domains', {})
|
56
60
|
end
|
57
61
|
|
62
|
+
it "uses HTTP authentication via exchange token if there's an exchange token provided" do
|
63
|
+
described_class.username = 'user'
|
64
|
+
described_class.password = 'pass'
|
65
|
+
described_class.exchange_token = 'exchange-token'
|
66
|
+
described_class.base_uri = 'https://api.example.com/'
|
67
|
+
|
68
|
+
HTTParty.expects(:get).
|
69
|
+
with('https://api.example.com/domains', has_entries(:basic_auth => { :username => 'exchange-token', :password => 'x-2fa-basic' })).
|
70
|
+
returns(response)
|
71
|
+
|
72
|
+
described_class.request(:get, '/domains', {})
|
73
|
+
end
|
74
|
+
|
58
75
|
it "raises an error if there's no password or api token provided" do
|
59
76
|
described_class.username = 'user'
|
60
|
-
described_class.password = nil
|
61
|
-
described_class.api_token = nil
|
62
77
|
described_class.base_uri = 'https://api.example.com/'
|
63
78
|
|
64
79
|
expect {
|
@@ -67,6 +82,8 @@ describe DNSimple::Client do
|
|
67
82
|
end
|
68
83
|
|
69
84
|
it "adds a custom user-agent" do
|
85
|
+
described_class.api_token = 'token'
|
86
|
+
|
70
87
|
HTTParty.expects(:get).
|
71
88
|
with(is_a(String), has_entries(:headers => has_entries({ 'User-Agent' => "dnsimple-ruby/#{DNSimple::VERSION}" }))).
|
72
89
|
returns(response)
|
@@ -75,12 +92,15 @@ describe DNSimple::Client do
|
|
75
92
|
end
|
76
93
|
|
77
94
|
it "performs a request" do
|
95
|
+
described_class.username = 'user'
|
96
|
+
described_class.password = 'pass'
|
97
|
+
|
78
98
|
HTTParty.expects(:get).
|
79
99
|
with("#{described_class.base_uri}/foo",
|
80
100
|
:format => :json,
|
81
101
|
:basic_auth => { :username => described_class.username, :password => described_class.password },
|
82
102
|
:headers => { 'Accept' => 'application/json', 'User-Agent' => "dnsimple-ruby/#{DNSimple::VERSION}" }
|
83
|
-
|
103
|
+
).
|
84
104
|
returns(response)
|
85
105
|
|
86
106
|
described_class.request(:get, '/foo', {})
|
@@ -104,4 +124,20 @@ describe DNSimple::Client do
|
|
104
124
|
end
|
105
125
|
end
|
106
126
|
|
127
|
+
|
128
|
+
describe "authentication" do
|
129
|
+
context "when 2FA is required" do
|
130
|
+
before do
|
131
|
+
stub_request(:get, %r[/foo]).
|
132
|
+
to_return(read_fixture("2fa/error-required.http"))
|
133
|
+
end
|
134
|
+
|
135
|
+
it "raises a TwoFactorAuthenticationRequired error" do
|
136
|
+
expect {
|
137
|
+
described_class.get('/foo', {})
|
138
|
+
}.to raise_error(DNSimple::TwoFactorAuthenticationRequired)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
107
143
|
end
|
@@ -40,7 +40,7 @@ describe DNSimple::Domain do
|
|
40
40
|
end
|
41
41
|
|
42
42
|
describe "#enable_auto_renew" do
|
43
|
-
let(:domain) { described_class.new(name
|
43
|
+
let(:domain) { described_class.new(:name => 'example.com', :auto_renew => false) }
|
44
44
|
|
45
45
|
context "when response is not 200" do
|
46
46
|
before do
|
@@ -54,7 +54,7 @@ describe DNSimple::Domain do
|
|
54
54
|
end
|
55
55
|
|
56
56
|
context "when auto_renew is true" do
|
57
|
-
let(:domain) { described_class.new(name
|
57
|
+
let(:domain) { described_class.new(:name => 'example.com', :auto_renew => true) }
|
58
58
|
|
59
59
|
it "does not send a web request" do
|
60
60
|
domain.enable_auto_renew
|
@@ -63,7 +63,7 @@ describe DNSimple::Domain do
|
|
63
63
|
end
|
64
64
|
|
65
65
|
context "when auto_renew is false" do
|
66
|
-
let(:domain) { described_class.new(name
|
66
|
+
let(:domain) { described_class.new(:name => 'example.com', :auto_renew => false) }
|
67
67
|
|
68
68
|
before do
|
69
69
|
stub_request(:post, %r[/v1/domains/example.com/auto_renewal]).
|
@@ -86,7 +86,7 @@ describe DNSimple::Domain do
|
|
86
86
|
end
|
87
87
|
|
88
88
|
describe "#disable_auto_renew" do
|
89
|
-
let(:domain) { described_class.new(name
|
89
|
+
let(:domain) { described_class.new(:name => 'example.com', :auto_renew => true) }
|
90
90
|
|
91
91
|
context "when response is not 200" do
|
92
92
|
before do
|
@@ -100,7 +100,7 @@ describe DNSimple::Domain do
|
|
100
100
|
end
|
101
101
|
|
102
102
|
context "when auto_renew is false" do
|
103
|
-
let(:domain) { described_class.new(name
|
103
|
+
let(:domain) { described_class.new(:name => 'example.com', :auto_renew => false) }
|
104
104
|
|
105
105
|
it "does not send a web request" do
|
106
106
|
domain.disable_auto_renew
|
@@ -109,7 +109,7 @@ describe DNSimple::Domain do
|
|
109
109
|
end
|
110
110
|
|
111
111
|
context "when auto_renew is true" do
|
112
|
-
let(:domain) { described_class.new(name
|
112
|
+
let(:domain) { described_class.new(:name => 'example.com', :auto_renew => true) }
|
113
113
|
|
114
114
|
before do
|
115
115
|
stub_request(:delete, %r[/v1/domains/example.com/auto_renewal]).
|
data/spec/dnsimple/user_spec.rb
CHANGED
@@ -28,4 +28,43 @@ describe DNSimple::User do
|
|
28
28
|
expect(result.updated_at).to be_a(String)
|
29
29
|
end
|
30
30
|
end
|
31
|
+
|
32
|
+
describe ".two_factor_exchange_token" do
|
33
|
+
before do
|
34
|
+
stub_request(:get, %r[/v1/user]).
|
35
|
+
to_return(read_fixture("2fa/exchange-token.http"))
|
36
|
+
end
|
37
|
+
|
38
|
+
let(:otp_token) { '1234567' }
|
39
|
+
|
40
|
+
it "builds the correct request" do
|
41
|
+
described_class.two_factor_exchange_token(otp_token)
|
42
|
+
|
43
|
+
expect(WebMock).to have_requested(:get, "https://#{CONFIG['username']}:#{CONFIG['password']}@#{CONFIG['host']}/v1/user").
|
44
|
+
# workaround for https://github.com/bblimke/webmock/issues/276
|
45
|
+
with do |req|
|
46
|
+
req.headers['Accept'] == 'application/json' &&
|
47
|
+
req.headers[DNSimple::CLient::HEADER_OTP_TOKEN] == otp_token
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it "returns the exchange_token" do
|
52
|
+
result = described_class.two_factor_exchange_token(otp_token)
|
53
|
+
|
54
|
+
expect(result).to eq("0c622716aaa64124219963075bc1c870")
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when the OTP token is invalid" do
|
58
|
+
before do
|
59
|
+
stub_request(:get, %r[/v1/user]).
|
60
|
+
to_return(read_fixture("2fa/error-badtoken.http"))
|
61
|
+
end
|
62
|
+
|
63
|
+
it "raises an AuthenticationFailed" do
|
64
|
+
expect {
|
65
|
+
described_class.two_factor_exchange_token("invalid-token")
|
66
|
+
}.to raise_error(DNSimple::AuthenticationFailed, "Bad OTP token")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
31
70
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
HTTP/1.1 401 Unauthorized
|
2
|
+
Server: nginx
|
3
|
+
Date: Fri, 19 Sep 2014 10:20:23 GMT
|
4
|
+
Content-Type: application/json; charset=utf-8
|
5
|
+
Transfer-Encoding: chunked
|
6
|
+
Connection: close
|
7
|
+
Status: 401 Unauthorized
|
8
|
+
Strict-Transport-Security: max-age=631138519
|
9
|
+
X-Frame-Options: SAMEORIGIN
|
10
|
+
X-XSS-Protection: 1
|
11
|
+
X-Content-Type-Options: nosniff
|
12
|
+
X-UA-Compatible: chrome=1
|
13
|
+
Access-Control-Allow-Origin: *
|
14
|
+
Access-Control-Allow-Headers: Authorization,Accepts,Content-Type,X-DNSimple-Token,X-DNSimple-Domain-Token,X-CSRF-Token,x-requested-with
|
15
|
+
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
|
16
|
+
Cache-Control: no-cache
|
17
|
+
Set-Cookie: user_credentials=f53d51039d1ac35e3398b604cb8eb8b8ecc085b69e76e6a8b41bf9b758380b97be2c65ba3124b1d5f8fc9dbeef9690386bdc5ccd775f0494a1a5a1edb5d98521; path=/; expires=Sat, 19 Sep 2015 10:20:23 -0000; secure
|
18
|
+
Set-Cookie: _dnsimple_session=MEM4ak83V0lDeVV6YnA1L2JPbG90cDExelZ6REF6dTNMR0NLejN1VG85WkphdU9FUDBvRmxUSnhlTGxGdmhOOXhiamtBQW1SYU9NSU9KUlV5SG5lTzlDS2N0dCtuay9mem5vL2ZxUzdFL2VpVDNMS0dwOVQxdWc3QmtYcWpLQXQ1NDE3UmpUeEZiMmwwTVlxdmJZQVU3UDYrbmJ3RnhDODBnZE5MWVJKekY5MFdoN2h0UVFQUmZzY204N1pTS1VpV2FZNWdZVlNENDJ3cThyZUx3U2l2NFN2eWx1UEZSVkNtTXdTUitxZG04VDBmZzErTmtvN1BPMmlRVGdiTW5mTXdVR0Jlb2V1RWxaS0xlU3NpNFlrUzdERDRCNzRRTnFLZ2Q1bS95WXM4M3M9LS1FdGpHYWlKRUlyVmhHYXZic3JIM3lnPT0%3D--8081ac131a8acf29688505a8b2a8224d0fd76ce5; path=/; HttpOnly; secure
|
19
|
+
X-Request-Id: 0567a347-9ea0-46cf-a73b-eb5b60bbe8bb
|
20
|
+
X-Runtime: 0.214109
|
21
|
+
|
22
|
+
{"message":"Bad OTP token"}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
HTTP/1.1 401 Unauthorized
|
2
|
+
Server: nginx
|
3
|
+
Date: Fri, 19 Sep 2014 10:33:42 GMT
|
4
|
+
Content-Type: application/json; charset=utf-8
|
5
|
+
Transfer-Encoding: chunked
|
6
|
+
Connection: close
|
7
|
+
Status: 401 Unauthorized
|
8
|
+
Strict-Transport-Security: max-age=631138519
|
9
|
+
X-Frame-Options: SAMEORIGIN
|
10
|
+
X-XSS-Protection: 1
|
11
|
+
X-Content-Type-Options: nosniff
|
12
|
+
X-UA-Compatible: chrome=1
|
13
|
+
Access-Control-Allow-Origin: *
|
14
|
+
Access-Control-Allow-Headers: Authorization,Accepts,Content-Type,X-DNSimple-Token,X-DNSimple-Domain-Token,X-CSRF-Token,x-requested-with
|
15
|
+
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
|
16
|
+
X-DNSimple-OTP: required
|
17
|
+
Cache-Control: no-cache
|
18
|
+
Set-Cookie: user_credentials=4ef7ff0d7228ea953f77be4044941c0f7e9c5e308e3b2c778dd175b0dd0d95eb032cef4334c6c3a7023e0e4754e2aea3133f44aa663d760f78997d97bde9336f; path=/; expires=Sat, 19 Sep 2015 10:33:42 -0000; secure
|
19
|
+
Set-Cookie: _dnsimple_session=TXFabkRHZlZyaWQvOEVMc09PU3RCckR4cVBDeXZrczRwOStMbkJiZnRjMWIzV3R1NzdubktHdU1pNlEyVVJ4SDNlODBNcUo0VzN5VmhsdWxTU1dwUUhXbXJhNk1tOUNpWUNUdlZ3NFVJd2cydVFWdFJGT0s4OExZd1NNS0lEeVdYSW0venA1WFpjVnhYN0NzNUlEdFJBS2NnSmY0ZHFJaG5UTitST2prTXBrVmpBYUlFNEkzZVUxeU1kVGN4aXlMMHhRMFI1MlRVNSs2SXpLd0Uyb3VleG5KODh1TEJvdG5rN2tXWGtjeEIxM3AzWmRrc052TmRsdEcvLzdwZ1hvK014OXpKM1N2YkNmaVppbDdMWWRNOXE4cERuU2l4c3lwWmNOYjgxVXlOOUE9LS15aXY1ekg4WTZlMDRtTGIyZ0JFanJRPT0%3D--8c7111719b56998a6343d0b8dc68d5607d6c05bf; path=/; HttpOnly; secure
|
20
|
+
X-Request-Id: 58e56818-4804-4452-b255-0650552a24cc
|
21
|
+
X-Runtime: 0.212325
|
22
|
+
|
23
|
+
{"message":"2FA Authentication failed"}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Server: nginx
|
3
|
+
Date: Fri, 19 Sep 2014 10:59:16 GMT
|
4
|
+
Content-Type: application/json; charset=utf-8
|
5
|
+
Transfer-Encoding: chunked
|
6
|
+
Connection: close
|
7
|
+
Status: 200 OK
|
8
|
+
Strict-Transport-Security: max-age=631138519
|
9
|
+
X-Frame-Options: SAMEORIGIN
|
10
|
+
X-XSS-Protection: 1
|
11
|
+
X-Content-Type-Options: nosniff
|
12
|
+
X-UA-Compatible: chrome=1
|
13
|
+
Access-Control-Allow-Origin: *
|
14
|
+
Access-Control-Allow-Headers: Authorization,Accepts,Content-Type,X-DNSimple-Token,X-DNSimple-Domain-Token,X-CSRF-Token,x-requested-with
|
15
|
+
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
|
16
|
+
X-DNSimple-OTP-Token: 0c622716aaa64124219963075bc1c870
|
17
|
+
ETag: "8f991ac7debfbe00277cb74b6d41c9a4"
|
18
|
+
Cache-Control: max-age=0, private, must-revalidate
|
19
|
+
Set-Cookie: user_credentials=5a9961782864b9d0601fedf748aaafaa009a8468d6864b5e648b65b0a4c05547d17a07bf162c9610efb422824633563a74e2e2bf58a0d4627bb5ddcb7fe07014; path=/; expires=Sat, 19 Sep 2015 10:59:16 -0000; secure
|
20
|
+
Set-Cookie: _dnsimple_session=OG0rMmJZSHVZWDNuMXd5Rjk4SGxPMU8zRjlrbWkydW44ek00bXRDSkhpUTA3ZkZzUWVsenlXMjcyWk93ZFhCRjZXejdpSTNrZWxqdE9DUFVqckg0YU13cTNFcVJVTnU1VTVTYjRZNHRWdGV3QmpXYlFEUlhRVDRrZHFPZXovMU9pUmhYTG16UXBTbjRoeWtBUVJudytUVmpaU2NFdDFDQlE2QTRFRXBwSEI2V1lCRnJ3Zmx3RUVYWVM0THN1WlIySkJtTkYwRG5CcC9CK2RzeXpMU2hxcWpMTld5OThPSWxmSHpWNXdEUFRFWnFHRktZSktlZkRGNEdIS2llN25aZkhxWTh4Q0w2UUREemNYTDNIMm1yLzQvTU5WUk1RSTgwTE1la3A2Vk1jeXc9LS03dml0dCtLWUx5MFRLa3phRWxNRmt3PT0%3D--6cbcdf7e930eb4343b632553396dfb6ee0ee04e6; path=/; HttpOnly; secure
|
21
|
+
X-Request-Id: 7e658b27-a46f-42bc-acb7-56fc94a5a623
|
22
|
+
X-Runtime: 0.437686
|
23
|
+
Strict-Transport-Security: max-age=315360000
|
24
|
+
|
25
|
+
{"user":{"id":19,"email":"example@example.com","referral_token":"ad932ffb60c295","single_access_token":"hZ1P7jz1V0u6g0MMv0pL","domain_count":2,"domain_limit":10,"login_count":1,"failed_login_count":0,"created_at":"2014-01-15T21:59:04.104Z","updated_at":"2014-09-19T10:59:16.231Z","first_name":null,"last_name":null,"default_contact_id":null}}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dnsimple-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anthony Eden
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|
@@ -233,6 +233,9 @@ files:
|
|
233
233
|
- spec/dnsimple/record_spec.rb
|
234
234
|
- spec/dnsimple/template_spec.rb
|
235
235
|
- spec/dnsimple/user_spec.rb
|
236
|
+
- spec/files/2fa/error-badtoken.http
|
237
|
+
- spec/files/2fa/error-required.http
|
238
|
+
- spec/files/2fa/exchange-token.http
|
236
239
|
- spec/files/account/user/success.http
|
237
240
|
- spec/files/certificates/index/success.http
|
238
241
|
- spec/files/certificates/show/notfound.http
|
@@ -311,6 +314,9 @@ test_files:
|
|
311
314
|
- spec/dnsimple/record_spec.rb
|
312
315
|
- spec/dnsimple/template_spec.rb
|
313
316
|
- spec/dnsimple/user_spec.rb
|
317
|
+
- spec/files/2fa/error-badtoken.http
|
318
|
+
- spec/files/2fa/error-required.http
|
319
|
+
- spec/files/2fa/exchange-token.http
|
314
320
|
- spec/files/account/user/success.http
|
315
321
|
- spec/files/certificates/index/success.http
|
316
322
|
- spec/files/certificates/show/notfound.http
|