Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OAuth authentication #66

Merged
merged 8 commits into from
Dec 16, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 13 additions & 18 deletions lib/dnsimple/client.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
require 'dnsimple/compatibility'
require 'dnsimple/extra'
require 'dnsimple/struct'

Expand All @@ -8,13 +7,11 @@ module Dnsimple
#
# @see http://developer.dnsimple.com
class Client
include Dnsimple::Compatibility

HEADER_2FA_STRICT = "X-DNSimple-2FA-Strict"
HEADER_API_TOKEN = "X-DNSimple-Token"
HEADER_DOMAIN_API_TOKEN = "X-DNSimple-Domain-Token"
HEADER_OTP_TOKEN = "X-DNSimple-OTP"
HEADER_EXCHANGE_TOKEN = "X-DNSimple-OTP-Token"
HEADER_OAUTH_ACCESS_TOKEN = "Authorization"


# @return [String] The current API version.
Expand All @@ -36,22 +33,22 @@ def self.versioned(path)
# @!attribute password
# @see http://developer.dnsimple.com/authentication/
# @return [String] DNSimple password for Basic Authentication
# @!attribute exchange_token
# @see http://developer.dnsimple.com/authentication/
# @return [String] Exchange Token for Basic Authentication with 2FA
# @!attribute api_token
# @see http://developer.dnsimple.com/authentication/
# @return [String] API access token for authentication
# @!attribute domain_api_token
# @see http://developer.dnsimple.com/authentication/
# @return [String] Domain API access token for authentication
# @!attribute oauth_client_id
# @see http://developer.dnsimple.com/authentication/
# @return [String] OAuth client id for authentication
# @!attribute oauth_client_secret
# @see http://developer.dnsimple.com/authentication/
# @return [String] OAuth client secret for authentication
# @!attribute user_agent
# @return [String] Configure User-Agent header for requests.
# @!attribute proxy
# @return [String,nil] Configure address:port values for proxy server

attr_accessor :api_endpoint, :username, :password, :exchange_token, :api_token, :domain_api_token,
:user_agent, :proxy
attr_accessor :api_endpoint, :username, :password, :domain_api_token, :oauth_access_token,
:oauth_client_id, :oauth_client_secret, :user_agent, :proxy


def initialize(options = {})
Expand Down Expand Up @@ -172,16 +169,14 @@ def base_options
options.merge!(http_proxyaddr: address, http_proxyport: port)
end

if exchange_token
options[:basic_auth] = { username: exchange_token, password: "x-2fa-basic" }
elsif password
if password
options[:basic_auth] = { username: username, password: password }
elsif domain_api_token
options[:headers][HEADER_DOMAIN_API_TOKEN] = domain_api_token
elsif api_token
options[:headers][HEADER_API_TOKEN] = "#{username}:#{api_token}"
elsif oauth_access_token
options[:headers][HEADER_OAUTH_ACCESS_TOKEN] = "Bearer #{oauth_access_token}"
else
raise Error, 'A password or API token is required for all API requests.'
raise Error, 'A password, domain API token or OAuth access token is required for all API requests.'
end

options
Expand Down
46 changes: 0 additions & 46 deletions lib/dnsimple/compatibility.rb

This file was deleted.

31 changes: 19 additions & 12 deletions lib/dnsimple/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ def keys
:api_endpoint,
:username,
:password,
:exchange_token,
:api_token,
:domain_api_token,
:oauth_access_token,
:oauth_client_id,
:oauth_client_secret,
:user_agent,
:proxy,
]
Expand Down Expand Up @@ -50,22 +51,28 @@ def password
ENV['DNSIMPLE_PASSWORD']
end

# Default DNSimple Exchange Token for Basic Auth from ENV
# @return [String]
def exchange_token
ENV['DNSIMPLE_API_EXCHANGE_TOKEN']
end

# Default DNSimple Domain API Token for Token Auth from ENV
# @return [String]
def domain_api_token
ENV['DNSIMPLE_API_DOMAIN_TOKEN']
end

