to_pass 0.2.2
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.
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +48 -0
- data/Rakefile +53 -0
- data/TODO +9 -0
- data/VERSION +1 -0
- data/bin/to_pass +47 -0
- data/lib/algorithms/basic_de.yml +30 -0
- data/lib/algorithms/basic_en.yml +32 -0
- data/lib/to_pass/algorithm_reader.rb +47 -0
- data/lib/to_pass/base.rb +20 -0
- data/lib/to_pass/converter.rb +62 -0
- data/lib/to_pass/integration.rb +13 -0
- data/lib/to_pass/string_conversions.rb +51 -0
- data/lib/to_pass.rb +14 -0
- data/test/helper.rb +26 -0
- data/test/test_algorithm_reader.rb +44 -0
- data/test/test_base.rb +20 -0
- data/test/test_converter.rb +93 -0
- data/test/test_integration.rb +30 -0
- data/test/test_string_conversions.rb +37 -0
- metadata +123 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009-2010 Matthias Viehweger
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
= to_pass
|
2
|
+
|
3
|
+
== Transformation of a String to a Password.
|
4
|
+
|
5
|
+
Passwords should be easy to remember and hard to guess. One technique is to
|
6
|
+
have a sentence which can be easily remembered transformed to a password.
|
7
|
+
|
8
|
+
This library encapsulates that and satisfies different usage scenarios.
|
9
|
+
Currently, only two algorithms are provided. But they are YAML-Files, so it's
|
10
|
+
extensible.
|
11
|
+
|
12
|
+
== Usage
|
13
|
+
|
14
|
+
=== IRB
|
15
|
+
|
16
|
+
ToPass::Integration provides a to_pass Method which transforms a sentence to a
|
17
|
+
password.
|
18
|
+
|
19
|
+
irb> require 'lib/to_pass'
|
20
|
+
irb> String.send(:include, ToPass::Integration)
|
21
|
+
irb> "Da steht ein Pferd auf dem Flur".to_pass
|
22
|
+
# => "Ds1P@dF"
|
23
|
+
|
24
|
+
=== CLI
|
25
|
+
|
26
|
+
$ to_pass "test" # => t35t
|
27
|
+
$ to_pass "there is one problem for us, too" -a basic_en # => ti1p4u2
|
28
|
+
|
29
|
+
=== CLI with Pipes
|
30
|
+
|
31
|
+
This example is Mac OSX-centric, but you should get the point.
|
32
|
+
|
33
|
+
$ echo "test" | to_pass | pbcopy
|
34
|
+
|
35
|
+
== Note on Patches/Pull Requests
|
36
|
+
|
37
|
+
* Fork the project.
|
38
|
+
* Make your feature addition or bug fix.
|
39
|
+
* Add tests for it. This is important so I don't break it in a
|
40
|
+
future version unintentionally.
|
41
|
+
* Commit, do not mess with rakefile, version, or history.
|
42
|
+
(if you want to have your own version, that is fine but bump version in a
|
43
|
+
commit by itself I can ignore when I pull)
|
44
|
+
* Send me a pull request. Bonus points for topic branches.
|
45
|
+
|
46
|
+
== Copyright
|
47
|
+
|
48
|
+
Copyright (c) 2010 Matthias Viehweger. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# vim:ft=ruby:enc=utf-8
|
3
|
+
|
4
|
+
# # enable trace to get better error output
|
5
|
+
# Rake.application.options.trace = true
|
6
|
+
|
7
|
+
# jeweler task
|
8
|
+
begin
|
9
|
+
require 'jeweler'
|
10
|
+
Jeweler::Tasks.new do |gem|
|
11
|
+
gem.name = "to_pass"
|
12
|
+
gem.summary = "generate password from words or sentences"
|
13
|
+
gem.description = %Q{Passwords should be easy to remember and hard to guess.
|
14
|
+
One technique is to have a sentence which can be easily remembered transformed to a password.}
|
15
|
+
gem.email = "kronn@kronn.de"
|
16
|
+
gem.homepage = "http://github.com/kronn/to_pass"
|
17
|
+
gem.authors = ["Matthias Viehweger"]
|
18
|
+
|
19
|
+
gem.add_development_dependency 'mocha'
|
20
|
+
gem.add_development_dependency 'sdoc'
|
21
|
+
end
|
22
|
+
Jeweler::GemcutterTasks.new
|
23
|
+
rescue LoadError
|
24
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
25
|
+
end
|
26
|
+
|
27
|
+
%w[ rake/rdoctask sdoc ].each { |lib| require lib }
|
28
|
+
Rake::RDocTask.new do |rdoc|
|
29
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
30
|
+
|
31
|
+
rdoc.rdoc_dir = 'doc/rdoc'
|
32
|
+
rdoc.title = "to_pass #{version}"
|
33
|
+
rdoc.options << '--fmt' << 'shtml'
|
34
|
+
rdoc.template = 'direct'
|
35
|
+
rdoc.rdoc_files.include('README*')
|
36
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "run tests"
|
40
|
+
task :test, :needs => [:check_dependencies] do
|
41
|
+
# optional libraries
|
42
|
+
%w[ redgreen ].each do |lib|
|
43
|
+
begin
|
44
|
+
require lib
|
45
|
+
rescue LoadError
|
46
|
+
end
|
47
|
+
end
|
48
|
+
( ['test/unit', 'test/helper'] + Dir['test/test_*.rb'] ).each do |file|
|
49
|
+
require file
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
task :default => :test
|
data/TODO
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
UP NEXT
|
2
|
+
============================================================================
|
3
|
+
- test and enble class methods of Converter-class
|
4
|
+
|
5
|
+
SOMEDAY
|
6
|
+
============================================================================
|
7
|
+
- move decision wether a string is a word or not into algorithm definition.
|
8
|
+
- make conversion-functions more pluggable
|
9
|
+
- test on windows
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.2
|
data/bin/to_pass
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# vim:ft=ruby:enc=utf-8
|
4
|
+
|
5
|
+
base_path = ( File.expand_path(File.dirname(__FILE__)+'/..') )
|
6
|
+
$LOAD_PATH << base_path unless $LOAD_PATH.include?(base_path)
|
7
|
+
|
8
|
+
require 'optparse'
|
9
|
+
require 'lib/to_pass'
|
10
|
+
|
11
|
+
# parse options
|
12
|
+
options = {
|
13
|
+
:pipe_usage => ARGV[0].nil?,
|
14
|
+
:algorithm => 'basic_de'
|
15
|
+
}
|
16
|
+
|
17
|
+
OptionParser.new do |opts|
|
18
|
+
opts.banner = "Usage: #{__FILE__} [options] passphrase"
|
19
|
+
opts.separator ""
|
20
|
+
|
21
|
+
opts.on('-a', '--algorithm ALGORITM', "use specified algorithm for transformation") do |value|
|
22
|
+
options[:algorithm] = value
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.separator ""
|
26
|
+
|
27
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
28
|
+
puts opts
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
end.parse!
|
32
|
+
|
33
|
+
# get input string
|
34
|
+
string= unless options[:pipe_usage]
|
35
|
+
ARGV[0]
|
36
|
+
else
|
37
|
+
$stdin.read.chomp
|
38
|
+
end
|
39
|
+
|
40
|
+
# perform "heavy" work
|
41
|
+
password = ToPass::Base.new( string, options[:algorithm] ).to_s
|
42
|
+
|
43
|
+
if options[:pipe_usage]
|
44
|
+
$stdout << password
|
45
|
+
else
|
46
|
+
puts password
|
47
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
desc: Basic Algorithm with a german usage in mind
|
2
|
+
name: Basic (german)
|
3
|
+
sentence:
|
4
|
+
- table: words
|
5
|
+
- first_chars
|
6
|
+
- table: symbols
|
7
|
+
- collapse_chars
|
8
|
+
word:
|
9
|
+
- table: chars
|
10
|
+
- table: symbols
|
11
|
+
tables:
|
12
|
+
words:
|
13
|
+
ein: 1
|
14
|
+
eine: 1
|
15
|
+
zwei: 2
|
16
|
+
drei: 3
|
17
|
+
vier: 4
|
18
|
+
fuenf: 5
|
19
|
+
sechs: 6
|
20
|
+
sieben: 7
|
21
|
+
acht: 8
|
22
|
+
neun: 9
|
23
|
+
symbols:
|
24
|
+
a: '@'
|
25
|
+
chars:
|
26
|
+
a: 4
|
27
|
+
e: 3
|
28
|
+
i: 1
|
29
|
+
o: 0
|
30
|
+
s: 5
|
@@ -0,0 +1,32 @@
|
|
1
|
+
desc: Basic Algorithm with a english usage in mind
|
2
|
+
name: Basic (english)
|
3
|
+
sentence:
|
4
|
+
- table: words
|
5
|
+
- first_chars
|
6
|
+
- table: symbols
|
7
|
+
- collapse_chars
|
8
|
+
word:
|
9
|
+
- table: chars
|
10
|
+
- table: symbols
|
11
|
+
tables:
|
12
|
+
words:
|
13
|
+
one: 1
|
14
|
+
single: 1
|
15
|
+
two: 2
|
16
|
+
too: 2
|
17
|
+
three: 3
|
18
|
+
four: 4
|
19
|
+
for: 4
|
20
|
+
five: 5
|
21
|
+
six: 6
|
22
|
+
seven: 7
|
23
|
+
eight: 8
|
24
|
+
nine: 9
|
25
|
+
symbols:
|
26
|
+
a: '@'
|
27
|
+
chars:
|
28
|
+
a: 4
|
29
|
+
e: 3
|
30
|
+
i: 1
|
31
|
+
o: 0
|
32
|
+
s: 5
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
# The AlgorithmReader's primary API is the load the rules from a YAML-file
|
5
|
+
# into a Hash.
|
6
|
+
#
|
7
|
+
# Algorithms are searched in the following locations
|
8
|
+
#
|
9
|
+
# 1. ~/.to_pass/algorithms
|
10
|
+
# 2. bundled algorithms of gem
|
11
|
+
#
|
12
|
+
class ToPass::AlgorithmReader
|
13
|
+
attr_reader :load_path
|
14
|
+
|
15
|
+
def initialize(algorithm) # :nodoc:
|
16
|
+
@algorithm = algorithm
|
17
|
+
@load_path = []
|
18
|
+
[
|
19
|
+
'~/.to_pass/algorithms',
|
20
|
+
"#{File.dirname(__FILE__)}/../algorithms"
|
21
|
+
].each do |dir|
|
22
|
+
dir = Pathname.new(dir)
|
23
|
+
@load_path << dir.expand_path if dir.exist?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
# load an algorithm with a given identifier
|
29
|
+
def load(algorithm)
|
30
|
+
new(algorithm).load_from_file
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def load_from_file # :nodoc:
|
35
|
+
fn = load_path.map do |dir|
|
36
|
+
file = Pathname.new("#{dir}/#{@algorithm}.yml")
|
37
|
+
|
38
|
+
if file.exist?
|
39
|
+
file
|
40
|
+
else
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end.compact.first
|
44
|
+
|
45
|
+
YAML.load_file(fn.expand_path)
|
46
|
+
end
|
47
|
+
end
|
data/lib/to_pass/base.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module ToPass
|
2
|
+
# ToPass::Base is mostly a facade for the library.
|
3
|
+
#
|
4
|
+
# Given a string and a algorithm identifier, the right rules
|
5
|
+
# are loaded and applied to the string. With a simple "to_s",
|
6
|
+
# you can get the final password
|
7
|
+
class Base
|
8
|
+
# transform a string according to a certain algorithm
|
9
|
+
def initialize( string, algorithm )
|
10
|
+
rules = AlgorithmReader.load(algorithm)
|
11
|
+
converter = Converter.new( rules )
|
12
|
+
@password = converter.convert(string)
|
13
|
+
end
|
14
|
+
|
15
|
+
# return the generated password
|
16
|
+
def to_s
|
17
|
+
@password
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module ToPass
|
2
|
+
# converts a given string into a password-like word
|
3
|
+
#
|
4
|
+
# the string can be a word or a sentence. everthing which
|
5
|
+
# contains whitespace is considered a sentence
|
6
|
+
#
|
7
|
+
# a more complete description of the algorithm capabilities
|
8
|
+
# is still pending.
|
9
|
+
class Converter
|
10
|
+
# create a new converter, based on a set of conversion-rules
|
11
|
+
def initialize( rules )
|
12
|
+
@rules = rules
|
13
|
+
end
|
14
|
+
|
15
|
+
# convert a string into a password
|
16
|
+
def convert( string )
|
17
|
+
process(string, rules_for(string))
|
18
|
+
end
|
19
|
+
|
20
|
+
# class << self
|
21
|
+
# # convenience method to apply conversion rules to a string
|
22
|
+
# def apply(rules, string)
|
23
|
+
# new(rules).convert(string)
|
24
|
+
# end
|
25
|
+
# # convenience method to convert a string based on rules
|
26
|
+
# def convert(string, rules)
|
27
|
+
# new(rules).convert(string)
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
include StringConversions
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# return the applicable rules for a given string.
|
38
|
+
#
|
39
|
+
# everything which contains whitespace is considered a sentence,
|
40
|
+
# otherwise it is most likely a word.
|
41
|
+
def rules_for( string )
|
42
|
+
if string.include? ' ' or /\s/.match(string)
|
43
|
+
@rules['sentence']
|
44
|
+
else
|
45
|
+
@rules['word']
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# process the string, rule by rule
|
50
|
+
def process(string, rules)
|
51
|
+
rules.inject(string) do |pwd, rule|
|
52
|
+
if rule.is_a?(String) and respond_to?(rule.to_sym)
|
53
|
+
send(rule.to_sym, pwd)
|
54
|
+
elsif rule.is_a?(Hash) and rule.has_key?('table')
|
55
|
+
replace(pwd, @rules['tables'][rule.fetch('table')])
|
56
|
+
else
|
57
|
+
pwd
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Any Object can be a password, as long as it has a string representation.
|
2
|
+
#
|
3
|
+
# This Module provides a shortcut to the password-conversion.
|
4
|
+
#
|
5
|
+
# String.send(:include, ToPass::Integration)
|
6
|
+
# puts "Da steht ein Pferd auf dem Flur".to_pass => 'Ds1P@dF'
|
7
|
+
#
|
8
|
+
module ToPass::Integration
|
9
|
+
# simplified password-creation
|
10
|
+
def to_pass( algorithm = 'basic_de' )
|
11
|
+
ToPass::Base.new(self.to_s, algorithm).to_s
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module ToPass
|
2
|
+
# The StringConversions-Module is a collection of available
|
3
|
+
# Transformations. Every method is given a string as single argument
|
4
|
+
# and is expected to return a transformed string. The only exception
|
5
|
+
# from this is general rule is the replace method which also takes a
|
6
|
+
# replacement table.
|
7
|
+
#
|
8
|
+
# This enables chaining and eases extending the capabilities.
|
9
|
+
module StringConversions
|
10
|
+
# all the blanks are removed.
|
11
|
+
#
|
12
|
+
# this is useful if you convert a sentence into a password.
|
13
|
+
def collapse_chars(string)
|
14
|
+
string = string.split(' ').join('')
|
15
|
+
end
|
16
|
+
|
17
|
+
# reduces every word to its first character, preserving case
|
18
|
+
def first_chars(string)
|
19
|
+
string.split(' ').map do |word|
|
20
|
+
word[0].chr
|
21
|
+
end.join(' ')
|
22
|
+
end
|
23
|
+
|
24
|
+
# alternate case of letter (not numbers)
|
25
|
+
def swapcase(string)
|
26
|
+
pwd = ""
|
27
|
+
last_upcase = true
|
28
|
+
|
29
|
+
string.each_char do |char|
|
30
|
+
char = if char.between?("0", "9")
|
31
|
+
char
|
32
|
+
elsif last_upcase
|
33
|
+
last_upcase = false
|
34
|
+
char.downcase
|
35
|
+
else
|
36
|
+
last_upcase = true
|
37
|
+
char.upcase
|
38
|
+
end
|
39
|
+
pwd << char
|
40
|
+
end
|
41
|
+
pwd
|
42
|
+
end
|
43
|
+
|
44
|
+
# perform replacements on a string, based on a replacment table
|
45
|
+
def replace(string, table)
|
46
|
+
table.inject(string) do |pwd, map|
|
47
|
+
pwd = pwd.gsub(/#{map[0].to_s}/, map[1].to_s)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/to_pass.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# Library to convert a String into a Password
|
2
|
+
module ToPass
|
3
|
+
# version of gem, read directly from the VERSION-File
|
4
|
+
VERSION = File.read(File.join(File.dirname(__FILE__), '../VERSION')).strip
|
5
|
+
|
6
|
+
# name of gem
|
7
|
+
APP_NAME = 'to_pass'
|
8
|
+
|
9
|
+
autoload :Converter, 'lib/to_pass/converter'
|
10
|
+
autoload :StringConversions, 'lib/to_pass/string_conversions'
|
11
|
+
autoload :Base, 'lib/to_pass/base'
|
12
|
+
autoload :Integration, 'lib/to_pass/integration'
|
13
|
+
autoload :AlgorithmReader, 'lib/to_pass/algorithm_reader'
|
14
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'test/unit/testcase'
|
2
|
+
require 'test/unit' unless defined?(Test::Unit)
|
3
|
+
require 'mocha'
|
4
|
+
|
5
|
+
base_path = ( File.expand_path(File.dirname(__FILE__)+'/..') )
|
6
|
+
$LOAD_PATH << base_path unless $LOAD_PATH.include?(base_path)
|
7
|
+
|
8
|
+
Test::Unit::TestCase.class_eval do
|
9
|
+
def assert_class_defined(klass)
|
10
|
+
assert defined?(klass), "#{klass} should be defined"
|
11
|
+
assert_kind_of Class, klass, "#{klass} should be a class"
|
12
|
+
end
|
13
|
+
|
14
|
+
def assert_module_defined(modul)
|
15
|
+
assert defined?(modul), "#{modul} should be defined"
|
16
|
+
assert_kind_of Module, modul, "#{modul} should be a module"
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.test_presence(klass)
|
20
|
+
define_method "test_presence" do
|
21
|
+
assert_class_defined klass
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'lib/to_pass'
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/helper'
|
2
|
+
|
3
|
+
class TestAlgorithmReader < Test::Unit::TestCase
|
4
|
+
test_presence ToPass::AlgorithmReader
|
5
|
+
|
6
|
+
def test_initialize
|
7
|
+
assert_nothing_raised do
|
8
|
+
klass.new( :basic_de )
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_load
|
13
|
+
assert_respond_to klass, :load
|
14
|
+
assert_kind_of Hash, klass.load(:basic_de)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_has_load_path
|
18
|
+
assert_respond_to reader, :load_path
|
19
|
+
assert_kind_of Array, reader.load_path
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_load_path_contains_standard_dirs
|
23
|
+
dirs = [
|
24
|
+
'~/.to_pass/algorithms' ,
|
25
|
+
"#{File.dirname(__FILE__)}/../lib/algorithms"
|
26
|
+
]
|
27
|
+
|
28
|
+
Pathname.any_instance.expects(:exist?).times(dirs.size).returns(true)
|
29
|
+
|
30
|
+
dirs.each do |reldir|
|
31
|
+
dir = Pathname.new(reldir).expand_path
|
32
|
+
assert( reader.load_path.include?(dir), "#{reader.load_path.inspect} should include #{dir.inspect}" )
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def klass
|
39
|
+
ToPass::AlgorithmReader
|
40
|
+
end
|
41
|
+
def reader(algorithm = :basic_de)
|
42
|
+
@reader ||= klass.new(algorithm)
|
43
|
+
end
|
44
|
+
end
|
data/test/test_base.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/helper'
|
2
|
+
|
3
|
+
class TestBase < Test::Unit::TestCase
|
4
|
+
test_presence ToPass::Base
|
5
|
+
|
6
|
+
def test_usage
|
7
|
+
assert_nothing_raised do
|
8
|
+
pwd = ToPass::Base.new("Da steht ein Pferd auf dem Flur", 'basic_de').to_s
|
9
|
+
assert_equal "Ds1P@dF", pwd
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_version
|
14
|
+
assert_equal Pathname.new("#{File.dirname(__FILE__)}/../VERSION").expand_path.read.chomp, ToPass::VERSION
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_appname
|
18
|
+
assert_equal "to_pass", ToPass::APP_NAME
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/helper'
|
2
|
+
|
3
|
+
class TestConverter < Test::Unit::TestCase
|
4
|
+
def teardown
|
5
|
+
@converter = nil
|
6
|
+
end
|
7
|
+
|
8
|
+
# basic presence and api-testing
|
9
|
+
test_presence ToPass::Converter
|
10
|
+
def test_instantiation
|
11
|
+
assert_nothing_raised do
|
12
|
+
ToPass::Converter.new({})
|
13
|
+
end
|
14
|
+
end
|
15
|
+
def test_convert
|
16
|
+
assert_respond_to converter, :convert
|
17
|
+
assert_nothing_raised do
|
18
|
+
converter.convert( "test" )
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# mock-tests to ensure presence and calling of protected methods
|
23
|
+
def test_replace
|
24
|
+
assert_respond_to converter, :replace
|
25
|
+
converter.expects(:replace).once
|
26
|
+
|
27
|
+
converter.convert("test")
|
28
|
+
end
|
29
|
+
def test_swapcase
|
30
|
+
converter({'word' => ['swapcase']})
|
31
|
+
assert_respond_to converter, :swapcase
|
32
|
+
converter.expects(:swapcase).once
|
33
|
+
converter.convert("test")
|
34
|
+
end
|
35
|
+
def test_first_chars
|
36
|
+
converter({'sentence' => ['first_chars']})
|
37
|
+
assert_respond_to converter, :first_chars
|
38
|
+
converter.expects(:first_chars).once
|
39
|
+
converter.convert("test test")
|
40
|
+
end
|
41
|
+
def test_collapse_chars
|
42
|
+
converter({'sentence' => ['collapse_chars']})
|
43
|
+
assert_respond_to converter, :collapse_chars
|
44
|
+
converter.expects(:collapse_chars).once
|
45
|
+
converter.convert("test test")
|
46
|
+
end
|
47
|
+
|
48
|
+
# more complex/real-life setups
|
49
|
+
def test_multiple_rules
|
50
|
+
converter(basic_rules.merge({
|
51
|
+
'word' => [{'table' => 'special'}, 'swapcase']
|
52
|
+
}))
|
53
|
+
|
54
|
+
assert_equal( "t35T", converter.convert('test'))
|
55
|
+
end
|
56
|
+
def test_sentence
|
57
|
+
converter(complex_rules)
|
58
|
+
assert_equal( "Ds1P@dF", converter.convert("Da steht ein Pferd auf dem Flur"))
|
59
|
+
end
|
60
|
+
def test_sentence_with_whitespace
|
61
|
+
converter(complex_rules)
|
62
|
+
assert_equal( "Ds1P@dF", converter.convert("Da steht ein
|
63
|
+
|
64
|
+
Pferd auf dem Flur"))
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def converter(rules = basic_rules)
|
70
|
+
@converter ||= ToPass::Converter.new(rules)
|
71
|
+
end
|
72
|
+
|
73
|
+
def basic_rules
|
74
|
+
{
|
75
|
+
'word' => [{ 'table' => 'special' }],
|
76
|
+
'tables' => {
|
77
|
+
'special' => {
|
78
|
+
'e' => '3',
|
79
|
+
's' => '5'
|
80
|
+
}
|
81
|
+
}
|
82
|
+
}
|
83
|
+
end
|
84
|
+
def complex_rules
|
85
|
+
{
|
86
|
+
'sentence' => [{'table'=>'words'},'first_chars','collapse_chars',{'table'=>'symbols'}],
|
87
|
+
'tables' => {
|
88
|
+
'words' => { 'ein' => '1' },
|
89
|
+
'symbols' => { 'a' => '@' }
|
90
|
+
}
|
91
|
+
}
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/helper'
|
2
|
+
|
3
|
+
class TestIntegration < Test::Unit::TestCase
|
4
|
+
def test_cli_usage_without_algorithm
|
5
|
+
assert_nothing_raised do
|
6
|
+
assert_equal "t35t", `bin/to_pass test`.chomp
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_cli_usage_with_algorithm
|
11
|
+
assert_nothing_raised do
|
12
|
+
assert_equal "ti1p4u2", `bin/to_pass 'there is one problem for us, too' -a basic_en`.chomp
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_cli_usage_with_pipes
|
17
|
+
assert_nothing_raised do
|
18
|
+
assert_equal 't35t', `echo "test" | bin/to_pass`
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_module_integration
|
23
|
+
assert_nothing_raised do
|
24
|
+
str = "test"
|
25
|
+
str.instance_eval "class << self; include ToPass::Integration; end"
|
26
|
+
|
27
|
+
assert_equal 't35t', str.to_pass
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/helper'
|
2
|
+
|
3
|
+
class TestStringConversion < Test::Unit::TestCase
|
4
|
+
def test_replacement
|
5
|
+
result = converter.replace("test", {
|
6
|
+
:e => 3,
|
7
|
+
:s => 5
|
8
|
+
})
|
9
|
+
assert_equal "t35t", result
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_case_swapping
|
13
|
+
assert_equal "tEsT", converter.swapcase("test")
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_case_swapping_ignores_numbers
|
17
|
+
assert_equal "tEsT4fUn", converter.swapcase("test4fun")
|
18
|
+
|
19
|
+
assert_equal "fUn4TeSt", converter.swapcase("fun4test")
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_char_collapsing
|
23
|
+
assert_equal "abc", converter.collapse_chars("a b c")
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_select_first_chars
|
27
|
+
assert_equal "t a t f t", converter.first_chars('test all the fucking time')
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def converter
|
33
|
+
klass = Class.new
|
34
|
+
klass.send(:extend, ToPass::StringConversions)
|
35
|
+
klass
|
36
|
+
end
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: to_pass
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 19
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
- 2
|
10
|
+
version: 0.2.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Matthias Viehweger
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-06-13 00:00:00 +02:00
|
19
|
+
default_executable: to_pass
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: mocha
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: sdoc
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
description: |-
|
50
|
+
Passwords should be easy to remember and hard to guess.
|
51
|
+
One technique is to have a sentence which can be easily remembered transformed to a password.
|
52
|
+
email: kronn@kronn.de
|
53
|
+
executables:
|
54
|
+
- to_pass
|
55
|
+
extensions: []
|
56
|
+
|
57
|
+
extra_rdoc_files:
|
58
|
+
- LICENSE
|
59
|
+
- README.rdoc
|
60
|
+
- TODO
|
61
|
+
files:
|
62
|
+
- .gitignore
|
63
|
+
- LICENSE
|
64
|
+
- README.rdoc
|
65
|
+
- Rakefile
|
66
|
+
- TODO
|
67
|
+
- VERSION
|
68
|
+
- bin/to_pass
|
69
|
+
- lib/algorithms/basic_de.yml
|
70
|
+
- lib/algorithms/basic_en.yml
|
71
|
+
- lib/to_pass.rb
|
72
|
+
- lib/to_pass/algorithm_reader.rb
|
73
|
+
- lib/to_pass/base.rb
|
74
|
+
- lib/to_pass/converter.rb
|
75
|
+
- lib/to_pass/integration.rb
|
76
|
+
- lib/to_pass/string_conversions.rb
|
77
|
+
- test/helper.rb
|
78
|
+
- test/test_algorithm_reader.rb
|
79
|
+
- test/test_base.rb
|
80
|
+
- test/test_converter.rb
|
81
|
+
- test/test_integration.rb
|
82
|
+
- test/test_string_conversions.rb
|
83
|
+
has_rdoc: true
|
84
|
+
homepage: http://github.com/kronn/to_pass
|
85
|
+
licenses: []
|
86
|
+
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options:
|
89
|
+
- --charset=UTF-8
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
hash: 3
|
98
|
+
segments:
|
99
|
+
- 0
|
100
|
+
version: "0"
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
hash: 3
|
107
|
+
segments:
|
108
|
+
- 0
|
109
|
+
version: "0"
|
110
|
+
requirements: []
|
111
|
+
|
112
|
+
rubyforge_project:
|
113
|
+
rubygems_version: 1.3.7
|
114
|
+
signing_key:
|
115
|
+
specification_version: 3
|
116
|
+
summary: generate password from words or sentences
|
117
|
+
test_files:
|
118
|
+
- test/helper.rb
|
119
|
+
- test/test_algorithm_reader.rb
|
120
|
+
- test/test_base.rb
|
121
|
+
- test/test_converter.rb
|
122
|
+
- test/test_integration.rb
|
123
|
+
- test/test_string_conversions.rb
|