sifar 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document CHANGED
@@ -1,5 +1,5 @@
1
- README.rdoc
2
1
  lib/**/*.rb
3
2
  bin/*
3
+ -
4
4
  features/**/*.feature
5
- LICENSE
5
+ LICENSE.txt
File without changes
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Suman Debnath
1
+ Copyright (c) 2011 Suman Debnath
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -2,32 +2,103 @@
2
2
 
3
3
  Sifar can be used to check for strong passwords. Apart from the standard tests for length and homogeneity, it can check passwords that sound and spell similar to a given word.
4
4
 
5
- Sifar can also generate passwords that satisfy the same criteria.
5
+ Sifar can also generate passwords that satisfy the given criteria.
6
+
7
+ == Upgrade Notes
8
+
9
+ Version 0.2.0 is a complete rewrite. Please check usage before upgrading.
6
10
 
7
11
  == Installation
8
12
 
9
13
  === Requirements
10
14
 
11
- You need english-0.4.0. This is installed automatically if ActiveMerchantCcavenue is installed as a gem.
15
+ You need the text gem. This is installed automatically if Sifar is installed as a gem. More information on text can be found at https://github.com/threedaymonk/text.
12
16
 
13
- More information on english can be found at http://rubyworks.github.com/english.
17
+ Sifar has been tested only on *nix systems.
14
18
 
15
- === As a gem (recommended)
19
+ === As a gem
16
20
 
17
21
  Install the gem:
18
22
 
19
23
  > sudo gem install sifar
20
24
 
21
- To use the Sifar gem in a Rails application, add the following line in your environment.rb:
25
+ To use the Sifar gem with bundler, add the following line in your Gemfile:
22
26
 
23
- config.gem 'sifar'
27
+ gem 'sifar'
24
28
 
25
29
  === As a Rails plugin
26
30
 
27
31
  To add Sifar as a plugin in a Rails application, run the following command from your application root:
28
32
 
29
33
  > ./script/plugin install git@github.com:meshbrain/sifar.git
34
+
35
+ == Usage
36
+ === Validate
37
+
38
+ require 'sifar'
39
+ checker = Sifar.new ...
40
+ p checker.errors unless checker.check(word)
41
+
42
+ === Generate
43
+
44
+ require 'sifar'
45
+ checker = Sifar.new ...
46
+ password = checker.generate
47
+
48
+ === Checks
49
+
50
+ Checks can be used separately or in combination.
51
+
52
+ ==== Minimum length
53
+
54
+ Password should be at least x characters long.
55
+
56
+ checker = Sifar.new :minimum_length => 8
57
+ checker.check('pword') # => false
58
+ checker.check('password') # => true
59
+ checker.check('longpassword') # => true
60
+
61
+ ==== Heterogeneous passwords
62
+
63
+ Password should contain a mix of digits, uppercase and lowercase characters.
64
+
65
+ checker = Sifar.new :heterogeneous => true
66
+ checker.check('password') # => false
67
+ checker.check('Pa55w0rD') # => true
68
+
69
+ ==== Dictionary passwords
70
+
71
+ Password should not contain any word from a given file.
72
+
73
+ checker = Sifar.new :dictionary => '/path/to/dictionary'
74
+ checker.check('indictionary') # => false
75
+
76
+ ==== Reject specific characters
77
+
78
+ Password should not contain any character from a given set.
79
+
80
+ checker = Sifar.new :character_blacklist => %w(& % $)
81
+ checker.check('pass%word') # => false
82
+
83
+ ==== Passwords that spell similar to a given name
84
+
85
+ Levenshtein distance of two words should be more than a given threshold. :name is mandatory.
86
+
87
+ checker = Sifar.new :similarity => 1, :name => 'shoeman'
88
+ checker.check('showman') # => false
89
+ checker.check('anothershowman') # => false
90
+ checker.check('password') # => true
91
+
92
+ ==== Words that sound similar to a given name
93
+
94
+ Phonetic similarity of two words should be more than a given threshold. :name is mandatory.
95
+
96
+ checker = Sifar.new :phonetic_similarity => 1, :name => 'suman'
97
+ checker.check('showman') # => false
98
+ checker.check('password') # => true
99
+
100
+ NOTE: This check uses metaphone; and might not work as expected in all languages.
30
101
 
