passphrase 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +6 -0
- data/README.md +82 -37
- data/lib/passphrase.rb +1 -0
- data/lib/passphrase/CLI.rb +29 -8
- data/lib/passphrase/default.rb +9 -1
- data/lib/passphrase/diceware_method.rb +4 -17
- data/lib/passphrase/diceware_random.rb +3 -1
- data/lib/passphrase/passphrase_string.rb +1 -1
- data/lib/passphrase/version.rb +1 -1
- data/lib/passphrase/wordlist_database.rb +103 -0
- data/spec/passphrase/cli_spec.rb +12 -0
- data/spec/passphrase/diceware_method_spec.rb +4 -14
- data/spec/passphrase/diceware_random_spec.rb +19 -1
- data/spec/passphrase/wordlist_database_spec.rb +147 -0
- metadata +4 -6
- metadata.gz.sig +1 -1
- data/lib/passphrase/language_query.rb +0 -26
- data/lib/passphrase/word_query.rb +0 -21
- data/spec/passphrase/language_query_spec.rb +0 -48
- data/spec/passphrase/word_query_spec.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4033cb9da1097b10de0c83644706f4b5ec71d04a
|
4
|
+
data.tar.gz: aba0d115c820f615f3f6124688f548b96f0fe9ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4cb595f7d1b1de7ee367a32817a6c5f9f1e568a12433303867f188192d7c8e4354c7ea3b4d3e40c5218c69592d3ff8b9a47b0fb81169c7263cea198f5bcc1b77
|
7
|
+
data.tar.gz: c2d8bb80517d152cfdf8f23e4c1c8bcb2f9ca52dfd01dee89b682b931940a849714219176919dade72afbdea97daaa7c9ba16bbbf6b4dea183081630d0352c5d
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
All notable changes to this project will be documented in this file. This
|
3
3
|
project adheres to [Semantic Versioning](http://semver.org/).
|
4
4
|
|
5
|
+
## UNRELEASED
|
6
|
+
### Added
|
7
|
+
- List available languages.
|
8
|
+
- Added option `--languages|-l` to select a subset of languages.
|
9
|
+
- Added a WordlistDatabase class.
|
10
|
+
|
5
11
|
## Version 1.1.0 - 2015-04-14
|
6
12
|
### Added
|
7
13
|
- Added SHA256 checksum calculation to rake_helper.rb.
|
data/README.md
CHANGED
@@ -2,9 +2,10 @@
|
|
2
2
|
Use Passphrase to generate a passphrase for SSH or GPG keys. For example, on
|
3
3
|
the command-line, run
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
```bash
|
6
|
+
$ passphrase --num-words=4
|
7
|
+
dokusi uolgo allunga totalisa
|
8
|
+
```
|
8
9
|
or programmatically,
|
9
10
|
|
10
11
|
```ruby
|
@@ -12,12 +13,15 @@ require "passphrase"
|
|
12
13
|
p = Passphrase::Passphrase.new(number_of_words: 4)
|
13
14
|
passphrase = p.passphrase
|
14
15
|
```
|
16
|
+
|
15
17
|
Passphrase also has the capability to generate passwords (indirectly).
|
16
18
|
|
17
19
|
## Installation
|
18
20
|
The Passphrase command-line tool and library can be installed with
|
19
21
|
|
20
|
-
|
22
|
+
```bash
|
23
|
+
$ gem install passphrase
|
24
|
+
```
|
21
25
|
|
22
26
|
However, because the gem is cryptographically signed to prevent tampering, the
|
23
27
|
preferred installation command should include the `--trust-policy` security
|
@@ -25,38 +29,63 @@ option, which causes the gem to be verified before being installed. To invoke
|
|
25
29
|
this option, you must first add my public key `esumbar.pem` to your list of
|
26
30
|
trusted certificates, as follows.
|
27
31
|
|
28
|
-
|
32
|
+
```bash
|
33
|
+
$ gem cert --add <(curl -Ls https://raw.githubusercontent.com/esumbar/passphrase/master/certs/esumbar.pem)
|
34
|
+
```
|
29
35
|
|
30
36
|
Finally, specify the appropriate security level when installing.
|
31
37
|
|
32
|
-
|
38
|
+
```bash
|
39
|
+
$ gem install passphrase --trust-policy MediumSecurity
|
40
|
+
```
|
33
41
|
|
34
|
-
Using `MediumSecurity` rather than `HighSecurity` omits dependent gems
|
35
|
-
|
42
|
+
Using `MediumSecurity` rather than `HighSecurity` omits dependent gems that
|
43
|
+
are not signed from the verification process. Passphrase depends on the
|
44
|
+
unsigned gem `sqlite3`.
|
36
45
|
|
37
46
|
## Basic usage
|
38
47
|
### Command-line tool
|
39
48
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
49
|
+
```bash
|
50
|
+
$ passphrase --help
|
51
|
+
Usage: passphrase [options]
|
52
|
+
-l, --languages=LANG1,... Specify languages to use, none for a listing
|
53
|
+
(default: --languages=all)
|
54
|
+
-n, --num-words=NUM Number of words in passphrase 3..10
|
55
|
+
(default: --num-words=5)
|
56
|
+
-p, --[no-]passwordize Add one cap, one num, and one special char
|
57
|
+
(default: --no-passwordize)
|
58
|
+
-r, --[no-]random-org Use RANDOM.ORG to generate random numbers
|
59
|
+
(default: --no-random-org)
|
60
|
+
-h, --help Show this message
|
61
|
+
-v, --version Show version
|
62
|
+
|
63
|
+
$ # generate a passphrase using default settings
|
64
|
+
$ passphrase
|
65
|
+
sinmak termyne ismus affidavo recur
|
66
|
+
|
67
|
+
$ # generate a four-word passphrase
|
68
|
+
$ passphrase -n4
|
69
|
+
apaisado vermouth seemag ebelik
|
70
|
+
|
71
|
+
$ # generate a passphrase using only English and Spanish words
|
72
|
+
$ passphrase --languages=english,spanish
|
73
|
+
fumada hearsay murcio phosphor azufroso
|
74
|
+
|
75
|
+
$ # generate a three-word Polish passphrase and password
|
76
|
+
$ passphrase -lp -n3 -p
|
77
|
+
podejrza zmalala wypadkow
|
78
|
+
8odej=za_Xmalala_wypadkow
|
79
|
+
|
80
|
+
$ # list the available languages
|
81
|
+
$ passphrase -l
|
82
|
+
afrikaans
|
83
|
+
croatian
|
84
|
+
czech
|
85
|
+
diceware
|
86
|
+
english
|
87
|
+
...
|
88
|
+
```
|
60
89
|
|
61
90
|
### Ruby library
|
62
91
|
|
@@ -78,10 +107,15 @@ passphrase3 = p.generate.passphrase
|
|
78
107
|
# generate an array of six-word passphrases
|
79
108
|
passphrase_array = Array.new(100)
|
80
109
|
Passphrase::Passphrase.new(number_of_words: 6) do |p|
|
81
|
-
passphrase_array.map! { |
|
110
|
+
passphrase_array.map! { |array_element| p.generate.passphrase }
|
82
111
|
end
|
83
112
|
|
84
|
-
# generate a
|
113
|
+
# generate a passphrase using only French and Italian words
|
114
|
+
options = { number_of_words: 4, languages: %w( fr it ) }
|
115
|
+
p = Passphrase::Passphrase.new(options)
|
116
|
+
passphrase = p.passphrase
|
117
|
+
|
118
|
+
# generate a password from a passphrase
|
85
119
|
p = Passphrase::Passphrase.new(number_of_words: 3)
|
86
120
|
passphrase = p.passphrase
|
87
121
|
password = passphrase.to_password
|
@@ -150,6 +184,13 @@ have the option of requesting random numbers from
|
|
150
184
|
[RANDOM.ORG](http://www.random.org). However, because RANDOM.ORG depends on
|
151
185
|
network access, it is susceptible to network problems, and is also slower.
|
152
186
|
|
187
|
+
### Subset of languages
|
188
|
+
By default, Passphrase randomly selects words from the collection of available
|
189
|
+
languages. If desired, the selection can be limited to a subset of languages.
|
190
|
+
This can be done on the command-line and in code by supplying a list of
|
191
|
+
language names or abbreviations. A minimal abbreviation uses the first one or
|
192
|
+
two letters of a language name, just enough to avoid ambiguity.
|
193
|
+
|
153
194
|
### Passwords
|
154
195
|
A typical passphrase will not satisfy password policies that require the use
|
155
196
|
of upper case letters, numbers, and special characters (~, !, and the like).
|
@@ -173,13 +214,17 @@ compliment of 7776 entries.
|
|
173
214
|
To run the command-line tool within the repository directory, try `ruby -Ilib
|
174
215
|
bin/passphrase`. You can also experiment with the library in irb. For example,
|
175
216
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
217
|
+
```bash
|
218
|
+
$ irb -Ilib -rpassphrase
|
219
|
+
>> p = Passphrase::Passphrase.new(number_of_words: 3)
|
220
|
+
=> {:passphrase=>"jazzy vannier viscount", :number_of_words=>3, ... }
|
221
|
+
>> p.passphrase
|
222
|
+
=> "jazzy vannier viscount"
|
223
|
+
>> p.passphrase.to_password
|
224
|
+
=> "jazz{_2annier_vBscount"
|
225
|
+
>> p = Passphrase::Passphrase.new(languages: ["e", "fr"])
|
226
|
+
=> {:passphrase=>"obstrua lamparos orgy forerez deduce", ... }
|
227
|
+
```
|
183
228
|
|
184
229
|
Run the tests with `rake spec`.
|
185
230
|
|
data/lib/passphrase.rb
CHANGED
data/lib/passphrase/CLI.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "optparse"
|
2
|
+
require "passphrase/wordlist_database"
|
2
3
|
|
3
4
|
module Passphrase
|
4
5
|
# The CLI class encapsulates a simple command line interface to the
|
@@ -38,13 +39,18 @@ module Passphrase
|
|
38
39
|
default_random_org = Default.options[:use_random_org] ? "--random-org" : "--no-random-org"
|
39
40
|
default_number_range = Default.number_range
|
40
41
|
|
41
|
-
parser = OptionParser.new do |opts|
|
42
|
+
parser = OptionParser.new(nil, 28, " " * 4) do |opts|
|
42
43
|
opts.banner = "Usage: passphrase [options]"
|
44
|
+
opts.on(:OPTIONAL, "-l", "--languages=LANG1,...", Array,
|
45
|
+
"Specify languages to use, none for a listing",
|
46
|
+
"(default: --languages=all)") do |l|
|
47
|
+
options[:languages] = (l || [])
|
48
|
+
end
|
43
49
|
opts.on(:REQUIRED, "-n NUM", "--num-words=NUM", Integer,
|
44
50
|
"Number of words in passphrase #{default_number_range}",
|
45
|
-
"(default:
|
51
|
+
"(default: --num-words=#{default_number_of_words})") do |n|
|
46
52
|
options[:number_of_words] = n
|
47
|
-
|
53
|
+
end
|
48
54
|
opts.on(:NONE, "-p", "--[no-]passwordize",
|
49
55
|
"Add one cap, one num, and one special char",
|
50
56
|
"(default: #{default_passwordize})") do |p|
|
@@ -67,15 +73,15 @@ module Passphrase
|
|
67
73
|
|
68
74
|
begin
|
69
75
|
parser.parse!(args)
|
76
|
+
display_languages(options)
|
70
77
|
validate_number_of_words(options)
|
71
78
|
passphrase = Passphrase.new(options).passphrase
|
72
|
-
|
73
|
-
puts passphrase.to_password if options[:passwordize]
|
79
|
+
print_out(passphrase, options)
|
74
80
|
rescue OptionParser::InvalidOption => e
|
75
81
|
handle_error(e)
|
76
82
|
rescue OptionParser::MissingArgument => e
|
77
83
|
handle_error(e)
|
78
|
-
#
|
84
|
+
# Gracefully handle exit(0) from --help, --version, and --language options
|
79
85
|
rescue SystemExit => e
|
80
86
|
exit(e.status)
|
81
87
|
rescue Exception => e
|
@@ -83,20 +89,35 @@ module Passphrase
|
|
83
89
|
end
|
84
90
|
end
|
85
91
|
|
92
|
+
def self.display_languages(options)
|
93
|
+
if options[:languages].empty?
|
94
|
+
puts WordlistDatabase.connect.from(:languages).all
|
95
|
+
exit(0)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
86
99
|
def self.validate_number_of_words(options)
|
87
100
|
number_of_words = options[:number_of_words]
|
88
101
|
unless Default.number_range.include?(number_of_words)
|
89
|
-
raise "
|
102
|
+
raise "Number of words out of range #{Default.number_range}"
|
90
103
|
end
|
91
104
|
end
|
92
105
|
|
106
|
+
def self.print_out(passphrase, options)
|
107
|
+
puts passphrase
|
108
|
+
puts passphrase.to_password if options[:passwordize]
|
109
|
+
end
|
110
|
+
|
93
111
|
def self.handle_error(error)
|
94
112
|
STDERR.puts "ERROR: #{error.message}"
|
95
113
|
exit(1)
|
96
114
|
end
|
97
115
|
|
98
116
|
class << self
|
99
|
-
private :
|
117
|
+
private :display_languages
|
118
|
+
private :validate_number_of_words
|
119
|
+
private :print_out
|
120
|
+
private :handle_error
|
100
121
|
end
|
101
122
|
end
|
102
123
|
end
|
data/lib/passphrase/default.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
module Passphrase
|
2
|
+
# Class instance variables in this class define default values for
|
3
|
+
# {Passphrase} options and the range for the number of words that can be
|
4
|
+
# specified for a passphrase on the command-line.
|
2
5
|
class Default
|
3
6
|
class << self
|
4
7
|
# @return [Hash] the default options that the command line interface
|
@@ -9,7 +12,12 @@ module Passphrase
|
|
9
12
|
attr_reader :number_range
|
10
13
|
end
|
11
14
|
|
12
|
-
@options = {
|
15
|
+
@options = {
|
16
|
+
languages: ["all"],
|
17
|
+
number_of_words: 5,
|
18
|
+
passwordize: nil,
|
19
|
+
use_random_org: nil
|
20
|
+
}
|
13
21
|
@number_range = (3..10)
|
14
22
|
end
|
15
23
|
end
|
@@ -1,7 +1,5 @@
|
|
1
|
-
require "sqlite3"
|
2
1
|
require "passphrase/diceware_random"
|
3
|
-
require "passphrase/
|
4
|
-
require "passphrase/word_query"
|
2
|
+
require "passphrase/wordlist_database"
|
5
3
|
|
6
4
|
module Passphrase
|
7
5
|
# This class implements the Diceware Method for generating a passphrase. It
|
@@ -20,8 +18,9 @@ module Passphrase
|
|
20
18
|
def initialize(options)
|
21
19
|
@number_of_words = options[:number_of_words]
|
22
20
|
@random = DicewareRandom.new(options[:use_random_org])
|
23
|
-
|
24
|
-
|
21
|
+
db = WordlistDatabase.connect
|
22
|
+
@languages = db.from(:languages).only(options[:languages])
|
23
|
+
@words = db.from(:words)
|
25
24
|
end
|
26
25
|
|
27
26
|
# Runs the Diceware method and returns its result to the calling
|
@@ -37,18 +36,6 @@ module Passphrase
|
|
37
36
|
|
38
37
|
private
|
39
38
|
|
40
|
-
def setup_database
|
41
|
-
wordlist_file = "wordlist/words.sqlite3"
|
42
|
-
wordlist_path = File.join(File.dirname(__FILE__), wordlist_file)
|
43
|
-
raise "Wordlist database not found" unless File.exist?(wordlist_path)
|
44
|
-
@db = SQLite3::Database.new(wordlist_path, readonly: true)
|
45
|
-
end
|
46
|
-
|
47
|
-
def setup_queries
|
48
|
-
@languages = LanguageQuery.new(@db)
|
49
|
-
@words = WordQuery.new(@db)
|
50
|
-
end
|
51
|
-
|
52
39
|
def get_random_languages
|
53
40
|
@random_languages = @random.indices(@number_of_words, @languages.count)
|
54
41
|
@random_languages.map! { |index| @languages[index] }
|
@@ -65,6 +65,7 @@ module Passphrase
|
|
65
65
|
def setup_remote_generator
|
66
66
|
self.class.class_eval do
|
67
67
|
def generate_random_numbers(count, maximum, minimum=0)
|
68
|
+
return Array.new(count, maximum) if maximum == minimum
|
68
69
|
# Check quota before proceeding and thereafter every 1000 uses to
|
69
70
|
# comply with random.org guidelines.
|
70
71
|
check_random_org_quota if self.class.random_org_requests % 1000 == 0
|
@@ -92,7 +93,7 @@ module Passphrase
|
|
92
93
|
open("#{@random_org_uri}#{query}") do |data|
|
93
94
|
quota = data.gets.chomp.to_i
|
94
95
|
over_quota_message = "RANDOM.ORG over quota, try again in 10 minutes"
|
95
|
-
raise "
|
96
|
+
raise "#{over_quota_message}" if quota < 0
|
96
97
|
end
|
97
98
|
end
|
98
99
|
|
@@ -104,6 +105,7 @@ module Passphrase
|
|
104
105
|
def setup_local_generator
|
105
106
|
self.class.class_eval do
|
106
107
|
def generate_random_numbers(count, maximum, minimum=0)
|
108
|
+
return Array.new(count, maximum) if maximum == minimum
|
107
109
|
max = maximum - minimum + 1
|
108
110
|
offset = minimum
|
109
111
|
# array_of_random_numbers
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Passphrase
|
2
|
-
# This subclass of String implements the {#
|
2
|
+
# This subclass of String implements the {#to_password} instance method for
|
3
3
|
# substituting a capital letter, number, and special character into a
|
4
4
|
# passphrase.
|
5
5
|
class PassphraseString < String
|
data/lib/passphrase/version.rb
CHANGED
@@ -0,0 +1,103 @@
|
|
1
|
+
require "sqlite3"
|
2
|
+
|
3
|
+
module Passphrase
|
4
|
+
# This class encapsulates the {#count} and {#[]} queries against the
|
5
|
+
# "languages" table in the "words" SQLite 3 database. It also provides the
|
6
|
+
# {#only} method which allows a subset of languages to be specified.
|
7
|
+
class Language
|
8
|
+
def initialize(db)
|
9
|
+
@languages = []
|
10
|
+
sql = "SELECT language FROM languages"
|
11
|
+
db.execute(sql).each { |lang| @languages << lang.first }
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Integer] the number of rows in the languages table
|
15
|
+
def count
|
16
|
+
@languages.size
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param index [Integer] selects a specific row in the languages table
|
20
|
+
# @return [String] the language corresponding to the given index
|
21
|
+
def [](index)
|
22
|
+
@languages[index]
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Array] all rows in the languages table
|
26
|
+
def all
|
27
|
+
@languages
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param language_list [Array] restrict languages to those in the list
|
31
|
+
# @return [self] to allow chaining methods
|
32
|
+
def only(language_list)
|
33
|
+
return self if /^all$/ =~ language_list.first
|
34
|
+
validate(language_list)
|
35
|
+
@languages.keep_if do |language|
|
36
|
+
language_list.any? { |l| language.match("^#{l}") }
|
37
|
+
end
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# Make sure that each language specification matches at least one
|
44
|
+
# language.
|
45
|
+
def validate(language_list)
|
46
|
+
language_list.each do |l|
|
47
|
+
matches_language = @languages.any? { |language| language.match("^#{l}") }
|
48
|
+
raise "No language match for #{l}" unless matches_language
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
# This class encapsulates the {#where} query against the "words" table in
|
53
|
+
# the "words" SQLite 3 database. The filter parameter must be a hash that
|
54
|
+
# specifies a language and sequence of die rolls.
|
55
|
+
# @example
|
56
|
+
# {language: "afrikaans", die_rolls: "11111"}
|
57
|
+
class Word
|
58
|
+
def initialize(db)
|
59
|
+
@db = db
|
60
|
+
end
|
61
|
+
|
62
|
+
# @param filter [Hash] specifies the language/die_roll combination
|
63
|
+
# @return [String] a string of space-separated words from the wordlist
|
64
|
+
def where(filter)
|
65
|
+
sql = "SELECT words " +
|
66
|
+
"FROM words " +
|
67
|
+
"WHERE language = :language AND die_rolls = :die_rolls"
|
68
|
+
@db.get_first_value(sql, filter)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
# This class encapsulates the Diceware wordlist database file and dispatches
|
72
|
+
# one of two table query objects via the {#from} method.
|
73
|
+
class WordlistDatabase
|
74
|
+
# A "connect" method seems more natural than "new" when connecting to a
|
75
|
+
# database.
|
76
|
+
def self.connect
|
77
|
+
new
|
78
|
+
end
|
79
|
+
|
80
|
+
def initialize
|
81
|
+
wordlist_file = "wordlist/words.sqlite3"
|
82
|
+
wordlist_path = File.join(File.dirname(__FILE__), wordlist_file)
|
83
|
+
raise "Wordlist database file not found" unless File.exist?(wordlist_path)
|
84
|
+
@db = SQLite3::Database.new(wordlist_path, readonly: true)
|
85
|
+
end
|
86
|
+
|
87
|
+
# @param table [Symbol] the table name
|
88
|
+
# @return [Language] if the "languages" table is specified
|
89
|
+
# @return [Word] if the "words" table is specified
|
90
|
+
def from(table)
|
91
|
+
case table
|
92
|
+
when :languages
|
93
|
+
Language.new(@db)
|
94
|
+
when :words
|
95
|
+
Word.new(@db)
|
96
|
+
else
|
97
|
+
raise "Unknown table #{table}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
alias [] from
|
102
|
+
end
|
103
|
+
end
|
data/spec/passphrase/cli_spec.rb
CHANGED
@@ -16,14 +16,26 @@ module Passphrase
|
|
16
16
|
expect(CLI).to respond_to(:parse).with(1).argument
|
17
17
|
end
|
18
18
|
|
19
|
+
it "does not respond to class method display_languages() (private)" do
|
20
|
+
expect(CLI).not_to respond_to(:display_languages)
|
21
|
+
end
|
22
|
+
|
19
23
|
it "does not respond to class method validate_number_of_words() (private)" do
|
20
24
|
expect(CLI).not_to respond_to(:validate_number_of_words)
|
21
25
|
end
|
22
26
|
|
27
|
+
it "does not respond to class method print_out() (private)" do
|
28
|
+
expect(CLI).not_to respond_to(:print_out)
|
29
|
+
end
|
30
|
+
|
23
31
|
it "does not respond to class method handle_error() (private)" do
|
24
32
|
expect(CLI).not_to respond_to(:handle_error)
|
25
33
|
end
|
26
34
|
|
35
|
+
it "exits when option -l is supplied" do
|
36
|
+
expect { CLI.parse(["-l"]) }.to raise_error(SystemExit)
|
37
|
+
end
|
38
|
+
|
27
39
|
it "does not emit an error when option -n 3 is supplied" do
|
28
40
|
expect(CLI).not_to receive(:handle_error)
|
29
41
|
CLI.parse(["-n", "3"])
|
@@ -6,25 +6,15 @@ module Passphrase
|
|
6
6
|
expect(DicewareMethod).to respond_to(:run).with(1).argument
|
7
7
|
end
|
8
8
|
|
9
|
-
context "initialized with
|
9
|
+
context "initialized with default options" do
|
10
10
|
before do
|
11
|
-
@
|
12
|
-
options = { number_of_words: @number_of_words, use_random_org: nil }
|
13
|
-
@diceware = DicewareMethod.new(options)
|
11
|
+
@diceware = DicewareMethod.new(Default.options)
|
14
12
|
end
|
15
13
|
|
16
14
|
it "responds to the run() method with no arguments" do
|
17
15
|
expect(@diceware).to respond_to(:run)
|
18
16
|
end
|
19
17
|
|
20
|
-
it "does not respond to the setup_database() method (private)" do
|
21
|
-
expect(@diceware).not_to respond_to(:setup_database)
|
22
|
-
end
|
23
|
-
|
24
|
-
it "does not respond to the setup_queries() method (private)" do
|
25
|
-
expect(@diceware).not_to respond_to(:setup_queries)
|
26
|
-
end
|
27
|
-
|
28
18
|
it "does not respond to the get_random_languages() method (private)" do
|
29
19
|
expect(@diceware).not_to respond_to(:get_random_languages)
|
30
20
|
end
|
@@ -50,9 +40,9 @@ module Passphrase
|
|
50
40
|
)
|
51
41
|
end
|
52
42
|
|
53
|
-
it "each array contains
|
43
|
+
it "each array contains the default number of words elements" do
|
54
44
|
@result.each do |result|
|
55
|
-
expect(result.size).to eq(
|
45
|
+
expect(result.size).to eq(Default.options[:number_of_words])
|
56
46
|
end
|
57
47
|
end
|
58
48
|
|
@@ -52,6 +52,24 @@ module Passphrase
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
describe "#indices(4, 1)" do
|
56
|
+
before do
|
57
|
+
@result = @random.indices(4, 1)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "returns an array" do
|
61
|
+
expect(@result).to be_an_instance_of(Array)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "of size 4" do
|
65
|
+
expect(@result.size).to eq(4)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "where each element is zero" do
|
69
|
+
expect(@result).to all eq(0)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
55
73
|
describe "#die_rolls(6)" do
|
56
74
|
before do
|
57
75
|
@result = @random.die_rolls(6)
|
@@ -97,7 +115,7 @@ module Passphrase
|
|
97
115
|
@random = DicewareRandom.new(true)
|
98
116
|
end
|
99
117
|
|
100
|
-
it "increments the
|
118
|
+
it "increments the RANDOM.ORG request count on indices()" do
|
101
119
|
expect {
|
102
120
|
@random.indices(4, 15)
|
103
121
|
}.to change(DicewareRandom, :random_org_requests).by(1)
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require "passphrase"
|
2
|
+
|
3
|
+
module Passphrase
|
4
|
+
RSpec.describe WordlistDatabase, "Diceware wordlist database class" do
|
5
|
+
before do
|
6
|
+
@db = WordlistDatabase.connect
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "#from(:languages)" do
|
10
|
+
it "returns a Language object" do
|
11
|
+
expect(@db.from(:languages)).to be_an_instance_of(Language)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#[:languages]" do
|
16
|
+
it "returns a Language object" do
|
17
|
+
expect(@db[:languages]).to be_an_instance_of(Language)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#from(:words)" do
|
22
|
+
it "returns a Word object" do
|
23
|
+
expect(@db.from(:words)).to be_an_instance_of(Word)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#[:words]" do
|
28
|
+
it "returns a Word object" do
|
29
|
+
expect(@db[:words]).to be_an_instance_of(Word)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#from(:nonexistant)" do
|
34
|
+
it "raises an error" do
|
35
|
+
expect { @db.from(:nonexistant) }.to raise_error("Unknown table nonexistant")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
RSpec.describe Word, "queries the words table in a valid wordlist database" do
|
41
|
+
before do
|
42
|
+
@words = WordlistDatabase.connect.from(:words)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "responds to the where() method with one argument" do
|
46
|
+
expect(@words).to respond_to(:where).with(1).argument
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#where([Hash])" do
|
50
|
+
before do
|
51
|
+
@selection = @words.where(language: "afrikaans", die_rolls: "11111")
|
52
|
+
end
|
53
|
+
|
54
|
+
it "returns a string" do
|
55
|
+
expect(@selection).to be_an_instance_of(String)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "that is not empty" do
|
59
|
+
expect(@selection).not_to be_empty
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
RSpec.describe Language, "queries the languages table in a valid wordlist database" do
|
65
|
+
before do
|
66
|
+
@languages = WordlistDatabase.connect.from(:languages)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "responds to the count() method with no arguments" do
|
70
|
+
expect(@languages).to respond_to(:count).with(0).arguments
|
71
|
+
end
|
72
|
+
|
73
|
+
it "responds to the []() method with one argument" do
|
74
|
+
expect(@languages).to respond_to(:[]).with(1).argument
|
75
|
+
end
|
76
|
+
|
77
|
+
it "responds to the all() method with no arguments" do
|
78
|
+
expect(@languages).to respond_to(:all).with(0).arguments
|
79
|
+
end
|
80
|
+
|
81
|
+
it "responds to the only() method with one argument" do
|
82
|
+
expect(@languages).to respond_to(:only).with(1).argument
|
83
|
+
end
|
84
|
+
|
85
|
+
it "does not respond to the validate() method (private)" do
|
86
|
+
expect(@languages).not_to respond_to(:validate)
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#count" do
|
90
|
+
it "returns 15" do
|
91
|
+
expect(@languages.count).to eq(15)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "#[](0)" do
|
96
|
+
before do
|
97
|
+
@language = @languages[0]
|
98
|
+
end
|
99
|
+
|
100
|
+
it "returns a string" do
|
101
|
+
expect(@language).to be_an_instance_of(String)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "that is not empty" do
|
105
|
+
expect(@language).not_to be_empty
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "#all" do
|
110
|
+
before do
|
111
|
+
@result = @languages.all
|
112
|
+
end
|
113
|
+
|
114
|
+
it "returns an array" do
|
115
|
+
expect(@result).to be_an_instance_of(Array)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "with 15 elements" do
|
119
|
+
expect(@result.size).to eq(15)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "of all strings" do
|
123
|
+
expect(@result).to all be_an_instance_of(String)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "#only([\"e\", \"fr\"])" do
|
128
|
+
before do
|
129
|
+
@languages.only(["e", "fr"])
|
130
|
+
end
|
131
|
+
|
132
|
+
it "produces a count of 2" do
|
133
|
+
expect(@languages.count).to eq(2)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "returns only english and french" do
|
137
|
+
expect(@languages.all).to eq(%w( english french ))
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "#only([\"zz\"])" do
|
142
|
+
it "raises an error" do
|
143
|
+
expect { @languages.only(["zz"]) }.to raise_error("No language match for zz")
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: passphrase
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Edmund Sumbar
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
aoKZhUfRmAZDUgzW4PMtMxO34fQDbDYevBzMcxmkHNDs87ROMOuPLWJGLcKpgGwJ
|
31
31
|
TxzUtBfb7E+Qa/wyo0Ws7A==
|
32
32
|
-----END CERTIFICATE-----
|
33
|
-
date: 2015-04-
|
33
|
+
date: 2015-04-18 00:00:00.000000000 Z
|
34
34
|
dependencies:
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: sqlite3
|
@@ -137,19 +137,17 @@ files:
|
|
137
137
|
- lib/passphrase/default.rb
|
138
138
|
- lib/passphrase/diceware_method.rb
|
139
139
|
- lib/passphrase/diceware_random.rb
|
140
|
-
- lib/passphrase/language_query.rb
|
141
140
|
- lib/passphrase/passphrase.rb
|
142
141
|
- lib/passphrase/passphrase_string.rb
|
143
142
|
- lib/passphrase/version.rb
|
144
|
-
- lib/passphrase/word_query.rb
|
145
143
|
- lib/passphrase/wordlist/words.sqlite3
|
144
|
+
- lib/passphrase/wordlist_database.rb
|
146
145
|
- spec/passphrase/cli_spec.rb
|
147
146
|
- spec/passphrase/diceware_method_spec.rb
|
148
147
|
- spec/passphrase/diceware_random_spec.rb
|
149
|
-
- spec/passphrase/language_query_spec.rb
|
150
148
|
- spec/passphrase/passphrase_spec.rb
|
151
149
|
- spec/passphrase/passphrase_string_spec.rb
|
152
|
-
- spec/passphrase/
|
150
|
+
- spec/passphrase/wordlist_database_spec.rb
|
153
151
|
- spec/spec_helper.rb
|
154
152
|
homepage: https://github.com/esumbar/passphrase
|
155
153
|
licenses:
|
metadata.gz.sig
CHANGED
@@ -1 +1 @@
|
|
1
|
-
�
|
1
|
+
-��1�,x�8X:����h6��j;��&R�SLY<�Li`p�����q���'�l��d�9/�S�`�O!N]&_ɩ�����{>C��?�$��ʎ�����lu_�R[][�~4r%�Rr��g=@��x�]f/&%����8�S0F�O|��X�Z$�c�UC��6��Mh���:gz��ê��������i.��0ë2Xx���0�IĹ�~I4-ٮ�+���b�%4���>�oc8G�
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module Passphrase
|
2
|
-
# This class encapsulates the {#count} and {#[]} queries against the
|
3
|
-
# "languages" table in the "words" SQLite 3 database.
|
4
|
-
class LanguageQuery
|
5
|
-
def initialize(db)
|
6
|
-
@db = db
|
7
|
-
end
|
8
|
-
|
9
|
-
# @return [Integer] the number of rows in the languages table
|
10
|
-
def count
|
11
|
-
sql = "SELECT COUNT(*) AS count FROM languages"
|
12
|
-
@db.get_first_value(sql)
|
13
|
-
end
|
14
|
-
|
15
|
-
# @param index [Integer] selects a specific row in the languages table
|
16
|
-
# @return [String] the language corresponding to the given index
|
17
|
-
def [](index)
|
18
|
-
sql = "SELECT language FROM languages"
|
19
|
-
unless @languages
|
20
|
-
@languages = []
|
21
|
-
@db.execute(sql).each { |lang| @languages << lang.first }
|
22
|
-
end
|
23
|
-
@languages[index]
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
module Passphrase
|
2
|
-
# This class encapsulates the {#where} query against the "words" table in
|
3
|
-
# the "words" SQLite 3 database. The filter parameter must be a hash that
|
4
|
-
# specifies a language and sequence of die rolls.
|
5
|
-
# @example
|
6
|
-
# {language: "afrikaans", die_rolls: "11111"}
|
7
|
-
class WordQuery
|
8
|
-
def initialize(db)
|
9
|
-
@db = db
|
10
|
-
end
|
11
|
-
|
12
|
-
# @param filter [Hash] specifies the language/die_roll combination
|
13
|
-
# @return [String] the selected words from the wordlist
|
14
|
-
def where(filter)
|
15
|
-
sql = "SELECT words " +
|
16
|
-
"FROM words " +
|
17
|
-
"WHERE language = :language AND die_rolls = :die_rolls"
|
18
|
-
@db.get_first_value(sql, filter)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
require "passphrase"
|
2
|
-
|
3
|
-
module Passphrase
|
4
|
-
RSpec.describe LanguageQuery, "queries the languages table in a valid wordlist database" do
|
5
|
-
before do
|
6
|
-
wordlist_file = "../../lib/passphrase/wordlist/words.sqlite3"
|
7
|
-
wordlist_path = File.join(File.dirname(__FILE__), wordlist_file)
|
8
|
-
db = SQLite3::Database.new(wordlist_path, readonly: true)
|
9
|
-
@languages = LanguageQuery.new(db)
|
10
|
-
end
|
11
|
-
|
12
|
-
it "responds to the count() method with no arguments" do
|
13
|
-
expect(@languages).to respond_to(:count).with(0).arguments
|
14
|
-
end
|
15
|
-
|
16
|
-
it "responds to the []() method with one argument" do
|
17
|
-
expect(@languages).to respond_to(:[]).with(1).argument
|
18
|
-
end
|
19
|
-
|
20
|
-
context "#count" do
|
21
|
-
before do
|
22
|
-
@count = @languages.count
|
23
|
-
end
|
24
|
-
|
25
|
-
it "returns an integer" do
|
26
|
-
expect(@count).to be_an_instance_of(Fixnum)
|
27
|
-
end
|
28
|
-
|
29
|
-
it "greater than zero" do
|
30
|
-
expect(@count).to be > 0
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
context "#[](0)" do
|
35
|
-
before do
|
36
|
-
@language = @languages[0]
|
37
|
-
end
|
38
|
-
|
39
|
-
it "returns a string" do
|
40
|
-
expect(@language).to be_an_instance_of(String)
|
41
|
-
end
|
42
|
-
|
43
|
-
it "that is not empty" do
|
44
|
-
expect(@language).not_to be_empty
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
require "passphrase"
|
2
|
-
|
3
|
-
module Passphrase
|
4
|
-
RSpec.describe WordQuery, "queries the words table in a valid wordlist database" do
|
5
|
-
before do
|
6
|
-
wordlist_file = "../../lib/passphrase/wordlist/words.sqlite3"
|
7
|
-
wordlist_path = File.join(File.dirname(__FILE__), wordlist_file)
|
8
|
-
db = SQLite3::Database.new(wordlist_path, readonly: true)
|
9
|
-
@words = WordQuery.new(db)
|
10
|
-
end
|
11
|
-
|
12
|
-
it "responds to the where() method with one argument" do
|
13
|
-
expect(@words).to respond_to(:where).with(1).argument
|
14
|
-
end
|
15
|
-
|
16
|
-
context "#where([Hash])" do
|
17
|
-
before do
|
18
|
-
@selection = @words.where(language: "afrikaans", die_rolls: "11111")
|
19
|
-
end
|
20
|
-
|
21
|
-
it "returns a string" do
|
22
|
-
expect(@selection).to be_an_instance_of(String)
|
23
|
-
end
|
24
|
-
|
25
|
-
it "that is not empty" do
|
26
|
-
expect(@selection).not_to be_empty
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|