to_pass 0.2.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +43 -11
- data/TODO +21 -2
- data/VERSION +1 -1
- data/lib/to_pass/algorithm_reader.rb +11 -2
- data/lib/{algorithms → to_pass/algorithms}/basic_de.yml +5 -5
- data/lib/{algorithms → to_pass/algorithms}/basic_en.yml +5 -5
- data/lib/to_pass/cli.rb +12 -4
- data/lib/to_pass/converter.rb +27 -10
- data/lib/to_pass/converter_reader.rb +107 -0
- data/lib/to_pass/converters/collapse_chars.rb +12 -0
- data/lib/to_pass/converters/first_chars.rb +12 -0
- data/lib/to_pass/converters/replace.rb +12 -0
- data/lib/to_pass/converters/swapcase.rb +25 -0
- data/lib/to_pass/converters.rb +14 -0
- data/lib/to_pass.rb +2 -1
- data/test/fixtures/user_alg.yml +5 -5
- data/test/fixtures/user_converter.rb +17 -0
- data/test/helper.rb +7 -1
- data/test/test_algorithm_reader.rb +6 -1
- data/test/test_cli.rb +12 -0
- data/test/test_converter.rb +38 -9
- data/test/test_converter_reader.rb +64 -0
- data/test/test_converters.rb +10 -0
- metadata +18 -9
- data/lib/to_pass/string_conversions.rb +0 -54
- data/test/test_string_conversions.rb +0 -40
data/Rakefile
CHANGED
@@ -24,20 +24,24 @@ rescue LoadError
|
|
24
24
|
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
27
|
+
# documentation task
|
28
|
+
begin
|
29
|
+
%w[ rake/rdoctask sdoc ].each { |lib| require lib }
|
30
|
+
Rake::RDocTask.new do |rdoc|
|
31
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
32
|
+
|
33
|
+
rdoc.rdoc_dir = 'doc/rdoc'
|
34
|
+
rdoc.title = "to_pass #{version}"
|
35
|
+
rdoc.options << '--fmt' << 'shtml'
|
36
|
+
rdoc.template = 'direct'
|
37
|
+
rdoc.rdoc_files.include('README*')
|
38
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
39
|
+
end
|
40
|
+
rescue LoadError
|
37
41
|
end
|
38
42
|
|
39
43
|
desc "run tests"
|
40
|
-
task :test
|
44
|
+
task :test do
|
41
45
|
# optional libraries
|
42
46
|
%w[ redgreen ].each do |lib|
|
43
47
|
begin
|
@@ -50,4 +54,32 @@ task :test, :needs => [:check_dependencies] do
|
|
50
54
|
end
|
51
55
|
end
|
52
56
|
|
57
|
+
desc "list available algorithms"
|
58
|
+
task :algorithms, :needs => [:to_pass] do
|
59
|
+
puts ""
|
60
|
+
puts " available password algorithms"
|
61
|
+
puts " ============================================"
|
62
|
+
ToPass::AlgorithmReader.discover.each do |algorithm|
|
63
|
+
puts " - #{algorithm}"
|
64
|
+
end
|
65
|
+
puts " ============================================"
|
66
|
+
puts ""
|
67
|
+
end
|
68
|
+
|
69
|
+
desc "list available converters"
|
70
|
+
task :converters, :needs => [:to_pass] do
|
71
|
+
puts ""
|
72
|
+
puts " available converters for password algorithms"
|
73
|
+
puts " ============================================"
|
74
|
+
ToPass::ConverterReader.new.discover.each do |converter|
|
75
|
+
puts " - #{converter}"
|
76
|
+
end
|
77
|
+
puts " ============================================"
|
78
|
+
puts ""
|
79
|
+
end
|
80
|
+
|
81
|
+
task :to_pass do
|
82
|
+
require 'lib/to_pass'
|
83
|
+
end
|
84
|
+
|
53
85
|
task :default => :test
|
data/TODO
CHANGED
@@ -1,9 +1,28 @@
|
|
1
1
|
UP NEXT
|
2
2
|
============================================================================
|
3
|
-
-
|
3
|
+
- make it debian compatible (most of which is common sense anyway)
|
4
|
+
http://pkg-ruby-extras.alioth.debian.org/upstream-devs.html
|
5
|
+
- Distribute your software as a source code archive
|
6
|
+
- Use setup.rb
|
7
|
+
- Remove all references to rubygems in the software you ship
|
8
|
+
- Don’t make your Rakefile depend on RubyGems
|
9
|
+
- Make your tests and examples usable outside of your directory tree
|
10
|
+
- Use a shebang that works everywhere
|
11
|
+
- Provide man pages for your binaries
|
12
|
+
- If possible, maintain a backward-compatible API
|
13
|
+
|
14
|
+
- Man-Pages http://www.linuxhowtos.org/System/creatingman.htm
|
15
|
+
order:
|
16
|
+
NAME
|
17
|
+
SYNOPSIS
|
18
|
+
DESCRIPTION
|
19
|
+
OPTIONS
|
20
|
+
BUGS
|
21
|
+
AUTHOR
|
22
|
+
SEE ALSO
|
23
|
+
|
4
24
|
|
5
25
|
SOMEDAY
|
6
26
|
============================================================================
|
7
27
|
- move decision wether a string is a word or not into algorithm definition.
|
8
|
-
- make conversion-functions more pluggable
|
9
28
|
- test on windows
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
@@ -4,7 +4,7 @@
|
|
4
4
|
require 'pathname'
|
5
5
|
require 'yaml'
|
6
6
|
|
7
|
-
# The AlgorithmReader's primary API is
|
7
|
+
# The AlgorithmReader's primary API is to load the rules from a YAML-file
|
8
8
|
# into a Hash.
|
9
9
|
#
|
10
10
|
# Algorithms are searched in the following locations
|
@@ -20,7 +20,7 @@ class ToPass::AlgorithmReader
|
|
20
20
|
@load_path = []
|
21
21
|
[
|
22
22
|
'~/.to_pass/algorithms',
|
23
|
-
"#{File.dirname(__FILE__)}
|
23
|
+
"#{File.dirname(__FILE__)}/algorithms"
|
24
24
|
].each do |dir|
|
25
25
|
dir = Pathname.new(dir).expand_path
|
26
26
|
@load_path << dir if dir.exist?
|
@@ -32,6 +32,15 @@ class ToPass::AlgorithmReader
|
|
32
32
|
def load(algorithm)
|
33
33
|
new(algorithm).load_from_file
|
34
34
|
end
|
35
|
+
|
36
|
+
# searches for available algorithms
|
37
|
+
def discover
|
38
|
+
new(nil).load_path.collect do |dir|
|
39
|
+
Dir["#{dir}/*.yml"]
|
40
|
+
end.flatten.compact.map do |fn|
|
41
|
+
File.basename(fn).gsub('.yml', '')
|
42
|
+
end
|
43
|
+
end
|
35
44
|
end
|
36
45
|
|
37
46
|
def load_from_file # :nodoc:
|
@@ -1,14 +1,14 @@
|
|
1
1
|
desc: Basic Algorithm with a german usage in mind
|
2
2
|
name: Basic (german)
|
3
3
|
sentence:
|
4
|
-
-
|
4
|
+
- replace: words
|
5
5
|
- first_chars
|
6
|
-
-
|
6
|
+
- replace: symbols
|
7
7
|
- collapse_chars
|
8
8
|
word:
|
9
|
-
-
|
10
|
-
-
|
11
|
-
|
9
|
+
- replace: chars
|
10
|
+
- replace: symbols
|
11
|
+
replacements:
|
12
12
|
words:
|
13
13
|
ein: 1
|
14
14
|
eine: 1
|
@@ -1,14 +1,14 @@
|
|
1
1
|
desc: Basic Algorithm with a english usage in mind
|
2
2
|
name: Basic (english)
|
3
3
|
sentence:
|
4
|
-
-
|
4
|
+
- replace: words
|
5
5
|
- first_chars
|
6
|
-
-
|
6
|
+
- replace: symbols
|
7
7
|
- collapse_chars
|
8
8
|
word:
|
9
|
-
-
|
10
|
-
-
|
11
|
-
|
9
|
+
- replace: chars
|
10
|
+
- replace: symbols
|
11
|
+
replacements:
|
12
12
|
words:
|
13
13
|
one: 1
|
14
14
|
single: 1
|
data/lib/to_pass/cli.rb
CHANGED
@@ -12,7 +12,7 @@ module ToPass
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def output
|
15
|
-
if @options[:
|
15
|
+
if @options[:pipe_out]
|
16
16
|
$stdout << @password
|
17
17
|
else
|
18
18
|
puts @password
|
@@ -24,7 +24,9 @@ module ToPass
|
|
24
24
|
# parse the options
|
25
25
|
def parse_options
|
26
26
|
options = {
|
27
|
-
:algorithm
|
27
|
+
:algorithm => 'basic_de',
|
28
|
+
:pipe_out => false,
|
29
|
+
:pipe_in => false
|
28
30
|
}
|
29
31
|
|
30
32
|
OptionParser.new do |opts|
|
@@ -35,6 +37,10 @@ module ToPass
|
|
35
37
|
options[:algorithm] = value
|
36
38
|
end
|
37
39
|
|
40
|
+
opts.on('-p', '--[no-]pipe', "pipe result to stdout (without trailing linebreak)") do |value|
|
41
|
+
options[:pipe_out] = value
|
42
|
+
end
|
43
|
+
|
38
44
|
opts.separator ""
|
39
45
|
|
40
46
|
opts.on_tail("-h", "--help", "Show this message") do
|
@@ -43,14 +49,16 @@ module ToPass
|
|
43
49
|
end
|
44
50
|
end.parse!
|
45
51
|
|
46
|
-
|
52
|
+
if ARGV[0].nil?
|
53
|
+
options[:pipe_in] = options[:pipe_out] = true
|
54
|
+
end
|
47
55
|
|
48
56
|
options
|
49
57
|
end
|
50
58
|
|
51
59
|
# get the input string
|
52
60
|
def get_input_string
|
53
|
-
unless @options[:
|
61
|
+
unless @options[:pipe_in]
|
54
62
|
ARGV[0]
|
55
63
|
else
|
56
64
|
$stdin.read.chomp
|
data/lib/to_pass/converter.rb
CHANGED
@@ -10,9 +10,12 @@ module ToPass
|
|
10
10
|
# a more complete description of the algorithm capabilities
|
11
11
|
# is still pending.
|
12
12
|
class Converter
|
13
|
+
attr_accessor :converters, :rules
|
13
14
|
# create a new converter, based on a set of conversion-rules
|
14
15
|
def initialize( rules )
|
15
16
|
@rules = rules
|
17
|
+
@reader = ConverterReader.new
|
18
|
+
@converters = @reader.discover
|
16
19
|
end
|
17
20
|
|
18
21
|
# convert a string into a password
|
@@ -31,9 +34,16 @@ module ToPass
|
|
31
34
|
# end
|
32
35
|
# end
|
33
36
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
+
# proxy to converters
|
38
|
+
def respond_to?(method_name) # :nodoc:
|
39
|
+
if [:convert, :rules_for, :process].include? method_name.to_sym
|
40
|
+
true
|
41
|
+
elsif @converters.include? method_name.to_sym
|
42
|
+
true
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
37
47
|
|
38
48
|
private
|
39
49
|
|
@@ -52,13 +62,20 @@ module ToPass
|
|
52
62
|
# process the string, rule by rule
|
53
63
|
def process(string, rules)
|
54
64
|
rules.inject(string) do |pwd, rule|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
65
|
+
apply_rule(pwd, rule)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# apply a single rule to the password-to-be
|
70
|
+
def apply_rule(pwd, rule)
|
71
|
+
cmd, args = rule.to_a.flatten
|
72
|
+
m = @reader.load(cmd.to_sym).method(cmd.to_sym)
|
73
|
+
|
74
|
+
case m.arity
|
75
|
+
when 1
|
76
|
+
m.call(pwd)
|
77
|
+
when 3, -2
|
78
|
+
m.call(pwd, @rules, *args)
|
62
79
|
end
|
63
80
|
end
|
64
81
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# vim:ft=ruby:enc=utf-8
|
3
|
+
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
# The ConverterReader's primary API is to load the converters from right
|
7
|
+
# directories into an Array
|
8
|
+
#
|
9
|
+
# Converters are searched in the following locations
|
10
|
+
#
|
11
|
+
# 1. ~/.to_pass/converters
|
12
|
+
# 2. bundled converters of gem
|
13
|
+
#
|
14
|
+
# The bundled converter are, however, lazily loaded with autoload.
|
15
|
+
# User-provided converters are always required (for now).
|
16
|
+
#
|
17
|
+
class ToPass::ConverterReader
|
18
|
+
attr_reader :load_path, :loaded
|
19
|
+
|
20
|
+
def initialize # :nodoc:
|
21
|
+
@load_path = []
|
22
|
+
@loaded = []
|
23
|
+
@discovered = []
|
24
|
+
[
|
25
|
+
'~/.to_pass/converters',
|
26
|
+
"#{File.dirname(__FILE__)}/converters"
|
27
|
+
].each do |dir|
|
28
|
+
dir = Pathname.new(dir).expand_path
|
29
|
+
@load_path << dir if dir.exist?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# loads the converters
|
34
|
+
def load(converter)
|
35
|
+
unless @loaded.include?(converter.to_sym)
|
36
|
+
load_from_file(converter)
|
37
|
+
@loaded << converter
|
38
|
+
end
|
39
|
+
|
40
|
+
classname(converter)
|
41
|
+
end
|
42
|
+
|
43
|
+
# discover a list of available converters
|
44
|
+
def discover
|
45
|
+
search_for_converters
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def search_for_converters # :nodoc:
|
51
|
+
files = load_path.collect do |directory|
|
52
|
+
dir = Pathname.new(directory)
|
53
|
+
if dir.exist?
|
54
|
+
Dir["#{dir}/*.rb"].collect do |converter|
|
55
|
+
fn_re = %r!/([^/]*)\.rb$!i
|
56
|
+
name = converter.match(fn_re)
|
57
|
+
if name
|
58
|
+
name[1].to_sym
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end.flatten.compact
|
63
|
+
|
64
|
+
raise LoadError, "converters could not be found in #{load_path}" if files.nil?
|
65
|
+
|
66
|
+
@discovered = files
|
67
|
+
end
|
68
|
+
|
69
|
+
def load_from_file(converter) # :nodoc:
|
70
|
+
fn = load_path.collect do |dir|
|
71
|
+
path = Pathname.new("#{dir}/#{converter}.rb").expand_path
|
72
|
+
|
73
|
+
if path.exist?
|
74
|
+
path
|
75
|
+
else
|
76
|
+
next
|
77
|
+
end
|
78
|
+
end.compact.first
|
79
|
+
|
80
|
+
raise LoadError, "converter '#{converter}' could not be found in #{load_path}" if fn.nil?
|
81
|
+
|
82
|
+
if require fn
|
83
|
+
classname converter
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def classname(converter) # :nodoc:
|
88
|
+
constantize("ToPass::Converters::#{camel_case(converter)}")
|
89
|
+
end
|
90
|
+
|
91
|
+
def constantize(camel_cased_word) # :nodoc:
|
92
|
+
names = camel_cased_word.split('::')
|
93
|
+
names.shift if names.empty? || names.first.empty?
|
94
|
+
|
95
|
+
constant = Object
|
96
|
+
names.each do |name|
|
97
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
98
|
+
end
|
99
|
+
constant
|
100
|
+
end
|
101
|
+
|
102
|
+
def camel_case(underscored_word) # :nodoc:
|
103
|
+
camel_cased_word = underscored_word.to_s.split('_').map do |part|
|
104
|
+
part.capitalize
|
105
|
+
end.join('')
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ToPass::Converters
|
2
|
+
class Replace
|
3
|
+
class << self
|
4
|
+
# perform replacements on a string, based on a replacment table
|
5
|
+
def replace(string, rules, tablename)
|
6
|
+
rules['replacements'][tablename].inject(string) do |pwd, map|
|
7
|
+
pwd = pwd.gsub(/#{map[0].to_s}/, map[1].to_s)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ToPass::Converters
|
2
|
+
class Swapcase
|
3
|
+
class << self
|
4
|
+
# alternate case of letter (not numbers)
|
5
|
+
def swapcase(string)
|
6
|
+
pwd = ""
|
7
|
+
last_upcase = true
|
8
|
+
|
9
|
+
string.each_char do |char|
|
10
|
+
char = if char.between?("0", "9")
|
11
|
+
char
|
12
|
+
elsif last_upcase
|
13
|
+
last_upcase = false
|
14
|
+
char.downcase
|
15
|
+
else
|
16
|
+
last_upcase = true
|
17
|
+
char.upcase
|
18
|
+
end
|
19
|
+
pwd << char
|
20
|
+
end
|
21
|
+
pwd
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# vim:ft=ruby:enc=utf-8
|
3
|
+
|
4
|
+
module ToPass
|
5
|
+
# The Converters-class is a collection of available
|
6
|
+
# Transformations. Every method is given a string as single argument
|
7
|
+
# and is expected to return a transformed string. The only exception
|
8
|
+
# from this is general rule is the replace method which also takes a
|
9
|
+
# replacement table.
|
10
|
+
#
|
11
|
+
# This enables chaining and eases extending the capabilities.
|
12
|
+
module Converters
|
13
|
+
end
|
14
|
+
end
|
data/lib/to_pass.rb
CHANGED
@@ -13,6 +13,7 @@ module ToPass
|
|
13
13
|
autoload :Base, 'lib/to_pass/base'
|
14
14
|
autoload :Cli, 'lib/to_pass/cli.rb'
|
15
15
|
autoload :Converter, 'lib/to_pass/converter'
|
16
|
+
autoload :ConverterReader, 'lib/to_pass/converter_reader'
|
17
|
+
autoload :Converters, 'lib/to_pass/converters'
|
16
18
|
autoload :Integration, 'lib/to_pass/integration'
|
17
|
-
autoload :StringConversions, 'lib/to_pass/string_conversions'
|
18
19
|
end
|
data/test/fixtures/user_alg.yml
CHANGED
@@ -2,13 +2,13 @@ desc: Test Algorithm
|
|
2
2
|
name: Test
|
3
3
|
sentence:
|
4
4
|
- first_chars
|
5
|
-
-
|
6
|
-
-
|
5
|
+
- replace: chars
|
6
|
+
- replace: symbols
|
7
7
|
- collapse_chars
|
8
8
|
word:
|
9
|
-
-
|
10
|
-
-
|
11
|
-
|
9
|
+
- replace: chars
|
10
|
+
- replace: symbols
|
11
|
+
replacements:
|
12
12
|
symbols:
|
13
13
|
p: '%'
|
14
14
|
s: '/'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# vim:ft=ruby:enc=utf-8
|
3
|
+
|
4
|
+
module ToPass::Converters
|
5
|
+
# replace the string "USER" with the current username
|
6
|
+
class Userize
|
7
|
+
def self.convert(string) # :nodoc:
|
8
|
+
username = if `which whoami`
|
9
|
+
`whoami`.chomp
|
10
|
+
else
|
11
|
+
"thecurrentuser"
|
12
|
+
end
|
13
|
+
|
14
|
+
string.gsub("USER",username)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/test/helper.rb
CHANGED
@@ -27,10 +27,16 @@ Test::Unit::TestCase.class_eval do
|
|
27
27
|
|
28
28
|
|
29
29
|
def with_algorithm_in_user_dir
|
30
|
-
`mkdir -p
|
30
|
+
`mkdir -p ~/.to_pass/algorithms; cp -f #{File.dirname(__FILE__)}/fixtures/user_alg.yml ~/.to_pass/algorithms/user_alg.yml`
|
31
31
|
yield
|
32
32
|
`rm ~/.to_pass/algorithms/user_alg.yml`
|
33
33
|
end
|
34
|
+
|
35
|
+
def with_converters_in_user_dir
|
36
|
+
`mkdir -p ~/.to_pass/converters; cp -f #{File.dirname(__FILE__)}/fixtures/user_converter.rb ~/.to_pass/converters/userize.rb`
|
37
|
+
yield
|
38
|
+
`rm ~/.to_pass/converters/userize.rb`
|
39
|
+
end
|
34
40
|
end
|
35
41
|
|
36
42
|
require 'lib/to_pass'
|
@@ -17,6 +17,11 @@ class TestAlgorithmReader < Test::Unit::TestCase
|
|
17
17
|
assert_kind_of Hash, klass.load(:basic_de)
|
18
18
|
end
|
19
19
|
|
20
|
+
def test_discover
|
21
|
+
assert_respond_to klass, :discover
|
22
|
+
assert_kind_of Array, klass.discover
|
23
|
+
end
|
24
|
+
|
20
25
|
def test_has_load_path
|
21
26
|
assert_respond_to reader, :load_path
|
22
27
|
assert_kind_of Array, reader.load_path
|
@@ -25,7 +30,7 @@ class TestAlgorithmReader < Test::Unit::TestCase
|
|
25
30
|
def test_load_path_contains_standard_dirs
|
26
31
|
dirs = [
|
27
32
|
'~/.to_pass/algorithms' ,
|
28
|
-
"#{File.dirname(__FILE__)}/../lib/algorithms"
|
33
|
+
"#{File.dirname(__FILE__)}/../lib/to_pass/algorithms"
|
29
34
|
]
|
30
35
|
|
31
36
|
Pathname.any_instance.expects(:exist?).times(dirs.size).returns(true)
|
data/test/test_cli.rb
CHANGED
@@ -24,6 +24,18 @@ class TestCli < Test::Unit::TestCase
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
def test_cli_usage_with_pipe_input
|
28
|
+
assert_nothing_raised do
|
29
|
+
assert_equal 't35t', `echo "test" | bin/to_pass --no-pipe`.chomp
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_cli_usage_with_pipe_output
|
34
|
+
assert_nothing_raised do
|
35
|
+
assert_equal 't35t', `bin/to_pass test --pipe`
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
27
39
|
|
28
40
|
def test_cli_usage_with_user_algorithm
|
29
41
|
with_algorithm_in_user_dir do
|
data/test/test_converter.rb
CHANGED
@@ -25,33 +25,34 @@ class TestConverter < Test::Unit::TestCase
|
|
25
25
|
# mock-tests to ensure presence and calling of protected methods
|
26
26
|
def test_replace
|
27
27
|
assert_respond_to converter, :replace
|
28
|
-
converter.expects(:replace).once
|
28
|
+
converter.expects(:apply_rule).with("test", {'replace'=>'special'}).once
|
29
29
|
|
30
30
|
converter.convert("test")
|
31
31
|
end
|
32
32
|
def test_swapcase
|
33
33
|
converter({'word' => ['swapcase']})
|
34
34
|
assert_respond_to converter, :swapcase
|
35
|
-
converter.expects(:swapcase).once
|
35
|
+
converter.expects(:apply_rule).with("test", 'swapcase' ).once
|
36
36
|
converter.convert("test")
|
37
37
|
end
|
38
38
|
def test_first_chars
|
39
39
|
converter({'sentence' => ['first_chars']})
|
40
40
|
assert_respond_to converter, :first_chars
|
41
|
-
converter.expects(:first_chars).once
|
41
|
+
converter.expects(:apply_rule).with("test test", 'first_chars').once
|
42
42
|
converter.convert("test test")
|
43
43
|
end
|
44
44
|
def test_collapse_chars
|
45
45
|
converter({'sentence' => ['collapse_chars']})
|
46
46
|
assert_respond_to converter, :collapse_chars
|
47
|
-
converter.expects(:collapse_chars).once
|
47
|
+
converter.expects(:apply_rule).with("test test", 'collapse_chars').once
|
48
48
|
converter.convert("test test")
|
49
49
|
end
|
50
50
|
|
51
|
+
|
51
52
|
# more complex/real-life setups
|
52
53
|
def test_multiple_rules
|
53
54
|
converter(basic_rules.merge({
|
54
|
-
'word' => [{'
|
55
|
+
'word' => [{'replace' => 'special'}, 'swapcase']
|
55
56
|
}))
|
56
57
|
|
57
58
|
assert_equal( "t35T", converter.convert('test'))
|
@@ -67,6 +68,34 @@ class TestConverter < Test::Unit::TestCase
|
|
67
68
|
Pferd auf dem Flur"))
|
68
69
|
end
|
69
70
|
|
71
|
+
|
72
|
+
# actual result-testing
|
73
|
+
def test_replacement
|
74
|
+
rules = {
|
75
|
+
'replacements' => {
|
76
|
+
'numbers' => {
|
77
|
+
:e => 3,
|
78
|
+
:s => 5
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
result = converter(rules).send(:apply_rule, "test", {'replace'=>'numbers'})
|
83
|
+
assert_equal "t35t", result
|
84
|
+
end
|
85
|
+
def test_case_swapping
|
86
|
+
assert_equal "tEsT", converter.send(:apply_rule, "test", 'swapcase')
|
87
|
+
end
|
88
|
+
def test_case_swapping_ignores_numbers
|
89
|
+
assert_equal "tEsT4fUn", converter.send(:apply_rule, "test4fun", 'swapcase')
|
90
|
+
assert_equal "fUn4TeSt", converter.send(:apply_rule, "fun4test", 'swapcase')
|
91
|
+
end
|
92
|
+
def test_char_collapsing
|
93
|
+
assert_equal "abc", converter.send(:apply_rule, "a b c", 'collapse_chars')
|
94
|
+
end
|
95
|
+
def test_select_first_chars
|
96
|
+
assert_equal "t a t f t", converter.send(:apply_rule, "test all the fucking time", 'first_chars')
|
97
|
+
end
|
98
|
+
|
70
99
|
protected
|
71
100
|
|
72
101
|
def converter(rules = basic_rules)
|
@@ -75,8 +104,8 @@ Pferd auf dem Flur"))
|
|
75
104
|
|
76
105
|
def basic_rules
|
77
106
|
{
|
78
|
-
'word' => [{ '
|
79
|
-
'
|
107
|
+
'word' => [{ 'replace' => 'special' }],
|
108
|
+
'replacements' => {
|
80
109
|
'special' => {
|
81
110
|
'e' => '3',
|
82
111
|
's' => '5'
|
@@ -86,8 +115,8 @@ Pferd auf dem Flur"))
|
|
86
115
|
end
|
87
116
|
def complex_rules
|
88
117
|
{
|
89
|
-
'sentence' => [{'
|
90
|
-
'
|
118
|
+
'sentence' => [{'replace'=>'words'},'first_chars','collapse_chars',{'replace'=>'symbols'}],
|
119
|
+
'replacements' => {
|
91
120
|
'words' => { 'ein' => '1' },
|
92
121
|
'symbols' => { 'a' => '@' }
|
93
122
|
}
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# vim:ft=ruby:enc=utf-8
|
3
|
+
|
4
|
+
require File.dirname(__FILE__)+'/helper'
|
5
|
+
|
6
|
+
class TestConverterReader < Test::Unit::TestCase
|
7
|
+
test_presence ToPass::ConverterReader
|
8
|
+
|
9
|
+
def test_load
|
10
|
+
assert_respond_to reader, :load
|
11
|
+
assert_kind_of Class, reader.load(:replace)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_discover
|
15
|
+
assert_respond_to reader, :discover
|
16
|
+
assert_kind_of Array, reader.discover
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_has_load_path
|
20
|
+
assert_respond_to reader, :load_path
|
21
|
+
assert_kind_of Array, reader.load_path
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_knows_loaded_converters
|
25
|
+
assert_respond_to reader, :loaded
|
26
|
+
assert_kind_of Array, reader.loaded
|
27
|
+
assert_equal 0, reader.loaded.size
|
28
|
+
|
29
|
+
reader.load(:replace)
|
30
|
+
reader.load(:first_chars)
|
31
|
+
|
32
|
+
assert_equal 2, reader.loaded.size
|
33
|
+
assert_equal :replace, reader.loaded.first
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_load_path_contains_standard_dirs
|
37
|
+
dirs = [
|
38
|
+
'~/.to_pass/converters',
|
39
|
+
"#{File.dirname(__FILE__)}/../lib/to_pass/converters"
|
40
|
+
]
|
41
|
+
|
42
|
+
Pathname.any_instance.expects(:exist?).times(dirs.size).returns(true)
|
43
|
+
|
44
|
+
dirs.each do |reldir|
|
45
|
+
dir = Pathname.new(reldir).expand_path
|
46
|
+
assert( reader.load_path.include?(dir), "#{reader.load_path.inspect} should include #{dir.inspect}" )
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_loads_from_user_dir
|
51
|
+
with_converters_in_user_dir do
|
52
|
+
assert_kind_of Class, reader.load(:userize), "Converter 'Userize' should have been found"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def klass
|
59
|
+
ToPass::ConverterReader
|
60
|
+
end
|
61
|
+
def reader
|
62
|
+
@reader ||= klass.new
|
63
|
+
end
|
64
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: to_pass
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
- 2
|
9
8
|
- 4
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 0.4.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Matthias Viehweger
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-06-
|
18
|
+
date: 2010-06-30 00:00:00 +02:00
|
19
19
|
default_executable: to_pass
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -66,23 +66,30 @@ files:
|
|
66
66
|
- TODO
|
67
67
|
- VERSION
|
68
68
|
- bin/to_pass
|
69
|
-
- lib/algorithms/basic_de.yml
|
70
|
-
- lib/algorithms/basic_en.yml
|
71
69
|
- lib/to_pass.rb
|
72
70
|
- lib/to_pass/algorithm_reader.rb
|
71
|
+
- lib/to_pass/algorithms/basic_de.yml
|
72
|
+
- lib/to_pass/algorithms/basic_en.yml
|
73
73
|
- lib/to_pass/base.rb
|
74
74
|
- lib/to_pass/cli.rb
|
75
75
|
- lib/to_pass/converter.rb
|
76
|
+
- lib/to_pass/converter_reader.rb
|
77
|
+
- lib/to_pass/converters.rb
|
78
|
+
- lib/to_pass/converters/collapse_chars.rb
|
79
|
+
- lib/to_pass/converters/first_chars.rb
|
80
|
+
- lib/to_pass/converters/replace.rb
|
81
|
+
- lib/to_pass/converters/swapcase.rb
|
76
82
|
- lib/to_pass/integration.rb
|
77
|
-
- lib/to_pass/string_conversions.rb
|
78
83
|
- test/fixtures/user_alg.yml
|
84
|
+
- test/fixtures/user_converter.rb
|
79
85
|
- test/helper.rb
|
80
86
|
- test/test_algorithm_reader.rb
|
81
87
|
- test/test_base.rb
|
82
88
|
- test/test_cli.rb
|
83
89
|
- test/test_converter.rb
|
90
|
+
- test/test_converter_reader.rb
|
91
|
+
- test/test_converters.rb
|
84
92
|
- test/test_integration.rb
|
85
|
-
- test/test_string_conversions.rb
|
86
93
|
has_rdoc: true
|
87
94
|
homepage: http://github.com/kronn/to_pass
|
88
95
|
licenses: []
|
@@ -118,10 +125,12 @@ signing_key:
|
|
118
125
|
specification_version: 3
|
119
126
|
summary: generate password from words or sentences
|
120
127
|
test_files:
|
128
|
+
- test/fixtures/user_converter.rb
|
121
129
|
- test/helper.rb
|
122
130
|
- test/test_algorithm_reader.rb
|
123
131
|
- test/test_base.rb
|
124
132
|
- test/test_cli.rb
|
125
133
|
- test/test_converter.rb
|
134
|
+
- test/test_converter_reader.rb
|
135
|
+
- test/test_converters.rb
|
126
136
|
- test/test_integration.rb
|
127
|
-
- test/test_string_conversions.rb
|
@@ -1,54 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
# vim:ft=ruby:enc=utf-8
|
3
|
-
|
4
|
-
module ToPass
|
5
|
-
# The StringConversions-Module is a collection of available
|
6
|
-
# Transformations. Every method is given a string as single argument
|
7
|
-
# and is expected to return a transformed string. The only exception
|
8
|
-
# from this is general rule is the replace method which also takes a
|
9
|
-
# replacement table.
|
10
|
-
#
|
11
|
-
# This enables chaining and eases extending the capabilities.
|
12
|
-
module StringConversions
|
13
|
-
# all the blanks are removed.
|
14
|
-
#
|
15
|
-
# this is useful if you convert a sentence into a password.
|
16
|
-
def collapse_chars(string)
|
17
|
-
string = string.split(' ').join('')
|
18
|
-
end
|
19
|
-
|
20
|
-
# reduces every word to its first character, preserving case
|
21
|
-
def first_chars(string)
|
22
|
-
string.split(' ').map do |word|
|
23
|
-
word[0].chr
|
24
|
-
end.join(' ')
|
25
|
-
end
|
26
|
-
|
27
|
-
# alternate case of letter (not numbers)
|
28
|
-
def swapcase(string)
|
29
|
-
pwd = ""
|
30
|
-
last_upcase = true
|
31
|
-
|
32
|
-
string.each_char do |char|
|
33
|
-
char = if char.between?("0", "9")
|
34
|
-
char
|
35
|
-
elsif last_upcase
|
36
|
-
last_upcase = false
|
37
|
-
char.downcase
|
38
|
-
else
|
39
|
-
last_upcase = true
|
40
|
-
char.upcase
|
41
|
-
end
|
42
|
-
pwd << char
|
43
|
-
end
|
44
|
-
pwd
|
45
|
-
end
|
46
|
-
|
47
|
-
# perform replacements on a string, based on a replacment table
|
48
|
-
def replace(string, table)
|
49
|
-
table.inject(string) do |pwd, map|
|
50
|
-
pwd = pwd.gsub(/#{map[0].to_s}/, map[1].to_s)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
# vim:ft=ruby:enc=utf-8
|
3
|
-
|
4
|
-
require File.dirname(__FILE__)+'/helper'
|
5
|
-
|
6
|
-
class TestStringConversion < Test::Unit::TestCase
|
7
|
-
def test_replacement
|
8
|
-
result = converter.replace("test", {
|
9
|
-
:e => 3,
|
10
|
-
:s => 5
|
11
|
-
})
|
12
|
-
assert_equal "t35t", result
|
13
|
-
end
|
14
|
-
|
15
|
-
def test_case_swapping
|
16
|
-
assert_equal "tEsT", converter.swapcase("test")
|
17
|
-
end
|
18
|
-
|
19
|
-
def test_case_swapping_ignores_numbers
|
20
|
-
assert_equal "tEsT4fUn", converter.swapcase("test4fun")
|
21
|
-
|
22
|
-
assert_equal "fUn4TeSt", converter.swapcase("fun4test")
|
23
|
-
end
|
24
|
-
|
25
|
-
def test_char_collapsing
|
26
|
-
assert_equal "abc", converter.collapse_chars("a b c")
|
27
|
-
end
|
28
|
-
|
29
|
-
def test_select_first_chars
|
30
|
-
assert_equal "t a t f t", converter.first_chars('test all the fucking time')
|
31
|
-
end
|
32
|
-
|
33
|
-
protected
|
34
|
-
|
35
|
-
def converter
|
36
|
-
klass = Class.new
|
37
|
-
klass.send(:extend, ToPass::StringConversions)
|
38
|
-
klass
|
39
|
-
end
|
40
|
-
end
|