dnsimple-ruby 1.5.5 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e029f4837e98c345acfa65282217bb0b1604ab06
4
- data.tar.gz: b515b8beda633b915cbd75fdea793eb080774235
3
+ metadata.gz: c40f147d4747a607ca8ae3cfb8bb1f99830513d9
4
+ data.tar.gz: 5d72dcfc3d7332887eec729b7d9122b51cc3fafb
5
5
  SHA512:
6
- metadata.gz: 7466fd29829d5f2c2f7b79d1ecf8dbf5b11d79c21c24f491e71c858f2e6a9ea37d740e0bdd1d585795bda544d47750f69544df3613c7aceb81d2dfdf3b502c0f
7
- data.tar.gz: 82639249a8b399a00a545e0fe63f336ffe0c4ee36ebbf61a4c6a8711c63a37eba9ec5ff0778d7bf12b25d378828eefddb12726b9d97e1eca8daeaa2a442337fb
6
+ metadata.gz: 268b16e76b613c51ba2da2f51c5955d54f3ca4437acfef0232a5b4718d69b805420a5dc9851ca4d72fff1e594c3c3c4d91abab15aea138ffbbafdab9c37e34be
7
+ data.tar.gz: eb197c1b3b13de5a44075bba53585a2ed2c9cfb884cb63ba916b0cb7f52e14ed41aec1d1022b28a0c8c565ff5ff0178760fbe2102ab71934bbc39315c3e09538
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ #### Release 1.6.0
4
+
5
+ - NEW: Add support for 2FA (GH-44)
6
+
3
7
  #### Release 1.5.5
4
8
 
5
9
  - NEW: Add notice about the CLI moving to a new location
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://githuv.com/aetrion/dnsimple-cli
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/
@@ -4,39 +4,18 @@ require 'yaml'
4
4
  module DNSimple
5
5
  class Client
6
6
 
7
- DEFAULT_BASE_URI = "https://api.dnsimple.com/"
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
- def self.debug?
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 ||= credentials['username']
76
- self.password ||= credentials['password']
77
- self.api_token ||= credentials['api_token']
78
- self.base_uri = credentials['site'] if credentials['site']
79
- self.base_uri = credentials['base_uri'] if credentials['base_uri']
80
- self.http_proxy = { :addr => credentials['proxy_addr'], :port => credentials['proxy_port'] } if credentials['proxy_addr'] || credentials['proxy_port']
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 => self.http_proxy[:addr],
102
- :http_proxyport => self.http_proxy[:port]
77
+ :http_proxyaddr => http_proxy[:addr],
78
+ :http_proxyport => http_proxy[:port]
103
79
  )
104
80
  end
105
81
 
106
- if password
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]['X-DNSimple-Token'] = "#{username}:#{api_token}"
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 AuthenticationFailed
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
@@ -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 AuthenticationFailed < Error
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
@@ -1,3 +1,3 @@
1
1
  module DNSimple
2
- VERSION = '1.5.5'
2
+ VERSION = '1.6.0'
3
3
  end
@@ -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: 'example.com', auto_renew: false) }
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: 'example.com', auto_renew: true) }
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: 'example.com', auto_renew: false) }
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: 'example.com', auto_renew: true) }
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: 'example.com', auto_renew: false) }
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: 'example.com', auto_renew: true) }
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]).
@@ -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.5.5
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-05 00:00:00.000000000 Z
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