jira-ruby 1.6.0 → 2.1.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
  SHA256:
3
- metadata.gz: 5433f708b438c961d0ca07b8aa60ae7733382aecb7374d4cc3ea3af1947f0e7f
4
- data.tar.gz: d6757f05e80e8c005ad0f20461fac98d5509f87f0e0bebaea025e43e7d180d3e
3
+ metadata.gz: 2fea6e6a55a6679e8dab6437b19ccb62d29f8d31ec4db8b36c449ce3cae78f44
4
+ data.tar.gz: e4f8e7e6c1be0344db51d6a516c3da9db55343f93a49cb08072d0dbf5c462f16
5
5
  SHA512:
6
- metadata.gz: 25f924c28544baf800585f9243d6d29622b0facab654aa03cf620bf76e4133731334b649c6af725fb0f246c68e3f5aa17bd8623eba5c2dd66303d2f2b2766dd2
7
- data.tar.gz: b15c6540010df98f03acd562a50520dd90705bfa58cb2815070bc0ba3a535aea00d092c61a0f58bd3e3f23345c8ee48cf14e5d602fe324c3f7197b408d64e252
6
+ metadata.gz: 934781eb8ab9ec5bc4da7ad9b1c048dc52adaffd17c389046c10f1078e93495f248d4af219dd10fc105a87e8ef6d58c99ada61db40c86238ee7f844ab632c831
7
+ data.tar.gz: 7ebaeb51adb377be540de240939188b3e88b85554958f3bd0cd187346778a3b45c0ee9cc70c23d4e5763f16fc2e96b36032a32253845c8a98d5c1c3de0bc2e7d
data/.gitignore CHANGED
@@ -9,3 +9,5 @@ pkg/*
9
9
  .DS_STORE
10
10
  doc
11
11
  .ruby-version
12
+
13
+ .rakeTasks
@@ -1,8 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.3
4
3
  - 2.4
5
- - ruby-head
4
+ - 2.5
5
+ - 2.6
6
+ - 2.7
6
7
  before_script:
7
8
  - rake jira:generate_public_cert
8
9
  script: bundle exec rake spec
data/README.md CHANGED
@@ -50,7 +50,7 @@ rake jira:generate_public_cert
50
50
 
51
51
  On Mac OS,
52
52
 
53
- * Follow the instructions under "Mac OSX Installer" here: https://developer.atlassian.com/display/DOCS/Install+the+Atlassian+SDK+on+a+Linux+or+Mac+System
53
+ * Follow the instructions under "Mac OSX Installer" here: https://developer.atlassian.com/server/framework/atlassian-sdk/install-the-atlassian-sdk-on-a-linux-or-mac-system
54
54
  * From within the archive directory, run:
55
55
 
56
56
  ```shell
@@ -153,14 +153,18 @@ require 'jira-ruby'
153
153
  # Consider the use of :use_ssl and :ssl_verify_mode options if running locally
154
154
  # for tests.
155
155
 
156
+ # 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
157
+
158
+ # You will see JIRA::HTTPError (JIRA::HTTPError) if you attempt to use basic auth with your user's password
159
+
156
160
  username = "myremoteuser"
157
- password = "myuserspassword"
161
+ api_token = "myApiToken"
158
162
 
159
163
  options = {
160
164
  :username => username,
161
- :password => password,
162
- :site => 'http://localhost:8080/',
163
- :context_path => '/myjira',
165
+ :password => api_token,
166
+ :site => 'http://localhost:8080/', # or 'https://<your_subdomain>.atlassian.net/'
167
+ :context_path => '/myjira', # often blank
164
168
  :auth_type => :basic,
165
169
  :read_timeout => 120
166
170
  }
@@ -303,7 +307,7 @@ class App < Sinatra::Base
303
307
  # site uri, and the request token, access token, and authorize paths
304
308
  before do
305
309
  options = {
306
- :site => 'http://localhost:2990',
310
+ :site => 'http://localhost:2990/',
307
311
  :context_path => '/jira',
308
312
  :signature_method => 'RSA-SHA1',
309
313
  :request_token_path => "/plugins/servlet/oauth/request-token",
@@ -401,7 +405,7 @@ require 'pp'
401
405
  require 'jira-ruby'
402
406
 
403
407
  options = {
404
- :site => 'http://localhost:2990',
408
+ :site => 'http://localhost:2990/',
405
409
  :context_path => '/jira',
406
410
  :signature_method => 'RSA-SHA1',
407
411
  :private_key_file => "rsakey.pem",
@@ -13,8 +13,6 @@ Gem::Specification.new do |s|
13
13
 
14
14
  s.required_ruby_version = '>= 1.9.3'
15
15
 
16
- s.rubyforge_project = 'jira-ruby'
17
-
18
16
  s.files = `git ls-files`.split("\n")
19
17
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
18
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
@@ -22,6 +20,7 @@ Gem::Specification.new do |s|
22
20
 
23
21
  # Runtime Dependencies
24
22
  s.add_runtime_dependency 'activesupport'
23
+ s.add_runtime_dependency 'atlassian-jwt'
25
24
  s.add_runtime_dependency 'multipart-post'
26
25
  s.add_runtime_dependency 'oauth', '~> 0.5', '>= 0.5.0'
27
26
 
@@ -38,10 +38,12 @@ require 'jira/resource/createmeta'
38
38
  require 'jira/resource/webhook'
39
39
  require 'jira/resource/agile'
40
40
  require 'jira/resource/board'
41
+ require 'jira/resource/board_configuration'
41
42
 
42
43
  require 'jira/request_client'
43
44
  require 'jira/oauth_client'
44
45
  require 'jira/http_client'
46
+ require 'jira/jwt_client'
45
47
  require 'jira/client'
46
48
 
47
49
  require 'jira/railtie' if defined?(Rails)
@@ -424,7 +424,7 @@ module JIRA
424
424
  end
425
425
  if @attrs['self']
426
426
  the_url = @attrs['self']
427
- the_url = the_url.sub(@client.options[:site], '') if @client.options[:site]
427
+ the_url = the_url.sub(@client.options[:site].chomp('/'), '') if @client.options[:site]
428
428
  the_url
429
429
  elsif key_value
430
430
  self.class.singular_path(client, key_value.to_s, prefix)
@@ -19,13 +19,20 @@ module JIRA
19
19
  # :consumer_key => nil,
20
20
  # :consumer_secret => nil,
21
21
  # :ssl_verify_mode => OpenSSL::SSL::VERIFY_PEER,
22
+ # :ssl_version => nil,
22
23
  # :use_ssl => true,
23
24
  # :username => nil,
24
25
  # :password => nil,
25
26
  # :auth_type => :oauth,
26
27
  # :proxy_address => nil,
27
28
  # :proxy_port => nil,
28
- # :additional_cookies => nil
29
+ # :proxy_username => nil,
30
+ # :proxy_password => nil,
31
+ # :additional_cookies => nil,
32
+ # :default_headers => {},
33
+ # :use_client_cert => false,
34
+ # :http_debug => false,
35
+ # :shared_secret => nil
29
36
  #
30
37
  # See the JIRA::Base class methods for all of the available methods on these accessor
31
38
  # objects.
@@ -44,6 +51,36 @@ module JIRA
44
51
 
45
52
  def_delegators :@request_client, :init_access_token, :set_access_token, :set_request_token, :request_token, :access_token, :authenticated?
46
53
 
54
+ DEFINED_OPTIONS = [
55
+ :site,
56
+ :context_path,
57
+ :signature_method,
58
+ :request_token_path,
59
+ :authorize_path,
60
+ :access_token_path,
61
+ :private_key_file,
62
+ :rest_base_path,
63
+ :consumer_key,
64
+ :consumer_secret,
65
+ :ssl_verify_mode,
66
+ :ssl_version,
67
+ :use_ssl,
68
+ :username,
69
+ :password,
70
+ :auth_type,
71
+ :proxy_address,
72
+ :proxy_port,
73
+ :proxy_username,
74
+ :proxy_password,
75
+ :additional_cookies,
76
+ :default_headers,
77
+ :use_client_cert,
78
+ :http_debug,
79
+ :issuer,
80
+ :base_url,
81
+ :shared_secret
82
+ ].freeze
83
+
47
84
  DEFAULT_OPTIONS = {
48
85
  site: 'http://localhost:2990',
49
86
  context_path: '/jira',
@@ -52,7 +89,8 @@ module JIRA
52
89
  use_ssl: true,
53
90
  use_client_cert: false,
54
91
  auth_type: :oauth,
55
- http_debug: false
92
+ http_debug: false,
93
+ default_headers: {}
56
94
  }.freeze
57
95
 
58
96
  def initialize(options = {})
@@ -60,6 +98,9 @@ module JIRA
60
98
  @options = options
61
99
  @options[:rest_base_path] = @options[:context_path] + @options[:rest_base_path]
62
100
 
101
+ unknown_options = options.keys.reject { |o| DEFINED_OPTIONS.include?(o) }
102
+ raise ArgumentError, "Unknown option(s) given: #{unknown_options}" unless unknown_options.empty?
103
+
63
104
  if options[:use_client_cert]
64
105
  raise ArgumentError, 'Options: :cert_path must be set when :use_client_cert is true' unless @options[:cert_path]
65
106
  raise ArgumentError, 'Options: :key_path must be set when :use_client_cert is true' unless @options[:key_path]
@@ -71,6 +112,8 @@ module JIRA
71
112
  when :oauth, :oauth_2legged
72
113
  @request_client = OauthClient.new(@options)
73
114
  @consumer = @request_client.consumer
115
+ when :jwt
116
+ @request_client = JwtClient.new(@options)
74
117
  when :basic
75
118
  @request_client = HttpClient.new(@options)
76
119
  when :cookie
@@ -155,6 +198,10 @@ module JIRA
155
198
  JIRA::Resource::BoardFactory.new(self)
156
199
  end
157
200
 
201
+ def BoardConfiguration
202
+ JIRA::Resource::BoardConfigurationFactory.new(self)
203
+ end
204
+
158
205
  def RapidView
159
206
  JIRA::Resource::RapidViewFactory.new(self)
160
207
  end
@@ -199,10 +246,6 @@ module JIRA
199
246
  JIRA::Resource::RemotelinkFactory.new(self)
200
247
  end
201
248
 
202
- def Sprint
203
- JIRA::Resource::SprintFactory.new(self)
204
- end
205
-
206
249
  def Agile
207
250
  JIRA::Resource::AgileFactory.new(self)
208
251
  end
@@ -226,6 +269,11 @@ module JIRA
226
269
  request(:post, path, body, merge_default_headers(headers))
227
270
  end
228
271
 
272
+ def post_multipart(path, file, headers = {})
273
+ puts "post multipart: #{path} - [#{file}]" if @http_debug
274
+ @request_client.request_multipart(path, file, headers)
275
+ end
276
+
229
277
  def put(path, body = '', headers = {})
230
278
  headers = { 'Content-Type' => 'application/json' }.merge(headers)
231
279
  request(:put, path, body, merge_default_headers(headers))
@@ -241,7 +289,7 @@ module JIRA
241
289
  protected
242
290
 
243
291
  def merge_default_headers(headers)
244
- { 'Accept' => 'application/json' }.merge(headers)
292
+ { 'Accept' => 'application/json' }.merge(@options[:default_headers]).merge(headers)
245
293
  end
246
294
  end
247
295
  end
@@ -6,8 +6,8 @@ require 'uri'
6
6
  module JIRA
7
7
  class HttpClient < RequestClient
8
8
  DEFAULT_OPTIONS = {
9
- username: '',
10
- password: ''
9
+ username: nil,
10
+ password: nil
11
11
  }.freeze
12
12
 
13
13
  attr_reader :options
@@ -18,7 +18,7 @@ module JIRA
18
18
  end
19
19
 
20
20
  def make_cookie_auth_request
21
- body = { username: @options[:username], password: @options[:password] }.to_json
21
+ body = { username: @options[:username].to_s, password: @options[:password].to_s }.to_json
22
22
  @options.delete(:username)
23
23
  @options.delete(:password)
24
24
  make_request(:post, @options[:context_path] + '/rest/auth/1/session', body, 'Content-Type' => 'application/json')
@@ -29,12 +29,15 @@ module JIRA
29
29
  path = request_path(url)
30
30
  request = Net::HTTP.const_get(http_method.to_s.capitalize).new(path, headers)
31
31
  request.body = body unless body.nil?
32
- add_cookies(request) if options[:use_cookies]
33
- request.basic_auth(@options[:username], @options[:password]) if @options[:username] && @options[:password]
34
- response = basic_auth_http_conn.request(request)
35
- @authenticated = response.is_a? Net::HTTPOK
36
- store_cookies(response) if options[:use_cookies]
37
- response
32
+
33
+ execute_request(request)
34
+ end
35
+
36
+ def make_multipart_request(url, body, headers = {})
37
+ path = request_path(url)
38
+ request = Net::HTTP::Post::Multipart.new(path, body, headers)
39
+
40
+ execute_request(request)
38
41
  end
39
42
 
40
43
  def basic_auth_http_conn
@@ -43,7 +46,7 @@ module JIRA
43
46
 
44
47
  def http_conn(uri)
45
48
  if @options[:proxy_address]
46
- http_class = Net::HTTP::Proxy(@options[:proxy_address], @options[:proxy_port] || 80)
49
+ http_class = Net::HTTP::Proxy(@options[:proxy_address], @options[:proxy_port] || 80, @options[:proxy_username], @options[:proxy_password])
47
50
  else
48
51
  http_class = Net::HTTP
49
52
  end
@@ -54,12 +57,13 @@ module JIRA
54
57
  http_conn.key = @options[:key]
55
58
  end
56
59
  http_conn.verify_mode = @options[:ssl_verify_mode]
60
+ http_conn.ssl_version = @options[:ssl_version] if @options[:ssl_version]
57
61
  http_conn.read_timeout = @options[:read_timeout]
58
62
  http_conn
59
63
  end
60
64
 
61
65
  def uri
62
- uri = URI.parse(@options[:site])
66
+ URI.parse(@options[:site])
63
67
  end
64
68
 
65
69
  def authenticated?
@@ -68,6 +72,17 @@ module JIRA
68
72
 
69
73
  private
70
74
 
75
+ def execute_request(request)
76
+ add_cookies(request) if options[:use_cookies]
77
+ request.basic_auth(@options[:username], @options[:password]) if @options[:username] && @options[:password]
78
+
79
+ response = basic_auth_http_conn.request(request)
80
+ @authenticated = response.is_a? Net::HTTPOK
81
+ store_cookies(response) if options[:use_cookies]
82
+
83
+ response
84
+ end
85
+
71
86
  def request_path(url)
72
87
  parsed_uri = URI(url)
73
88
 
@@ -8,7 +8,7 @@ module JIRA
8
8
 
9
9
  def initialize(response)
10
10
  @response = response
11
- @message = response.try(:message) || response.try(:body)
11
+ @message = response.try(:message).presence || response.try(:body)
12
12
  end
13
13
  end
14
14
  end
@@ -0,0 +1,67 @@
1
+ require 'atlassian/jwt'
2
+
3
+ module JIRA
4
+ class JwtClient < HttpClient
5
+ def make_request(http_method, url, body = '', headers = {})
6
+ @http_method = http_method
7
+
8
+ super(http_method, url, body, headers)
9
+ end
10
+
11
+ def make_multipart_request(url, data, headers = {})
12
+ @http_method = :post
13
+
14
+ super(url, data, headers)
15
+ end
16
+
17
+ class JwtUriBuilder
18
+ attr_reader :request_url, :http_method, :shared_secret, :site, :issuer
19
+
20
+ def initialize(request_url, http_method, shared_secret, site, issuer)
21
+ @request_url = request_url
22
+ @http_method = http_method
23
+ @shared_secret = shared_secret
24
+ @site = site
25
+ @issuer = issuer
26
+ end
27
+
28
+ def build
29
+ uri = URI.parse(request_url)
30
+ new_query = URI.decode_www_form(String(uri.query)) << ['jwt', jwt_header]
31
+ uri.query = URI.encode_www_form(new_query)
32
+
33
+ return uri.to_s unless uri.is_a?(URI::HTTP)
34
+
35
+ uri.request_uri
36
+ end
37
+
38
+ private
39
+
40
+ def jwt_header
41
+ claim = Atlassian::Jwt.build_claims \
42
+ issuer,
43
+ request_url,
44
+ http_method.to_s,
45
+ site,
46
+ (Time.now - 60).to_i,
47
+ (Time.now + 86_400).to_i
48
+
49
+ JWT.encode claim, shared_secret
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ attr_reader :http_method
56
+
57
+ def request_path(url)
58
+ JwtUriBuilder.new(
59
+ url,
60
+ http_method.to_s,
61
+ @options[:shared_secret],
62
+ @options[:site],
63
+ @options[:issuer]
64
+ ).build
65
+ end
66
+ end
67
+ end
@@ -72,29 +72,39 @@ module JIRA
72
72
  @access_token
73
73
  end
74
74
 
75
- def make_request(http_method, path, body = '', headers = {})
75
+ def make_request(http_method, url, body = '', headers = {})
76
76
  # When using oauth_2legged we need to add an empty oauth_token parameter to every request.
77
77
  if @options[:auth_type] == :oauth_2legged
78
78
  oauth_params_str = 'oauth_token='
79
- uri = URI.parse(path)
79
+ uri = URI.parse(url)
80
80
  uri.query = if uri.query.to_s == ''
81
81
  oauth_params_str
82
82
  else
83
83
  uri.query + '&' + oauth_params_str
84
84
  end
85
- path = uri.to_s
85
+ url = uri.to_s
86
86
  end
87
87
 
88
88
  case http_method
89
89
  when :delete, :get, :head
90
- response = access_token.send http_method, path, headers
90
+ response = access_token.send http_method, url, headers
91
91
  when :post, :put
92
- response = access_token.send http_method, path, body, headers
92
+ response = access_token.send http_method, url, body, headers
93
93
  end
94
94
  @authenticated = true
95
95
  response
96
96
  end
97
97
 
98
+ def make_multipart_request(url, data, headers = {})
99
+ request = Net::HTTP::Post::Multipart.new url, data, headers
100
+
101
+ access_token.sign! request
102
+
103
+ response = consumer.http.request(request)
104
+ @authenticated = true
105
+ response
106
+ end
107
+
98
108
  def authenticated?
99
109
  @authenticated
100
110
  end