attr_digest 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cf52ffb7a487f0b9c13e9fb6e2cb3d1882d21a4a
4
+ data.tar.gz: a6918d2fd2c13d8a204eb095ddc6c403af4fd246
5
+ SHA512:
6
+ metadata.gz: 5dd307ca3c3dbb20782064d484fcac971c3ad46da759dd048cee458a6996702199a407e8f44076f02a5c829ff279d8c78fa106b12facb8eacc197d02ad07f373
7
+ data.tar.gz: b83b2d233a00f196ecfb7656ed37144f948e12972f97a38c8f6f680fbf62dc956b45af587a24f245f1da9b77fe6abc7041b4c31e6eb63b669eb6455f45a718a7
data/CHANGELOG.md ADDED
@@ -0,0 +1 @@
1
+ v1.0.0 - Initial release.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2016 Brightcommerce, Inc. All rights reserved.
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.md ADDED
@@ -0,0 +1,143 @@
1
+ [![Gem Version](https://badge.fury.io/rb/attr_digest.svg)](https://badge.fury.io/rb/attr_digest)
2
+ [![Build Status](https://travis-ci.org/brightcommerce/attr_digest.svg?branch=master)](https://travis-ci.org/brightcommerce/attr_digest)
3
+ [![codecov.io](https://codecov.io/github/brightcommerce/attr_digest/coverage.svg?branch=master)](https://codecov.io/github/brightcommerce/attr_digest?branch=master)
4
+ [![HitCount](https://hitt.herokuapp.com/brightcommerce/attr_digest.svg)](https://github.com/brightcommerce/attr_digest)
5
+ [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/dwyl/esta/issues)
6
+
7
+ # AttrDigest
8
+
9
+ [**AttrDigest**](https://github.com/brightcommerce/attr_digest) provides functionality to store a hash digest of an attribute using [Argon2](https://github.com/P-H-C/phc-winner-argon2).
10
+
11
+ Argon2 is the official winner and recommendation of the [Password Hashing Competition (PHC)](https://password-hashing.net) which ran between 2013 and 2015, and is a password-hashing function that summarizes the state of the art in the design of memory-hard functions and can be used to hash passwords for credential storage, key derivation, or other applications.
12
+
13
+ This Gem uses the [Ruby Argon2 Gem](https://github.com/technion/ruby-argon2) which provides FFI bindings, and a simplified interface, to the Argon2 algorithm.
14
+
15
+ **AttrDigest** provides similar functionality to Rails `has_secure_password`, but permits any number attributes to be hashed in a model, and obviously you're not limited to just the `password` attribute.
16
+
17
+ ## Installation
18
+
19
+ To install add the following line to your `Gemfile`:
20
+
21
+ ``` ruby
22
+ gem 'attr_digest', '~> 1.0'
23
+ ```
24
+
25
+ And run `bundle install`.
26
+
27
+ ## Dependencies
28
+
29
+ Runtime:
30
+ - activerecord (~> 4.2.5.1)
31
+ - activesupport (~> 4.2.5.1)
32
+ - argon2 (~> 0.1.4)
33
+
34
+ Development/Test:
35
+ - rake (~> 10.5)
36
+ - rspec (~> 3.4)
37
+ - sqlite3 (~> 1.3)
38
+ - simplecov (~> 0.11.2)
39
+ - factory_girl (~> 4.5)
40
+
41
+ ## Compatibility
42
+
43
+ Tested with Ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-darwin15] against ActiveRecord 4.2.5.1 on Mac OS X El Capitan 10.11.3 (15D21).
44
+
45
+ Argon2 requires Ruby 2.2 minimum and an OS platform that supports Ruby FFI Bindings, so unfortunately Windows is out.
46
+
47
+
48
+ ## Usage
49
+
50
+ Attributes to be digested are declared using the `attr_digest` class method in your model:
51
+
52
+ ```ruby
53
+ ActiveRecord::Schema.define do
54
+ create_table :users, force: true do |t|
55
+ t.string :security_question, null: false
56
+ t.string :security_answer_digest, null: false
57
+ end
58
+ end
59
+
60
+ class User < ActiveRecord::Base
61
+ attr_digest :security_answer
62
+ end
63
+ ```
64
+
65
+ **AttrDigest** automatically creates the `#security_answer` getter and `#security_answer=` setter. The setter creates a digest of the value provided and stores it in the `security_answer_digest` column.
66
+
67
+ **AttrDigest** also defines the method `authenticate_security_answer(value)` which returns `false` if the `value` given does not correspond to the saved digest, or returns `true` if it does.
68
+
69
+ ### Validations
70
+
71
+ **AttrDigest** adds some default validations. Using the example above:
72
+ * it creates a `confirmation` validation on `security_answer`, but only if `security_answer` is given (for confirmation validations see [ActiveRecord Validations](http://http://guides.rubyonrails.org/active_record_validations.html#confirmation)).
73
+ * it creates a `presence` validation on `security_answer` but only on `create`.
74
+ * it creates a `presence` validation on `security_answer_confirmation` but only if `security_answer` has been given; and
75
+ * it raises an `exception` if `security_answer_digest` is empty on `create`.
76
+
77
+ You can disable all validations by passing `false` to the `validations` option:
78
+
79
+ ```ruby
80
+ attr_digest :security_answer, validations: false
81
+ ```
82
+
83
+ #### Case Sensitivity
84
+
85
+ If you want values passed to be case insensitive, you can pass `false` to the `case_sensitive` option:
86
+
87
+ ```ruby
88
+ attr_digest :security_answer, case_sensitive: false
89
+ ```
90
+
91
+ Then differing cases will match, e.g. `pizza` will match `PizzA`.
92
+
93
+ #### Confirmations
94
+
95
+ If you prefer to skip confirmations for the attribute you are hashing, you can pass `false` to the `confirmation` option:
96
+
97
+ ```ruby
98
+ attr_digest :security_answer, confirmation: false
99
+ ```
100
+
101
+ #### Protected Digest Setter
102
+
103
+ If you want to prevent the attribute's digest being set directly, you can include the `protected` option:
104
+
105
+ ```ruby
106
+ attr_digest :security_answer, protected: true
107
+ ```
108
+
109
+ The attribute's digest is *not* protected from direct setting by default.
110
+
111
+ ## Tests
112
+
113
+ Tests are written using Rspec, FactoryGirl and Sqlite3. There are 31 examples with 100% code coverage.
114
+
115
+ To run the tests, execute the default rake task:
116
+
117
+ ``` bash
118
+ bundle exec rake
119
+ ```
120
+
121
+ ## Roadmap
122
+
123
+ I like to add the ability to pass a `secret` to the Argon2 hasher. This functionality exists in the Ruby Argon2 Gem.
124
+
125
+ ## Contributing
126
+
127
+ 1. Fork it
128
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
129
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
130
+ 4. Push to the branch (`git push origin my-new-feature`)
131
+ 5. Create new Pull Request
132
+
133
+ ## Credit
134
+
135
+ I like to thank [Panayotis Matsinopoulos](http://www.matsinopoulos.gr) for his [has_secure_attribute](https://github.com/pmatsinopoulos/has_secure_attribute) gem which provided a lot of the inspiration and framework for **AttrDigest**.
136
+
137
+ ## License
138
+
139
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
140
+
141
+ ## Copyright
142
+
143
+ Copyright 2016 Brightcommerce, Inc.
@@ -0,0 +1,72 @@
1
+ require 'argon2'
2
+ require 'active_record'
3
+ require 'active_support/all'
4
+
5
+ module AttrDigest
6
+ extend ActiveSupport::Concern
7
+
8
+ class << self
9
+ attr_accessor :time_cost
10
+ attr_accessor :memory_cost
11
+ end
12
+
13
+ self.time_cost = 2
14
+ self.memory_cost = 16
15
+
16
+ module ClassMethods
17
+ def attr_digest(meth, *args, &block)
18
+ attribute_sym = meth.to_sym
19
+ attr_reader attribute_sym
20
+
21
+ options = { validations: true, protected: false, case_sensitive: true, confirmation: true }
22
+ options.merge! args[0] unless args.blank?
23
+
24
+ if options[:validations]
25
+ confirm attribute_sym if options[:confirmation]
26
+ validates attribute_sym, presence: true, on: :create
27
+ before_create { raise "#{attribute_sym}_digest missing on new record" if send("#{attribute_sym}_digest").blank? }
28
+ end
29
+
30
+ define_setter(attribute_sym, options)
31
+ protect_setter(attribute_sym) if options[:protected]
32
+ define_authenticate_method(attribute_sym, options)
33
+ end
34
+
35
+ def confirm(attribute_sym)
36
+ validates attribute_sym, confirmation: true, if: lambda { |m| m.send(attribute_sym).present? }
37
+ validates "#{attribute_sym}_confirmation".to_sym, presence: true, if: lambda { |m| m.send(attribute_sym).present? }
38
+ end
39
+
40
+ def define_setter(attribute_sym, options)
41
+ define_method "#{attribute_sym.to_s}=" do |unencrypted_value|
42
+ unless unencrypted_value.blank?
43
+ instance_variable_set("@#{attribute_sym.to_s}".to_sym, unencrypted_value)
44
+ password = Argon2::Password.new(t_cost: AttrDigest.time_cost, m_cost: AttrDigest.memory_cost)
45
+ send("#{attribute_sym.to_s}_digest=".to_sym, password.hash(options[:case_sensitive] ? unencrypted_value : unencrypted_value.downcase))
46
+ end
47
+ end
48
+ end
49
+
50
+ def protect_setter(attribute_sym)
51
+ define_method "#{attribute_sym}_digest=" do |value|
52
+ write_attribute "#{attribute_sym}_digest".to_sym, value
53
+ end
54
+ protected "#{attribute_sym}_digest=".to_sym
55
+ end
56
+
57
+ def define_authenticate_method(attribute_sym, options)
58
+ define_method "authenticate_#{attribute_sym}" do |value|
59
+ Argon2::Password.verify_password((options[:case_sensitive] ? value : value.downcase), send("#{attribute_sym}_digest"))
60
+ end
61
+ end
62
+
63
+ protected :attr_digest
64
+ protected :confirm
65
+ protected :define_setter
66
+ protected :protect_setter
67
+ protected :define_authenticate_method
68
+ end
69
+
70
+ end
71
+
72
+ ActiveRecord::Base.send :include, AttrDigest
@@ -0,0 +1,14 @@
1
+ module AttrDigest
2
+ module VERSION
3
+ MAJOR = 1
4
+ MINOR = 0
5
+ TINY = 0
6
+ PRE = nil
7
+
8
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
9
+
10
+ SUMMARY = "AttrDigest v#{STRING}"
11
+
12
+ DESCRIPTION = "Provides functionality to store a hash digest of an attribute using Argon2"
13
+ end
14
+ end
@@ -0,0 +1,2 @@
1
+ require 'attr_digest/version'
2
+ require 'attr_digest/attr_digest'
@@ -0,0 +1,46 @@
1
+ FactoryGirl.define do
2
+
3
+ factory :model_with_attr_digest do
4
+ username 'username'
5
+ password 'password'
6
+ password_confirmation 'password'
7
+ security_question 'question'
8
+ security_answer 'answer'
9
+ security_answer_confirmation 'answer'
10
+ end
11
+
12
+ factory :model_with_attr_digest_and_validations_option do
13
+ username 'username_no_validation'
14
+ password 'password'
15
+ password_confirmation 'password'
16
+ security_question 'question'
17
+ security_answer 'answer'
18
+ end
19
+
20
+ factory :model_with_attr_digest_and_protected_option do
21
+ username 'username_protect'
22
+ password 'password'
23
+ password_confirmation 'password'
24
+ security_question 'question'
25
+ security_answer 'answer'
26
+ security_answer_confirmation 'answer'
27
+ end
28
+
29
+ factory :model_with_attr_digest_and_case_sensitive_option do
30
+ username 'username_protect'
31
+ password 'password'
32
+ password_confirmation 'password'
33
+ security_question 'question'
34
+ security_answer 'answer'
35
+ security_answer_confirmation 'answer'
36
+ end
37
+
38
+ factory :model_with_attr_digest_and_confirmation_option do
39
+ username 'username_protect'
40
+ password 'password'
41
+ password_confirmation 'password'
42
+ security_question 'question'
43
+ security_answer 'answer'
44
+ end
45
+
46
+ end
@@ -0,0 +1,209 @@
1
+ require 'spec_helper'
2
+
3
+ describe ModelWithAttrDigest do
4
+ it 'responds to :security_answer' do
5
+ respond_to(:security_answer)
6
+ end
7
+
8
+ it 'responds to :security_answer=' do
9
+ respond_to(:security_answer=)
10
+ end
11
+
12
+ it 'responds to :security_answer_confirmation' do
13
+ respond_to(:security_answer_confirmation)
14
+ end
15
+
16
+ it 'responds to :security_answer_confirmation=' do
17
+ respond_to(:security_answer_confirmation=)
18
+ end
19
+
20
+ it 'responds to :authenticate_security_answer' do
21
+ respond_to(:authenticate_security_answer)
22
+ end
23
+
24
+ it 'confirms :security_answer' do
25
+ subject.security_answer = 'hello there'
26
+ subject.security_answer_confirmation = 'there hello'
27
+ subject.valid?
28
+ expect(subject.errors[:security_answer_confirmation]).to include("doesn't match Security answer")
29
+ end
30
+
31
+ it 'does not confirm :security_answer if not given' do
32
+ subject.security_answer = nil
33
+ subject.security_answer_confirmation = 'there hello'
34
+ subject.valid?
35
+ expect(subject.errors[:security_answer_confirmation]).to be_blank
36
+ end
37
+
38
+ it 'requires :security_answer on create' do
39
+ expect(subject).to be_new_record
40
+ subject.security_answer = nil
41
+ subject.valid?
42
+ expect(subject.errors[:security_answer]).to include("can't be blank")
43
+ end
44
+
45
+ it 'requires :security_answer_confirmation if :security_answer given' do
46
+ subject.security_answer = 'hello there'
47
+ subject.valid?
48
+ expect(subject.errors[:security_answer_confirmation]).to include("can't be blank")
49
+ end
50
+
51
+ it 'does not require :security_answer_confirmation if :security_answer is not given' do
52
+ subject.security_answer = ''
53
+ subject.valid?
54
+ expect(subject.errors[:security_answer_confirmation]).to be_blank
55
+ end
56
+
57
+ it 'requires :security_answer_digest on create' do
58
+ subject = FactoryGirl.build(:model_with_attr_digest)
59
+ expect(subject).to be_new_record
60
+ # change the security_answer_digest to verify the test
61
+ subject.security_answer_digest = ''
62
+ expect(lambda do
63
+ begin
64
+ subject.save!
65
+ rescue Exception => exception
66
+ expect(exception.message).to include("security_answer_digest missing on new record")
67
+ raise
68
+ end
69
+ end).to raise_error(RuntimeError)
70
+ end
71
+
72
+ it 'does not require :security_answer_digest on update' do
73
+ subject = FactoryGirl.build(:model_with_attr_digest)
74
+ expect(subject).to be_new_record
75
+ subject.save!
76
+ # change the security_answer_digest to verify the test
77
+ subject.security_answer_digest = ''
78
+ subject.save!
79
+ subject.reload
80
+ expect(subject.security_answer_digest).to be_blank
81
+ end
82
+
83
+ it 'allows to call :security_answer_digest directly if :protect_setter_for_digest is not given as option' do
84
+ lambda do
85
+ subject.security_answer_digest = 'hello'
86
+ expect(subject.security_answer_digest).to eq('hello')
87
+ end
88
+ end
89
+
90
+ describe "#security_answer=" do
91
+ it 'sets the :security_answer and saves the digest' do
92
+ model = FactoryGirl.create(:model_with_attr_digest, security_answer: 'old answer', security_answer_confirmation: 'old answer')
93
+ expect(model.security_answer_digest).to_not be_blank
94
+ old_security_answer_digest = model.security_answer_digest
95
+ model.security_answer = 'new answer'
96
+ model.security_answer_confirmation = 'new answer'
97
+ expect(model.instance_variable_get(:@security_answer)).to eq('new answer')
98
+ model.save!
99
+ expect(model.security_answer_digest).to_not be_blank
100
+ expect(model.security_answer_digest).to_not eq(old_security_answer_digest)
101
+ end
102
+ end
103
+
104
+ describe '#authenticate_security_answer' do
105
+ it 'returns true if :security_answer given matches the one stored' do
106
+ model = FactoryGirl.create(:model_with_attr_digest, security_answer: 'some answer', security_answer_confirmation: 'some answer')
107
+ expect(model.authenticate_security_answer('some answer')).to be(true)
108
+ end
109
+
110
+ it 'returns false if :security_answer given does not match the one stored' do
111
+ model = FactoryGirl.create(:model_with_attr_digest, security_answer: 'some answer', security_answer_confirmation: 'some answer')
112
+ expect(model.authenticate_security_answer('some other answer')).to be(false)
113
+ end
114
+ end
115
+
116
+ end
117
+
118
+ describe ModelWithAttrDigestAndValidationsOption do
119
+ it 'responds to :security_answer' do
120
+ respond_to(:security_answer)
121
+ end
122
+
123
+ it 'responds to :security_answer=' do
124
+ respond_to(:security_answer=)
125
+ end
126
+
127
+ it 'responds to :security_answer_confirmation' do
128
+ respond_to(:security_answer_confirmation)
129
+ end
130
+
131
+ it 'responds to :security_answer_confirmation=' do
132
+ respond_to(:security_answer_confirmation=)
133
+ end
134
+
135
+ it 'responds to :authenticate_security_answer' do
136
+ respond_to(:authenticate_security_answer)
137
+ end
138
+
139
+ it 'does not require :security_answer on create' do
140
+ expect(subject).to be_new_record
141
+ subject.security_answer = nil
142
+ subject.valid?
143
+ expect(subject.errors[:security_answer]).to be_blank
144
+ end
145
+
146
+ it 'does not require :security_answer_confirmation if :security_answer given' do
147
+ subject.security_answer = 'hello there'
148
+ subject.valid?
149
+ expect(subject.errors[:security_answer_confirmation]).to be_blank
150
+ end
151
+
152
+ it 'does not require :security_answer_confirmation if :security_answer is not given' do
153
+ subject.security_answer = ''
154
+ subject.valid?
155
+ expect(subject.errors[:security_answer_confirmation]).to be_blank
156
+ end
157
+
158
+ it 'does not require :security_answer_digest on create' do
159
+ subject = FactoryGirl.build(:model_with_attr_digest_and_validations_option)
160
+ expect(subject).to be_new_record
161
+ # change the security_answer_digest to verify the test
162
+ subject.security_answer_digest = ''
163
+ subject.save!
164
+ end
165
+
166
+ it 'does not require :security_answer_digest on update' do
167
+ subject = FactoryGirl.build(:model_with_attr_digest_and_validations_option)
168
+ expect(subject).to be_new_record
169
+ subject.save!
170
+ # change the :security_answer_digest to verify the test
171
+ subject.send(:security_answer_digest=, '')
172
+ subject.save!
173
+ subject.reload
174
+ expect(subject.security_answer_digest).to be_blank
175
+ end
176
+ end
177
+
178
+ describe ModelWithAttrDigestAndProtectedOption do
179
+ it 'does not allow to call to protected setter for :security_answer_digest' do
180
+ model = FactoryGirl.create(:model_with_attr_digest_and_protected_option, security_answer: 'Answer', security_answer_confirmation: 'Answer')
181
+ expect(lambda do
182
+ model.security_answer_digest = 'hello'
183
+ end).to raise_error(NoMethodError)
184
+ end
185
+ end
186
+
187
+ describe ModelWithAttrDigestAndCaseSensitiveOption do
188
+ it 'authenticates even if :security_answer is of different case' do
189
+ model = FactoryGirl.create(:model_with_attr_digest_and_case_sensitive_option, security_answer: 'Answer', security_answer_confirmation: 'Answer')
190
+ expect(model.authenticate_security_answer('answer')).to eq(true)
191
+ end
192
+ end
193
+
194
+ describe ModelWithAttrDigestAndConfirmationOption do
195
+ it 'does not respond to :security_answer_confirmation' do
196
+ respond_to(:security_answer_confirmation) == false
197
+ end
198
+
199
+ it 'does not respond to :security_answer_confirmation=' do
200
+ respond_to(:security_answer_confirmation=) == false
201
+ end
202
+
203
+ it 'allows to create and save without any confirmation on :security_answer' do
204
+ model = FactoryGirl.create(:model_with_attr_digest_and_confirmation_option, security_answer: 'Answer')
205
+ model.save!
206
+ expect(model.authenticate_security_answer('another answer')).to be(false)
207
+ expect(model.authenticate_security_answer('Answer')).to be(true)
208
+ end
209
+ end
@@ -0,0 +1,34 @@
1
+ begin
2
+ require 'simplecov'
3
+ SimpleCov.start
4
+ if ENV['CI']=='true'
5
+ require 'codecov'
6
+ SimpleCov.formatter = SimpleCov::Formatter::Codecov
7
+ end
8
+ rescue LoadError
9
+ end
10
+
11
+ require 'active_record'
12
+
13
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: File.dirname(__FILE__) + "/tmp/test.sqlite3")
14
+
15
+ puts "Using ActiveRecord #{ActiveRecord::VERSION::STRING}"
16
+
17
+ load File.dirname(__FILE__) + '/support/schema.rb'
18
+
19
+ require File.dirname(__FILE__) + '/../lib/attr_digest.rb'
20
+
21
+ require 'support/models'
22
+
23
+ require 'factory_girl'
24
+ FactoryGirl.find_definitions
25
+
26
+ require "rspec/expectations"
27
+
28
+ RSpec.configure do |config|
29
+ config.before(:each) do
30
+ ModelWithAttrDigest.delete_all
31
+ end
32
+
33
+ config.order = 'random'
34
+ end
@@ -0,0 +1,47 @@
1
+ class ModelWithAttrDigest < ActiveRecord::Base
2
+ attr_digest :password
3
+ attr_digest :security_answer
4
+
5
+ validates :username, presence: true, uniqueness: { case_sensitive: false }
6
+ validates :security_question, presence: true
7
+ end
8
+
9
+ class ModelWithAttrDigestAndCaseSensitiveOption < ActiveRecord::Base
10
+ self.table_name = "model_with_attr_digests"
11
+
12
+ attr_digest :password
13
+ attr_digest :security_answer, case_sensitive: false
14
+
15
+ validates :username, presence: true, uniqueness: { case_sensitive: false }
16
+ validates :security_question, presence: true
17
+ end
18
+
19
+ class ModelWithAttrDigestAndProtectedOption < ActiveRecord::Base
20
+ self.table_name = "model_with_attr_digests"
21
+
22
+ attr_digest :password
23
+ attr_digest :security_answer, protected: true
24
+
25
+ validates :username, presence: true, uniqueness: { case_sensitive: false }
26
+ validates :security_question, presence: true
27
+ end
28
+
29
+ class ModelWithAttrDigestAndValidationsOption < ActiveRecord::Base
30
+ self.table_name = "model_with_attr_digests"
31
+
32
+ attr_digest :password
33
+ attr_digest :security_answer, validations: false
34
+
35
+ validates :username, presence: true, uniqueness: { case_sensitive: false }
36
+ validates :security_question, presence: true
37
+ end
38
+
39
+ class ModelWithAttrDigestAndConfirmationOption < ActiveRecord::Base
40
+ self.table_name = "model_with_attr_digests"
41
+
42
+ attr_digest :password
43
+ attr_digest :security_answer, confirmation: false
44
+
45
+ validates :username, presence: true, uniqueness: { case_sensitive: false }
46
+ validates :security_question, presence: true
47
+ end
@@ -0,0 +1,17 @@
1
+ ActiveRecord::Schema.define do
2
+
3
+ self.verbose = false
4
+
5
+ create_table :model_with_attr_digests, force: true do |t|
6
+ t.string :username, null: false
7
+ t.string :password_digest, null: false
8
+ t.string :security_question, null: false
9
+ t.string :security_answer_digest, null: false
10
+ t.timestamps
11
+ end
12
+
13
+ change_table :model_with_attr_digests do |t|
14
+ t.index :username, unique: true
15
+ end
16
+
17
+ end
Binary file
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: attr_digest
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jurgen Jocubeit
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: argon2
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.1.4
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.1.4
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.4'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sqlite3
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: factory_girl
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '4.5'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '4.5'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.11.2
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.11.2
125
+ description: Provides functionality to store a hash digest of an attribute using Argon2
126
+ email:
127
+ - support@brightcommerce.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - CHANGELOG.md
133
+ - MIT-LICENSE
134
+ - README.md
135
+ - lib/attr_digest.rb
136
+ - lib/attr_digest/attr_digest.rb
137
+ - lib/attr_digest/version.rb
138
+ - spec/factories/models.rb
139
+ - spec/lib/attr_digest_spec.rb
140
+ - spec/spec_helper.rb
141
+ - spec/support/models.rb
142
+ - spec/support/schema.rb
143
+ - spec/tmp/test.sqlite3
144
+ homepage: https://github.com/brightcommerce/attr_digest
145
+ licenses:
146
+ - MIT
147
+ metadata:
148
+ copyright: Copyright 2016 Brightcommerce, Inc.
149
+ post_install_message:
150
+ rdoc_options: []
151
+ require_paths:
152
+ - lib
153
+ required_ruby_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '2.2'
158
+ required_rubygems_version: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ requirements: []
164
+ rubyforge_project:
165
+ rubygems_version: 2.4.5.1
166
+ signing_key:
167
+ specification_version: 4
168
+ summary: AttrDigest v1.0.0
169
+ test_files: []