rubysl-webrick 1.0.0 → 2.0.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 (39) hide show
  1. checksums.yaml +14 -6
  2. data/.travis.yml +5 -6
  3. data/lib/rubysl/webrick/version.rb +1 -1
  4. data/lib/rubysl/webrick/webrick.rb +199 -2
  5. data/lib/webrick/accesslog.rb +96 -5
  6. data/lib/webrick/cgi.rb +80 -29
  7. data/lib/webrick/compat.rb +20 -0
  8. data/lib/webrick/config.rb +59 -5
  9. data/lib/webrick/cookie.rb +66 -5
  10. data/lib/webrick/htmlutils.rb +4 -1
  11. data/lib/webrick/httpauth.rb +53 -3
  12. data/lib/webrick/httpauth/authenticator.rb +53 -16
  13. data/lib/webrick/httpauth/basicauth.rb +45 -2
  14. data/lib/webrick/httpauth/digestauth.rb +82 -17
  15. data/lib/webrick/httpauth/htdigest.rb +38 -1
  16. data/lib/webrick/httpauth/htgroup.rb +32 -0
  17. data/lib/webrick/httpauth/htpasswd.rb +40 -2
  18. data/lib/webrick/httpauth/userdb.rb +27 -4
  19. data/lib/webrick/httpproxy.rb +197 -112
  20. data/lib/webrick/httprequest.rb +268 -50
  21. data/lib/webrick/httpresponse.rb +170 -33
  22. data/lib/webrick/https.rb +26 -3
  23. data/lib/webrick/httpserver.rb +75 -7
  24. data/lib/webrick/httpservlet/abstract.rb +88 -6
  25. data/lib/webrick/httpservlet/cgi_runner.rb +5 -4
  26. data/lib/webrick/httpservlet/cgihandler.rb +37 -18
  27. data/lib/webrick/httpservlet/erbhandler.rb +40 -7
  28. data/lib/webrick/httpservlet/filehandler.rb +116 -28
  29. data/lib/webrick/httpservlet/prochandler.rb +17 -4
  30. data/lib/webrick/httpstatus.rb +86 -18
  31. data/lib/webrick/httputils.rb +131 -23
  32. data/lib/webrick/httpversion.rb +28 -2
  33. data/lib/webrick/log.rb +72 -5
  34. data/lib/webrick/server.rb +158 -33
  35. data/lib/webrick/ssl.rb +78 -9
  36. data/lib/webrick/utils.rb +151 -5
  37. data/lib/webrick/version.rb +5 -1
  38. data/rubysl-webrick.gemspec +0 -1
  39. metadata +12 -24
@@ -13,10 +13,31 @@ require 'webrick/httpauth/authenticator'
13
13
 
14
14
  module WEBrick
15
15
  module HTTPAuth
16
+
17
+ ##
18
+ # Basic Authentication for WEBrick
19
+ #
20
+ # Use this class to add basic authentication to a WEBrick servlet.
21
+ #
22
+ # Here is an example of how to set up a BasicAuth:
23
+ #
24
+ # config = { :Realm => 'BasicAuth example realm' }
25
+ #
26
+ # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file'
27
+ # htpasswd.set_passwd config[:Realm], 'username', 'password'
28
+ # htpasswd.flush
29
+ #
30
+ # config[:UserDB] = htpasswd
31
+ #
32
+ # basic_auth = WEBrick::HTTPAuth::BasicAuth.new config
33
+
16
34
  class BasicAuth
17
35
  include Authenticator
18
36
 
19
- AuthScheme = "Basic"
37
+ AuthScheme = "Basic" # :nodoc:
38
+
39
+ ##
40
+ # Used by UserDB to create a basic password entry
20
41
 
21
42
  def self.make_passwd(realm, user, pass)
22
43
  pass ||= ""
@@ -25,16 +46,31 @@ module WEBrick
25
46
 
26
47
  attr_reader :realm, :userdb, :logger
27
48
 
49
+ ##
50
+ # Creates a new BasicAuth instance.
51
+ #
52
+ # See WEBrick::Config::BasicAuth for default configuration entries
53
+ #
54
+ # You must supply the following configuration entries:
55
+ #
56
+ # :Realm:: The name of the realm being protected.
57
+ # :UserDB:: A database of usernames and passwords.
58
+ # A WEBrick::HTTPAuth::Htpasswd instance should be used.
59
+
28
60
  def initialize(config, default=Config::BasicAuth)
29
61
  check_init(config)
30
62
  @config = default.dup.update(config)
31
63
  end
