Commit 8a6f9a1
Changed files (9)
bin
lib
bin/tfa
@@ -1,4 +1,4 @@
#!/usr/bin/env ruby
require 'tfa'
-puts TFA::Console.new.run(ARGV)
+TFA::CLI.start(ARGV)
lib/tfa/cli.rb
@@ -0,0 +1,29 @@
+require "thor"
+
+module TFA
+ class CLI < Thor
+ package_name "TFA"
+ class_option :filename
+
+ desc "add NAME SECRET", "add a new secret to the database"
+ def add(name, secret)
+ AddCommand.new(storage).run([name, secret])
+ end
+
+ desc "show NAME", "shows the secret for the given key"
+ def show(name)
+ ShowCommand.new(storage).run([name])
+ end
+
+ desc "totp NAME", "generate a Time based One Time Password"
+ def totp(name)
+ TotpCommand.new(storage).run([name])
+ end
+
+ private
+
+ def storage
+ @storage ||= Storage.new(filename: options[:filename] || 'tfa')
+ end
+ end
+end
lib/tfa/console.rb
@@ -1,26 +0,0 @@
-module TFA
- class Console
- def initialize(filename = "tfa")
- @storage = Storage.new(filename)
- end
-
- def run(arguments)
- command_name = arguments.first
- command_for(command_name).run(arguments - [command_name])
- end
-
- private
-
- def command_for(command_name)
- registry[command_name].call
- end
-
- def registry
- Hash.new { |x, y| lambda { UsageCommand.new(@storage) } }.tap do |commands|
- commands['add'] = lambda { AddCommand.new(@storage) }
- commands['show'] = lambda { ShowCommand.new(@storage) }
- commands['totp'] = lambda { TotpCommand.new(@storage) }
- end
- end
- end
-end
lib/tfa/storage.rb
@@ -1,6 +1,6 @@
module TFA
class Storage
- def initialize(filename)
+ def initialize(filename:)
@storage = PStore.new(File.join(Dir.home, ".#{filename}.pstore"))
end
lib/tfa.rb
@@ -5,5 +5,5 @@ require "tfa/add_command"
require "tfa/show_command"
require "tfa/totp_command"
require "tfa/usage_command"
-require "tfa/console"
+require "tfa/cli"
require "tfa/storage"
spec/lib/console_spec.rb
@@ -1,26 +1,20 @@
module TFA
- describe Console do
- subject { Console.new('testing') }
+ describe CLI do
+ subject { CLI.new }
let(:secret) { ::ROTP::Base32.random_base32 }
describe "#run" do
context "when adding a key" do
it "saves a new secret" do
- subject.run(["add", "development", secret])
- expect(subject.run(["show", "development"])).to eql(secret)
+ subject.add("development", secret)
+ expect(subject.show("development")).to eql(secret)
end
end
context "when getting a one time password" do
it "creates a totp for a certain key" do
- subject.run(["add", "development", secret])
- expect(subject.run(["totp", "development"])).to_not be_nil
- end
- end
-
- context "when running an unknown command" do
- it "returns the usage" do
- expect(subject.run([])).to_not be_nil
+ subject.add("development", secret)
+ expect(subject.totp("development")).to_not be_nil
end
end
end
spec/lib/show_command_spec.rb
@@ -1,7 +1,7 @@
module TFA
describe ShowCommand do
subject { ShowCommand.new(storage) }
- let(:storage) { Storage.new(SecureRandom.uuid) }
+ let(:storage) { Storage.new(filename: SecureRandom.uuid) }
describe "#run" do
context "when looking up the secret for a specific key" do
spec/lib/totp_command_spec.rb
@@ -1,8 +1,7 @@
module TFA
describe TotpCommand do
subject { TotpCommand.new(storage) }
- let(:storage) { Storage.new(Tempfile.new('test').path) }
- let(:storage) { Storage.new(SecureRandom.uuid) }
+ let(:storage) { Storage.new(filename: SecureRandom.uuid) }
def code_for(secret)
::ROTP::TOTP.new(secret).now
tfa.gemspec
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
spec.require_paths = ["lib"]
spec.add_dependency "rotp"
+ spec.add_dependency "thor"
spec.add_development_dependency "bundler", "~> 1.6"
spec.add_development_dependency "rake"
spec.add_development_dependency "rspec"