31
102
  == Copyright
32
103
 
33
- Copyright (c) 2010 Suman Debnath. See LICENSE for details.
104
+ Copyright (c) 2011 Suman Debnath. See LICENSE for details.
data/Rakefile CHANGED
@@ -1,37 +1,34 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
3
 
4
- begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
7
- gem.name = 'sifar'
8
- gem.summary = %Q{A library to generate strong passwords and check password strength.}
9
- gem.description = %Q{Sifar can be used to check for strong passwords. Apart from the standard tests for length and homogeneity, it can check passwords that sound and spell similar to a given word. Sifar can also generate passwords that satisfy the same criteria.}
10
- gem.email = 'contact@meshbrain.com'
11
- gem.homepage = 'http://github.com/meshbrain/sifar'
12
- gem.authors = ['Suman Debnath']
13
- gem.add_development_dependency 'rspec', '~> 1.2'
14
- gem.add_dependency 'english', '= 0.4.0'
15
- end
16
- Jeweler::GemcutterTasks.new
17
- rescue LoadError
18
- puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gem|
6
+ gem.name = 'sifar'
7
+ gem.homepage = 'http://github.com/meshbrain/sifar'
8
+ gem.license = 'MIT'
9
+ gem.summary = %Q{A library to generate strong passwords and check password strength.}
10
+ gem.description = %Q{Sifar can be used to check for strong passwords. Apart from the standard tests for length and homogeneity, it can check passwords that sound and spell similar to a given word. Sifar can also generate passwords that satisfy the same criteria.}
11
+ gem.email = 'contact@meshbrain.com'
12
+ gem.authors = ['Suman Debnath']
13
+ gem.add_dependency 'text', '~> 0.2.0'
14
+ gem.add_development_dependency 'rspec', '~> 2.3.0'
15
+ #gem.add_development_dependency 'bundler', '~> 1.0.0'
16
+ gem.add_development_dependency 'jeweler', '~> 1.5.2'
17
+ gem.add_development_dependency 'rcov', '>= 0'
19
18
  end
19
+ Jeweler::RubygemsDotOrgTasks.new
20
20
 
21
- require 'spec/rake/spectask'
22
- Spec::Rake::SpecTask.new(:spec) do |spec|
23
- spec.libs << 'lib' << 'spec'
24
- spec.spec_files = FileList['spec/**/*_spec.rb']
21
+ require 'rspec/core'
22
+ require 'rspec/core/rake_task'
23
+ RSpec::Core::RakeTask.new(:spec) do |spec|
24
+ spec.pattern = FileList['spec/**/*_spec.rb']
25
25
  end
26
26
 
27
- Spec::Rake::SpecTask.new(:rcov) do |spec|
28
- spec.libs << 'lib' << 'spec'
27
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
29
28
  spec.pattern = 'spec/**/*_spec.rb'
30
29
  spec.rcov = true
31
30
  end
32
31
 
33
- task :spec => :check_dependencies
34
-
35
32
  task :default => :spec
36
33
 
37
34
  require 'rake/rdoctask'
@@ -39,7 +36,7 @@ Rake::RDocTask.new do |rdoc|
39
36
  version = File.exist?('VERSION') ? File.read('VERSION') : ''
40
37
 
41
38
  rdoc.rdoc_dir = 'rdoc'
42
- rdoc.title = "sifar #{version}"
39
+ rdoc.title = 'sifar #{version}'
43
40
  rdoc.rdoc_files.include('README*')
