xkpassword 0.4.0 → 0.6.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 +4 -4
- data/.gitignore +0 -1
- data/README.md +62 -33
- data/doc/README.md +9 -0
- data/doc/presets.md +37 -0
- data/exe/xkpassword +3 -45
- data/lib/xkpassword/cli.rb +125 -0
- data/lib/xkpassword/config_file.rb +105 -0
- data/lib/xkpassword/generator.rb +90 -14
- data/lib/xkpassword/version.rb +3 -1
- data/lib/xkpassword.rb +17 -1
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a8679b970d4279654ff7aa2f1212267bd2874466af8adfb197d4005dd94fb62a
|
|
4
|
+
data.tar.gz: 46877ecf578b62ced17e1ba35c8463387c34676d15cf301e8de5188af7619a79
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 512d12534d4a4d0072dfb2581f98c7b9e1464647739b7b508cea5e3fb4e9ab972158d3b4ffa58fafc12f1a904ddc369b308ec33d57a32355b29028e2424d5476
|
|
7
|
+
data.tar.gz: '0828510166c54f05316e349a0187aee1d9fd49acf19ee4d8fd970309c938e9a0cb2a06390436f32788a87dc2e71a3cbc23aaf31d82dff868340b45dbd3244a38'
|
data/.gitignore
CHANGED
data/README.md
CHANGED
|
@@ -29,7 +29,7 @@ it.
|
|
|
29
29
|
|
|
30
30
|
## Installation
|
|
31
31
|
|
|
32
|
-
Developed in Linux/Ubuntu, this should work fine
|
|
32
|
+
Developed in Linux/Ubuntu, this should work fine on Linux machines. I cannot say
|
|
33
33
|
the same is true with Mac or Windows systems.
|
|
34
34
|
|
|
35
35
|
Add this line to your application's Gemfile:
|
|
@@ -50,69 +50,108 @@ Or install it yourself as:
|
|
|
50
50
|
~# gem install xkpassword
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
Installing the gem also installs the `xkpassword` executable.
|
|
54
|
+
|
|
53
55
|
## Usage
|
|
54
56
|
|
|
55
|
-
You can use this app
|
|
56
|
-
|
|
57
|
+
You can use this app as a Ruby gem in your application or as a command line utility.
|
|
58
|
+
For a fuller guide to presets and examples, see [doc/README.md](doc/README.md).
|
|
59
|
+
|
|
60
|
+
### Command Line
|
|
57
61
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
the
|
|
61
|
-
full list of options.
|
|
62
|
+
The command line app accepts the same generation options as `XKPassword.generate`.
|
|
63
|
+
If you installed the gem, use the executable directly. If you are working from a
|
|
64
|
+
checkout, run the executable through Bundler.
|
|
62
65
|
|
|
63
66
|
```bash
|
|
64
67
|
~# xkpassword
|
|
65
68
|
~# xkpassword --help
|
|
69
|
+
~# bundle exec exe/xkpassword
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
#### Global CLI config
|
|
73
|
+
|
|
74
|
+
The CLI reads optional defaults from `~/.xkpassword`. The file uses YAML, so it can
|
|
75
|
+
include comments while you experiment with different settings. Use `xkpassword --init`
|
|
76
|
+
to create a commented starter file.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
~# xkpassword --init
|
|
80
|
+
~# xkpassword
|
|
81
|
+
~# xkpassword --separator .
|
|
66
82
|
```
|
|
67
83
|
|
|
84
|
+
```yaml
|
|
85
|
+
# ~/.xkpassword
|
|
86
|
+
# preset: wifi
|
|
87
|
+
words: 5
|
|
88
|
+
min_length: 4
|
|
89
|
+
max_length: 8
|
|
90
|
+
separator: "-"
|
|
91
|
+
# case_transform: capitalize
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
CLI flags always override values from `~/.xkpassword`, so you can keep preferred defaults
|
|
95
|
+
in the file and still override them per command.
|
|
96
|
+
|
|
68
97
|
### Ruby Apps
|
|
69
98
|
|
|
99
|
+
Use the library API inside your Ruby application:
|
|
100
|
+
|
|
70
101
|
```ruby
|
|
71
102
|
require 'xkpassword'
|
|
72
103
|
|
|
73
104
|
options = {
|
|
105
|
+
words: 5,
|
|
106
|
+
min_length: 5,
|
|
74
107
|
max_length: 8,
|
|
75
|
-
|
|
108
|
+
separator: '.',
|
|
76
109
|
case_transform: :capitalize,
|
|
77
|
-
separator: '-',
|
|
78
|
-
words: 4,
|
|
79
110
|
}
|
|
80
111
|
|
|
81
112
|
XKPassword.generate(options)
|
|
82
113
|
```
|
|
83
114
|
|
|
84
|
-
|
|
85
|
-
|
|
115
|
+
You can still use presets when they fit your use case:
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
require 'xkpassword'
|
|
119
|
+
|
|
120
|
+
XKPassword.generate(preset: :security, separator: '.')
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
If you are generating multiple passwords at once, use a single generator instance so
|
|
124
|
+
the dictionary only needs to be loaded once:
|
|
86
125
|
|
|
87
126
|
```ruby
|
|
88
127
|
require 'xkpassword/generator'
|
|
89
128
|
|
|
90
129
|
options = {
|
|
91
|
-
|
|
130
|
+
words: 4,
|
|
92
131
|
min_length: 4,
|
|
93
|
-
|
|
132
|
+
max_length: 6,
|
|
94
133
|
separator: '-',
|
|
95
|
-
|
|
134
|
+
case_transform: :downcase,
|
|
96
135
|
}
|
|
97
|
-
|
|
136
|
+
|
|
98
137
|
generator = XKPassword::Generator.new
|
|
99
138
|
generator.generate(options)
|
|
100
139
|
|
|
101
140
|
# 10.times { generator.generate(options) }
|
|
102
141
|
```
|
|
103
142
|
|
|
143
|
+
`preset` supports `:xkcd` (default), `:web32`, `:wifi`, `:security`, and `:apple_id`.
|
|
144
|
+
You can still override any individual option in the selected preset.
|
|
145
|
+
|
|
146
|
+
The default `:xkcd` preset keeps the gem's original behavior of generating 4 words.
|
|
147
|
+
|
|
104
148
|
`case_transform` supports `:upcase`, `:downcase`, and `:capitalize`, and applies the
|
|
105
149
|
selected transform to every generated word in the password.
|
|
106
150
|
|
|
107
151
|
## The GEM
|
|
108
152
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
functional aspect of the app.
|
|
112
|
-
|
|
113
|
-
I am thinking of releasing gems only on functional updates (real code changes) that
|
|
114
|
-
happen with new features and bug fixes. So for now, there will be no 0.3.0v available
|
|
115
|
-
in Ruby GEMs and the latest would be 0.2.3.
|
|
153
|
+
Releases are intended for functional updates such as new features and bug fixes.
|
|
154
|
+
Check the Git tags or RubyGems release history for the latest published version.
|
|
116
155
|
|
|
117
156
|
## Development
|
|
118
157
|
|
|
@@ -124,16 +163,6 @@ a new version, update the version number in `version.rb`, and then run `bundle e
|
|
|
124
163
|
release`, which will create a git tag for the version, push git commits and tags, and push
|
|
125
164
|
the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
126
165
|
|
|
127
|
-
## TODO
|
|
128
|
-
|
|
129
|
-
Some of the things I am interested in doing in the near future.
|
|
130
|
-
|
|
131
|
-
- More tests, didn't have the time to write it all
|
|
132
|
-
- Local configuration file -> ex: `~/.xkpassword`
|
|
133
|
-
- Check for a better dictionary
|
|
134
|
-
- Ability to provide a dictionary (this should help languages other than English)
|
|
135
|
-
- A black-list - words that will not show up in the generation
|
|
136
|
-
|
|
137
166
|
## Contributing
|
|
138
167
|
|
|
139
168
|
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/xkpassword.
|
data/doc/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# XKPassword Docs
|
|
2
|
+
|
|
3
|
+
Additional documentation for this gem lives in this folder.
|
|
4
|
+
|
|
5
|
+
- [Preset guide](presets.md)
|
|
6
|
+
- [Project README](../README.md) for installation, Ruby gem usage, CLI usage, and `~/.xkpassword` defaults
|
|
7
|
+
- [GitHub repository](https://github.com/jdeen/xkpassword)
|
|
8
|
+
|
|
9
|
+
Use these docs for feature-specific details, then jump back to the main README or repository as needed.
|
data/doc/presets.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Presets
|
|
2
|
+
|
|
3
|
+
Back to the [docs index](README.md) or the [project README](../README.md).
|
|
4
|
+
|
|
5
|
+
`XKPassword.generate` and `XKPassword::Generator#generate` accept a `:preset` option.
|
|
6
|
+
If omitted, `:preset` defaults to `:xkcd`.
|
|
7
|
+
|
|
8
|
+
## Supported presets
|
|
9
|
+
|
|
10
|
+
| Preset | Defaults |
|
|
11
|
+
| --- | --- |
|
|
12
|
+
| `:xkcd` | 4 words, 4-8 letters, `-` separator, random per-word uppercasing |
|
|
13
|
+
| `:web32` | 4 words, 4-5 letters, `-` separator, random per-word uppercasing |
|
|
14
|
+
| `:wifi` | 6 words, 4-8 letters, `-` separator, random per-word uppercasing |
|
|
15
|
+
| `:security` | 6 words, 4-8 letters, space separator, lowercase words |
|
|
16
|
+
| `:apple_id` | 3 words, 4-7 letters, `-` separator, random per-word uppercasing |
|
|
17
|
+
|
|
18
|
+
## Examples
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
require 'xkpassword'
|
|
22
|
+
|
|
23
|
+
XKPassword.generate
|
|
24
|
+
XKPassword.generate(preset: :wifi)
|
|
25
|
+
XKPassword.generate(preset: :security, separator: '.')
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
You can pass a preset as a symbol or string. String values like `"apple_id"` and `"apple-id"`
|
|
29
|
+
are normalized to the matching preset.
|
|
30
|
+
|
|
31
|
+
Explicit options always win over the preset defaults, so this is valid:
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
XKPassword.generate(preset: :xkcd, words: 6, case_transform: :capitalize)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For the full source and latest updates, see the [GitHub repository](https://github.com/jdeen/xkpassword).
|
data/exe/xkpassword
CHANGED
|
@@ -1,48 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
|
-
require '
|
|
4
|
-
require 'artii'
|
|
5
|
-
|
|
6
|
-
require 'bundler/setup'
|
|
7
|
-
require 'xkpassword'
|
|
8
|
-
|
|
9
|
-
artii = Artii::Base.new font: 'standard'
|
|
10
|
-
message = """
|
|
11
|
-
#{ artii.asciify('XKPassword') }
|
|
12
|
-
by Ziyan Junaideen
|
|
13
|
-
|
|
14
|
-
How many times have you changed your password just because you forgot it?
|
|
15
|
-
Well, you are not alone. In todays security requirements, passwords need
|
|
16
|
-
to be secure and difficult to break. Passwords need to be secure, sure,
|
|
17
|
-
but they can also be easy to remember. Follow up this XKCD article for more
|
|
18
|
-
information - http://xkcd.com/936/
|
|
19
|
-
|
|
20
|
-
This does exactly what the picture predicts. You can use this in your Ruby
|
|
21
|
-
applications (Ex: Rails, Sinatra) or standalone if you install the gem (as
|
|
22
|
-
you have done here).
|
|
23
|
-
|
|
24
|
-
Wish you all the best keeping things secure.
|
|
25
|
-
|
|
26
|
-
Ziyan Junaideen
|
|
27
|
-
ziyan@jdeen.com
|
|
28
|
-
|
|
29
|
-
"""
|
|
30
|
-
|
|
31
|
-
options = {}
|
|
32
|
-
OptionParser.new do |opts|
|
|
33
|
-
opts.banner = "Usage: ./exe/xkpassword [options]"
|
|
34
|
-
|
|
35
|
-
opts.on('-v', '--version', 'Gem version') { options[:version] = true }
|
|
36
|
-
opts.on('-i', '--info', 'Gem info') { options[:info] = true }
|
|
37
|
-
|
|
38
|
-
opts.on('--words [INTEGER]', 'Number of wrods to be used in the generated password') { |words| options[:words] = words.to_i }
|
|
39
|
-
opts.on('--min-length [INTEGER]', 'Minimum length of a word') { |min| options[:min_length] = min.to_i }
|
|
40
|
-
opts.on('--max-length [INTEGER]', 'Maximum length of a word') { |max| options[:max_length] = max.to_i }
|
|
41
|
-
opts.on('--separator [STRING]', 'The separator to separate password') { |separator| options[:separator] = separator }
|
|
42
|
-
opts.on('--case-transform [STRING]', 'Transform each word using upcase, downcase, or capitalize') { |case_transform| options[:case_transform] = case_transform }
|
|
43
|
-
end.parse!
|
|
44
|
-
|
|
45
|
-
puts message if options[:info]
|
|
46
|
-
puts XKPassword::VERSION if options[:version]
|
|
47
|
-
puts XKPassword.generate(options) if !options[:info] && !options[:version]
|
|
4
|
+
require 'xkpassword/cli'
|
|
48
5
|
|
|
6
|
+
exit XKPassword::CLI.new.run
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'optparse'
|
|
4
|
+
require 'artii'
|
|
5
|
+
|
|
6
|
+
require 'xkpassword'
|
|
7
|
+
require 'xkpassword/config_file'
|
|
8
|
+
|
|
9
|
+
module XKPassword
|
|
10
|
+
# Command-line entrypoint for the xkpassword executable.
|
|
11
|
+
class CLI
|
|
12
|
+
INFO_MESSAGE = <<~TEXT.freeze
|
|
13
|
+
#{Artii::Base.new(font: 'standard').asciify('XKPassword')}
|
|
14
|
+
by Ziyan Junaideen
|
|
15
|
+
|
|
16
|
+
How many times have you changed your password just because you forgot it?
|
|
17
|
+
Well, you are not alone. In todays security requirements, passwords need
|
|
18
|
+
to be secure and difficult to break. Passwords need to be secure, sure,
|
|
19
|
+
but they can also be easy to remember. Follow up this XKCD article for more
|
|
20
|
+
information - http://xkcd.com/936/
|
|
21
|
+
|
|
22
|
+
This does exactly what the picture predicts. You can use this in your Ruby
|
|
23
|
+
applications (Ex: Rails, Sinatra) or standalone if you install the gem (as
|
|
24
|
+
you have done here).
|
|
25
|
+
|
|
26
|
+
Wish you all the best keeping things secure.
|
|
27
|
+
|
|
28
|
+
Ziyan Junaideen
|
|
29
|
+
ziyan@jdeen.com
|
|
30
|
+
TEXT
|
|
31
|
+
|
|
32
|
+
def initialize(argv = ARGV, stdout: $stdout, stderr: $stderr, env: ENV)
|
|
33
|
+
@argv = argv.dup
|
|
34
|
+
@stdout = stdout
|
|
35
|
+
@stderr = stderr
|
|
36
|
+
@config_file = XKPassword::ConfigFile.new(XKPassword::ConfigFile.default_path(env))
|
|
37
|
+
@options = {}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def run
|
|
41
|
+
option_parser.parse!(@argv)
|
|
42
|
+
|
|
43
|
+
return initialize_config if @options[:init]
|
|
44
|
+
return write_special_output if special_output?
|
|
45
|
+
|
|
46
|
+
options = @config_file.load.merge(@options)
|
|
47
|
+
write_line(XKPassword.generate(options))
|
|
48
|
+
rescue OptionParser::ParseError, XKPassword::ConfigFile::Error, ArgumentError => e
|
|
49
|
+
@stderr.puts(e.message)
|
|
50
|
+
1
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def option_parser
|
|
56
|
+
@option_parser ||= OptionParser.new do |opts|
|
|
57
|
+
opts.banner = 'Usage: xkpassword [options]'
|
|
58
|
+
|
|
59
|
+
add_command_options(opts)
|
|
60
|
+
add_generation_options(opts)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def add_command_options(opts)
|
|
65
|
+
opts.on('-v', '--version', 'Gem version') { @options[:version] = true }
|
|
66
|
+
opts.on('-i', '--info', 'Gem info') { @options[:info] = true }
|
|
67
|
+
opts.on('--init', "Create #{@config_file.path} with commented defaults") { @options[:init] = true }
|
|
68
|
+
opts.on('-h', '--help', 'Show help') { @options[:help] = true }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def add_generation_options(opts)
|
|
72
|
+
add_numeric_generation_options(opts)
|
|
73
|
+
add_text_generation_options(opts)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def add_numeric_generation_options(opts)
|
|
77
|
+
opts.on(
|
|
78
|
+
'--words [INTEGER]',
|
|
79
|
+
'Number of words to use in the generated password'
|
|
80
|
+
) { |words| @options[:words] = words.to_i }
|
|
81
|
+
opts.on('--min-length [INTEGER]', 'Minimum length of a word') { |min| @options[:min_length] = min.to_i }
|
|
82
|
+
opts.on('--max-length [INTEGER]', 'Maximum length of a word') { |max| @options[:max_length] = max.to_i }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def add_text_generation_options(opts)
|
|
86
|
+
opts.on(
|
|
87
|
+
'--separator [STRING]',
|
|
88
|
+
'The separator between generated words'
|
|
89
|
+
) { |separator| @options[:separator] = separator }
|
|
90
|
+
opts.on(
|
|
91
|
+
'--case-transform [STRING]',
|
|
92
|
+
'Transform each word using upcase, downcase, or capitalize'
|
|
93
|
+
) { |case_transform| @options[:case_transform] = case_transform }
|
|
94
|
+
add_preset_option(opts)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def add_preset_option(opts)
|
|
98
|
+
opts.on(
|
|
99
|
+
'--preset [STRING]',
|
|
100
|
+
'Preset to use: xkcd, web32, wifi, security, or apple_id'
|
|
101
|
+
) { |preset| @options[:preset] = preset }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def special_output?
|
|
105
|
+
@options[:help] || @options[:info] || @options[:version]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def write_special_output
|
|
109
|
+
write_line(option_parser) if @options[:help]
|
|
110
|
+
write_line(INFO_MESSAGE) if @options[:info]
|
|
111
|
+
write_line(XKPassword::VERSION) if @options[:version]
|
|
112
|
+
0
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def initialize_config
|
|
116
|
+
path = @config_file.init!
|
|
117
|
+
write_line("Created #{path}")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def write_line(message)
|
|
121
|
+
@stdout.puts(message)
|
|
122
|
+
0
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'psych'
|
|
4
|
+
|
|
5
|
+
module XKPassword
|
|
6
|
+
# Loads and initializes the CLI config file stored at ~/.xkpassword.
|
|
7
|
+
class ConfigFile
|
|
8
|
+
Error = Class.new(StandardError)
|
|
9
|
+
|
|
10
|
+
ALLOWED_OPTIONS = %i[
|
|
11
|
+
preset
|
|
12
|
+
words
|
|
13
|
+
min_length
|
|
14
|
+
max_length
|
|
15
|
+
separator
|
|
16
|
+
case_transform
|
|
17
|
+
].freeze
|
|
18
|
+
INTEGER_OPTIONS = %i[words min_length max_length].freeze
|
|
19
|
+
TEMPLATE = <<~YAML
|
|
20
|
+
# Global defaults for the xkpassword CLI.
|
|
21
|
+
# Remove the leading "#" from any setting you want to enable.
|
|
22
|
+
#
|
|
23
|
+
# Supported keys:
|
|
24
|
+
# preset: xkcd, web32, wifi, security, apple_id
|
|
25
|
+
# words: integer
|
|
26
|
+
# min_length: integer
|
|
27
|
+
# max_length: integer
|
|
28
|
+
# separator: string
|
|
29
|
+
# case_transform: upcase, downcase, capitalize
|
|
30
|
+
#
|
|
31
|
+
# Example:
|
|
32
|
+
# preset: wifi
|
|
33
|
+
# words: 6
|
|
34
|
+
# min_length: 4
|
|
35
|
+
# max_length: 8
|
|
36
|
+
# separator: "-"
|
|
37
|
+
# case_transform: capitalize
|
|
38
|
+
YAML
|
|
39
|
+
|
|
40
|
+
attr_reader :path
|
|
41
|
+
|
|
42
|
+
def initialize(path = self.class.default_path)
|
|
43
|
+
@path = path
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.default_path(env = ENV)
|
|
47
|
+
home = env['HOME'] || Dir.home
|
|
48
|
+
File.expand_path('.xkpassword', home)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def load
|
|
52
|
+
return {} unless File.exist?(path)
|
|
53
|
+
|
|
54
|
+
data = Psych.safe_load(File.read(path), aliases: false)
|
|
55
|
+
return {} if data.nil?
|
|
56
|
+
raise Error, "#{path} must contain a YAML mapping of options" unless data.is_a?(Hash)
|
|
57
|
+
|
|
58
|
+
normalize_options(data)
|
|
59
|
+
rescue Psych::SyntaxError => e
|
|
60
|
+
raise Error, "Could not parse #{path}: #{e.message}"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def init!
|
|
64
|
+
raise Error, "#{path} already exists" if File.exist?(path)
|
|
65
|
+
|
|
66
|
+
File.write(path, TEMPLATE)
|
|
67
|
+
path
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def normalize_options(data)
|
|
73
|
+
data.each_with_object({}) do |(key, value), normalized|
|
|
74
|
+
normalized_key = normalize_key(key)
|
|
75
|
+
|
|
76
|
+
unless ALLOWED_OPTIONS.include?(normalized_key)
|
|
77
|
+
raise Error, "Unsupported config option #{key.inspect} in #{path}"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
normalized[normalized_key] = normalize_value(normalized_key, value)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def normalize_key(key)
|
|
85
|
+
key.to_s.strip.downcase.tr(' -', '_').to_sym
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def normalize_value(key, value)
|
|
89
|
+
return normalize_integer(key, value) if INTEGER_OPTIONS.include?(key)
|
|
90
|
+
|
|
91
|
+
return value if value.nil? || value.is_a?(String) || value.is_a?(Symbol)
|
|
92
|
+
|
|
93
|
+
raise Error, "#{key} in #{path} should be a string or left unset"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def normalize_integer(key, value)
|
|
97
|
+
integer_value = value.is_a?(String) ? Integer(value, 10) : value
|
|
98
|
+
return integer_value if integer_value.is_a?(Integer)
|
|
99
|
+
|
|
100
|
+
raise Error, "#{key} in #{path} should be an integer"
|
|
101
|
+
rescue ArgumentError, TypeError
|
|
102
|
+
raise Error, "#{key} in #{path} should be an integer"
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
data/lib/xkpassword/generator.rb
CHANGED
|
@@ -2,16 +2,60 @@ require 'xkpassword/words'
|
|
|
2
2
|
|
|
3
3
|
# The Generator class which finds words based on the requirement and using the provided options to build a
|
|
4
4
|
# new random passowrd.
|
|
5
|
+
# Presets provide convenient defaults for common password styles, and callers can override any
|
|
6
|
+
# preset-derived value by passing explicit generation options.
|
|
5
7
|
#
|
|
6
8
|
# @attr_reader [XKPassword::Words] words A word database that gen provide you words for the length required
|
|
7
9
|
class XKPassword::Generator
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
# The preset used when `:preset` is omitted.
|
|
11
|
+
DEFAULT_PRESET = :xkcd
|
|
12
|
+
|
|
13
|
+
# Built-in password presets.
|
|
14
|
+
#
|
|
15
|
+
# - `:xkcd` generates 4 words between 4 and 8 letters separated by `-`, preserving the gem's
|
|
16
|
+
# original default behavior.
|
|
17
|
+
# - `:web32` generates 4 shorter words between 4 and 5 letters separated by `-`.
|
|
18
|
+
# - `:wifi` generates 6 words between 4 and 8 letters separated by `-`.
|
|
19
|
+
# - `:security` generates 6 lowercase words between 4 and 8 letters separated by spaces.
|
|
20
|
+
# - `:apple_id` generates 3 words between 4 and 7 letters separated by `-`.
|
|
21
|
+
PRESETS = {
|
|
22
|
+
xkcd: {
|
|
23
|
+
case_transform: nil,
|
|
24
|
+
max_length: 8,
|
|
25
|
+
min_length: 4,
|
|
26
|
+
separator: '-',
|
|
27
|
+
words: 4,
|
|
28
|
+
},
|
|
29
|
+
web32: {
|
|
30
|
+
case_transform: nil,
|
|
31
|
+
max_length: 5,
|
|
32
|
+
min_length: 4,
|
|
33
|
+
separator: '-',
|
|
34
|
+
words: 4,
|
|
35
|
+
},
|
|
36
|
+
wifi: {
|
|
37
|
+
case_transform: nil,
|
|
38
|
+
max_length: 8,
|
|
39
|
+
min_length: 4,
|
|
40
|
+
separator: '-',
|
|
41
|
+
words: 6,
|
|
42
|
+
},
|
|
43
|
+
security: {
|
|
44
|
+
case_transform: :downcase,
|
|
45
|
+
max_length: 8,
|
|
46
|
+
min_length: 4,
|
|
47
|
+
separator: ' ',
|
|
48
|
+
words: 6,
|
|
49
|
+
},
|
|
50
|
+
apple_id: {
|
|
51
|
+
case_transform: nil,
|
|
52
|
+
max_length: 7,
|
|
53
|
+
min_length: 4,
|
|
54
|
+
separator: '-',
|
|
55
|
+
words: 3,
|
|
56
|
+
},
|
|
14
57
|
}
|
|
58
|
+
VALID_CASE_TRANSFORMS = [:upcase, :downcase, :capitalize]
|
|
15
59
|
|
|
16
60
|
attr_reader :words
|
|
17
61
|
|
|
@@ -20,8 +64,14 @@ class XKPassword::Generator
|
|
|
20
64
|
end
|
|
21
65
|
|
|
22
66
|
# Generates a password absed on the configuration provided.
|
|
67
|
+
# A preset supplies a base configuration and any explicit options passed in `options` override
|
|
68
|
+
# those preset defaults.
|
|
23
69
|
#
|
|
24
70
|
# @param [Hash] options The options to populate a generator
|
|
71
|
+
# @option options [String, Symbol] :preset The preset to use. Supports `:xkcd`, `:web32`,
|
|
72
|
+
# `:wifi`, `:security`, and `:apple_id`.
|
|
73
|
+
# String values like `"apple_id"` and `"apple-id"`
|
|
74
|
+
# are normalized to `:apple_id`. Defaults to `:xkcd`.
|
|
25
75
|
# @option options [Integer] :words The number of words to include in the generated password
|
|
26
76
|
# @option options [String] :separator The separator symbol to use joining words used in password
|
|
27
77
|
# @option options [Integer] :min_length The minimum length of a word to be used in the process
|
|
@@ -30,20 +80,37 @@ class XKPassword::Generator
|
|
|
30
80
|
#
|
|
31
81
|
# @return [String] The generated password
|
|
32
82
|
#
|
|
33
|
-
# @example
|
|
83
|
+
# @example Using the default xkcd preset
|
|
84
|
+
# generator = XKPassword::Generator.new
|
|
85
|
+
# generator.generate
|
|
86
|
+
#
|
|
87
|
+
# @example Using the security preset
|
|
88
|
+
# generator = XKPassword::Generator.new
|
|
89
|
+
# generator.generate(preset: :security)
|
|
90
|
+
#
|
|
91
|
+
# @example Populating the method with a preset and explicit overrides
|
|
34
92
|
# options = {
|
|
93
|
+
# preset: :security,
|
|
35
94
|
# separator: ' ',
|
|
36
|
-
# words:
|
|
95
|
+
# words: 6,
|
|
37
96
|
# min_length: 4,
|
|
38
97
|
# max_length: 8,
|
|
39
|
-
# case_transform: :
|
|
98
|
+
# case_transform: :downcase
|
|
40
99
|
# }
|
|
41
100
|
#
|
|
42
101
|
# generator = XKPassword::Generator.new
|
|
43
102
|
# generator.generate(options)
|
|
103
|
+
#
|
|
104
|
+
# @example Built-in preset defaults
|
|
105
|
+
# :xkcd # 4 words, 4..8 letters, "-" separator, random per-word uppercasing
|
|
106
|
+
# :web32 # 4 words, 4..5 letters, "-" separator, random per-word uppercasing
|
|
107
|
+
# :wifi # 6 words, 4..8 letters, "-" separator, random per-word uppercasing
|
|
108
|
+
# :security # 6 words, 4..8 letters, " " separator, lowercase
|
|
109
|
+
# :apple_id # 3 words, 4..7 letters, "-" separator, random per-word uppercasing
|
|
44
110
|
def generate(options = nil)
|
|
45
|
-
options
|
|
46
|
-
|
|
111
|
+
options = (options || {}).dup
|
|
112
|
+
preset = normalize_preset(options.delete(:preset))
|
|
113
|
+
options = PRESETS.fetch(preset).merge(options)
|
|
47
114
|
case_transform = normalize_case_transform(options[:case_transform])
|
|
48
115
|
length_vals = (options[:min_length]..options[:max_length]).to_a
|
|
49
116
|
|
|
@@ -73,11 +140,20 @@ class XKPassword::Generator
|
|
|
73
140
|
return nil if case_transform.nil?
|
|
74
141
|
|
|
75
142
|
case_transform = case_transform.to_sym if case_transform.is_a?(String)
|
|
76
|
-
valid_case_transforms = [:upcase, :downcase, :capitalize]
|
|
77
143
|
|
|
78
|
-
return case_transform if
|
|
144
|
+
return case_transform if VALID_CASE_TRANSFORMS.include?(case_transform)
|
|
145
|
+
|
|
146
|
+
fail ArgumentError, "case_transform should be one of: #{ VALID_CASE_TRANSFORMS.join(', ') }"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def normalize_preset(preset)
|
|
150
|
+
return DEFAULT_PRESET if preset.nil?
|
|
151
|
+
|
|
152
|
+
preset = preset.strip.downcase.tr(' -', '_').to_sym if preset.is_a?(String)
|
|
153
|
+
|
|
154
|
+
return preset if PRESETS.key?(preset)
|
|
79
155
|
|
|
80
|
-
fail ArgumentError, "
|
|
156
|
+
fail ArgumentError, "preset should be one of: #{ PRESETS.keys.join(', ') }"
|
|
81
157
|
end
|
|
82
158
|
|
|
83
159
|
end
|
data/lib/xkpassword/version.rb
CHANGED
data/lib/xkpassword.rb
CHANGED
|
@@ -7,12 +7,29 @@ module XKPassword
|
|
|
7
7
|
# use the `XKPassword::Generator` class as it will be faster since it will only need to load
|
|
8
8
|
# the dictionary once.
|
|
9
9
|
#
|
|
10
|
+
# Presets provide named defaults for common password styles, and any explicit options passed to
|
|
11
|
+
# this method override the selected preset.
|
|
12
|
+
#
|
|
10
13
|
# @param [Hash] options The options to populate a generator
|
|
14
|
+
# @option options [String, Symbol] :preset The preset to use. Supports `:xkcd`, `:web32`,
|
|
15
|
+
# `:wifi`, `:security`, and `:apple_id`.
|
|
16
|
+
# Defaults to `:xkcd`.
|
|
17
|
+
# - `:xkcd` uses 4 words of 4..8 letters joined by `-`
|
|
18
|
+
# - `:web32` uses 4 words of 4..5 letters joined by `-`
|
|
19
|
+
# - `:wifi` uses 6 words of 4..8 letters joined by `-`
|
|
20
|
+
# - `:security` uses 6 lowercase words of 4..8 letters joined by spaces
|
|
21
|
+
# - `:apple_id` uses 3 words of 4..7 letters joined by `-`
|
|
11
22
|
# @option options [Integer] :words The number of words to include in the generated password
|
|
12
23
|
# @option options [String] :separator The separator symbol to use joining words used in password
|
|
13
24
|
# @option options [Integer] :min_length The minimum length of a word to be used in the process
|
|
14
25
|
# @option options [Integer] :max_length The maximum length of a word to be used in the process
|
|
15
26
|
# @option options [String, Symbol] :case_transform The transform to apply to every generated word
|
|
27
|
+
#
|
|
28
|
+
# @example Generate with the default preset
|
|
29
|
+
# XKPassword.generate
|
|
30
|
+
#
|
|
31
|
+
# @example Generate with a preset and override one of its defaults
|
|
32
|
+
# XKPassword.generate(preset: :apple_id, separator: '.')
|
|
16
33
|
def self.generate(options = nil)
|
|
17
34
|
generator = XKPassword::Generator.new
|
|
18
35
|
generator.generate(options)
|
|
@@ -22,4 +39,3 @@ end
|
|
|
22
39
|
|
|
23
40
|
require 'xkpassword/version'
|
|
24
41
|
require 'xkpassword/generator'
|
|
25
|
-
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: xkpassword
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ziyan Junaideen
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: artii
|
|
@@ -90,8 +90,12 @@ files:
|
|
|
90
90
|
- bin/console
|
|
91
91
|
- bin/setup
|
|
92
92
|
- bin/xkpassword
|
|
93
|
+
- doc/README.md
|
|
94
|
+
- doc/presets.md
|
|
93
95
|
- exe/xkpassword
|
|
94
96
|
- lib/xkpassword.rb
|
|
97
|
+
- lib/xkpassword/cli.rb
|
|
98
|
+
- lib/xkpassword/config_file.rb
|
|
95
99
|
- lib/xkpassword/data/google-10000-english-no-swears.txt
|
|
96
100
|
- lib/xkpassword/generator.rb
|
|
97
101
|
- lib/xkpassword/store.rb
|