diff --git a/README.md b/README.md index 386b912a..a65df45d 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ require 'jira-ruby' options = { :username => 'username', - :password => 'pass1234', + :api_access_token => 'myAPIAccessToken1234', # see link below to generate one. :site => 'http://mydomain.atlassian.net:443/', :context_path => '', :auth_type => :basic @@ -38,6 +38,8 @@ end * [Reference](http://docs.atlassian.com/jira/REST/latest/) +* [How to generate an API Access Token](https://confluence.atlassian.com/cloud/api-tokens-938839638.html) + ## Running tests Before running tests, you will need a public certificate generated. @@ -153,16 +155,12 @@ require 'jira-ruby' # Consider the use of :use_ssl and :ssl_verify_mode options if running locally # for tests. -# NOTE basic auth no longer works with Jira, you must generate an API token, to do so you must have jira instance access rights. You can generate a token here: https://id.atlassian.com/manage/api-tokens - -# You will see JIRA::HTTPError (JIRA::HTTPError) if you attempt to use basic auth with your user's password - username = "myremoteuser" api_token = "myApiToken" options = { :username => username, - :password => api_token, + :api_access_token => api_token, :site => 'http://localhost:8080/', # or 'https://.atlassian.net' :context_path => '/myjira', # often blank :auth_type => :basic, diff --git a/lib/jira/client.rb b/lib/jira/client.rb index d9f88b06..0fd18237 100644 --- a/lib/jira/client.rb +++ b/lib/jira/client.rb @@ -22,6 +22,7 @@ module JIRA # :use_ssl => true, # :username => nil, # :password => nil, + # :api_access_token => nil, # :auth_type => :oauth, # :proxy_address => nil, # :proxy_port => nil, @@ -76,9 +77,11 @@ def initialize(options = {}) when :jwt @request_client = JwtClient.new(@options) when :basic + raise ArgumentError, 'Options: :you must specify a :api_access_token as opposed to a :password' if @options.key?(:password) && !@options.key?(:api_access_token) @request_client = HttpClient.new(@options) when :cookie raise ArgumentError, 'Options: :use_cookies must be true for :cookie authorization type' if @options.key?(:use_cookies) && !@options[:use_cookies] + raise ArgumentError, 'Options: when :use_cookies is true you must specify a :password as opposed to an :api_access_token' if @options.key?(:api_access_token) && !@options.key?(:password) @options[:use_cookies] = true @request_client = HttpClient.new(@options) @request_client.make_cookie_auth_request diff --git a/lib/jira/http_client.rb b/lib/jira/http_client.rb index d99c6f59..0fdb401f 100644 --- a/lib/jira/http_client.rb +++ b/lib/jira/http_client.rb @@ -7,7 +7,8 @@ module JIRA class HttpClient < RequestClient DEFAULT_OPTIONS = { username: '', - password: '' + password: '', + api_access_token: '' }.freeze attr_reader :options @@ -30,7 +31,7 @@ def make_request(http_method, url, body = '', headers = {}) request = Net::HTTP.const_get(http_method.to_s.capitalize).new(path, headers) request.body = body unless body.nil? add_cookies(request) if options[:use_cookies] - request.basic_auth(@options[:username], @options[:password]) if @options[:username] && @options[:password] + request.basic_auth(@options[:username], @options[:api_access_token]) if @options[:username] && @options[:api_access_token] response = basic_auth_http_conn.request(request) @authenticated = response.is_a? Net::HTTPOK store_cookies(response) if options[:use_cookies] diff --git a/lib/jira/resource/attachment.rb b/lib/jira/resource/attachment.rb index 2f2e5983..e6c5aaa7 100644 --- a/lib/jira/resource/attachment.rb +++ b/lib/jira/resource/attachment.rb @@ -25,7 +25,7 @@ def save!(attrs) request = Net::HTTP::Post::Multipart.new url, data, headers request.basic_auth(client.request_client.options[:username], - client.request_client.options[:password]) + client.request_client.options[:api_access_token]) response = client.request_client.basic_auth_http_conn.request(request) diff --git a/spec/jira/client_spec.rb b/spec/jira/client_spec.rb index 51a16c20..ee794596 100644 --- a/spec/jira/client_spec.rb +++ b/spec/jira/client_spec.rb @@ -141,7 +141,7 @@ it 'sets the username and password' do expect(subject.options[:username]).to eq('foo') - expect(subject.options[:password]).to eq('bar') + expect(subject.options[:api_access_token]).to eq('bar') end it 'fails with wrong user name and password' do @@ -155,6 +155,12 @@ expect(subject.Project.all).to be_empty expect(subject.authenticated?).to be_truthy end + + it 'raises an Exception when constructed with a password' do + expect(lambda { + JIRA::Client.new(username: 'foo', password: 'bad_password', auth_type: :basic) + }).to raise_exception(ArgumentError, 'can only construct an auth_type: :basic client with an :api_access_token') + end end context 'with cookie authentication' do @@ -205,6 +211,13 @@ expect(subject.options[:username]).to be_nil expect(subject.options[:password]).to be_nil end + + it 'raises an Exception when constructed with an api_access_token' do + expect(lambda { + JIRA::Client.new(username: 'foo', api_access_token: 'incorrect_usage_of_token', auth_type: :cookie) + }).to raise_exception(ArgumentError, 'can only construct a auth_type: :cookie client with a :password') + + end end context 'with jwt authentication' do diff --git a/spec/jira/http_client_spec.rb b/spec/jira/http_client_spec.rb index 4c81150e..f4cf673b 100644 --- a/spec/jira/http_client_spec.rb +++ b/spec/jira/http_client_spec.rb @@ -78,7 +78,7 @@ basic_auth_http_conn = double request = double allow(basic_client).to receive(:basic_auth_http_conn).and_return(basic_auth_http_conn) - expect(request).to receive(:basic_auth).with(basic_client.options[:username], basic_client.options[:password]).exactly(5).times.and_return(request) + expect(request).to receive(:basic_auth).with(basic_client.options[:username], basic_client.options[:api_access_token]).exactly(5).times.and_return(request) expect(basic_auth_http_conn).to receive(:request).exactly(5).times.with(request).and_return(response) %i[delete get head].each do |method| expect(Net::HTTP.const_get(method.to_s.capitalize)).to receive(:new).with('/path', headers).and_return(request) @@ -141,7 +141,7 @@ expect(Net::HTTP::Get).to receive(:new).with('/foo', headers).and_return(http_request) expect(basic_auth_http_conn).to receive(:request).with(http_request).and_return(response) - expect(http_request).to receive(:basic_auth).with(basic_client.options[:username], basic_client.options[:password]).and_return(http_request) + expect(http_request).to receive(:basic_auth).with(basic_client.options[:username], basic_client.options[:api_access_token]).and_return(http_request) allow(basic_client).to receive(:basic_auth_http_conn).and_return(basic_auth_http_conn) basic_client.make_request(:get, '/foo', body, headers) end @@ -154,7 +154,7 @@ expect(Net::HTTP::Get).to receive(:new).with('/foo', headers).and_return(http_request) expect(basic_auth_http_conn).to receive(:request).with(http_request).and_return(response) - expect(http_request).to receive(:basic_auth).with(basic_client.options[:username], basic_client.options[:password]).and_return(http_request) + expect(http_request).to receive(:basic_auth).with(basic_client.options[:username], basic_client.options[:api_access_token]).and_return(http_request) allow(basic_client).to receive(:basic_auth_http_conn).and_return(basic_auth_http_conn) basic_client.make_request(:get, 'http://mydomain.com/foo', body, headers) end