44
41
  rdoc.rdoc_files.include('lib/**/*.rb')
45
42
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -1,170 +1,132 @@
1
1
  require 'rubygems'
2
- gem 'english', '= 0.4.0'
2
+ require 'text'
3
+ require 'digest'
3
4
 
4
5
  class Sifar
5
- attr_accessor :errors
6
-
7
- def initialize
8
- @checks = {
9
- 'L' => :check_length,
10
- 'D' => :check_dictionary,
11
- 'H' => :check_heterogeneity,
12
- 'W' => :check_word_blacklist,
13
- 'C' => :check_char_blacklist,
14
- 'P' => :check_phonetic,
15
- 'T' => :check_similarity,
16
- 'S' => :check_string
17
- }
6
+ attr_accessor :errors
7
+
8
+ def initialize(options = {}, error_messages = {})
9
+ set_opt options, error_messages
10
+ end
11
+
12
+ def check(word)
13
+ @errors = []
14
+
15
+ check_length(word) unless @options[:minimum_length].nil?
16
+ check_dictionary(word) unless @options[:dictionary].nil?
17
+ check_character_blacklist(word) unless @options[:character_blacklist].nil?
18
+ check_phonetic(word, @options[:name]) unless @options[:phonetic_similarity].nil?
19
+ check_similarity(word, @options[:name]) unless @options[:similarity].nil?
20
+
21
+ check_heterogeneity(word) if true == @options[:heterogeneous]
22
+
23
+ 1 > @errors.length
24
+ end
25
+
26
+ def generate
27
+ n = 0
28
+ begin
29
+ n += 1
30
+ word = create
31
+ end until true == check(word)
32
+ word
33
+ end
34
+
35
+ private
36
+ def create
37
+ max = @options[:minimum_length] || 8
38
+ seed = (0..max).map{|i| (32 + rand(126-32)).chr}.join
39
+ Digest::SHA1.hexdigest(seed)[0..max]
40
+ end
41
+
42
+ def check_length(word)
43
+ @errors << @error_messages[:length] if word.length < @options[:minimum_length].to_i
44
+ end
45
+
46
+ def check_heterogeneity(word)
47
+ num_chars = word.length
48
+ num = {}
49
+ num[:upper] = word.gsub(/[^[:upper:]]/, '').length
50
+ num[:lower] = word.gsub(/[^[:lower:]]/, '').length
51
+ num[:digit] = word.gsub(/[^[:digit:]]/, '').length
52
+ num[:special] = num_chars - (num[:upper] + num[:lower] + num[:digit])
53
+
54
+ max = 4 > num_chars ? 3 : num_chars - 3
55
+ num.each_value do |value|
56
+ if value > max
57
+ @errors << @error_messages[:heter]
58
+ return false
59
+ end
18
60
  end
19
61
 
20
- def check_password(password, name = '', options = {}, error_messages = {})
21
- self.set_opt options, error_messages
22
- @errors = []
62
+ true
63
+ end
23
64
 
24
- self.check_strength(password)
25
- self.check_against_name(password, name) unless name.empty?
26
-
27
- 1 > @errors.length
28
- end
29
-
30
- def generate_password(name = '', options = {}, error_messages = {})
31
- self.set_opt options, error_messages
32
- end
33
-
34
- def check_strength(password)
35
- @options[:password_check_type].each(''){|option| self.send(@checks[option], password)}
36
- end
37
-
38
- def check_against_name(password, name)
39
- @options[:name_check_type].each(''){|option| self.send(@checks[option], password, name)}
40
- end
41
-
42
- def check_length(word)
43
- @errors << @error_messages[:length] if word.length < @options[:minimum_length]
65
+ def check_dictionary(word)
66
+ if File.readable? @options[:dictionary]
67
+ @errors << @error_messages[:dict] if find_word_in_file(word, @options[:dictionary])
68
+ else
69
+ @errors << @error_messages[:config_blackword]
44
70
  end