32
64
 
65
+ ##
66
+ # Authenticates a +req+ and returns a 401 Unauthorized using +res+ if
67
+ # the authentication was not correct.
68
+
33
69
  def authenticate(req, res)
34
70
  unless basic_credentials = check_scheme(req)
35
71
  challenge(req, res)
36
72
  end
37
- userid, password = basic_credentials.unpack("m*")[0].split(":", 2)
73
+ userid, password = basic_credentials.unpack("m*")[0].split(":", 2)
38
74
  password ||= ""
39
75
  if userid.empty?
40
76
  error("user id was not given.")
@@ -52,12 +88,19 @@ module WEBrick
52
88
  req.user = userid
53
89
  end
54
90
 
91
+ ##
92
+ # Returns a challenge response which asks for for authentication
93
+ # information
94
+
55
95
  def challenge(req, res)
56
96
  res[@response_field] = "#{@auth_scheme} realm=\"#{@realm}\""
57
97
  raise @auth_exception
58
98
  end
59
99
  end
60
100
 
101
+ ##
102
+ # Basic authentication for proxy servers. See BasicAuth for details.
103
+
61
104
  class ProxyBasicAuth < BasicAuth
62
105
  include ProxyAuthenticator
63
106
  end
@@ -19,18 +19,70 @@ require 'digest/sha1'
19
19
 
20
20
  module WEBrick
21
21
  module HTTPAuth
22
+
23
+ ##
24
+ # RFC 2617 Digest Access Authentication for WEBrick
25
+ #
26
+ # Use this class to add digest authentication to a WEBrick servlet.
27
+ #
28
+ # Here is an example of how to set up DigestAuth:
29
+ #
30
+ # config = { :Realm => 'DigestAuth example realm' }
31
+ #
32
+ # htdigest = WEBrick::HTTPAuth::Htdigest.new 'my_password_file'
33
+ # htdigest.set_passwd config[:Realm], 'username', 'password'
34
+ # htdigest.flush
35
+ #
36
+ # config[:UserDB] = htdigest
37
+ #
38
+ # digest_auth = WEBrick::HTTPAuth::DigestAuth.new config
39
+ #
40
+ # When using this as with a servlet be sure not to create a new DigestAuth
41
+ # object in the servlet's #initialize. By default WEBrick creates a new
42
+ # servlet instance for every request and the DigestAuth object must be
43
+ # used across requests.
44
+
22
45
  class DigestAuth
23
46
  include Authenticator
24
47
 
25
- AuthScheme = "Digest"
26
- OpaqueInfo = Struct.new(:time, :nonce, :nc)
27
- attr_reader :algorithm, :qop
48
+ AuthScheme = "Digest" # :nodoc:
49
+
50
+ ##
51
+ # Struct containing the opaque portion of the digest authentication
52
+
53
+ OpaqueInfo = Struct.new(:time, :nonce, :nc) # :nodoc:
54
+
55
+ ##
56
+ # Digest authentication algorithm
57
+
58
+ attr_reader :algorithm
59
+
60
+ ##
61
+ # Quality of protection. RFC 2617 defines "auth" and "auth-int"
62
+
63
+ attr_reader :qop
64
+
65
+ ##
66
+ # Used by UserDB to create a digest password entry
28
67
 
29
68
  def self.make_passwd(realm, user, pass)
30
69
  pass ||= ""
31
70
  Digest::MD5::hexdigest([user, realm, pass].join(":"))
32
71
  end
33
72
 
73
+ ##
74
+ # Creates a new DigestAuth instance. Be sure to use the same DigestAuth
75
+ # instance for multiple requests as it saves state between requests in
76
+ # order to perform authentication.
77
+ #
78
+ # See WEBrick::Config::DigestAuth for default configuration entries
79
+ #
80
+ # You must supply the following configuration entries:
81
+ #
82
+ # :Realm:: The name of the realm being protected.
83
+ # :UserDB:: A database of usernames and passwords.
84
+ # A WEBrick::HTTPAuth::Htdigest instance should be used.
85
+
34
86
  def initialize(config, default=Config::DigestAuth)
35
87
  check_init(config)
36
88
  @config = default.dup.update(config)
@@ -44,7 +96,6 @@ module WEBrick
44
96
  @nonce_expire_period = @config[:NonceExpirePeriod]
45
97
  @nonce_expire_delta = @config[:NonceExpireDelta]
46
98
  @internet_explorer_hack = @config[:InternetExplorerHack]
47
- @opera_hack = @config[:OperaHack]
48
99
 
49
100
  case @algorithm
50
101
  when 'MD5','MD5-sess'
@@ -52,7 +103,7 @@ module WEBrick
52
103
  when 'SHA1','SHA1-sess' # it is a bonus feature :-)
53
104
  @h = Digest::SHA1
54
105
  else
55
- msg = format('Alogrithm "%s" is not supported.', @algorithm)
106
+ msg = format('Algorithm "%s" is not supported.', @algorithm)
56
107
  raise ArgumentError.new(msg)
57
108
  end
58
109
 
@@ -62,6 +113,10 @@ module WEBrick
62
113
  @mutex = Mutex.new
63
114
  end
64
115
 
116
+ ##
117
+ # Authenticates a +req+ and returns a 401 Unauthorized using +res+ if
118
+ # the authentication was not correct.
119
+
65
120
  def authenticate(req, res)
66
121
  unless result = @mutex.synchronize{ _authenticate(req, res) }
67
122
  challenge(req, res)
@@ -72,6 +127,10 @@ module WEBrick
72
127
  return true
73
128
  end
74
129
 
130
+ ##
131
+ # Returns a challenge response which asks for for authentication
132
+ # information
133
+
75
134
  def challenge(req, res, stale=false)
76
135
  nonce = generate_next_nonce(req)
77
136
  if @use_opaque
@@ -96,6 +155,8 @@ module WEBrick
96
155
 
97
156
  private
98
157
 
158
+ # :stopdoc:
159
+
99
160
  MustParams = ['username','realm','nonce','uri','response']
100
161
  MustParamsAuth = ['cnonce','nc']
101
162
 
@@ -118,18 +179,17 @@ module WEBrick
118
179
  }
119
180
 
120
181
  if !check_uri(req, auth_req)
121
- raise HTTPStatus::BadRequest
182
+ raise HTTPStatus::BadRequest
122
183
  end
123
184
 
124
- if auth_req['realm'] != @realm
185
+ if auth_req['realm'] != @realm
125
186
  error('%s: realm unmatch. "%s" for "%s"',
126
187
  auth_req['username'], auth_req['realm'], @realm)
127
188
  return false
128
189
  end
129
190
 
130
- auth_req['algorithm'] ||= 'MD5'
131
- if auth_req['algorithm'] != @algorithm &&
132
- (@opera_hack && auth_req['algorithm'] != @algorithm.upcase)
191
+ auth_req['algorithm'] ||= 'MD5'
192
+ if auth_req['algorithm'].upcase != @algorithm.upcase
133
193
  error('%s: algorithm unmatch. "%s" for "%s"',
134
194
  auth_req['username'], auth_req['algorithm'], @algorithm)
135
195
  return false
@@ -165,8 +225,7 @@ module WEBrick
165
225
  nonce_is_invalid = true
166
226
  end
167
227
 
168
- if /-sess$/ =~ auth_req['algorithm'] ||
169
- (@opera_hack && /-SESS$/ =~ auth_req['algorithm'])
228
+ if /-sess$/i =~ auth_req['algorithm']
170
229
  ha1 = hexdigest(password, auth_req['nonce'], auth_req['cnonce'])
171
230
  else
172
231
  ha1 = password
@@ -222,15 +281,15 @@ module WEBrick
222
281
  end
223
282
  }.join(', ')
224
283
  end
225
- info('%s: authentication scceeded.', auth_req['username'])
284
+ info('%s: authentication succeeded.', auth_req['username'])
226
285
  req.user = auth_req['username']
227
286
  return true
228
287
  end
229
288
 
230
289
  def split_param_value(string)
231
290
  ret = {}
232
- while string.size != 0
233
- case string
291
+ while string.bytesize != 0
292
+ case string
234
293
  when /^\s*([\w\-\.\*\%\!]+)=\s*\"((\\.|[^\"])*)\"\s*,?/
235
294
  key = $1
236
295
  matched = $2
@@ -320,7 +379,7 @@ module WEBrick
320
379
  uri = auth_req['uri']
321
380
  if uri != req.request_uri.to_s && uri != req.unparsed_uri &&
322
381
  (@internet_explorer_hack && uri != req.path)
