keepass-password-generator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ .yardoc
4
+ Gemfile.lock
5
+ doc
6
+ pkg
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format documentation
data/.yardopts ADDED
@@ -0,0 +1,3 @@
1
+ --markup markdown
2
+ lib/**/*.rb
3
+ README.md MIT-LICENSE.txt
data/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ ## Version 0.1.0 / 2011-03-22
2
+
3
+ * updated license
4
+ * bumped version to 0.1.0
5
+
6
+ ## Version 0.0.1 / 2011-02-19
7
+
8
+ * initial release
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 John Nishinaga and Pat Deegan, PhD & Associates, LLC.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # keepass-password-generator
2
+
3
+ Generate passwords using KeePass password generator patterns.
4
+
5
+ ## RubyGems installation
6
+
7
+ gem install keepass-password-generator
8
+
9
+ ## Bundler installation
10
+
11
+ In your `Gemfile`:
12
+
13
+ gem 'keepass-password-generator'
14
+
15
+ Install bundled gems:
16
+
17
+ bundle
18
+
19
+ ## Usage
20
+
21
+ require 'keepass/password'
22
+
23
+ KeePass::Password.generate('A{6}s')
24
+ #=> "Un2hd#t"
25
+
26
+ See <http://keepass.info/help/base/pwgenerator.html> for information about KeePass patterns.
27
+
28
+ ## Examples
29
+
30
+ A 40-bit WEP key:
31
+
32
+ KeePass::Password.generate('h{10}')
33
+ #=> "ae6929dc0e"
34
+
35
+ A random MAC address:
36
+
37
+ KeePass::Password.generate('HH\-HH\-HH\-HH\-HH\-HH', :permute => false)
38
+ #=> "0D-4D-32-64-EB-7D"
39
+
40
+ A password with 10 alphanumeric characters, where at least 2 are upper case and at least are 2 lower case characters:
41
+
42
+ KeePass::Password.generate('uullA{6}')
43
+ #=> "us2j1nTIQT"
44
+
45
+ A password with 20 alphanumeric and symbol characters, without any lookalike characters (e.g., I and |):
46
+
47
+ KeePass::Password.generate('[As]{20}', :remove_lookalikes => true)
48
+ #=> "-2~[+Rze{hZezk(\\nZ-W"
49
+
50
+ Invalid patterns raise an exception:
51
+
52
+ KeePass::Password.generate('[\I\|]{3}', :remove_lookalikes => true)
53
+ #=> KeePass::Password::InvalidPatternError: empty character set for token 1 for "[\\I\\|]{3}"
54
+
55
+ ## Related gems
56
+
57
+ * <https://github.com/dmke/simple-password-gen>
58
+ * <http://rubygems.org/gems/ruby-password>
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ require 'yard'
7
+ YARD::Rake::YardocTask.new
8
+
9
+ require 'rspec/core/rake_task'
10
+ RSpec::Core::RakeTask.new(:spec)
11
+
12
+ task :default => :spec
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "keepass/password/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "keepass-password-generator"
7
+ s.version = KeePass::Password::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["John Nishinaga"]
10
+ s.email = ["jingoro@casa-z.org"]
11
+ s.homepage = "https://github.com/patdeegan/keepass-password-generator"
12
+ s.summary = "keepass-password-generator-#{KeePass::Password::VERSION}"
13
+ s.description = "Generate passwords using KeePass password generator patterns"
14
+
15
+ s.rubyforge_project = "keepass-password-generator"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency 'activesupport', '>= 2.2.0'
23
+
24
+ s.add_development_dependency 'yard'
25
+ s.add_development_dependency 'bluecloth'
26
+ s.add_development_dependency 'rspec'
27
+
28
+ end
@@ -0,0 +1 @@
1
+ require 'keepass/password'
@@ -0,0 +1,58 @@
1
+ require 'keepass/password/char_set'
2
+ require 'keepass/password/generator'
3
+ require 'keepass/password/version'
4
+
5
+ module KeePass
6
+
7
+ module Password
8
+
9
+ # Returns a generated password.
10
+ #
11
+ # @param [String] pattern the pattern
12
+ # @param [Hash] options the options
13
+ # @option options [Boolean] :permute (true) whether or not to randomly permute generated passwords
14
+ # @option options [Boolean] :remove_lookalikes (false) whether or not to remove lookalike characters
15
+ # @option options [Hash] :charset_mapping (CharSet::DEFAULT_MAPPING) the KeePass character set ID mapping
16
+ # @return [String] the new password
17
+ # @raise [InvalidPatternError] if `pattern` is invalid
18
+ def self.generate(pattern, options = {})
19
+ Generator.new(pattern, options).generate
20
+ end
21
+
22
+ # Returns whether or not the pattern is valid.
23
+ #
24
+ # @param [String] pattern the pattern
25
+ # @param [Hash] options the options
26
+ # @option options [Boolean] :permute (true) whether or not to randomly permute generated passwords
27
+ # @option options [Boolean] :remove_lookalikes (false) whether or not to remove lookalike characters
28
+ # @option options [Hash] :charset_mapping (CharSet::DEFAULT_MAPPING) the KeePass character set ID mapping
29
+ # @return [Boolean] whether or not the pattern is valid
30
+ def self.validate_pattern(pattern, options = {})
31
+ begin
32
+ generate(pattern, options)
33
+ true
34
+ rescue InvalidPatternError
35
+ false
36
+ end
37
+ end
38
+
39
+ # Returns an entropy estimate of a password.
40
+ #
41
+ # @param [String] test the password to test
42
+ # @see http://en.wikipedia.org/wiki/Password_strength
43
+ def self.estimate_entropy(test)
44
+ chars = 0
45
+ chars += 26 if test =~ LOWERCASE_TEST_RE
46
+ chars += 26 if test =~ UPPERCASE_TEST_RE
47
+ chars += 10 if test =~ DIGITS_TEST_RE
48
+ chars += CharSet::PRINTABLE_ASCII_SPECIAL.size if test =~ SPECIAL_TEST_RE
49
+ if chars == 0
50
+ 0
51
+ else
52
+ (test.size * Math.log(chars) / Math.log(2)).to_i
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,91 @@
1
+ require 'set'
2
+
3
+ module KeePass
4
+
5
+ module Password
6
+
7
+ class InvalidCharSetIDError < RuntimeError; end
8
+
9
+ # Character sets for the KeePass password generator.
10
+ #
11
+ # @see http://keepass.info/help/base/pwgenerator.html#pattern
12
+ class CharSet < Set
13
+
14
+ UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
15
+ LOWERCASE = "abcdefghijklmnopqrstuvwxyz"
16
+ DIGITS = "0123456789"
17
+ UPPER_CONSONANTS = "BCDFGHJKLMNPQRSTVWXYZ"
18
+ LOWER_CONSONANTS = "bcdfghjklmnpqrstvwxyz"
19
+ UPPER_VOWELS = "AEIOU"
20
+ LOWER_VOWELS = "aeiou"
21
+ PUNCTUATION = ",.;:"
22
+ BRACKETS = "[]{}()<>"
23
+ PRINTABLE_ASCII_SPECIAL = "!\"#\$%&'()*+,-./:;<=>?[\\]^_{|}~"
24
+ UPPER_HEX = "0123456789ABCDEF"
25
+ LOWER_HEX = "0123456789abcdef"
26
+ HIGH_ANSI = (0x7f..0xfe).map { |i| i.chr }.join
27
+
28
+ DEFAULT_MAPPING = {
29
+ 'a' => [LOWERCASE, DIGITS],
30
+ 'A' => [LOWERCASE, UPPERCASE, DIGITS],
31
+ 'U' => [UPPERCASE, DIGITS],
32
+ 'c' => [LOWER_CONSONANTS],
33
+ 'C' => [LOWER_CONSONANTS, UPPER_CONSONANTS],
34
+ 'z' => [UPPER_CONSONANTS],
35
+ 'd' => [DIGITS],
36
+ 'h' => [LOWER_HEX],
37
+ 'H' => [UPPER_HEX],
38
+ 'l' => [LOWERCASE],
39
+ 'L' => [LOWERCASE, UPPERCASE],
40
+ 'u' => [UPPERCASE],
41
+ 'p' => [PUNCTUATION],
42
+ 'b' => [BRACKETS],
43
+ 's' => [PRINTABLE_ASCII_SPECIAL],
44
+ 'S' => [UPPERCASE, LOWERCASE, DIGITS, PRINTABLE_ASCII_SPECIAL],
45
+ 'v' => [LOWER_VOWELS],
46
+ 'V' => [LOWER_VOWELS, UPPER_VOWELS],
47
+ 'Z' => [UPPER_VOWELS],
48
+ 'x' => [HIGH_ANSI],
49
+ }
50
+
51
+ ASCII_MAPPING = DEFAULT_MAPPING.reject { |k, v| k == 'x' }
52
+
53
+ # @return [Hash] the KeePass character set ID mapping
54
+ attr_accessor :mapping
55
+
56
+ # Instantiates a new CharSet object.
57
+ #
58
+ # @see Set#new
59
+ def initialize(*args)
60
+ @mapping = DEFAULT_MAPPING
61
+ super
62
+ end
63
+
64
+ # Adds several characters according to the KeePass character class.
65
+ #
66
+ # @see http://keepass.info/help/base/pwgenerator.html#pattern
67
+ # @param [String] char_set_id the KeePass character set ID
68
+ # @raise [InvalidCharSetIDError] if mapping does not contain `char_set_id`
69
+ # @return [CharSet] self
70
+ def add_from_char_set_id(char_set_id)
71
+ if strings = mapping[char_set_id]
72
+ add_from_strings *strings
73
+ else
74
+ raise InvalidCharSetIDError, "no such char set ID #{char_set_id.inspect}"
75
+ end
76
+ end
77
+
78
+ # Adds each character from one or more strings.
79
+ #
80
+ # @param [Array] *strings one or more strings to add
81
+ # @return [CharSet] self
82
+ def add_from_strings(*strings)
83
+ strings.each { |s| merge Set.new(s.split('')) }
84
+ self
85
+ end
86
+
87
+ end
88
+
89
+ end
90
+
91
+ end
@@ -0,0 +1,111 @@
1
+ require 'keepass/password/char_set'
2
+ require 'keepass/random'
3
+
4
+ module KeePass
5
+
6
+ module Password
7
+
8
+ class InvalidPatternError < RuntimeError; end
9
+
10
+ # Generate passwords using KeePass password generator patterns.
11
+ #
12
+ # @see http://keepass.info/help/base/pwgenerator.html
13
+ class Generator
14
+
15
+ # Available character sets
16
+ CHARSET_IDS = CharSet::DEFAULT_MAPPING.keys.join
17
+
18
+ # ASCII printables regular expression
19
+ LITERALS_RE = /[\x20-\x7e]/
20
+ CHAR_TOKEN_RE = Regexp.new("([#{CHARSET_IDS}])|\\\\(#{LITERALS_RE.source})")
21
+ GROUP_TOKEN_RE = Regexp.new("(#{CHAR_TOKEN_RE.source}|" +
22
+ "\\[((#{CHAR_TOKEN_RE.source})*?)\\])" +
23
+ "(\\{(\\d+)\\})?")
24
+ VALIDATOR_RE = Regexp.new("\\A(#{GROUP_TOKEN_RE.source})+\\Z")
25
+
26
+ LOOKALIKE = "O0l1I|"
27
+ LOOKALIKE_CHARSET = CharSet.new.add_from_strings LOOKALIKE
28
+
29
+ # @return [String] the pattern
30
+ attr_reader :pattern
31
+
32
+ # @return [Array<CharSet>] the character sets from the pattern
33
+ attr_reader :char_sets
34
+
35
+ # @return [Boolean] whether or not to permute the password
36
+ attr_accessor :permute
37
+
38
+ # Instantiates a new PasswordGenerator object.
39
+ #
40
+ # @param [String] pattern the pattern
41
+ # @param [Hash] options the options
42
+ # @option options [Boolean] :permute (true) whether or not to randomly permute generated passwords
43
+ # @option options [Boolean] :remove_lookalikes (false) whether or not to remove lookalike characters
44
+ # @option options [Hash] :charset_mapping (CharSet::DEFAULT_MAPPING) the KeePass character set ID mapping
45
+ # @return [PasswordGenerator] self
46
+ # @raise [InvalidPatternError] if `pattern` is invalid
47
+ def initialize(pattern, options = {})
48
+ @permute = options.has_key?(:permute) ? options[:permute] : true
49
+ @pattern = pattern
50
+ @char_sets = pattern_to_char_sets(pattern, options)
51
+ end
52
+
53
+ # Returns a new password.
54
+ #
55
+ # @return [String] a new password
56
+ def generate
57
+ result = char_sets.map { |c| Random.sample_array(c.to_a) }
58
+ result = Random.shuffle_array(result) if permute
59
+ result.join
60
+ end
61
+
62
+ private
63
+
64
+ def pattern_to_char_sets(pattern, options) #:nodoc:
65
+ remove_lookalikes = options[:remove_lookalikes] || false
66
+ mapping = options[:charset_mapping] || CharSet::DEFAULT_MAPPING
67
+ char_sets = []
68
+ i = 1
69
+ pattern.scan(GROUP_TOKEN_RE) do |x1, char, bs_char, char_group, x5, x6, x7, x8, repeat|
70
+ char_set = CharSet.new
71
+ char_set.mapping = mapping
72
+ begin
73
+ if char
74
+ char_set.add_from_char_set_id(char)
75
+ elsif bs_char
76
+ char_set.add(bs_char)
77
+ else
78
+ char_group.scan(CHAR_TOKEN_RE) do |c, e|
79
+ if c
80
+ char_set.add_from_char_set_id(c)
81
+ else
82
+ char_set.add(e)
83
+ end
84
+ end
85
+ end
86
+ rescue InvalidCharSetIDError => e
87
+ raise InvalidPatternError, e.message
88
+ end
89
+ char_set -= LOOKALIKE_CHARSET if remove_lookalikes
90
+ if char_set.empty?
91
+ raise InvalidPatternError, "empty character set for token #{i} for #{pattern.inspect}"
92
+ end
93
+ (repeat ? repeat.to_i : 1).times { char_sets << char_set }
94
+ i += 1
95
+ end
96
+
97
+ if char_sets.any?
98
+ char_sets
99
+ else
100
+ raise InvalidPatternError, "no char sets from #{pattern.inspect}"
101
+ end
102
+
103
+ end
104
+
105
+ # private
106
+
107
+ end
108
+
109
+ end
110
+
111
+ end
@@ -0,0 +1,5 @@
1
+ module KeePass
2
+ module Password
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,38 @@
1
+ require 'active_support/secure_random'
2
+
3
+ module KeePass
4
+
5
+ module Random
6
+
7
+ # If `n` is a positive integer, then returns a random
8
+ # integer `r` such that 0 <= `r` < `n`.
9
+ #
10
+ # If `n` is 0 or unspecified, then returns a random
11
+ # float `r` such that 0 <= `r` < 1.
12
+ #
13
+ # @param [Integer] n the upper bound
14
+ # @return [Integer|Float] the random number
15
+ # @see ActiveSupport::SecureRandom#random_number
16
+ def self.random_number(n = 0)
17
+ ActiveSupport::SecureRandom.random_number(n)
18
+ end
19
+
20
+ # Returns a randomly sampled item from the array.
21
+ #
22
+ # @param [Array] array the array to sample from
23
+ # @return [Object] random item or nil if no items exist
24
+ def self.sample_array(array)
25
+ array[random_number(array.size)]
26
+ end
27
+
28
+ # Returns the array shuffled randomly.
29
+ #
30
+ # @param [Array] array the array to shuffle
31
+ # @return [Array] the shuffled array
32
+ def self.shuffle_array(array)
33
+ array.sort_by { random_number }
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe KeePass::Password::CharSet do
4
+
5
+ describe "#add_from_strings" do
6
+
7
+ it "should add from multiple arguments" do
8
+ subject.add_from_strings('abc', 'cde', 'QQ')
9
+ subject.should == Set.new(%w(a b c d e Q))
10
+ end
11
+
12
+ end
13
+
14
+ describe "#add_from_char_set_id" do
15
+
16
+ it "should add the digits" do
17
+ subject.add_from_char_set_id('d')
18
+ subject.should == Set.new('0'..'9')
19
+ end
20
+
21
+ it "should support chaining" do
22
+ subject.add_from_char_set_id('l').add_from_char_set_id('u')
23
+ subject.should == (Set.new('a'..'z') + Set.new('A'..'Z'))
24
+ end
25
+
26
+ it "should allow x with default mapping" do
27
+ subject.add_from_char_set_id('x')
28
+ subject.should include(0x7f.chr)
29
+ end
30
+
31
+ it "should raise an error with ASCII mapping" do
32
+ subject.mapping = KeePass::Password::CharSet::ASCII_MAPPING
33
+ expect { subject.add_from_char_set_id('x') }.to raise_error(KeePass::Password::InvalidCharSetIDError)
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,117 @@
1
+ require 'spec_helper'
2
+
3
+ describe KeePass::Password::Generator do
4
+
5
+ def char_set(*ids)
6
+ char_set = KeePass::Password::CharSet.new
7
+ ids.each { |id| char_set.add_from_char_set_id(id) }
8
+ char_set
9
+ end
10
+
11
+ subject { described_class.new(pattern, options) }
12
+ let(:options) { { } }
13
+ let(:random_class) { KeePass::Random }
14
+
15
+ describe "pattern 'h{10}' (40-bit WEP key)" do
16
+
17
+ let(:pattern) { 'h{10}' }
18
+
19
+ its(:pattern) { should == 'h{10}' }
20
+ its(:permute) { should be_true }
21
+ its(:char_sets) { should have(10).items }
22
+
23
+ it "should generate 10 hex digits " do
24
+ random_class.should_receive(:sample_array) do |array|
25
+ array.sort.should == char_set('h').to_a.sort
26
+ '0'
27
+ end.exactly(10).times
28
+ random_class.should_receive(:shuffle_array) do |array|
29
+ array.sort
30
+ end.once
31
+ subject.generate.should == '0000000000'
32
+ end
33
+
34
+ end
35
+
36
+ describe "pattern 'HH\-HH\-HH\-HH\-HH\-HH', :permute => false" do
37
+
38
+ let(:pattern) { 'HH\-HH\-HH\-HH\-HH\-HH' }
39
+ let(:options) { { :permute => false } }
40
+
41
+ its(:pattern) { should == 'HH\-HH\-HH\-HH\-HH\-HH' }
42
+ its(:permute) { should be_false }
43
+ its(:char_sets) { should have(17).items }
44
+
45
+ it "should generate a MAC address" do
46
+ random_class.should_receive(:sample_array) do |array|
47
+ if array == ['-']
48
+ '-'
49
+ else
50
+ array.sort.should == char_set('H').to_a.sort
51
+ '0'
52
+ end
53
+ end.exactly(17).times
54
+ random_class.should_not_receive(:shuffle_array)
55
+ subject.generate.should == '00-00-00-00-00-00'
56
+ end
57
+
58
+ end
59
+
60
+ describe "pattern 'uullA{6}'" do
61
+
62
+ let(:pattern) { 'uullA{6}' }
63
+
64
+ its(:pattern) { should == 'uullA{6}' }
65
+ its(:permute) { should be_true }
66
+ its(:char_sets) { should have(10).items }
67
+
68
+ it "should generate a 10-character alphanumeric password" do
69
+ random_class.should_receive(:sample_array) do |array|
70
+ array.sort.first
71
+ end.exactly(10).times
72
+ random_class.should_receive(:shuffle_array) do |array|
73
+ array.sort
74
+ end.once
75
+ subject.generate.should == '000000AAaa'
76
+ end
77
+
78
+ end
79
+
80
+ describe "pattern '[As]{20}', :remove_lookalikes => true" do
81
+
82
+ let(:pattern) { '[As]{20}' }
83
+ let(:options) { { :remove_lookalikes => true } }
84
+
85
+ its(:pattern) { should == '[As]{20}' }
86
+ its(:permute) { should be_true }
87
+ its(:char_sets) { should have(20).items }
88
+
89
+ it "should generate a 20-character password" do
90
+ test_set = (char_set('A', 's') - Set.new(%w(O 0 l 1 I |))).to_a.sort
91
+ i = 0
92
+ random_class.should_receive(:sample_array) do |array|
93
+ array.sort.should == test_set
94
+ result = array.sort[i]
95
+ i += 1
96
+ result
97
+ end.exactly(20).times
98
+ random_class.should_receive(:shuffle_array) do |array|
99
+ array.sort
100
+ end.once
101
+ subject.generate.should == '!"#$%&\'()*+,-./23456'
102
+ end
103
+
104
+ end
105
+
106
+ describe "pattern '[\\I\\|]{3}', :remove_lookalikes => true" do
107
+
108
+ let(:pattern) { '[\I\|]{3}' }
109
+ let(:options) { { :remove_lookalikes => true } }
110
+
111
+ it "should raise an error" do
112
+ expect { subject }.to raise_error(KeePass::Password::InvalidPatternError)
113
+ end
114
+
115
+ end
116
+
117
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe KeePass::Random do
4
+
5
+ describe "#random_number" do
6
+
7
+ it "should use ActiveSupport::SecureRandom" do
8
+ ActiveSupport::SecureRandom.should_receive(:random_number).once.with(12)
9
+ described_class.random_number(12)
10
+ end
11
+
12
+ it "should accept default argument" do
13
+ ActiveSupport::SecureRandom.should_receive(:random_number).with(0)
14
+ described_class.random_number
15
+ end
16
+
17
+ end
18
+
19
+ describe "#sample_array" do
20
+
21
+ it "should call random_number with the array size" do
22
+ described_class.should_receive(:random_number).with(6).and_return(3)
23
+ described_class.sample_array(%w(a b c d e f)).should == 'd'
24
+ end
25
+
26
+ it "should return expected values for deterministic random number" do
27
+ described_class.stub(:random_number) { |arg| 0 }
28
+ described_class.sample_array(%w(a b c)).should == 'a'
29
+ described_class.sample_array(%w(b a c)).should == 'b'
30
+ described_class.sample_array(%w(c b a)).should == 'c'
31
+ end
32
+
33
+ end
34
+
35
+ describe "#shuffle_array" do
36
+
37
+ it "should call random_number with no parameters" do
38
+ described_class.should_receive(:random_number).with().at_least(5).times.and_return(0.5)
39
+ described_class.shuffle_array(%w(a b c d e))
40
+ end
41
+
42
+ it "should return the same elements" do
43
+ described_class.stub(:random_number) { 0.5 }
44
+ described_class.shuffle_array(%w(a b c d e)).sort.should == %w(a b c d e)
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,32 @@
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ require 'rspec'
4
+ require 'keepass/password/generator'
5
+
6
+ # module DeterministicRandomness
7
+ #
8
+ # def deterministic_random_number(&block)
9
+ # ActiveSupport::SecureRandom.stub(:random_number) do |arg|
10
+ # if block
11
+ # block.call(arg)
12
+ # else
13
+ # 0
14
+ # end
15
+ # end
16
+ # end
17
+ #
18
+ # # def deterministic_shuffle
19
+ # # Array.any_instance.stub(:shuffle!)
20
+ # # end
21
+ #
22
+ # end
23
+ #
24
+ # RSpec.configure do |config|
25
+ # config.include DeterministicRandomness
26
+ # end
27
+
28
+ # RSpec::Matchers.define :have_char_set_length_of do |expected|
29
+ # match do |actual|
30
+ # actual.char_sets.size == expected
31
+ # end
32
+ # end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: keepass-password-generator
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - John Nishinaga
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-22 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ prerelease: false
23
+ type: :runtime
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 7
30
+ segments:
31
+ - 2
32
+ - 2
33
+ - 0
34
+ version: 2.2.0
35
+ name: activesupport
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ prerelease: false
39
+ type: :development
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ name: yard
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ prerelease: false
53
+ type: :development
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ name: bluecloth
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ prerelease: false
67
+ type: :development
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ name: rspec
78
+ version_requirements: *id004
79
+ description: Generate passwords using KeePass password generator patterns
80
+ email:
81
+ - jingoro@casa-z.org
82
+ executables: []
83
+
84
+ extensions: []
85
+
86
+ extra_rdoc_files: []
87
+
88
+ files:
89
+ - .gitignore
90
+ - .rspec
91
+ - .yardopts
92
+ - CHANGELOG.md
93
+ - Gemfile
94
+ - MIT-LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - keepass-password-generator.gemspec
98
+ - lib/keepass-password-generator.rb
99
+ - lib/keepass/password.rb
100
+ - lib/keepass/password/char_set.rb
101
+ - lib/keepass/password/generator.rb
102
+ - lib/keepass/password/version.rb
103
+ - lib/keepass/random.rb
104
+ - spec/char_set_spec.rb
105
+ - spec/generator_spec.rb
106
+ - spec/random_spec.rb
107
+ - spec/spec_helper.rb
108
+ has_rdoc: true
109
+ homepage: https://github.com/patdeegan/keepass-password-generator
110
+ licenses: []
111
+
112
+ post_install_message:
113
+ rdoc_options: []
114
+
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ hash: 3
123
+ segments:
124
+ - 0
125
+ version: "0"
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ hash: 3
132
+ segments:
133
+ - 0
134
+ version: "0"
135
+ requirements: []
136
+
137
+ rubyforge_project: keepass-password-generator
138
+ rubygems_version: 1.5.2
139
+ signing_key:
140
+ specification_version: 3
141
+ summary: keepass-password-generator-0.1.0
142
+ test_files:
143
+ - spec/char_set_spec.rb
144
+ - spec/generator_spec.rb
145
+ - spec/random_spec.rb
146
+ - spec/spec_helper.rb