71
+ end
45
72
 
46
- def check_heterogeneity(word)
47
- num_chars = word.length
48
- num = {}
49
- num[:upper] = word.gsub(/[^[:upper:]]/, '').length
50
- num[:lower] = word.gsub(/[^[:lower:]]/, '').length
51
- num[:digit] = word.gsub(/[^[:digit:]]/, '').length
52
- num[:special] = num_chars - (num[:upper] + num[:lower] + num[:digit])
53
-
54
- max = 4 > num_chars ? 3 : num_chars - 3
55
- num.each do |type, value|
56
- if value > max
57
- @errors << @error_messages[:heter]
58
- return false
59
- end
60
- end
61
-
62
- true
63
- end
64
-
65
- def check_dictionary(word)
66
- if File.readable? @options[:dictionary]
67
- @errors << @error_messages[:dict] if find_word_in_file(word, @options[:dictionary])
68
- else
69
- @errors << @error_messages[:config_blackword]
70
- end
71
- end
73
+ def check_character_blacklist(word)
74
+ pattern = /#{@options[:character_blacklist].map{|c| Regexp.escape c}.join('|')}/i
75
+ @errors << @error_messages[:config_blackword] unless pattern.match(word).nil?
76
+ end
72
77
 
73
- def check_word_blacklist(word)
74
- if File.readable? @options[:word_blacklist]
75
- @errors << @error_messages[:blackword] if find_word_in_file(word, @options[:word_blacklist])
76
- else
77
- @errors << @error_messages[:config_dict]
78
- end
78
+ def check_phonetic(word, name)
79
+ unless @options[:phonetic_similarity] < Text::Levenshtein.distance(Text::Metaphone.metaphone(word), Text::Metaphone.metaphone(name))
80
+ @errors << @error_messages[:phonetic]
81
+ return false
79
82
  end
80
-
81
- def check_char_blacklist(word)
82
- if File.readable? @options[:char_blacklist]
83
- File.open(@options[:char_blacklist]) do |f|
84
- f.readlines.each do |char|
85
- char = char.strip
86
- if not(char.empty?) and word.include?(char)
87
- @errors << @error_messages[:blackchar]
88
- return false
89
- end
90
- end
91
- end
92
- else
93
- @errors << @error_messages[:config_blackchar]
94
- return false
95
- end
96
- true
97
- end
98
-
99
- def check_phonetic(password, name)
100
- require 'english/levenshtein'
101
- require 'english/soundex'
102
- unless 2 < English::Levenshtein.distance(password.soundex, name.soundex)
103
- @errors << @error_messages[:phonetic]
104
- return false
105
- end
106
- true
107
- end
108
-
109
- def check_similarity(password, name)
110
- require 'english/similarity'
111
- if 0.8 < password.similarity(name)
112
- @errors << @error_messages[:similar]
113
- return false
114
- end
115
- true
116
- end
117
-
118
- def check_string(password, name)
119
- longer, shorter = password.size > name.size ? [password, name] : [name, password]
120
- if longer.include?(shorter)
121
- @errors << @error_messages[:similar]
122
- return false
123
- end
124
- true
125
- end
126
-
127
- def find_word_in_file(word, file)
128
- t = []
129
- IO.popen("#{@options[:grep_path]} -ixs '#{word}' #{file}") {|io|
130
- r = io.readlines
131
- t << r unless r.empty?
132
- }
133
- not t.empty?
134
- end
135
-
136
- def set_opt(options = {}, error_messages = {})
137
- @options = {
138
- :minimum_length => 8,
139
- :password_check_type => 'LD',
140
- :name_check_type => 'S',
141
- :dictionary => '',
142
- :word_blacklist => '',
143
- :char_blacklist => '',
144
- :error_msg_hash => {},
145
- :umlaut_hash => {},
146
- :grep_path => '/bin/grep'
147
- }.merge options
148
- @options[:minimum_length] = 1 > @options[:minimum_length] ? 8 : @options[:minimum_length]
149
- @options[:password_check_type] = @options[:password_check_type].gsub(/[^DLHWC]/, '').split('').sort.join
150
- @options[:name_check_type] = @options[:name_check_type].gsub(/[^PTS]/, '').split('').sort.join
151
-
152
- raise ArgumentError, 'grep not found' unless (File.stat(@options[:grep_path]).executable? rescue false)
153
-
154
- @error_messages = {
155
- :length => 'The password is too short!',
156
- :similar => 'The password is too similar to the given name!',
157
- :phonetic => 'The password sounds too similar to the given name!',
158
- :dict => 'The password is based upon a dictionary word!',
159
- :heter => 'The password is too homogeneous!',
160
- :blackword => 'The password is based upon a blacklsited word!',
161
- :blackchar => 'The password contains a blacklisted character!',
162
- :similar_looking_chars => 'The password contains similar looking characters!',
163
- :config_blackchar => 'Could not load a valid blacklisted character file!',
164
- :config_blackword => 'Could not load a valid blacklisted word file!',
165
- :config_dict => 'Could not load a valid dictionary file!',
166
- :config_heter => 'Conflict in heterogeneity check and password generation configuration!',
167
- :config_length => 'Generated password length cannot be less then specified minimum length!'
168
- }.merge error_messages
83
+ true
84
+ end
85
+
86
+ def check_similarity(word, name)
87
+ longer, shorter = [word, name].sort{|x,y| y.size <=> x.size}
88
+ unless !longer.include?(shorter) and @options[:similarity] < Text::Levenshtein.distance(longer, shorter)
89
+ @errors << @error_messages[:similar]
90
+ return false
169
91
  end
