ppg 0.0.1.1
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 +7 -0
- data/bin/ppg +89 -0
- data/lib/ppg.rb +1 -0
- data/lib/ppg/password.rb +18 -0
- data/lib/ppg/phonemes.rb +41 -0
- data/lib/ppg/pwgen.rb +143 -0
- metadata +49 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8cfd70a8672f3df6fe2516af336b97f686aa3001
|
4
|
+
data.tar.gz: 82bfb856d6a3cfe78bbfcbcd70066abfd6a00d8a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: db880f29757216f42134cb11376b231931a2bb1ada582d7dc1771bafd2c36f1d110efaccf551852b2fbb28f2ccf78457f2a4ea521209e6c7994fcf534e036819
|
7
|
+
data.tar.gz: 6fb661bc2b099fbf6b15aad50b16484f5907e21d702e2b57927863561e3aec3e7ac2b451c408bbdf67c9d2cd286e4ef4f3bd0062412cb8648c2f211802ca6cf2
|
data/bin/ppg
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- encoding: utf-8 -*-
|
3
|
+
|
4
|
+
require 'ppg'
|
5
|
+
|
6
|
+
require 'getoptlong'
|
7
|
+
|
8
|
+
DEFAULT_PASSWORD_LENGTH = 8
|
9
|
+
|
10
|
+
opts = GetoptLong.new(
|
11
|
+
['--help', '-h', GetoptLong::NO_ARGUMENT],
|
12
|
+
['--digits', '-d', GetoptLong::OPTIONAL_ARGUMENT],
|
13
|
+
['--upcase', '-u', GetoptLong::OPTIONAL_ARGUMENT],
|
14
|
+
['--length', '-l', GetoptLong::REQUIRED_ARGUMENT],
|
15
|
+
['--verbous', '-v', GetoptLong::NO_ARGUMENT]
|
16
|
+
)
|
17
|
+
|
18
|
+
def print_help
|
19
|
+
puts <<-EOF
|
20
|
+
generator.rb [OPTIONS]
|
21
|
+
|
22
|
+
-h --help: prints this help
|
23
|
+
|
24
|
+
-d N include N digits in password
|
25
|
+
--digits N 1 by default
|
26
|
+
|
27
|
+
-u N Include N letters in upper case
|
28
|
+
--upcase N 1 by default
|
29
|
+
|
30
|
+
-l N make password of N characters
|
31
|
+
--length N
|
32
|
+
|
33
|
+
-v turns on additional output
|
34
|
+
--verbous
|
35
|
+
|
36
|
+
EOF
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
begin
|
41
|
+
|
42
|
+
mutators = {}
|
43
|
+
password_length = DEFAULT_PASSWORD_LENGTH
|
44
|
+
|
45
|
+
opts.each do |opt, arg|
|
46
|
+
case opt
|
47
|
+
when '--help'
|
48
|
+
print_help
|
49
|
+
exit
|
50
|
+
when '--digits'
|
51
|
+
|
52
|
+
mutators[:Digits] = Proc.new { Random.rand(10).to_s },
|
53
|
+
arg.empty? ? 1 : arg.to_i
|
54
|
+
|
55
|
+
when '--upcase'
|
56
|
+
mutators[:Upcase] = Proc.new { |c| c.upcase },
|
57
|
+
arg.empty? ? 1 : arg.to_i
|
58
|
+
|
59
|
+
when '--length'
|
60
|
+
password_length = arg.to_i
|
61
|
+
raise "password length must be greater than 0" if password_length <= 0
|
62
|
+
|
63
|
+
when '--verbous'
|
64
|
+
class Password
|
65
|
+
def to_s
|
66
|
+
"\n" \
|
67
|
+
"password: #{password}\n" \
|
68
|
+
"pattern: #{password_pattern.to_s}\n"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
pwgen = PwGen.new(password_length)
|
76
|
+
mutators.each { |name, mutator|
|
77
|
+
puts "enabling #{name} mutator for #{mutator.last} character(s)"
|
78
|
+
pwgen.add_mutator(*mutator)
|
79
|
+
}
|
80
|
+
|
81
|
+
puts pwgen.generate_password
|
82
|
+
|
83
|
+
rescue GetoptLong::MissingArgument
|
84
|
+
puts
|
85
|
+
print_help
|
86
|
+
exit
|
87
|
+
|
88
|
+
end
|
89
|
+
|
data/lib/ppg.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'ppg/pwgen'
|
data/lib/ppg/password.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
class Password
|
2
|
+
attr_accessor :password, :password_pattern
|
3
|
+
attr_reader :PASSWORD_LENGTH
|
4
|
+
|
5
|
+
def initialize (password_length, password_pattern = nil)
|
6
|
+
@PASSWORD_LENGTH = password_length.freeze
|
7
|
+
@password = String.new
|
8
|
+
#TODO: make password_pattern validation check method
|
9
|
+
@password_pattern ||= []
|
10
|
+
end
|
11
|
+
|
12
|
+
def length= (length)
|
13
|
+
@PASSWORD_LENGTH = length.freeze
|
14
|
+
end
|
15
|
+
|
16
|
+
alias :to_s :password
|
17
|
+
|
18
|
+
end
|
data/lib/ppg/phonemes.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
$ENGLISH_PHONEMES_HASH = {
|
2
|
+
|
3
|
+
:vowels => [ "a","e","i","o","u" ],
|
4
|
+
|
5
|
+
:consonants => [ "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n",
|
6
|
+
"p", "r", "s", "t", "v", "w", "x", "y", "z" ],
|
7
|
+
|
8
|
+
:dipthongs => {
|
9
|
+
:vowel => ["ae", "oh", "oo", "ah", "ai", "ee", "ei", "ie"],
|
10
|
+
:consonant => ["ng", "gh", "ch", "ph", "qu", "sh", "th"]
|
11
|
+
}
|
12
|
+
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
$ENGLISH_DOWNCASE_REGEXP = /[a-z]/.freeze
|
16
|
+
|
17
|
+
class Phonemes
|
18
|
+
attr_reader :downcase_regexp
|
19
|
+
|
20
|
+
def initialize(
|
21
|
+
vowels = nil,
|
22
|
+
consonants = nil,
|
23
|
+
dipthongs = nil,
|
24
|
+
downcase_regexp = $ENGLISH_DOWNCASE_REGEXP
|
25
|
+
)
|
26
|
+
|
27
|
+
@phonemes = { :vowel => vowels || $ENGLISH_PHONEMES_HASH[:vowels],
|
28
|
+
:consonant => consonants || $ENGLISH_PHONEMES_HASH[:consonants],
|
29
|
+
:dipthong => dipthongs || $ENGLISH_PHONEMES_HASH[:dipthongs]
|
30
|
+
}
|
31
|
+
@downcase_regexp = downcase_regexp
|
32
|
+
end
|
33
|
+
|
34
|
+
def keys
|
35
|
+
@phonemes.keys
|
36
|
+
end
|
37
|
+
|
38
|
+
def [](index)
|
39
|
+
@phonemes[index]
|
40
|
+
end
|
41
|
+
end
|
data/lib/ppg/pwgen.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require_relative 'password'
|
3
|
+
require_relative 'phonemes'
|
4
|
+
|
5
|
+
require 'securerandom'
|
6
|
+
|
7
|
+
class MutatorError < Exception; end
|
8
|
+
|
9
|
+
class PwGen
|
10
|
+
attr_reader :password
|
11
|
+
attr_writer :phonemes
|
12
|
+
|
13
|
+
def initialize(length, phonemes = nil)
|
14
|
+
@password = Password.new(length)
|
15
|
+
@phonemes = phonemes || Phonemes.new
|
16
|
+
@mutators = {}
|
17
|
+
|
18
|
+
#TODO: избавиться от константы
|
19
|
+
@dipthong_length = 2
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_mutator(proc, count)
|
23
|
+
@mutated_chars ||= []
|
24
|
+
@clean_chars_left ||= @password.PASSWORD_LENGTH
|
25
|
+
|
26
|
+
return false if count == 0
|
27
|
+
|
28
|
+
if @clean_chars_left == 0
|
29
|
+
count = 0
|
30
|
+
raise MutatorError, "nothing left to mutate, rejecting!"
|
31
|
+
end
|
32
|
+
|
33
|
+
unless proc.respond_to? :call
|
34
|
+
raise MutatorError, "mutator's not callable, rejecting!"
|
35
|
+
end
|
36
|
+
|
37
|
+
if count > @clean_chars_left
|
38
|
+
error_string = "can't mutate #{count} characters, decreasing to #{@clean_chars_left}!\n"
|
39
|
+
count = @clean_chars_left
|
40
|
+
raise MutatorError, error_string
|
41
|
+
end
|
42
|
+
|
43
|
+
@clean_chars_left -= count
|
44
|
+
@mutators[proc] = count
|
45
|
+
true
|
46
|
+
|
47
|
+
rescue MutatorError
|
48
|
+
STDERR << $!.message
|
49
|
+
retry
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
def generate_password
|
54
|
+
generate_password_pattern!
|
55
|
+
.fill_in_password_pattern!
|
56
|
+
.mutate!
|
57
|
+
@password
|
58
|
+
end
|
59
|
+
|
60
|
+
def fill_in_password_pattern!
|
61
|
+
@password.password_pattern.each do |pattern|
|
62
|
+
if pattern.kind_of?(Symbol)
|
63
|
+
elem = @phonemes[pattern][SecureRandom.random_number(@phonemes[pattern].length)]
|
64
|
+
else pattern.kind_of?(Array)
|
65
|
+
# most definitely is dipthong
|
66
|
+
length = @phonemes[pattern[0]][pattern[1]].length
|
67
|
+
elem = @phonemes[pattern[0]][pattern[1]][SecureRandom.random_number(length)]
|
68
|
+
end
|
69
|
+
@password.password << elem
|
70
|
+
end
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
def generate_password_pattern!
|
75
|
+
length_left = @password.PASSWORD_LENGTH
|
76
|
+
next_pattern = previous_pattern = nil
|
77
|
+
can_be_next_patterns = @phonemes.keys
|
78
|
+
selector_block = Proc.new { |pattern|
|
79
|
+
pattern if pattern != next_pattern || pattern == :dipthong
|
80
|
+
}
|
81
|
+
while length_left > 0 do
|
82
|
+
loop do
|
83
|
+
next_pattern = generate_random_pattern(can_be_next_patterns)
|
84
|
+
break unless requirements_not_met?(next_pattern,
|
85
|
+
previous_pattern, length_left)
|
86
|
+
end
|
87
|
+
|
88
|
+
can_be_next_patterns.select! &selector_block
|
89
|
+
|
90
|
+
can_be_next_patterns << previous_pattern if previous_pattern != nil
|
91
|
+
|
92
|
+
if next_pattern == :dipthong
|
93
|
+
if @password.password_pattern.empty? #first element
|
94
|
+
auxiliary_pattern = generate_random_pattern([:vowel, :consonant])
|
95
|
+
else
|
96
|
+
auxiliary_pattern =
|
97
|
+
case previous_pattern
|
98
|
+
when :vowel then :consonant
|
99
|
+
when :consonant then :vowel
|
100
|
+
end
|
101
|
+
end
|
102
|
+
next_pattern = next_pattern, auxiliary_pattern
|
103
|
+
can_be_next_patterns -= [auxiliary_pattern]
|
104
|
+
length_left -= @dipthong_length
|
105
|
+
previous_pattern = auxiliary_pattern
|
106
|
+
|
107
|
+
else
|
108
|
+
length_left -= 1
|
109
|
+
previous_pattern = next_pattern
|
110
|
+
|
111
|
+
end
|
112
|
+
@password.password_pattern << next_pattern
|
113
|
+
end
|
114
|
+
self
|
115
|
+
end
|
116
|
+
|
117
|
+
def mutate!
|
118
|
+
@mutators.each_pair do |mutator, count|
|
119
|
+
while count > 0
|
120
|
+
loop do
|
121
|
+
index = SecureRandom.random_number(@password.PASSWORD_LENGTH)
|
122
|
+
next unless @password.password[index] =~ @phonemes.downcase_regexp
|
123
|
+
@password.password[index] = (mutator.call @password.password[index])
|
124
|
+
break
|
125
|
+
end
|
126
|
+
count -= 1
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def requirements_not_met?(next_pattern, previous_pattern, length_left)
|
132
|
+
return true if next_pattern == :dipthong && length_left < @dipthong_length
|
133
|
+
return true if next_pattern == previous_pattern
|
134
|
+
false
|
135
|
+
end
|
136
|
+
|
137
|
+
def generate_random_pattern(patterns)
|
138
|
+
patterns [ SecureRandom.random_number(patterns.length) ]
|
139
|
+
end
|
140
|
+
|
141
|
+
private :generate_random_pattern, :requirements_not_met?
|
142
|
+
|
143
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ppg
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- enoch0x5a
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-22 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A simple password generator with pronunciation in mind
|
14
|
+
email:
|
15
|
+
executables:
|
16
|
+
- ppg
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- bin/ppg
|
21
|
+
- lib/ppg.rb
|
22
|
+
- lib/ppg/password.rb
|
23
|
+
- lib/ppg/phonemes.rb
|
24
|
+
- lib/ppg/pwgen.rb
|
25
|
+
homepage:
|
26
|
+
licenses:
|
27
|
+
- MIT
|
28
|
+
metadata: {}
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
requirements: []
|
44
|
+
rubyforge_project:
|
45
|
+
rubygems_version: 2.2.2
|
46
|
+
signing_key:
|
47
|
+
specification_version: 4
|
48
|
+
summary: Phonetic Password Generator
|
49
|
+
test_files: []
|