diff --git a/README.md b/README.md index 53dfd07..0261d4a 100644 --- a/README.md +++ b/README.md @@ -73,14 +73,20 @@ to run the tests require 'ruby-bandwidth-iris' # Using directly -client = BandwidthIris::Client.new('accountId', 'userName', 'password') +client = BandwidthIris::Client.new('accountId', 'userName', 'password') # Basic Auth +client = BandwidthIris::Client.new({access_token: 'accessToken'}) # Bearer Token Auth +client = BandwidthIris::Client.new({client_id: 'clientId', client_secret: 'clientSecret'}) # OAuth 2 using Client Credentials sites = BandwidthIris::Site.list(client) # Or you can use default client instance (do this only once) BandwidthIris::Client.global_options = { :account_id => 'accountId', :username => 'userName', - :password => 'password' + :password => 'password', + :access_token => 'accessToken', + :access_token_expiration => Time.now + 3600 + :client_id => 'client_id', + :client_secret => 'client_secret' } # Now you can call any functions without first arg 'client' diff --git a/lib/bandwidth-iris/client.rb b/lib/bandwidth-iris/client.rb index 84185de..62e2e54 100644 --- a/lib/bandwidth-iris/client.rb +++ b/lib/bandwidth-iris/client.rb @@ -19,8 +19,12 @@ def initialize (account_id = nil, user_name = nil, password = nil, options = nil end options = options || @@global_options account_id = options[:account_id] unless account_id - user_name = options[:user_name] || options[:username] unless user_name + user_name = options[:user_name] || options[:username] unless user_name password = options[:password] unless password + @access_token = options[:access_token] + @access_token_expiration = options[:access_token_expiration] || Time.now + 3600 + @client_id = options[:client_id] + @client_secret = options[:client_secret] options[:api_endpoint] = @@global_options[:api_endpoint] unless options[:api_endpoint] options[:api_version] = @@global_options[:api_version] unless options[:api_version] api_endpoint = options[:api_endpoint] || "https://dashboard.bandwidth.com" @@ -31,8 +35,15 @@ def initialize (account_id = nil, user_name = nil, password = nil, options = nil @create_connection = lambda{|| Faraday.new(api_endpoint) { |faraday| # To make this gem compatible with Faraday v1 and v2, the basic_auth middleware can't be used because it was removed in v2 - faraday.request :authorization, 'Basic', Base64.strict_encode64("#{user_name}:#{password}") - #faraday.response :logger + if @access_token && @access_token_expiration > Time.now + 60 + faraday.request :authorization, 'Bearer', @access_token + elsif @client_id && @client_secret + refresh_auth_token + faraday.request :authorization, 'Bearer', @access_token + else + faraday.request :authorization, 'Basic', Base64.strict_encode64("#{user_name}:#{password}") + end + # faraday.response :logger faraday.headers['Accept'] = 'application/xml' faraday.headers['user-agent'] = 'Ruby-Bandwidth-Iris' faraday.response :follow_redirects # use Faraday::FollowRedirects::Middleware @@ -67,9 +78,24 @@ def Client.get_id_from_location_header(location) items.last end + def refresh_auth_token + token_url = 'https://api.bandwidth.com/api/v1/oauth2/token' + response = Faraday.new do |faraday| + faraday.request :url_encoded + faraday.request :authorization, :basic, @client_id, @client_secret + @set_adapter.call(faraday) + end.post(token_url, {grant_type: 'client_credentials'}) + if response.status >= 400 + raise Errors::GenericError.new(response.status, response.reason_phrase, response.headers, response.body) + end + body = JSON.parse(response.body) + @access_token = body['access_token'] + @access_token_expiration = Time.now + body['expires_in'] + end + # Make HTTP request to IRIS API # @param method [Symbol] http method to make - # @param path [string] path of url (exclude api verion and endpoint) to make call + # @param path [string] path of url (exclude api version and endpoint) to make call # @param data [Hash] data which will be sent with request (for :get and :delete request they will be sent with query in url) # @return [Array] array with 2 elements: parsed data of response and response headers def make_request(method, path, data = {}) @@ -89,7 +115,7 @@ def make_request(method, path, data = {}) # Makes an HTTP request for file uploads # @param method [Symbol] http method to make - # @param path [string] path of url (exclude api verion and endpoint) to make call + # @param path [string] path of url (exclude api version and endpoint) to make call # @param data [string] the raw binary string representing the file to upload # @param content_type [string] the content type of the request # @return [Array] array with 2 elements: parsed data of response and response headers @@ -102,7 +128,7 @@ def make_request_file_upload(method, path, data, content_type) # Makes an HTTP request for a file download # @param method [Symbol] http method to make - # @param path [string] path of url (exclude api verion and endpoint) to make call + # @param path [string] path of url (exclude api version and endpoint) to make call # @param data [Hash] data which will be sent with request (for :get and :delete request they will be sent with query in url) # @return [string] raw response from the API def make_request_file_download(method, path, data = {}) diff --git a/lib/bandwidth-iris/site.rb b/lib/bandwidth-iris/site.rb index d6cb2c0..770ba59 100644 --- a/lib/bandwidth-iris/site.rb +++ b/lib/bandwidth-iris/site.rb @@ -39,7 +39,7 @@ def delete() def get_sip_peer(peer_id) item = @client.make_request(:get, "#{@client.concat_account_path(SITE_PATH)}/#{id}/sippeers/#{peer_id}")[0][:sip_peer] item[:site_id] = id - puts item + # puts item item end diff --git a/lib/bandwidth-iris/version.rb b/lib/bandwidth-iris/version.rb index 38f8315..ae7d4d7 100644 --- a/lib/bandwidth-iris/version.rb +++ b/lib/bandwidth-iris/version.rb @@ -1,4 +1,4 @@ module BandwidthIris # Version of this gem - VERSION = "7.3.2" + VERSION = "7.4.0" end diff --git a/spec/bandwidth-iris/client_spec.rb b/spec/bandwidth-iris/client_spec.rb index a75e41b..03622d1 100644 --- a/spec/bandwidth-iris/client_spec.rb +++ b/spec/bandwidth-iris/client_spec.rb @@ -20,8 +20,8 @@ expect(Client.get_id_from_location_header('http://localhost/path1/path2/id')).to eql('id') end it 'should raise error if location is missing or nil' do - expect{Client.get_id_from_location_header('')}.to raise_error - expect{Client.get_id_from_location_header(nil)}.to raise_error + expect{Client.get_id_from_location_header('')}.to raise_error(StandardError) + expect{Client.get_id_from_location_header(nil)}.to raise_error(StandardError) end end @@ -51,12 +51,22 @@ describe '#make_request' do client = nil + token_client = nil + expired_token_client = nil + client_credentials_client = nil + before :each do client = Helper.get_client() + token_client = Helper.get_token_client() + expired_token_client = Helper.get_expired_token_client() + client_credentials_client = Helper.get_client_credentials_client() end after :each do client.stubs.verify_stubbed_calls() + token_client.stubs.verify_stubbed_calls() + expired_token_client.stubs.verify_stubbed_calls() + client_credentials_client.stubs.verify_stubbed_calls() end it 'should pass basic auth headers' do @@ -65,6 +75,29 @@ expect(client.make_request(:get, '/test-auth')).to eql([{:echoed_auth=>"Basic #{Base64.strict_encode64('username:password')}"}, {}]) end + it 'should pass bearer auth header using token' do + token_client.stubs.get('/v1.0/test-auth-token') { |env| [200, {}, "#{env[:request_headers]['Authorization']}"] } + expect(token_client.make_request(:get, '/test-auth-token')).to eql([{:echoed_auth=>"Bearer accessToken"}, {}]) + expect(token_client.instance_variable_get(:@access_token)).to eql('accessToken') + expect(token_client.instance_variable_get(:@access_token_expiration)).to be_a(Time) + end + + it 'should refresh expired token and pass bearer auth header' do + expired_token_client.stubs.post('https://api.bandwidth.com/api/v1/oauth2/token') { |env| [200, {}, '{"access_token":"newAccessToken","expires_in":3600}'] } + expired_token_client.stubs.get('/v1.0/test-auth-expired-token') { |env| [200, {}, "#{env[:request_headers]['Authorization']}"] } + expect(expired_token_client.make_request(:get, '/test-auth-expired-token')).to eql([{:echoed_auth=>"Bearer newAccessToken"}, {}]) + expect(expired_token_client.instance_variable_get(:@access_token)).to eql('newAccessToken') + expect(expired_token_client.instance_variable_get(:@access_token_expiration)).to be_a(Time) + end + + it 'should use client credentials to get token and pass bearer auth header' do + client_credentials_client.stubs.post('https://api.bandwidth.com/api/v1/oauth2/token') { |env| [200, {}, '{"access_token":"clientCredentialsAccessToken","expires_in":3600}'] } + client_credentials_client.stubs.get('/v1.0/test-auth-client-credentials') { |env| [200, {}, "#{env[:request_headers]['Authorization']}"] } + expect(client_credentials_client.make_request(:get, '/test-auth-client-credentials')).to eql([{:echoed_auth=>"Bearer clientCredentialsAccessToken"}, {}]) + expect(client_credentials_client.instance_variable_get(:@access_token)).to eql('clientCredentialsAccessToken') + expect(client_credentials_client.instance_variable_get(:@access_token_expiration)).to be_a(Time) + end + it 'should make GET request and return xml data' do client.stubs.get('/v1.0/path1') { |env| [200, {}, 'data'] } client.stubs.get('/v1.0/path2?testField=10') { |env| [200, {'Location'=>'url'}, '1012truefalse2015-05-29T01:02:03Z'] } diff --git a/spec/helper.rb b/spec/helper.rb index eccfee3..cb47f4c 100644 --- a/spec/helper.rb +++ b/spec/helper.rb @@ -30,6 +30,23 @@ def self.get_client() Client.new('accountId', 'username', 'password') end + def self.get_token_client() + Client.new({access_token: 'accessToken'}) + end + + def self.get_expired_token_client() + Client.new({ + access_token: 'expiredAccessToken', + access_token_expiration: Time.now - 3600, + client_id: 'clientId', + client_secret: 'clientSecret' + }) + end + + def self.get_client_credentials_client() + Client.new({client_id: 'clientId', client_secret: 'clientSecret'}) + end + def self.setup_environment() Client.global_options[:account_id] = 'accountId' Client.global_options[:username] = 'username'