net_dav 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (6) hide show
  1. data/Rakefile +5 -1
  2. data/VERSION +1 -1
  3. data/bin/dav +4 -2
  4. data/lib/net/dav.rb +82 -21
  5. data/net_dav.gemspec +2 -2
  6. metadata +2 -2
data/Rakefile CHANGED
@@ -36,7 +36,11 @@ task :spec => :check_dependencies
36
36
 
37
37
  task :default => :spec
38
38
 
39
- task :dist => [:clean, :release]
39
+ task "dist" => [:clean, :release]
40
+
41
+ task "dist:patch" => [:clean, "version:bump:patch", :release]
42
+
43
+ task "dist:minor" => [:clean, "version:bump:minor", :release]
40
44
 
41
45
  task :clean do
42
46
  Dir.glob("**/*~").each do |file|
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.2
1
+ 0.3.0
data/bin/dav CHANGED
@@ -6,6 +6,9 @@ require 'net/dav'
6
6
 
7
7
  dav_user = ENV['DAVUSER']
8
8
  dav_pass = ENV['DAVPASS']
9
+ disable_basic_auth = (ENV['DISABLE_BASIC_AUTH'] && ENV['DISABLE_BASIC_AUTH'] != "0")
10
+ enable_curl = !(ENV['DISABLE_CURL'] && ENV['DISABLE_CURL'] != "0")
11
+
9
12
  cmd = $*[0]
10
13
 
11
14
  def print_usage
@@ -36,9 +39,8 @@ else
36
39
  url = URI.parse $*[1]
37
40
  end
38
41
 
39
- enable_curl = !(ENV['DISABLE_CURL'] && ENV['DISABLE_CURL'] != "0")
40
-
41
42
  dav = Net::DAV.new(url, :curl => enable_curl)
43
+ dav.disable_basic_auth = disable_basic_auth
42
44
 
43
45
  if (url.scheme == 'https')
44
46
  dav.verify_callback = lambda do |preverify_ok, x509_store|
data/lib/net/dav.rb CHANGED
@@ -3,6 +3,7 @@ require 'uri'
3
3
  require 'nokogiri'
4
4
  require 'net/dav/item'
5
5
  require 'base64'
6
+ require 'digest/md5'
6
7
  begin
7
8
  require 'curb'
8
9
  rescue LoadError
@@ -15,6 +16,8 @@ module Net #:nodoc:
15
16
  class NetHttpHandler
16
17
  attr_writer :user, :pass
17
18
 
19
+ attr_accessor :disable_basic_auth
20
+
18
21
  def verify_callback=(callback)
19
22
  @http.verify_callback = callback
20
23
  end
@@ -24,6 +27,7 @@ module Net #:nodoc:
24
27
  end
25
28
 
26
29
  def initialize(uri)
30
+ @disable_basic_auth = false
27
31
  @uri = uri
28
32
  case @uri.scheme
29
33
  when "http"
@@ -69,9 +73,6 @@ module Net #:nodoc:
69
73
  req.content_length = length
70
74
  headers.each_pair { |key, value| req[key] = value } if headers
71
75
  req.content_type = 'text/xml; charset="utf-8"'
72
- if (@user)
73
- req.basic_auth @user, @pass
74
- end
75
76
  res = handle_request(req, headers)
76
77
  res
77
78
  end
@@ -87,9 +88,6 @@ module Net #:nodoc:
87
88
  req.body = body
88
89
  headers.each_pair { |key, value| req[key] = value } if headers
89
90
  req.content_type = 'text/xml; charset="utf-8"'
90
- if (@user)
91
- req.basic_auth @user, @pass
92
- end
93
91
  res = handle_request(req, headers)
94
92
  res
95
93
  end
@@ -103,9 +101,6 @@ module Net #:nodoc:
103
101
  raise "unkown returning_body verb #{verb}"
104
102
  end
105
103
  headers.each_pair { |key, value| req[key] = value } if headers
106
- if (@user)
107
- req.basic_auth @user, @pass
108
- end
109
104
  res = handle_request(req, headers, MAX_REDIRECTS, &block)
110
105
  res.body
111
106
  end
@@ -123,9 +118,6 @@ module Net #:nodoc:
123
118
  req.body = body
124
119
  headers.each_pair { |key, value| req[key] = value } if headers
125
120
  req.content_type = 'text/xml; charset="utf-8"'
126
- if (@user)
127
- req.basic_auth @user, @pass
128
- end
129
121
  res = handle_request(req, headers)
130
122
  res
131
123
  end
@@ -144,7 +136,21 @@ module Net #:nodoc:
144
136
  response = @http.request(req)
145
137
  end
146
138
  case response
147
- when Net::HTTPSuccess then response
139
+ when Net::HTTPSuccess then
140
+ return response
141
+ when Net::HTTPUnauthorized then
142
+ response.error! unless @user
143
+ response.error! if req['authorization']
144
+ new_req = clone_req(req.path, req, headers)
145
+ if response['www-authenticate'] =~ /^Basic/
146
+ if disable_basic_auth
147
+ raise "server requested basic auth, but that is disabled"
148
+ end
149
+ new_req.basic_auth @user, @pass
150
+ else
151
+ digest_auth(new_req, @user, @pass, response)
152
+ end
153
+ return handle_request(new_req, headers, limit - 1, &block)
148
154
  when Net::HTTPRedirection then
