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 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