Class: WEBrick::HTTPAuth::Htpasswd

Inherits:
Object
  • Object
show all
Includes:
UserDB
Defined in:
lib/webrick/httpauth/htpasswd.rb

Overview

Htpasswd accesses apache-compatible password files. Passwords are matched to a realm where they are valid. For security, the path for a password database should be stored outside of the paths available to the HTTP server.

Htpasswd is intended for use with WEBrick::HTTPAuth::BasicAuth.

To create an Htpasswd database with a single user:

htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file'
htpasswd.set_passwd 'my realm', 'username', 'password'
htpasswd.flush

Instance Attribute Summary

Attributes included from UserDB

#auth_type

Instance Method Summary collapse

Methods included from UserDB

#make_passwd

Constructor Details

#initialize(path, password_hash: nil) ⇒ Htpasswd

Open a password database at path



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
# File 'lib/webrick/httpauth/htpasswd.rb', line 38

def initialize(path, password_hash: nil)
  @path = path
  @mtime = Time.at(0)
  @passwd = Hash.new
  @auth_type = BasicAuth
  @password_hash = password_hash

  case @password_hash
  when nil
    # begin
    #   require "string/crypt"
    # rescue LoadError
    #   warn("Unable to load string/crypt, proceeding with deprecated use of String#crypt, consider using password_hash: :bcrypt")
    # end
    @password_hash = :crypt
  when :crypt
    # require "string/crypt"
  when :bcrypt
    require "bcrypt"
  else
    raise ArgumentError, "only :crypt and :bcrypt are supported for password_hash keyword argument"
  end

  File.open(@path,"a").close unless File.exist?(@path)
  reload
end

Instance Method Details

#delete_passwd(realm, user) ⇒ Object

Removes a password from the database for user in realm.



144
145
146
# File 'lib/webrick/httpauth/htpasswd.rb', line 144

def delete_passwd(realm, user)
  @passwd.delete(user)
end

#eachObject

Iterate passwords in the database.



151
152
153
154
155
# File 'lib/webrick/httpauth/htpasswd.rb', line 151

def each # :yields: [user, password]
  @passwd.keys.sort.each{|user|
    yield([user, @passwd[user]])
  }
end

#flush(output = nil) ⇒ Object

Flush the password database. If output is given the database will be written there instead of to the original path.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/webrick/httpauth/htpasswd.rb', line 103

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

#get_passwd(realm, user, reload_db) ⇒ Object

Retrieves a password from the database for user in realm. If reload_db is true the database will be reloaded first.



122
123
124
125
# File 'lib/webrick/httpauth/htpasswd.rb', line 122

def get_passwd(realm, user, reload_db)
  reload() if reload_db
  @passwd[user]
end

#reloadObject

Reload passwords from the database



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
# File 'lib/webrick/httpauth/htpasswd.rb', line 68

def reload
  mtime = File::mtime(@path)
  if mtime > @mtime
    @passwd.clear
    File.open(@path){|io|
      while line = io.gets
        line.chomp!
        case line
        when %r!\A[^:]+:[a-zA-Z0-9./]{13}\z!
          if @password_hash == :bcrypt
            raise StandardError, ".htpasswd file contains crypt password, only bcrypt passwords supported"
          end
          user, pass = line.split(":", 2)
        when %r!\A[^:]+:\$2[aby]\$\d{2}\$.{53}\z!
          if @password_hash == :crypt
            raise StandardError, ".htpasswd file contains bcrypt password, only crypt passwords supported"
          end
          user, pass = line.split(":", 2)
        when /:\$/, /:{SHA}/
          raise NotImplementedError,
                'MD5, SHA1 .htpasswd file not supported'
        else
          raise StandardError, 'bad .htpasswd file'
        end
        @passwd[user] = pass
      end
    }
    @mtime = mtime
  end
end

#set_passwd(realm, user, pass) ⇒ Object

Sets a password in the database for user in realm to pass.



130
131
132
133
134
135
136
137
138
139
# File 'lib/webrick/httpauth/htpasswd.rb', line 130

def set_passwd(realm, user, pass)
  if @password_hash == :bcrypt
    # Cost of 5 to match Apache default, and because the
    # bcrypt default of 10 will introduce significant delays
    # for every request.
    @passwd[user] = BCrypt::Password.create(pass, :cost=>5)
  else
    @passwd[user] = make_passwd(realm, user, pass)
  end
end