92
+ true
93
+ end
94
+
95
+ def find_word_in_file(word, file)
96
+ t = []
97
+ IO.popen("#{@options[:grep_path]} -ixs '#{word}' #{file}") {|io|
98
+ r = io.readlines
99
+ t << r unless r.empty?
100
+ }
101
+ not t.empty?
102
+ end
103
+
104
+ def set_opt(options = {}, error_messages = {})
105
+ @options = {
106
+ :password_check_type => 'LD',
107
+ :name_check_type => 'S',
108
+ :error_msg_hash => {},
109
+ :umlaut_hash => {},
110
+ :grep_path => `which grep`.chomp
111
+ }.merge options
112
+
113
+ raise ArgumentError, 'grep not found' unless (File.stat(@options[:grep_path]).executable? rescue false)
114
+
115
+ @error_messages = {
116
+ :length => 'The password is too short!',
117
+ :similar => 'The password is too similar to the given name!',
118
+ :phonetic => 'The password sounds too similar to the given name!',
119
+ :dict => 'The password is based upon a dictionary word!',
120
+ :heter => 'The password is too homogeneous!',
121
+ :blackword => 'The password is based upon a blacklsited word!',
122
+ :blackchar => 'The password contains a blacklisted character!',
123
+ :similar_looking_chars => 'The password contains similar looking characters!',
124
+ :config_blackchar => 'Could not load a valid blacklisted character file!',
125
+ :config_blackword => 'Could not load a valid blacklisted word file!',
126
+ :config_dict => 'Could not load a valid dictionary file!',
127
+ :config_heter => 'Conflict in heterogeneity check and password generation configuration!',
128
+ :config_length => 'Generated password length cannot be less then specified minimum length!'
129
+ }.merge error_messages
130
+ @errors ||= []
131
+ end
170
132
  end
@@ -1,43 +1,41 @@
1
1
  # Generated by jeweler
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{sifar}
8
- s.version = "0.1.0"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Suman Debnath"]
12
- s.date = %q{2010-10-01}
12
+ s.date = %q{2011-01-28}
13
13
  s.description = %q{Sifar can be used to check for strong passwords. Apart from the standard tests for length and homogeneity, it can check passwords that sound and spell similar to a given word. Sifar can also generate passwords that satisfy the same criteria.}
