polypass 1.0.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.
@@ -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