149
155
  location = URI.parse(response['location'])
150
156
  if (@uri.scheme != location.scheme ||
@@ -152,18 +158,56 @@ module Net #:nodoc:
152
158
  @uri.port != location.port)
153
159
  raise "cannot redirect to a different host #{@uri} => #{location}"
154
160
  end
155
- new_req = req.class.new(location.path)
156
- new_req.body = req.body
157
- new_req.body_stream = req.body_stream
158
- headers.each_pair { |key, value| new_req[key] = value } if headers
159
- if (@user)
160
- new_req.basic_auth @user, @pass
161
- end
162
- handle_request(new_req, limit - 1, &block)
161
+ new_req = clone_req(location.path, req, headers)
162
+ return handle_request(new_req, headers, limit - 1, &block)
163
163
  else
164
164
  response.error!
165
165
  end
166
166
  end
167
+ def clone_req(path, req, headers)
168
+ new_req = req.class.new(path)
169
+ new_req.body = req.body
170
+ new_req.body_stream = req.body_stream
171
+ headers.each_pair { |key, value| new_req[key] = value } if headers
172
+ return new_req
173
+ end
174
+
175
+ CNONCE = Digest::MD5.hexdigest("%x" % (Time.now.to_i + rand(65535))).slice(0, 8)
176
+
177
+ def digest_auth(request, user, password, response)
178
+ # based on http://segment7.net/projects/ruby/snippets/digest_auth.rb
179
+ @nonce_count = 0 if @nonce_count.nil?
180
+ @nonce_count += 1
181
+
182
+ raise "bad www-authenticate header" unless (response['www-authenticate'] =~ /^(\w+) (.*)/)
183
+
184
+ params = {}
185
+ $2.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
186
+
187
+ a_1 = "#{user}:#{params['realm']}:#{password}"
188
+ a_2 = "#{request.method}:#{request.path}"
189
+ request_digest = ''
190
+ request_digest << Digest::MD5.hexdigest(a_1)
191
+ request_digest << ':' << params['nonce']
192
+ request_digest << ':' << ('%08x' % @nonce_count)
193
+ request_digest << ':' << CNONCE
194
+ request_digest << ':' << params['qop']
195
+ request_digest << ':' << Digest::MD5.hexdigest(a_2)
196
+
197
+ header = []
198
+ header << "Digest username=\"#{user}\""
199
+ header << "realm=\"#{params['realm']}\""
200
+ header << "nonce=\"#{params['nonce']}\""
201
+ header << "uri=\"#{request.path}\""
202
+ header << "cnonce=\"#{CNONCE}\""
203
+ header << "nc=#{'%08x' % @nonce_count}"
204
+ header << "qop=#{params['qop']}"
205
+ header << "response=\"#{Digest::MD5.hexdigest(request_digest)}\""
206
+ header << "algorithm=\"MD5\""
207
+
208
+ header = header.join(', ')
209
+ request['Authorization'] = header
210
+ end
167
211
  end
168
212
 
169
213
 
@@ -187,6 +231,9 @@ module Net #:nodoc:
187
231
  @curl.timeout = @http.read_timeout
188
232
  @curl.follow_location = true
189
233
  @curl.max_redirects = MAX_REDIRECTS
234
+ if disable_basic_auth
235
+ @curl.http_auth_types = Curl::CURLAUTH_DIGEST
236
+ end
190
237
  end
191
238
  @curl
192
239
  end
@@ -210,11 +257,25 @@ module Net #:nodoc:
210
257
  end
211
258
  end
212
259
  curl.perform
260
+ unless curl.response_code >= 200 && curl.response_code < 300
261
+ headers = curl.header_str.split(/\r?\n/)
262
+ raise Exception.new("Curl response #{headers[0]}")
263
+ end
213
264
  curl.body_str
214
265
  end
215
266
 
216
267
  end
217
268
 
269
+ # Disable basic auth - to protect passwords from going in the clear
270
+ # through a man-in-the-middle attack.
271
+ def disable_basic_auth?
272
+ @handler.disable_basic_auth
273
+ end
274
+
275
+ def disable_basic_auth=(value)
276
+ @handler.disable_basic_auth = value
277
+ end
278
+
218
279
  # Seconds to wait until reading one block (by one system call).
219
280
  # If the DAV object cannot read a block in this many seconds,
220
281
  # it raises a TimeoutError exception.
data/net_dav.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{net_dav}
8
- s.version = "0.2.2"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Miron Cuperman"]
12
- s.date = %q{2009-11-01}
12
+ s.date = %q{2009-11-02}
13
13
  s.default_executable = %q{dav}
14
14
  s.description = %q{WebDAV client library in the style of Net::HTTP}
15
15
  s.email = %q{c1.github@niftybox.net}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net_dav
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miron Cuperman
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-11-01 00:00:00 -07:00
12
+ date: 2009-11-02 00:00:00 -08:00
13
13
  default_executable: dav
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency