passrock 0.0.5 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7b901d76f9ebf5e7aeae7fac88edbba47f632bb6
4
- data.tar.gz: c0102ec9aaaaa8f4536c1787187604140cad70e3
3
+ metadata.gz: ed1e2aff3b96416160e88dde57f911e727895521
4
+ data.tar.gz: dc7046386fc7a537d6a1b73594079cae40e735f4
5
5
  SHA512:
6
- metadata.gz: d7c54fa479abe10d5993a1dd3da3190e7b0ad4e421fa0ed6db25e2d27ac4e2c0ea4aa0b68b707495a16dde840ad327b640f7fd701bee9cb01216a2f6b29aa8b3
7
- data.tar.gz: 44b468162e89408c5fa95ed1af2714611334a18b3997702d6a677727cca0ddf8466f48c49bd07bda0d945353580f4fe79802782aac4f21c4b3cf5e6d874470a4
6
+ metadata.gz: e1a113b26a7a606f42058d644ff1a5d005838dd3729d8ecd2fb759d779da24d8477218d5f5b75f2c9def4800b2c0d00a1d780b337858090d2587ab88e6f69efe
7
+ data.tar.gz: cda555c54b742e2cb80d4b22db94549b2b044c0c2fb5173da3eac6de7bee395105f7ef1e4516c6dfa5a32df755d7118f707adf689e1c061d43cb78e5385ca505
@@ -1,2 +1,3 @@
1
- PASSROCK_PASSWORD_DB=/path/to/passrock/testPRbinary.dat
1
+ PASSROCK_PASSWORD_DB=/path/to/PRbinary_Directory/
2
+ PASSROCK_PASSWORD_DB_FILE=/path/to/PRbinary.dat
2
3
  PASSROCK_PRIVATE_KEY=xxx
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Ruby client library for programmatic access to the [Passrock Binary Database](https://www.passrock.com/demo.php).
4
4
 
5
+ This library adheres to [SemVer](http://semver.org). Pre v1.0.0 is considered alpha level software.
6
+
5
7
 
6
8
  ## Installation
7
9
 
@@ -18,34 +20,38 @@ And then execute:
18
20
 
19
21
  ### Plain Ol' Ruby (PORO)
20
22
 
21
- require 'passrock'
22
-
23
- passrock_db = Passrock::PasswordDb.new('/path/to/passrock/binary.dat', 'your private key')
24
- passrock_db.secure?('password') # => false
25
- passrock_db.insecure?('av3r^securePass') # => false
23
+ ```ruby
24
+ require 'passrock'
26
25
 
26
+ passrock_db = Passrock::PasswordDb.new(:password_db => '/path/to/passrock_db_dir', :private_key => 'your private key')
27
+ passrock_db.secure?('password') # => false
28
+ passrock_db.insecure?('PASSWORD') # => true
29
+ ```
27
30
 
28
31
  ### Ruby on Rails
29
32
 
30
33
  This library provides a custom ActiveModel validation:
31
34
 
32
- # Configure: config/initializers/passrock.rb
33
- Passrock.configure do |config|
34
- config.password_db = '/path/to/passrock/binary.dat'
35
- config.private_key = 'your private key'
36
- end
37
-
38
- # Model
39
- # e.g. app/models/user.rb
40
- validates :password, :passrock_secure => true
41
-
42
- # Customize the error message (see: http://guides.rubyonrails.org/i18n.html#error-message-scopes)
43
- # e.g. config/locales/en.yml
44
- activerecord:
45
- errors:
46
- messages:
47
- passrock_secure: "appears to be a commonly used password"
48
-
35
+ ```ruby
36
+ # Configure: config/initializers/passrock.rb
37
+ Passrock.configure do |config|
38
+ config.password_db = '/path/to/passrock_db_dir'
39
+ config.private_key = 'your private key'
40
+ end
41
+
42
+ # Model
43
+ # e.g. app/models/user.rb
44
+ validates :password, :passrock_secure => true
45
+ ```
46
+
47
+ ```yaml
48
+ # Customize the error message (see: http://guides.rubyonrails.org/i18n.html#error-message-scopes)
49
+ # e.g. config/locales/en.yml
50
+ activerecord:
51
+ errors:
52
+ messages:
53
+ passrock_secure: "appears to be a commonly used password"
54
+ ```
49
55
 
50
56
  ## Contributing
51
57
 
@@ -56,7 +62,7 @@ This library provides a custom ActiveModel validation:
56
62
  5. Create new Pull Request
57
63
 
58
64
 
59
- # Specs
65
+ ## Specs
60
66
 
61
67
  To run the spec suite:
62
68
 
@@ -6,7 +6,7 @@ Dotenv.load
6
6
 
7
7
  PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '../'))
