1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
# frozen_string_literal: true
#
# httpauth/htdigest.rb -- Apache compatible htdigest file
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: htdigest.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
require_relative 'userdb'
require_relative 'digestauth'
require 'tempfile'
module WEBrick
module HTTPAuth
##
# Htdigest accesses apache-compatible digest password files. Passwords are
# matched to a realm where they are valid. For security, the path for a
# digest password database should be stored outside of the paths available
# to the HTTP server.
#
# Htdigest is intended for use with WEBrick::HTTPAuth::DigestAuth and
# stores passwords using cryptographic hashes.
#
# htpasswd = WEBrick::HTTPAuth::Htdigest.new 'my_password_file'
# htpasswd.set_passwd 'my realm', 'username', 'password'
# htpasswd.flush
class Htdigest
include UserDB
##
# Open a digest password database at +path+
def initialize(path)
@path = path
@mtime = Time.at(0)
@digest = Hash.new
@mutex = Thread::Mutex::new
@auth_type = DigestAuth
File.open(@path,"a").close unless File.exist?(@path)
reload
end
##
# Reloads passwords from the database
def reload
mtime = File::mtime(@path)
if mtime > @mtime
@digest.clear
File.open(@path){|io|
while line = io.gets
line.chomp!
user, realm, pass = line.split(/:/, 3)
unless @digest[realm]
@digest[realm] = Hash.new
end
@digest[realm][user] = pass
end
}
@mtime = mtime
end
end
##
# Flush the password database. If +output+ is given the database will
# be written there instead of to the original path.
def flush(output=nil)
output ||= @path
tmp = Tempfile.create("htpasswd", File::dirname(output))
renamed = false
begin
each{|item| tmp.puts(item.join(":")) }
tmp.close
File::rename(tmp.path, output)
renamed = true
ensure
tmp.close
File.unlink(tmp.path) if !renamed
end
end
##
# Retrieves a password from the database for +user+ in +realm+. If
# +reload_db+ is true the database will be reloaded first.
def get_passwd(realm, user, reload_db)
reload() if reload_db
if hash = @digest[realm]
hash[user]
end
end
##
# Sets a password in the database for +user+ in +realm+ to +pass+.
def set_passwd(realm, user, pass)
@mutex.synchronize{
unless @digest[realm]
@digest[realm] = Hash.new
end
@digest[realm][user] = make_passwd(realm, user, pass)
}
end
##
# Removes a password from the database for +user+ in +realm+.
def delete_passwd(realm, user)
if hash = @digest[realm]
hash.delete(user)
end
end
##
# Iterate passwords in the database.
def each # :yields: [user, realm, password_hash]
@digest.keys.sort.each{|realm|
hash = @digest[realm]
hash.keys.sort.each{|user|
yield([user, realm, hash[user]])
}
}
end
end
end
end
|