14
14
  s.email = %q{contact@meshbrain.com}
15
15
  s.extra_rdoc_files = [
16
- "LICENSE",
17
- "README.rdoc"
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
18
  ]
19
19
  s.files = [
20
20
  ".document",
21
- ".gitignore",
22
- "LICENSE",
23
- "README.rdoc",
24
- "Rakefile",
25
- "VERSION",
26
- "init.rb",
27
- "lib/sifar.rb",
28
- "sifar.gemspec",
29
- "spec/sifar_spec.rb",
30
- "spec/spec.opts",
31
- "spec/spec_helper.rb"
21
+ ".rspec",
22
+ "LICENSE.txt",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/sifar.rb",
27
+ "sifar.gemspec",
28
+ "spec/sifar_spec.rb",
29
+ "spec/spec_helper.rb"
32
30
  ]
33
31
  s.homepage = %q{http://github.com/meshbrain/sifar}
34
- s.rdoc_options = ["--charset=UTF-8"]
32
+ s.licenses = ["MIT"]
35
33
  s.require_paths = ["lib"]
36
34
  s.rubygems_version = %q{1.3.7}
37
35
  s.summary = %q{A library to generate strong passwords and check password strength.}
38
36
  s.test_files = [
39
- "spec/spec_helper.rb",
40
- "spec/sifar_spec.rb"
37
+ "spec/sifar_spec.rb",
38
+ "spec/spec_helper.rb"
41
39
  ]
42
40
 
43
41
  if s.respond_to? :specification_version then
@@ -45,15 +43,21 @@ Gem::Specification.new do |s|
45
43
  s.specification_version = 3
46
44
 
47
45
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
48
- s.add_development_dependency(%q<rspec>, ["~> 1.2"])
49
- s.add_runtime_dependency(%q<english>, ["= 0.4.0"])
46
+ s.add_runtime_dependency(%q<text>, ["~> 0.2.0"])
47
+ s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
48
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
49
+ s.add_development_dependency(%q<rcov>, [">= 0"])
50
50
  else
51
- s.add_dependency(%q<rspec>, ["~> 1.2"])
52
- s.add_dependency(%q<english>, ["= 0.4.0"])
51
+ s.add_dependency(%q<text>, ["~> 0.2.0"])
52
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
53
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
54
+ s.add_dependency(%q<rcov>, [">= 0"])
53
55
  end
54
56
  else
55
- s.add_dependency(%q<rspec>, ["~> 1.2"])
56
- s.add_dependency(%q<english>, ["= 0.4.0"])
57
+ s.add_dependency(%q<text>, ["~> 0.2.0"])
58
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
59
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
60
+ s.add_dependency(%q<rcov>, [">= 0"])
57
61
  end
58
62
  end
59
63
 
@@ -1,7 +1,108 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
- describe "Sifar" do
4
- it "fails" do
5
- fail "hey buddy, you should probably rename this file and start specing for real"
3
+ describe Sifar, 'when testing for minimum length' do
4
+ it 'accepts words equal or bigger than given length' do
5
+ checker = Sifar.new :minimum_length => 8
6
+
7
+ checker.check(password(8)).should == true
8
+ checker.check(password(10)).should == true
9
+ end
10
+
11
+ it 'rejects words smaller than given length' do
12
+ checker = Sifar.new :minimum_length => 8
13
+
14
+ checker.check(password(7)).should == false
15
+ end
16
+ end
17
+
18
+ describe Sifar, 'when testing for heterogeneity' do
19
+ it 'accepts heterogeneous words' do
20
+ checker = Sifar.new :heterogeneous => true
21
+
22
+ checker.check('aA34zX9j').should == true
23
+ end
24
+
25
+ it 'rejects homogeneous words' do
26
+ checker = Sifar.new :heterogeneous => true
27
+
28
+ checker.check(password(10)).should == false
6
29
  end
7
30
  end
31
+
32
+ describe Sifar, 'when testing for dictionary words' do
33
+ it 'rejects words in dictionary' do
34
+ word = password(10)
35
+
36
+ temporary_file(word) do |dictionary|
37
+ checker = Sifar.new :dictionary => dictionary
38
+ checker.check(word).should == false
39
+ end
40
+ end
41
+
42
+ it 'accepts words not in dictionary' do
43
+ temporary_file('indictionary') do |dictionary|
44
+ checker = Sifar.new :dictionary => dictionary
45
+ checker.check('notindictionary').should == true
46
+ end
47
+ end
48
+ end
49
+
50
+ describe Sifar, 'when testing for blacklisted characters' do
51
+ it 'rejects words with blacklisted characters' do
52
+ checker = Sifar.new :character_blacklist => %w(& % $)
53
+ checker.check('blacklisted&character').should == false
54
+ end
55
+
56
+ it 'accepts words without blacklisted characters' do
57
+ checker = Sifar.new :character_blacklist => %w(& % $)
58
+ checker.check('blacklistedcharacter').should == true
59
+ end
60
+ end
61
+
62
+ describe Sifar, 'when testing for similar spelling' do
63
+ it 'rejects words with similarity equal or more than the given threshold' do
64
+ checker = Sifar.new :similarity => 1, :name => 'shoeman'
65
+ checker.check('showman').should == false
66
+ checker.check('anothershoeman').should == false
67
+ end
68
+
69
+ it 'accepts words with similarity less than the given threshold' do
70
+ checker = Sifar.new :similarity => 1, :name => 'password'
71
+ checker.check('pa55word').should == true
72
+ end
73
+ end
74
+
75
+ describe Sifar, 'when testing for similar pronounciation' do
76
+ it 'rejects words with phonetic similarity equal or more than the given threshold' do
77
+ checker = Sifar.new :phonetic_similarity => 1, :name => 'suman'
78
+ checker.check('showman').should == false
79
+ end
80
+
81
+ it 'accepts words with phonetic similarity less than the given threshold' do
82
+ checker = Sifar.new :phonetic_similarity => 1, :name => 'password'
83
+ checker.check('firstpassword').should == true
84
+ end
85
+ end
86
+
87
+ describe Sifar, 'when generating passwords' do
88
+ it 'passes all checks' do
89
+ temporary_file('indictionary') do |dictionary|
90
+ checks = {
91
+ :minimum_length => 8,
92
+ :dictionary => dictionary,
93
+ :character_blacklist => %w(& % $),
94
+ :phonetic_similarity => 2,
95
+ :similarity => 2
96
+ }
97
+
98
+ c = checks.keys
99
+ (1..c.size).each do |n|
100
+ c.combination(n).each do |options|
101
+ checker = Sifar.new checks.reject{|key, value| !options.include?(key)}.merge({:name => 'suman'})
102
+ password = checker.generate
103
+ checker.check(password).should == true
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -1,9 +1,23 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__))
2
1
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
3
4
  require 'sifar'
