passrock 0.0.5 → 0.0.8

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