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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4b47d22265e7ed8e07665edec308a71ce4decb3ccc293699d7736a6ca0ddca1
4
- data.tar.gz: 6127bf17e0c114580adac497ddf51f5822e4cfda49989e32bdf6af385dc83295
3
+ metadata.gz: ad5523ccfeacb957acb9cf2fae40e19e51bc92e10d093a950949fbe00bb50b1a
4
+ data.tar.gz: 280ed439110fd03ca5510ae81d8b17a4f0db21f4f0aed7360238bbcef83fe6c1
5
5
  SHA512:
6
- metadata.gz: 39faa1fb2b894b7fc6329c2fc19ca63049a0645fe7c42c10e5ae0b708172d994e7b544074f901750273c4b0871ec454206f32d1f26027add807a7818ad87907a
7
- data.tar.gz: 4d5112aec7a3839e797422ff429ee8e6bf14b968d46be97ef6e12a0c37196d1b6fbfa42772f6d488cec037f44fc37f85c69bd5a68ee8ee36fc7ad1f5d6ef84b6
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
- * [Github](http://github.com/copiousfreetime/htauth/tree/master)
4
- * [![Build Status](https://travis-ci.org/copiousfreetime/htauth.svg?branch=master)](https://travis-ci.org/copiousfreetime/htauth)
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 is a pure ruby replacement for the Apache support programs htdigest and
9
- htpasswd. Command line and API access are provided for access to htdigest and
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 to drop in commands *htdigest-ruby* and *htpasswd-ruby* that
15
- can manipulate the digest and passwd files in the same manner as Apache's
16
- original commands.
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
- *htdigest-ruby* and *htpasswd-ruby* are command line compatible with *htdigest*
19
- and *htpasswd*. They support the same exact same command line options as the
20
- originals, and have some extras.
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, you can access all the functionality of *htdigest-ruby* and
23
- *htpasswd-ruby* through an API.
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
- -b, --batch Batch mode, get the password from the command line, rather than prompt
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
- ### API Usage
83
+ ## Supported Hash Algorithms
63
84
 
64
- HTAuth::DigestFile.open("some.htdigest") do |df|
65
- df.add_or_update('someuser', 'myrealm', 'a password')
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
- HTAuth::PasswdFile.open("some.htpasswd", HTAuth::File::CREATE) do |pf|
70
- pf.add('someuser', 'a password', 'md5')
71
- pf.add('someotheruser', 'a different password', 'sha1')
72
- end
88
+ - Built in
89
+ - Generally accepted
90
+ - MD5 (default for compatibility reasons)
91
+ - bcrypt (probably the better choice)
73
92
 
74
- HTAuth::PasswdFile.open("some.htpasswd", HTAuth::File::ALTER) do |pf|
75
- pf.update('someuser', 'a password', 'bcrypt')
76
- end
93
+ - **Not Recommended** - available only for backwards compatibility with `htpasswd`
94
+ - SHA1
95
+ - crypt
96
+ - plaintext
77
97
 
78
- HTAuth::PasswdFile.open("some.htpasswd") do |pf|
79
- pf.authenticated?('someuser', 'a password')
80
- end
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 htpassword
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( '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' )
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
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  begin
4
- require 'htauth/cli'
4
+ require 'htauth/cli/digest'
5
5
  rescue LoadError
6
6
  path = File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
7
7
  raise if $:.include?(path)
data/bin/htpasswd-ruby CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  begin
4
- require 'htauth/cli'
4
+ require 'htauth/cli/passwd'
5
5
  rescue LoadError
6
6
  path = File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
7
7
  raise if $:.include?(path)
@@ -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) ; end
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 apache protable runtime library
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
@@ -31,5 +31,10 @@ module HTAuth
31
31
  def encode(password)
32
32
  ::BCrypt::Password.create(password, :cost => cost)
33
33
  end
34
+
35
+ def verify_password?(password, digest)
36
+ bc = ::BCrypt::Password.new(digest)
37
+ bc.is_password?(password)
38
+ end
34
39
  end
35
40
  end
@@ -1,7 +1,4 @@
1
- require 'htauth/version'
2
- require 'htauth/error'
3
- require 'htauth/digest_file'
4
- require 'htauth/console'
1
+ require 'htauth/cli'
5
2
 
6
3
  require 'ostruct'
7
4
  require 'optparse'
@@ -1,6 +1,4 @@
1
- require 'htauth/error'
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} [-cimBdpsD] [-C cost] passwordfile username
48
- #{op.program_name} -b[cmBdpsD] [-C cost] passwordfile username password
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("-b", "--batch", "Batch mode, get the password from the command line, rather than prompt") do |b|
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
@@ -1,8 +1,7 @@
1
1
  require 'htauth'
2
+ require 'htauth/console'
2
3
  module HTAuth
3
4
  module CLI
4
5
 
5
6
  end
6
7
  end
7
- require 'htauth/cli/digest'
8
- require 'htauth/cli/passwd'
@@ -1,5 +1,6 @@
1
1
  require 'htauth/error'
2
2
  require 'htauth/entry'
3
+ require 'htauth/algorithm'
3
4
  require 'digest/md5'
4
5
 
5
6
  module HTAuth
@@ -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
- # check the password and make sure it works, in the case that the algorithm is unknown it
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
- authed = false
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
- return "#{user}"
116
+ "#{user}"
128
117
  end
129
118
 
130
119
  # Internal: Returns the file line for this entry
@@ -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', 'myrealm')
15
- # pf.add_or_update('someuser', 'myrealm', 'a password')
16
- # pf.delete('someolduser', 'myotherrealm')
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
@@ -16,7 +16,7 @@ module HTAuth
16
16
  end
17
17
 
18
18
  # ignore the params
19
- def initialize(params = {})
19
+ def initialize(params = {})
20
20
  end
21
21
 
22
22
  def encode(password)
@@ -1,4 +1,4 @@
1
1
  module HTAuth
2
2
  # Public: The version of the htauth library
3
- VERSION = "2.1.1"
3
+ VERSION = "2.3.0"
4
4
  end
data/lib/htauth.rb CHANGED
@@ -30,7 +30,7 @@ end
30
30
 
31
31
  require 'htauth/version'
32
32
  require 'htauth/algorithm'
33
- require 'htauth/console'
33
+ require 'htauth/argon2'
34
34
  require 'htauth/bcrypt'
35
35
  require 'htauth/crypt'
36
36
  require 'htauth/digest_entry'
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/algorithm'
3
2
 
4
3
  describe HTAuth::Algorithm do
5
4
  it "raises an error if it encouners an unknown algorithm" do
@@ -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
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/bcrypt'
3
2
 
4
3
  describe HTAuth::Bcrypt do
5
4
  it "encrypts the same way that apache does by default" do
@@ -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
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/crypt'
3
2
 
4
3
  describe HTAuth::Crypt do
5
4
  it "encrypts the same way that apache does" do
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/digest_entry'
3
2
 
4
3
  describe HTAuth::DigestEntry do
5
4
  before(:each) do
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/digest_file'
3
2
  require 'tempfile'
4
3
 
5
4
  describe HTAuth::DigestFile do
data/spec/md5_spec.rb CHANGED
@@ -1,6 +1,4 @@
1
- require File.join(File.dirname(__FILE__),"spec_helper.rb")
2
-
3
- require 'htauth/md5'
1
+ require 'spec_helper'
4
2
 
5
3
  describe HTAuth::Md5 do
6
4
  it "encrypts the same way that apache does" do
@@ -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 assinging an invalid algorithm" do
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
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/sha1'
3
2
 
4
3
  describe HTAuth::Sha1 do
5
4
  it "encrypts the same way that apache does" do
data/spec/spec_helper.rb CHANGED
@@ -1,10 +1,6 @@
1
- if RUBY_VERSION >= '1.9.2' then
2
- require 'simplecov'
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 'rake/testtask'
40
- Rake::TestTask.new( :test ) do |t|
41
- t.ruby_opts = %w[ -w ]
42
- t.libs = %w[ lib spec test ]
43
- t.pattern = "{test,spec}/**/{test_*,*_spec}.rb"
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
- ( Digest::SHA256.file( local ) != Digest::SHA256.file( upstream ) )
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 << FileList["**/*.rbc"]
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 master branch
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 master
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` =~ /^\* master$/
228
- abort "You must be on the master branch to release!"
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 master"
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.2.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.1.1
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: 2020-04-02 00:00:00.000000000 Z
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.0'
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.0'
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.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: '5.5'
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.2'
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.2'
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.17'
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.17'
83
- description: HTAuth is a pure ruby replacement for the Apache support programs htdigest
84
- and htpasswd. Command line and API access are provided for access to htdigest and
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.2.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.0.3
176
- signing_key:
218
+ rubygems_version: 3.5.3
219
+ signing_key:
177
220
  specification_version: 4
178
- summary: HTAuth is a pure ruby replacement for the Apache support programs htdigest
179
- and htpasswd. Command line and API access are provided for access to htdigest and
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