4
- require 'spec'
5
- require 'spec/autorun'
5
+ require 'tmpdir'
6
6
 
7
- Spec::Runner.configure do |config|
8
-
7
+ module SifarHelperMethods
8
+ def password(num)
9
+ (0...num).map{ ('a'..'z').to_a[rand(26)] }.join
10
+ end
11
+
12
+ def temporary_file(text, &block)
13
+ temp_file = File.join(Dir.tmpdir, 'sifar_temporary_file')
14
+ open(temp_file, 'w'){ |f| f.write text }
15
+ block.call temp_file
16
+ File.delete temp_file
17
+ end
9
18
  end
19
+
20
+ RSpec.configure do |config|
21
+ config.before(:each) {}
22
+ include SifarHelperMethods
23
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sifar
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Suman Debnath
@@ -15,40 +15,71 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-01 00:00:00 +05:30
18
+ date: 2011-01-28 00:00:00 +05:30
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- name: rspec
22
+ name: text
23
23
  prerelease: false
24
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
25
  none: false
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- hash: 11
29
+ hash: 23
30
30
  segments:
31
- - 1
31
+ - 0
32
32
  - 2
33
- version: "1.2"
34
- type: :development
33
+ - 0
34
+ version: 0.2.0
35
+ type: :runtime
35
36
  version_requirements: *id001
