perch 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +23 -0
- data/History.rdoc +36 -0
- data/Manifest.txt +9 -0
- data/README.rdoc +84 -0
- data/Rakefile +17 -0
- data/bin/ha-gen +47 -0
- data/lib/haddock.rb +129 -0
- data/test/names.txt +2 -0
- data/test/test_haddock.rb +89 -0
- metadata +61 -0
data/.autotest
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'autotest/restart'
|
4
|
+
|
5
|
+
# Autotest.add_hook :initialize do |at|
|
6
|
+
# at.extra_files << "../some/external/dependency.rb"
|
7
|
+
#
|
8
|
+
# at.libs << ":../some/external"
|
9
|
+
#
|
10
|
+
# at.add_exception 'vendor'
|
11
|
+
#
|
12
|
+
# at.add_mapping(/dependency.rb/) do |f, _|
|
13
|
+
# at.files_matching(/test_.*rb$/)
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# %w(TestA TestB).each do |klass|
|
17
|
+
# at.extra_class_map[klass] = "test/test_misc.rb"
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
|
21
|
+
# Autotest.add_hook :run_command do |at|
|
22
|
+
# system "rake build"
|
23
|
+
# end
|
data/History.rdoc
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
=== 0.2.1 / 2009-05-14
|
2
|
+
|
3
|
+
* 1 major, minor enhancement
|
4
|
+
|
5
|
+
* Ruby 1.9 compatibility.
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
=== 0.2.0 / 2009-03-29
|
10
|
+
|
11
|
+
* 1 major enhancement
|
12
|
+
|
13
|
+
* Changed "haddock" to "ha-gen" for Haskell compliance.
|
14
|
+
|
15
|
+
|
16
|
+
=== 0.1.1 / 2009-03-29
|
17
|
+
|
18
|
+
* 2 minor enhancements
|
19
|
+
|
20
|
+
* Add version option to command-line utility.
|
21
|
+
* Bumping up version numbers to improve credibility.
|
22
|
+
|
23
|
+
|
24
|
+
=== 0.1.0 / 2009-03-29
|
25
|
+
|
26
|
+
* 2 minor enhancements
|
27
|
+
|
28
|
+
* Friendlier command-line errors.
|
29
|
+
* General cleanup.
|
30
|
+
|
31
|
+
|
32
|
+
=== 0.0.1 / 2009-03-28
|
33
|
+
|
34
|
+
* 1 major enhancement
|
35
|
+
|
36
|
+
* Birthday!
|
data/Manifest.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
= haddock
|
2
|
+
|
3
|
+
http://github.com/stephencelis/haddock
|
4
|
+
|
5
|
+
|
6
|
+
== DESCRIPTION
|
7
|
+
|
8
|
+
A more memorable password generator. Swordfish? No, I got tired of that. I
|
9
|
+
changed it.
|
10
|
+
|
11
|
+
|
12
|
+
== FEATURES/PROBLEMS
|
13
|
+
|
14
|
+
* Secure!
|
15
|
+
|
16
|
+
|
17
|
+
== SYNOPSIS
|
18
|
+
|
19
|
+
In your apps:
|
20
|
+
|
21
|
+
require "rubygems"
|
22
|
+
require "haddock"
|
23
|
+
include Haddock
|
24
|
+
Password.generate # => "bowl9&bracky"
|
25
|
+
Password.generate(30) # => "Phlebotomus2473?nonconditioned"
|
26
|
+
Password.generate(8) # => "amy7@rax"
|
27
|
+
|
28
|
+
|
29
|
+
On the command line:
|
30
|
+
|
31
|
+
% ha-gen
|
32
|
+
bowl9&bracky
|
33
|
+
% ha-gen -l31
|
34
|
+
symbolistically5<overthwartways
|
35
|
+
|
36
|
+
|
37
|
+
For more information:
|
38
|
+
|
39
|
+
http://stephencelis.com/2009/03/29/whats-the-password-haddock.html
|
40
|
+
|
41
|
+
|
42
|
+
== REQUIREMENTS
|
43
|
+
|
44
|
+
A newline-delimited words file. By default, it uses "/usr/share/dict/words" or
|
45
|
+
"/usr/share/words".
|
46
|
+
|
47
|
+
Otherwise:
|
48
|
+
|
49
|
+
Haddock::Password.diction = "/path/to/words"
|
50
|
+
|
51
|
+
|
52
|
+
Or:
|
53
|
+
|
54
|
+
% ha-gen -f /path/to/words
|
55
|
+
|
56
|
+
|
57
|
+
== INSTALL
|
58
|
+
|
59
|
+
% [sudo] gem install haddock
|
60
|
+
|
61
|
+
|
62
|
+
== LICENSE
|
63
|
+
|
64
|
+
(The MIT License)
|
65
|
+
|
66
|
+
(c) 2009-2010 Stephen Celis, stephen@stephencelis.com.
|
67
|
+
|
68
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
69
|
+
of this software and associated documentation files (the "Software"), to deal
|
70
|
+
in the Software without restriction, including without limitation the rights
|
71
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
72
|
+
copies of the Software, and to permit persons to whom the Software is
|
73
|
+
furnished to do so, subject to the following conditions:
|
74
|
+
|
75
|
+
The above copyright notice and this permission notice shall be included in all
|
76
|
+
copies or substantial portions of the Software.
|
77
|
+
|
78
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
79
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
80
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
81
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
82
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
83
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
84
|
+
SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
$: << File.dirname(__FILE__) + "/lib"
|
2
|
+
require "rubygems"
|
3
|
+
require "haddock"
|
4
|
+
|
5
|
+
require "rake/testtask"
|
6
|
+
require 'bundler/gem_tasks'
|
7
|
+
|
8
|
+
desc "Default: run unit tests."
|
9
|
+
task :default => :test
|
10
|
+
|
11
|
+
desc "Run unit tests."
|
12
|
+
Rake::TestTask.new(:test) do |t|
|
13
|
+
t.libs << 'lib'
|
14
|
+
t.libs << 'test'
|
15
|
+
t.pattern = 'test/**/test_*.rb'
|
16
|
+
t.verbose = true
|
17
|
+
end
|
data/bin/ha-gen
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$: << File.dirname(__FILE__) + "/../lib"
|
4
|
+
require "optparse"
|
5
|
+
require "haddock"
|
6
|
+
include Haddock
|
7
|
+
|
8
|
+
OptionParser.new do |opts|
|
9
|
+
opts.banner = "usage: #{File.basename($0)} [options]"
|
10
|
+
|
11
|
+
opts.on("-V", "--version") do
|
12
|
+
puts "#{File.basename($0)}: v#{Haddock::VERSION}"
|
13
|
+
exit
|
14
|
+
end
|
15
|
+
|
16
|
+
opts.on("-h", "--help") do
|
17
|
+
puts opts
|
18
|
+
exit
|
19
|
+
end
|
20
|
+
|
21
|
+
opts.on("-l", "--length [length]", Integer) do |value|
|
22
|
+
@length = value
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on("-f", "--words [words file]") do |value|
|
26
|
+
Password.diction = value
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on("-d", "--delimiters [symbols]") do |value|
|
30
|
+
Password.delimiters = value
|
31
|
+
end
|
32
|
+
|
33
|
+
begin
|
34
|
+
opts.parse!
|
35
|
+
puts @length ? Password.generate(@length) : Password.generate
|
36
|
+
rescue OptionParser::ParseError, Password::LengthError => error
|
37
|
+
warn "#{File.basename($0)}: #{error.message} " +
|
38
|
+
"(must be between #{Password::MINIMUM} and #{Password::MAXIMUM})."
|
39
|
+
puts opts
|
40
|
+
abort
|
41
|
+
rescue Password::NoWordsError => error
|
42
|
+
warn "#{File.basename($0)}: #{error.message}."
|
43
|
+
puts "Word lists are available here: http://wordlist.sourceforge.net"
|
44
|
+
puts opts
|
45
|
+
abort
|
46
|
+
end
|
47
|
+
end
|
data/lib/haddock.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# A more memorable password generator. Swordfish? No, I got tired of that. I
|
2
|
+
# changed it.
|
3
|
+
module Haddock
|
4
|
+
VERSION = '0.2.3'
|
5
|
+
|
6
|
+
module Password
|
7
|
+
# The minimum password legnth.
|
8
|
+
MINIMUM = 8
|
9
|
+
|
10
|
+
# The maximum password length.
|
11
|
+
MAXIMUM = 31
|
12
|
+
|
13
|
+
# The default password length.
|
14
|
+
DEFAULT = 12
|
15
|
+
|
16
|
+
# Paths used to detect default words files.
|
17
|
+
PATHS = %w(/usr/share/dict/words /usr/share/words)
|
18
|
+
|
19
|
+
#TODO: move these out to file.txt and expand
|
20
|
+
ADJECTIVES = %w(reflective happy hungry light breathing handsome purple careful gifted mushy powerful mysterious itchy eager crooked immense loud hissing massive freezing heavy foolish internal ruthless)
|
21
|
+
ANIMALS = %w(badger horse squirrel cats beaver armadillo camel dingo fish hamster chipmunk crocodile cougar hedgehog pony shrew puma pocrupine platypus mustang sheep mongoose rabbit fox sloth stallion)
|
22
|
+
|
23
|
+
|
24
|
+
@@delimiters = '`~!@#$%^&*()-_=+[{]}\\|;:\'",<.>/?'
|
25
|
+
|
26
|
+
class << self
|
27
|
+
# Generates a more memorable password. Its one optional argument
|
28
|
+
# determines the length of the generated password, and cannot be less
|
29
|
+
# than 8 or greater than 31 characters (default: 12). Allows simplification
|
30
|
+
# with options
|
31
|
+
#
|
32
|
+
# Password.generate # => "bowl9&bracky"
|
33
|
+
# Password.generate(30) # => "Phlebotomus2473?nonconditioned"
|
34
|
+
# Password.generate(8) # => "amy7@rax"
|
35
|
+
# Password.generate(8, {:use_delimeter=>false} => "bonk5and"
|
36
|
+
# Password.generate(8, {:use_numbers=>false} => "jack!man"
|
37
|
+
|
38
|
+
def generate(length = DEFAULT, options = {})
|
39
|
+
unless defined? @@diction or options[:animal]
|
40
|
+
self.diction = PATHS.find { |path| File.exist? path }
|
41
|
+
end
|
42
|
+
options = {
|
43
|
+
:use_delimeter=>true,
|
44
|
+
:use_number=>true,
|
45
|
+
:animal => false,
|
46
|
+
:variable_length=>false
|
47
|
+
}.merge(options)
|
48
|
+
if length && length.to_s.upcase == "ANY"
|
49
|
+
length = rand(DEFAULT)+DEFAULT
|
50
|
+
options[:variable_length] = true
|
51
|
+
end
|
52
|
+
|
53
|
+
raise LengthError, "Invalid length" unless length.is_a? Integer
|
54
|
+
raise LengthError, "Password length is too short" if length < MINIMUM
|
55
|
+
raise LengthError, "Password length is too long" if length > MAXIMUM
|
56
|
+
|
57
|
+
words_limit = length * 0.75 # Ensure over-proportionate word lengths.
|
58
|
+
|
59
|
+
begin
|
60
|
+
if options[:animal]
|
61
|
+
words = %W(#{random_adj} #{random_animal})
|
62
|
+
else
|
63
|
+
words = %W(#{random_word} #{random_delimiter if options[:use_delimeter]}#{random_word})
|
64
|
+
end
|
65
|
+
words_length = words.join.length
|
66
|
+
return words.join if (words_length == length && !options[:use_number]) || options[:variable_length]
|
67
|
+
end until words_length < length && words_length > words_limit && options[:use_number]
|
68
|
+
|
69
|
+
words.join random_number(length - words_length) if options[:use_number]
|
70
|
+
end
|
71
|
+
|
72
|
+
def generate_fun
|
73
|
+
generate("any", {:use_delimeter=>false, :use_number=>false, :animal=>true})
|
74
|
+
end
|
75
|
+
|
76
|
+
# Sets the dictionary. Uses "/usr/share/dict/words" or
|
77
|
+
# "/usr/share/words" otherwise.
|
78
|
+
#
|
79
|
+
# Password.diction = File.expand_path(__FILE__) + "/my_words.txt"
|
80
|
+
def diction=(path)
|
81
|
+
@@diction = IO.readlines path
|
82
|
+
rescue TypeError
|
83
|
+
raise NoWordsError, "No words file found"
|
84
|
+
rescue Errno::ENOENT
|
85
|
+
raise NoWordsError, "No words file at #{path.inspect}"
|
86
|
+
end
|
87
|
+
|
88
|
+
# Sets the list of characters that can delimit words. Default:
|
89
|
+
#
|
90
|
+
# `~!@#$%^&*()-_=+[{]}\|;:'",<.>/?
|
91
|
+
def delimiters=(string)
|
92
|
+
@@delimiters = string
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def random_adj
|
97
|
+
ADJECTIVES[rand(ADJECTIVES.length)]
|
98
|
+
end
|
99
|
+
|
100
|
+
def random_animal
|
101
|
+
ANIMALS[rand(ANIMALS.length)].capitalize
|
102
|
+
end
|
103
|
+
|
104
|
+
def random_word
|
105
|
+
@@diction[rand(@@diction.length)].chomp
|
106
|
+
end
|
107
|
+
|
108
|
+
def random_delimiter
|
109
|
+
@@delimiters[rand(@@delimiters.length), 1]
|
110
|
+
end
|
111
|
+
|
112
|
+
def random_number(digits)
|
113
|
+
begin
|
114
|
+
number = rand(10 ** digits).to_s
|
115
|
+
end until number.length == digits
|
116
|
+
|
117
|
+
number
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Raised if a password is generated with too few or too many characters.
|
122
|
+
class LengthError < ArgumentError
|
123
|
+
end
|
124
|
+
|
125
|
+
# Raised if no words file is found.
|
126
|
+
class NoWordsError < StandardError
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/test/names.txt
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "haddock"
|
3
|
+
|
4
|
+
class TestHaddock < Test::Unit::TestCase
|
5
|
+
include Haddock
|
6
|
+
|
7
|
+
def test_generates_password
|
8
|
+
password = Password.generate
|
9
|
+
assert_instance_of String, password
|
10
|
+
assert_equal Password::DEFAULT, password.length
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_password_format
|
14
|
+
symbols = Password.send :class_variable_get, :@@delimiters
|
15
|
+
symbols = "[#{Regexp.quote symbols}]"
|
16
|
+
pattern = /^[a-z]+[0-9]+#{symbols}{1}[a-z]+/i
|
17
|
+
assert_match(pattern, Password.generate)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_generates_variable_password
|
21
|
+
assert_equal Password.generate(18).length, 18
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_accepts_alternate_wordlist
|
25
|
+
Password.diction = path = File.dirname(__FILE__) + "/names.txt"
|
26
|
+
pattern = Regexp.new File.read(path).split.join("|")
|
27
|
+
assert_match(pattern, Password.generate(14))
|
28
|
+
ensure
|
29
|
+
Password.diction = "/usr/share/dict/words"
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_accepts_alternate_symbols_list
|
33
|
+
Password.delimiters = "X"
|
34
|
+
assert_equal "X", Password.send(:random_delimiter)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_fail_on_too_short
|
38
|
+
assert_raise Password::LengthError do
|
39
|
+
Password.generate(Password::MINIMUM - 1)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_fail_on_too_long
|
44
|
+
assert_raise Password::LengthError do
|
45
|
+
Password.generate(Password::MAXIMUM + 1)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_fail_on_invalid
|
50
|
+
assert_raise Password::LengthError do
|
51
|
+
Password.generate("invalid")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_fail_on_invalid_path
|
56
|
+
assert_raise Password::NoWordsError do
|
57
|
+
Password.diction = "invalid/path"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_fail_on_nil_path
|
62
|
+
assert_raise Password::NoWordsError do
|
63
|
+
Password.diction = nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_allows_non_delimiters
|
68
|
+
Password.delimiters = "#"
|
69
|
+
password = Password.generate(10, {:use_delimeter=>false})
|
70
|
+
assert_no_match /\#/, password
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_allows_non_numbers
|
74
|
+
password = Password.generate(10, {:use_number=>false})
|
75
|
+
assert_no_match /\d/, password
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_length_persists_without_numbers_or_delimeters
|
79
|
+
password = Password.generate(10, {:use_number=>false, :use_delimeter=>false})
|
80
|
+
assert_equal 10, password.length
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_can_generate_fun
|
84
|
+
password = Password.generate_fun
|
85
|
+
assert_instance_of String, password
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
end
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: perch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Stephen Celis, Paul Brennan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-08-11 00:00:00.000000000Z
|
13
|
+
dependencies: []
|
14
|
+
description: A more memorable password generator. Swordfish? No, I got tired of that.
|
15
|
+
I changed it.
|
16
|
+
email: stephen@stephencelis.com
|
17
|
+
executables:
|
18
|
+
- ha-gen
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files:
|
21
|
+
- History.rdoc
|
22
|
+
- Manifest.txt
|
23
|
+
- README.rdoc
|
24
|
+
files:
|
25
|
+
- .autotest
|
26
|
+
- History.rdoc
|
27
|
+
- Manifest.txt
|
28
|
+
- README.rdoc
|
29
|
+
- Rakefile
|
30
|
+
- bin/ha-gen
|
31
|
+
- lib/haddock.rb
|
32
|
+
- test/names.txt
|
33
|
+
- test/test_haddock.rb
|
34
|
+
homepage: http://github.com/yule/haddock
|
35
|
+
licenses: []
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options:
|
38
|
+
- --main
|
39
|
+
- README.rdoc
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubyforge_project: haddock
|
56
|
+
rubygems_version: 1.8.6
|
57
|
+
signing_key:
|
58
|
+
specification_version: 3
|
59
|
+
summary: A more memorable password generator, based on haddock
|
60
|
+
test_files:
|
61
|
+
- test/test_haddock.rb
|