8
8
 
9
- passrock_db = Passrock::PasswordDb.new(ENV['PASSROCK_PASSWORD_DB'], ENV['PASSROCK_PRIVATE_KEY'])
9
+ passrock_db = Passrock::PasswordDb.new(:password_db => ENV['PASSROCK_PASSWORD_DB'], :private_key => ENV['PASSROCK_PRIVATE_KEY'])
10
10
 
11
11
  Benchmark.bm do |x|
12
12
  x.report("#find_by_binary_search\n") { puts "Password secure? #{passrock_db.secure?('password')}" }
@@ -1,6 +1,7 @@
1
1
  require 'passrock/version'
2
2
  require 'passrock/exceptions'
3
3
  require 'passrock/configuration'
4
+ require 'passrock/password_db_finder'
4
5
  require 'passrock/password_db'
5
6
 
6
7
  require 'passrock/railtie' if defined?(::Rails::Railtie)
@@ -3,6 +3,6 @@ module Passrock
3
3
  class PassrockError < ::StandardError; end
4
4
 
5
5
  class PasswordDbNotFoundError < PassrockError; end
6
- class PrivateKeyInvalidError < PassrockError; end
6
+ class BinaryFileReadError < PassrockError; end
7
7
 
8
8
  end
@@ -1,77 +1,19 @@
1
- require 'base64'
2
- require 'bcrypt'
3
-
4
1
  module Passrock
5
2
  class PasswordDb
6
3
 
7
- RECORD_LENGTH = 23
8
-
9
-
10
- def self.bcrypt_hash(secret, salt)
11
- BCrypt::Engine.hash_secret(secret, "$2a$07$#{salt}")
12
- end
13
-
14
-
15
- attr_reader :password_db, :private_key
4
+ attr_reader :password_db_finder
16
5
 
17
6
  def initialize(opts = {})
18
- @password_db = opts[:password_db]
19
- @private_key = opts[:private_key]
20
-
21
- raise PasswordDbNotFoundError, "Passrock Password DB not found at: #{@password_db}" unless File.file?(@password_db)
22
- end
23
-
24
- def password_in_searchable_form(password)
25
- hashed_password = self.class.bcrypt_hash(password, private_key)
26
-
27
- searchable = hashed_password[29..-1]
28
- searchable.tr!('./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/')
29
- searchable += '=' * (3 - (searchable.size + 3) % 4)
7
+ @password_db_finder = PasswordDbFinder.new(opts)
30
8
  end
31
9
 
32
10
  def secure?(password)
33
- !find_by_binary_search(password)
11
+ !password_db_finder.find(password)
34
12
  end
35
13
 
36
14
  def insecure?(password)
37
15
  !secure?(password)
38
16
  end
39
17
 
40
-
41
- private
42
-
43
- def total_records
44
- # Minus 1 for length in file and 1 for 0-up counting
45
- @total_records ||= (File.size(password_db) / RECORD_LENGTH) - 2
46
- end
47
-
48
- def find_by_binary_search(password)
49
- file = File.new(password_db, 'rb')
50
- target = password_in_searchable_form(password)
51
-
52
- lo = 1 # start at 1 because the testKey is at 0
53
- hi = total_records
54
- while lo <= hi
55
- mid = (lo + (hi - lo) / 2)
56
- file.seek(RECORD_LENGTH * mid, IO::SEEK_SET)
57
- midtest = file.read(RECORD_LENGTH)
58
- raise 'Error reading binary file' if midtest.nil?
59
-
60
- midtest = Base64.strict_encode64(midtest)
61
-
62
- if ( (midtest <=> target) == 0 )
63
- file.close
64
- return true
65
- elsif ( (midtest <=> target) < 0 )
66
- lo = mid + 1
67
- else
68
- hi = mid - 1
69
- end
70
- end
71
-
72
- file.close
73
- return false
74
- end
75
-
76
18
  end
77
19
  end
@@ -0,0 +1,87 @@
1
+ require 'base64'
2
+ require 'bcrypt'
3
+
4
+ module Passrock
5
+ class PasswordDbFinder
6
+
7
+ RECORD_LENGTH = 12
8
+
9
+
10
+ def self.bcrypt_hash(secret, salt)
11
+ BCrypt::Engine.hash_secret(secret, "$2a$07$#{salt}")
12
+ end
13
+
14
+
15
+ attr_reader :password_db, :private_key
16
+
17
+ def initialize(opts = {})
18
+ @password_db = opts[:password_db]
19
+ @private_key = opts[:private_key]
20
+ end
21
+
22
+ def valid?
23
+ password_db_is_a_file? || password_db_is_a_directory?
24
+ end
25
+
26
+ def find(password)
27
+ raise PasswordDbNotFoundError, "Passrock Password DB not found at: #{password_db}" unless valid?
28
+ find_by_binary_search(password)
29
+ end
30
+
31
+ def filename(hashed_password = nil)
32
+ return password_db if password_db_is_a_file?
33
+ File.join(password_db, "PRbinary#{hashed_password.ord}.dat")
34
+ end
35
+
36
+ def password_db_is_a_file?
37
+ File.file?(password_db)
38
+ end
39
+
40
+ def password_db_is_a_directory?
41
+ File.directory?(password_db)
42
+ end
43
+
44
+
45
+ private
46
+
47
+ def password_in_searchable_form(password)
48
+ password = password.to_s.downcase
49
+ hashed_password = self.class.bcrypt_hash(password, private_key)
50
+
51
+ searchable = hashed_password[29..-1]
52
+ searchable.tr!('./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/')
53
+ searchable += '=' * (3 - (searchable.size + 3) % 4)
54
+ searchable[0, 16]
55
+ end
56
+
57
+ def find_by_binary_search(password)
58
+ target = password_in_searchable_form(password)
59
+ file = File.new(filename(target), 'rb')
60
+ total_records = (File.size(file) / RECORD_LENGTH) - 1
61
+
62
+ lo = 0
63
+ hi = total_records
64
+ while lo <= hi
65
+ mid = (lo + (hi - lo) / 2)
66
+ file.seek(RECORD_LENGTH * mid, IO::SEEK_SET)
67
+ midtest = file.read(RECORD_LENGTH)
68
+ raise BinaryFileReadError if midtest.nil?
69
+
70
+ midtest = Base64.strict_encode64(midtest)
71
+
72
+ if ( (midtest <=> target) == 0 )
73
+ file.close
74
+ return target
75
+ elsif ( (midtest <=> target) < 0 )
76
+ lo = mid + 1
77
+ else
78
+ hi = mid - 1
79
+ end
80
+ end
81
+
82
+ file.close
83
+ return nil
84
+ end
85
+
86
+ end
87
+ end
@@ -1,3 +1,3 @@
1
1
  module Passrock
2
- VERSION = '0.0.5'
2
+ VERSION = '0.0.8'
3
3
  end
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+
3
+ describe Passrock::PasswordDbFinder do
4
+
5
+ let(:password_db) { passrock_password_db }
6
+ let(:private_key) { passrock_private_key }
7
+ let(:init_opts) { {:password_db => password_db, :private_key => private_key} }
8
+
9
+
10
+ describe '.bcrypt_hash' do
11
+
12
+ it 'calculates and returns the bcrypt password hash given a secret and salt' do
13
+ secret = 'password'
14
+ salt = private_key
15
+ expect(described_class.bcrypt_hash(secret, salt)).to eq('$2a$07$c16iYVArVz3hYEvtakjiXO8jPyn2MxhVHlrY92EErobY/OCDNObhG')
16
+ end
17
+
18
+ end
19
+
20
+
21
+ describe '#valid?' do
22
+
23
+ let(:subject) { described_class.new(init_opts) }
24
+
25
+ context 'password_db is a directory' do
26
+ let(:password_db) { passrock_password_db }
27
+
28
+ it 'returns true' do
29
+ expect(subject).to be_valid
30
+ end
31
+ end
32
+
33
+ context 'password_db is a file' do
34
+ let(:password_db) { passrock_password_db_file }
35
+
36
+ it 'returns true' do
37
+ expect(subject).to be_valid
38
+ end
39
+ end
40
+
41
+ context 'password_db is does not exist as a file or directory' do
42
+ let(:password_db) { '/invalid/path/to/password_db' }
43
+
44
+ it 'returns false' do
45
+ expect(subject).to_not be_valid
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ describe '#filename' do
52
+
53
+ let(:subject) { described_class.new(init_opts) }
54
+
55
+ context 'password_db is a directory' do
56
+ let(:password_db) { passrock_password_db }
57
+
58
+ it 'returns the filename based on the integer ordinal of the first character of the given hashed password' do
59
+ hashed_password = 'g+vOBRu/5hi40RA5'
60
+ expect(subject.filename(hashed_password)).to eq("#{password_db}/PRbinary#{hashed_password[0].ord}.dat")
61
+ end
62
+ end
63
+
64
+ context 'password_db is a file' do
65
+ let(:password_db) { passrock_password_db_file }
66
+
67
+ it 'returns the password_db as the filename verbatim' do
68
+ expect(subject.filename).to eq(passrock_password_db_file)
69
+ end
70
+ end
71
+
72
+ end
73
+
74
+ describe '#find' do
75
+
76
+ let(:subject) { described_class.new(init_opts) }
77
+ let(:known_password) { 'password' }
78
+ let(:unknown_password) { 'BoatActKnowsDog' }
79
+
80
+ context 'when given password is present in the password database' do
81
+ it 'returns the Base64 representation of the hashed password' do
82
+ expect(subject.find(known_password)).to eq('+lR0p4OzjXJnta/4')
83
+ end
84
+ end
85
+
86
+ context 'when given password is mixed cased' do
87
+ it 'returns the Base64 representation of the hashed password ignoring case' do
88
+ expect(subject.find(known_password.upcase)).to eq('+lR0p4OzjXJnta/4')
89
+ end
90
+ end
91
+
92
+ context 'when given password does not appear in the password database' do
93
+ it 'returns true' do
94
+ expect(subject.find(unknown_password)).to be_nil
95
+ end
96
+ end
97
+
98
+ context 'when password_db does not exist' do
99
+ let(:password_db) { '/invalid/path/to/password_db' }
100
+
101
+ it 'raises PasswordDbNotFoundError' do
102
+ expect {
103
+ subject.find(known_password)
104
+ }.to raise_error(Passrock::PasswordDbNotFoundError)
105
+ end
106
+ end
107
+
108
+ context 'multiple sequential calls' do
109
+ it 'does not error out' do
110
+ expect {
111
+ subject.find(unknown_password)
112
+ subject.find(known_password)
113
+ }.to_not raise_error
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+ end
@@ -4,50 +4,22 @@ describe Passrock::PasswordDb do
4
4
 
5
5
  let(:password_db) { passrock_password_db }
6
6
  let(:private_key) { passrock_private_key }
7
- let(:valid_init_opts) { {:password_db => password_db, :private_key => private_key} }
7
+ let(:init_opts) { {:password_db => password_db, :private_key => private_key} }
8
8
  let(:insecure_password) { 'password' }
9
9
  let(:secure_password) { 'BoatActKnowsDog' }
10
10
 
11
-
12
- describe '.bcrypt_hash' do
13
-
14
- it 'calculates and returns the bcrypt password hash given a secret and salt' do
15
- secret = 'password'
16
- salt = private_key
17
- expect(described_class.bcrypt_hash(secret, salt)).to eq('$2a$07$c16iYVArVz3hYEvtakjiXO8jPyn2MxhVHlrY92EErobY/OCDNObhG')
18
- end
19
-
20
- end
21
-
22
-
23
- describe '#initialize' do
24
-
25
- context 'when password_db file does not exist' do
26
- it 'raises PasswordDbNotFoundError' do
27
- expect {
28
- described_class.new(:password_db => '/invalid/path/to/password_db', :private_key => private_key)
29
- }.to raise_error(Passrock::PasswordDbNotFoundError)
30
- end
31
- end
32
-
33
- end
34
-
35
- describe '#password_in_searchable_form' do
36
-
37
- it 'returns the given password in a searchable format' do
38
- subject = described_class.new(valid_init_opts)
39
- expect(subject.password_in_searchable_form(insecure_password)).to eq('+lR0p4OzjXJnta/4GGtqdaBQEFPQdjI=')
40
- end
41
-
42
- end
43
-
44
11
  describe '#secure?' do
45
12
 
46
- let(:subject) { described_class.new(valid_init_opts) }
13
+ let(:subject) { described_class.new(init_opts) }
47
14
 
48
15
  context 'when given password is present in the password database' do
49
16
  it 'returns false' do
50
17
  expect(subject.secure?(insecure_password)).to be_false
18
+
19
+ # sanity check other known insecure passwords
20
+ [ 'inIUfiWO13', 'PVGWpkf81', 'cSAuOcUW58', 'XxPRBGF11', 'WjNYUmGj72', 'P0RQU33SM3N3ST3r' ].each do |password|
21
+ expect(subject.secure?(password)).to be_false
22
+ end
51
23
  end
52
24
  end
53
25
 
@@ -57,20 +29,11 @@ describe Passrock::PasswordDb do
57
29
  end
58
30
  end
59
31
 
60
- context 'multiple sequential calls' do
61
- it 'does not error out' do
62
- expect {
63
- subject.secure?(secure_password)
64
- subject.secure?(insecure_password)
65
- }.to_not raise_error
66
- end
67
- end
68
-
69
32
  end
70
33
 
71
34
  describe '#insecure?' do
72
35
 
73
- let(:subject) { described_class.new(valid_init_opts) }
36
+ let(:subject) { described_class.new(init_opts) }
74
37
 
75
38
  context 'when given password is present in the password database' do
76
39
  it 'returns true' do
@@ -27,6 +27,10 @@ def passrock_password_db
27
27
  ENV['PASSROCK_PASSWORD_DB']
28
28
  end
29
29
 
30
+ def passrock_password_db_file
31
+ ENV['PASSROCK_PASSWORD_DB_FILE']
32
+ end
33
+
30
34
  def passrock_private_key
31
35
  ENV['PASSROCK_PRIVATE_KEY']
32
36
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: passrock
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bitium, Inc
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-09-12 00:00:00.000000000 Z
11
+ date: 2013-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bcrypt-ruby
@@ -100,11 +100,13 @@ files:
100
100
  - lib/passrock/configuration.rb
101
101
  - lib/passrock/exceptions.rb
102
102
  - lib/passrock/password_db.rb
103
+ - lib/passrock/password_db_finder.rb
103
104
  - lib/passrock/railtie.rb
104
105
  - lib/passrock/version.rb
105
106
  - locales/en.yml
106
107
  - passrock.gemspec
107
108
  - spec/active_model/validations/passrock_secure_validator_spec.rb
109
+ - spec/passrock/password_db_finder_spec.rb
108
110
  - spec/passrock/password_db_spec.rb
109
111
  - spec/passrock_spec.rb
110
112
  - spec/spec_helper.rb
@@ -134,6 +136,7 @@ specification_version: 4
134
136
  summary: Ruby client library to access a Passrock (Passrock.com) binary database
135
137
  test_files:
136
138
  - spec/active_model/validations/passrock_secure_validator_spec.rb
139
+ - spec/passrock/password_db_finder_spec.rb
137
140
  - spec/passrock/password_db_spec.rb
138
141
  - spec/passrock_spec.rb
139
142
  - spec/spec_helper.rb