36
37
  - !ruby/object:Gem::Dependency
37
- name: english
38
+ name: rspec
38
39
  prerelease: false
39
40
  requirement: &id002 !ruby/object:Gem::Requirement
40
41
  none: false
41
42
  requirements:
42
- - - "="
43
+ - - ~>
43
44
  - !ruby/object:Gem::Version
44
- hash: 15
45
+ hash: 3
45
46
  segments:
47
+ - 2
48
+ - 3
46
49
  - 0
47
- - 4
48
- - 0
49
- version: 0.4.0
50
- type: :runtime
50
+ version: 2.3.0
51
+ type: :development
51
52
  version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: jeweler
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ hash: 7
62
+ segments:
63
+ - 1
64
+ - 5
65
+ - 2
66
+ version: 1.5.2
67
+ type: :development
68
+ version_requirements: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ name: rcov
71
+ prerelease: false
72
+ requirement: &id004 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ hash: 3
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ type: :development
82
+ version_requirements: *id004
52
83
  description: Sifar can be used to check for strong passwords. Apart from the standard tests for length and homogeneity, it can check passwords that sound and spell similar to a given word. Sifar can also generate passwords that satisfy the same criteria.
53
84
  email: contact@meshbrain.com
54
85
  executables: []
@@ -56,28 +87,26 @@ executables: []
56
87
  extensions: []
57
88
 
58
89
  extra_rdoc_files:
59
- - LICENSE
90
+ - LICENSE.txt
60
91
  - README.rdoc
61
92
  files:
62
93
  - .document
63
- - .gitignore
64
- - LICENSE
94
+ - .rspec
95
+ - LICENSE.txt
65
96
  - README.rdoc
66
97
  - Rakefile
67
98
  - VERSION
68
- - init.rb
69
99
  - lib/sifar.rb
70
100
  - sifar.gemspec
71
101
  - spec/sifar_spec.rb
72
- - spec/spec.opts
73
102
  - spec/spec_helper.rb
74
103
  has_rdoc: true
75
104
  homepage: http://github.com/meshbrain/sifar
76
- licenses: []
77
-
105
+ licenses:
106
+ - MIT
78
107
  post_install_message:
79
- rdoc_options:
80
- - --charset=UTF-8
108
+ rdoc_options: []
109
+
81
110
  require_paths:
82
111
  - lib
83
112
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -106,5 +135,5 @@ signing_key:
106
135
  specification_version: 3
107
136
  summary: A library to generate strong passwords and check password strength.
108
137
  test_files:
109
- - spec/spec_helper.rb
110
138
  - spec/sifar_spec.rb
139
+ - spec/spec_helper.rb
data/.gitignore DELETED
@@ -1,24 +0,0 @@
1
- ## MAC OS
2
- .DS_Store
3
-
4
- ## TEXTMATE
5
- *.tmproj
6
- tmtags
7
-
8
- ## EMACS
9
- *~
10
- \#*
11
- .\#*
12
-
13
- ## VIM
14
- *.swp
15
-
16
- ## Netbeans
17
- nbproject
18
-
19
- ## PROJECT::GENERAL
20
- coverage
21
- rdoc
22
- pkg
23
-
24
- ## PROJECT::SPECIFIC
data/init.rb DELETED
@@ -1 +0,0 @@
1
- require 'sifar'