attribute_queryable_encrypted 0.0.1
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/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +29 -0
- data/Rakefile +2 -0
- data/attribute_queryable_encrypted.gemspec +24 -0
- data/lib/attribute_queryable_encrypted.rb +10 -0
- data/lib/attribute_queryable_encrypted/adapters/active_record.rb +46 -0
- data/lib/attribute_queryable_encrypted/core_ext/lower_higher.rb +17 -0
- data/lib/attribute_queryable_encrypted/core_ext/prefix.rb +24 -0
- data/lib/attribute_queryable_encrypted/core_ext/stretch_digest.rb +19 -0
- data/lib/attribute_queryable_encrypted/prefix_attributes.rb +81 -0
- data/lib/attribute_queryable_encrypted/railtie.rb +0 -0
- data/lib/attribute_queryable_encrypted/version.rb +3 -0
- data/readme.md +72 -0
- data/spec/active_record_adapter_spec.rb +31 -0
- data/spec/attribute_queryable_encrypted_spec.rb +46 -0
- data/spec/db/schema.rb +8 -0
- data/spec/spec_helper.rb +3 -0
- metadata +111 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
attribute_queryable_encrypted (0.0.1)
|
5
|
+
active_support (>= 3.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
active_support (3.0.0)
|
11
|
+
activesupport (= 3.0.0)
|
12
|
+
activesupport (3.0.0)
|
13
|
+
diff-lcs (1.1.3)
|
14
|
+
rspec (2.7.0)
|
15
|
+
rspec-core (~> 2.7.0)
|
16
|
+
rspec-expectations (~> 2.7.0)
|
17
|
+
rspec-mocks (~> 2.7.0)
|
18
|
+
rspec-core (2.7.1)
|
19
|
+
rspec-expectations (2.7.0)
|
20
|
+
diff-lcs (~> 1.1.2)
|
21
|
+
rspec-mocks (2.7.0)
|
22
|
+
|
23
|
+
PLATFORMS
|
24
|
+
ruby
|
25
|
+
|
26
|
+
DEPENDENCIES
|
27
|
+
active_support (>= 3.0)
|
28
|
+
attribute_queryable_encrypted!
|
29
|
+
rspec
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "attribute_queryable_encrypted/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "attribute_queryable_encrypted"
|
7
|
+
s.version = AttributeQueryableEncrypted::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Scott Burton"]
|
10
|
+
s.email = ["scott@chaione.com"]
|
11
|
+
s.homepage = "https://github.com/chaione/attribute_queryable_encrypted"
|
12
|
+
s.summary = %q{Makes querying encrypted & salted attributes mildly less horrible}
|
13
|
+
s.description = %q{Makes querying encrypted & salted attributes mildly less horrible}
|
14
|
+
|
15
|
+
s.rubyforge_project = "attribute_queryable_encrypted"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency "active_support", ">= 3.0"
|
23
|
+
s.add_development_dependency "rspec"
|
24
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_support/core_ext/array/extract_options'
|
3
|
+
require 'active_support/core_ext/class/attribute'
|
4
|
+
require 'digest'
|
5
|
+
require 'attribute_queryable_encrypted/core_ext/lower_higher'
|
6
|
+
require 'attribute_queryable_encrypted/core_ext/prefix'
|
7
|
+
require 'attribute_queryable_encrypted/core_ext/stretch_digest'
|
8
|
+
require 'attribute_queryable_encrypted/prefix_attributes'
|
9
|
+
require 'attribute_queryable_encrypted/railtie' if defined? Rails
|
10
|
+
require 'attribute_queryable_encrypted/adapters/active_record' if defined? ActiveRecord
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module AttributeQueryableEncrypted
|
2
|
+
module Adapters
|
3
|
+
module ActiveRecord
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
include AttributeQueryableEncrypted::PrefixAttributes
|
7
|
+
|
8
|
+
included do
|
9
|
+
attrbute_queryable_encrypted_default_options[:encode] = true
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def attribute_queryable_encrypted *args
|
14
|
+
define_attribute_methods rescue nil
|
15
|
+
super *args
|
16
|
+
|
17
|
+
args.reject { |arg| arg.is_a?(Hash) }.each do |attribute|
|
18
|
+
options = queryable_encrypted_attributes[attribute]
|
19
|
+
|
20
|
+
find_all_by_method = proc do |prefix_value|
|
21
|
+
send("find_all_by_#{[options[:prefix], attribute, options[:suffix]].join('_')}", prefix_encrypt(prefix_value, options))
|
22
|
+
end
|
23
|
+
|
24
|
+
find_by_method = proc do |original_value|
|
25
|
+
send("find_all_by_#{[options[:prefix], attribute].join('_')}", original_value.prefix(options[:length])).detect do |result|
|
26
|
+
result[attribute] === original_value
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
singleton = class << self
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
alias_method "original_find_by_#{attribute}", "find_by_#{attribute}" if respond_to?(attribute)
|
35
|
+
|
36
|
+
singleton.send(:define_method, "find_all_by_#{[options[:prefix], attribute].join('_')}", find_all_by_method)
|
37
|
+
singleton.send(:define_method, "find_by_#{attribute}", find_by_method)
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
ActiveRecord::Base.send(:include, AttributeQueryableEncrypted::Adapters::ActiveRecord)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module AttributeQueryableEncrypted
|
2
|
+
module CoreExt
|
3
|
+
module LowerHigher
|
4
|
+
# Returns the lower of self or n
|
5
|
+
def lower(n)
|
6
|
+
self < n ? self : n
|
7
|
+
end
|
8
|
+
|
9
|
+
# Returns the higher of self or n
|
10
|
+
def higher(n)
|
11
|
+
self > n ? self : n
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Numeric.send(:include, AttributeQueryableEncrypted::CoreExt::LowerHigher)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module AttributeQueryableEncrypted
|
2
|
+
module CoreExt
|
3
|
+
module Prefix
|
4
|
+
# Returns an integer of the available length provided
|
5
|
+
# a fixed or percentage-based requested length, or self's
|
6
|
+
# length, whichever is lowest.
|
7
|
+
#
|
8
|
+
# Examples
|
9
|
+
# "This is a string".prefix_length #=> 8
|
10
|
+
# "This is a string".prefix_length(1000) => 16
|
11
|
+
# "This is a string".prefix_length("75%") => 12
|
12
|
+
#
|
13
|
+
def prefix_length(requested_length)
|
14
|
+
requested_length.is_a?(Numeric) ? length.lower(requested_length) : (length/(100/requested_length.match(/^([0-9.]+)%$/)[0].to_f)).ceil
|
15
|
+
end
|
16
|
+
|
17
|
+
def prefix(requested_length)
|
18
|
+
self[0, prefix_length(requested_length)]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
String.send(:include, AttributeQueryableEncrypted::CoreExt::Prefix)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module AttributeQueryableEncrypted
|
2
|
+
module CoreExt
|
3
|
+
module StretchDigest
|
4
|
+
def stretch_digest(options={})
|
5
|
+
options[:digest] ||= Digest::SHA2
|
6
|
+
options[:stretches] ||= 1
|
7
|
+
|
8
|
+
digest = options[:digest].new
|
9
|
+
options[:stretches].times do
|
10
|
+
string = options[:key] ? self + options[:key] : self
|
11
|
+
digest.update(string)
|
12
|
+
end
|
13
|
+
digest.to_s
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
String.send(:include, AttributeQueryableEncrypted::CoreExt::StretchDigest)
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module AttributeQueryableEncrypted
|
2
|
+
module PrefixAttributes
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :queryable_encrypted_attributes, :attrbute_queryable_encrypted_default_options
|
7
|
+
self.queryable_encrypted_attributes = {}
|
8
|
+
self.attrbute_queryable_encrypted_default_options = {
|
9
|
+
:length => "50%",
|
10
|
+
:prefix => "prefix",
|
11
|
+
:suffix => "digest",
|
12
|
+
:encode => false,
|
13
|
+
:stretches => 3
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
|
19
|
+
# Assigns a digest-hashed value to an attribute writer using a portion of the
|
20
|
+
# value assigned to each attribute's normal writer. The digest-hashed prefix
|
21
|
+
# can then be used to identify other objects with the same prefix without
|
22
|
+
# revealing the underlying value.
|
23
|
+
#
|
24
|
+
# Example:
|
25
|
+
# --------
|
26
|
+
# class HiddenValue
|
27
|
+
# include AttributeQueryableEncrypted::PrefixAttributes
|
28
|
+
# attr_writer :data
|
29
|
+
# attr_accessor :prefix_data_digest
|
30
|
+
# attribute_queryable_encrypted :data
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# hider = HiddenValue.new
|
34
|
+
#
|
35
|
+
# hider.data = "This is a string"
|
36
|
+
# hider.prefix_data_digest
|
37
|
+
# # => "a37010c994067764d86540bf479d93b4d0c3bb3955de7b61f951caf2fd0301b0"
|
38
|
+
#
|
39
|
+
# This technique is valuable when the queryable encrypted attribute is not
|
40
|
+
# persisted, or is persisted in a non-deterministic way (i.e. a salted, encrypted
|
41
|
+
# database column)
|
42
|
+
#
|
43
|
+
#
|
44
|
+
# Options:
|
45
|
+
# --------
|
46
|
+
# :length - an integer value length, or percentage expressed as a string ("72%")
|
47
|
+
# :prefix - prefix name for the storage accessor. Default is "prefix"
|
48
|
+
# :suffix - suffix name for the storage accessor. Defuault is "suffix"
|
49
|
+
# :encode - Base64 encode the digest hash, suitable for database persistence. Default is false.
|
50
|
+
#
|
51
|
+
def attribute_queryable_encrypted *attributes
|
52
|
+
|
53
|
+
options = attrbute_queryable_encrypted_default_options.merge(attributes.extract_options!)
|
54
|
+
|
55
|
+
attributes.each do |attribute|
|
56
|
+
queryable_encrypted_attributes[attribute] = options
|
57
|
+
class_eval do
|
58
|
+
alias_method "unprefixed_#{attribute}=".to_sym, "#{attribute}=".to_sym
|
59
|
+
|
60
|
+
define_method "#{attribute}=", do |*args, &blk|
|
61
|
+
send("#{[options[:prefix], attribute, options[:suffix]].join('_')}=".to_sym, prefix_encrypt(args[0], options))
|
62
|
+
send("unprefixed_#{attribute}=".to_sym, *args, &blk)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def prefix_encrypt(value, options)
|
69
|
+
prefix_encrypted_value = value.prefix(options[:length]).stretch_digest(options)
|
70
|
+
prefix_encrypted_value = [prefix_encrypted_value].pack("m*") if options[:encode]
|
71
|
+
prefix_encrypted_value
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
module InstanceMethods
|
76
|
+
def prefix_encrypt(value, options)
|
77
|
+
self.class.prefix_encrypt(value, options)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
File without changes
|
data/readme.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
AttributeQueryableEncrypted
|
2
|
+
===========================
|
3
|
+
Assigns a digest-hashed value to an attribute writer using a portion of the value assigned to each attribute's normal writer. The digest-hashed prefix can then be used to identify other objects with the same prefix without revealing the underlying value.
|
4
|
+
|
5
|
+
AttributeQueryableEncrypted was inspried by shuber's excellent [attr_encrypted](https://github.com/shuber/attr_encrypted) gem, and aims for compatibility. It attempts to addresses a shortcoming of encryption, where encrypted columns are queryable when unsalted, but attackable using a precomputed "rainbow table".
|
6
|
+
|
7
|
+
Selecting multiple candidates with matching prefix digests and subsequently decrypting the full salted/encrypted data field to find a exact match will reduce the need for a full table scan. You should use attr_encrypted, or your own crypto logic, to handle encrypting and decrypting the appropriate full data field.
|
8
|
+
|
9
|
+
Example:
|
10
|
+
--------
|
11
|
+
class HiddenValue
|
12
|
+
include AttributeQueryableEncrypted::PrefixAttributes
|
13
|
+
attr_accessor :prefix_data_digest
|
14
|
+
attribute_queryable_encrypted :data
|
15
|
+
|
16
|
+
def data=(something)
|
17
|
+
...something fancy that obscures the data...
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
hider = HiddenValue.new
|
22
|
+
|
23
|
+
hider.data = "This is a string"
|
24
|
+
hider.prefix_data_digest
|
25
|
+
# => "a37010c994067764d86540bf479d93b4d0c3bb3955de7b61f951caf2fd0301b0"
|
26
|
+
|
27
|
+
|
28
|
+
ActiveRecord:
|
29
|
+
-------------
|
30
|
+
ActiveRecord users gain a query method for their prefix digest column:
|
31
|
+
|
32
|
+
HiddenValue.find_all_by_prefix_data("This is ")
|
33
|
+
# => [#<HiddenValue id: 1, encrypted_data: "MWE2ODg0ZTVmNTA2M2I3MTZmMWQxZGI3NzA0MjgyMzRj...", prefix_data_digest: "MTgyODBlMWFkNGZiMjAyZTc5Y2FiYTcxODZhYTg1OWM3OGNhOWI...">, #<HiddenValue id: 2, encrypted_data: "WQxZGI3NzANTA2M2I3MTZmMj0MjgyMzRMWE2ODg0ZTVm...", prefix_data_digest: "MTgyODBlMWFkNGZiMjAyZTc5Y2FiYTcxODZhYTg1OWM3OGNhOWI...">]
|
34
|
+
|
35
|
+
The returned records - a subset of the full table - can then be iterated over to find an exact match.
|
36
|
+
|
37
|
+
A convenience method is provided to do this for you - note that it requires an attribute getter method (but not necessarily a database column) that provides a cleartext match for your query argument:
|
38
|
+
|
39
|
+
HiddenValue.find_by_prefix_data("This is a string")
|
40
|
+
# => #<HiddenValue id: 1, encrypted_data: "MWE2ODg0ZTVmNTA2M2I3MTZmMWQxZGI3NzA0MjgyMzRj...", prefix_data_digest: "MTgyODBlMWFkNGZiMjAyZTc5Y2FiYTcxODZhYTg1OWM3OGNhOWI...">
|
41
|
+
|
42
|
+
You'll need to create an appropriately-named prefix digest column on your own.
|
43
|
+
|
44
|
+
|
45
|
+
Options:
|
46
|
+
--------
|
47
|
+
* :length - an integer value length, or percentage expressed as a string ("72%"). Default is "50%".
|
48
|
+
* :prefix - prefix name for the storage accessor. Default is "prefix"
|
49
|
+
* :suffix - suffix name for the storage accessor. Defuault is "suffix"
|
50
|
+
* :encode - Base64 encode the digest hash, suitable for database persistence. Default is false.
|
51
|
+
* :stretches - an integer number of iterations through the digest algorithm. More will reduce the ease of a precomputed attack. Default is 3.
|
52
|
+
* :key - an optional key to salt the digest algorithm. Default is nil.
|
53
|
+
|
54
|
+
If you choose to use :stretches and/or :key, you should keep their values secret.
|
55
|
+
|
56
|
+
Requirements:
|
57
|
+
-------------
|
58
|
+
* ActiveSupport >= 3.0
|
59
|
+
* ActiveRecord >= 3.0 for ActiveRecord usage
|
60
|
+
|
61
|
+
Warnings
|
62
|
+
--------
|
63
|
+
* This technique is not without shortcomings, notably that the prefix digest is subject to a precomputed attack.
|
64
|
+
* You should consider using secret values for :stretches and :key, and setting the :length option to a level that obscures an appropriate amount of your data without potentially giving away too much.
|
65
|
+
* Increasing :stretches incurs a small performance penalty.
|
66
|
+
* Decreasing :length can return more records in the initial matched set, potentially decreasing performance.
|
67
|
+
|
68
|
+
Copyright
|
69
|
+
---------
|
70
|
+
(The MIT License)
|
71
|
+
|
72
|
+
© 2011 (Scott Burton, ChaiOne)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'logger'
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3",
|
6
|
+
:database => File.dirname(__FILE__) + "/db/attribute_queryable_encrypted.sqlite3")
|
7
|
+
|
8
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
9
|
+
ActiveRecord::Base.logger.level = 3
|
10
|
+
|
11
|
+
load File.dirname(__FILE__) + '/db/schema.rb'
|
12
|
+
|
13
|
+
class TestModel < ActiveRecord::Base
|
14
|
+
attribute_queryable_encrypted :data, :length => 9
|
15
|
+
end
|
16
|
+
|
17
|
+
describe TestModel do
|
18
|
+
before(:all) do
|
19
|
+
@match1, @match2, @not_match = ["This is a string", "This is another string", "This string doesn't match"].map {|data| TestModel.create(:data => data)}
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "class query methods" do
|
23
|
+
it "finds the model instances with the appropriate prefix data" do
|
24
|
+
TestModel.find_all_by_prefix_data("This is a").should eql([@match1, @match2])
|
25
|
+
end
|
26
|
+
|
27
|
+
it "finds the first exact match for the full original value" do
|
28
|
+
TestModel.find_by_data("This is another string").should eql @match2
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class TestModel
|
4
|
+
attr_accessor :default_length_data, :prefix_default_length_data_digest
|
5
|
+
attr_accessor :fixed_length_data, :prefix_fixed_length_data_digest
|
6
|
+
attr_accessor :percentage_length_data, :prefix_percentage_length_data_digest
|
7
|
+
include AttributeQueryableEncrypted::PrefixAttributes
|
8
|
+
attrbute_queryable_encrypted_default_options[:encode] = false
|
9
|
+
end
|
10
|
+
|
11
|
+
describe TestModel do
|
12
|
+
let(:input_string) {"It's a wicked string"}
|
13
|
+
context "without a :length" do
|
14
|
+
before(:all) do
|
15
|
+
TestModel.attribute_queryable_encrypted :default_length_data
|
16
|
+
end
|
17
|
+
before(:each) do
|
18
|
+
subject.default_length_data = input_string
|
19
|
+
end
|
20
|
+
|
21
|
+
its(:prefix_default_length_data_digest) {should eql "bc1b6f5cd503fad53c32b002176ca65f0c7409194ecb987825d6875ce1392aa1"}
|
22
|
+
its(:default_length_data) {should eql input_string}
|
23
|
+
end
|
24
|
+
|
25
|
+
context "with a :length" do
|
26
|
+
context "as an integer" do
|
27
|
+
before(:all) do
|
28
|
+
TestModel.attribute_queryable_encrypted :fixed_length_data, :length => 14
|
29
|
+
end
|
30
|
+
before(:each) do
|
31
|
+
subject.fixed_length_data = input_string
|
32
|
+
end
|
33
|
+
|
34
|
+
its(:prefix_fixed_length_data_digest) {should eql "9c0dcb0f5d3429f9ac6c8d7bd1e5b056fb326f677806dbd8d813d046e7f3e764"}
|
35
|
+
end
|
36
|
+
|
37
|
+
context "as a string percentage" do
|
38
|
+
before(:each) do
|
39
|
+
TestModel.attribute_queryable_encrypted :percentage_length_data, :length => "75%"
|
40
|
+
subject.percentage_length_data = input_string
|
41
|
+
end
|
42
|
+
|
43
|
+
its(:prefix_percentage_length_data_digest) {should eql "0594e426cced44e4ea358b0dbc10f71ba622661a43a0f3b460a2437e85b43ddc"}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/spec/db/schema.rb
ADDED
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: attribute_queryable_encrypted
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Scott Burton
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-11-16 00:00:00 -08:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: active_support
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 3
|
30
|
+
- 0
|
31
|
+
version: "3.0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rspec
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
segments:
|
43
|
+
- 0
|
44
|
+
version: "0"
|
45
|
+
type: :development
|
46
|
+
version_requirements: *id002
|
47
|
+
description: Makes querying encrypted & salted attributes mildly less horrible
|
48
|
+
email:
|
49
|
+
- scott@chaione.com
|
50
|
+
executables: []
|
51
|
+
|
52
|
+
extensions: []
|
53
|
+
|
54
|
+
extra_rdoc_files: []
|
55
|
+
|
56
|
+
files:
|
57
|
+
- .gitignore
|
58
|
+
- Gemfile
|
59
|
+
- Gemfile.lock
|
60
|
+
- Rakefile
|
61
|
+
- attribute_queryable_encrypted.gemspec
|
62
|
+
- lib/attribute_queryable_encrypted.rb
|
63
|
+
- lib/attribute_queryable_encrypted/adapters/active_record.rb
|
64
|
+
- lib/attribute_queryable_encrypted/core_ext/lower_higher.rb
|
65
|
+
- lib/attribute_queryable_encrypted/core_ext/prefix.rb
|
66
|
+
- lib/attribute_queryable_encrypted/core_ext/stretch_digest.rb
|
67
|
+
- lib/attribute_queryable_encrypted/prefix_attributes.rb
|
68
|
+
- lib/attribute_queryable_encrypted/railtie.rb
|
69
|
+
- lib/attribute_queryable_encrypted/version.rb
|
70
|
+
- readme.md
|
71
|
+
- spec/active_record_adapter_spec.rb
|
72
|
+
- spec/attribute_queryable_encrypted_spec.rb
|
73
|
+
- spec/db/schema.rb
|
74
|
+
- spec/spec_helper.rb
|
75
|
+
has_rdoc: true
|
76
|
+
homepage: https://github.com/chaione/attribute_queryable_encrypted
|
77
|
+
licenses: []
|
78
|
+
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
segments:
|
98
|
+
- 0
|
99
|
+
version: "0"
|
100
|
+
requirements: []
|
101
|
+
|
102
|
+
rubyforge_project: attribute_queryable_encrypted
|
103
|
+
rubygems_version: 1.3.7
|
104
|
+
signing_key:
|
105
|
+
specification_version: 3
|
106
|
+
summary: Makes querying encrypted & salted attributes mildly less horrible
|
107
|
+
test_files:
|
108
|
+
- spec/active_record_adapter_spec.rb
|
109
|
+
- spec/attribute_queryable_encrypted_spec.rb
|
110
|
+
- spec/db/schema.rb
|
111
|
+
- spec/spec_helper.rb
|