encrypt_attributes 0.0.1

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: 54d5d3aa82796fd065586ae0b609832b930a8c29
4
+ data.tar.gz: 865088b383ee116f2e16a0621209586f0ca856b2
5
+ SHA512:
6
+ metadata.gz: 2b0b1f47209ca40d8a36c94798f0de32b778c4f787433240920154282701824306ed4a7e9bd9b72d1a2766326edb231b428484bbeac0bceb327de81538392e70
7
+ data.tar.gz: 76d23dd15a2cf34783a90ea7a6e19f8c1773c4e3f9267c1c34aed82757025ca5783c1b3f6ff033b0ed428df05e364ed2b3402d44e69a38f3dc0ae65b559e847e
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ db/*.sqlite3
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
4
+ services:
5
+ - mongodb
6
+ before_script:
7
+ - cp config/database.yml.travis config/database.yml
8
+ script:
9
+ - bundle exec rspec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in encrypt_attributes.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Artur Hebda
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
@@ -0,0 +1,137 @@
1
+ # EncryptedAttributes
2
+
3
+ [![Build Status](https://travis-ci.org/aenain/encrypted_attributes.svg?branch=master)](https://travis-ci.org/aenain/encrypted_attributes)
4
+
5
+ This gem provides a dead-simple encryption of string / text attributes of ActiveRecord and Mongoid models. Encryptor internally uses [ActiveSupport::MessageEncryptor](http://api.rubyonrails.org/classes/ActiveSupport/MessageEncryptor.html) and therefore it uses 'aes-256-cbc' cipher by default. Currently there is no way to pass custom options to the encryptor. Gem does NOT require column with different name. It works just fine without it, but works only with columns of type `string` or `text`. From user perspective encryption is completely transparent - they use decrypted values all the time, but encrypted values are stored in database.
6
+
7
+ ## ORM Support
8
+
9
+ Integration with ActiveRecord and Mongoid has been tested. Other frameworks should work as expected if they provide `#read_attribute` and `#write_attribute` methods. They are supposed to work in similar way as in ActiveRecord.
10
+
11
+ ## Recommendations
12
+
13
+ Provided functionality is sufficient for storing oauth tokens and secrets that are not to be searched for. If you are looking for something more sophisticated, then check out my recommendations below.
14
+
15
+ [attr_encrypted](https://github.com/attr-encrypted/attr_encrypted) extends `Object`, so it works with any Ruby class, but requires an encrypted attribute to have a different name than the regular one.
16
+
17
+ [CryptKepper](http://jmazzi.github.io/crypt_keeper/) is a perfect solution if you need to search through encrypted fields, but supports ActiveRecord only.
18
+
19
+ [symmetric-encryption](https://github.com/reidmorrison/symmetric-encryption) is based on RSA-encrypted keys and supports ActiveRecord, MongoMapper and Mongoid. It provides type coercion and can be used to secure configuration files as well. PCI compliant.
20
+
21
+ ## Installation
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ gem 'encrypt_attributes'
26
+
27
+ And then execute:
28
+
29
+ $ bundle
30
+
31
+ Or install it yourself as:
32
+
33
+ $ gem install encrypt_attributes
34
+
35
+ ## Usage
36
+
37
+ Provide a secret for `Encryptor`. Create a model with fields of type `string` or `text`, include `EncryptAttributes::Model` and call `encrypt_attrs(*attrs)`.
38
+
39
+ ### Secret
40
+
41
+ If you are using Rails, secret by default will be set to `Rails.application.secrets.secret_key_base`. If you want to use a different secret, create an initializer:
42
+
43
+ # config/initializers/encrypted_attributes.rb
44
+ EncryptAttributes::Encryptor.secret = "<super-secure-key>"
45
+
46
+ If you do not use Rails, set the secret before any encryption is done, otherwise an error will be thrown.
47
+
48
+ ### Models
49
+
50
+ * ActiveRecord
51
+
52
+ ```
53
+ # db/migrate/create_authentications.rb
54
+ class CreateAuthentication < ActiveRecord::Migration
55
+ def change
56
+ create_table :authentications, force: true do |t|
57
+ t.string :token
58
+ t.string :secret
59
+ end
60
+ end
61
+ end
62
+ ```
63
+
64
+ ```
65
+ # app/models/authentication.rb
66
+ class Authentication < ActiveRecord::Base
67
+ include EncryptAttributes::Model
68
+ encrypt_attrs :token, :secret
69
+ end
70
+ ```
71
+
72
+ * Mongoid
73
+
74
+ ```
75
+ # app/models/authentication.rb
76
+ class Authentication
77
+ include Mongoid::Document
78
+ # it is important to include Mongoid::Document before
79
+ include EncryptAttributes::Model
80
+
81
+ field :token, type: String
82
+ field :secret, type: String
83
+
84
+ encrypt_attrs :token, :secret
85
+ end
86
+ ```
87
+
88
+ ### Custom accessors
89
+
90
+ If you want to define custom accessors for encrypted attributes, create methods you need to override and call `super` to execute encryption / decryption. It will work the same way with all supported ORMs.
91
+
92
+ # app/models/authentication.rb
93
+ class Authentication < ActiveRecord::Base
94
+ include EncryptAttributes::Model
95
+ encrypt_attrs :token, :secret
96
+
97
+ def secret
98
+ super.tap do |decrypted|
99
+ # do whatever you want here
100
+ end
101
+ end
102
+
103
+ def secret=(new_secret)
104
+ super
105
+ # do whatever you want here
106
+ end
107
+ end
108
+
109
+ ### Gotchas
110
+
111
+ Beware that each time encryption is done it returns a different value for the same input argument. Check out the consequences on the example below.
112
+
113
+ user = User.create(secret: "secret")
114
+ user.secret = "secret" # the same value
115
+ user.secret_changed? # returns true
116
+
117
+ ORMs will think that the secret has changed, because after setting a value, it gets encoded and a new value is stored in `attributes`.
118
+
119
+ ## Running tests
120
+
121
+ Before running entire suite, you have to start Mongo DB, create and migrate database for ActiveRecord. Read more about [Mongo installation](http://docs.mongodb.org/manual/installation/).
122
+ To prepare database:
123
+
124
+ bundle exec rake db:create
125
+ bundle exec rake db:migrate RACK_ENV=test
126
+
127
+ To run tests:
128
+
129
+ bundle exec rspec
130
+
131
+ ## Contributing
132
+
133
+ 1. Fork it ( https://github.com/aenain/encrypted_attributes/fork )
134
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
135
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
136
+ 4. Push to the branch (`git push origin my-new-feature`)
137
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ ENV['RACK_ENV'] ||= "development"
2
+ require "bundler/gem_tasks"
3
+
4
+ Dir[File.join(__dir__, "lib/tasks/**/*.rake")].each { |f| load f }
@@ -0,0 +1,4 @@
1
+ test:
2
+ adapter: sqlite3
3
+ timeout: 500
4
+ database: "db/test.sqlite3"
@@ -0,0 +1,4 @@
1
+ test:
2
+ adapter: sqlite3
3
+ timeout: 500
4
+ database: ":memory:"
@@ -0,0 +1,6 @@
1
+ test:
2
+ sessions:
3
+ default:
4
+ database: encrypt_attributes_test
5
+ hosts:
6
+ - localhost:27017
@@ -0,0 +1,8 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def change
3
+ create_table :users, force: true do |t|
4
+ t.string :token
5
+ t.string :secret
6
+ end
7
+ end
8
+ end
data/db/schema.rb ADDED
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+ # This file is auto-generated from the current state of the database. Instead
3
+ # of editing this file, please use the migrations feature of Active Record to
4
+ # incrementally modify your database, and then regenerate this schema definition.
5
+ #
6
+ # Note that this schema.rb definition is the authoritative source for your
7
+ # database schema. If you need to create the application database on another
8
+ # system, you should be using db:schema:load, not running all the migrations
9
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
11
+ #
12
+ # It's strongly recommended that you check this file into your version control system.
13
+
14
+ ActiveRecord::Schema.define(version: 20140610120934) do
15
+
16
+ create_table "users", force: true do |t|
17
+ t.string "token"
18
+ t.string "secret"
19
+ end
20
+
21
+ end
@@ -0,0 +1,33 @@
1
+ ENV["RACK_ENV"] ||= "test"
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'encrypt_attributes/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "encrypt_attributes"
9
+ spec.version = EncryptAttributes::VERSION
10
+ spec.authors = ["Artur Hebda"]
11
+ spec.email = ["arturhebda@gmail.com"]
12
+ spec.summary = "Dead-simple attributes encryption for ORMs"
13
+ spec.description = "This gem provides a dead-simple encryption of string / text attributes of ActiveRecord and Mongoid models. Encryptor internally uses ActiveSupport::MessageEncryptor and therefore it uses 'aes-256-cbc' cipher by default. Gem does NOT require column with different name. From user perspective encryption is completely transparent - they use decrypted values all the time, but encrypted values are stored in database."
14
+ spec.homepage = "https://github.com/aenain/encrypted_attributes"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_runtime_dependency "activesupport", ">= 4.1"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.6"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "rspec", "~> 3.0.0.rc1"
27
+ spec.add_development_dependency "rspec-mocks", "~> 3.0.0.rc1"
28
+
29
+ spec.add_development_dependency "activerecord", ">= 4.1"
30
+ spec.add_development_dependency "mongoid", "~> 4.0.0.beta1"
31
+ spec.add_development_dependency "sqlite3"
32
+ spec.add_development_dependency "pry-byebug"
33
+ end
@@ -0,0 +1,29 @@
1
+ require "active_support/core_ext/object/try"
2
+
3
+ module EncryptAttributes
4
+ class MissingAdapterError < StandardError; end
5
+
6
+ module_function
7
+
8
+ def find_adapter(base)
9
+ adapter = adapters.find do |receiver, adapter|
10
+ base.ancestors.map(&:to_s).include?(receiver)
11
+ end.try(:last)
12
+
13
+ adapter || (raise MissingAdapterError)
14
+ end
15
+
16
+ def register_adapter(receiver, adapter)
17
+ adapters[receiver] = adapter
18
+ end
19
+
20
+ def adapters
21
+ @adapters ||= {}
22
+ end
23
+ end
24
+
25
+ require "encrypt_attributes/version"
26
+ require "encrypt_attributes/macros"
27
+ require "encrypt_attributes/encryptor"
28
+ require "encrypt_attributes/model"
29
+ require "encrypt_attributes/adapters"
@@ -0,0 +1,7 @@
1
+ module EncryptAttributes
2
+ module Adapters
3
+ end
4
+ end
5
+
6
+ require "encrypt_attributes/adapters/base"
7
+ Dir[File.join(__dir__, 'adapters', '*.rb')].each { |adapter| require adapter }
@@ -0,0 +1,10 @@
1
+ module EncryptAttributes
2
+ module Adapters
3
+ module ActiveRecord
4
+ include Base
5
+ end
6
+ end
7
+
8
+ self.register_adapter("ActiveRecord::Base",
9
+ EncryptAttributes::Adapters::ActiveRecord)
10
+ end
@@ -0,0 +1,19 @@
1
+ module EncryptAttributes
2
+ module Adapters
3
+ module Base
4
+ def read_encrypted_attribute(attr_name)
5
+ encryptor.decrypt(read_attribute(attr_name))
6
+ end
7
+
8
+ def write_encrypted_attribute(attr_name, attr_value)
9
+ write_attribute(attr_name, encryptor.encrypt(attr_value))
10
+ end
11
+
12
+ private
13
+
14
+ def encryptor
15
+ Encryptor.new
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ module EncryptAttributes
2
+ module Adapters
3
+ module Mongoid
4
+ include Base
5
+ end
6
+ end
7
+
8
+ self.register_adapter("Mongoid::Document",
9
+ EncryptAttributes::Adapters::Mongoid)
10
+ end
@@ -0,0 +1,27 @@
1
+ require "active_support/core_ext/module/attribute_accessors"
2
+ require "active_support/message_encryptor"
3
+ require "encrypt_attributes"
4
+
5
+ module EncryptAttributes
6
+ class Encryptor
7
+ cattr_accessor :secret
8
+
9
+ if defined?(Rails)
10
+ self.secret ||= Rails.application.secrets.secret_key_base
11
+ end
12
+
13
+ def encrypt(value)
14
+ return value if value.to_s.empty?
15
+ message_encryptor.encrypt_and_sign(value)
16
+ end
17
+
18
+ def decrypt(value)
19
+ return value if value.to_s.empty?
20
+ message_encryptor.decrypt_and_verify(value)
21
+ end
22
+
23
+ def message_encryptor
24
+ ActiveSupport::MessageEncryptor.new(self.class.secret)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ module EncryptAttributes
2
+ module Macros
3
+ def encrypt_attrs(*attr_names)
4
+ mod = Module.new
5
+ mod.module_eval do
6
+ attr_names.each do |attr_name|
7
+ # getter
8
+ define_method(attr_name) do
9
+ read_encrypted_attribute(attr_name)
10
+ end
11
+
12
+ # setter
13
+ define_method("#{attr_name}=") do |value|
14
+ write_encrypted_attribute(attr_name, value)
15
+ end
16
+ end
17
+ end
18
+ include mod
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ require "encrypt_attributes"
2
+
3
+ module EncryptAttributes
4
+ module Model
5
+ def self.included(base)
6
+ base.class_eval do
7
+ include EncryptAttributes.find_adapter(base)
8
+ end
9
+ base.extend(Macros)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module EncryptAttributes
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,21 @@
1
+ # Excerpt from https://github.com/janko-m/sinatra-activerecord/blob/master/lib/sinatra/activerecord/tasks.rake
2
+ require "active_record"
3
+ require "sqlite3"
4
+
5
+ load "active_record/railties/databases.rake"
6
+
7
+ ActiveRecord::Base.configurations = YAML.load_file("config/database.yml")
8
+
9
+ ActiveRecord::Tasks::DatabaseTasks.tap do |config|
10
+ config.root = Rake.application.original_dir
11
+ config.env = "test"
12
+ config.db_dir = "db"
13
+ config.migrations_paths = ["db/migrate"]
14
+ config.database_configuration = ActiveRecord::Base.configurations
15
+ end
16
+
17
+ namespace :db do
18
+ task :environment do
19
+ ActiveRecord::Base.establish_connection(:test)
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ require "spec_helper"
2
+ require "support/connections/active_record"
3
+
4
+ require "encrypt_attributes/model"
5
+
6
+ class User < ActiveRecord::Base
7
+ include EncryptAttributes::Model
8
+ encrypt_attrs :secret
9
+ end unless defined?(User)
10
+
11
+ describe User do
12
+ after(:all) { User.delete_all }
13
+ include_examples "adapter"
14
+ end
@@ -0,0 +1,18 @@
1
+ require "spec_helper"
2
+ require "support/connections/mongoid"
3
+
4
+ require "encrypt_attributes/model"
5
+
6
+ class MongoidUser
7
+ include Mongoid::Document
8
+ include EncryptAttributes::Model
9
+
10
+ field :secret, type: String
11
+
12
+ encrypt_attrs :secret
13
+ end unless defined?(MongoidUser)
14
+
15
+ describe MongoidUser do
16
+ after(:all) { Mongoid.purge! }
17
+ include_examples "adapter"
18
+ end
@@ -0,0 +1,91 @@
1
+ require "spec_helper"
2
+ require "encrypt_attributes/encryptor"
3
+
4
+ def set_valid_encryptor_secret
5
+ EncryptAttributes::Encryptor.secret = EncryptorHelpers::VALID_SECRET
6
+ end
7
+
8
+ describe EncryptAttributes::Encryptor do
9
+ include MessageEncryptorHelpers
10
+
11
+ it { should respond_to(:secret) }
12
+ it { should respond_to(:secret=) }
13
+
14
+ describe "#message_encryptor" do
15
+ let(:secret) { "secret" }
16
+ let(:encryptor_class) { ActiveSupport::MessageEncryptor }
17
+
18
+ it "uses secret to instantiate encryptor" do
19
+ described_class.secret = secret
20
+ allow(encryptor_class).to receive(:new)
21
+ described_class.new.message_encryptor
22
+ expect(encryptor_class).to have_received(:new).with(secret)
23
+ end
24
+ end
25
+
26
+ describe "#encrypt" do
27
+ subject { described_class.new }
28
+
29
+ before(:each) do
30
+ set_valid_encryptor_secret
31
+ end
32
+
33
+ context "nil" do
34
+ it "returns nil" do
35
+ expect(subject.encrypt(nil)).to be_nil
36
+ end
37
+ end
38
+
39
+ context "empty string" do
40
+ it "returns empty string" do
41
+ expect(subject.encrypt("")).to eq ""
42
+ end
43
+ end
44
+
45
+ context "not empty value" do
46
+ let(:decrypted) { "decrypted" }
47
+
48
+ it "encrypts correctly" do
49
+ expect(subject.encrypt(decrypted))
50
+ .to be_encryption_of(decrypted)
51
+ end
52
+ end
53
+ end
54
+
55
+ describe "#decrypt" do
56
+ subject { described_class.new }
57
+
58
+ before(:each) do
59
+ set_valid_encryptor_secret
60
+ end
61
+
62
+ context "nil" do
63
+ it "returns nil" do
64
+ expect(subject.decrypt(nil)).to be_nil
65
+ end
66
+ end
67
+
68
+ context "empty string" do
69
+ it "returns empty string" do
70
+ expect(subject.decrypt("")).to eq ""
71
+ end
72
+ end
73
+
74
+ context "not empty value" do
75
+ let(:decrypted) { "decrypted" }
76
+ let(:encrypted) { subject.encrypt(decrypted) }
77
+
78
+ it "decrypts correctly" do
79
+ expect(subject.decrypt(encrypted)).to eq decrypted
80
+ end
81
+ end
82
+
83
+ context "tampered value" do
84
+ it "raises error" do
85
+ expect {
86
+ subject.decrypt("tampered")
87
+ }.to raise_error(ActiveSupport::MessageVerifier::InvalidSignature)
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,6 @@
1
+ $LOAD_PATH.unshift(File.join(__dir__, "..", "lib"))
2
+ Dir[File.join(__dir__, "support/**/*.rb")].each { |f| require f }
3
+
4
+ RSpec.configure do |config|
5
+ config.raise_errors_for_deprecations!
6
+ end
@@ -0,0 +1,10 @@
1
+ require "rake"
2
+
3
+ load File.join(__dir__, '..', '..', '..', 'lib', 'tasks', 'active_record.rake')
4
+
5
+ if ENV['TRAVIS']
6
+ Rake::Task["db:create"].invoke
7
+ Rake::Task["db:schema:load"].invoke
8
+ else
9
+ ActiveRecord::Base.establish_connection(:test)
10
+ end
@@ -0,0 +1,6 @@
1
+ require "mongoid"
2
+
3
+ Mongoid.configure do |config|
4
+ raw_config = YAML.load(File.read("config/mongoid.yml"))
5
+ config.sessions = raw_config["test"]["sessions"]
6
+ end
@@ -0,0 +1,23 @@
1
+ RSpec::Matchers.define :be_encryption_of do |expected|
2
+ match do |actual|
3
+ begin
4
+ secret = EncryptAttributes::Encryptor.secret
5
+ encryptor = ActiveSupport::MessageEncryptor.new(secret)
6
+ encryptor.decrypt_and_verify(actual) == expected
7
+ rescue ActiveSupport::MessageVerifier::InvalidSignature
8
+ false
9
+ end
10
+ end
11
+
12
+ failure_message do |actual|
13
+ "expected that #{actual} would be an encryption of #{expected}"
14
+ end
15
+
16
+ failure_message_when_negated do |actual|
17
+ "expected that #{actual} would not be an encryption of #{expected}"
18
+ end
19
+
20
+ description do
21
+ "be an encryption of #{expected}"
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ module EncryptorHelpers
2
+ VALID_SECRET = "14a678aa4a78286d94878d2256f6e591b53707b3f1ec9cdd4cbfde1514670d32".freeze
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ let(:decrypted) { "decrypted" }
7
+ let(:encrypted) { encrypt(decrypted) }
8
+ end
9
+ end
10
+
11
+ def encrypt(value)
12
+ encryptor = EncryptAttributes::Encryptor.new
13
+ encryptor.class.secret ||= VALID_SECRET
14
+ encryptor.encrypt(value)
15
+ end
16
+ end
@@ -0,0 +1,81 @@
1
+ shared_examples_for "adapter:storing" do
2
+ subject { described_class.new }
3
+
4
+ describe "storing" do
5
+ it "encrypts attributes" do
6
+ subject.secret = decrypted
7
+ subject.save
8
+
9
+ expect(read_attribute.call(:secret)).to eq encrypted
10
+ end
11
+ end
12
+ end
13
+
14
+ shared_examples_for "adapter:retrieving" do
15
+ subject { described_class.create(secret: decrypted) }
16
+
17
+ describe "retrieving" do
18
+ it "decrypts specified attributes" do
19
+ subject.reload
20
+ expect(subject.secret).to eq decrypted
21
+ end
22
+ end
23
+ end
24
+
25
+ shared_examples_for "adapter:overrides:reader" do
26
+ subject { described_class.create(secret: decrypted) }
27
+
28
+ describe "overriden reader" do
29
+ it "supports super chain" do
30
+ def subject.secret
31
+ super.tap { |v| yield v if block_given? }
32
+ end
33
+
34
+ injection = double(call: nil)
35
+ subject.secret { |v| injection.call(v) }
36
+
37
+ expect(injection).to have_received(:call).with(decrypted)
38
+ end
39
+ end
40
+ end
41
+
42
+ shared_examples_for "adapter:overrides:writer" do
43
+ subject { described_class.create(secret: decrypted) }
44
+
45
+ describe "overriden writer" do
46
+ let(:injection) { double(call: nil) }
47
+
48
+ before(:each) do
49
+ subject.instance_variable_set(:@injection, injection)
50
+ def subject.secret=(new_secret)
51
+ @injection.call
52
+ super
53
+ end
54
+ end
55
+
56
+ it "supports super chain" do
57
+ subject.secret = decrypted
58
+ subject.save
59
+
60
+ expect(injection).to have_received(:call)
61
+ expect(read_attribute.call(:secret)).to eq encrypted
62
+ end
63
+ end
64
+ end
65
+
66
+ shared_examples_for "adapter" do
67
+ include EncryptorHelpers
68
+
69
+ let(:read_attribute) { subject.public_method(:read_attribute) }
70
+ let(:encryptor) { double(encrypt: encrypted, decrypt: decrypted) }
71
+
72
+ before(:each) do
73
+ allow(EncryptAttributes::Encryptor)
74
+ .to receive(:new).and_return(encryptor)
75
+ end
76
+
77
+ include_examples "adapter:storing"
78
+ include_examples "adapter:retrieving"
79
+ include_examples "adapter:overrides:reader"
80
+ include_examples "adapter:overrides:writer"
81
+ end
@@ -0,0 +1,13 @@
1
+ module MessageEncryptorHelpers
2
+ def stub_message_encryption(input, output)
3
+ allow_any_instance_of(ActiveSupport::MessageEncryptor)
4
+ .to receive(:encrypt_and_sign).with(input)
5
+ .and_return(output)
6
+ end
7
+
8
+ def stub_message_decryption(input, output)
9
+ allow_any_instance_of(ActiveSupport::MessageEncryptor)
10
+ .to receive(:decrypt_and_verify).with(input)
11
+ .and_return(output)
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,217 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: encrypt_attributes
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Artur Hebda
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-11 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.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '4.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.0.0.rc1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.0.0.rc1
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-mocks
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 3.0.0.rc1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 3.0.0.rc1
83
+ - !ruby/object:Gem::Dependency
84
+ name: activerecord
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '4.1'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '4.1'
97
+ - !ruby/object:Gem::Dependency
98
+ name: mongoid
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 4.0.0.beta1
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 4.0.0.beta1
111
+ - !ruby/object:Gem::Dependency
112
+ name: sqlite3
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: pry-byebug
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: This gem provides a dead-simple encryption of string / text attributes
140
+ of ActiveRecord and Mongoid models. Encryptor internally uses ActiveSupport::MessageEncryptor
141
+ and therefore it uses 'aes-256-cbc' cipher by default. Gem does NOT require column
142
+ with different name. From user perspective encryption is completely transparent
143
+ - they use decrypted values all the time, but encrypted values are stored in database.
144
+ email:
145
+ - arturhebda@gmail.com
146
+ executables: []
147
+ extensions: []
148
+ extra_rdoc_files: []
149
+ files:
150
+ - ".gitignore"
151
+ - ".travis.yml"
152
+ - Gemfile
153
+ - LICENSE.txt
154
+ - README.md
155
+ - Rakefile
156
+ - config/database.yml
157
+ - config/database.yml.travis
158
+ - config/mongoid.yml
159
+ - db/migrate/20140610120934_create_users.rb
160
+ - db/schema.rb
161
+ - encrypt_attributes.gemspec
162
+ - lib/encrypt_attributes.rb
163
+ - lib/encrypt_attributes/adapters.rb
164
+ - lib/encrypt_attributes/adapters/active_record.rb
165
+ - lib/encrypt_attributes/adapters/base.rb
166
+ - lib/encrypt_attributes/adapters/mongoid.rb
167
+ - lib/encrypt_attributes/encryptor.rb
168
+ - lib/encrypt_attributes/macros.rb
169
+ - lib/encrypt_attributes/model.rb
170
+ - lib/encrypt_attributes/version.rb
171
+ - lib/tasks/active_record.rake
172
+ - spec/adapters/active_record_spec.rb
173
+ - spec/adapters/mongoid_spec.rb
174
+ - spec/encryptor_spec.rb
175
+ - spec/spec_helper.rb
176
+ - spec/support/connections/active_record.rb
177
+ - spec/support/connections/mongoid.rb
178
+ - spec/support/encryption_matcher.rb
179
+ - spec/support/encryptor_helpers.rb
180
+ - spec/support/examples/adapter.rb
181
+ - spec/support/message_encryptor_helpers.rb
182
+ homepage: https://github.com/aenain/encrypted_attributes
183
+ licenses:
184
+ - MIT
185
+ metadata: {}
186
+ post_install_message:
187
+ rdoc_options: []
188
+ require_paths:
189
+ - lib
190
+ required_ruby_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ required_rubygems_version: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - ">="
198
+ - !ruby/object:Gem::Version
199
+ version: '0'
200
+ requirements: []
201
+ rubyforge_project:
202
+ rubygems_version: 2.2.2
203
+ signing_key:
204
+ specification_version: 4
205
+ summary: Dead-simple attributes encryption for ORMs
206
+ test_files:
207
+ - spec/adapters/active_record_spec.rb
208
+ - spec/adapters/mongoid_spec.rb
209
+ - spec/encryptor_spec.rb
210
+ - spec/spec_helper.rb
211
+ - spec/support/connections/active_record.rb
212
+ - spec/support/connections/mongoid.rb
213
+ - spec/support/encryption_matcher.rb
214
+ - spec/support/encryptor_helpers.rb
215
+ - spec/support/examples/adapter.rb
216
+ - spec/support/message_encryptor_helpers.rb
217
+ has_rdoc: