passphrase 1.0.0 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 04b13682c8ca0850aec87893eacd4e3ef53bcdd8
4
- data.tar.gz: e758d10b4ae02a3b76906512fbd545fd21f461dc
3
+ metadata.gz: 6f0c06ef87ec317d113d67632f44873e4c0b6c7f
4
+ data.tar.gz: c144b9ec21879b8717a5c3c3a0f5dc13be273542
5
5
  SHA512:
6
- metadata.gz: ed1766c82ecf11aab014e0b41fdccb00504f13456671130b5da70f013369d855673a2a4e84548a5c65edda150161b797503e1bcc62e606f99a2d01c29e05e626
7
- data.tar.gz: 59d155d31002868739678ddd654ebb2196ebc74e5a852a56afa216f69e18871b8ae311e0194a9de5850fee1dd3329b68b9b1dcc72a2994be83b0442c0cbd8577
6
+ metadata.gz: f2711c0224023e76b00f2f1fea8358be40ee078edaa1891c3623f9803398cf4d2868261db067e0b9d0e072679cdb385e9b4809934325c06686de5d747c3110f1
7
+ data.tar.gz: a425a87a7134ffc3efa2262f208f278e0f941a33ea97f1eb35558fa8a68cba03661e139856e5d72a402ff49c6d50cd4ecf9eef950326b54270f8f7383160c07b
@@ -1 +1 @@
1
- |��)VMg�Ų�<R)SMLy�|�9����/�����ɟ��.�&:��,����<�Kζ�?��7&�t]f��K1�uN��sc���S��I�� x<т�׼iBtGo&J�O���~�[�w��ٕ�O�}�I�ˋ��+�Hg��ŅEG{<��~�R|��5p�.�gMn�,���u��z|(����8;w�.��ౙ���w3`]�/.�u��Ѓ��_��-�Xa�Ϭ�"u��j �[��<N�nC
1
+ �^�殗�I���KVXV:J��9���l �>�'sc^(m����;�*C��A��1�����[r��^ ��I������{Tϓ��D�ֹ~R*�tGi�u �^nj���?K1b${p��z�݊1v���ʪeŀ�DV�c���>��1L��N�W�C}��:1 ��+)������
data.tar.gz.sig CHANGED
Binary file
@@ -2,6 +2,18 @@
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
+ ## Version 1.1.0 - 2015-04-14
6
+ ### Added
7
+ - Added SHA256 checksum calculation to rake_helper.rb.
8
+ - Added `:use_random_org` key to Passphrase#inspect.
9
+ - Added `--passwordize|-p` option to convert a passphrase into a password.
10
+
11
+ ### Changed
12
+ - Made `number_of_words` an attribute of the Passphrase class.
13
+ - Generate a passphrase when a Passphrase object is instantiated.
14
+ - The passphrase attribute is now an instance of PassphraseString which
15
+ encapsulates the `#to_password` method.
16
+
5
17
  ## Version 1.0.0 - 2015-04-06
6
18
  This release is a complete re-write. None of the code from the previous
7
19
  release was retained.
data/README.md CHANGED
@@ -7,12 +7,12 @@ the command-line, run
7
7
 
8
8
  or programmatically,
9
9
 
10
- require "passphrase"
11
- p = Passphrase::Passphrase.new(number_of_words: 4)
12
- p.generate
13
- passphrase = p.passphrase
14
-
15
- Eliminate the spaces between words to use the result as a potential password.
10
+ ```ruby
11
+ require "passphrase"
12
+ p = Passphrase::Passphrase.new(number_of_words: 4)
13
+ passphrase = p.passphrase
14
+ ```
15
+ Passphrase also has the capability to generate passwords (indirectly).
16
16
 
17
17
  ## Installation
18
18
  The Passphrase command-line tool and library can be installed with
@@ -41,7 +41,9 @@ this case `sqlite3`, which is not signed, from the verification process.
41
41
  Usage: passphrase [options]
42
42
  -n, --num-words=NUM Number of words in passphrase 3..10
43
43
  (default: 5)
44
- -r, --[no-]random-org Use random.org to generate random numbers
44
+ -p, --[no-]passwordize Add one cap, one num, and one special char
45
+ (default: --no-passwordize)
46
+ -r, --[no-]random-org Use RANDOM.ORG to generate random numbers
45
47
  (default: --no-random-org)
46
48
  -h, --help Show this message
47
49
  -v, --version Show version
@@ -52,30 +54,41 @@ this case `sqlite3`, which is not signed, from the verification process.
52
54
  $ passphrase -n 4
53
55
  apaisado vermouth seemag ebelik
54
56
 
55
- ### Ruby library
56
-
57
- require "passphrase"
58
-
59
- # generate a passphrase with default options
60
- p = Passphrase::Passphrase.new
61
- p.generate
62
- puts "passphrase: #{p}"
63
- puts "passphrase internals: #{p.inspect}"
57
+ $ passphrase -n 3 -p
58
+ pothvati alewives escatima
59
+ pothvati_alewiNes_e&cat5ma
64
60
 
65
- # generate three four-word passphrases using RANDOM.ORG
66
- options = { number_of_words: 4, use_random_org: true }
67
- p = Passphrase::Passphrase.new(options)
68
- passphrase1 = p.generate.passphrase
69
- passphrase2 = p.generate.passphrase
70
- passphrase3 = p.generate.passphrase
61
+ ### Ruby library
71
62
 
72
- # generate an array of six-word passphrases
73
- passphrase_array = Array.new(100)
74
- Passphrase::Passphrase.new(number_of_words: 6) do |p|
75
- passphrase_array.map! { |e| p.generate.passphrase }
76
- end
63
+ ```ruby
64
+ require "passphrase"
65
+
66
+ # generate a passphrase with default options
67
+ p = Passphrase::Passphrase.new
68
+ puts "passphrase: #{p}"
69
+ puts "passphrase internals: #{p.inspect}"
70
+
71
+ # generate three four-word passphrases using RANDOM.ORG
72
+ options = { number_of_words: 4, use_random_org: true }
73
+ p = Passphrase::Passphrase.new(options)
74
+ passphrase1 = p.generate.passphrase
75
+ passphrase2 = p.generate.passphrase
76
+ passphrase3 = p.generate.passphrase
77
+
78
+ # generate an array of six-word passphrases
79
+ passphrase_array = Array.new(100)
80
+ Passphrase::Passphrase.new(number_of_words: 6) do |p|
81
+ passphrase_array.map! { |e| p.generate.passphrase }
82
+ end
83
+
84
+ # generate a password
85
+ p = Passphrase::Passphrase.new(number_of_words: 3)
86
+ passphrase = p.passphrase
87
+ password = passphrase.to_password
88
+ ```
77
89
 
78
90
  ## Background
91
+ ### Diceware Method
79
92
  Passphrase implements the [Diceware
80
93
  Method](http://world.std.com/~reinhold/diceware.html) which constructs a
81
94
  passphrase by randomly selecting words from a predefined list of more-or-less
@@ -113,11 +126,11 @@ SQLite 3 database file).
113
126
 
114
127
  Except in the original Diceware list, all words are lower case, comprised of
115
128
  characters from the ascii set `[a-z]`. The original Diceware list includes
116
- some "words" with numerical and punctuation characters. No word appears more
117
- than once in this set of wordlists.
129
+ some "words" with numerical and punctuation characters. To the best of my
130
+ knowledge, no word appears more than once in this set of wordlists.
118
131
 
119
132
  The _dice_ in Diceware refers to the act of rolling a die to achieve
120
- randomness. A sequence of five consecutive rolls has 7776 (6<sup>5</sup>)
133
+ randomness. A sequence of five consecutive rolls has 7776 (or 6<sup>5</sup>)
121
134
  possible outcomes. Each combination maps to one line in a given wordlist, and
122
135
  each line, in turn, is composed of between 1 and 15 words (depending on the
123
136
  language, the average being 5).
@@ -128,8 +141,8 @@ Therefore, to select a word, Passphrase
128
141
  2. randomly selects one of 7776 lines from the corresponding wordlist
129
142
  3. randomly selects one word from the corresponding line
130
143
 
131
- This leads to roughly 10<sup>28</sup> possible five-word passphrases, for
132
- example.
144
+ This leads to roughly 2<sup>66</sup> (or 10<sup>28</sup>) possible five-word
145
+ passphrases, for example.
133
146
 
134
147
  Passphrase simulates the rolls of a die by using random numbers from one of
135
148
  two sources. The default is to use the standard SecureRandom class. You also
@@ -137,8 +150,16 @@ have the option of requesting random numbers from
137
150
  [RANDOM.ORG](http://www.random.org). However, because RANDOM.ORG depends on
138
151
  network access, it is susceptible to network problems, and is also slower.
139
152
 
153
+ ### Passwords
154
+ A typical passphrase will not satisfy password policies that require the use
155
+ of upper case letters, numbers, and special characters (~, !, and the like).
156
+ Therefore, Passphrase gives you the option to replace three randomly selected
157
+ characters in a generated passphrase with one random upper case letter
158
+ `[A-Z]`, one random integer `[0-9]`, and one random special character. Spaces
159
+ are not usually allowed either, so they are replaced with underscores as part
160
+ of this process.
161
+
140
162
  ## Contributing to Passphrase
141
- ### Getting started
142
163
  After forking the [repository on
143
164
  GitHub](https://github.com/esumbar/passphrase), go to your local copy of the
144
165
  repository and execute `bundle install` to ensure that all development gems
@@ -154,21 +175,14 @@ bin/passphrase`. You can also experiment with the library in irb. For example,
154
175
 
155
176
  $ irb -Ilib -rpassphrase
156
177
  >> p = Passphrase::Passphrase.new(number_of_words: 3)
157
- => {:passphrase=>"", :number_of_words=>0, :word_origins=>{}}
158
- >> p.generate
159
- => {:passphrase=>"bolt flanella ininaen", :number_of_words=>3,...}
178
+ => {:passphrase=>"jazzy vannier viscount", :number_of_words=>3, ... }
160
179
  >> p.passphrase
161
- => "bolt flanella ininaen"
180
+ => "jazzy vannier viscount"
181
+ >> p.passphrase.to_password
182
+ => "jazz{_2annier_vBscount"
162
183
 
163
184
  Run the tests with `rake spec`.
164
185
 
165
- ### Future enhancements
166
- In order to make passphrases more acceptable as passwords, a feature could be
167
- added to the library to upcase a random letter, replace a random alphabetic
168
- character with a numeric character (if one does not already exist), and mix in
169
- a special character, like "`$`" or "`%`" etc. The command-line option could be
170
- called `--passwordize|-p`.
171
-
172
186
  ## Changelog
173
187
  See {file:CHANGELOG.md} for a list of changes.
174
188
 
@@ -1,10 +1,6 @@
1
1
  require "passphrase/default"
2
- require "passphrase/diceware_method"
3
- require "passphrase/diceware_random"
4
- require "passphrase/language_query"
5
2
  require "passphrase/passphrase"
6
3
  require "passphrase/version"
7
- require "passphrase/word_query"
8
4
 
9
5
  module Passphrase
10
6
  autoload(:CLI, "passphrase/CLI")
@@ -14,20 +14,44 @@ module Passphrase
14
14
  # given by ARGV (may be empty)
15
15
  # @return [void]
16
16
  def self.parse(args)
17
- options = Default.options
17
+ # Gotcha:
18
+ # The contents of an instance variable can be changed outside the class
19
+ # through a reader attribute. For example,
20
+ # class A
21
+ # attr_reader :x
22
+ # def initialize
23
+ # @x = { a: 1, b: 2 }
24
+ # end
25
+ # end
26
+ # a = A.new
27
+ # p a.x #=> {:a=>1, :b=>2}
28
+ # x = a.x
29
+ # x[:b] = 99
30
+ # p a.x #=> {:a=>1, :b=>99}
31
+ # Therefore, need to clone the default hash otherwise parsing could
32
+ # alter the hash's contents. Although this causes no harm in the
33
+ # command-line tool, it causes havoc in the test suite.
34
+ options = Default.options.clone
18
35
 
19
36
  default_number_of_words = Default.options[:number_of_words]
37
+ default_passwordize = Default.options[:passwordize] ? "--passwordize" : "--no-passwordize"
20
38
  default_random_org = Default.options[:use_random_org] ? "--random-org" : "--no-random-org"
39
+ default_number_range = Default.number_range
21
40
 
22
41
  parser = OptionParser.new do |opts|
23
42
  opts.banner = "Usage: passphrase [options]"
24
43
  opts.on(:REQUIRED, "-n NUM", "--num-words=NUM", Integer,
25
- "Number of words in passphrase #{Default.number_range}",
44
+ "Number of words in passphrase #{default_number_range}",
26
45
  "(default: #{default_number_of_words})") do |n|
27
46
  options[:number_of_words] = n
28
47
  end
48
+ opts.on(:NONE, "-p", "--[no-]passwordize",
49
+ "Add one cap, one num, and one special char",
50
+ "(default: #{default_passwordize})") do |p|
51
+ options[:passwordize] = p
52
+ end
29
53
  opts.on(:NONE, "-r", "--[no-]random-org",
30
- "Use random.org to generate random numbers",
54
+ "Use RANDOM.ORG to generate random numbers",
31
55
  "(default: #{default_random_org})") do |r|
32
56
  options[:use_random_org] = r
33
57
  end
@@ -44,7 +68,9 @@ module Passphrase
44
68
  begin
45
69
  parser.parse!(args)
46
70
  validate_number_of_words(options)
47
- puts Passphrase.new(options).generate
71
+ passphrase = Passphrase.new(options).passphrase
72
+ puts passphrase
73
+ puts passphrase.to_password if options[:passwordize]
48
74
  rescue OptionParser::InvalidOption => e
49
75
  handle_error(e)
50
76
  rescue OptionParser::MissingArgument => e
@@ -9,7 +9,7 @@ module Passphrase
9
9
  attr_reader :number_range
10
10
  end
11
11
 
12
- @options = { number_of_words: 5, use_random_org: nil }
12
+ @options = { number_of_words: 5, passwordize: nil, use_random_org: nil }
13
13
  @number_range = (3..10)
14
14
  end
15
15
  end
@@ -1,4 +1,7 @@
1
1
  require "sqlite3"
2
+ require "passphrase/diceware_random"
3
+ require "passphrase/language_query"
4
+ require "passphrase/word_query"
2
5
 
3
6
  module Passphrase
4
7
  # This class implements the Diceware Method for generating a passphrase. It
@@ -19,7 +19,7 @@ module Passphrase
19
19
 
20
20
  # @param use_random_org [Boolean] a flag that triggers the use of
21
21
  # RANDOM.ORG for generating random numbers
22
- def initialize(use_random_org=nil)
22
+ def initialize(use_random_org)
23
23
  @random_org_uri = "https://www.random.org"
24
24
  use_random_org ? setup_remote_generator : setup_local_generator
25
25
  end
@@ -1,3 +1,6 @@
1
+ require "passphrase/diceware_method"
2
+ require "passphrase/passphrase_string"
3
+
1
4
  module Passphrase
2
5
  # This is the main class of the Passphrase library for generating
3
6
  # passphrases. It's initialized with a two-element hash that specifies the
@@ -12,14 +15,15 @@ module Passphrase
12
15
  # @return [String] the passphrase string
13
16
  attr_reader :passphrase
14
17
 
18
+ # @return [Integer] the number of words in the passphrase
19
+ attr_reader :number_of_words
20
+
15
21
  # @param options [Hash] characteristics for a new Passphrase object
16
22
  # @yieldparam obj [self] the Passphrase object
17
23
  def initialize(options={})
18
24
  @options = Default.options.merge(options)
19
- @passphrase = ""
20
- @languages = []
21
- @die_rolls = []
22
- @words = []
25
+ @number_of_words = @options[:number_of_words]
26
+ generate
23
27
  yield self if block_given?
24
28
  end
25
29
 
@@ -27,23 +31,16 @@ module Passphrase
27
31
  # resulting arrays are accumulated into instance variables. The words
28
32
  # array is formatted into a single passphrase string and stored in another
29
33
  # instance variable. The method returns itself to allow method chaining.
30
- # @return [self] a Passphrase object
34
+ # @return [self] the Passphrase object
31
35
  def generate
32
36
  @languages, @die_rolls, @words = DicewareMethod.run(@options)
33
- @passphrase = @words.join(" ")
37
+ @passphrase = PassphraseString.new(@words.join(" "), @options[:use_random_org])
34
38
  self
35
39
  end
36
40
 
37
- # This virtual attribute accessor returns the number of words in the
38
- # generated passphrase.
39
- # @return [Integer] the number of words in the passphrase
40
- def number_of_words
41
- @words.size
42
- end
43
-
44
41
  # A predicate method that returns true if the Passphrase object is
45
42
  # initialized to use RANDOM.ORG
46
- # @return [Boolean] returns true of RANDOM.ORG is being used
43
+ # @return [Boolean] returns true if RANDOM.ORG is being used
47
44
  def using_random_org?
48
45
  @options[:use_random_org]
49
46
  end
@@ -51,15 +48,16 @@ module Passphrase
51
48
  # String representation of a Passphrase object
52
49
  # @return [String] the passphrase string
53
50
  def to_s
54
- @passphrase
51
+ passphrase
55
52
  end
56
53
 
57
54
  # Returns details for the Passphrase object as a hash.
58
55
  # @return [Hash] the details of a Passphrase object
59
56
  def inspect
60
57
  {
61
- passphrase: @passphrase,
62
- number_of_words: @words.size,
58
+ passphrase: passphrase,
59
+ number_of_words: number_of_words,
60
+ use_random_org: using_random_org?,
63
61
  word_origins: word_origins
64
62
  }
65
63
  end
@@ -0,0 +1,60 @@
1
+ module Passphrase
2
+ # This subclass of String implements the {#passwordize} instance method for
3
+ # substituting a capital letter, number, and special character into a
4
+ # passphrase.
5
+ class PassphraseString < String
6
+ # @param passphrase [String] the passphrase as an ordinary String object
7
+ # @param use_random_org [Boolean] a flag that triggers the use of
8
+ # RANDOM.ORG for generating random numbers
9
+ def initialize(passphrase, use_random_org)
10
+ super(passphrase)
11
+ @use_random_org = use_random_org
12
+ @random = DicewareRandom.new(use_random_org)
13
+ end
14
+
15
+ # @return [String] a new String that is the passwordized version of the
16
+ # passphrase
17
+ def to_password
18
+ @scratch = self.gsub(/\s/, "_")
19
+ capital_index, number_index, special_index = @random.indices(3, @scratch.length)
20
+ select_and_substitute_capital_letter(capital_index)
21
+ select_and_substitute_number(number_index)
22
+ select_and_substitute_special_character(special_index)
23
+ String.new(@scratch)
24
+ end
25
+
26
+ private
27
+
28
+ def select_and_substitute_capital_letter(random_index)
29
+ # Just in case the passphrase already includes a capital letter.
30
+ return if /[A-Z]/ =~ @scratch
31
+ capital_letter = ("A".."Z").to_a.sample
32
+ substitute(capital_letter, random_index)
33
+ end
34
+
35
+ def select_and_substitute_number(random_index)
36
+ # The Diceware wordlist includes "words" that include numbers.
37
+ return if /[0-9]/ =~ @scratch
38
+ number = ("0".."9").to_a.sample
39
+ substitute(number, random_index)
40
+ end
41
+
42
+ def select_and_substitute_special_character(random_index)
43
+ # The Diceware wordlist includes "words" that include special characters.
44
+ return if /[~!#\$%\^&\*\(\)\-=\+\[\]\\\{\}:;"'<>\?\/]/ =~ @scratch
45
+ special_character = %w( ~ ! # $ % ^ & * \( \) - = + [ ] \\ { } : ; " ' < > ? / ).sample
46
+ substitute(special_character, random_index)
47
+ end
48
+
49
+ def substitute(substitution, random_index)
50
+ character = @scratch[random_index]
51
+ @scratch[random_index] = substitution and return if /[a-z]/ =~ character
52
+ next_index = random_index.succ.modulo(@scratch.length)
53
+ until next_index == random_index
54
+ character = @scratch[next_index]
55
+ @scratch[next_index] = substitution and return if /[a-z]/ =~ character
56
+ next_index = next_index.succ.modulo(@scratch.length)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,5 +1,5 @@
1
1
  module Passphrase
2
2
  # Version numbers are bumped according to {http://semver.org Semantic
3
3
  # Versioning}.
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
  end
@@ -3,6 +3,15 @@ require "optparse"
3
3
 
4
4
  module Passphrase
5
5
  RSpec.describe CLI, "implements the command line interface" do
6
+ before do
7
+ @saved_stdout = $stdout
8
+ $stdout = StringIO.new
9
+ end
10
+
11
+ after(:each) do
12
+ $stdout = @saved_stdout
13
+ end
14
+
6
15
  it "responds to class method parse() with one argument" do
7
16
  expect(CLI).to respond_to(:parse).with(1).argument
8
17
  end
@@ -15,12 +24,35 @@ module Passphrase
15
24
  expect(CLI).not_to respond_to(:handle_error)
16
25
  end
17
26
 
27
+ it "does not emit an error when option -n 3 is supplied" do
28
+ expect(CLI).not_to receive(:handle_error)
29
+ CLI.parse(["-n", "3"])
30
+ end
31
+
32
+ it "does not emit an error when option -p is supplied" do
33
+ expect(CLI).not_to receive(:handle_error)
34
+ CLI.parse(["-p"])
35
+ end
36
+
37
+ it "does not emit an error when option -r is supplied" do
38
+ expect(CLI).not_to receive(:handle_error)
39
+ CLI.parse(["-r"])
40
+ end
41
+
42
+ it "exits when option -h is supplied" do
43
+ expect { CLI.parse(["-h"]) }.to raise_error(SystemExit)
44
+ end
45
+
46
+ it "exits when option -v is supplied" do
47
+ expect { CLI.parse(["-v"]) }.to raise_error(SystemExit)
48
+ end
49
+
18
50
  it "emits an error when an invalid option is supplied" do
19
51
  # For some reason, :handle_error doesn't need to be stubbed
20
52
  expect(CLI).to receive(:handle_error)
21
53
  .with(kind_of(OptionParser::InvalidOption))
22
- bad_option = ["-x"]
23
- CLI.parse(bad_option)
54
+ invalid_option = ["-x"]
55
+ CLI.parse(invalid_option)
24
56
  end
25
57
 
26
58
  it "emits an error when a mandatory argument is missing" do
@@ -50,11 +50,19 @@ module Passphrase
50
50
  )
51
51
  end
52
52
 
53
- it "each array is of size given by the number of words specified" do
53
+ it "each array contains :number_of_words elements" do
54
54
  @result.each do |result|
55
55
  expect(result.size).to eq(@number_of_words)
56
56
  end
57
57
  end
58
+
59
+ it "each array contains String elements" do
60
+ @result.each do |result|
61
+ result.each do |element|
62
+ expect(element).to be_an_instance_of(String)
63
+ end
64
+ end
65
+ end
58
66
  end
59
67
  end
60
68
  end
@@ -46,6 +46,10 @@ module Passphrase
46
46
  it "each one in the range 0...15" do
47
47
  expect(@result).to all be_between(0, 14)
48
48
  end
49
+
50
+ it "produces a different result when called a second time" do
51
+ expect(@random.indices(4, 15)).not_to eq(@result)
52
+ end
49
53
  end
50
54
 
51
55
  describe "#die_rolls(6)" do
@@ -82,7 +86,7 @@ module Passphrase
82
86
 
83
87
  context "initialized by default to use the local Ruby random number generator" do
84
88
  before do
85
- @random = DicewareRandom.new
89
+ @random = DicewareRandom.new(false)
86
90
  end
87
91
 
88
92
  include_examples "DicewareRandom object"
@@ -90,11 +94,7 @@ module Passphrase
90
94
 
91
95
  context "initialized to use random numbers from RANDOM.ORG" do
92
96
  before do
93
- @random = DicewareRandom.new(:use_random_org)
94
- end
95
-
96
- it "initially has a zero random.org request count" do
97
- expect(DicewareRandom.random_org_requests).to eq(0)
97
+ @random = DicewareRandom.new(true)
98
98
  end
99
99
 
100
100
  it "increments the random.org request count on indices()" do
@@ -1,58 +1,91 @@
1
1
  require "passphrase"
2
2
 
3
3
  module Passphrase
4
+ RSpec.shared_examples "Passphrase object" do
5
+ it "responds to attribute reader method passphrase()" do
6
+ expect(@passphrase).to respond_to(:passphrase)
7
+ end
8
+
9
+ it "responds to attribute reader method number_of_words()" do
10
+ expect(@passphrase).to respond_to(:number_of_words).with(0).arguments
11
+ end
12
+
13
+ it "responds to the generate() method with zero arguments" do
14
+ expect(@passphrase).to respond_to(:generate).with(0).arguments
15
+ end
16
+
17
+ it "responds to predicate method using_random_org?()" do
18
+ expect(@passphrase).to respond_to(:using_random_org?)
19
+ end
20
+
21
+ it "responds to the to_s() method" do
22
+ expect(@passphrase).to respond_to(:to_s)
23
+ end
24
+
25
+ it "responds to the inspect() method with zero arguments" do
26
+ expect(@passphrase).to respond_to(:inspect).with(0).arguments
27
+ end
28
+
29
+ it "does not respond to the word_origins() method (private)" do
30
+ expect(@passphrase).not_to respond_to(:word_origins)
31
+ end
32
+ end
33
+
4
34
  RSpec.describe Passphrase, "for generating passphrase objects" do
5
35
  it "yields itself during instantiation" do
6
- expect { |b| Passphrase.new(Default.options, &b) }.to yield_with_args(Passphrase)
36
+ expect { |b| Passphrase.new(&b) }.to yield_with_args(Passphrase)
7
37
  end
8
38
 
9
39
  # Dependence on RANDOM.ORG only affects the DicewareRandom class.
10
- # Therefore, only need to test the case where RANDOM.ORG is not used.
11
- context "initialized to generate a passphrase with 1 word, not using RANDOM.ORG" do
12
- before do
13
- @passphrase = Passphrase.new(number_of_words: 1, use_random_org: nil)
14
- end
15
-
16
- it "responds to the generate() method with zero arguments" do
17
- expect(@passphrase).to respond_to(:generate).with(0).arguments
40
+ # Therefore, only need to thoroughly test the case where RANDOM.ORG is not used.
41
+ context "initialized to use RANDOM.ORG" do
42
+ it "the predicate method should confirm it is using RANDOM.ORG" do
43
+ passphrase = Passphrase.new(use_random_org: true)
44
+ expect(passphrase).to be_using_random_org
18
45
  end
46
+ end
19
47
 
20
- it "responds to the to_s() method" do
21
- expect(@passphrase).to respond_to(:to_s)
48
+ context "initialized with default options" do
49
+ before do
50
+ @passphrase = Passphrase.new
22
51
  end
23
52
 
24
- it "responds to the inspect() method with zero arguments" do
25
- expect(@passphrase).to respond_to(:inspect).with(0).arguments
53
+ it "returns a passphrase of type PassphraseString" do
54
+ expect(@passphrase.passphrase).to be_an_instance_of(PassphraseString)
26
55
  end
27
-
28
- it "responds to attribute reader method passphrase()" do
29
- expect(@passphrase).to respond_to(:passphrase)
56
+
57
+ it "returns a passphrase with the default number of words" do
58
+ number_of_words = @passphrase.passphrase.split.length
59
+ expect(number_of_words).to eq(Default.options[:number_of_words])
30
60
  end
31
61
 
32
- it "responds to predicate method using_random_org?()" do
33
- expect(@passphrase).to respond_to(:using_random_org?)
62
+ it "should not be using RANDOM.ORG" do
63
+ expect(@passphrase).not_to be_using_random_org
34
64
  end
35
65
 
36
- it "responds to virtual attribute reader method number_of_words()" do
37
- expect(@passphrase).to respond_to(:number_of_words).with(0).arguments
38
- end
66
+ include_examples "Passphrase object"
67
+ end
39
68
 
40
- it "does not respond to the word_origins() method (private)" do
41
- expect(@passphrase).not_to respond_to(:word_origins)
69
+ context "initialized to generate a passphrase with 1 word" do
70
+ before do
71
+ @passphrase = Passphrase.new(number_of_words: 1)
42
72
  end
43
73
 
44
- it "initially contains an empty passphrase string" do
45
- expect(@passphrase.passphrase).to be_empty
74
+ it "returns a passphrase of type PassphraseString" do
75
+ expect(@passphrase.passphrase).to be_an_instance_of(PassphraseString)
46
76
  end
47
77
 
48
- it "initially contains zero words" do
49
- expect(@passphrase.number_of_words).to eq(0)
78
+ it "returns a passphrase with one word" do
79
+ number_of_words = @passphrase.passphrase.split.length
80
+ expect(number_of_words).to eq(1)
50
81
  end
51
82
 
52
83
  it "should not be using RANDOM.ORG" do
53
84
  expect(@passphrase).not_to be_using_random_org
54
85
  end
55
86
 
87
+ include_examples "Passphrase object"
88
+
56
89
  context "after executing the generate() method" do
57
90
  before do
58
91
  dwr = [["language"], ["11111"], ["passphrase"]]
@@ -64,12 +97,17 @@ module Passphrase
64
97
  expect(@result).to equal(@passphrase)
65
98
  end
66
99
 
67
- it "contains the passphrase string" do
68
- expect(@result.passphrase).to eq("passphrase")
100
+ it "returns a passphrase of type PassphraseString" do
101
+ expect(@result.passphrase).to be_an_instance_of(PassphraseString)
102
+ end
103
+
104
+ it "contains a passphrase with one word" do
105
+ number_of_words = @result.passphrase.split.length
106
+ expect(number_of_words).to eq(1)
69
107
  end
70
108
 
71
- it "contains a number of words equal to one" do
72
- expect(@result.number_of_words).to eq(1)
109
+ it "contains the passphrase string" do
110
+ expect(@result.passphrase).to eq("passphrase")
73
111
  end
74
112
 
75
113
  it "returns the passphrase when printed" do
@@ -83,6 +121,7 @@ module Passphrase
83
121
  expect(@result.inspect).to match(
84
122
  passphrase: "passphrase",
85
123
  number_of_words: 1,
124
+ use_random_org: nil,
86
125
  word_origins: { "passphrase" => {
87
126
  language: "language",
88
127
  die_rolls: "11111"
@@ -92,27 +131,5 @@ module Passphrase
92
131
  end
93
132
  end
94
133
  end
95
-
96
- context "initialized to use RANDOM.ORG" do
97
- it "the predicate method should confirm it is using RANDOM.ORG" do
98
- passphrase = Passphrase.new(number_of_words: 3, use_random_org: true)
99
- expect(passphrase).to be_using_random_org
100
- end
101
- end
102
-
103
- context "initialized with default options" do
104
- before do
105
- @passphrase = Passphrase.new
106
- @passphrase.generate
107
- end
108
-
109
- it "return a passphrase with the default number of words" do
110
- expect(@passphrase.number_of_words).to eq(Default.options[:number_of_words])
111
- end
112
-
113
- it "should not be using RANDOM.ORG" do
114
- expect(@passphrase).not_to be_using_random_org
115
- end
116
- end
117
134
  end
118
135
  end
@@ -0,0 +1,55 @@
1
+ require "passphrase"
2
+
3
+ module Passphrase
4
+ RSpec.describe PassphraseString, "subclass of String class" do
5
+ context "initialized with a sample string" do
6
+ before do
7
+ @sample = "a sample passphrase string"
8
+ @passphrase_string = PassphraseString.new(@sample, false)
9
+ end
10
+
11
+ it "contains the same string as the sample string" do
12
+ expect(@passphrase_string).to eq(@sample)
13
+ end
14
+
15
+ it "has the same length as the sample string" do
16
+ expect(@passphrase_string.length).to eq(26)
17
+ end
18
+
19
+ it "responds to the to_password() method with zero arguments" do
20
+ expect(@passphrase_string).to respond_to(:to_password).with(0).arguments
21
+ end
22
+
23
+ describe "#to_password" do
24
+ before do
25
+ @passwordized_string = @passphrase_string.to_password
26
+ end
27
+
28
+ it "returns an ordinary String" do
29
+ expect(@passwordized_string).to be_an_instance_of(String)
30
+ end
31
+
32
+ it "returns a String that is not the same object as the PassphraseString" do
33
+ expect(@passwordized_string).not_to equal(@passphrase_string)
34
+ end
35
+
36
+ it "returns a String that has the same length as the PassphraseString" do
37
+ expect(@passwordized_string.length).to eq(@passphrase_string.length)
38
+ end
39
+
40
+ it "returns a String that contains at least one captial letter" do
41
+ expect(@passwordized_string).to match(/[A-Z]/)
42
+ end
43
+
44
+ it "returns a String that contains at least one number" do
45
+ expect(@passwordized_string).to match(/[0-9]/)
46
+ end
47
+
48
+ it "returns a String that contains at least one special character" do
49
+ special_character = /[~!#\$%\^&\*\(\)\-=\+\[\]\\\{\}:;"'<>\?\/]/
50
+ expect(@passwordized_string).to match(special_character)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ 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.0.0
4
+ version: 1.1.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-06 00:00:00.000000000 Z
33
+ date: 2015-04-15 00:00:00.000000000 Z
34
34
  dependencies:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: sqlite3
@@ -139,6 +139,7 @@ files:
139
139
  - lib/passphrase/diceware_random.rb
140
140
  - lib/passphrase/language_query.rb
141
141
  - lib/passphrase/passphrase.rb
142
+ - lib/passphrase/passphrase_string.rb
142
143
  - lib/passphrase/version.rb
143
144
  - lib/passphrase/word_query.rb
144
145
  - lib/passphrase/wordlist/words.sqlite3
@@ -147,6 +148,7 @@ files:
147
148
  - spec/passphrase/diceware_random_spec.rb
148
149
  - spec/passphrase/language_query_spec.rb
149
150
  - spec/passphrase/passphrase_spec.rb
151
+ - spec/passphrase/passphrase_string_spec.rb
150
152
  - spec/passphrase/word_query_spec.rb
151
153
  - spec/spec_helper.rb
152
154
  homepage: https://github.com/esumbar/passphrase
metadata.gz.sig CHANGED
Binary file