htauth 2.1.1 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/HISTORY.md +12 -0
- data/Manifest.txt +2 -0
- data/README.md +55 -33
- data/Rakefile +7 -4
- data/bin/htdigest-ruby +1 -1
- data/bin/htpasswd-ruby +1 -1
- data/lib/htauth/algorithm.rb +15 -2
- data/lib/htauth/argon2.rb +77 -0
- data/lib/htauth/bcrypt.rb +5 -0
- data/lib/htauth/cli/digest.rb +1 -4
- data/lib/htauth/cli/passwd.rb +9 -7
- data/lib/htauth/cli.rb +1 -2
- data/lib/htauth/digest_entry.rb +1 -0
- data/lib/htauth/passwd_entry.rb +4 -15
- data/lib/htauth/passwd_file.rb +6 -6
- data/lib/htauth/sha1.rb +1 -1
- data/lib/htauth/version.rb +1 -1
- data/lib/htauth.rb +1 -1
- data/spec/algorithm_spec.rb +0 -1
- data/spec/argon2_spec.rb +28 -0
- data/spec/bcrypt_spec.rb +0 -1
- data/spec/cli/passwd_spec.rb +17 -1
- data/spec/crypt_spec.rb +0 -1
- data/spec/digest_entry_spec.rb +0 -1
- data/spec/digest_file_spec.rb +0 -1
- data/spec/md5_spec.rb +1 -3
- data/spec/passwd_entry_spec.rb +15 -2
- data/spec/sha1_spec.rb +0 -1
- data/spec/spec_helper.rb +3 -6
- data/tasks/default.rake +22 -14
- data/tasks/this.rb +2 -2
- metadata +64 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad5523ccfeacb957acb9cf2fae40e19e51bc92e10d093a950949fbe00bb50b1a
|
4
|
+
data.tar.gz: 280ed439110fd03ca5510ae81d8b17a4f0db21f4f0aed7360238bbcef83fe6c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc5ddd224ce189eeb5d16a60059a9fcab868ff65de9aad4efac3177425004d98b22d9af29f556d001ae0c083b31cdaef4cf723c7d1342739fc01c7be12841f42
|
7
|
+
data.tar.gz: 28a12fef7f3c7a96c9bdb186a71b06b8263da2c1f6b77dfcba45239129a17311d9fbcfa799aaf004efbf09876cc380de99bb91cab6be23ffefc23ce70286618b
|
data/HISTORY.md
CHANGED
@@ -1,4 +1,16 @@
|
|
1
1
|
# Changelog
|
2
|
+
## Version 2.3.0 - 2024-02-03
|
3
|
+
|
4
|
+
* Add support for argon2 encryption [#18](https://github.com/copiousfreetime/htauth/pull/18)
|
5
|
+
* Update supported ruby version, now supporting ruby 3.x only
|
6
|
+
* Semaphore updates
|
7
|
+
|
8
|
+
## Version 2.2.0 - 2023-02-06
|
9
|
+
|
10
|
+
* Update ruby versions
|
11
|
+
* Add to sempahore for CI
|
12
|
+
* fix various spelling issues
|
13
|
+
|
2
14
|
## Version 2.1.1 - 2020-04-02
|
3
15
|
|
4
16
|
* Update minimum ruby versions to modern versions
|
data/Manifest.txt
CHANGED
@@ -8,6 +8,7 @@ bin/htdigest-ruby
|
|
8
8
|
bin/htpasswd-ruby
|
9
9
|
lib/htauth.rb
|
10
10
|
lib/htauth/algorithm.rb
|
11
|
+
lib/htauth/argon2.rb
|
11
12
|
lib/htauth/bcrypt.rb
|
12
13
|
lib/htauth/cli.rb
|
13
14
|
lib/htauth/cli/digest.rb
|
@@ -27,6 +28,7 @@ lib/htauth/plaintext.rb
|
|
27
28
|
lib/htauth/sha1.rb
|
28
29
|
lib/htauth/version.rb
|
29
30
|
spec/algorithm_spec.rb
|
31
|
+
spec/argon2_spec.rb
|
30
32
|
spec/bcrypt_spec.rb
|
31
33
|
spec/cli/digest_spec.rb
|
32
34
|
spec/cli/passwd_spec.rb
|
data/README.md
CHANGED
@@ -1,29 +1,51 @@
|
|
1
1
|
## HTAuth
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
[![Build Status](https://copiousfreetime.semaphoreci.com/badges/htauth/branches/main.svg)](https://copiousfreetime.semaphoreci.com/projects/htauth)
|
4
|
+
|
5
|
+
* [Homepage](http://github.com/copiousfreetime/htauth)
|
6
|
+
* [Github](http://github.com/copiousfreetime/htauth)
|
5
7
|
|
6
8
|
## DESCRIPTION
|
7
9
|
|
8
|
-
HTAuth
|
9
|
-
htpasswd
|
10
|
-
htpasswd files.
|
10
|
+
HTAuth provides an API and commandline tools for managing Apache/httpd style
|
11
|
+
htpasswd and htdigest files.
|
11
12
|
|
12
13
|
## FEATURES
|
13
14
|
|
14
|
-
HTAuth provides
|
15
|
-
|
16
|
-
|
15
|
+
HTAuth provides an API allowing direct manipulation of Apache/httpd style
|
16
|
+
`htdigest` and `htpasswd` files. Supporting full programmatic manipulation and
|
17
|
+
authentication of user credentials.
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
HTAuth also includes drop-in, commandline compatible replacements for the Apache
|
20
|
+
utilities `htpasswd` and `htdigest` with the respective `htpasswd-ruby` and
|
21
|
+
`htdigest-ruby` commands.
|
21
22
|
|
22
|
-
Additionally,
|
23
|
-
|
23
|
+
Additionally, support for the [argon2](https://github.com/technion/ruby-argon2)
|
24
|
+
password hashing algorithm is provided for most platforms.
|
24
25
|
|
25
26
|
## SYNOPSIS
|
26
27
|
|
28
|
+
### API Usage
|
29
|
+
|
30
|
+
HTAuth::DigestFile.open("some.htdigest") do |df|
|
31
|
+
df.add_or_update('someuser', 'myrealm', 'a password')
|
32
|
+
df.delete('someolduser', 'myotherrealm')
|
33
|
+
end
|
34
|
+
|
35
|
+
HTAuth::PasswdFile.open("some.htpasswd", HTAuth::File::CREATE) do |pf|
|
36
|
+
pf.add('someuser', 'a password', 'md5')
|
37
|
+
pf.add('someotheruser', 'a different password', 'sha1')
|
38
|
+
end
|
39
|
+
|
40
|
+
HTAuth::PasswdFile.open("some.htpasswd", HTAuth::File::ALTER) do |pf|
|
41
|
+
pf.update('someuser', 'a password', 'bcrypt')
|
42
|
+
end
|
43
|
+
|
44
|
+
HTAuth::PasswdFile.open("some.htpasswd") do |pf|
|
45
|
+
pf.authenticated?('someuser', 'a password')
|
46
|
+
end
|
47
|
+
|
48
|
+
|
27
49
|
### htpasswd-ruby command line application
|
28
50
|
|
29
51
|
Usage:
|
@@ -33,7 +55,8 @@ Additionally, you can access all the functionality of *htdigest-ruby* and
|
|
33
55
|
htpasswd-ruby -n[imBdps] [-C cost] username
|
34
56
|
htpasswd-ruby -nb[mBdps] [-C cost] username password
|
35
57
|
|
36
|
-
|
58
|
+
--argon2 Force argon2 encryption of the password.
|
59
|
+
-b, --batch Batch mode, get the password from the command line, rather than prompt.
|
37
60
|
-B, --bcrypt Force bcrypt encryption of the password.
|
38
61
|
-C, --cost COST Set the computing time used for the bcrypt algorithm
|
39
62
|
(higher is more secure but slower, default: 5, valid: 4 to 31).
|
@@ -47,9 +70,7 @@ Additionally, you can access all the functionality of *htdigest-ruby* and
|
|
47
70
|
-p, --plaintext Do not encrypt the password (plaintext).
|
48
71
|
-s, --sha1 Force SHA encryption of the password.
|
49
72
|
-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.
|
73
|
+
--verify Verify password for the specified user.
|
53
74
|
|
54
75
|
### htdigest-ruby command line application
|
55
76
|
|
@@ -59,30 +80,31 @@ Additionally, you can access all the functionality of *htdigest-ruby* and
|
|
59
80
|
-h, --help Display this help.
|
60
81
|
-v, --version Show version info.
|
61
82
|
|
62
|
-
|
83
|
+
## Supported Hash Algorithms
|
63
84
|
|
64
|
-
|
65
|
-
|
66
|
-
df.delete('someolduser', 'myotherrealm')
|
67
|
-
end
|
85
|
+
Out of the box, `htauth` supports the classic algorithms that ship with Apache
|
86
|
+
`htpasswd`.
|
68
87
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
88
|
+
- Built in
|
89
|
+
- Generally accepted
|
90
|
+
- MD5 (default for compatibility reasons)
|
91
|
+
- bcrypt (probably the better choice)
|
73
92
|
|
74
|
-
|
75
|
-
|
76
|
-
|
93
|
+
- **Not Recommended** - available only for backwards compatibility with `htpasswd`
|
94
|
+
- SHA1
|
95
|
+
- crypt
|
96
|
+
- plaintext
|
77
97
|
|
78
|
-
|
79
|
-
|
80
|
-
|
98
|
+
- Available with the installation of additional libraries:
|
99
|
+
- argon2 - to use, add `gem 'argon2'` to your `Gemfile`. `argon2` will
|
100
|
+
now be a valid algorithm to use in `HTAuth::PasswdFile` API. Currently
|
101
|
+
argon2 is not supported on windows as the upstream `argon2` gem does not
|
102
|
+
support windows.
|
81
103
|
|
82
104
|
## CREDITS
|
83
105
|
|
84
106
|
* [The Apache Software Foundation](http://www.apache.org/)
|
85
|
-
* all the folks who contributed to htdigest and
|
107
|
+
* all the folks who contributed to htdigest and htpasswd
|
86
108
|
|
87
109
|
## MIT LICENSE
|
88
110
|
|
data/Rakefile
CHANGED
@@ -8,11 +8,14 @@ This.homepage = "http://github.com/copiousfreetime/#{ This.name }"
|
|
8
8
|
|
9
9
|
This.ruby_gemspec do |spec|
|
10
10
|
spec.add_dependency( 'bcrypt', '~> 3.1' )
|
11
|
+
spec.add_dependency( 'base64', '~> 0.2' )
|
11
12
|
|
12
|
-
spec.add_development_dependency( '
|
13
|
-
spec.add_development_dependency( '
|
14
|
-
spec.add_development_dependency( '
|
15
|
-
spec.add_development_dependency( '
|
13
|
+
spec.add_development_dependency( 'argon2' , '~> 2.3')
|
14
|
+
spec.add_development_dependency( 'rake' , '~> 13.1')
|
15
|
+
spec.add_development_dependency( 'minitest' , '~> 5.21' )
|
16
|
+
spec.add_development_dependency( 'minitest-junit' , '~> 1.1' )
|
17
|
+
spec.add_development_dependency( 'rdoc' , '~> 6.6' )
|
18
|
+
spec.add_development_dependency( 'simplecov', '~> 0.21' )
|
16
19
|
|
17
20
|
spec.metadata = {
|
18
21
|
"bug_tracker_uri" => "https://github.com/copiousfreetime/htauth/issues",
|
data/bin/htdigest-ruby
CHANGED
data/bin/htpasswd-ruby
CHANGED
data/lib/htauth/algorithm.rb
CHANGED
@@ -13,6 +13,8 @@ module HTAuth
|
|
13
13
|
SALT_CHARS = (%w[ . / ] + ("0".."9").to_a + ('A'..'Z').to_a + ('a'..'z').to_a).freeze
|
14
14
|
SALT_LENGTH = 8
|
15
15
|
|
16
|
+
# Public: flag for the argon2 algorithm
|
17
|
+
ARGON2 = "argon2".freeze
|
16
18
|
# Public: flag for the bcrypt algorithm
|
17
19
|
BCRYPT = "bcrypt".freeze
|
18
20
|
# Public: flag for the md5 algorithm
|
@@ -84,7 +86,17 @@ module HTAuth
|
|
84
86
|
end
|
85
87
|
|
86
88
|
# Internal
|
87
|
-
def encode(password)
|
89
|
+
def encode(password)
|
90
|
+
raise NotImplementedError, "#{self.class.name} must implement #{self.class.name}.encode(password)"
|
91
|
+
end
|
92
|
+
|
93
|
+
# Internal: Does the given password match the digest, the default just
|
94
|
+
# encodes and secure compares the result, different algorithms may overide
|
95
|
+
# this method
|
96
|
+
def verify_password?(password, digest)
|
97
|
+
encoded = encode(password)
|
98
|
+
self.class.secure_compare(encoded, digest)
|
99
|
+
end
|
88
100
|
|
89
101
|
# Internal: 8 bytes of random items from SALT_CHARS
|
90
102
|
def gen_salt(length = SALT_LENGTH)
|
@@ -92,7 +104,8 @@ module HTAuth
|
|
92
104
|
end
|
93
105
|
|
94
106
|
# Internal: this is not the Base64 encoding, this is the to64()
|
95
|
-
# method from the
|
107
|
+
# method from the Apache Portable Runtime (APR) library
|
108
|
+
# https://github.com/apache/apr/blob/trunk/crypto/apr_md5.c#L493-L502
|
96
109
|
def to_64(number, rounds)
|
97
110
|
r = StringIO.new
|
98
111
|
rounds.times do |x|
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'htauth/algorithm'
|
2
|
+
begin
|
3
|
+
require 'argon2'
|
4
|
+
rescue LoadError
|
5
|
+
end
|
6
|
+
|
7
|
+
module HTAuth
|
8
|
+
# Internal: Support of the argon2id algorithm and password format.
|
9
|
+
|
10
|
+
class Argon2 < Algorithm
|
11
|
+
class NotSupportedError < ::HTAuth::InvalidAlgorithmError
|
12
|
+
def message
|
13
|
+
"Unfortunately Argon2 passwords are not supported on `#{RUBY_PLATFORM} at this time. This because the upstream argon2 gem does not support windows."
|
14
|
+
end
|
15
|
+
end
|
16
|
+
class NotInstalledError < ::HTAuth::InvalidAlgorithmError
|
17
|
+
def message
|
18
|
+
"Argon2 passwords are supported if the `argon2' gem is installed. Add `gem 'argon2', '~> 2.3'` to your Gemfile"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# from upstream, used to help make a nice error message if its not installed
|
23
|
+
# https://github.com/technion/ruby-argon2/blob/3388d7e05e8b486ea4ba8bd2aeb1e9988f025f13/lib/argon2/hash_format.rb#L45
|
24
|
+
PREFIX = /^\$argon2(id?|d).{,113}/.freeze
|
25
|
+
ARGON2_GEM_INSTALLED = defined?(::Argon2)
|
26
|
+
|
27
|
+
def self.supported?
|
28
|
+
!::Gem.win_platform?
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.ensure_available!
|
32
|
+
raise NotSupportedError unless supported?
|
33
|
+
raise NotInstalledError unless ARGON2_GEM_INSTALLED
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_accessor :options
|
37
|
+
|
38
|
+
def self.handles?(password_entry)
|
39
|
+
return false unless PREFIX.match?(password_entry)
|
40
|
+
ensure_available!
|
41
|
+
|
42
|
+
return ::Argon2::Password.valid_hash?(password_entry)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.extract_options_from_existing_password_field(existing)
|
46
|
+
hash_format = ::Argon2::HashFormat.new(existing)
|
47
|
+
|
48
|
+
# m_cost on the input is the 2**m_cost, but in the hash its the number of
|
49
|
+
# bytes, so need to convert it back to a power of 2, which is the
|
50
|
+
# log2(m_cost)
|
51
|
+
|
52
|
+
{
|
53
|
+
t_cost: hash_format.t_cost,
|
54
|
+
m_cost: ::Math.log2(hash_format.m_cost).floor,
|
55
|
+
p_cost: hash_format.p_cost,
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize(params = { profile: :rfc_9106_low_memory })
|
60
|
+
self.class.ensure_available!
|
61
|
+
if existing = (params['existing'] || params[:existing]) then
|
62
|
+
@options = self.class.extract_options_from_existing_password_field(existing)
|
63
|
+
else
|
64
|
+
@options = params
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def encode(password)
|
69
|
+
argon2 = ::Argon2::Password.new(options)
|
70
|
+
argon2.create(password)
|
71
|
+
end
|
72
|
+
|
73
|
+
def verify_password?(password, digest)
|
74
|
+
::Argon2::Password.verify_password(password, digest)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/htauth/bcrypt.rb
CHANGED
data/lib/htauth/cli/digest.rb
CHANGED
data/lib/htauth/cli/passwd.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
require 'htauth/
|
2
|
-
require 'htauth/passwd_file'
|
3
|
-
require 'htauth/console'
|
1
|
+
require 'htauth/cli'
|
4
2
|
|
5
3
|
require 'ostruct'
|
6
4
|
require 'optparse'
|
@@ -44,8 +42,8 @@ module HTAuth
|
|
44
42
|
@option_parser = OptionParser.new(nil, 16) do |op|
|
45
43
|
op.banner = <<-EOB
|
46
44
|
Usage:
|
47
|
-
#{op.program_name} [-
|
48
|
-
#{op.program_name} -b[
|
45
|
+
#{op.program_name} [-acimBdpsD] [--verify] [-C cost] passwordfile username
|
46
|
+
#{op.program_name} -b[acmBdpsD] [--verify] [-C cost] passwordfile username password
|
49
47
|
|
50
48
|
#{op.program_name} -n[imBdps] [-C cost] username
|
51
49
|
#{op.program_name} -nb[mBdps] [-C cost] username password
|
@@ -53,7 +51,11 @@ Usage:
|
|
53
51
|
|
54
52
|
op.separator ""
|
55
53
|
|
56
|
-
op.on("
|
54
|
+
op.on("--argon2", "Force argon2 encryption of the password.") do |a|
|
55
|
+
options.algorithm = Algorithm::ARGON2
|
56
|
+
end
|
57
|
+
|
58
|
+
op.on("-b", "--batch", "Batch mode, get the password from the command line, rather than prompt.") do |b|
|
57
59
|
options.batch_mode = b
|
58
60
|
end
|
59
61
|
|
@@ -118,7 +120,7 @@ Usage:
|
|
118
120
|
options.show_version = v
|
119
121
|
end
|
120
122
|
|
121
|
-
op.on("--verify", "Verify password for the specified user") do |v|
|
123
|
+
op.on("--verify", "Verify password for the specified user.") do |v|
|
122
124
|
options.operation << :verify
|
123
125
|
end
|
124
126
|
|
data/lib/htauth/cli.rb
CHANGED
data/lib/htauth/digest_entry.rb
CHANGED
data/lib/htauth/passwd_entry.rb
CHANGED
@@ -62,7 +62,7 @@ module HTAuth
|
|
62
62
|
# Internal: Create a new Entry with the given user, password, and algorithm
|
63
63
|
def initialize(user, password = nil, alg = Algorithm::DEFAULT, alg_params = {} )
|
64
64
|
@user = user
|
65
|
-
alg = Algorithm::DEFAULT if alg == Algorithm::EXISTING
|
65
|
+
alg = Algorithm::DEFAULT if alg == Algorithm::EXISTING
|
66
66
|
@algorithm = Algorithm.algorithm_from_name(alg, alg_params)
|
67
67
|
@digest = calc_digest(password)
|
68
68
|
end
|
@@ -106,25 +106,14 @@ module HTAuth
|
|
106
106
|
end
|
107
107
|
|
108
108
|
# Public: Check if the given password is the password of this entry
|
109
|
-
#
|
110
|
-
# tries all of the ones that it thinks it could be, and marks the algorithm if it matches
|
111
|
-
# when looking for a matche, we always compare all of them, no short
|
112
|
-
# circuiting
|
109
|
+
#
|
113
110
|
def authenticated?(check_password)
|
114
|
-
|
115
|
-
if algorithm.kind_of?(Bcrypt) then
|
116
|
-
bc = ::BCrypt::Password.new(digest)
|
117
|
-
authed = bc.is_password?(check_password)
|
118
|
-
else
|
119
|
-
encoded = algorithm.encode(check_password)
|
120
|
-
authed = Algorithm.secure_compare(encoded, digest)
|
121
|
-
end
|
122
|
-
return authed
|
111
|
+
algorithm.verify_password?(check_password, @digest)
|
123
112
|
end
|
124
113
|
|
125
114
|
# Internal: Returns the key of this entry
|
126
115
|
def key
|
127
|
-
|
116
|
+
"#{user}"
|
128
117
|
end
|
129
118
|
|
130
119
|
# Internal: Returns the file line for this entry
|
data/lib/htauth/passwd_file.rb
CHANGED
@@ -11,9 +11,9 @@ module HTAuth
|
|
11
11
|
# Examples
|
12
12
|
#
|
13
13
|
# ::HTAuth::PasswdFile.open("my.passwd") do |pf|
|
14
|
-
# pf.has_entry?('myuser'
|
15
|
-
# pf.add_or_update('someuser', '
|
16
|
-
# pf.delete('someolduser'
|
14
|
+
# pf.has_entry?('myuser')
|
15
|
+
# pf.add_or_update('someuser', 'a password')
|
16
|
+
# pf.delete('someolduser')
|
17
17
|
# end
|
18
18
|
#
|
19
19
|
class PasswdFile < HTAuth::File
|
@@ -68,7 +68,7 @@ module HTAuth
|
|
68
68
|
# username - the username of the entry
|
69
69
|
# password - the password of the entry
|
70
70
|
# algorithm - the algorithm to use (default: "md5"). Valid options are:
|
71
|
-
# "md5", "bcrypt", "sha1", "plaintext", or "crypt"
|
71
|
+
# "md5", "bcrypt", "argon2", "sha1", "plaintext", or "crypt"
|
72
72
|
# algorithm_args - key-value pairs of arguments that are passed to the
|
73
73
|
# algorithm, currently this is only used to pass the cost
|
74
74
|
# to the bcrypt algorithm
|
@@ -96,7 +96,7 @@ module HTAuth
|
|
96
96
|
# username - the username of the entry
|
97
97
|
# password - the password of the entry
|
98
98
|
# algorithm - the algorithm to use (default: "md5"). Valid options are:
|
99
|
-
# "md5", "bcrypt", "sha1", "plaintext", or "crypt"
|
99
|
+
# "md5", "bcrypt", "argon2", "sha1", "plaintext", or "crypt"
|
100
100
|
# algorithm_args - key-value pairs of arguments that are passed to the
|
101
101
|
# algorithm, currently this is only used to pass the cost
|
102
102
|
# to the bcrypt algorithm
|
@@ -133,7 +133,7 @@ module HTAuth
|
|
133
133
|
# username - the username of the entry
|
134
134
|
# password - the password of the entry
|
135
135
|
# algorithm - the algorithm to use (default: "existing"). Valid options are:
|
136
|
-
# "existing", "md5", "bcrypt", "sha1", "plaintext", or "crypt"
|
136
|
+
# "existing", "md5", "bcrypt", "argon2", "sha1", "plaintext", or "crypt"
|
137
137
|
# algorithm_args - key-value pairs of arguments that are passed to the
|
138
138
|
# algorithm, currently this is only used to pass the cost
|
139
139
|
# to the bcrypt algorithm
|
data/lib/htauth/sha1.rb
CHANGED
data/lib/htauth/version.rb
CHANGED
data/lib/htauth.rb
CHANGED
data/spec/algorithm_spec.rb
CHANGED
data/spec/argon2_spec.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'argon2'
|
3
|
+
|
4
|
+
describe HTAuth::Argon2 do
|
5
|
+
|
6
|
+
it "decodes the hash back to the original options" do
|
7
|
+
hash = '$argon2id$v=19$m=65536,t=3,p=4$V1ln1M4o1RS7SzWHAtqyWQ$jEHi1Qo2FSBgLAPpOa1mPx6OD/twtjj8M1AlVZwamPg'
|
8
|
+
options = HTAuth::Argon2.extract_options_from_existing_password_field(hash)
|
9
|
+
_(options).must_equal ::Argon2::Profiles[:rfc_9106_low_memory]
|
10
|
+
end
|
11
|
+
|
12
|
+
it "encrypts the same way that argon2 does by default" do
|
13
|
+
argon2 = HTAuth::Argon2.new
|
14
|
+
hash = argon2.encode("a secret")
|
15
|
+
_(::Argon2::Password.verify_password('a secret', hash)).must_equal true
|
16
|
+
end
|
17
|
+
|
18
|
+
it "allow changing the parameters directly" do
|
19
|
+
hash = '$argon2id$v=19$m=262144,t=3,p=4$7DRAuE1yIHPHqISmcyaJTg$M0EErpbxqv8dvrMrQMoGDEA7KQCw67jGdXwtCbRINFs'
|
20
|
+
options = HTAuth::Argon2.extract_options_from_existing_password_field(hash)
|
21
|
+
|
22
|
+
options[:m_cost] = 11
|
23
|
+
|
24
|
+
argon2 = HTAuth::Argon2.new(options)
|
25
|
+
local_hash = argon2.encode("a secret")
|
26
|
+
_(::Argon2::Password.verify_password('a secret', local_hash)).must_equal true
|
27
|
+
end
|
28
|
+
end
|
data/spec/bcrypt_spec.rb
CHANGED
data/spec/cli/passwd_spec.rb
CHANGED
@@ -129,7 +129,23 @@ describe HTAuth::CLI::Passwd do
|
|
129
129
|
_(@stderr.string).must_match( /ERROR:/m )
|
130
130
|
_(se.status).must_equal 1
|
131
131
|
end
|
132
|
-
|
132
|
+
end
|
133
|
+
|
134
|
+
it "creates a new file with one argon2 entry" do
|
135
|
+
begin
|
136
|
+
@stdin.puts "a secret"
|
137
|
+
@stdin.puts "a secret"
|
138
|
+
@stdin.rewind
|
139
|
+
@htauth.run([ "--argon", "-c", @new_file, "agatha" ])
|
140
|
+
rescue SystemExit => se
|
141
|
+
_(se.status).must_equal 0
|
142
|
+
l = IO.readlines(@new_file)
|
143
|
+
fields = l.first.split(':')
|
144
|
+
_(fields.first).must_equal "agatha"
|
145
|
+
argon2_hash = fields.last
|
146
|
+
|
147
|
+
_(::Argon2::Password.valid_hash?(argon2_hash)).wont_be_nil
|
148
|
+
end
|
133
149
|
end
|
134
150
|
|
135
151
|
it "does not verify the password from stdin on -i option" do
|
data/spec/crypt_spec.rb
CHANGED
data/spec/digest_entry_spec.rb
CHANGED
data/spec/digest_file_spec.rb
CHANGED
data/spec/md5_spec.rb
CHANGED
data/spec/passwd_entry_spec.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'htauth/passwd_entry'
|
3
2
|
|
4
3
|
describe HTAuth::PasswdEntry do
|
5
4
|
before(:each) do
|
@@ -32,6 +31,14 @@ describe HTAuth::PasswdEntry do
|
|
32
31
|
_(bob.digest).must_equal "b secret"
|
33
32
|
end
|
34
33
|
|
34
|
+
it "encypts correctly for argon2" do
|
35
|
+
# Don't do this in real life, do not pass in a salt, let the algorithm generate it internally
|
36
|
+
salt = ";L\xCDMRO\v\x13;\x012\x9B'\xEE\\i"
|
37
|
+
agatha = HTAuth::PasswdEntry.new("agatha","ag secret", "argon2", {salt_do_not_supply: salt} )
|
38
|
+
expected = "$argon2id$v=19$m=65536,t=3,p=4$O0zNTVJPCxM7ATKbJ+5caQ$e7wIsl7AY+uIbN+1StYOKkVCJhOrvX7BxAlQ+sPC+Nc"
|
39
|
+
_(agatha.digest).must_equal expected
|
40
|
+
end
|
41
|
+
|
35
42
|
it "encrypts with crypt as a default, when parsed from crypt()'d line" do
|
36
43
|
bob2 = HTAuth::PasswdEntry.from_line(@bob.to_s)
|
37
44
|
_(bob2.algorithm).must_be_instance_of(HTAuth::Crypt)
|
@@ -97,6 +104,12 @@ describe HTAuth::PasswdEntry do
|
|
97
104
|
_(s2.authenticated?("b secret")).must_equal true
|
98
105
|
end
|
99
106
|
|
107
|
+
it "authenticates correctly against argon2" do
|
108
|
+
s = HTAuth::PasswdEntry.new("agatha", "ag secret", "argon2")
|
109
|
+
s2 = HTAuth::PasswdEntry.from_line(s.to_s)
|
110
|
+
_(s2.authenticated?("ag secret")).must_equal true
|
111
|
+
end
|
112
|
+
|
100
113
|
it "can update the cost of an entry after initialization before encoding password" do
|
101
114
|
s = HTAuth::PasswdEntry.new("brenda", "b secret", "bcrypt")
|
102
115
|
_(s.algorithm.cost).must_equal(::HTAuth::Bcrypt::DEFAULT_APACHE_COST)
|
@@ -108,7 +121,7 @@ describe HTAuth::PasswdEntry do
|
|
108
121
|
_(s2.algorithm.cost).must_equal(12)
|
109
122
|
end
|
110
123
|
|
111
|
-
it "raises an error if
|
124
|
+
it "raises an error if assigning an invalid algorithm" do
|
112
125
|
b = HTAuth::PasswdEntry.new("brenda", "b secret", "bcrypt")
|
113
126
|
_ { b.algorithm = 42 }.must_raise(HTAuth::InvalidAlgorithmError)
|
114
127
|
end
|
data/spec/sha1_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
puts "Using coverage!"
|
4
|
-
SimpleCov.start if ENV['COVERAGE']
|
5
|
-
end
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start if ENV['COVERAGE']
|
6
3
|
|
7
|
-
gem 'minitest'
|
8
4
|
require 'minitest/autorun'
|
9
5
|
require 'minitest/pride'
|
10
6
|
|
@@ -26,3 +22,4 @@ class ConsoleIO < StringIO
|
|
26
22
|
yield self
|
27
23
|
end
|
28
24
|
end
|
25
|
+
require 'htauth'
|
data/tasks/default.rake
CHANGED
@@ -36,11 +36,13 @@ task :develop => "develop:default"
|
|
36
36
|
# Minitest - standard TestTask
|
37
37
|
#------------------------------------------------------------------------------
|
38
38
|
begin
|
39
|
-
require '
|
40
|
-
|
41
|
-
t.
|
42
|
-
t.libs
|
43
|
-
t.
|
39
|
+
require 'minitest/test_task'
|
40
|
+
Minitest::TestTask.create( :test) do |t|
|
41
|
+
t.libs << "lib"
|
42
|
+
t.libs << "spec"
|
43
|
+
t.libs << "test"
|
44
|
+
t.warning = true
|
45
|
+
t.test_globs = "{test,spec}/**/{test_*,*_spec}.rb"
|
44
46
|
end
|
45
47
|
|
46
48
|
task :test_requirements
|
@@ -143,14 +145,20 @@ namespace :fixme do
|
|
143
145
|
end
|
144
146
|
|
145
147
|
def local_fixme_files
|
146
|
-
This.manifest.select { |p| p =~ %r|^tasks/| }
|
148
|
+
local_files = This.manifest.select { |p| p =~ %r|^tasks/| }
|
149
|
+
local_files << ".semaphore/semaphore.yml"
|
147
150
|
end
|
148
151
|
|
149
152
|
def outdated_fixme_files
|
150
153
|
local_fixme_files.select do |local|
|
151
154
|
upstream = fixme_project_path( local )
|
152
|
-
upstream.exist?
|
153
|
-
|
155
|
+
if upstream.exist? then
|
156
|
+
if File.exist?( local ) then
|
157
|
+
( Digest::SHA256.file( local ) != Digest::SHA256.file( upstream ) )
|
158
|
+
else
|
159
|
+
true
|
160
|
+
end
|
161
|
+
end
|
154
162
|
end
|
155
163
|
end
|
156
164
|
|
@@ -201,7 +209,7 @@ task :gemspec do
|
|
201
209
|
end
|
202
210
|
|
203
211
|
# .rbc files from ruby 2.0
|
204
|
-
CLOBBER <<
|
212
|
+
CLOBBER << "**/*.rbc"
|
205
213
|
|
206
214
|
# The standard gem packaging task, everyone has it.
|
207
215
|
require 'rubygems/package_task'
|
@@ -213,19 +221,19 @@ end
|
|
213
221
|
# Release - the steps we go through to do a final release, this is pulled from
|
214
222
|
# a compbination of mojombo's rakegem, hoe and hoe-git
|
215
223
|
#
|
216
|
-
# 1) make sure we are on the
|
224
|
+
# 1) make sure we are on the main branch
|
217
225
|
# 2) make sure there are no uncommitted items
|
218
226
|
# 3) check the manifest and make sure all looks good
|
219
227
|
# 4) build the gem
|
220
228
|
# 5) do an empty commit to have the commit message of the version
|
221
229
|
# 6) tag that commit as the version
|
222
|
-
# 7) push
|
230
|
+
# 7) push main
|
223
231
|
# 8) push the tag
|
224
232
|
# 7) pus the gem
|
225
233
|
#------------------------------------------------------------------------------
|
226
234
|
task :release_check do
|
227
|
-
unless `git branch` =~ /^\*
|
228
|
-
abort "You must be on the
|
235
|
+
unless `git branch` =~ /^\* main/
|
236
|
+
abort "You must be on the main branch to release!"
|
229
237
|
end
|
230
238
|
unless `git status` =~ /^nothing to commit/m
|
231
239
|
abort "Nope, sorry, you have unfinished business"
|
@@ -236,7 +244,7 @@ desc "Create tag v#{This.version}, build and push #{This.platform_gemspec.full_n
|
|
236
244
|
task :release => [ :release_check, 'manifest:check', :gem ] do
|
237
245
|
sh "git commit --allow-empty -a -m 'Release #{This.version}'"
|
238
246
|
sh "git tag -a -m 'v#{This.version}' v#{This.version}"
|
239
|
-
sh "git push origin
|
247
|
+
sh "git push origin main"
|
240
248
|
sh "git push origin v#{This.version}"
|
241
249
|
sh "gem push pkg/#{This.platform_gemspec.full_name}.gem"
|
242
250
|
end
|
data/tasks/this.rb
CHANGED
@@ -25,7 +25,7 @@ class ThisProject
|
|
25
25
|
#
|
26
26
|
# Yields self
|
27
27
|
def initialize(&block)
|
28
|
-
@exclude_from_manifest = Regexp.union(/\.(git|DS_Store)/,
|
28
|
+
@exclude_from_manifest = Regexp.union(/\.(git|DS_Store|semaphore)/,
|
29
29
|
/^(doc|coverage|pkg|tmp|Gemfile(\.lock)?)/,
|
30
30
|
/^[^\/]+\.gemspec/,
|
31
31
|
/\.(swp|jar|bundle|so|rvmrc|travis.yml|byebug_history|fossa.yml|ruby-version)$/,
|
@@ -146,7 +146,7 @@ class ThisProject
|
|
146
146
|
spec.rdoc_options = [ "--main" , 'README.md',
|
147
147
|
"--markup", "tomdoc" ]
|
148
148
|
|
149
|
-
spec.required_ruby_version = '>= 2.
|
149
|
+
spec.required_ruby_version = '>= 2.3.0'
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: htauth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Hinegardner
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bcrypt
|
@@ -24,65 +24,106 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: base64
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: argon2
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.3'
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: rake
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
30
58
|
requirements:
|
31
59
|
- - "~>"
|
32
60
|
- !ruby/object:Gem::Version
|
33
|
-
version: '13.
|
61
|
+
version: '13.1'
|
34
62
|
type: :development
|
35
63
|
prerelease: false
|
36
64
|
version_requirements: !ruby/object:Gem::Requirement
|
37
65
|
requirements:
|
38
66
|
- - "~>"
|
39
67
|
- !ruby/object:Gem::Version
|
40
|
-
version: '13.
|
68
|
+
version: '13.1'
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
70
|
name: minitest
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
44
72
|
requirements:
|
45
73
|
- - "~>"
|
46
74
|
- !ruby/object:Gem::Version
|
47
|
-
version: '5.
|
75
|
+
version: '5.21'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '5.21'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: minitest-junit
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.1'
|
48
90
|
type: :development
|
49
91
|
prerelease: false
|
50
92
|
version_requirements: !ruby/object:Gem::Requirement
|
51
93
|
requirements:
|
52
94
|
- - "~>"
|
53
95
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
96
|
+
version: '1.1'
|
55
97
|
- !ruby/object:Gem::Dependency
|
56
98
|
name: rdoc
|
57
99
|
requirement: !ruby/object:Gem::Requirement
|
58
100
|
requirements:
|
59
101
|
- - "~>"
|
60
102
|
- !ruby/object:Gem::Version
|
61
|
-
version: '6.
|
103
|
+
version: '6.6'
|
62
104
|
type: :development
|
63
105
|
prerelease: false
|
64
106
|
version_requirements: !ruby/object:Gem::Requirement
|
65
107
|
requirements:
|
66
108
|
- - "~>"
|
67
109
|
- !ruby/object:Gem::Version
|
68
|
-
version: '6.
|
110
|
+
version: '6.6'
|
69
111
|
- !ruby/object:Gem::Dependency
|
70
112
|
name: simplecov
|
71
113
|
requirement: !ruby/object:Gem::Requirement
|
72
114
|
requirements:
|
73
115
|
- - "~>"
|
74
116
|
- !ruby/object:Gem::Version
|
75
|
-
version: '0.
|
117
|
+
version: '0.21'
|
76
118
|
type: :development
|
77
119
|
prerelease: false
|
78
120
|
version_requirements: !ruby/object:Gem::Requirement
|
79
121
|
requirements:
|
80
122
|
- - "~>"
|
81
123
|
- !ruby/object:Gem::Version
|
82
|
-
version: '0.
|
83
|
-
description: HTAuth
|
84
|
-
|
85
|
-
htpasswd files.
|
124
|
+
version: '0.21'
|
125
|
+
description: HTAuth provides an API and commandline tools for managing Apache/httpd
|
126
|
+
style htpasswd and htdigest files.
|
86
127
|
email: jeremy@copiousfreetime.org
|
87
128
|
executables:
|
88
129
|
- htdigest-ruby
|
@@ -104,6 +145,7 @@ files:
|
|
104
145
|
- bin/htpasswd-ruby
|
105
146
|
- lib/htauth.rb
|
106
147
|
- lib/htauth/algorithm.rb
|
148
|
+
- lib/htauth/argon2.rb
|
107
149
|
- lib/htauth/bcrypt.rb
|
108
150
|
- lib/htauth/cli.rb
|
109
151
|
- lib/htauth/cli/digest.rb
|
@@ -123,6 +165,7 @@ files:
|
|
123
165
|
- lib/htauth/sha1.rb
|
124
166
|
- lib/htauth/version.rb
|
125
167
|
- spec/algorithm_spec.rb
|
168
|
+
- spec/argon2_spec.rb
|
126
169
|
- spec/bcrypt_spec.rb
|
127
170
|
- spec/cli/digest_spec.rb
|
128
171
|
- spec/cli/passwd_spec.rb
|
@@ -153,7 +196,7 @@ metadata:
|
|
153
196
|
changelog_uri: https://github.com/copiousfreetime/htauth/blob/master/HISTORY.md
|
154
197
|
homepage_uri: https://github.com/copiousfreetime/htauth
|
155
198
|
source_code_uri: https://github.com/copiousfreetime/htauth
|
156
|
-
post_install_message:
|
199
|
+
post_install_message:
|
157
200
|
rdoc_options:
|
158
201
|
- "--main"
|
159
202
|
- README.md
|
@@ -165,21 +208,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
165
208
|
requirements:
|
166
209
|
- - ">="
|
167
210
|
- !ruby/object:Gem::Version
|
168
|
-
version: 2.
|
211
|
+
version: 2.3.0
|
169
212
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
170
213
|
requirements:
|
171
214
|
- - ">="
|
172
215
|
- !ruby/object:Gem::Version
|
173
216
|
version: '0'
|
174
217
|
requirements: []
|
175
|
-
rubygems_version: 3.
|
176
|
-
signing_key:
|
218
|
+
rubygems_version: 3.5.3
|
219
|
+
signing_key:
|
177
220
|
specification_version: 4
|
178
|
-
summary: HTAuth
|
179
|
-
|
180
|
-
htpasswd files.
|
221
|
+
summary: HTAuth provides an API and commandline tools for managing Apache/httpd style
|
222
|
+
htpasswd and htdigest files.
|
181
223
|
test_files:
|
182
224
|
- spec/algorithm_spec.rb
|
225
|
+
- spec/argon2_spec.rb
|
183
226
|
- spec/bcrypt_spec.rb
|
184
227
|
- spec/cli/digest_spec.rb
|
185
228
|
- spec/cli/passwd_spec.rb
|