has_protected_token 0.0.0.pre.alpha
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 +7 -0
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +12 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +65 -0
- data/LICENSE.txt +22 -0
- data/README.md +3 -0
- data/Rakefile +10 -0
- data/has_protected_token.gemspec +23 -0
- data/lib/has_protected_token.rb +116 -0
- data/spec/lib/has_protected_token_spec.rb +138 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/support/model.rb +13 -0
- data/spec/support/schema.rb +6 -0
- metadata +187 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2402637577f83fe36f06f2f78c890acf609d2f88f119c19aa611c50b62e01883
|
|
4
|
+
data.tar.gz: 070f0809cc1e72075fc117fa108fbad54a96269efc621dacd61565a7b1ba9d87
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 9e4e4b5a199ace9e249752acedd415fa0082d70374971c0aaa6c4368c96db97c9bafe21837ee886d4eabec7db8a5cce398e99d5c7dba6829b9d0995471bec8dd
|
|
7
|
+
data.tar.gz: 52b14529a9d74b2cfa230149fcde14751d0560331d76b1b178bc17e8e1a3d452e040efc4a2805ab2ae48de8a00ba86957723f780d25b16cd88de52128523edab
|
data/.gitignore
ADDED
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--require spec_helper
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.5.7
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
has_protected_token (0.0.0.pre.alpha)
|
|
5
|
+
activerecord (>= 3.0)
|
|
6
|
+
bcrypt (~> 3.1.1)
|
|
7
|
+
|
|
8
|
+
GEM
|
|
9
|
+
remote: https://rubygems.org/
|
|
10
|
+
specs:
|
|
11
|
+
activemodel (6.0.0)
|
|
12
|
+
activesupport (= 6.0.0)
|
|
13
|
+
activerecord (6.0.0)
|
|
14
|
+
activemodel (= 6.0.0)
|
|
15
|
+
activesupport (= 6.0.0)
|
|
16
|
+
activesupport (6.0.0)
|
|
17
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
18
|
+
i18n (>= 0.7, < 2)
|
|
19
|
+
minitest (~> 5.1)
|
|
20
|
+
tzinfo (~> 1.1)
|
|
21
|
+
zeitwerk (~> 2.1, >= 2.1.8)
|
|
22
|
+
bcrypt (3.1.13)
|
|
23
|
+
bump (0.8.0)
|
|
24
|
+
byebug (11.0.1)
|
|
25
|
+
concurrent-ruby (1.1.5)
|
|
26
|
+
database_cleaner (1.7.0)
|
|
27
|
+
diff-lcs (1.3)
|
|
28
|
+
i18n (1.7.0)
|
|
29
|
+
concurrent-ruby (~> 1.0)
|
|
30
|
+
minitest (5.12.2)
|
|
31
|
+
rake (13.0.0)
|
|
32
|
+
rspec (3.8.0)
|
|
33
|
+
rspec-core (~> 3.8.0)
|
|
34
|
+
rspec-expectations (~> 3.8.0)
|
|
35
|
+
rspec-mocks (~> 3.8.0)
|
|
36
|
+
rspec-core (3.8.2)
|
|
37
|
+
rspec-support (~> 3.8.0)
|
|
38
|
+
rspec-expectations (3.8.5)
|
|
39
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
40
|
+
rspec-support (~> 3.8.0)
|
|
41
|
+
rspec-mocks (3.8.2)
|
|
42
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
43
|
+
rspec-support (~> 3.8.0)
|
|
44
|
+
rspec-support (3.8.3)
|
|
45
|
+
sqlite3 (1.4.1)
|
|
46
|
+
thread_safe (0.3.6)
|
|
47
|
+
tzinfo (1.2.5)
|
|
48
|
+
thread_safe (~> 0.1)
|
|
49
|
+
zeitwerk (2.2.0)
|
|
50
|
+
|
|
51
|
+
PLATFORMS
|
|
52
|
+
ruby
|
|
53
|
+
|
|
54
|
+
DEPENDENCIES
|
|
55
|
+
bump
|
|
56
|
+
bundler (~> 1.17.3)
|
|
57
|
+
byebug
|
|
58
|
+
database_cleaner
|
|
59
|
+
has_protected_token!
|
|
60
|
+
rake
|
|
61
|
+
rspec
|
|
62
|
+
sqlite3
|
|
63
|
+
|
|
64
|
+
BUNDLED WITH
|
|
65
|
+
1.17.3
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2019 David Allen
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Gem::Specification.new do |s|
|
|
2
|
+
s.name = 'has_protected_token'
|
|
3
|
+
s.version = '0.0.0-alpha'
|
|
4
|
+
s.date = '2019-10-06'
|
|
5
|
+
s.summary = 'Easily generate random tokens for any ActiveRecord model and store them securely in the database.'
|
|
6
|
+
s.description = 'Generate random tokens (or use your own) for any ActiveRecord model. Hashes and salts the token before storage in the database using the same methodology as has_secure_password.'
|
|
7
|
+
s.author = 'David Allen'
|
|
8
|
+
s.email = '1337dallen@gmail.com' # yes, I know it's a terrible email address...
|
|
9
|
+
s.files = `git ls-files`.split("\n")
|
|
10
|
+
s.homepage = 'https://github.com/StaphSynth/has_protected_token'
|
|
11
|
+
s.license = 'MIT'
|
|
12
|
+
|
|
13
|
+
s.add_dependency 'activerecord', '>= 3.0'
|
|
14
|
+
s.add_dependency 'bcrypt', '~> 3.1.1'
|
|
15
|
+
|
|
16
|
+
s.add_development_dependency 'bundler', '~> 1.17.3'
|
|
17
|
+
s.add_development_dependency 'rake'
|
|
18
|
+
s.add_development_dependency 'rspec'
|
|
19
|
+
s.add_development_dependency 'sqlite3'
|
|
20
|
+
s.add_development_dependency 'byebug'
|
|
21
|
+
s.add_development_dependency 'database_cleaner'
|
|
22
|
+
s.add_development_dependency 'bump'
|
|
23
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
require 'active_record'
|
|
2
|
+
require 'bcrypt'
|
|
3
|
+
|
|
4
|
+
module ActiveRecord
|
|
5
|
+
module ProtectedToken
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
module ClassMethods
|
|
9
|
+
# == has_protected_token
|
|
10
|
+
#
|
|
11
|
+
# Adds methods to set and validate against a token
|
|
12
|
+
# that has been hashed and salted using BCrypt.
|
|
13
|
+
# It assumes you have a 'token' attribute on
|
|
14
|
+
# your model.
|
|
15
|
+
#
|
|
16
|
+
# === Options
|
|
17
|
+
#
|
|
18
|
+
# +has_protected_token+ accepts an optional hash
|
|
19
|
+
# for modifying the following default behaviour:
|
|
20
|
+
#
|
|
21
|
+
# +column_name+
|
|
22
|
+
# If you would like to use an attribute other than
|
|
23
|
+
# 'token', pass +column_name: :my_attribute_name+
|
|
24
|
+
#
|
|
25
|
+
# +cost+
|
|
26
|
+
# BCrypt's default hashing cost is used. To use a
|
|
27
|
+
# different value, pass +cost: <value>+. The cost
|
|
28
|
+
# value must be an integer.
|
|
29
|
+
#
|
|
30
|
+
# === Example 1
|
|
31
|
+
#
|
|
32
|
+
# Generating a new token on the fly.
|
|
33
|
+
#
|
|
34
|
+
# class UserOne < ActiveRecord::Base
|
|
35
|
+
# has_protected_token column_name: :shared_secret, cost: 8
|
|
36
|
+
# end
|
|
37
|
+
#
|
|
38
|
+
# user1 = UserOne.new
|
|
39
|
+
# user1.regenerate_shared_secret
|
|
40
|
+
# => 'e13d0bbd4a12d2aea673127c7e995a67'
|
|
41
|
+
#
|
|
42
|
+
# user1.authenticate_shared_secret('not_even_close_to_correct')
|
|
43
|
+
# => false
|
|
44
|
+
#
|
|
45
|
+
# user1.authenticate_shared_secret('e13d0bbd4a12d2aea673127c7e995a67')
|
|
46
|
+
# => true
|
|
47
|
+
#
|
|
48
|
+
# === Example 2
|
|
49
|
+
#
|
|
50
|
+
# Passing your own token.
|
|
51
|
+
#
|
|
52
|
+
# class UserTwo < ActiveRecord::Base
|
|
53
|
+
# has_protected_token cost: 8
|
|
54
|
+
# end
|
|
55
|
+
#
|
|
56
|
+
# user2 = UserTwo.new
|
|
57
|
+
# user2.token = 'super_secret_token'
|
|
58
|
+
# => 'super_secret_token'
|
|
59
|
+
# user2.save!
|
|
60
|
+
# => true
|
|
61
|
+
# user2.token
|
|
62
|
+
# => '$2a$12$5xVuny6Z79bYfgMMU7nyzeaOSjygRnXfsJjeJHzRZ0vUYRGeUjo6u'
|
|
63
|
+
#
|
|
64
|
+
# user2.authenticate_token('totally_wrong')
|
|
65
|
+
# => false
|
|
66
|
+
#
|
|
67
|
+
# user2.authenticate_token('super_secret_token')
|
|
68
|
+
# => true
|
|
69
|
+
def has_protected_token(options = {})
|
|
70
|
+
attribute = options[:column_name] || :token
|
|
71
|
+
cost = options[:cost] || BCrypt::Engine::DEFAULT_COST
|
|
72
|
+
|
|
73
|
+
define_method("regenerate_#{attribute}") do
|
|
74
|
+
raw_token = self.class.generate_token
|
|
75
|
+
hashed_token = hash_token(raw_token, cost)
|
|
76
|
+
|
|
77
|
+
update_attribute(attribute, hashed_token)
|
|
78
|
+
raw_token
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
define_method("#{attribute}=") do |raw_token|
|
|
82
|
+
super(hash_token(raw_token, cost))
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
define_method("authenticate_#{attribute}") do |raw_token|
|
|
86
|
+
begin
|
|
87
|
+
BCrypt::Password.new(self.send(attribute)) == raw_token
|
|
88
|
+
rescue BCrypt::Error
|
|
89
|
+
false
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# == .generate_token
|
|
95
|
+
# Class method to generate random tokens
|
|
96
|
+
#
|
|
97
|
+
# Accepts an optional integer to specify the length
|
|
98
|
+
# of the returned token.
|
|
99
|
+
def generate_token(length = 24)
|
|
100
|
+
n = length.to_i
|
|
101
|
+
SecureRandom.hex(n / 2) # hex returns n * 2
|
|
102
|
+
|
|
103
|
+
rescue NoMethodError
|
|
104
|
+
raise ArgumentError, 'Token length must be an integer'
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
|
|
110
|
+
def hash_token(raw_token, cost)
|
|
111
|
+
BCrypt::Password.create(raw_token, :cost => cost)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
ActiveRecord::Base.send(:include, ActiveRecord::ProtectedToken)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ActiveRecord::ProtectedToken do
|
|
4
|
+
let(:user) { User.create }
|
|
5
|
+
let(:raw_token) { 'raw_token' }
|
|
6
|
+
let(:hashed_token) { '$hashed_token$' }
|
|
7
|
+
|
|
8
|
+
describe 'options hash' do
|
|
9
|
+
describe 'column_name' do
|
|
10
|
+
context 'when no value is provided' do
|
|
11
|
+
it 'defaults to "token"' do
|
|
12
|
+
expect(user.respond_to?(:regenerate_token)).to be(true)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
context 'when a symbol is passed' do
|
|
17
|
+
let(:user) { SpecialUser.create }
|
|
18
|
+
|
|
19
|
+
it 'uses it as an attribute name' do
|
|
20
|
+
expect(user.respond_to?(:regenerate_shared_secret)).to be(true)
|
|
21
|
+
expect(user.respond_to?(:regenerate_token)).to be(false)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe 'cost' do
|
|
27
|
+
context 'when no value provided' do
|
|
28
|
+
it 'defaults to BCrypt::Engine::DEFAULT_COST' do
|
|
29
|
+
expect(BCrypt::Password).to receive(:create).with(
|
|
30
|
+
raw_token,
|
|
31
|
+
cost: BCrypt::Engine::DEFAULT_COST
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
user.token = raw_token
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
context 'when an integer is provided' do
|
|
39
|
+
let(:user) { CostedUser.new }
|
|
40
|
+
|
|
41
|
+
it 'accepts that instead' do
|
|
42
|
+
expect(BCrypt::Password).to receive(:create).with(
|
|
43
|
+
raw_token,
|
|
44
|
+
cost: 9
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
user.token = raw_token
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
context 'instance methods' do
|
|
54
|
+
before do
|
|
55
|
+
allow(User).to receive(:generate_token).and_return(raw_token)
|
|
56
|
+
allow(BCrypt::Password).to receive(:create).and_return(hashed_token)
|
|
57
|
+
allow(BCrypt::Password).to(
|
|
58
|
+
receive(:new).with(hashed_token).and_return(raw_token)
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe '#regenerate_{attribute}' do
|
|
63
|
+
it 'returns a new token' do
|
|
64
|
+
expect(user.regenerate_token).to eq(raw_token)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'hashes the new token and stores it in the database' do
|
|
68
|
+
user.regenerate_token
|
|
69
|
+
|
|
70
|
+
expect(user.reload.token).to eq(hashed_token)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
describe '#authenticate_{attribute}' do
|
|
75
|
+
before do
|
|
76
|
+
user.regenerate_token
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
context 'when passed an plain text token' do
|
|
80
|
+
it 'returns true if it matches the stored value' do
|
|
81
|
+
expect(user.authenticate_token(raw_token)).to eq(true)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'returns false if it does not match the stored value' do
|
|
85
|
+
expect(user.authenticate_token('derp derp')).to eq(false)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
context 'when passed bad data' do
|
|
90
|
+
before do
|
|
91
|
+
allow(BCrypt::Password).to receive(:new).and_raise(BCrypt::Error)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it 'returns false' do
|
|
95
|
+
expect(user.authenticate_token({ bad: 'data' })).to eq(false)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe '#{attribute}=' do
|
|
101
|
+
context 'when passed a value' do
|
|
102
|
+
it 'hashes it and stores the hashed value in the model instance' do
|
|
103
|
+
user.token = raw_token
|
|
104
|
+
|
|
105
|
+
expect(user.token).to eq(hashed_token)
|
|
106
|
+
expect(User.find(user.id).token).to be_nil
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'returns the original value' do
|
|
110
|
+
expect(user.token = raw_token).to eq(raw_token)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
describe 'class methods' do
|
|
117
|
+
describe '.generate_token' do
|
|
118
|
+
let(:random_token) { 'abc123' }
|
|
119
|
+
|
|
120
|
+
context 'with no arguments' do
|
|
121
|
+
it 'returns a token 24 chars in length' do
|
|
122
|
+
expect(User.generate_token.size).to eq(24)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
context 'when passing a length' do
|
|
127
|
+
it 'validates the length is coercable to an integer' do
|
|
128
|
+
expect{ User.generate_token(12) }.not_to raise_error
|
|
129
|
+
expect{ User.generate_token(false) }.to raise_error(ArgumentError)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it 'returns a token of that length' do
|
|
133
|
+
expect(User.generate_token(20).size).to eq(20)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'byebug'
|
|
2
|
+
require 'database_cleaner'
|
|
3
|
+
require 'has_protected_token'
|
|
4
|
+
require_relative './support/model'
|
|
5
|
+
|
|
6
|
+
RSpec.configure do |config|
|
|
7
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
|
8
|
+
|
|
9
|
+
config.expect_with :rspec do |expectations|
|
|
10
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
config.mock_with :rspec do |mocks|
|
|
14
|
+
mocks.verify_partial_doubles = true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
config.before :suite do
|
|
18
|
+
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
|
|
19
|
+
ActiveRecord::Migration.suppress_messages do
|
|
20
|
+
load 'support/schema.rb'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
config.before :each do
|
|
25
|
+
DatabaseCleaner.strategy = :transaction
|
|
26
|
+
DatabaseCleaner.start
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
config.after :each do
|
|
30
|
+
DatabaseCleaner.clean
|
|
31
|
+
end
|
|
32
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: has_protected_token
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.0.pre.alpha
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- David Allen
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2019-10-06 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activerecord
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '3.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: bcrypt
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 3.1.1
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 3.1.1
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: bundler
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: 1.17.3
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 1.17.3
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rspec
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: sqlite3
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: byebug
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: database_cleaner
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: bump
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0'
|
|
132
|
+
type: :development
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
139
|
+
description: Generate random tokens (or use your own) for any ActiveRecord model.
|
|
140
|
+
Hashes and salts the token before storage in the database using the same methodology
|
|
141
|
+
as has_secure_password.
|
|
142
|
+
email: 1337dallen@gmail.com
|
|
143
|
+
executables: []
|
|
144
|
+
extensions: []
|
|
145
|
+
extra_rdoc_files: []
|
|
146
|
+
files:
|
|
147
|
+
- ".gitignore"
|
|
148
|
+
- ".rspec"
|
|
149
|
+
- ".ruby-version"
|
|
150
|
+
- ".travis.yml"
|
|
151
|
+
- Gemfile
|
|
152
|
+
- Gemfile.lock
|
|
153
|
+
- LICENSE.txt
|
|
154
|
+
- README.md
|
|
155
|
+
- Rakefile
|
|
156
|
+
- has_protected_token.gemspec
|
|
157
|
+
- lib/has_protected_token.rb
|
|
158
|
+
- spec/lib/has_protected_token_spec.rb
|
|
159
|
+
- spec/spec_helper.rb
|
|
160
|
+
- spec/support/model.rb
|
|
161
|
+
- spec/support/schema.rb
|
|
162
|
+
homepage: https://github.com/StaphSynth/has_protected_token
|
|
163
|
+
licenses:
|
|
164
|
+
- MIT
|
|
165
|
+
metadata: {}
|
|
166
|
+
post_install_message:
|
|
167
|
+
rdoc_options: []
|
|
168
|
+
require_paths:
|
|
169
|
+
- lib
|
|
170
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
171
|
+
requirements:
|
|
172
|
+
- - ">="
|
|
173
|
+
- !ruby/object:Gem::Version
|
|
174
|
+
version: '0'
|
|
175
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
176
|
+
requirements:
|
|
177
|
+
- - ">"
|
|
178
|
+
- !ruby/object:Gem::Version
|
|
179
|
+
version: 1.3.1
|
|
180
|
+
requirements: []
|
|
181
|
+
rubyforge_project:
|
|
182
|
+
rubygems_version: 2.7.6.2
|
|
183
|
+
signing_key:
|
|
184
|
+
specification_version: 4
|
|
185
|
+
summary: Easily generate random tokens for any ActiveRecord model and store them securely
|
|
186
|
+
in the database.
|
|
187
|
+
test_files: []
|