323
- error('%s: uri unmatch. "%s" for "%s"', auth_req['username'],
382
+ error('%s: uri unmatch. "%s" for "%s"', auth_req['username'],
324
383
  auth_req['uri'], req.request_uri.to_s)
325
384
  return false
326
385
  end
@@ -330,12 +389,18 @@ module WEBrick
330
389
  def hexdigest(*args)
331
390
  @h.hexdigest(args.join(":"))
332
391
  end
392
+
393
+ # :startdoc:
333
394
  end
334
395
 
396
+ ##
397
+ # Digest authentication for proxy servers. See DigestAuth for details.
398
+
335
399
  class ProxyDigestAuth < DigestAuth
336
400
  include ProxyAuthenticator
337
401
 
338
- def check_uri(req, auth_req)
402
+ private
403
+ def check_uri(req, auth_req) # :nodoc:
339
404
  return true
340
405
  end
341
406
  end
@@ -13,9 +13,26 @@ require 'tempfile'
13
13
 
14
14
  module WEBrick
15
15
  module HTTPAuth
16
+
17
+ ##
18
+ # Htdigest accesses apache-compatible digest password files. Passwords are
19
+ # matched to a realm where they are valid. For security, the path for a
20
+ # digest password database should be stored outside of the paths available
21
+ # to the HTTP server.
22
+ #
23
+ # Htdigest is intended for use with WEBrick::HTTPAuth::DigestAuth and
24
+ # stores passwords using cryptographic hashes.
25
+ #
26
+ # htpasswd = WEBrick::HTTPAuth::Htdigest.new 'my_password_file'
27
+ # htpasswd.set_passwd 'my realm', 'username', 'password'
28
+ # htpasswd.flush
29
+
16
30
  class Htdigest
17
31
  include UserDB
18
32
 
33
+ ##
34
+ # Open a digest password database at +path+
35
+
19
36
  def initialize(path)
20
37
  @path = path
21
38
  @mtime = Time.at(0)
@@ -26,6 +43,9 @@ module WEBrick
26
43
  reload
27
44
  end
28
45
 
46
+ ##
47
+ # Reloads passwords from the database
48
+
29
49
  def reload
30
50
  mtime = File::mtime(@path)
31
51
  if mtime > @mtime
@@ -44,6 +64,10 @@ module WEBrick
44
64
  end
45
65
  end
46
66
 
67
+ ##
68
+ # Flush the password database. If +output+ is given the database will
69
+ # be written there instead of to the original path.
70
+
47
71
  def flush(output=nil)
48
72
  output ||= @path
49
73
  tmp = Tempfile.new("htpasswd", File::dirname(output))
@@ -56,6 +80,10 @@ module WEBrick
56
80
  end
57
81
  end
58
82
 
83
+ ##
84
+ # Retrieves a password from the database for +user+ in +realm+. If
85
+ # +reload_db+ is true the database will be reloaded first.
86
+
59
87
  def get_passwd(realm, user, reload_db)
60
88
  reload() if reload_db
61
89
  if hash = @digest[realm]
@@ -63,6 +91,9 @@ module WEBrick
63
91
  end
64
92
  end
65
93
 
94
+ ##
95
+ # Sets a password in the database for +user+ in +realm+ to +pass+.
96
+
66
97
  def set_passwd(realm, user, pass)
67
98
  @mutex.synchronize{
68
99
  unless @digest[realm]
@@ -72,13 +103,19 @@ module WEBrick
72
103
  }
73
104
  end
74
105
 
106
+ ##
107
+ # Removes a password from the database for +user+ in +realm+.
108
+
75
109
  def delete_passwd(realm, user)
76
110
  if hash = @digest[realm]
77
111
  hash.delete(user)
78
112
  end
79
113
  end
80
114
 
81
- def each
115
+ ##
116
+ # Iterate passwords in the database.
117
+
118
+ def each # :yields: [user, realm, password_hash]
82
119
  @digest.keys.sort.each{|realm|
83
120
  hash = @digest[realm]
84
121
  hash.keys.sort.each{|user|
@@ -11,7 +11,26 @@ require 'tempfile'
11
11
 
12
12
  module WEBrick
13
13
  module HTTPAuth
14
+
15
+ ##
16
+ # Htgroup accesses apache-compatible group files. Htgroup can be used to
17
+ # provide group-based authentication for users. Currently Htgroup is not
18
+ # directly integrated with any authenticators in WEBrick. For security,
19
+ # the path for a digest password database should be stored outside of the
20
+ # paths available to the HTTP server.
21
+ #
22
+ # Example:
23
+ #
24
+ # htgroup = WEBrick::HTTPAuth::Htgroup.new 'my_group_file'
25
+ # htgroup.add 'superheroes', %w[spiderman batman]
26
+ #
27
+ # htgroup.members('superheroes').include? 'magneto' # => false
28
+
14
29
  class Htgroup
30
+
31
+ ##
32
+ # Open a group database at +path+
33
+
15
34
  def initialize(path)
16
35
  @path = path
17
36
  @mtime = Time.at(0)
@@ -20,6 +39,9 @@ module WEBrick
20
39
  reload
21
40
  end
22
41
 
42
+ ##
43
+ # Reload groups from the database
44
+
23
45
  def reload
24
46
  if (mtime = File::mtime(@path)) > @mtime
25
47
  @group.clear
@@ -34,6 +56,10 @@ module WEBrick
34
56
  end
35
57
  end
36
58
 
59
+ ##
60
+ # Flush the group database. If +output+ is given the database will be
61
+ # written there instead of to the original path.
62
+
37
63
  def flush(output=nil)
38
64
  output ||= @path
39
65
  tmp = Tempfile.new("htgroup", File::dirname(output))
@@ -48,11 +74,17 @@ module WEBrick
48
74
  end
49
75
  end
50
76
 
77
+ ##
78
+ # Retrieve the list of members from +group+
79
+
51
80
  def members(group)
52
81
  reload
53
82
  @group[group] || []
54
83
  end
55
84
 
85
+ ##
86
+ # Add an Array of +members+ to +group+
87
+
56
88
  def add(group, members)
57
89
  @group[group] = members(group) | members
58
90
  end
@@ -13,9 +13,27 @@ require 'tempfile'
13
13
 
14
14
  module WEBrick
15
15
  module HTTPAuth
16
+
17
+ ##
18
+ # Htpasswd accesses apache-compatible password files. Passwords are
19
+ # matched to a realm where they are valid. For security, the path for a
20
+ # password database should be stored outside of the paths available to the
21
+ # HTTP server.
22
+ #
23
+ # Htpasswd is intended for use with WEBrick::HTTPAuth::BasicAuth.
24
+ #
25
+ # To create an Htpasswd database with a single user:
26
+ #
27
+ # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file'
28
+ # htpasswd.set_passwd 'my realm', 'username', 'password'
29
+ # htpasswd.flush
30
+
16
31
  class Htpasswd
17
32
  include UserDB
18
33
 
34
+ ##
35
+ # Open a password database at +path+
36
+
19
37
  def initialize(path)
20
38
  @path = path
21
39
  @mtime = Time.at(0)
@@ -25,6 +43,9 @@ module WEBrick
25
43
  reload
26
44
  end
27
45
 
46
+ ##
47
+ # Reload passwords from the database
48
+
28
49
  def reload
29
50
  mtime = File::mtime(@path)
30
51
  if mtime > @mtime
@@ -35,7 +56,7 @@ module WEBrick
35
56
  case line
36
57
  when %r!\A[^:]+:[a-zA-Z0-9./]{13}\z!
37
58
  user, pass = line.split(":")
38
- when /:\$/, /:\{SHA\}/
59
+ when /:\$/, /:{SHA}/
39
60
  raise NotImplementedError,
40
61
  'MD5, SHA1 .htpasswd file not supported'
41
62
  else
@@ -48,6 +69,10 @@ module WEBrick
48
69
  end
49
70
  end
50
71
 
72
+ ##
73
+ # Flush the password database. If +output+ is given the database will
74
+ # be written there instead of to the original path.
75
+
51
76
  def flush(output=nil)
52
77
  output ||= @path
53
78
  tmp = Tempfile.new("htpasswd", File::dirname(output))
@@ -60,20 +85,33 @@ module WEBrick
60
85
  end
61
86
  end
62
87
 
88
+ ##
89
+ # Retrieves a password from the database for +user+ in +realm+. If
90
+ # +reload_db+ is true the database will be reloaded first.
91
+
63
92
  def get_passwd(realm, user, reload_db)
64
93
  reload() if reload_db
65
94
  @passwd[user]
66
95
  end
67
96
 
97
+ ##
98
+ # Sets a password in the database for +user+ in +realm+ to +pass+.
99
+
68
100
  def set_passwd(realm, user, pass)
69
101
  @passwd[user] = make_passwd(realm, user, pass)
70
102
  end
71
103
 
104
+ ##
105
+ # Removes a password from the database for +user+ in +realm+.
106
+
72
107
  def delete_passwd(realm, user)
73
108
  @passwd.delete(user)
74
109
  end
75
110
 
76
- def each
111
+ ##
112
+ # Iterate passwords in the database.
113
+
114
+ def each # :yields: [user, password]
77
115
  @passwd.keys.sort.each{|user|
78
116
  yield([user, @passwd[user]])
79
117
  }