# Default DNSimple API Token for Token Auth from ENV
# Default DNSimple OAuth access token for OAuth authentication from ENV
# @return [String]
def oauth_access_token
ENV['DNSIMPLE_OAUTH_ACCESS_TOKEN']
end

# Default DNSimple OAuth client ID for OAuth authentication from ENV
# @return [String]
def oauth_client_id
ENV['DNSIMPLE_OAUTH_CLIENT_ID']
end

# Default DNSimple OAuth client secret for OAuth authentication from ENV
# @return [String]
def api_token
ENV['DNSIMPLE_API_TOKEN']
def oauth_client_secret
ENV['DNSIMPLE_OAUTH_CLIENT_SECRET']
end

# Default User-Agent header string from ENV or {USER_AGENT}
Expand All @@ -83,4 +90,4 @@ def proxy
end
end

end
end
32 changes: 17 additions & 15 deletions spec/dnsimple/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@
expect(subject.api_endpoint).to eq("https://api.example.com/")
end

it "accepts :oauth_client_id and :oauth_client_secret options" do
subject = described_class.new(oauth_client_id: "id", oauth_client_secret: "secret")
expect(subject.oauth_client_id).to eq("id")
expect(subject.oauth_client_secret).to eq("secret")
end

it "access :oauth_access_token option" do
subject = described_class.new(oauth_access_token: "token")
expect(subject.oauth_access_token).to eq("token")
end

it "normalizes :api_endpoint trailing slash" do
subject = described_class.new(api_endpoint: "https://api.example.com/missing/slash")
expect(subject.api_endpoint).to eq("https://api.example.com/missing/slash/")
Expand Down Expand Up @@ -39,31 +50,22 @@
with { |req| req.headers["X-Dnsimple-Domain-Token"] == "domaintoken" }
end

it "uses header authentication if there's an api token provided" do
it "uses OAuth access token if there's an access token provided" do
stub_request(:any, %r[/test])

subject = described_class.new(username: "user", api_token: "token")
subject = described_class.new(oauth_access_token: "access-token")
subject.execute(:get, "test", {})

expect(WebMock).to have_requested(:get, "https://api.dnsimple.com/test").
with { |req| req.headers["X-Dnsimple-Token"] == "user:token" }
end

it "uses HTTP authentication via exchange token if there's an exchange token provided" do
stub_request(:any, %r[/test])

subject = described_class.new(username: "user", password: "pass", exchange_token: "exchange-token")
subject.execute(:get, "test", {})

expect(WebMock).to have_requested(:get, "https://exchange-token:x-2fa-basic@api.dnsimple.com/test")
with { |req| req.headers["Authorization"] == "Bearer access-token" }
end

it "raises an error if there's no password or api token provided" do
subject = described_class.new(username: "user")
it "raises an error if there's no password, domain token or access token provided" do
subject = described_class.new(username: "user", oauth_client_id: "id", oauth_client_secret: "secret")

expect {
subject.execute(:get, "test", {})
}.to raise_error(Dnsimple::Error, 'A password or API token is required for all API requests.')
}.to raise_error(Dnsimple::Error, "A password, domain API token or OAuth access token is required for all API requests.")
end

context "when 2FA is required" do
Expand Down
57 changes: 0 additions & 57 deletions spec/dnsimple/compatibility_spec.rb

This file was deleted.

6 changes: 0 additions & 6 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@
SPEC_ROOT = File.expand_path("../", __FILE__)
end

CONFIG = { 'username' => 'username', 'password' => 'password', 'base_uri' => 'https://api.sandbox.dnsimple.com/', 'host' => 'api.sandbox.dnsimple.com' }
Dnsimple::Client.base_uri = CONFIG['base_uri']
Dnsimple::Client.username = CONFIG['username']
Dnsimple::Client.password = CONFIG['password']


RSpec.configure do |c|
# Silent the puts call in the commands
# c.before do
Expand Down