jira-ruby 1.6.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +3 -2
  4. data/README.md +40 -11
  5. data/Rakefile +2 -2
  6. data/example.rb +8 -0
  7. data/jira-ruby.gemspec +1 -2
  8. data/lib/jira/base.rb +1 -1
  9. data/lib/jira/client.rb +85 -12
  10. data/lib/jira/http_client.rb +29 -13
  11. data/lib/jira/http_error.rb +1 -1
  12. data/lib/jira/jwt_client.rb +67 -0
  13. data/lib/jira/oauth_client.rb +18 -6
  14. data/lib/jira/request_client.rb +15 -3
  15. data/lib/jira/resource/attachment.rb +19 -14
  16. data/lib/jira/resource/board.rb +8 -1
  17. data/lib/jira/resource/board_configuration.rb +9 -0
  18. data/lib/jira/resource/issue_picker_suggestions.rb +24 -0
  19. data/lib/jira/resource/issue_picker_suggestions_issue.rb +10 -0
  20. data/lib/jira/resource/sprint.rb +12 -8
  21. data/lib/jira/resource/suggested_issue.rb +9 -0
  22. data/lib/jira/resource/user.rb +1 -1
  23. data/lib/jira/resource/watcher.rb +7 -0
  24. data/lib/jira/version.rb +1 -1
  25. data/lib/jira-ruby.rb +6 -0
  26. data/spec/integration/user_spec.rb +3 -3
  27. data/spec/integration/watcher_spec.rb +15 -6
  28. data/spec/jira/base_spec.rb +12 -0
  29. data/spec/jira/client_spec.rb +70 -0
  30. data/spec/jira/http_client_spec.rb +137 -8
  31. data/spec/jira/jwt_uri_builder_spec.rb +59 -0
  32. data/spec/jira/oauth_client_spec.rb +47 -10
  33. data/spec/jira/request_client_spec.rb +37 -10
  34. data/spec/jira/resource/attachment_spec.rb +79 -22
  35. data/spec/jira/resource/board_spec.rb +50 -1
  36. data/spec/jira/resource/issue_picker_suggestions_spec.rb +79 -0
  37. data/spec/jira/resource/issue_spec.rb +1 -1
  38. data/spec/jira/resource/jira_picker_suggestions_issue_spec.rb +18 -0
  39. data/spec/jira/resource/sprint_spec.rb +23 -11
  40. metadata +32 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5433f708b438c961d0ca07b8aa60ae7733382aecb7374d4cc3ea3af1947f0e7f
4
- data.tar.gz: d6757f05e80e8c005ad0f20461fac98d5509f87f0e0bebaea025e43e7d180d3e
3
+ metadata.gz: 113ad755633d6eb87e63d7a97d3228f6649828547c01dee1ae1900ef0d575e2d
4
+ data.tar.gz: d59620f52976814ee7db58df10213bdb512f042a8b8214789ed07288f2899b65
5
5
  SHA512:
