net_dav 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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