polypass 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'literate_randomizer'
5
+ require 'polypass/alpha_numeric'
6
+ require 'polypass/alpha_symbol'
7
+ require 'polypass/helpers'
8
+ require 'polypass/natural_language'
9
+ require 'polypass/version'
10
+ require 'securerandom'
11
+ require 'yaml'
12
+
13
+ # The polymorphous password generator
14
+ class Polypass
15
+ attr_accessor :length, :output_type
16
+
17
+ def initialize(length = 32, output_type = 'stdout')
18
+ @length = length.to_i
19
+ @output_type = output_type.to_s
20
+ end
21
+
22
+ # Set of general built-in helper methods
23
+ include PolypassHelpers
24
+
25
+ # Create and generate secure random alphanumeric passwords
26
+ class AlphaNumeric < Polypass
27
+ include PolypassAlphaNumeric
28
+ end
29
+
30
+ # Create and generate secure random alphanumeric symbol passwords
31
+ class AlphaSymbol < Polypass
32
+ include PolypassAlphaSymbol
33
+ end
34
+
35
+ # Create and generate secure random natural language passwords
36
+ class NaturalLanguage < Polypass
37
+ include PolypassNaturalLanguage
38
+ end
39
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Polypass::AlphaNumberic Class methods
4
+ module PolypassAlphaNumeric
5
+ def initialize(length = 32, output_type = 'stdout')
6
+ super(length, output_type)
7
+ end
8
+
9
+ def create
10
+ case @output_type
11
+ when 'stdout'
12
+ random_element(random_sample_set('alphanumeric', @length))
13
+ when 'json'
14
+ output_json({ 'password' => random_element(random_sample_set('alphanumeric', @length)) })
15
+ when 'yaml'
16
+ output_yaml({ 'password' => random_element(random_sample_set('alphanumeric', @length)) })
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Polypass::AlphaSymbol Class methods
4
+ module PolypassAlphaSymbol
5
+ def initialize(length = 32, output_type = 'stdout')
6
+ super(length, output_type)
7
+ end
8
+
9
+ def create
10
+ case @output_type
11
+ when 'stdout'
12
+ random_element(random_sample_set('alphasymbol', @length))
13
+ when 'json'
14
+ output_json({ 'password' => random_element(random_sample_set('alphasymbol', @length)) })
15
+ when 'yaml'
16
+ output_yaml({ 'password' => random_element(random_sample_set('alphasymbol', @length)) })
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'polypass'
4
+ require 'thor'
5
+
6
+ # Thor powered command-line interface
7
+ class PolypassCLI < Thor
8
+ desc 'version', 'print the version of Polypass'
9
+ def version
10
+ puts 'polypass ' + Polypass::VERSION
11
+ end
12
+
13
+ desc 'create TYPE', 'create a TYPE secure random generated password'
14
+ option :length, aliases: ['-l'], required: false, type: :numeric,
15
+ desc: 'length of password'
16
+ option :output, aliases: ['-o'], required: false, type: :string,
17
+ desc: 'type of output formatter. Valid types are: stdout, json, yaml'
18
+ long_desc <<-LONGDESC
19
+ `polypass create TYPE` will generate a new secure random password.
20
+
21
+ TYPE is the type of password Polypass will create such as alphanumeric,
22
+ alphanumeric with symbols, natural language, and more.
23
+
24
+ TYPE can be one of the following values:
25
+
26
+ alphanum alphasym natural
27
+ LONGDESC
28
+ def create(type)
29
+ length = options[:length].nil? ? 32 : options[:length]
30
+ output = options[:output].nil? ? 'stdout' : options[:output]
31
+
32
+ unless ['stdout', 'json', 'yaml'].include?(output)
33
+ raise ArgumentError,
34
+ output + ' is not a valid output type. ' +
35
+ 'Available types include: stdout, json, yaml'
36
+ end
37
+
38
+ case type
39
+ when 'alphanum'
40
+ puts 'Generating a new secure random alphanumeric password:'
41
+ puts Polypass::AlphaNumeric.new(length, output).create
42
+ when 'alphasym'
43
+ puts 'Generating a new secure random alphanumeric symbol password:'
44
+ puts Polypass::AlphaSymbol.new(length, output).create
45
+ when 'natural'
46
+ length = options[:length].nil? ? 4 : options[:length]
47
+ puts 'Generating a new secure random natural language password:'
48
+ puts Polypass::NaturalLanguage.new(length, output).create
49
+ else
50
+ raise ArgumentError,
51
+ type + ' is not a valid password type. Avaiable types include: alphanum, alphasym, natural'
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Collection of general Polypass helper methods
4
+ module PolypassHelpers
5
+ def alphanumeric_chars
6
+ characters = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
7
+ return characters
8
+ end
9
+
10
+ def alphasymbol_chars
11
+ characters = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
12
+ characters += %w(! @ # $ % ^ & * \( \) { } [ ] - _ ~ < > ? ; : ` ~ / + = , . |)
13
+ return characters
14
+ end
15
+
16
+ def output_json(hash)
17
+ JSON.pretty_generate(hash)
18
+ end
19
+
20
+ def output_yaml(hash)
21
+ YAML.dump(hash)
22
+ end
23
+
24
+ def random_element(array)
25
+ array[SecureRandom.random_number(array.size)].to_s
26
+ end
27
+
28
+ def random_number(integer = 1000)
29
+ (SecureRandom.random_number(integer) + (SecureRandom.random_number(100) + 1)).to_i
30
+ end
31
+
32
+ def random_sample_chars(chars, length = 32)
33
+ (1..length).map { chars[SecureRandom.random_number(chars.size)] }.join
34
+ end
35
+
36
+ def random_sample_set(chartype, length = 32, rounds = 1000)
37
+ random_chars = Array.new
38
+ (1..random_number(rounds)).each do
39
+ case chartype
40
+ when 'alphanumeric'
41
+ random_chars << random_sample_chars(alphanumeric_chars, length)
42
+ when 'alphasymbol'
43
+ random_chars << random_sample_chars(alphasymbol_chars, length)
44
+ else
45
+ raise ArgumentError,
46
+ chartype + 'is an invalid chartype argument. alphanumeric and alphasymbol are the ' +
47
+ 'only valid chartype arguments for the random_sample_set method'
48
+ end
49
+ end
50
+ return random_chars
51
+ end
52
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Polypass::NaturalLanguage Class methods
4
+ module PolypassNaturalLanguage
5
+ attr_accessor :language_data
6
+
7
+ def initialize(length = 32, output_type = 'stdout')
8
+ super(length, output_type)
9
+ @language_data = Dir.glob(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'data')) + '/*.txt')
10
+ end
11
+
12
+ def init_language_randomizer
13
+ return LiterateRandomizer.create(
14
+ {
15
+ source_material_file: random_element(@language_data),
16
+ randomizer: SecureRandom.random_number(Random.new_seed)
17
+ }
18
+ )
19
+ end
20
+
21
+ def create
22
+ language = init_language_randomizer
23
+ case @output_type
24
+ when 'stdout'
25
+ language.sentence + random_element(random_sample_set('alphanumeric', @length))
26
+ when 'json'
27
+ output_json({ 'password' => language.sentence + random_element(random_sample_set('alphanumeric', @length)) })
28
+ when 'yaml'
29
+ output_yaml({ 'password' => language.sentence + random_element(random_sample_set('alphanumeric', @length)) })
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Polypass
4
+ VERSION = '1.0.0'
5
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path('lib', __dir__)
4
+ require 'polypass/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'polypass'
8
+ s.date = '2018-07-12'
9
+ s.version = Polypass::VERSION
10
+ s.authors = ['Shane R. Sofos']
11
+ s.email = ['ssofos@gmail.com']
12
+ s.homepage = 'https://shanesofos.com/projects/polypass'
13
+ s.license = 'GPL-3.0'
14
+ s.metadata = { 'source_code_uri' => 'https://gitlab.com/ssofos/polypass' }
15
+ s.description = 'The polymorphous password generator.'
16
+ s.summary =
17
+ 'A simple multitool that can generate different types of passwords ' +
18
+ 'including secure random, alphanumeric, natural language, and custom, ' +
19
+ 'salt, hash, and output them in different formats for personal and application consumption.'
20
+
21
+ s.files = %x(git ls-files).split($INPUT_RECORD_SEPARATOR)
22
+ s.bindir = ['bin']
23
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
24
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
25
+ s.require_paths = ['lib']
26
+
27
+ s.add_dependency 'literate_randomizer'
28
+ s.add_dependency 'thor'
29
+ s.add_development_dependency 'bundler'
30
+ s.add_development_dependency 'rake'
31
+ s.add_development_dependency 'rspec'
32
+ s.add_development_dependency 'rubocop', '<=0.56.0'
33
+ s.add_development_dependency 'simplecov'
34
+
35
+ s.required_ruby_version = '>= 2.4.0'
36
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'polypass'
4
+
5
+ describe Polypass::AlphaNumeric do
6
+ before :all do
7
+ @polypass_alphanumeric = Polypass::AlphaNumeric.new
8
+ end
9
+
10
+ it 'is a Polypass::AlphaNumeric object' do
11
+ expect(@polypass_alphanumeric).to be_kind_of(Polypass::AlphaNumeric)
12
+ end
13
+
14
+ it 'should respond to 2 arguments when initialized' do
15
+ expect(Polypass).to respond_to(:new).with(2).arguments
16
+ end
17
+
18
+ describe '#length' do
19
+ it 'returns the length of the generated password as Integer class object' do
20
+ expect(@polypass_alphanumeric.length).to be_kind_of(Integer).and eq(32)
21
+ end
22
+ end
23
+
24
+ describe '#output_type' do
25
+ it 'returns the output type for generated passwords as a String class object' do
26
+ expect(@polypass_alphanumeric.output_type).to be_kind_of(String).and eq('stdout')
27
+ end
28
+ end
29
+
30
+ describe '.create' do
31
+ context 'stdout output type' do
32
+ it 'returns a secure random generated alphanumeric 32 character password as a String class object' do
33
+ expect(@polypass_alphanumeric.create).to be_kind_of(String)
34
+ expect(@polypass_alphanumeric.create.length).to eq(32)
35
+ end
36
+ end
37
+
38
+ context 'json output type' do
39
+ it 'returns a secure random generated alphanumeric 32 character password as JSON formatted String class object' do
40
+ expect(Polypass::AlphaNumeric.new(32, 'json').create).to be_kind_of(String)
41
+ end
42
+ end
43
+
44
+ context 'yaml output type' do
45
+ it 'returns a secure random generated alphanumeric 32 character password as YAML formatted String class object' do
46
+ expect(Polypass::AlphaNumeric.new(32, 'yaml').create).to be_kind_of(String)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'polypass'
4
+
5
+ describe Polypass::AlphaSymbol do
6
+ before :all do
7
+ @polypass_alphasymbol = Polypass::AlphaSymbol.new
8
+ end
9
+
10
+ it 'is a Polypass::AlphaSymbol object' do
11
+ expect(@polypass_alphasymbol).to be_kind_of(Polypass::AlphaSymbol)
12
+ end
13
+
14
+ it 'should respond to 2 arguments when initialized' do
15
+ expect(Polypass).to respond_to(:new).with(2).arguments
16
+ end
17
+
18
+ describe '#length' do
19
+ it 'returns the length of the generated password as Integer class object' do
20
+ expect(@polypass_alphasymbol.length).to be_kind_of(Integer).and eq(32)
21
+ end
22
+ end
23
+
24
+ describe '#output_type' do
25
+ it 'returns the output type for generated passwords as a String class object' do
26
+ expect(@polypass_alphasymbol.output_type).to be_kind_of(String).and eq('stdout')
27
+ end
28
+ end
29
+
30
+ describe '.create' do
31
+ context 'stdout output type' do
32
+ it 'returns a secure random generated alphanumeric symbol 32 character password as a String class object' do
33
+ expect(@polypass_alphasymbol.create).to be_kind_of(String)
34
+ expect(@polypass_alphasymbol.create.length).to eq(32)
35
+ end
36
+ end
37
+
38
+ context 'json output type' do
39
+ it 'returns a secure random generated alphanumeric symbol password as JSON formatted String class object' do
40
+ expect(Polypass::AlphaSymbol.new(32, 'json').create).to be_kind_of(String)
41
+ end
42
+ end
43
+
44
+ context 'yaml output type' do
45
+ it 'returns a secure random generated alphanumeric symbol password as YAML formatted String class object' do
46
+ expect(Polypass::AlphaSymbol.new(32, 'yaml').create).to be_kind_of(String)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'polypass'
4
+
5
+ describe 'Polypass Helpers' do
6
+ before :all do
7
+ @polypass = Polypass.new
8
+ end
9
+
10
+ describe '.alphanumeric_chars' do
11
+ it 'returns 62 alphanumeric characters as an Array class object' do
12
+ expect(@polypass.alphanumeric_chars).to be_kind_of(Array)
13
+ expect(@polypass.alphanumeric_chars.length).to eq(62)
14
+ end
15
+ end
16
+
17
+ describe '.alphasymbol_chars' do
18
+ it 'returns 92 alphanumeric and symbol characters as an Array class object' do
19
+ expect(@polypass.alphasymbol_chars).to be_kind_of(Array)
20
+ expect(@polypass.alphasymbol_chars.length).to eq(92)
21
+ end
22
+ end
23
+
24
+ describe '.output_json' do
25
+ it 'should respond to 1 argument' do
26
+ expect(@polypass).to respond_to(:output_json).with(1).argument
27
+ end
28
+ end
29
+
30
+ describe '.output_yaml' do
31
+ it 'should respond to 1 argument' do
32
+ expect(@polypass).to respond_to(:output_yaml).with(1).argument
33
+ end
34
+ end
35
+
36
+ describe '.random_element' do
37
+ it 'should respond to 1 argument' do
38
+ expect(@polypass).to respond_to(:random_element).with(1).argument
39
+ end
40
+
41
+ it 'returns a secure random single element from an Array as a String class object' do
42
+ expect(@polypass.random_element(@polypass.alphanumeric_chars)).to be_kind_of(String)
43
+ expect(@polypass.random_element(@polypass.alphanumeric_chars).length).to eq(1)
44
+ end
45
+ end
46
+
47
+ describe '.random_number' do
48
+ it 'should respond to 1 argument' do
49
+ expect(@polypass).to respond_to(:random_element).with(1).argument
50
+ end
51
+
52
+ context 'default integer argument of 1000' do
53
+ it 'returns a secure random number less than or equal to 1101 as an Integer class object' do
54
+ expect(@polypass.random_number).to be_kind_of(Integer)
55
+ expect(@polypass.random_number).to be <= 1101
56
+ end
57
+ end
58
+
59
+ context 'integer argument of 10000' do
60
+ it 'returns a secure random number less than or equal to 10101 as an Integer class object' do
61
+ expect(@polypass.random_number).to be_kind_of(Integer)
62
+ expect(@polypass.random_number).to be <= 10101
63
+ end
64
+ end
65
+ end
66
+
67
+ describe '.random_sample_chars' do
68
+ it 'should respond to 2 arguments' do
69
+ expect(@polypass).to respond_to(:random_sample_chars).with(2).arguments
70
+ end
71
+
72
+ context 'random alphanumberic characters with default length' do
73
+ it 'returns a random sample of 32 characters as a String class object' do
74
+ expect(@polypass.random_sample_chars(@polypass.alphanumeric_chars)).to be_kind_of(String)
75
+ expect(@polypass.random_sample_chars(@polypass.alphanumeric_chars).length).to eq(32)
76
+ end
77
+ end
78
+
79
+ context 'random alphanumberic and symbol characters with default character length' do
80
+ it 'returns a random sample of 32 characters as a String class object' do
81
+ expect(@polypass.random_sample_chars(@polypass.alphasymbol_chars)).to be_kind_of(String)
82
+ expect(@polypass.random_sample_chars(@polypass.alphasymbol_chars).length).to eq(32)
83
+ end
84
+ end
85
+
86
+ context 'random alphanumberic and symbol characters with 128 character length' do
87
+ it 'returns a random sample of 32 characters as a String class object' do
88
+ expect(@polypass.random_sample_chars(@polypass.alphasymbol_chars)).to be_kind_of(String)
89
+ expect(@polypass.random_sample_chars(@polypass.alphasymbol_chars, 128).length).to eq(128)
90
+ end
91
+ end
92
+ end
93
+
94
+ describe '.random_sample_set' do
95
+ it 'should respond to 3 arguments' do
96
+ expect(@polypass).to respond_to(:random_sample_set).with(3).arguments
97
+ end
98
+
99
+ context 'given an alphanumeric character type with defult length of 32 and rounds of 1000' do
100
+ it 'should return a set of of less than or equal to 1101 random passwords as an Array class object' do
101
+ expect(@polypass.random_sample_set('alphanumeric')).to be_kind_of(Array)
102
+ expect(@polypass.random_sample_set('alphanumeric').length).to be <= 1101
103
+ end
104
+ end
105
+
106
+ context 'given an alphansymbol character type with defult length of 32 and rounds of 1000' do
107
+ it 'should return a set of of less than or equal to 1101 random passwords as an Array class object' do
108
+ expect(@polypass.random_sample_set('alphasymbol')).to be_kind_of(Array)
109
+ expect(@polypass.random_sample_set('alphasymbol').length).to be <= 1101
110
+ end
111
+ end
112
+
113
+ context 'given an invalid character type argument' do
114
+ it 'should raise an ArgumentError expection' do
115
+ expect { @polypass.random_sample_set('foobar') }.to raise_error(ArgumentError)
116
+ end
117
+ end
118
+ end
119
+ end