6
- metadata.gz: 25f924c28544baf800585f9243d6d29622b0facab654aa03cf620bf76e4133731334b649c6af725fb0f246c68e3f5aa17bd8623eba5c2dd66303d2f2b2766dd2
7
- data.tar.gz: b15c6540010df98f03acd562a50520dd90705bfa58cb2815070bc0ba3a535aea00d092c61a0f58bd3e3f23345c8ee48cf14e5d602fe324c3f7197b408d64e252
6
+ metadata.gz: e523698732b5cef8a220259ccf5c42c568ac7eea435ee2ed2f2018cdd25959597d23337a67fac037e00be85a8838d9d32a2c79a156da008380699d2c9529cb03
7
+ data.tar.gz: cefe63455cb070951d73420dc1144b0c0a4651836427f507d218d3eb0780936e6a740842f6f2df8f68b14be8034bb196b4f5069d21ac0c3cd9e2a4292e43b456
data/.gitignore CHANGED
@@ -9,3 +9,5 @@ pkg/*
9
9
  .DS_STORE
10
10
  doc
11
11
  .ruby-version
12
+
13
+ .rakeTasks
data/.travis.yml CHANGED
@@ -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
@@ -99,7 +99,7 @@ defaults to HTTP Basic Auth.
99
99
 
100
100
  Jira supports cookie based authentication whereby user credentials are passed
101
101
  to JIRA via a JIRA REST API call. This call returns a session cookie which must
102
- then be sent to all following JIRA REST API calls.
102
+ then be sent to all following JIRA REST API calls.
103
103
 
104
104
  To enable cookie based authentication, set `:auth_type` to `:cookie`,
105
105
  set `:use_cookies` to `true` and set `:username` and `:password` accordingly.
@@ -114,7 +114,7 @@ options = {
114
114
  :context_path => '',
115
115
  :auth_type => :cookie, # Set cookie based authentication
116
116
  :use_cookies => true, # Send cookies with each request
117
- :additional_cookies => ['AUTH=vV7uzixt0SScJKg7'] # Optional cookies to send
117
+ :additional_cookies => ['AUTH=vV7uzixt0SScJKg7'] # Optional cookies to send
118
118
  # with each request
119
119
  }
120
120
 
@@ -134,15 +134,40 @@ cookie to add to the request.
134
134
 
135
135
  Some authentication schemes that require additional cookies ignore the username
136
136
  and password sent in the JIRA REST API call. For those use cases, `:username`
137
- and `:password` may be omitted from `options`.
137
+ and `:password` may be omitted from `options`.
138
138
 
139
+ ## Configuring JIRA to use Personal Access Tokens Auth
140
+ If your JIRA system is configured to support Personal Access Token authorization, minor modifications are needed in how credentials are communicated to the server. Specifically, the paremeters `:username` and `:password` are not needed. Also, the parameter `:default_headers` is needed to contain the api_token, which can be obtained following the official documentation from [Atlassian](https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html). Please note that the Personal Access Token can only be used as it is. If it is encoded (with base64 or any other encoding method) then the token will not work correctly and authentication will fail.
141
+
142
+ ```ruby
143
+ require 'jira-ruby'
144
+
145
+ # NOTE: the token should not be encoded
146
+ api_token = API_TOKEN_OBTAINED_FROM_JIRA_UI
147
+
148
+ options = {
149
+ :site => 'http://mydomain.atlassian.net:443/',
150
+ :context_path => '',
151
+ :username => '<the email you sign-in to Jira>',
152
+ :password => api_token,
153
+ :auth_type => :basic
154
+ }
155
+
156
+ client = JIRA::Client.new(options)
157
+
158
+ project = client.Project.find('SAMPLEPROJECT')
159
+
160
+ project.issues.each do |issue|
161
+ puts "#{issue.id} - #{issue.summary}"
162
+ end
163
+ ```
139
164
  ## Using the API Gem in a command line application
140
165
 
141
166
  Using HTTP Basic Authentication, configure and connect a client to your instance
142
167
  of JIRA.
143
168
 
144
169
  Note: If your Jira install is hosted on [atlassian.net](atlassian.net), it will have no context
145
- path by default. If you're having issues connecting, try setting context_path
170
+ path by default. If you're having issues connecting, try setting context_path
146
171
  to an empty string in the options hash.
147
172
 
148
173
  ```ruby
@@ -153,14 +178,18 @@ require 'jira-ruby'
153
178
  # Consider the use of :use_ssl and :ssl_verify_mode options if running locally
154
179
  # for tests.
155
180
 
181
+ # 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
182
+
183
+ # You will see JIRA::HTTPError (JIRA::HTTPError) if you attempt to use basic auth with your user's password
184
+
156
185
  username = "myremoteuser"
157
- password = "myuserspassword"
186
+ api_token = "myApiToken"
158
187
 
159
188
  options = {
160
189
  :username => username,
161
- :password => password,
162
- :site => 'http://localhost:8080/',
163
- :context_path => '/myjira',
190
+ :password => api_token,
191
+ :site => 'http://localhost:8080/', # or 'https://<your_subdomain>.atlassian.net/'
192
+ :context_path => '/myjira', # often blank
164
193
  :auth_type => :basic,
165
194
  :read_timeout => 120
166
195
  }
@@ -303,7 +332,7 @@ class App < Sinatra::Base
303
332
  # site uri, and the request token, access token, and authorize paths
304
333
  before do
305
334
  options = {
306
- :site => 'http://localhost:2990',
335
+ :site => 'http://localhost:2990/',
307
336
  :context_path => '/jira',
308
337
  :signature_method => 'RSA-SHA1',
309
338
  :request_token_path => "/plugins/servlet/oauth/request-token",
@@ -401,7 +430,7 @@ require 'pp'
401
430
  require 'jira-ruby'
402
431
 
403
432
  options = {
404
- :site => 'http://localhost:2990',
433
+ :site => 'http://localhost:2990/',
405
434
  :context_path => '/jira',
406
435
  :signature_method => 'RSA-SHA1',
407
436
  :private_key_file => "rsakey.pem",
data/Rakefile CHANGED
@@ -14,13 +14,13 @@ desc 'Prepare and run rspec tests'
14
14
  task :prepare do
15
15
  rsa_key = File.expand_path('rsakey.pem')
16
16
  unless File.exist?(rsa_key)
17
- raise 'rsakey.pem does not exist, tests will fail. Run `rake jira:generate_public_cert` first'
17
+ Rake::Task['jira:generate_public_cert'].invoke
18
18
  end
19
19
  end
20
20
 
21
21
  desc 'Run RSpec tests'
22
22
  # RSpec::Core::RakeTask.new(:spec)
23
- RSpec::Core::RakeTask.new(:spec) do |task|
23
+ RSpec::Core::RakeTask.new(:spec, [] => [:prepare]) do |task|
24
24
  task.rspec_opts = ['--color', '--format', 'doc']
25
25
  end
26
26
 
data/example.rb CHANGED
@@ -166,6 +166,14 @@ client.Issue.jql(a_normal_jql_search, max_results: 500)
166
166
  # # --------------------------
167
167
  # issue.comments.first.save({"body" => "an updated comment frome example.rb"})
168
168
 
169
+
170
+ # # Add attachment to Issue
171
+ # # ------------------------
172
+ # issue = client.Issue.find('PROJ-1')
173
+ # attachment = issue.attachments.build
174
+ # attachment.save('file': '/path/to/file')
175
+ #
176
+
169
177
  # List all available link types
170
178
  # ------------------------------
171
179
  pp client.Issuelinktype.all
data/jira-ruby.gemspec CHANGED
@@ -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
 
data/lib/jira/base.rb CHANGED
@@ -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)
data/lib/jira/client.rb CHANGED
@@ -6,7 +6,7 @@ module JIRA
6
6
  # This class is the main access point for all JIRA::Resource instances.
7
7
  #
8
8
  # The client must be initialized with an options hash containing
9
- # configuration options. The available options are:
9
+ # configuration options. The available options are:
10
10
  #
11
11
  # :site => 'http://localhost:2990',
12
12
  # :context_path => '/jira',
@@ -14,18 +14,33 @@ module JIRA
14
14
  # :request_token_path => "/plugins/servlet/oauth/request-token",
15
15
  # :authorize_path => "/plugins/servlet/oauth/authorize",
16
16
  # :access_token_path => "/plugins/servlet/oauth/access-token",
17
+ # :private_key => nil,
17
18
  # :private_key_file => "rsakey.pem",
18
19
  # :rest_base_path => "/rest/api/2",
19
20
  # :consumer_key => nil,
20
21
  # :consumer_secret => nil,
21
22
  # :ssl_verify_mode => OpenSSL::SSL::VERIFY_PEER,
23
+ # :ssl_version => nil,
22
24
  # :use_ssl => true,
23
25
  # :username => nil,
24
26
  # :password => nil,
25
27
  # :auth_type => :oauth,
26
28
  # :proxy_address => nil,
27
29
  # :proxy_port => nil,
28
- # :additional_cookies => nil
30
+ # :proxy_username => nil,
31
+ # :proxy_password => nil,
32
+ # :use_cookies => nil,
33
+ # :additional_cookies => nil,
34
+ # :default_headers => {},
35
+ # :use_client_cert => false,
36
+ # :read_timeout => nil,
37
+ # :http_debug => false,
38
+ # :shared_secret => nil,
39
+ # :cert_path => nil,
40
+ # :key_path => nil,
41
+ # :ssl_client_cert => nil,
42
+ # :ssl_client_key => nil
43
+ # :ca_file => nil
29
44
  #
30
45
  # See the JIRA::Base class methods for all of the available methods on these accessor
31
46
  # objects.
@@ -44,6 +59,43 @@ module JIRA
44
59
 
45
60
  def_delegators :@request_client, :init_access_token, :set_access_token, :set_request_token, :request_token, :access_token, :authenticated?
46
61
 
62
+ DEFINED_OPTIONS = [
63
+ :site,
64
+ :context_path,
65
+ :signature_method,
66
+ :request_token_path,
67
+ :authorize_path,
68
+ :access_token_path,
69
+ :private_key,
70
+ :private_key_file,
71
+ :rest_base_path,
72
+ :consumer_key,
73
+ :consumer_secret,
74
+ :ssl_verify_mode,
75
+ :ssl_version,
76
+ :use_ssl,
77
+ :username,
78
+ :password,
79
+ :auth_type,
80
+ :proxy_address,
81
+ :proxy_port,
82
+ :proxy_username,
83
+ :proxy_password,
84
+ :use_cookies,
85
+ :additional_cookies,
86
+ :default_headers,
87
+ :use_client_cert,
88
+ :read_timeout,
89
+ :http_debug,
90
+ :issuer,
91
+ :base_url,
92
+ :shared_secret,
93
+ :cert_path,
94
+ :key_path,
95
+ :ssl_client_cert,
96
+ :ssl_client_key
97
+ ].freeze
98
+
47
99
  DEFAULT_OPTIONS = {
48
100
  site: 'http://localhost:2990',
49
101
  context_path: '/jira',
@@ -52,7 +104,8 @@ module JIRA
52
104
  use_ssl: true,
53
105
  use_client_cert: false,
54
106
  auth_type: :oauth,
55
- http_debug: false
107
+ http_debug: false,
108
+ default_headers: {}
56
109
  }.freeze
57
110
 
58
111
  def initialize(options = {})
@@ -60,17 +113,23 @@ module JIRA
60
113
  @options = options
61
114
  @options[:rest_base_path] = @options[:context_path] + @options[:rest_base_path]
62
115
 
116
+ unknown_options = options.keys.reject { |o| DEFINED_OPTIONS.include?(o) }
117
+ raise ArgumentError, "Unknown option(s) given: #{unknown_options}" unless unknown_options.empty?
118
+
63
119
  if options[:use_client_cert]
64
- raise ArgumentError, 'Options: :cert_path must be set when :use_client_cert is true' unless @options[:cert_path]
65
- raise ArgumentError, 'Options: :key_path must be set when :use_client_cert is true' unless @options[:key_path]
66
- @options[:cert] = OpenSSL::X509::Certificate.new(File.read(@options[:cert_path]))
67
- @options[:key] = OpenSSL::PKey::RSA.new(File.read(@options[:key_path]))
120
+ @options[:ssl_client_cert] = OpenSSL::X509::Certificate.new(File.read(@options[:cert_path])) if @options[:cert_path]
121
+ @options[:ssl_client_key] = OpenSSL::PKey::RSA.new(File.read(@options[:key_path])) if @options[:key_path]
122
+
123
+ raise ArgumentError, 'Options: :cert_path or :ssl_client_cert must be set when :use_client_cert is true' unless @options[:ssl_client_cert]
124
+ raise ArgumentError, 'Options: :key_path or :ssl_client_key must be set when :use_client_cert is true' unless @options[:ssl_client_key]
68
125
  end
69
126
 
70
127
  case options[:auth_type]
71
128
  when :oauth, :oauth_2legged
72
129
  @request_client = OauthClient.new(@options)
73
130
  @consumer = @request_client.consumer
131
+ when :jwt
132
+ @request_client = JwtClient.new(@options)
74
133
  when :basic
75
134
  @request_client = HttpClient.new(@options)
76
135
  when :cookie
@@ -155,6 +214,10 @@ module JIRA
155
214
  JIRA::Resource::BoardFactory.new(self)
156
215
  end
157
216
 
217
+ def BoardConfiguration
218
+ JIRA::Resource::BoardConfigurationFactory.new(self)
219
+ end
220
+
158
221
  def RapidView
159
222
  JIRA::Resource::RapidViewFactory.new(self)
160
223
  end
@@ -195,12 +258,12 @@ module JIRA
195
258
  JIRA::Resource::IssuelinktypeFactory.new(self)
196
259
  end
197
260
 
198
- def Remotelink
199
- JIRA::Resource::RemotelinkFactory.new(self)
261
+ def IssuePickerSuggestions
262
+ JIRA::Resource::IssuePickerSuggestionsFactory.new(self)
200
263
  end
201
264
 
202
- def Sprint
203
- JIRA::Resource::SprintFactory.new(self)
265
+ def Remotelink
266
+ JIRA::Resource::RemotelinkFactory.new(self)
204
267
  end
205
268
 
206
269
  def Agile
@@ -226,6 +289,11 @@ module JIRA
226
289
  request(:post, path, body, merge_default_headers(headers))
227
290
  end
228
291
 
292
+ def post_multipart(path, file, headers = {})
293
+ puts "post multipart: #{path} - [#{file}]" if @http_debug
294
+ @request_client.request_multipart(path, file, headers)
295
+ end
296
+
229
297
  def put(path, body = '', headers = {})
230
298
  headers = { 'Content-Type' => 'application/json' }.merge(headers)
231
299
  request(:put, path, body, merge_default_headers(headers))
@@ -238,10 +306,15 @@ module JIRA
238
306
  @request_client.request(http_method, path, body, headers)
239
307
  end
240
308
 
309
+ # Stops sensitive client information from being displayed in logs
310
+ def inspect
311
+ "#<JIRA::Client:#{object_id}>"
312
+ end
313
+
241
314
  protected
242
315
 
243
316
  def merge_default_headers(headers)
244
- { 'Accept' => 'application/json' }.merge(headers)
317
+ { 'Accept' => 'application/json' }.merge(@options[:default_headers]).merge(headers)
245
318
  end
246
319
  end
247
320
  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,23 +46,25 @@ 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
50
53
  http_conn = http_class.new(uri.host, uri.port)
51
54
  http_conn.use_ssl = @options[:use_ssl]
52
55
  if @options[:use_client_cert]
53
- http_conn.cert = @options[:cert]
54
- http_conn.key = @options[:key]
56
+ http_conn.cert = @options[:ssl_client_cert]
57
+ http_conn.key = @options[:ssl_client_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]
62
+ http_conn.ca_file = @options[:ca_file] if @options[:ca_file]
58
63
  http_conn
59
64
  end
60
65
 
61
66
  def uri
62
- uri = URI.parse(@options[:site])
67
+ URI.parse(@options[:site])
63
68
  end
64
69
 
65
70
  def authenticated?
@@ -68,6 +73,17 @@ module JIRA
68
73
 
69
74
  private
70
75
 
76
+ def execute_request(request)
77
+ add_cookies(request) if options[:use_cookies]
78
+ request.basic_auth(@options[:username], @options[:password]) if @options[:username] && @options[:password]
79
+
80
+ response = basic_auth_http_conn.request(request)
81
+ @authenticated = response.is_a? Net::HTTPOK
82
+ store_cookies(response) if options[:use_cookies]
83
+
84
+ response
85
+ end
86
+
71
87
  def request_path(url)
72
88
  parsed_uri = URI(url)
73
89
 
@@ -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
@@ -38,13 +38,15 @@ module JIRA
38
38
  @options[:request_token_path] = @options[:context_path] + @options[:request_token_path]
39
39
  @options[:authorize_path] = @options[:context_path] + @options[:authorize_path]
40
40
  @options[:access_token_path] = @options[:context_path] + @options[:access_token_path]
41
+ # proxy_address does not exist in oauth's gem context but proxy does
42
+ @options[:proxy] = @options[:proxy_address] if @options[:proxy_address]
41
43
  OAuth::Consumer.new(@options[:consumer_key], @options[:consumer_secret], @options)
42
44
  end
43
45
 
44
46
  # Returns the current request token if it is set, else it creates
45
47
  # and sets a new token.
46
48
  def request_token(options = {}, *arguments, &block)
47
- @request_token ||= get_request_token(options, *arguments, block)
49
+ @request_token ||= get_request_token(options, *arguments, &block)
48
50
  end
49
51
 
50
52
  # Sets the request token from a given token and secret.
@@ -72,29 +74,39 @@ module JIRA
72
74
  @access_token
73
75
  end
74
76
 
75
- def make_request(http_method, path, body = '', headers = {})
77
+ def make_request(http_method, url, body = '', headers = {})
76
78
  # When using oauth_2legged we need to add an empty oauth_token parameter to every request.
77
79
  if @options[:auth_type] == :oauth_2legged
78
80
  oauth_params_str = 'oauth_token='
79
- uri = URI.parse(path)
81
+ uri = URI.parse(url)
80
82
  uri.query = if uri.query.to_s == ''
81
83
  oauth_params_str
82
84
  else
83
85
  uri.query + '&' + oauth_params_str
84
86
  end
85
- path = uri.to_s
87
+ url = uri.to_s
86
88
  end
87
89
 
88
90
  case http_method
89
91
  when :delete, :get, :head
90
- response = access_token.send http_method, path, headers
92
+ response = access_token.send http_method, url, headers
91
93
  when :post, :put
92
- response = access_token.send http_method, path, body, headers
94
+ response = access_token.send http_method, url, body, headers
93
95
  end
94
96
  @authenticated = true
95
97
  response
96
98
  end
97
99
 
100
+ def make_multipart_request(url, data, headers = {})
101
+ request = Net::HTTP::Post::Multipart.new url, data, headers
102
+
103
+ access_token.sign! request
104
+
105
+ response = consumer.http.request(request)
106
+ @authenticated = true
107
+ response
108
+ end
109
+
98
110
  def authenticated?
99
111
  @authenticated
100
112
  end
@@ -1,7 +1,6 @@
1
1
  require 'oauth'
2
2
  require 'json'
3
3
  require 'net/https'
4
- # require 'pry'
5
4
 
6
5
  module JIRA
7
6
  class RequestClient
@@ -11,9 +10,22 @@ module JIRA
11
10
 
12
11
  def request(*args)
13
12
  response = make_request(*args)
14
- # binding.pry unless response.kind_of?(Net::HTTPSuccess)
15
13
  raise HTTPError, response unless response.is_a?(Net::HTTPSuccess)
16
14
  response
17
15
  end
16
+
17
+ def request_multipart(*args)
18
+ response = make_multipart_request(*args)
19
+ raise HTTPError, response unless response.is_a?(Net::HTTPSuccess)
20
+ response
21
+ end
22
+
23
+ def make_request(*args)
24
+ raise NotImplementedError
25
+ end
26
+
27
+ def make_multipart_request(*args)
28
+ raise NotImplementedError
29
+ end
18
30
  end
19
- end
31
+ end