htauth 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,9 @@
1
1
  = HTAuth
2
2
 
3
+ == Version 1.0.2 (2008-11-30)
4
+
5
+ * Change project layout
6
+
3
7
  == Version 1.0.1 (2008-02-06)
4
8
 
5
9
  === Changes
data/README CHANGED
@@ -2,17 +2,18 @@
2
2
 
3
3
  * Homepage[http://copiousfreetime.rubyforge.org/htauth]
4
4
  * {Rubyforge Project}[http://rubyforge.org/projects/copiousfreetime/]
5
- * email jeremy at hinegardner dot org
5
+ * Github[http://github.com/copiousfreetime/htauth/tree/master]
6
+ * email jeremy at copiousfreetime dot org
6
7
 
7
8
  == DESCRIPTION
8
9
 
9
- HTAuth is a pure ruby replacement for the Apache support programs +htdigest+ and
10
- +htpasswd+. Command line and API access are provided for access to htdigest and
10
+ HTAuth is a pure ruby replacement for the Apache support programs htdigest and
11
+ htpasswd. Command line and API access are provided for access to htdigest and
11
12
  htpasswd files.
12
13
 
13
14
  == FEATURES
14
15
 
15
- Rpassword provides to drop in commands *htdigest-ruby* and *htpasswd-ruby* that
16
+ HTAuth provides to drop in commands *htdigest-ruby* and *htpasswd-ruby* that
16
17
  can manipulate the digest and passwd files in the same manner as Apache's
17
18
  original commands.
18
19
 
@@ -1,12 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ begin
4
+ require 'highline'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'highline'
8
+ end
9
+
3
10
  begin
4
- require 'htauth'
11
+ require 'htauth'
5
12
  rescue LoadError
6
- path = File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
7
- raise if $:.include?(path)
8
- $: << path
9
- retry
13
+ path = File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
14
+ raise if $:.include?(path)
15
+ $: << path
16
+ retry
10
17
  end
11
18
 
12
- HTAuth::Digest.new.run(ARGV)
19
+ HTAuth::Digest.new.run(ARGV, ENV)
@@ -1,5 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ begin
4
+ require 'highline'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'highline'
8
+ end
9
+
3
10
  begin
4
11
  require 'htauth'
5
12
  rescue LoadError
@@ -9,4 +16,4 @@ rescue LoadError
9
16
  retry
10
17
  end
11
18
 
12
- HTAuth::Passwd.new.run(ARGV)
19
+ HTAuth::Passwd.new.run(ARGV, ENV)
@@ -0,0 +1,43 @@
1
+ require 'rubygems'
2
+ require 'htauth/version'
3
+ require 'tasks/config'
4
+
5
+ HTAuth::GEM_SPEC = Gem::Specification.new do |spec|
6
+ proj = Configuration.for('project')
7
+ spec.name = proj.name
8
+ spec.version = HTAuth::VERSION
9
+
10
+ spec.author = proj.author
11
+ spec.email = proj.email
12
+ spec.homepage = proj.homepage
13
+ spec.summary = proj.summary
14
+ spec.description = proj.description
15
+ spec.platform = Gem::Platform::RUBY
16
+
17
+
18
+ pkg = Configuration.for('packaging')
19
+ spec.files = pkg.files.all
20
+ spec.executables = pkg.files.bin.collect { |b| File.basename(b) }
21
+
22
+ # add dependencies here
23
+ # spec.add_dependency("rake", ">= 0.8.1")
24
+ spec.add_dependency("highline", "~> 1.4.0")
25
+
26
+
27
+ if rdoc = Configuration.for_if_exist?('rdoc') then
28
+ spec.has_rdoc = true
29
+ spec.extra_rdoc_files = pkg.files.rdoc
30
+ spec.rdoc_options = rdoc.options + [ "--main" , rdoc.main_page ]
31
+ else
32
+ spec.has_rdoc = false
33
+ end
34
+
35
+ if test = Configuration.for_if_exist?('testing') then
36
+ spec.test_files = test.files
37
+ end
38
+
39
+ if rf = Configuration.for_if_exist?('rubyforge') then
40
+ spec.rubyforge_project = rf.project
41
+ end
42
+
43
+ end
@@ -1,14 +1,36 @@
1
+ #--
2
+ # Copyrigth (c) 2008 Jeremy Hinegardner
3
+ # All rights reserved. See LICENSE and/or COPYING for details
4
+ #++
5
+
1
6
  module HTAuth
2
-
3
- ROOT_DIR = ::File.expand_path(::File.join(::File.dirname(__FILE__),".."))
4
- LIB_DIR = ::File.join(ROOT_DIR,"lib").freeze
5
7
 
6
- class FileAccessError < StandardError ; end
7
- class TempFileError < StandardError ; end
8
- class PasswordError < StandardError ; end
8
+ # The root directory of the project is considered to be the parent directory
9
+ # of the 'lib' directory.
10
+ #
11
+ def self.root_dir
12
+ unless @root_dir
13
+ path_parts = ::File.expand_path( __FILE__ ).split( ::File::SEPARATOR )
14
+ lib_index = path_parts.rindex( 'lib' )
15
+ @root_dir = path_parts[ 0...lib_index].join( ::File::SEPARATOR ) + ::File::SEPARATOR
16
+ end
17
+ return @root_dir
18
+ end
19
+
20
+ def self.lib_path( *args )
21
+ self.sub_path( "lib", *args )
22
+ end
23
+
24
+ def self.sub_path( sub, *args )
25
+ sp = ::File.join( root_dir, sub ) + ::File::SEPARATOR
26
+ sp = ::File.join( sp, *args ) if args
27
+ end
28
+
29
+ class FileAccessError < StandardError ; end
30
+ class TempFileError < StandardError ; end
31
+ class PasswordError < StandardError ; end
9
32
  end
10
33
 
11
34
  require 'htauth/version'
12
- require 'htauth/gemspec'
13
35
  require 'htauth/passwd'
14
36
  require 'htauth/digest'
@@ -1,67 +1,68 @@
1
1
  require 'htauth'
2
2
 
3
3
  module HTAuth
4
- class InvalidAlgorithmError < StandardError ; end
5
- # base class all the Passwd algorithms derive from
6
- class Algorithm
4
+ class InvalidAlgorithmError < StandardError ; end
5
+ # base class all the Passwd algorithms derive from
6
+ class Algorithm
7
7
 
8
- SALT_CHARS = (%w[ . / ] + ("0".."9").to_a + ('A'..'Z').to_a + ('a'..'z').to_a).freeze
9
- DEFAULT = ( RUBY_PLATFORM !~ /mswin32/ ) ? "crypt" : "md5"
10
- EXISTING = "existing"
8
+ SALT_CHARS = (%w[ . / ] + ("0".."9").to_a + ('A'..'Z').to_a + ('a'..'z').to_a).freeze
9
+ DEFAULT = ( RUBY_PLATFORM !~ /mswin32/ ) ? "crypt" : "md5"
10
+ EXISTING = "existing"
11
11
 
12
- class << self
13
- def algorithm_from_name(a_name, params = {})
14
- raise InvalidAlgorithmError, "`#{a_name}' is an invalid encryption algorithm, use one of #{sub_klasses.keys.join(', ')}" unless sub_klasses[a_name.downcase]
15
- sub_klasses[a_name.downcase].new(params)
16
- end
12
+ class << self
13
+ def algorithm_from_name(a_name, params = {})
14
+ raise InvalidAlgorithmError, "`#{a_name}' is an invalid encryption algorithm, use one of #{sub_klasses.keys.join(', ')}" unless sub_klasses[a_name.downcase]
15
+ sub_klasses[a_name.downcase].new(params)
16
+ end
17
17
 
18
- def algorithms_from_field(password_field)
19
- matches = []
18
+ def algorithms_from_field(password_field)
19
+ matches = []
20
20
 
21
- if password_field.index(sub_klasses['sha1'].new.prefix) then
22
- matches << sub_klasses['sha1'].new
23
- elsif password_field.index(sub_klasses['md5'].new.prefix) then
24
- p = password_field.split("$")
25
- matches << sub_klasses['md5'].new( :salt => p[2] )
26
- else
27
- matches << sub_klasses['plaintext'].new
28
- matches << sub_klasses['crypt'].new( :salt => password_field[0,2] )
29
- end
21
+ if password_field.index(sub_klasses['sha1'].new.prefix) then
22
+ matches << sub_klasses['sha1'].new
23
+ elsif password_field.index(sub_klasses['md5'].new.prefix) then
24
+ p = password_field.split("$")
25
+ matches << sub_klasses['md5'].new( :salt => p[2] )
26
+ else
27
+ matches << sub_klasses['plaintext'].new
28
+ matches << sub_klasses['crypt'].new( :salt => password_field[0,2] )
29
+ end
30
30
 
31
- return matches
32
- end
31
+ return matches
32
+ end
33
33
 
34
- def inherited(sub_klass)
35
- k = sub_klass.name.split("::").last.downcase
36
- sub_klasses[k] = sub_klass
37
- end
34
+ def inherited(sub_klass)
35
+ k = sub_klass.name.split("::").last.downcase
36
+ sub_klasses[k] = sub_klass
37
+ end
38
38
 
39
- def sub_klasses
40
- @sub_klasses ||= {}
41
- end
42
- end
39
+ def sub_klasses
40
+ @sub_klasses ||= {}
41
+ end
42
+ end
43
43
 
44
- def prefix ; end
45
- def encode(password) ; end
46
-
47
- # 8 bytes of random items from SALT_CHARS
48
- def gen_salt
49
- chars = []
50
- 8.times { chars << SALT_CHARS[rand(SALT_CHARS.size)] }
51
- chars.join('')
52
- end
44
+ def prefix ; end
45
+ def encode(password) ; end
53
46
 
54
- # this is not the Base64 encoding, this is the to64() method from apr
55
- def to_64(number, rounds)
56
- r = StringIO.new
57
- rounds.times do |x|
58
- r.print(SALT_CHARS[number % 64])
59
- number >>= 6
60
- end
61
- return r.string
62
- end
47
+ # 8 bytes of random items from SALT_CHARS
48
+ def gen_salt
49
+ chars = []
50
+ 8.times { chars << SALT_CHARS[rand(SALT_CHARS.size)] }
51
+ chars.join('')
52
+ end
53
+
54
+ # this is not the Base64 encoding, this is the to64() method from apr
55
+ def to_64(number, rounds)
56
+ r = StringIO.new
57
+ rounds.times do |x|
58
+ r.print(SALT_CHARS[number % 64])
59
+ number >>= 6
60
+ end
61
+ return r.string
63
62
  end
63
+ end
64
64
  end
65
+
65
66
  require 'htauth/md5'
66
67
  require 'htauth/sha1'
67
68
  require 'htauth/crypt'
@@ -2,19 +2,19 @@ require 'htauth/algorithm'
2
2
 
3
3
  module HTAuth
4
4
 
5
- # The basic crypt algorithm
6
- class Crypt < Algorithm
7
-
8
- def initialize(params = {})
9
- @salt = params[:salt] || params['salt'] || gen_salt
10
- end
5
+ # The basic crypt algorithm
6
+ class Crypt < Algorithm
11
7
 
12
- def prefix
8
+ def initialize(params = {})
9
+ @salt = params[:salt] || params['salt'] || gen_salt
10
+ end
11
+
12
+ def prefix
13
13
  ""
14
- end
14
+ end
15
15
 
16
- def encode(password)
17
- password.crypt(@salt)
18
- end
16
+ def encode(password)
17
+ password.crypt(@salt)
19
18
  end
19
+ end
20
20
  end
@@ -4,127 +4,126 @@ require 'htauth/digest_file'
4
4
  require 'ostruct'
5
5
  require 'optparse'
6
6
 
7
- require 'rubygems'
8
7
  require 'highline'
9
8
 
10
9
  module HTAuth
11
- class Digest
10
+ class Digest
12
11
 
13
- MAX_PASSWD_LENGTH = 255
12
+ MAX_PASSWD_LENGTH = 255
14
13
 
15
- attr_accessor :digest_file
14
+ attr_accessor :digest_file
16
15
 
17
- def initialize
18
- @digest_file = nil
19
- end
16
+ def initialize
17
+ @digest_file = nil
18
+ end
20
19
 
21
- def options
22
- if @options.nil? then
23
- @options = ::OpenStruct.new
24
- @options.show_version = false
25
- @options.show_help = false
26
- @options.file_mode = DigestFile::ALTER
27
- @options.passwdfile = nil
28
- @options.realm = nil
29
- @options.username = nil
30
- @options.delete_entry = false
31
- end
32
- @options
33
- end
20
+ def options
21
+ if @options.nil? then
22
+ @options = ::OpenStruct.new
23
+ @options.show_version = false
24
+ @options.show_help = false
25
+ @options.file_mode = DigestFile::ALTER
26
+ @options.passwdfile = nil
27
+ @options.realm = nil
28
+ @options.username = nil
29
+ @options.delete_entry = false
30
+ end
31
+ @options
32
+ end
34
33
 
35
- def option_parser
36
- if not @option_parser then
37
- @option_parser = OptionParser.new do |op|
38
- op.banner = "Usage: #{op.program_name} [options] passwordfile realm username"
39
- op.on("-c", "--create", "Create a new digest password file; this overwrites an existing file.") do |c|
40
- options.file_mode = DigestFile::CREATE
41
- end
42
-
43
- op.on("-D", "--delete", "Delete the specified user.") do |d|
44
- options.delete_entry = d
45
- end
46
-
47
- op.on("-h", "--help", "Display this help.") do |h|
48
- options.show_help = h
49
- end
50
-
51
- op.on("-v", "--version", "Show version info.") do |v|
52
- options.show_version = v
53
- end
54
- end
55
- end
56
- @option_parser
34
+ def option_parser
35
+ if not @option_parser then
36
+ @option_parser = OptionParser.new do |op|
37
+ op.banner = "Usage: #{op.program_name} [options] passwordfile realm username"
38
+ op.on("-c", "--create", "Create a new digest password file; this overwrites an existing file.") do |c|
39
+ options.file_mode = DigestFile::CREATE
40
+ end
41
+
42
+ op.on("-D", "--delete", "Delete the specified user.") do |d|
43
+ options.delete_entry = d
44
+ end
45
+
46
+ op.on("-h", "--help", "Display this help.") do |h|
47
+ options.show_help = h
48
+ end
49
+
50
+ op.on("-v", "--version", "Show version info.") do |v|
51
+ options.show_version = v
52
+ end
57
53
  end
54
+ end
55
+ @option_parser
56
+ end
58
57
 
59
- def show_help
60
- $stdout.puts option_parser
61
- exit 1
62
- end
58
+ def show_help
59
+ $stdout.puts option_parser
60
+ exit 1
61
+ end
63
62
 
64
- def show_version
65
- $stdout.puts "#{option_parser.program_name}: version #{HTAuth::VERSION}"
66
- exit 1
67
- end
63
+ def show_version
64
+ $stdout.puts "#{option_parser.program_name}: version #{HTAuth::VERSION}"
65
+ exit 1
66
+ end
68
67
 
69
- def parse_options(argv)
70
- begin
71
- option_parser.parse!(argv)
72
- show_version if options.show_version
73
- show_help if options.show_help or argv.size < 3
74
-
75
- options.passwdfile = argv.shift
76
- options.realm = argv.shift
77
- options.username = argv.shift
78
- rescue ::OptionParser::ParseError => pe
79
- $stderr.puts "ERROR: #{option_parser.program_name} - #{pe}"
80
- $stderr.puts "Try `#{option_parser.program_name} --help` for more information"
81
- exit 1
82
- end
83
- end
68
+ def parse_options(argv)
69
+ begin
70
+ option_parser.parse!(argv)
71
+ show_version if options.show_version
72
+ show_help if options.show_help or argv.size < 3
73
+
74
+ options.passwdfile = argv.shift
75
+ options.realm = argv.shift
76
+ options.username = argv.shift
77
+ rescue ::OptionParser::ParseError => pe
78
+ $stderr.puts "ERROR: #{option_parser.program_name} - #{pe}"
79
+ $stderr.puts "Try `#{option_parser.program_name} --help` for more information"
80
+ exit 1
81
+ end
82
+ end
83
+
84
+ def run(argv, env = ENV)
85
+ begin
86
+ parse_options(argv)
87
+ digest_file = DigestFile.new(options.passwdfile, options.file_mode)
88
+
89
+ if options.delete_entry then
90
+ digest_file.delete(options.username, options.realm)
91
+ else
92
+ # initialize here so that if $stdin is overwritten it gets picked up
93
+ hl = ::HighLine.new
94
+
95
+ action = digest_file.has_entry?(options.username, options.realm) ? "Changing" : "Adding"
84
96
 
85
- def run(argv)
86
- begin
87
- parse_options(argv)
88
- digest_file = DigestFile.new(options.passwdfile, options.file_mode)
89
-
90
- if options.delete_entry then
91
- digest_file.delete(options.username, options.realm)
92
- else
93
- # initialize here so that if $stdin is overwritten it gest picked up
94
- hl = ::HighLine.new
95
-
96
- action = digest_file.has_entry?(options.username, options.realm) ? "Changing" : "Adding"
97
-
98
- $stdout.puts "#{action} password for #{options.username} in realm #{options.realm}."
99
-
100
- pw_in = hl.ask(" New password: ") { |q| q.echo = '*' }
101
- raise PasswordError, "password '#{pw_in}' too long" if pw_in.length >= MAX_PASSWD_LENGTH
102
-
103
- pw_validate = hl.ask("Re-type new password: ") { |q| q.echo = '*' }
104
- raise PasswordError, "They don't match, sorry." unless pw_in == pw_validate
105
-
106
- digest_file.add_or_update(options.username, options.realm, pw_in)
107
- end
108
-
109
- digest_file.save!
110
-
111
- rescue HTAuth::FileAccessError => fae
112
- msg = "Could not open password file #{options.passwdfile} "
113
- $stderr.puts "#{msg}: #{fae.message}"
114
- $stderr.puts fae.backtrace.join("\n")
115
- exit 1
116
- rescue HTAuth::PasswordError => pe
117
- $stderr.puts "#{pe.message}"
118
- exit 1
119
- rescue HTAuth::DigestFileError => fe
120
- $stderr.puts "#{fe.message}"
121
- exit 1
122
- rescue SignalException => se
123
- $stderr.puts
124
- $stderr.puts "Interrupted"
125
- exit 1
126
- end
127
- exit 0
97
+ $stdout.puts "#{action} password for #{options.username} in realm #{options.realm}."
98
+
99
+ pw_in = hl.ask(" New password: ") { |q| q.echo = '*' }
100
+ raise PasswordError, "password '#{pw_in}' too long" if pw_in.length >= MAX_PASSWD_LENGTH
101
+
102
+ pw_validate = hl.ask("Re-type new password: ") { |q| q.echo = '*' }
103
+ raise PasswordError, "They don't match, sorry." unless pw_in == pw_validate
104
+
105
+ digest_file.add_or_update(options.username, options.realm, pw_in)
128
106
  end
107
+
108
+ digest_file.save!
109
+
110
+ rescue HTAuth::FileAccessError => fae
111
+ msg = "Could not open password file #{options.passwdfile} "
112
+ $stderr.puts "#{msg}: #{fae.message}"
113
+ $stderr.puts fae.backtrace.join("\n")
114
+ exit 1
115
+ rescue HTAuth::PasswordError => pe
116
+ $stderr.puts "#{pe.message}"
117
+ exit 1
118
+ rescue HTAuth::DigestFileError => fe
119
+ $stderr.puts "#{fe.message}"
120
+ exit 1
121
+ rescue SignalException => se
122
+ $stderr.puts
123
+ $stderr.puts "Interrupted"
124
+ exit 1
125
+ end
126
+ exit 0
129
127
  end
128
+ end
130
129
  end