htauth 2.0.0 → 2.1.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.
- checksums.yaml +5 -5
- data/HISTORY.md +7 -0
- data/Manifest.txt +4 -0
- data/README.md +36 -22
- data/Rakefile +8 -4
- data/lib/htauth.rb +1 -0
- data/lib/htauth/algorithm.rb +32 -29
- data/lib/htauth/bcrypt.rb +35 -0
- data/lib/htauth/cli/digest.rb +1 -1
- data/lib/htauth/cli/passwd.rb +90 -30
- data/lib/htauth/console.rb +5 -1
- data/lib/htauth/crypt.rb +15 -4
- data/lib/htauth/descendant_tracker.rb +46 -0
- data/lib/htauth/md5.rb +27 -7
- data/lib/htauth/passwd_entry.rb +22 -17
- data/lib/htauth/passwd_file.rb +46 -12
- data/lib/htauth/plaintext.rb +11 -4
- data/lib/htauth/sha1.rb +8 -5
- data/lib/htauth/version.rb +1 -1
- data/spec/algorithm_spec.rb +8 -0
- data/spec/bcrypt_spec.rb +33 -0
- data/spec/cli/digest_spec.rb +22 -23
- data/spec/cli/passwd_spec.rb +160 -36
- data/spec/crypt_spec.rb +1 -5
- data/spec/digest_entry_spec.rb +19 -19
- data/spec/digest_file_spec.rb +10 -10
- data/spec/md5_spec.rb +1 -5
- data/spec/passwd_entry_spec.rb +63 -42
- data/spec/passwd_file_spec.rb +31 -13
- data/spec/plaintext_spec.rb +1 -5
- data/spec/sha1_spec.rb +1 -5
- data/tasks/default.rake +3 -3
- data/tasks/this.rb +2 -2
- metadata +32 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1a0330f64106269682197c83a695b2efbb98b05e367ba56a90aef1794cdc27dd
|
4
|
+
data.tar.gz: 4fd5c4452211b4747543941477c65eb83a33ed075d4dd352b1763f00daaabcb5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f5cee2bcf691c57abf009a02a5d383839c1b5ae9f38345785398a258f72936b593e1672f74e62bc10f87124f12b2783b0587c4dfc5d73b413391d996824d0b1
|
7
|
+
data.tar.gz: c583805c7ee4a2f33d7297df69dc118d8abc60b91ece304c04b84c2e873bed1c8abd8dff31624a8fffd32fe008e22dcc38aa47677dd7bae143f32eba73a02287
|
data/HISTORY.md
CHANGED
@@ -1,4 +1,11 @@
|
|
1
1
|
# Changelog
|
2
|
+
## Version 2.1.0 - 2020-04-02
|
3
|
+
|
4
|
+
* Update minimum ruby versions to modern versions
|
5
|
+
* Support bcrypt password entries [#12](https://github.com/copiousfreetime/htauth/issues/12)
|
6
|
+
* Support authentication at the password file level
|
7
|
+
* implement --verify commandline option (-v in apache htpasswd)
|
8
|
+
* implement --stdin commandline option (-i in apache htpasswd)
|
2
9
|
|
3
10
|
## Version 2.0.0 - 2015-09-13
|
4
11
|
|
data/Manifest.txt
CHANGED
@@ -8,11 +8,13 @@ bin/htdigest-ruby
|
|
8
8
|
bin/htpasswd-ruby
|
9
9
|
lib/htauth.rb
|
10
10
|
lib/htauth/algorithm.rb
|
11
|
+
lib/htauth/bcrypt.rb
|
11
12
|
lib/htauth/cli.rb
|
12
13
|
lib/htauth/cli/digest.rb
|
13
14
|
lib/htauth/cli/passwd.rb
|
14
15
|
lib/htauth/console.rb
|
15
16
|
lib/htauth/crypt.rb
|
17
|
+
lib/htauth/descendant_tracker.rb
|
16
18
|
lib/htauth/digest_entry.rb
|
17
19
|
lib/htauth/digest_file.rb
|
18
20
|
lib/htauth/entry.rb
|
@@ -24,6 +26,8 @@ lib/htauth/passwd_file.rb
|
|
24
26
|
lib/htauth/plaintext.rb
|
25
27
|
lib/htauth/sha1.rb
|
26
28
|
lib/htauth/version.rb
|
29
|
+
spec/algorithm_spec.rb
|
30
|
+
spec/bcrypt_spec.rb
|
27
31
|
spec/cli/digest_spec.rb
|
28
32
|
spec/cli/passwd_spec.rb
|
29
33
|
spec/crypt_spec.rb
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
## HTAuth
|
2
2
|
|
3
3
|
* [Github](http://github.com/copiousfreetime/htauth/tree/master)
|
4
|
-
*
|
4
|
+
* [](https://travis-ci.org/copiousfreetime/htauth)
|
5
5
|
|
6
6
|
## DESCRIPTION
|
7
7
|
|
@@ -26,32 +26,38 @@ Additionally, you can access all the functionality of *htdigest-ruby* and
|
|
26
26
|
|
27
27
|
### htpasswd-ruby command line application
|
28
28
|
|
29
|
-
|
30
29
|
Usage:
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
30
|
+
htpasswd-ruby [-cimBdpsD] [-C cost] passwordfile username
|
31
|
+
htpasswd-ruby -b[cmBdpsD] [-C cost] passwordfile username password
|
32
|
+
|
33
|
+
htpasswd-ruby -n[imBdps] [-C cost] username
|
34
|
+
htpasswd-ruby -nb[mBdps] [-C cost] username password
|
35
|
+
|
36
|
+
-b, --batch Batch mode, get the password from the command line, rather than prompt
|
37
|
+
-B, --bcrypt Force bcrypt encryption of the password.
|
38
|
+
-C, --cost COST Set the computing time used for the bcrypt algorithm
|
39
|
+
(higher is more secure but slower, default: 5, valid: 4 to 31).
|
40
|
+
-c, --create Create a new file; this overwrites an existing file.
|
41
|
+
-d, --crypt Force CRYPT encryption of the password.
|
42
|
+
-D, --delete Delete the specified user.
|
43
|
+
-h, --help Display this help.
|
44
|
+
-i, --stdin Read the passwod from stdin without verivication (for script usage).
|
45
|
+
-m, --md5 Force MD5 encryption of the password (default).
|
46
|
+
-n, --stdout Do not update the file; Display the results on stdout instead.
|
47
|
+
-p, --plaintext Do not encrypt the password (plaintext).
|
48
|
+
-s, --sha1 Force SHA encryption of the password.
|
49
|
+
-v, --version Show version info.
|
50
|
+
--verify Verify password for the specified user
|
51
|
+
|
52
|
+
The SHA algorihtm does not use a salt and is less secure than the MD5 algorithm.
|
47
53
|
|
48
54
|
### htdigest-ruby command line application
|
49
55
|
|
50
56
|
Usage: htdigest-ruby [options] passwordfile realm username
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
57
|
+
-c, --create Create a new digest password file; this overwrites an existing file.
|
58
|
+
-D, --delete Delete the specified user.
|
59
|
+
-h, --help Display this help.
|
60
|
+
-v, --version Show version info.
|
55
61
|
|
56
62
|
### API Usage
|
57
63
|
|
@@ -65,6 +71,14 @@ Additionally, you can access all the functionality of *htdigest-ruby* and
|
|
65
71
|
pf.add('someotheruser', 'a different password', 'sha1')
|
66
72
|
end
|
67
73
|
|
74
|
+
HTAuth::PasswdFile.open("some.htpasswd", HTAuth::File::ALTER) do |pf|
|
75
|
+
pf.update('someuser', 'a password', 'bcrypt')
|
76
|
+
end
|
77
|
+
|
78
|
+
HTAuth::PasswdFile.open("some.htpasswd") do |pf|
|
79
|
+
pf.authenticated?('someuser', 'a password')
|
80
|
+
end
|
81
|
+
|
68
82
|
## CREDITS
|
69
83
|
|
70
84
|
* [The Apache Software Foundation](http://www.apache.org/)
|
data/Rakefile
CHANGED
@@ -7,10 +7,14 @@ This.email = "jeremy@copiousfreetime.org"
|
|
7
7
|
This.homepage = "http://github.com/copiousfreetime/#{ This.name }"
|
8
8
|
|
9
9
|
This.ruby_gemspec do |spec|
|
10
|
-
spec.
|
11
|
-
|
12
|
-
spec.add_development_dependency( '
|
13
|
-
spec.add_development_dependency( '
|
10
|
+
spec.add_dependency( 'bcrypt', '~> 3.1' )
|
11
|
+
|
12
|
+
spec.add_development_dependency( 'rake' , '~> 13.0')
|
13
|
+
spec.add_development_dependency( 'minitest' , '~> 5.5' )
|
14
|
+
spec.add_development_dependency( 'rdoc' , '~> 6.2' )
|
15
|
+
spec.add_development_dependency( 'simplecov', '~> 0.17' )
|
16
|
+
|
17
|
+
spec.license = "MIT"
|
14
18
|
end
|
15
19
|
|
16
20
|
load 'tasks/default.rake'
|
data/lib/htauth.rb
CHANGED
data/lib/htauth/algorithm.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'htauth/error'
|
2
|
+
require 'htauth/descendant_tracker'
|
2
3
|
require 'securerandom'
|
3
4
|
module HTAuth
|
4
5
|
class InvalidAlgorithmError < Error; end
|
@@ -7,8 +8,13 @@ module HTAuth
|
|
7
8
|
#
|
8
9
|
class Algorithm
|
9
10
|
|
11
|
+
extend DescendantTracker
|
12
|
+
|
10
13
|
SALT_CHARS = (%w[ . / ] + ("0".."9").to_a + ('A'..'Z').to_a + ('a'..'z').to_a).freeze
|
14
|
+
SALT_LENGTH = 8
|
11
15
|
|
16
|
+
# Public: flag for the bcrypt algorithm
|
17
|
+
BCRYPT = "bcrypt".freeze
|
12
18
|
# Public: flag for the md5 algorithm
|
13
19
|
MD5 = "md5".freeze
|
14
20
|
# Public: flag for the sha1 algorithm
|
@@ -26,34 +32,36 @@ module HTAuth
|
|
26
32
|
|
27
33
|
|
28
34
|
class << self
|
29
|
-
def
|
30
|
-
|
31
|
-
sub_klasses[a_name.downcase].new(params)
|
35
|
+
def algorithm_name
|
36
|
+
self.name.split("::").last.downcase
|
32
37
|
end
|
33
38
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
elsif password_field.index(sub_klasses[MD5].new.prefix) then
|
40
|
-
p = password_field.split("$")
|
41
|
-
matches << sub_klasses[MD5].new( :salt => p[2] )
|
42
|
-
else
|
43
|
-
matches << sub_klasses[PLAINTEXT].new
|
44
|
-
matches << sub_klasses[CRYPT].new( :salt => password_field[0,2] )
|
39
|
+
def algorithm_from_name(a_name, params = {})
|
40
|
+
found = children.find { |c| c.algorithm_name == a_name }
|
41
|
+
if !found then
|
42
|
+
names = children.map { |c| c.algorithm_name }
|
43
|
+
raise InvalidAlgorithmError, "`#{a_name}' is an unknown encryption algorithm, use one of #{names.join(', ')}"
|
45
44
|
end
|
46
|
-
|
47
|
-
return matches
|
45
|
+
return found.new(params)
|
48
46
|
end
|
49
47
|
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
# NOTE: if it is plaintext, and the length is 13 - it may matched crypt
|
49
|
+
# and be tested that way. If that is the case - this is explicitly
|
50
|
+
# siding with crypt() as you shouldn't be using plaintext. Or
|
51
|
+
# crypt for that matter.
|
52
|
+
def algorithm_from_field(password_field)
|
53
|
+
match = find_child(:handles?, password_field)
|
54
|
+
match = ::HTAuth::Plaintext if match.nil? && ::HTAuth::Plaintext.entry_matches?(password_field)
|
55
|
+
|
56
|
+
raise InvalidAlgorithmError, "unknown encryption algorithm used for `#{password_field}`" if match.nil?
|
57
|
+
|
58
|
+
return match.new(:existing => password_field)
|
53
59
|
end
|
54
60
|
|
55
|
-
|
56
|
-
|
61
|
+
# Internal: Does this class handle this type of password entry
|
62
|
+
#
|
63
|
+
def handles?(password_entry)
|
64
|
+
raise NotImplementedError, "#{self.name} must implement #{self.name}.handles?(password_entry)"
|
57
65
|
end
|
58
66
|
|
59
67
|
# Internal: Constant time string comparison.
|
@@ -75,20 +83,15 @@ module HTAuth
|
|
75
83
|
end
|
76
84
|
end
|
77
85
|
|
78
|
-
# Internal
|
79
|
-
def prefix ; end
|
80
|
-
|
81
86
|
# Internal
|
82
87
|
def encode(password) ; end
|
83
88
|
|
84
89
|
# Internal: 8 bytes of random items from SALT_CHARS
|
85
|
-
def gen_salt
|
86
|
-
|
87
|
-
8.times { chars << SALT_CHARS[SecureRandom.random_number(SALT_CHARS.size)] }
|
88
|
-
chars.join('')
|
90
|
+
def gen_salt(length = SALT_LENGTH)
|
91
|
+
Array.new(length) { SALT_CHARS.sample }.join('')
|
89
92
|
end
|
90
93
|
|
91
|
-
# Internal: this is not the Base64 encoding, this is the to64()
|
94
|
+
# Internal: this is not the Base64 encoding, this is the to64()
|
92
95
|
# method from the apache protable runtime library
|
93
96
|
def to_64(number, rounds)
|
94
97
|
r = StringIO.new
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'htauth/algorithm'
|
2
|
+
require 'bcrypt'
|
3
|
+
|
4
|
+
module HTAuth
|
5
|
+
# Internal: an implementation of the Bcrypt based encoding algorithm
|
6
|
+
# as used in the apache htpasswd -B option
|
7
|
+
|
8
|
+
class Bcrypt < Algorithm
|
9
|
+
|
10
|
+
attr_accessor :cost
|
11
|
+
|
12
|
+
DEFAULT_APACHE_COST = 5 # this is the default cost from htpasswd
|
13
|
+
|
14
|
+
def self.handles?(password_entry)
|
15
|
+
return ::BCrypt::Password.valid_hash?(password_entry)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.extract_cost_from_existing_password_field(existing)
|
19
|
+
password = ::BCrypt::Password.new(existing)
|
20
|
+
password.cost
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(params = {})
|
24
|
+
if existing = (params['existing'] || params[:existing]) then
|
25
|
+
@cost = self.class.extract_cost_from_existing_password_field(existing)
|
26
|
+
else
|
27
|
+
@cost = params['cost'] || params[:cost] || DEFAULT_APACHE_COST
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def encode(password)
|
32
|
+
::BCrypt::Password.create(password, :cost => cost)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/htauth/cli/digest.rb
CHANGED
@@ -37,7 +37,7 @@ module HTAuth
|
|
37
37
|
|
38
38
|
def option_parser
|
39
39
|
if not @option_parser then
|
40
|
-
@option_parser = OptionParser.new do |op|
|
40
|
+
@option_parser = OptionParser.new(nil, 14) do |op|
|
41
41
|
op.banner = "Usage: #{op.program_name} [options] passwordfile realm username"
|
42
42
|
op.on("-c", "--create", "Create a new digest password file; this overwrites an existing file.") do |c|
|
43
43
|
options.file_mode = DigestFile::CREATE
|
data/lib/htauth/cli/passwd.rb
CHANGED
@@ -27,26 +27,28 @@ module HTAuth
|
|
27
27
|
@options.file_mode = File::ALTER
|
28
28
|
@options.passwdfile = nil
|
29
29
|
@options.algorithm = Algorithm::EXISTING
|
30
|
+
@options.algorithm_args = {}
|
31
|
+
@options.read_stdin_once= false
|
30
32
|
@options.send_to_stdout = false
|
31
33
|
@options.show_version = false
|
32
34
|
@options.show_help = false
|
33
35
|
@options.username = nil
|
34
|
-
@options.delete_entry = false
|
35
36
|
@options.password = ""
|
37
|
+
@options.operation = []
|
36
38
|
end
|
37
39
|
@options
|
38
40
|
end
|
39
41
|
|
40
42
|
def option_parser
|
41
43
|
if not @option_parser then
|
42
|
-
@option_parser = OptionParser.new do |op|
|
44
|
+
@option_parser = OptionParser.new(nil, 16) do |op|
|
43
45
|
op.banner = <<-EOB
|
44
|
-
Usage:
|
45
|
-
|
46
|
-
|
46
|
+
Usage:
|
47
|
+
#{op.program_name} [-cimBdpsD] [-C cost] passwordfile username
|
48
|
+
#{op.program_name} -b[cmBdpsD] [-C cost] passwordfile username password
|
47
49
|
|
48
|
-
|
49
|
-
|
50
|
+
#{op.program_name} -n[imBdps] [-C cost] username
|
51
|
+
#{op.program_name} -nb[mBdps] [-C cost] username password
|
50
52
|
EOB
|
51
53
|
|
52
54
|
op.separator ""
|
@@ -55,8 +57,27 @@ Usage:
|
|
55
57
|
options.batch_mode = b
|
56
58
|
end
|
57
59
|
|
60
|
+
op.on("-B", "--bcrypt", "Force bcrypt encryption of the password.") do |b|
|
61
|
+
options.algorithm = Algorithm::BCRYPT
|
62
|
+
end
|
63
|
+
|
64
|
+
op.on("-CCOST", "--cost COST", "Set the computing time used for the bcrypt algorithm",
|
65
|
+
"(higher is more secure but slower, default: 5, valid: 4 to 31).") do |c|
|
66
|
+
if c !~ /\A\d+\z/ then
|
67
|
+
raise ::OptionParser::ParseError, "the bcrypt cost must be an integer from 4 to 31, `#{c}` is invalid"
|
68
|
+
end
|
69
|
+
|
70
|
+
cost = c.to_i
|
71
|
+
if (4..31).include?(cost)
|
72
|
+
options.algorithm_args = { :cost => cost }
|
73
|
+
else
|
74
|
+
raise ::OptionParser::ParseError, "the bcrypt cost must be an integer from 4 to 31, `#{c}` is invalid"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
58
78
|
op.on("-c", "--create", "Create a new file; this overwrites an existing file.") do |c|
|
59
79
|
options.file_mode = HTAuth::File::CREATE
|
80
|
+
options.operation << :add_or_update
|
60
81
|
end
|
61
82
|
|
62
83
|
op.on("-d", "--crypt", "Force CRYPT encryption of the password.") do |c|
|
@@ -64,13 +85,17 @@ Usage:
|
|
64
85
|
end
|
65
86
|
|
66
87
|
op.on("-D", "--delete", "Delete the specified user.") do |d|
|
67
|
-
options.
|
88
|
+
options.operation << :delete
|
68
89
|
end
|
69
90
|
|
70
91
|
op.on("-h", "--help", "Display this help.") do |h|
|
71
92
|
options.show_help = h
|
72
93
|
end
|
73
94
|
|
95
|
+
op.on("-i", "--stdin", "Read the passwod from stdin without verivication (for script usage).") do |i|
|
96
|
+
options.read_stdin_once = true
|
97
|
+
end
|
98
|
+
|
74
99
|
op.on("-m", "--md5", "Force MD5 encryption of the password (default).") do |m|
|
75
100
|
options.algorithm = Algorithm::MD5
|
76
101
|
end
|
@@ -78,6 +103,7 @@ Usage:
|
|
78
103
|
op.on("-n", "--stdout", "Do not update the file; Display the results on stdout instead.") do |n|
|
79
104
|
options.send_to_stdout = true
|
80
105
|
options.passwdfile = HTAuth::File::STDOUT_FLAG
|
106
|
+
options.operation << :stdout
|
81
107
|
end
|
82
108
|
|
83
109
|
op.on("-p", "--plaintext", "Do not encrypt the password (plaintext).") do |p|
|
@@ -92,9 +118,13 @@ Usage:
|
|
92
118
|
options.show_version = v
|
93
119
|
end
|
94
120
|
|
121
|
+
op.on("--verify", "Verify password for the specified user") do |v|
|
122
|
+
options.operation << :verify
|
123
|
+
end
|
124
|
+
|
95
125
|
op.separator ""
|
96
126
|
|
97
|
-
op.separator "The SHA algorihtm does not use a salt and is less secure than the MD5 algorithm"
|
127
|
+
op.separator "The SHA algorihtm does not use a salt and is less secure than the MD5 algorithm."
|
98
128
|
end
|
99
129
|
end
|
100
130
|
@option_parser
|
@@ -114,14 +144,17 @@ Usage:
|
|
114
144
|
begin
|
115
145
|
option_parser.parse!(argv)
|
116
146
|
show_version if options.show_version
|
117
|
-
show_help if options.show_help
|
147
|
+
show_help if options.show_help
|
118
148
|
|
149
|
+
raise ::OptionParser::ParseError, "only one of --create, --stdout, --verify, --delete may be specified" if options.operation.size > 1
|
119
150
|
raise ::OptionParser::ParseError, "Unable to send to stdout AND create a new file" if options.send_to_stdout and (options.file_mode == File::CREATE)
|
120
151
|
raise ::OptionParser::ParseError, "a username is needed" if options.send_to_stdout and argv.size < 1
|
121
152
|
raise ::OptionParser::ParseError, "a username and password are needed" if options.send_to_stdout and options.batch_mode and ( argv.size < 2 )
|
122
153
|
raise ::OptionParser::ParseError, "a passwordfile, username and password are needed " if not options.send_to_stdout and options.batch_mode and ( argv.size < 3 )
|
123
154
|
raise ::OptionParser::ParseError, "a passwordfile and username are needed" if argv.size < 2
|
155
|
+
raise ::OptionParser::ParseError, "options -i and -b are mutually exclusive" if options.batch_mode && options.read_stdin_once
|
124
156
|
|
157
|
+
options.operation = options.operation.shift || :add_or_update
|
125
158
|
options.passwdfile = argv.shift unless options.send_to_stdout
|
126
159
|
options.username = argv.shift
|
127
160
|
options.password = argv.shift if options.batch_mode
|
@@ -133,33 +166,60 @@ Usage:
|
|
133
166
|
end
|
134
167
|
end
|
135
168
|
|
169
|
+
def fetch_password(width=20)
|
170
|
+
return options.password if options.batch_mode
|
171
|
+
console = Console.new
|
172
|
+
if options.read_stdin_once then
|
173
|
+
pw_in = console.read_answer
|
174
|
+
return pw_in
|
175
|
+
end
|
176
|
+
|
177
|
+
case options.operation
|
178
|
+
when :verify
|
179
|
+
pw_in = console.ask("Enter password: ".rjust(width))
|
180
|
+
raise PasswordError, "password '#{pw_in}' too long" if pw_in.length >= MAX_PASSWD_LENGTH
|
181
|
+
when :add_or_update
|
182
|
+
pw_in = console.ask("New password: ".rjust(width))
|
183
|
+
raise PasswordError, "password '#{pw_in}' too long" if pw_in.length >= MAX_PASSWD_LENGTH
|
184
|
+
|
185
|
+
pw_validate = console.ask("Re-type new password: ".rjust(width))
|
186
|
+
raise PasswordError, "They don't match, sorry." unless pw_in == pw_validate
|
187
|
+
end
|
188
|
+
|
189
|
+
return pw_in
|
190
|
+
end
|
191
|
+
|
136
192
|
def run(argv, env = ENV)
|
137
193
|
begin
|
138
194
|
parse_options(argv)
|
195
|
+
console = Console.new
|
139
196
|
passwd_file = PasswdFile.new(options.passwdfile, options.file_mode)
|
140
|
-
|
141
|
-
|
197
|
+
case options.operation
|
198
|
+
when :delete
|
142
199
|
passwd_file.delete(options.username)
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
pw_validate = console.ask("Re-type new password: ")
|
155
|
-
raise PasswordError, "They don't match, sorry." unless pw_in == pw_validate
|
156
|
-
options.password = pw_in
|
200
|
+
passwd_file.save!
|
201
|
+
when :verify
|
202
|
+
if passwd_file.has_entry?(options.username) then
|
203
|
+
pw_in = fetch_password
|
204
|
+
if passwd_file.authenticated?(options.username, pw_in) then
|
205
|
+
$stderr.puts "Password for user #{options.username} correct."
|
206
|
+
else
|
207
|
+
raise HTAuth::Error, "Password verification for user #{options.username} failed."
|
208
|
+
end
|
209
|
+
else
|
210
|
+
raise HTAuth::Error, "User #{options.username} not found"
|
157
211
|
end
|
158
|
-
|
212
|
+
when :add_or_update
|
213
|
+
options.password = fetch_password
|
214
|
+
action = passwd_file.has_entry?(options.username) ? "Changing" : "Adding"
|
215
|
+
console.say "#{action} password for #{options.username}."
|
216
|
+
passwd_file.add_or_update(options.username, options.password, options.algorithm, options.algorithm_args)
|
217
|
+
passwd_file.save!
|
218
|
+
when :stdout
|
219
|
+
options.password = fetch_password
|
220
|
+
passwd_file.add_or_update(options.username, options.password, options.algorithm, options.algorithm_args)
|
221
|
+
passwd_file.save!
|
159
222
|
end
|
160
|
-
|
161
|
-
passwd_file.save!
|
162
|
-
|
163
223
|
rescue HTAuth::FileAccessError => fae
|
164
224
|
msg = "Password file failure (#{options.passwdfile}) "
|
165
225
|
$stderr.puts "#{msg}: #{fae.message}"
|