Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 50 additions & 14 deletions lib/rotp/cli.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'rotp/arguments'
require 'rotp/base32'

module ROTP
class CLI
Expand All @@ -16,25 +17,22 @@ def run
# :nocov:

def errors
if %i[time hmac].include?(options.mode)
if options.secret.to_s == ''
red 'You must also specify a --secret. Try --help for help.'
elsif options.secret.to_s.chars.any? { |c| ROTP::Base32::CHARS.index(c.upcase).nil? }
red 'Secret must be in RFC4648 Base32 format - http://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet'
if requires_secret? && blank_secret?
return red 'You must also specify a --secret. Try --help for help.'
end

if secret_provided?
if invalid_secret?
return red 'Secret must be in RFC4648 Base32 format - http://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet'
end
if options.secret.tr('=', '').length < 32
return red 'Secret must be at least 160 bits (32 characters in Base32)'
end
end
end

def output
return options.warnings if options.warnings
return errors if errors
return arguments.to_s if options.mode == :help

if options.mode == :time
ROTP::TOTP.new(options.secret, options.to_h).now
elsif options.mode == :hmac
ROTP::HOTP.new(options.secret, options.to_h).at options.counter
end
errors || options.warnings || (help_message if options.mode == :help) || otp_value
end

def arguments
Expand All @@ -48,5 +46,43 @@ def options
def red(string)
"\033[31m#{string}\033[0m"
end

private

def help_message
arguments.to_s
end

def otp_value
case options.mode
when :time
ROTP::TOTP.new(options.secret, options.to_h).now
when :hmac
ROTP::HOTP.new(options.secret, options.to_h).at(options.counter)
end
end

def requires_secret?
%i[time hmac].include?(options.mode)
end

def secret_provided?
!options.secret.to_s.empty?
end

def blank_secret?
options.secret.to_s.empty?
end

def invalid_secret?
decoded_secret
false
rescue ROTP::Base32::Base32Error
true
end

def decoded_secret
@decoded_secret ||= ROTP::Base32.decode(options.secret)
end
end
end
Loading