sifar 0.1.0 → 0.2.0

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/.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'