diff --git a/bin/twik b/bin/twik new file mode 100755 index 0000000..b8da6ad --- /dev/null +++ b/bin/twik @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby + +require 'twik/cli' + +Twik::Cli.new(ARGV).run diff --git a/lib/twik/cli.rb b/lib/twik/cli.rb new file mode 100644 index 0000000..9ff4b50 --- /dev/null +++ b/lib/twik/cli.rb @@ -0,0 +1,30 @@ +require 'twik' +require 'twik/cli/config' +require 'twik/cli/options' + +class Twik + class Cli + attr_reader :config + + def initialize(args) + @config = Config.new(args) + end + + def twik + @twik ||= Twik.new(config.privatekey, length: config.length, type: config.type.to_sym) + end + + def run + masterkey = ask('Master key: ') + puts twik.generate(config.tag, masterkey) + end + + def ask(prompt) + print prompt + res = STDIN.noecho(&:gets).chomp + puts + + res + end + end +end diff --git a/lib/twik/cli/config.rb b/lib/twik/cli/config.rb new file mode 100644 index 0000000..f542d40 --- /dev/null +++ b/lib/twik/cli/config.rb @@ -0,0 +1,64 @@ +require 'twik/private_key' + +require 'xdg' +require 'yaml' + +class Twik + class Cli + class Config + CONFIG_FILE = [XDG['CONFIG_HOME'], 'twik', 'config.yml'].join(File::SEPARATOR) + + def self.default + { + 'profiles' => { + 'default' => { + 'privatekey' => Twik::PrivateKey.generate, + 'length' => 16, + 'type' => 'alphanumeric_and_special_chars', + } + } + } + end + + attr_reader :yaml, :args + + def initialize(args) + file = File.new(CONFIG_FILE) + @args = Options.parse(args) + @yaml = YAML.load_file(file.path) + end + + def profile + args['profile'] || 'default' + end + + def options + @options ||= yaml['profiles'][profile].merge(args) + end + + def method_missing(symbol) + options.key?(symbol.to_s) ? options[symbol.to_s] : super + end + + class File + attr_reader :path + + def initialize(path) + @path = path + create unless ::File.exist?(path) + end + + private + + def create + directory = ::File.dirname(path) + ::FileUtils.mkdir_p directory unless ::File.directory?(directory) + + ::File.open(path, 'w') do |file| + file.write(YAML.dump(Twik::Cli::Config.default)) + end + end + end + end + end +end diff --git a/lib/twik/cli/options.rb b/lib/twik/cli/options.rb new file mode 100644 index 0000000..93402ba --- /dev/null +++ b/lib/twik/cli/options.rb @@ -0,0 +1,56 @@ +require 'optparse' + +class Twik + class Cli + class Options + def self.parse(args) + options = {} + + parser = ::OptionParser.new do |opts| + opts.banner = 'Usage: twik [options] tag' + + opts.separator '' + opts.separator 'Specific options:' + + opts.on('-l', '--length LENGTH', 'length of generated password (4-26). Default: 16') do |length| + options['length'] = length.to_i + end + + opts.on('-p', '--profile PROFILE', "profile to use. Default: 'default'") do |profile| + options['profile'] = profile + end + + opts.on('-t', '--type TYPE', Twik::TYPE, + "type of password:", " #{Twik::TYPE.join(', ')}", " Default: alphanumeric_and_special_chars") do |type| + options['type'] = type.to_sym + end + + opts.separator '' + opts.separator 'Common options:' + + opts.on_tail('-h', '--help', 'Show this message') do + puts opts + exit + end + + opts.on_tail('--version', 'Show version') do + puts "#{opts.program_name} #{Immoconv::VERSION}" + exit + end + end + + begin + parser.parse!(args) + rescue OptionParser::MissingArgument, OptionParser::InvalidOption => e + puts e.message + puts parser + exit + end + + options['tag'] = args.shift + + options + end + end + end +end diff --git a/lib/twik/private_key.rb b/lib/twik/private_key.rb new file mode 100644 index 0000000..38c4846 --- /dev/null +++ b/lib/twik/private_key.rb @@ -0,0 +1,15 @@ +class Twik + class PrivateKey + def self.generate + format = [8, 4, 4, 4, 12] + separator = '-' + chars = ('A'..'Z').to_a + ('0'..'9').to_a + + format.map do |i| + i.times.inject('') do |res, j| + res + chars[Random.rand(chars.length)] + end + end.join(separator) + end + end +end diff --git a/twik.gemspec b/twik.gemspec index 5c9567e..4d17eea 100644 --- a/twik.gemspec +++ b/twik.gemspec @@ -18,6 +18,8 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] + spec.add_dependency 'xdg' + spec.add_development_dependency "bundler", "~> 1.7" spec.add_development_dependency "rake", "~> 10.0"