pax_global_header 0000666 0000000 0000000 00000000064 13274570231 0014516 g ustar 00root root 0000000 0000000 52 comment=38fbfaf466d4238bf1c77d686efdab6a1137d1d0
ruby-attr-encrypted-3.1.0/ 0000775 0000000 0000000 00000000000 13274570231 0015443 5 ustar 00root root 0000000 0000000 ruby-attr-encrypted-3.1.0/.gitignore 0000664 0000000 0000000 00000000072 13274570231 0017432 0 ustar 00root root 0000000 0000000 .bundle
.DS_Store
.ruby-version
pkg
Gemfile.lock
coverage
ruby-attr-encrypted-3.1.0/.travis.yml 0000664 0000000 0000000 00000002301 13274570231 0017550 0 ustar 00root root 0000000 0000000 sudo: false
language: ruby
cache: bundler
rvm:
- 2.0
- 2.1
- 2.2.2
- 2.3.0
- 2.4.0
- 2.5.0
- rbx
env:
- ACTIVERECORD=3.0.0
- ACTIVERECORD=3.1.0
- ACTIVERECORD=3.2.0
- ACTIVERECORD=4.0.0
- ACTIVERECORD=4.1.0
- ACTIVERECORD=4.2.0
- ACTIVERECORD=5.0.0
- ACTIVERECORD=5.1.1
matrix:
exclude:
- rvm: 2.0
env: ACTIVERECORD=5.0.0
- rvm: 2.0
env: ACTIVERECORD=5.1.1
- rvm: 2.1
env: ACTIVERECORD=5.0.0
- rvm: 2.1
env: ACTIVERECORD=5.1.1
- rvm: 2.4.0
env: ACTIVERECORD=3.0.0
- rvm: 2.4.0
env: ACTIVERECORD=3.1.0
- rvm: 2.4.0
env: ACTIVERECORD=3.2.0
- rvm: 2.4.0
env: ACTIVERECORD=4.0.0
- rvm: 2.4.0
env: ACTIVERECORD=4.1.0
- rvm: 2.5.0
env: ACTIVERECORD=3.0.0
- rvm: 2.5.0
env: ACTIVERECORD=3.1.0
- rvm: 2.5.0
env: ACTIVERECORD=3.2.0
- rvm: 2.5.0
env: ACTIVERECORD=4.0.0
- rvm: 2.5.0
env: ACTIVERECORD=4.1.0
- rvm: rbx
env: ACTIVERECORD=5.0.0
- rvm: rbx
env: ACTIVERECORD=5.1.1
allow_failures:
- rvm: rbx
fast_finish: true
addons:
code_climate:
repo_token: a90435ed4954dd6e9f3697a20c5bc3754f67d94703f870e8fc7b00f69f5b2d06
ruby-attr-encrypted-3.1.0/CHANGELOG.md 0000664 0000000 0000000 00000011764 13274570231 0017265 0 ustar 00root root 0000000 0000000 # attr_encrypted #
## 3.1.0 ##
* Added: Abitilty to encrypt empty values. (@tamird)
* Added: MIT license
* Added: MRI 2.5.x support (@saghaulor)
* Fixed: No long generate IV and salt if value is empty, unless :allow_empty_value options is passed. (@saghaulor)
* Fixed: Only generate IV and salt when :if and :unless options evaluate such that encryption should be performed. (@saghaulor)
* Fixed: Private methods are correctly evaluated as options. (@saghaulor)
* Fixed: Mark virtual attributes for Rails 5.x compatibility (@grosser)
* Fixed: Only check empty on strings, allows for encrypting non-string type objects
* Fixed: Fixed how accessors for db columns are defined in the ActiveRecord adapter, preventing premature definition. (@nagachika)
## 3.0.3 ##
* Fixed: attr_was would decrypt the attribute upon every call. This is inefficient and introduces problems when the options change between decrypting an old value and encrypting a new value; for example, when rotating the encryption key. As such, the new approach caches the decrypted value of the old encrypted value such that the old options are no longer needed. (@johnny-lai) (@saghaulor)
## 3.0.2 ##
* Changed: Removed alias_method_chain for compatibility with Rails v5.x (@grosser)
* Changed: Updated Travis build matrix to include Rails 5. (@saghaulor) (@connorshea)
* Changed: Removed `.silence_stream` from tests as it has been removed from Rails 5. (@sblackstone)
## 3.0.1 ##
* Fixed: attr_was method no longer calls undefined methods. (@saghaulor)
## 3.0.0 ##
* Changed: Updated gemspec to use Encryptor v3.0.0. (@saghaulor)
* Changed: Updated README with instructions related to moving from v2.0.0 to v3.0.0. (@saghaulor)
* Fixed: ActiveModel::Dirty methods in the ActiveRecord adapter. (@saghaulor)
## 2.0.0 ##
* Added: Now using Encryptor v2.0.0 (@saghaulor)
* Added: Options are copied to the instance. (@saghaulor)
* Added: Operation option is set during encryption/decryption to allow options to be evaluated in the context of the current operation. (@saghaulor)
* Added: IV and salt can be conditionally encoded. (@saghaulor)
* Added: Changelog! (@saghaulor)
* Changed: attr_encrypted no longer extends object, to use with PORO extend your class, all supported ORMs are already extended. (@saghaulor)
* Changed: Salt is now generated with more entropy. (@saghaulor)
* Changed: The default algorithm is now `aes-256-gcm`. (@saghaulor)
* Changed: The default mode is now `:per_attribute_iv`' (@saghaulor)
* Changed: Extracted class level default options hash to a private method. (@saghaulor)
* Changed: Dynamic finders only work with `:single_iv_and_salt` mode. (@saghaulor)
* Changed: Updated documentation to include v2.0.0 changes and 'things to consider' section. (@saghaulor)
* Fixed: All options are evaluated correctly. (@saghaulor)
* Fixed: IV is generated for every encryption operation. (@saghaulor)
* Deprecated: `:single_iv_and_salt` and `:per_attribute_iv_and_salt` modes are deprecated and will be removed in the next major release. (@saghaulor)
* Deprecated: Dynamic finders via `method_missing` is deprecated and will be removed in the next major release. (@saghaulor)
* Removed: Support for Ruby < 2.x (@saghaulor)
* Removed: Support for Rails < 3.x (@saghaulor)
* Removed: Unnecessary use of `alias_method` from ActiveRecord adapter. (@saghaulor)
## 1.4.0 ##
* Added: ActiveModel::Dirty#attribute_was (@saghaulor)
* Added: ActiveModel::Dirty#attribute_changed? (@mwean)
## 1.3.5 ##
* Changed: Fixed gemspec to explicitly depend on Encryptor v1.3.0 (@saghaulor)
* Fixed: Evaluate `:mode` option as a symbol or proc. (@cheynewallace)
## 1.3.4 ##
* Added: ActiveRecord::Base.reload support. (@rcook)
* Fixed: ActiveRecord adapter no longer forces attribute hashes to be string-keyed. (@tamird)
* Fixed: Mass assignment protection in ActiveRecord 4. (@tamird)
* Changed: Now using rubygems over https. (@tamird)
* Changed: Let ActiveRecord define attribute methods. (@saghaulor)
## 1.3.3 ##
* Added: Alias attr_encryptor and attr_encrpted. (@Billy Monk)
## 1.3.2 ##
* Fixed: Bug regarding strong parameters. (@S. Brent Faulkner)
* Fixed: Bug regarding loading per instance IV and salt. (@S. Brent Faulkner)
* Fixed: Bug regarding assigning nil. (@S. Brent Faulkner)
* Added: Support for protected attributes. (@S. Brent Faulkner)
* Added: Support for ActiveRecord 4. (@S. Brent Faulkner)
## 1.3.1 ##
* Added: Support for Rails 2.3.x and 3.1.x. (@S. Brent Faulkner)
## 1.3.0 ##
* Fixed: Serialization bug. (@Billy Monk)
* Added: Support for :per_attribute_iv_and_salt mode. (@rcook)
* Fixed: Added dependencies to gemspec. (@jmazzi)
## 1.2.1 ##
* Added: Force encoding when not marshaling. (@mosaicxm)
* Fixed: Issue specifying multiple attributes on the same line. (@austintaylor)
* Added: Typecasting to String before encryption (@shuber)
* Added: `"#{attribute}?"` method. (@shuber)
## 1.2.0 ##
* Changed: General code refactoring (@shuber)
## 1.1.2 ##
* No significant changes
## 1.1.1 ##
* Changled: Updated README. (@shuber)
* Added: `before_type_cast` alias to ActiveRecord adapter. (@shuber)
ruby-attr-encrypted-3.1.0/Gemfile 0000664 0000000 0000000 00000000047 13274570231 0016737 0 ustar 00root root 0000000 0000000 source 'https://rubygems.org'
gemspec
ruby-attr-encrypted-3.1.0/MIT-LICENSE 0000664 0000000 0000000 00000002062 13274570231 0017077 0 ustar 00root root 0000000 0000000 Copyright (c) 2008 Sean Huber - shuber@huberry.com
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ruby-attr-encrypted-3.1.0/README.md 0000664 0000000 0000000 00000046635 13274570231 0016740 0 ustar 00root root 0000000 0000000 # attr_encrypted
[](https://travis-ci.org/attr-encrypted/attr_encrypted) [](https://codeclimate.com/github/attr-encrypted/attr_encrypted/coverage) [](https://codeclimate.com/github/attr-encrypted/attr_encrypted) [](https://badge.fury.io/rb/attr_encrypted) [](https://hakiri.io/github/attr-encrypted/attr_encrypted/master)
Generates attr_accessors that transparently encrypt and decrypt attributes.
It works with ANY class, however, you get a few extra features when you're using it with `ActiveRecord`, `DataMapper`, or `Sequel`.
## Installation
Add attr_encrypted to your gemfile:
```ruby
gem "attr_encrypted", "~> 3.0.0"
```
Then install the gem:
```bash
bundle install
```
## Usage
If you're using an ORM like `ActiveRecord`, `DataMapper`, or `Sequel`, using attr_encrypted is easy:
```ruby
class User
attr_encrypted :ssn, key: 'This is a key that is 256 bits!!'
end
```
If you're using a PORO, you have to do a little bit more work by extending the class:
```ruby
class User
extend AttrEncrypted
attr_accessor :name
attr_encrypted :ssn, key: 'This is a key that is 256 bits!!'
def load
# loads the stored data
end
def save
# saves the :name and :encrypted_ssn attributes somewhere (e.g. filesystem, database, etc)
end
end
user = User.new
user.ssn = '123-45-6789'
user.ssn # returns the unencrypted object ie. '123-45-6789'
user.encrypted_ssn # returns the encrypted version of :ssn
user.save
user = User.load
user.ssn # decrypts :encrypted_ssn and returns '123-45-6789'
```
### Encrypt/decrypt attribute class methods
Two class methods are available for each attribute: `User.encrypt_email` and `User.decrypt_email`. They accept as arguments the same options that the `attr_encrypted` class method accepts. For example:
```ruby
key = SecureRandom.random_bytes(32)
iv = SecureRandom.random_bytes(12)
encrypted_email = User.encrypt_email('test@test.com', iv: iv, key: key)
email = User.decrypt_email(encrypted_email, iv: iv, key: key)
```
The `attr_encrypted` class method is also aliased as `attr_encryptor` to conform to Ruby's `attr_` naming conventions. I should have called this project `attr_encryptor` but it was too late when I realized it ='(.
### attr_encrypted with database persistence
By default, `attr_encrypted` uses the `:per_attribute_iv` encryption mode. This mode requires a column to store your cipher text and a column to store your IV (initialization vector).
Create or modify the table that your model uses to add a column with the `encrypted_` prefix (which can be modified, see below), e.g. `encrypted_ssn` via a migration like the following:
```ruby
create_table :users do |t|
t.string :name
t.string :encrypted_ssn
t.string :encrypted_ssn_iv
t.timestamps
end
```
You can use a string or binary column type. (See the encode option section below for more info)
### Specifying the encrypted attribute name
By default, the encrypted attribute name is `encrypted_#{attribute}` (e.g. `attr_encrypted :email` would create an attribute named `encrypted_email`). So, if you're storing the encrypted attribute in the database, you need to make sure the `encrypted_#{attribute}` field exists in your table. You have a couple of options if you want to name your attribute or db column something else, see below for more details.
## attr_encrypted options
#### Options are evaluated
All options will be evaluated at the instance level. If you pass in a symbol it will be passed as a message to the instance of your class. If you pass a proc or any object that responds to `:call` it will be called. You can pass in the instance of your class as an argument to the proc. Anything else will be returned. For example:
##### Symbols representing instance methods
Here is an example class that uses an instance method to determines the encryption key to use.
```ruby
class User
attr_encrypted :email, key: :encryption_key
def encryption_key
# does some fancy logic and returns an encryption key
end
end
```
##### Procs
Here is an example of passing a proc/lambda object as the `:key` option as well:
```ruby
class User
attr_encrypted :email, key: proc { |user| user.key }
end
```
### Default options
The following are the default options used by `attr_encrypted`:
```ruby
prefix: 'encrypted_',
suffix: '',
if: true,
unless: false,
encode: false,
encode_iv: true,
encode_salt: true,
default_encoding: 'm',
marshal: false,
marshaler: Marshal,
dump_method: 'dump',
load_method: 'load',
encryptor: Encryptor,
encrypt_method: 'encrypt',
decrypt_method: 'decrypt',
mode: :per_attribute_iv,
algorithm: 'aes-256-gcm',
allow_empty_value: false
```
All of the aforementioned options are explained in depth below.
Additionally, you can specify default options for all encrypted attributes in your class. Instead of having to define your class like this:
```ruby
class User
attr_encrypted :email, key: 'This is a key that is 256 bits!!', prefix: '', suffix: '_crypted'
attr_encrypted :ssn, key: 'a different secret key', prefix: '', suffix: '_crypted'
attr_encrypted :credit_card, key: 'another secret key', prefix: '', suffix: '_crypted'
end
```
You can simply define some default options like so:
```ruby
class User
attr_encrypted_options.merge!(prefix: '', :suffix => '_crypted')
attr_encrypted :email, key: 'This is a key that is 256 bits!!'
attr_encrypted :ssn, key: 'a different secret key'
attr_encrypted :credit_card, key: 'another secret key'
end
```
This should help keep your classes clean and DRY.
### The `:attribute` option
You can simply pass the name of the encrypted attribute as the `:attribute` option:
```ruby
class User
attr_encrypted :email, key: 'This is a key that is 256 bits!!', attribute: 'email_encrypted'
end
```
This would generate an attribute named `email_encrypted`
### The `:prefix` and `:suffix` options
If you don't like the `encrypted_#{attribute}` naming convention then you can specify your own:
```ruby
class User
attr_encrypted :email, key: 'This is a key that is 256 bits!!', prefix: 'secret_', suffix: '_crypted'
end
```
This would generate the following attribute: `secret_email_crypted`.
### The `:key` option
The `:key` option is used to pass in a data encryption key to be used with whatever encryption class you use. If you're using `Encryptor`, the key must meet minimum length requirements respective to the algorithm that you use; aes-256 requires a 256 bit key, etc. The `:key` option is not required (see custom encryptor below).
##### Unique keys for each attribute
You can specify unique keys for each attribute if you'd like:
```ruby
class User
attr_encrypted :email, key: 'This is a key that is 256 bits!!'
attr_encrypted :ssn, key: 'a different secret key'
end
```
It is recommended to use a symbol or a proc for the key and to store information regarding what key was used to encrypt your data. (See below for more details.)
### The `:if` and `:unless` options
There may be times that you want to only encrypt when certain conditions are met. For example maybe you're using rails and you don't want to encrypt attributes when you're in development mode. You can specify conditions like this:
```ruby
class User < ActiveRecord::Base
attr_encrypted :email, key: 'This is a key that is 256 bits!!', unless: Rails.env.development?
attr_encrypted :ssn, key: 'This is a key that is 256 bits!!', if: Rails.env.development?
end
```
You can specify both `:if` and `:unless` options.
### The `:encryptor`, `:encrypt_method`, and `:decrypt_method` options
The `Encryptor` class is used by default. You may use your own custom encryptor by specifying the `:encryptor`, `:encrypt_method`, and `:decrypt_method` options.
Lets suppose you'd like to use this custom encryptor class:
```ruby
class SillyEncryptor
def self.silly_encrypt(options)
(options[:value] + options[:secret_key]).reverse
end
def self.silly_decrypt(options)
options[:value].reverse.gsub(/#{options[:secret_key]}$/, '')
end
end
```
Simply set up your class like so:
```ruby
class User
attr_encrypted :email, secret_key: 'This is a key that is 256 bits!!', encryptor: SillyEncryptor, encrypt_method: :silly_encrypt, decrypt_method: :silly_decrypt
end
```
Any options that you pass to `attr_encrypted` will be passed to the encryptor class along with the `:value` option which contains the string to encrypt/decrypt. Notice that the above example uses `:secret_key` instead of `:key`. See [encryptor](https://github.com/attr-encrypted/encryptor) for more info regarding the default encryptor class.
### The `:mode` option
The mode options allows you to specify in what mode your data will be encrypted. There are currently three modes: `:per_attribute_iv`, `:per_attribute_iv_and_salt`, and `:single_iv_and_salt`.
__NOTE: `:per_attribute_iv_and_salt` and `:single_iv_and_salt` modes are deprecated and will be removed in the next major release.__
### The `:algorithm` option
The default `Encryptor` class uses the standard ruby OpenSSL library. Its default algorithm is `aes-256-gcm`. You can modify this by passing the `:algorithm` option to the `attr_encrypted` call like so:
```ruby
class User
attr_encrypted :email, key: 'This is a key that is 256 bits!!', algorithm: 'aes-256-cbc'
end
```
To view a list of all cipher algorithms that are supported on your platform, run the following code in your favorite Ruby REPL:
```ruby
require 'openssl'
puts OpenSSL::Cipher.ciphers
```
See [Encryptor](https://github.com/attr-encrypted/encryptor#algorithms) for more information.
### The `:encode`, `:encode_iv`, `:encode_salt`, and `:default_encoding` options
You're probably going to be storing your encrypted attributes somehow (e.g. filesystem, database, etc). You can simply pass the `:encode` option to automatically encode/decode when encrypting/decrypting. The default behavior assumes that you're using a string column type and will base64 encode your cipher text. If you choose to use the binary column type then encoding is not required, but be sure to pass in `false` with the `:encode` option.
```ruby
class User
attr_encrypted :email, key: 'some secret key', encode: true, encode_iv: true, encode_salt: true
end
```
The default encoding is `m` (base64). You can change this by setting `encode: 'some encoding'`. See [`Array#pack`](http://ruby-doc.org/core-2.3.0/Array.html#method-i-pack) for more encoding options.
### The `:marshal`, `:dump_method`, and `:load_method` options
You may want to encrypt objects other than strings (e.g. hashes, arrays, etc). If this is the case, simply pass the `:marshal` option to automatically marshal when encrypting/decrypting.
```ruby
class User
attr_encrypted :credentials, key: 'some secret key', marshal: true
end
```
You may also optionally specify `:marshaler`, `:dump_method`, and `:load_method` if you want to use something other than the default `Marshal` object.
### The `:allow_empty_value` option
You may want to encrypt empty strings or nil so as to not reveal which records are populated and which records are not.
```ruby
class User
attr_encrypted :credentials, key: 'some secret key', marshal: true, allow_empty_value: true
end
```
## ORMs
### ActiveRecord
If you're using this gem with `ActiveRecord`, you get a few extra features:
#### Default options
The `:encode` option is set to true by default.
#### Dynamic `find_by_` and `scoped_by_` methods
Let's say you'd like to encrypt your user's email addresses, but you also need a way for them to login. Simply set up your class like so:
```ruby
class User < ActiveRecord::Base
attr_encrypted :email, key: 'This is a key that is 256 bits!!'
attr_encrypted :password, key: 'some other secret key'
end
```
You can now lookup and login users like so:
```ruby
User.find_by_email_and_password('test@example.com', 'testing')
```
The call to `find_by_email_and_password` is intercepted and modified to `find_by_encrypted_email_and_encrypted_password('ENCRYPTED EMAIL', 'ENCRYPTED PASSWORD')`. The dynamic scope methods like `scoped_by_email_and_password` work the same way.
NOTE: This only works if all records are encrypted with the same encryption key (per attribute).
__NOTE: This feature is deprecated and will be removed in the next major release.__
### DataMapper and Sequel
#### Default options
The `:encode` option is set to true by default.
## Deprecations
attr_encrypted v2.0.0 now depends on encryptor v2.0.0. As part of both major releases many insecure defaults and behaviors have been deprecated. The new default behavior is as follows:
* Default `:mode` is now `:per_attribute_iv`, the default `:mode` in attr_encrypted v1.x was `:single_iv_and_salt`.
* Default `:algorithm` is now 'aes-256-gcm', the default `:algorithm` in attr_encrypted v1.x was 'aes-256-cbc'.
* The encryption key provided must be of appropriate length respective to the algorithm used. Previously, encryptor did not verify minimum key length.
* The dynamic finders available in ActiveRecord will only work with `:single_iv_and_salt` mode. It is strongly advised that you do not use this mode. If you can search the encrypted data, it wasn't encrypted securely. This functionality will be deprecated in the next major release.
* `:per_attribute_iv_and_salt` and `:single_iv_and_salt` modes are deprecated and will be removed in the next major release.
Backwards compatibility is supported by providing a special option that is passed to encryptor, namely, `:insecure_mode`:
```ruby
class User
attr_encrypted :email, key: 'a secret key', algorithm: 'aes-256-cbc', mode: :single_iv_and_salt, insecure_mode: true
end
```
The `:insecure_mode` option will allow encryptor to ignore the new security requirements. It is strongly advised that if you use this older insecure behavior that you migrate to the newer more secure behavior.
## Upgrading from attr_encrypted v1.x to v3.x
Modify your gemfile to include the new version of attr_encrypted:
```ruby
gem attr_encrypted, "~> 3.0.0"
```
The update attr_encrypted:
```bash
bundle update attr_encrypted
```
Then modify your models using attr\_encrypted to account for the changes in default options. Specifically, pass in the `:mode` and `:algorithm` options that you were using if you had not previously done so. If your key is insufficient length relative to the algorithm that you use, you should also pass in `insecure_mode: true`; this will prevent Encryptor from raising an exception regarding insufficient key length. Please see the Deprecations sections for more details including an example of how to specify your model with default options from attr_encrypted v1.x.
## Upgrading from attr_encrypted v2.x to v3.x
A bug was discovered in Encryptor v2.0.0 that inccorectly set the IV when using an AES-\*-GCM algorithm. Unfornately fixing this major security issue results in the inability to decrypt records encrypted using an AES-*-GCM algorithm from Encryptor v2.0.0. Please see [Upgrading to Encryptor v3.0.0](https://github.com/attr-encrypted/encryptor#upgrading-from-v200-to-v300) for more info.
It is strongly advised that you re-encrypt your data encrypted with Encryptor v2.0.0. However, you'll have to take special care to re-encrypt. To decrypt data encrypted with Encryptor v2.0.0 using an AES-\*-GCM algorithm you can use the `:v2_gcm_iv` option.
It is recommended that you implement a strategy to insure that you do not mix the encryption implementations of Encryptor. One way to do this is to re-encrypt everything while your application is offline.Another way is to add a column that keeps track of what implementation was used. The path that you choose will depend on your situtation. Below is an example of how you might go about re-encrypting your data.
```ruby
class User
attr_encrypted :ssn, key: :encryption_key, v2_gcm_iv: is_decrypting?(:ssn)
def is_decrypting?(attribute)
encrypted_attributes[attribute][:operation] == :decrypting
end
end
User.all.each do |user|
old_ssn = user.ssn
user.ssn= old_ssn
user.save
end
```
## Things to consider before using attr_encrypted
#### Searching, joining, etc
While choosing to encrypt at the attribute level is the most secure solution, it is not without drawbacks. Namely, you cannot search the encrypted data, and because you can't search it, you can't index it either. You also can't use joins on the encrypted data. Data that is securely encrypted is effectively noise. So any operations that rely on the data not being noise will not work. If you need to do any of the aforementioned operations, please consider using database and file system encryption along with transport encryption as it moves through your stack.
#### Data leaks
Please also consider where your data leaks. If you're using attr_encrypted with Rails, it's highly likely that this data will enter your app as a request parameter. You'll want to be sure that you're filtering your request params from you logs or else your data is sitting in the clear in your logs. [Parameter Filtering in Rails](http://apidock.com/rails/ActionDispatch/Http/FilterParameters) Please also consider other possible leak points.
#### Storage requirements
When storing your encrypted data, please consider the length requirements of the db column that you're storing the cipher text in. Older versions of Mysql attempt to 'help' you by truncating strings that are too large for the column. When this happens, you will not be able to decrypt your data. [MySQL Strict Trans](http://www.davidpashley.com/2009/02/15/silently-truncated/)
#### Metadata regarding your crypto implementation
It is advisable to also store metadata regarding the circumstances of your encrypted data. Namely, you should store information about the key used to encrypt your data, as well as the algorithm. Having this metadata with every record will make key rotation and migrating to a new algorithm signficantly easier. It will allow you to continue to decrypt old data using the information provided in the metadata and new data can be encrypted using your new key and algorithm of choice.
#### Enforcing the IV as a nonce
On a related note, most alorithms require that your IV be unique for every record and key combination. You can enforce this using composite unique indexes on your IV and encryption key name/id column. [RFC 5084](https://tools.ietf.org/html/rfc5084#section-1.5)
#### Unique key per record
Lastly, while the `:per_attribute_iv_and_salt` mode is more secure than `:per_attribute_iv` mode because it uses a unique key per record, it uses a PBKDF function which introduces a huge performance hit (175x slower by my benchmarks). There are other ways of deriving a unique key per record that would be much faster.
## Note on Patches/Pull Requests
* Fork the project.
* Make your feature addition or bug fix.
* Add tests for it. This is important so I don't break it in a
future version unintentionally.
* Commit, do not mess with rakefile, version, changelog, or history.
* Send me a pull request. Bonus points for topic branches.
ruby-attr-encrypted-3.1.0/Rakefile 0000664 0000000 0000000 00000001112 13274570231 0017103 0 ustar 00root root 0000000 0000000 require 'rake/testtask'
require 'rdoc/task'
require "bundler/gem_tasks"
desc 'Test the attr_encrypted gem.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.pattern = 'test/**/*_test.rb'
t.warning = false
t.verbose = true
end
desc 'Generate documentation for the attr_encrypted gem.'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'attr_encrypted'
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
end
desc 'Default: run unit tests.'
task :default => :test
ruby-attr-encrypted-3.1.0/attr_encrypted.gemspec 0000664 0000000 0000000 00000005221 13274570231 0022037 0 ustar 00root root 0000000 0000000 # -*- encoding: utf-8 -*-
lib = File.expand_path('../lib/', __FILE__)
$:.unshift lib unless $:.include?(lib)
require 'attr_encrypted/version'
require 'date'
Gem::Specification.new do |s|
s.name = 'attr_encrypted'
s.version = AttrEncrypted::Version.string
s.date = Date.today
s.summary = 'Encrypt and decrypt attributes'
s.description = 'Generates attr_accessors that encrypt and decrypt attributes transparently'
s.authors = ['Sean Huber', 'S. Brent Faulkner', 'William Monk', 'Stephen Aghaulor']
s.email = ['seah@shuber.io', 'sbfaulkner@gmail.com', 'billy.monk@gmail.com', 'saghaulor@gmail.com']
s.homepage = 'http://github.com/attr-encrypted/attr_encrypted'
s.license = 'MIT'
s.has_rdoc = false
s.rdoc_options = ['--line-numbers', '--inline-source', '--main', 'README.rdoc']
s.require_paths = ['lib']
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- test/*`.split("\n")
s.required_ruby_version = '>= 2.0.0'
s.add_dependency('encryptor', ['~> 3.0.0'])
# support for testing with specific active record version
activerecord_version = if ENV.key?('ACTIVERECORD')
"~> #{ENV['ACTIVERECORD']}"
else
'>= 2.0.0'
end
s.add_development_dependency('activerecord', activerecord_version)
s.add_development_dependency('actionpack', activerecord_version)
s.add_development_dependency('datamapper')
s.add_development_dependency('rake')
s.add_development_dependency('minitest')
s.add_development_dependency('sequel')
if RUBY_VERSION < '2.1.0'
s.add_development_dependency('nokogiri', '< 1.7.0')
s.add_development_dependency('public_suffix', '< 3.0.0')
end
if defined?(RUBY_ENGINE) && RUBY_ENGINE.to_sym == :jruby
s.add_development_dependency('activerecord-jdbcsqlite3-adapter')
s.add_development_dependency('jdbc-sqlite3', '< 3.8.7') # 3.8.7 is nice and broke
else
s.add_development_dependency('sqlite3')
end
s.add_development_dependency('dm-sqlite-adapter')
s.add_development_dependency('simplecov')
s.add_development_dependency('simplecov-rcov')
s.add_development_dependency("codeclimate-test-reporter", '<= 0.6.0')
s.cert_chain = ['certs/saghaulor.pem']
s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/
s.post_install_message = "\n\n\nWARNING: Several insecure default options and features were deprecated in attr_encrypted v2.0.0.\n
Additionally, there was a bug in Encryptor v2.0.0 that insecurely encrypted data when using an AES-*-GCM algorithm.\n
This bug was fixed but introduced breaking changes between v2.x and v3.x.\n
Please see the README for more information regarding upgrading to attr_encrypted v3.0.0.\n\n\n"
end
ruby-attr-encrypted-3.1.0/certs/ 0000775 0000000 0000000 00000000000 13274570231 0016563 5 ustar 00root root 0000000 0000000 ruby-attr-encrypted-3.1.0/certs/saghaulor.pem 0000664 0000000 0000000 00000002351 13274570231 0021254 0 ustar 00root root 0000000 0000000 -----BEGIN CERTIFICATE-----
MIIDdDCCAlygAwIBAgIBATANBgkqhkiG9w0BAQUFADBAMRIwEAYDVQQDDAlzYWdo
YXVsb3IxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv
bTAeFw0xODAyMTIwMzMzMThaFw0xOTAyMTIwMzMzMThaMEAxEjAQBgNVBAMMCXNh
Z2hhdWxvcjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYD
Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvOLqbSmj5txfw39a
Ki0g3BJWGrfGBiSRq9aThUGzoiaqyDo/m1WMQdgPioZG+P923okChEWFjhSymBQU
eMdys6JRPm5ortp5sh9gesOWoozqb8R55d8rr1V7pY533cCut53Kb1wiitjkfXjR
efT2HPh6nV6rYjGMJek/URaCNzsZo7HCkRsKdezP+BKr4V4wOka69tfJX5pcvFvR
iiqfaiP4RK12hYdsFnSVKiKP7SAFTFiYcohbL8TUW6ezQQqJCK0M6fu74EWVCnBS
gFVjj931BuD8qhuxMiB6uC6FKxemB5TRGBLzn7RcrOMAo2inMAopjkGeQJUAyVCm
J5US3wIDAQABo3kwdzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQU
hJEuSZgvuuIhIsxQ/0pRQTBVzokwHgYDVR0RBBcwFYETc2FnaGF1bG9yQGdtYWls
LmNvbTAeBgNVHRIEFzAVgRNzYWdoYXVsb3JAZ21haWwuY29tMA0GCSqGSIb3DQEB
BQUAA4IBAQCsBS2cxqTmV4nXJEH/QbdgjVDAZbK6xf2gpM3vCRlYsy7Wz6GEoOpD
bzRkjxZwGNbhXShMUZwm6zahYQ/L1/HFztLoMBMkm8EIfPxH0PDrP4aWl0oyWxmU
OLm0/t9icSWRPPJ1tLJvuAaDdVpY5dEHd6VdnNJGQC5vHKRInt1kEyqEttIJ/xmJ
leSEFyMeoFsR+C/WPG9WSC+xN0eXqakCu6YUJoQzCn/7znv8WxpHEbeZjNIHq0qb
nbqZ/ZW1bwzj1T/NIbnMr37wqV29XwkI4+LbewMkb6/bDPYl0qZpAkCxKtGYCCJp
l6KPs9K/72yH00dxuAhiTXikTkcLXeQJ
-----END CERTIFICATE-----
ruby-attr-encrypted-3.1.0/checksum/ 0000775 0000000 0000000 00000000000 13274570231 0017245 5 ustar 00root root 0000000 0000000 ruby-attr-encrypted-3.1.0/checksum/attr_encrypted-3.0.0.gem.sha256 0000664 0000000 0000000 00000000100 13274570231 0024420 0 ustar 00root root 0000000 0000000 845fc3cb09a19c3ac76192aba443788f92c880744617bca99b16fd31ce843e07 ruby-attr-encrypted-3.1.0/checksum/attr_encrypted-3.0.0.gem.sha512 0000664 0000000 0000000 00000000200 13274570231 0024414 0 ustar 00root root 0000000 0000000 81a065442258cc3702aab62c7b2307a48ed3e0deb803600d11a7480cce0db7c43fd9929acd2755081042f8989236553fd694b6cb62776bbfc53f9165a22cbca1 ruby-attr-encrypted-3.1.0/checksum/attr_encrypted-3.0.1.gem.sha256 0000664 0000000 0000000 00000000100 13274570231 0024421 0 ustar 00root root 0000000 0000000 33140af4b223177db7a19efb2fa38472a299a745b29ca1c5ba9d3fa947390b77 ruby-attr-encrypted-3.1.0/checksum/attr_encrypted-3.0.1.gem.sha512 0000664 0000000 0000000 00000000200 13274570231 0024415 0 ustar 00root root 0000000 0000000 0c467cab98b9b2eb331f9818323a90ae01392d6cb03cf1f32faccc954d0fc54be65f0fc7bf751b0fce57925eef1c9e2af90181bc40d81ad93e21d15a001c53c6 ruby-attr-encrypted-3.1.0/checksum/attr_encrypted-3.0.2.gem.sha256 0000664 0000000 0000000 00000000100 13274570231 0024422 0 ustar 00root root 0000000 0000000 c1256b459336d4a2012a0d0c70ce5cd3dac46acb5e78da6f77f6f104cb1e8b7b ruby-attr-encrypted-3.1.0/checksum/attr_encrypted-3.0.2.gem.sha512 0000664 0000000 0000000 00000000200 13274570231 0024416 0 ustar 00root root 0000000 0000000 dca0c8a729974c0e26fde4cd4216c7d0f66d9eca9f6cf0ccca64999f5180a00bf7c05b630c1d420ec1673141a2923946e8bd28b12e711faf64a4cd42c7a3ac9e ruby-attr-encrypted-3.1.0/checksum/attr_encrypted-3.0.3.gem.sha256 0000664 0000000 0000000 00000000100 13274570231 0024423 0 ustar 00root root 0000000 0000000 6d84c64852c4bbc0926b92fe7a93295671a9e69cb2939b96fb1e4b5e8a5b33b6 ruby-attr-encrypted-3.1.0/checksum/attr_encrypted-3.0.3.gem.sha512 0000664 0000000 0000000 00000000200 13274570231 0024417 0 ustar 00root root 0000000 0000000 0f960e8a2f63c747c273241f7395dcceb0dd8a6f79349bee453db741fc7ea5ceb4342d7d5908e540e3b5acea2216ff38bef8c743e6e7c8559bacb4a731ab27c4 ruby-attr-encrypted-3.1.0/checksums.yaml.gz.sig 0000664 0000000 0000000 00000000400 13274570231 0021506 0 ustar 00root root 0000000 0000000 )áºû`'\Ç´±ðÃÙl{Éþ‚Æù-áÆp.åIâKÍyŸ£4˜Ë•’`ì{‚¹ó§ÍŽL‰öóªÁ—uho¾dÞˆEÉVHzP ™¹ÓUO˜#Ùt.ÖÂòo£†Ø/±ñ§tªÑ˜z}àÅÝ#3l`a¿JDKcòô¡½‰)^ŽÈ ‰†ã?µ<¨ø±^EÛÞ2& ´BoF«qZé£Ë9•–ù¤Ìúà—69Z—³s.Á„ö(hïU—È?{³þ¼zp?øP›»\3§·éâ mª¨³ëNäKjó¢#`0nÝ9J”Otݼý{Þ£ªº}‰Å ¾) ruby-attr-encrypted-3.1.0/data.tar.gz.sig 0000664 0000000 0000000 00000000400 13274570231 0020256 0 ustar 00root root 0000000 0000000 Môr¿ø¿+õp"!,¨rkE⤗äÙ,bN®;¶›z4EÓÁ0‰¡Sc\@âå¬]$¢ÅÆ uD“+5OŒBøÕ(ˆ/:ÕE™ì ¡Ø|4½÷W…¨€è`¬•¯Z¥õúð¯âõ²tíUaµæj<±z6¤²læ€ÀÆ¿„E\ý$Ø8^ìmuóºkárØ”±;Ü“‰*®¿ë¼¼†Aཧ^°q–ñªK:†/°ü/µÐ…ÍY©ìG4_£ûIŸ ¼àdHC”ÛÝ÷u{°_ê
".¼2/éswÛ;UQ)rªël(µ‘ÂdS
Qm ±näÜHÄꪑ¦.C¤ ruby-attr-encrypted-3.1.0/lib/ 0000775 0000000 0000000 00000000000 13274570231 0016211 5 ustar 00root root 0000000 0000000 ruby-attr-encrypted-3.1.0/lib/attr_encrypted.rb 0000664 0000000 0000000 00000043523 13274570231 0021574 0 ustar 00root root 0000000 0000000 require 'encryptor'
# Adds attr_accessors that encrypt and decrypt an object's attributes
module AttrEncrypted
autoload :Version, 'attr_encrypted/version'
def self.extended(base) # :nodoc:
base.class_eval do
include InstanceMethods
attr_writer :attr_encrypted_options
@attr_encrypted_options, @encrypted_attributes = {}, {}
end
end
# Generates attr_accessors that encrypt and decrypt attributes transparently
#
# Options (any other options you specify are passed to the Encryptor's encrypt and decrypt methods)
#
# attribute: The name of the referenced encrypted attribute. For example
# attr_accessor :email, attribute: :ee would generate an
# attribute named 'ee' to store the encrypted email. This is useful when defining
# one attribute to encrypt at a time or when the :prefix and :suffix options
# aren't enough.
# Defaults to nil.
#
# prefix: A prefix used to generate the name of the referenced encrypted attributes.
# For example attr_accessor :email, prefix: 'crypted_' would
# generate attributes named 'crypted_email' to store the encrypted
# email and password.
# Defaults to 'encrypted_'.
#
# suffix: A suffix used to generate the name of the referenced encrypted attributes.
# For example attr_accessor :email, prefix: '', suffix: '_encrypted'
# would generate attributes named 'email_encrypted' to store the
# encrypted email.
# Defaults to ''.
#
# key: The encryption key. This option may not be required if
# you're using a custom encryptor. If you pass a symbol
# representing an instance method then the :key option
# will be replaced with the result of the method before
# being passed to the encryptor. Objects that respond
# to :call are evaluated as well (including procs).
# Any other key types will be passed directly to the encryptor.
# Defaults to nil.
#
# encode: If set to true, attributes will be encoded as well as
# encrypted. This is useful if you're planning on storing
# the encrypted attributes in a database. The default
# encoding is 'm' (base64), however this can be overwritten
# by setting the :encode option to some other encoding
# string instead of just 'true'. See
# http://www.ruby-doc.org/core/classes/Array.html#M002245
# for more encoding directives.
# Defaults to false unless you're using it with ActiveRecord, DataMapper, or Sequel.
#
# encode_iv: Defaults to true.
# encode_salt: Defaults to true.
#
# default_encoding: Defaults to 'm' (base64).
#
# marshal: If set to true, attributes will be marshaled as well
# as encrypted. This is useful if you're planning on
# encrypting something other than a string.
# Defaults to false.
#
# marshaler: The object to use for marshaling.
# Defaults to Marshal.
#
# dump_method: The dump method name to call on the :marshaler object to.
# Defaults to 'dump'.
#
# load_method: The load method name to call on the :marshaler object.
# Defaults to 'load'.
#
# encryptor: The object to use for encrypting.
# Defaults to Encryptor.
#
# encrypt_method: The encrypt method name to call on the :encryptor object.
# Defaults to 'encrypt'.
#
# decrypt_method: The decrypt method name to call on the :encryptor object.
# Defaults to 'decrypt'.
#
# if: Attributes are only encrypted if this option evaluates
# to true. If you pass a symbol representing an instance
# method then the result of the method will be evaluated.
# Any objects that respond to :call are evaluated as well.
# Defaults to true.
#
# unless: Attributes are only encrypted if this option evaluates
# to false. If you pass a symbol representing an instance
# method then the result of the method will be evaluated.
# Any objects that respond to :call are evaluated as well.
# Defaults to false.
#
# mode: Selects encryption mode for attribute: choose :single_iv_and_salt for compatibility
# with the old attr_encrypted API: the IV is derived from the encryption key by the underlying Encryptor class; salt is not used.
# The :per_attribute_iv_and_salt mode uses a per-attribute IV and salt. The salt is used to derive a unique key per attribute.
# A :per_attribute_iv mode derives a unique IV per attribute; salt is not used.
# Defaults to :per_attribute_iv.
#
# allow_empty_value: Attributes which have nil or empty string values will not be encrypted unless this option
# has a truthy value.
#
# You can specify your own default options
#
# class User
# # Now all attributes will be encoded and marshaled by default
# attr_encrypted_options.merge!(encode: true, marshal: true, some_other_option: true)
# attr_encrypted :configuration, key: 'my secret key'
# end
#
#
# Example
#
# class User
# attr_encrypted :email, key: 'some secret key'
# attr_encrypted :configuration, key: 'some other secret key', marshal: true
# end
#
# @user = User.new
# @user.encrypted_email # nil
# @user.email? # false
# @user.email = 'test@example.com'
# @user.email? # true
# @user.encrypted_email # returns the encrypted version of 'test@example.com'
#
# @user.configuration = { time_zone: 'UTC' }
# @user.encrypted_configuration # returns the encrypted version of configuration
#
# See README for more examples
def attr_encrypted(*attributes)
options = attributes.last.is_a?(Hash) ? attributes.pop : {}
options = attr_encrypted_default_options.dup.merge!(attr_encrypted_options).merge!(options)
options[:encode] = options[:default_encoding] if options[:encode] == true
options[:encode_iv] = options[:default_encoding] if options[:encode_iv] == true
options[:encode_salt] = options[:default_encoding] if options[:encode_salt] == true
attributes.each do |attribute|
encrypted_attribute_name = (options[:attribute] ? options[:attribute] : [options[:prefix], attribute, options[:suffix]].join).to_sym
instance_methods_as_symbols = attribute_instance_methods_as_symbols
if attribute_instance_methods_as_symbols_available?
attr_reader encrypted_attribute_name unless instance_methods_as_symbols.include?(encrypted_attribute_name)
attr_writer encrypted_attribute_name unless instance_methods_as_symbols.include?(:"#{encrypted_attribute_name}=")
iv_name = "#{encrypted_attribute_name}_iv".to_sym
attr_reader iv_name unless instance_methods_as_symbols.include?(iv_name)
attr_writer iv_name unless instance_methods_as_symbols.include?(:"#{iv_name}=")
salt_name = "#{encrypted_attribute_name}_salt".to_sym
attr_reader salt_name unless instance_methods_as_symbols.include?(salt_name)
attr_writer salt_name unless instance_methods_as_symbols.include?(:"#{salt_name}=")
end
define_method(attribute) do
instance_variable_get("@#{attribute}") || instance_variable_set("@#{attribute}", decrypt(attribute, send(encrypted_attribute_name)))
end
define_method("#{attribute}=") do |value|
send("#{encrypted_attribute_name}=", encrypt(attribute, value))
instance_variable_set("@#{attribute}", value)
end
define_method("#{attribute}?") do
value = send(attribute)
value.respond_to?(:empty?) ? !value.empty? : !!value
end
encrypted_attributes[attribute.to_sym] = options.merge(attribute: encrypted_attribute_name)
end
end
alias_method :attr_encryptor, :attr_encrypted
# Default options to use with calls to attr_encrypted
#
# It will inherit existing options from its superclass
def attr_encrypted_options
@attr_encrypted_options ||= superclass.attr_encrypted_options.dup
end
def attr_encrypted_default_options
{
prefix: 'encrypted_',
suffix: '',
if: true,
unless: false,
encode: false,
encode_iv: true,
encode_salt: true,
default_encoding: 'm',
marshal: false,
marshaler: Marshal,
dump_method: 'dump',
load_method: 'load',
encryptor: Encryptor,
encrypt_method: 'encrypt',
decrypt_method: 'decrypt',
mode: :per_attribute_iv,
algorithm: 'aes-256-gcm',
allow_empty_value: false,
}
end
private :attr_encrypted_default_options
# Checks if an attribute is configured with attr_encrypted
#
# Example
#
# class User
# attr_accessor :name
# attr_encrypted :email
# end
#
# User.attr_encrypted?(:name) # false
# User.attr_encrypted?(:email) # true
def attr_encrypted?(attribute)
encrypted_attributes.has_key?(attribute.to_sym)
end
# Decrypts a value for the attribute specified
#
# Example
#
# class User
# attr_encrypted :email
# end
#
# email = User.decrypt(:email, 'SOME_ENCRYPTED_EMAIL_STRING')
def decrypt(attribute, encrypted_value, options = {})
options = encrypted_attributes[attribute.to_sym].merge(options)
if options[:if] && !options[:unless] && not_empty?(encrypted_value)
encrypted_value = encrypted_value.unpack(options[:encode]).first if options[:encode]
value = options[:encryptor].send(options[:decrypt_method], options.merge!(value: encrypted_value))
if options[:marshal]
value = options[:marshaler].send(options[:load_method], value)
elsif defined?(Encoding)
encoding = Encoding.default_internal || Encoding.default_external
value = value.force_encoding(encoding.name)
end
value
else
encrypted_value
end
end
# Encrypts a value for the attribute specified
#
# Example
#
# class User
# attr_encrypted :email
# end
#
# encrypted_email = User.encrypt(:email, 'test@example.com')
def encrypt(attribute, value, options = {})
options = encrypted_attributes[attribute.to_sym].merge(options)
if options[:if] && !options[:unless] && (options[:allow_empty_value] || not_empty?(value))
value = options[:marshal] ? options[:marshaler].send(options[:dump_method], value) : value.to_s
encrypted_value = options[:encryptor].send(options[:encrypt_method], options.merge!(value: value))
encrypted_value = [encrypted_value].pack(options[:encode]) if options[:encode]
encrypted_value
else
value
end
end
def not_empty?(value)
!value.nil? && !(value.is_a?(String) && value.empty?)
end
# Contains a hash of encrypted attributes with virtual attribute names as keys
# and their corresponding options as values
#
# Example
#
# class User
# attr_encrypted :email, key: 'my secret key'
# end
#
# User.encrypted_attributes # { email: { attribute: 'encrypted_email', key: 'my secret key' } }
def encrypted_attributes
@encrypted_attributes ||= superclass.encrypted_attributes.dup
end
# Forwards calls to :encrypt_#{attribute} or :decrypt_#{attribute} to the corresponding encrypt or decrypt method
# if attribute was configured with attr_encrypted
#
# Example
#
# class User
# attr_encrypted :email, key: 'my secret key'
# end
#
# User.encrypt_email('SOME_ENCRYPTED_EMAIL_STRING')
def method_missing(method, *arguments, &block)
if method.to_s =~ /^((en|de)crypt)_(.+)$/ && attr_encrypted?($3)
send($1, $3, *arguments)
else
super
end
end
module InstanceMethods
# Decrypts a value for the attribute specified using options evaluated in the current object's scope
#
# Example
#
# class User
# attr_accessor :secret_key
# attr_encrypted :email, key: :secret_key
#
# def initialize(secret_key)
# self.secret_key = secret_key
# end
# end
#
# @user = User.new('some-secret-key')
# @user.decrypt(:email, 'SOME_ENCRYPTED_EMAIL_STRING')
def decrypt(attribute, encrypted_value)
encrypted_attributes[attribute.to_sym][:operation] = :decrypting
encrypted_attributes[attribute.to_sym][:value_present] = self.class.not_empty?(encrypted_value)
self.class.decrypt(attribute, encrypted_value, evaluated_attr_encrypted_options_for(attribute))
end
# Encrypts a value for the attribute specified using options evaluated in the current object's scope
#
# Example
#
# class User
# attr_accessor :secret_key
# attr_encrypted :email, key: :secret_key
#
# def initialize(secret_key)
# self.secret_key = secret_key
# end
# end
#
# @user = User.new('some-secret-key')
# @user.encrypt(:email, 'test@example.com')
def encrypt(attribute, value)
encrypted_attributes[attribute.to_sym][:operation] = :encrypting
encrypted_attributes[attribute.to_sym][:value_present] = self.class.not_empty?(value)
self.class.encrypt(attribute, value, evaluated_attr_encrypted_options_for(attribute))
end
# Copies the class level hash of encrypted attributes with virtual attribute names as keys
# and their corresponding options as values to the instance
#
def encrypted_attributes
@encrypted_attributes ||= self.class.encrypted_attributes.dup
end
protected
# Returns attr_encrypted options evaluated in the current object's scope for the attribute specified
def evaluated_attr_encrypted_options_for(attribute)
evaluated_options = Hash.new
attribute_option_value = encrypted_attributes[attribute.to_sym][:attribute]
encrypted_attributes[attribute.to_sym].map do |option, value|
evaluated_options[option] = evaluate_attr_encrypted_option(value)
end
evaluated_options[:attribute] = attribute_option_value
evaluated_options.tap do |options|
if options[:if] && !options[:unless] && options[:value_present] || options[:allow_empty_value]
unless options[:mode] == :single_iv_and_salt
load_iv_for_attribute(attribute, options)
end
if options[:mode] == :per_attribute_iv_and_salt
load_salt_for_attribute(attribute, options)
end
end
end
end
# Evaluates symbol (method reference) or proc (responds to call) options
#
# If the option is not a symbol or proc then the original option is returned
def evaluate_attr_encrypted_option(option)
if option.is_a?(Symbol) && respond_to?(option, true)
send(option)
elsif option.respond_to?(:call)
option.call(self)
else
option
end
end
def load_iv_for_attribute(attribute, options)
encrypted_attribute_name = options[:attribute]
encode_iv = options[:encode_iv]
iv = options[:iv] || send("#{encrypted_attribute_name}_iv")
if options[:operation] == :encrypting
begin
iv = generate_iv(options[:algorithm])
iv = [iv].pack(encode_iv) if encode_iv
send("#{encrypted_attribute_name}_iv=", iv)
rescue RuntimeError
end
end
if iv && !iv.empty?
iv = iv.unpack(encode_iv).first if encode_iv
options[:iv] = iv
end
end
def generate_iv(algorithm)
algo = OpenSSL::Cipher.new(algorithm)
algo.encrypt
algo.random_iv
end
def load_salt_for_attribute(attribute, options)
encrypted_attribute_name = options[:attribute]
encode_salt = options[:encode_salt]
salt = options[:salt] || send("#{encrypted_attribute_name}_salt")
if options[:operation] == :encrypting
salt = SecureRandom.random_bytes
salt = prefix_and_encode_salt(salt, encode_salt) if encode_salt
send("#{encrypted_attribute_name}_salt=", salt)
end
if salt && !salt.empty?
salt = decode_salt_if_encoded(salt, encode_salt) if encode_salt
options[:salt] = salt
end
end
def prefix_and_encode_salt(salt, encoding)
prefix = '_'
prefix + [salt].pack(encoding)
end
def decode_salt_if_encoded(salt, encoding)
prefix = '_'
salt.slice(0).eql?(prefix) ? salt.slice(1..-1).unpack(encoding).first : salt
end
end
protected
def attribute_instance_methods_as_symbols
instance_methods.collect { |method| method.to_sym }
end
def attribute_instance_methods_as_symbols_available?
true
end
end
Dir[File.join(File.dirname(__FILE__), 'attr_encrypted', 'adapters', '*.rb')].each { |adapter| require adapter }
ruby-attr-encrypted-3.1.0/lib/attr_encrypted/ 0000775 0000000 0000000 00000000000 13274570231 0021240 5 ustar 00root root 0000000 0000000 ruby-attr-encrypted-3.1.0/lib/attr_encrypted/adapters/ 0000775 0000000 0000000 00000000000 13274570231 0023043 5 ustar 00root root 0000000 0000000 ruby-attr-encrypted-3.1.0/lib/attr_encrypted/adapters/active_record.rb 0000664 0000000 0000000 00000013045 13274570231 0026204 0 ustar 00root root 0000000 0000000 if defined?(ActiveRecord::Base)
module AttrEncrypted
module Adapters
module ActiveRecord
def self.extended(base) # :nodoc:
base.class_eval do
# https://github.com/attr-encrypted/attr_encrypted/issues/68
alias_method :reload_without_attr_encrypted, :reload
def reload(*args, &block)
result = reload_without_attr_encrypted(*args, &block)
self.class.encrypted_attributes.keys.each do |attribute_name|
instance_variable_set("@#{attribute_name}", nil)
end
result
end
attr_encrypted_options[:encode] = true
class << self
alias_method :method_missing_without_attr_encrypted, :method_missing
alias_method :method_missing, :method_missing_with_attr_encrypted
end
def perform_attribute_assignment(method, new_attributes, *args)
return if new_attributes.blank?
send method, new_attributes.reject { |k, _| self.class.encrypted_attributes.key?(k.to_sym) }, *args
send method, new_attributes.reject { |k, _| !self.class.encrypted_attributes.key?(k.to_sym) }, *args
end
private :perform_attribute_assignment
if ::ActiveRecord::VERSION::STRING > "3.1"
alias_method :assign_attributes_without_attr_encrypted, :assign_attributes
def assign_attributes(*args)
perform_attribute_assignment :assign_attributes_without_attr_encrypted, *args
end
end
alias_method :attributes_without_attr_encrypted=, :attributes=
def attributes=(*args)
perform_attribute_assignment :attributes_without_attr_encrypted=, *args
end
end
end
protected
# attr_encrypted method
def attr_encrypted(*attrs)
super
options = attrs.extract_options!
attr = attrs.pop
attribute attr if ::ActiveRecord::VERSION::STRING >= "5.1.0"
options.merge! encrypted_attributes[attr]
define_method("#{attr}_was") do
attribute_was(attr)
end
if ::ActiveRecord::VERSION::STRING >= "4.1"
define_method("#{attr}_changed?") do |options = {}|
attribute_changed?(attr, options)
end
else
define_method("#{attr}_changed?") do
attribute_changed?(attr)
end
end
define_method("#{attr}_change") do
attribute_change(attr)
end
define_method("#{attr}_with_dirtiness=") do |value|
attribute_will_change!(attr) if value != __send__(attr)
__send__("#{attr}_without_dirtiness=", value)
end
alias_method "#{attr}_without_dirtiness=", "#{attr}="
alias_method "#{attr}=", "#{attr}_with_dirtiness="
alias_method "#{attr}_before_type_cast", attr
end
def attribute_instance_methods_as_symbols
# We add accessor methods of the db columns to the list of instance
# methods returned to let ActiveRecord define the accessor methods
# for the db columns
if connected? && table_exists?
columns_hash.keys.inject(super) {|instance_methods, column_name| instance_methods.concat [column_name.to_sym, :"#{column_name}="]}
else
super
end
end
def attribute_instance_methods_as_symbols_available?
connected? && table_exists?
end
# Allows you to use dynamic methods like find_by_email or scoped_by_email for
# encrypted attributes
#
# NOTE: This only works when the :key option is specified as a string (see the README)
#
# This is useful for encrypting fields like email addresses. Your user's email addresses
# are encrypted in the database, but you can still look up a user by email for logging in
#
# Example
#
# class User < ActiveRecord::Base
# attr_encrypted :email, key: 'secret key'
# end
#
# User.find_by_email_and_password('test@example.com', 'testing')
# # results in a call to
# User.find_by_encrypted_email_and_password('the_encrypted_version_of_test@example.com', 'testing')
def method_missing_with_attr_encrypted(method, *args, &block)
if match = /^(find|scoped)_(all_by|by)_([_a-zA-Z]\w*)$/.match(method.to_s)
attribute_names = match.captures.last.split('_and_')
attribute_names.each_with_index do |attribute, index|
if attr_encrypted?(attribute) && encrypted_attributes[attribute.to_sym][:mode] == :single_iv_and_salt
args[index] = send("encrypt_#{attribute}", args[index])
warn "DEPRECATION WARNING: This feature will be removed in the next major release."
attribute_names[index] = encrypted_attributes[attribute.to_sym][:attribute]
end
end
method = "#{match.captures[0]}_#{match.captures[1]}_#{attribute_names.join('_and_')}".to_sym
end
method_missing_without_attr_encrypted(method, *args, &block)
end
end
end
end
ActiveRecord::Base.extend AttrEncrypted
ActiveRecord::Base.extend AttrEncrypted::Adapters::ActiveRecord
end
ruby-attr-encrypted-3.1.0/lib/attr_encrypted/adapters/data_mapper.rb 0000664 0000000 0000000 00000001142 13274570231 0025643 0 ustar 00root root 0000000 0000000 if defined?(DataMapper)
module AttrEncrypted
module Adapters
module DataMapper
def self.extended(base) # :nodoc:
class << base
alias_method :included_without_attr_encrypted, :included
alias_method :included, :included_with_attr_encrypted
end
end
def included_with_attr_encrypted(base)
included_without_attr_encrypted(base)
base.extend AttrEncrypted
base.attr_encrypted_options[:encode] = true
end
end
end
end
DataMapper::Resource.extend AttrEncrypted::Adapters::DataMapper
end ruby-attr-encrypted-3.1.0/lib/attr_encrypted/adapters/sequel.rb 0000664 0000000 0000000 00000000467 13274570231 0024675 0 ustar 00root root 0000000 0000000 if defined?(Sequel)
module AttrEncrypted
module Adapters
module Sequel
def self.extended(base) # :nodoc:
base.attr_encrypted_options[:encode] = true
end
end
end
end
Sequel::Model.extend AttrEncrypted
Sequel::Model.extend AttrEncrypted::Adapters::Sequel
end ruby-attr-encrypted-3.1.0/lib/attr_encrypted/version.rb 0000664 0000000 0000000 00000000571 13274570231 0023255 0 ustar 00root root 0000000 0000000 module AttrEncrypted
# Contains information about this gem's version
module Version
MAJOR = 3
MINOR = 1
PATCH = 0
# Returns a version string by joining MAJOR, MINOR, and PATCH with '.'
#
# Example
#
# Version.string # '1.0.2'
def self.string
[MAJOR, MINOR, PATCH].join('.')
end
end
end
ruby-attr-encrypted-3.1.0/metadata.gz.sig 0000664 0000000 0000000 00000000400 13274570231 0020340 0 ustar 00root root 0000000 0000000 AeÃmL¶ÄúøÏÉ6>©o(pƒñÍ{ÿž~xóO.]Là|ZgfQËL44uؤť:òøS!oÜ@€Äž€§óau9s- Xx÷6ïù²ÏM
…+ bæmš7“öÚžG‘¾«£Ð8Ñ8ˆ’+Ãúàͬ\‰Ýàç?´êüÎ) nÖÁ‘¶ƒ¸»C7†§–¾àñ”rv”å`by¥¨$8&Ë.Ô«Í@"¸¿ˆø¾é^ÒÕ&xxUÉ2ïÔÈØœfž5øØNi<ÆÆe}á÷½Ò7øÓ:È[]çüÎ{˜DÏè”åkÒ(W»Ù„ë#ꕣʱ® ruby-attr-encrypted-3.1.0/test/ 0000775 0000000 0000000 00000000000 13274570231 0016422 5 ustar 00root root 0000000 0000000 ruby-attr-encrypted-3.1.0/test/active_record_test.rb 0000664 0000000 0000000 00000026433 13274570231 0022627 0 ustar 00root root 0000000 0000000 require_relative 'test_helper'
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
def create_tables
ActiveRecord::Schema.define(version: 1) do
self.verbose = false
create_table :people do |t|
t.string :encrypted_email
t.string :password
t.string :encrypted_credentials
t.binary :salt
t.binary :key_iv
t.string :encrypted_email_salt
t.string :encrypted_credentials_salt
t.string :encrypted_email_iv
t.string :encrypted_credentials_iv
end
create_table :accounts do |t|
t.string :encrypted_password
t.string :encrypted_password_iv
t.string :encrypted_password_salt
t.string :key
end
create_table :users do |t|
t.string :login
t.string :encrypted_password
t.string :encrypted_password_iv
t.boolean :is_admin
end
create_table :prime_ministers do |t|
t.string :encrypted_name
t.string :encrypted_name_iv
end
create_table :addresses do |t|
t.binary :encrypted_street
t.binary :encrypted_street_iv
t.binary :encrypted_zipcode
t.string :mode
end
end
end
ActiveRecord::MissingAttributeError = ActiveModel::MissingAttributeError unless defined?(ActiveRecord::MissingAttributeError)
if ::ActiveRecord::VERSION::STRING > "4.0"
module Rack
module Test
class UploadedFile; end
end
end
require 'action_controller/metal/strong_parameters'
end
class Person < ActiveRecord::Base
self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
attr_encrypted :email, key: SECRET_KEY
attr_encrypted :credentials, key: Proc.new { |user| Encryptor.encrypt(value: user.salt, key: SECRET_KEY, iv: user.key_iv) }, marshal: true
after_initialize :initialize_salt_and_credentials
protected
def initialize_salt_and_credentials
self.key_iv ||= SecureRandom.random_bytes(12)
self.salt ||= Digest::SHA256.hexdigest((Time.now.to_i * rand(1000)).to_s)[0..15]
self.credentials ||= { username: 'example', password: 'test' }
end
end
class PersonWithValidation < Person
validates_presence_of :email
end
class PersonWithProcMode < Person
attr_encrypted :email, key: SECRET_KEY, mode: Proc.new { :per_attribute_iv_and_salt }
attr_encrypted :credentials, key: SECRET_KEY, mode: Proc.new { :single_iv_and_salt }, insecure_mode: true
end
class Account < ActiveRecord::Base
ACCOUNT_ENCRYPTION_KEY = SecureRandom.urlsafe_base64(24)
attr_encrypted :password, key: :password_encryption_key
def encrypting?(attr)
encrypted_attributes[attr][:operation] == :encrypting
end
def password_encryption_key
if encrypting?(:password)
self.key = ACCOUNT_ENCRYPTION_KEY
else
self.key
end
end
end
class PersonWithSerialization < ActiveRecord::Base
self.table_name = 'people'
attr_encrypted :email, key: SECRET_KEY
serialize :password
end
class UserWithProtectedAttribute < ActiveRecord::Base
self.table_name = 'users'
attr_encrypted :password, key: SECRET_KEY
attr_protected :is_admin if ::ActiveRecord::VERSION::STRING < "4.0"
end
class PersonUsingAlias < ActiveRecord::Base
self.table_name = 'people'
attr_encryptor :email, key: SECRET_KEY
end
class PrimeMinister < ActiveRecord::Base
attr_encrypted :name, marshal: true, key: SECRET_KEY
end
class Address < ActiveRecord::Base
self.attr_encrypted_options[:marshal] = false
self.attr_encrypted_options[:encode] = false
attr_encrypted :street, encode_iv: false, key: SECRET_KEY
attr_encrypted :zipcode, key: SECRET_KEY, mode: Proc.new { |address| address.mode.to_sym }, insecure_mode: true
end
class ActiveRecordTest < Minitest::Test
def setup
drop_all_tables
create_tables
end
def test_should_encrypt_email
@person = Person.create(email: 'test@example.com')
refute_nil @person.encrypted_email
refute_equal @person.email, @person.encrypted_email
assert_equal @person.email, Person.first.email
end
def test_should_marshal_and_encrypt_credentials
@person = Person.create
refute_nil @person.encrypted_credentials
refute_equal @person.credentials, @person.encrypted_credentials
assert_equal @person.credentials, Person.first.credentials
end
def test_should_encode_by_default
assert Person.attr_encrypted_options[:encode]
end
def test_should_validate_presence_of_email
@person = PersonWithValidation.new
assert !@person.valid?
assert !@person.errors[:email].empty? || @person.errors.on(:email)
end
def test_should_encrypt_decrypt_with_iv
@person = Person.create(email: 'test@example.com')
@person2 = Person.find(@person.id)
refute_nil @person2.encrypted_email_iv
assert_equal 'test@example.com', @person2.email
end
def test_should_ensure_attributes_can_be_deserialized
@person = PersonWithSerialization.new(email: 'test@example.com', password: %w(an array of strings))
@person.save
assert_equal @person.password, %w(an array of strings)
end
def test_should_create_an_account_regardless_of_arguments_order
Account.create!(key: SECRET_KEY, password: "password")
Account.create!(password: "password" , key: SECRET_KEY)
end
def test_should_set_attributes_regardless_of_arguments_order
# minitest does not implement `assert_nothing_raised` https://github.com/seattlerb/minitest/issues/112
Account.new.attributes = { password: "password", key: SECRET_KEY }
end
def test_should_create_changed_predicate
person = Person.create!(email: 'test@example.com')
refute person.email_changed?
person.email = 'test@example.com'
refute person.email_changed?
person.email = nil
assert person.email_changed?
person.email = 'test2@example.com'
assert person.email_changed?
end
def test_should_create_was_predicate
original_email = 'test@example.com'
person = Person.create!(email: original_email)
assert_equal original_email, person.email_was
person.email = 'test2@example.com'
assert_equal original_email, person.email_was
old_pm_name = "Winston Churchill"
pm = PrimeMinister.create!(name: old_pm_name)
assert_equal old_pm_name, pm.name_was
old_zipcode = "90210"
address = Address.create!(zipcode: old_zipcode, mode: "single_iv_and_salt")
assert_equal old_zipcode, address.zipcode_was
end
def test_attribute_was_works_when_options_for_old_encrypted_value_are_different_than_options_for_new_encrypted_value
pw = 'password'
crypto_key = SecureRandom.urlsafe_base64(24)
old_iv = SecureRandom.random_bytes(12)
account = Account.create
encrypted_value = Encryptor.encrypt(value: pw, iv: old_iv, key: crypto_key)
Account.where(id: account.id).update_all(key: crypto_key, encrypted_password_iv: [old_iv].pack('m'), encrypted_password: [encrypted_value].pack('m'))
account = Account.find(account.id)
assert_equal pw, account.password
account.password = pw.reverse
assert_equal pw, account.password_was
account.save
account.reload
assert_equal Account::ACCOUNT_ENCRYPTION_KEY, account.key
assert_equal pw.reverse, account.password
end
if ::ActiveRecord::VERSION::STRING > "4.0"
def test_should_assign_attributes
@user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
@user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true).permit(:login)
assert_equal 'modified', @user.login
end
def test_should_not_assign_protected_attributes
@user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
@user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true).permit(:login)
assert !@user.is_admin?
end
def test_should_raise_exception_if_not_permitted
@user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
assert_raises ActiveModel::ForbiddenAttributesError do
@user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true)
end
end
def test_should_raise_exception_on_init_if_not_permitted
assert_raises ActiveModel::ForbiddenAttributesError do
@user = UserWithProtectedAttribute.new ActionController::Parameters.new(login: 'modified', is_admin: true)
end
end
else
def test_should_assign_attributes
@user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
@user.attributes = { login: 'modified', is_admin: true }
assert_equal 'modified', @user.login
end
def test_should_not_assign_protected_attributes
@user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
@user.attributes = { login: 'modified', is_admin: true }
assert !@user.is_admin?
end
def test_should_assign_protected_attributes
@user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
if ::ActiveRecord::VERSION::STRING > "3.1"
@user.send(:assign_attributes, { login: 'modified', is_admin: true }, without_protection: true)
else
@user.send(:attributes=, { login: 'modified', is_admin: true }, false)
end
assert @user.is_admin?
end
end
def test_should_allow_assignment_of_nil_attributes
@person = Person.new
assert_nil(@person.attributes = nil)
end
def test_should_allow_proc_based_mode
@person = PersonWithProcMode.create(email: 'test@example.com', credentials: 'password123')
# Email is :per_attribute_iv_and_salt
assert_equal @person.class.encrypted_attributes[:email][:mode].class, Proc
assert_equal @person.class.encrypted_attributes[:email][:mode].call, :per_attribute_iv_and_salt
refute_nil @person.encrypted_email_salt
refute_nil @person.encrypted_email_iv
# Credentials is :single_iv_and_salt
assert_equal @person.class.encrypted_attributes[:credentials][:mode].class, Proc
assert_equal @person.class.encrypted_attributes[:credentials][:mode].call, :single_iv_and_salt
assert_nil @person.encrypted_credentials_salt
assert_nil @person.encrypted_credentials_iv
end
if ::ActiveRecord::VERSION::STRING > "3.1"
def test_should_allow_assign_attributes_with_nil
@person = Person.new
assert_nil(@person.assign_attributes nil)
end
end
def test_that_alias_encrypts_column
user = PersonUsingAlias.new
user.email = 'test@example.com'
user.save
refute_nil user.encrypted_email
refute_equal user.email, user.encrypted_email
assert_equal user.email, PersonUsingAlias.first.email
end
# See https://github.com/attr-encrypted/attr_encrypted/issues/68
def test_should_invalidate_virtual_attributes_on_reload
old_pm_name = 'Winston Churchill'
new_pm_name = 'Neville Chamberlain'
pm = PrimeMinister.create!(name: old_pm_name)
assert_equal old_pm_name, pm.name
pm.name = new_pm_name
assert_equal new_pm_name, pm.name
result = pm.reload
assert_equal pm, result
assert_equal old_pm_name, pm.name
end
def test_should_save_encrypted_data_as_binary
street = '123 Elm'
address = Address.create!(street: street)
refute_equal address.encrypted_street, street
assert_equal Address.first.street, street
end
def test_should_evaluate_proc_based_mode
street = '123 Elm'
zipcode = '12345'
address = Address.create(street: street, zipcode: zipcode, mode: :single_iv_and_salt)
address.reload
refute_equal address.encrypted_zipcode, zipcode
assert_equal address.zipcode, zipcode
end
end
ruby-attr-encrypted-3.1.0/test/attr_encrypted_test.rb 0000664 0000000 0000000 00000040630 13274570231 0023040 0 ustar 00root root 0000000 0000000 # encoding: UTF-8
require_relative 'test_helper'
class SillyEncryptor
def self.silly_encrypt(options)
(options[:value] + options[:some_arg]).reverse
end
def self.silly_decrypt(options)
options[:value].reverse.gsub(/#{options[:some_arg]}$/, '')
end
end
class User
extend AttrEncrypted
self.attr_encrypted_options[:key] = Proc.new { |user| SECRET_KEY } # default key
self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
attr_encrypted :email, :without_encoding, :key => SECRET_KEY
attr_encrypted :password, :prefix => 'crypted_', :suffix => '_test'
attr_encrypted :ssn, :key => :secret_key, :attribute => 'ssn_encrypted'
attr_encrypted :credit_card, :encryptor => SillyEncryptor, :encrypt_method => :silly_encrypt, :decrypt_method => :silly_decrypt, :some_arg => 'test'
attr_encrypted :with_encoding, :key => SECRET_KEY, :encode => true
attr_encrypted :with_custom_encoding, :key => SECRET_KEY, :encode => 'm'
attr_encrypted :with_marshaling, :key => SECRET_KEY, :marshal => true
attr_encrypted :with_true_if, :key => SECRET_KEY, :if => true, mode: :per_attribute_iv_and_salt
attr_encrypted :with_false_if, :key => SECRET_KEY, :if => false, mode: :per_attribute_iv_and_salt
attr_encrypted :with_true_unless, :key => SECRET_KEY, :unless => true, mode: :per_attribute_iv_and_salt
attr_encrypted :with_false_unless, :key => SECRET_KEY, :unless => false, mode: :per_attribute_iv_and_salt
attr_encrypted :with_if_changed, :key => SECRET_KEY, :if => :should_encrypt
attr_encrypted :with_allow_empty_value, key: SECRET_KEY, allow_empty_value: true, marshal: true
attr_encryptor :aliased, :key => SECRET_KEY
attr_accessor :salt
attr_accessor :should_encrypt
def initialize(email: nil)
self.email = email
self.salt = Time.now.to_i.to_s
self.should_encrypt = true
end
private
def secret_key
SECRET_KEY
end
end
class Admin < User
attr_encrypted :testing
end
class SomeOtherClass
extend AttrEncrypted
def self.call(object)
object.class
end
end
class YetAnotherClass
extend AttrEncrypted
self.attr_encrypted_options[:encode_iv] = false
attr_encrypted :email, :key => SECRET_KEY
attr_encrypted :phone_number, :key => SECRET_KEY, mode: Proc.new { |thing| thing.mode }, encode_iv: Proc.new { |thing| thing.encode_iv }, encode_salt: Proc.new { |thing| thing.encode_salt }
def initialize(email: nil, encode_iv: 'm', encode_salt: 'm', mode: :per_attribute_iv_and_salt)
self.email = email
@encode_iv = encode_iv
@encode_salt = encode_salt
@mode = mode
end
attr_reader :encode_iv, :encode_salt, :mode
end
class AttrEncryptedTest < Minitest::Test
def setup
@iv = SecureRandom.random_bytes(12)
end
def test_should_store_email_in_encrypted_attributes
assert User.encrypted_attributes.include?(:email)
end
def test_should_not_store_salt_in_encrypted_attributes
refute User.encrypted_attributes.include?(:salt)
end
def test_attr_encrypted_should_return_true_for_email
assert User.attr_encrypted?('email')
end
def test_attr_encrypted_should_not_use_the_same_attribute_name_for_two_attributes_in_the_same_line
refute_equal User.encrypted_attributes[:email][:attribute], User.encrypted_attributes[:without_encoding][:attribute]
end
def test_attr_encrypted_should_return_false_for_salt
assert !User.attr_encrypted?('salt')
end
def test_should_generate_an_encrypted_attribute
assert User.new.respond_to?(:encrypted_email)
end
def test_should_generate_an_encrypted_attribute_with_a_prefix_and_suffix
assert User.new.respond_to?(:crypted_password_test)
end
def test_should_generate_an_encrypted_attribute_with_the_attribute_option
assert User.new.respond_to?(:ssn_encrypted)
end
def test_should_not_encrypt_nil_value
assert_nil User.encrypt_email(nil, iv: @iv)
end
def test_should_not_encrypt_empty_string_by_default
assert_equal '', User.encrypt_email('', iv: @iv)
end
def test_should_encrypt_email
refute_nil User.encrypt_email('test@example.com', iv: @iv)
refute_equal 'test@example.com', User.encrypt_email('test@example.com', iv: @iv)
end
def test_should_encrypt_email_when_modifying_the_attr_writer
@user = User.new
assert_nil @user.encrypted_email
@user.email = 'test@example.com'
refute_nil @user.encrypted_email
iv = @user.encrypted_email_iv.unpack('m').first
salt = @user.encrypted_email_salt[1..-1].unpack('m').first
assert_equal User.encrypt_email('test@example.com', iv: iv, salt: salt), @user.encrypted_email
end
def test_should_not_decrypt_nil_value
assert_nil User.decrypt_email(nil, iv: @iv)
end
def test_should_not_decrypt_empty_string
assert_equal '', User.decrypt_email('', iv: @iv)
end
def test_should_decrypt_email
encrypted_email = User.encrypt_email('test@example.com', iv: @iv)
refute_equal 'test@test.com', encrypted_email
assert_equal 'test@example.com', User.decrypt_email(encrypted_email, iv: @iv)
end
def test_should_decrypt_email_when_reading
@user = User.new
assert_nil @user.email
options = @user.encrypted_attributes[:email]
iv = @user.send(:generate_iv, options[:algorithm])
encoded_iv = [iv].pack(options[:encode_iv])
salt = SecureRandom.random_bytes
encoded_salt = @user.send(:prefix_and_encode_salt, salt, options[:encode_salt])
@user.encrypted_email = User.encrypt_email('test@example.com', iv: iv, salt: salt)
@user.encrypted_email_iv = encoded_iv
@user.encrypted_email_salt = encoded_salt
assert_equal 'test@example.com', @user.email
end
def test_should_encrypt_with_encoding
assert_equal User.encrypt_with_encoding('test', iv: @iv), [User.encrypt_without_encoding('test', iv: @iv)].pack('m')
end
def test_should_decrypt_with_encoding
encrypted = User.encrypt_with_encoding('test', iv: @iv)
assert_equal 'test', User.decrypt_with_encoding(encrypted, iv: @iv)
assert_equal User.decrypt_with_encoding(encrypted, iv: @iv), User.decrypt_without_encoding(encrypted.unpack('m').first, iv: @iv)
end
def test_should_encrypt_with_custom_encoding
assert_equal User.encrypt_with_encoding('test', iv: @iv), [User.encrypt_without_encoding('test', iv: @iv)].pack('m')
end
def test_should_decrypt_with_custom_encoding
encrypted = User.encrypt_with_encoding('test', iv: @iv)
assert_equal 'test', User.decrypt_with_encoding(encrypted, iv: @iv)
assert_equal User.decrypt_with_encoding(encrypted, iv: @iv), User.decrypt_without_encoding(encrypted.unpack('m').first, iv: @iv)
end
def test_should_encrypt_with_marshaling
@user = User.new
@user.with_marshaling = [1, 2, 3]
refute_nil @user.encrypted_with_marshaling
end
def test_should_use_custom_encryptor_and_crypt_method_names_and_arguments
assert_equal SillyEncryptor.silly_encrypt(:value => 'testing', :some_arg => 'test'), User.encrypt_credit_card('testing')
end
def test_should_evaluate_a_key_passed_as_a_symbol
@user = User.new
assert_nil @user.ssn_encrypted
@user.ssn = 'testing'
refute_nil @user.ssn_encrypted
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.ssn_encrypted_iv.unpack("m").first, :salt => @user.ssn_encrypted_salt.unpack("m").first )
assert_equal encrypted, @user.ssn_encrypted
end
def test_should_evaluate_a_key_passed_as_a_proc
@user = User.new
assert_nil @user.crypted_password_test
@user.password = 'testing'
refute_nil @user.crypted_password_test
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.crypted_password_test_iv.unpack("m").first, :salt => @user.crypted_password_test_salt.unpack("m").first)
assert_equal encrypted, @user.crypted_password_test
end
def test_should_use_options_found_in_the_attr_encrypted_options_attribute
@user = User.new
assert_nil @user.crypted_password_test
@user.password = 'testing'
refute_nil @user.crypted_password_test
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.crypted_password_test_iv.unpack("m").first, :salt => @user.crypted_password_test_salt.unpack("m").first)
assert_equal encrypted, @user.crypted_password_test
end
def test_should_inherit_encrypted_attributes
assert_equal [User.encrypted_attributes.keys, :testing].flatten.collect { |key| key.to_s }.sort, Admin.encrypted_attributes.keys.collect { |key| key.to_s }.sort
end
def test_should_inherit_attr_encrypted_options
assert !User.attr_encrypted_options.empty?
assert_equal User.attr_encrypted_options, Admin.attr_encrypted_options
end
def test_should_not_inherit_unrelated_attributes
assert SomeOtherClass.attr_encrypted_options.empty?
assert SomeOtherClass.encrypted_attributes.empty?
end
def test_should_evaluate_a_symbol_option
assert_equal SomeOtherClass, SomeOtherClass.new.send(:evaluate_attr_encrypted_option, :class)
end
def test_should_evaluate_a_proc_option
assert_equal SomeOtherClass, SomeOtherClass.new.send(:evaluate_attr_encrypted_option, proc { |object| object.class })
end
def test_should_evaluate_a_lambda_option
assert_equal SomeOtherClass, SomeOtherClass.new.send(:evaluate_attr_encrypted_option, lambda { |object| object.class })
end
def test_should_evaluate_a_method_option
assert_equal SomeOtherClass, SomeOtherClass.new.send(:evaluate_attr_encrypted_option, SomeOtherClass.method(:call))
end
def test_should_return_a_string_option
class_string = 'SomeOtherClass'
assert_equal class_string, SomeOtherClass.new.send(:evaluate_attr_encrypted_option, class_string)
end
def test_should_encrypt_with_true_if
@user = User.new
assert_nil @user.encrypted_with_true_if
@user.with_true_if = 'testing'
refute_nil @user.encrypted_with_true_if
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.encrypted_with_true_if_iv.unpack("m").first, :salt => @user.encrypted_with_true_if_salt.unpack("m").first)
assert_equal encrypted, @user.encrypted_with_true_if
end
def test_should_not_encrypt_with_false_if
@user = User.new
assert_nil @user.encrypted_with_false_if
@user.with_false_if = 'testing'
refute_nil @user.encrypted_with_false_if
assert_equal 'testing', @user.encrypted_with_false_if
end
def test_should_encrypt_with_false_unless
@user = User.new
assert_nil @user.encrypted_with_false_unless
@user.with_false_unless = 'testing'
refute_nil @user.encrypted_with_false_unless
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.encrypted_with_false_unless_iv.unpack("m").first, :salt => @user.encrypted_with_false_unless_salt.unpack("m").first)
assert_equal encrypted, @user.encrypted_with_false_unless
end
def test_should_not_encrypt_with_true_unless
@user = User.new
assert_nil @user.encrypted_with_true_unless
@user.with_true_unless = 'testing'
refute_nil @user.encrypted_with_true_unless
assert_equal 'testing', @user.encrypted_with_true_unless
end
def test_should_encrypt_empty_with_truthy_allow_empty_value_option
@user = User.new
assert_nil @user.encrypted_with_allow_empty_value
@user.with_allow_empty_value = ''
refute_nil @user.encrypted_with_allow_empty_value
assert_equal '', @user.with_allow_empty_value
@user = User.new
@user.with_allow_empty_value = nil
refute_nil @user.encrypted_with_allow_empty_value
assert_nil @user.with_allow_empty_value
end
def test_should_work_with_aliased_attr_encryptor
assert User.encrypted_attributes.include?(:aliased)
end
def test_should_always_reset_options
@user = User.new
@user.with_if_changed = "encrypt_stuff"
@user = User.new
@user.should_encrypt = false
@user.with_if_changed = "not_encrypted_stuff"
assert_equal "not_encrypted_stuff", @user.with_if_changed
assert_equal "not_encrypted_stuff", @user.encrypted_with_if_changed
end
def test_should_cast_values_as_strings_before_encrypting
string_encrypted_email = User.encrypt_email('3', iv: @iv)
assert_equal string_encrypted_email, User.encrypt_email(3, iv: @iv)
assert_equal '3', User.decrypt_email(string_encrypted_email, iv: @iv)
end
def test_should_create_query_accessor
@user = User.new
assert !@user.email?
@user.email = ''
assert !@user.email?
@user.email = 'test@example.com'
assert @user.email?
end
def test_should_vary_iv_per_attribute
@user = User.new
@user.email = 'email@example.com'
@user.password = 'p455w0rd'
refute_equal @user.encrypted_email_iv, @user.crypted_password_test_iv
end
def test_should_generate_iv_per_attribute_by_default
thing = YetAnotherClass.new(email: 'thing@thing.com')
refute_nil thing.encrypted_email_iv
end
def test_should_vary_iv_per_instance
@user1 = User.new
@user1.email = 'email@example.com'
@user2 = User.new
@user2.email = 'email@example.com'
refute_equal @user1.encrypted_email_iv, @user2.encrypted_email_iv
refute_equal @user1.encrypted_email, @user2.encrypted_email
end
def test_should_vary_salt_per_attribute
@user = User.new
@user.email = 'email@example.com'
@user.password = 'p455w0rd'
refute_equal @user.encrypted_email_salt, @user.crypted_password_test_salt
end
def test_should_vary_salt_per_instance
@user1 = User.new
@user1.email = 'email@example.com'
@user2 = User.new
@user2.email = 'email@example.com'
refute_equal @user1.encrypted_email_salt, @user2.encrypted_email_salt
end
def test_should_not_generate_salt_per_attribute_by_default
thing = YetAnotherClass.new(email: 'thing@thing.com')
assert_nil thing.encrypted_email_salt
end
def test_should_decrypt_second_record
@user1 = User.new
@user1.email = 'test@example.com'
@user2 = User.new
@user2.email = 'test@example.com'
assert_equal 'test@example.com', @user1.decrypt(:email, @user1.encrypted_email)
end
def test_should_specify_the_default_algorithm
assert YetAnotherClass.encrypted_attributes[:email][:algorithm]
assert_equal YetAnotherClass.encrypted_attributes[:email][:algorithm], 'aes-256-gcm'
end
def test_should_not_encode_iv_when_encode_iv_is_false
email = 'thing@thing.com'
thing = YetAnotherClass.new(email: email)
refute thing.encrypted_email_iv =~ base64_encoding_regex
assert_equal thing.email, email
end
def test_should_base64_encode_iv_by_default
phone_number = '555-555-5555'
thing = YetAnotherClass.new
thing.phone_number = phone_number
assert thing.encrypted_phone_number_iv =~ base64_encoding_regex
assert_equal thing.phone_number, phone_number
end
def test_should_generate_unique_iv_for_every_encrypt_operation
user = User.new
user.email = 'initial_value@test.com'
original_iv = user.encrypted_email_iv
user.email = 'revised_value@test.com'
refute_equal original_iv, user.encrypted_email_iv
end
def test_should_not_generate_iv_for_attribute_when_if_option_is_false
user = User.new
user.with_false_if = 'derp'
assert_nil user.encrypted_with_false_if_iv
end
def test_should_generate_iv_for_attribute_when_if_option_is_true
user = User.new
user.with_true_if = 'derp'
refute_nil user.encrypted_with_true_if_iv
user.with_true_if = Object.new
refute_nil user.encrypted_with_true_if_iv
end
def test_should_not_generate_salt_for_attribute_when_if_option_is_false
user = User.new
user.with_false_if = 'derp'
assert_nil user.encrypted_with_false_if_salt
end
def test_should_generate_salt_for_attribute_when_if_option_is_true
user = User.new
user.with_true_if = 'derp'
refute_nil user.encrypted_with_true_if_salt
end
def test_should_generate_iv_for_attribute_when_unless_option_is_false
user = User.new
user.with_false_unless = 'derp'
refute_nil user.encrypted_with_false_unless_iv
end
def test_should_not_generate_iv_for_attribute_when_unless_option_is_true
user = User.new
user.with_true_unless = 'derp'
assert_nil user.encrypted_with_true_unless_iv
end
def test_should_generate_salt_for_attribute_when_unless_option_is_false
user = User.new
user.with_false_unless = 'derp'
refute_nil user.encrypted_with_false_unless_salt
end
def test_should_not_generate_salt_for_attribute_when_unless_option_is_true
user = User.new
user.with_true_unless = 'derp'
assert_nil user.encrypted_with_true_unless_salt
end
def test_should_not_by_default_generate_iv_when_attribute_is_empty
user = User.new
user.with_true_if = nil
assert_nil user.encrypted_with_true_if_iv
end
end
ruby-attr-encrypted-3.1.0/test/compatibility_test.rb 0000664 0000000 0000000 00000010401 13274570231 0022653 0 ustar 00root root 0000000 0000000 # -*- encoding: utf-8 -*-
require_relative 'test_helper'
# Test to ensure that existing representations in database do not break on
# migrating to new versions of this gem. This ensures that future versions of
# this gem will retain backwards compatibility with data generated by earlier
# versions.
class CompatibilityTest < Minitest::Test
class NonmarshallingPet < ActiveRecord::Base
PET_NICKNAME_SALT = Digest::SHA256.hexdigest('my-really-really-secret-pet-nickname-salt')
PET_NICKNAME_KEY = 'my-really-really-secret-pet-nickname-key'
PET_BIRTHDATE_SALT = Digest::SHA256.hexdigest('my-really-really-secret-pet-birthdate-salt')
PET_BIRTHDATE_KEY = 'my-really-really-secret-pet-birthdate-key'
self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
self.attr_encrypted_options[:algorithm] = 'aes-256-cbc'
self.attr_encrypted_options[:insecure_mode] = true
attr_encrypted :nickname,
:key => proc { Encryptor.encrypt(:value => PET_NICKNAME_SALT, :key => PET_NICKNAME_KEY, insecure_mode: true, algorithm: 'aes-256-cbc') }
attr_encrypted :birthdate,
:key => proc { Encryptor.encrypt(:value => PET_BIRTHDATE_SALT, :key => PET_BIRTHDATE_KEY, insecure_mode: true, algorithm: 'aes-256-cbc') }
end
class MarshallingPet < ActiveRecord::Base
PET_NICKNAME_SALT = Digest::SHA256.hexdigest('my-really-really-secret-pet-nickname-salt')
PET_NICKNAME_KEY = 'my-really-really-secret-pet-nickname-key'
PET_BIRTHDATE_SALT = Digest::SHA256.hexdigest('my-really-really-secret-pet-birthdate-salt')
PET_BIRTHDATE_KEY = 'my-really-really-secret-pet-birthdate-key'
self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
self.attr_encrypted_options[:algorithm] = 'aes-256-cbc'
self.attr_encrypted_options[:insecure_mode] = true
attr_encrypted :nickname,
:key => proc { Encryptor.encrypt(:value => PET_NICKNAME_SALT, :key => PET_NICKNAME_KEY, insecure_mode: true, algorithm: 'aes-256-cbc') },
:marshal => true
attr_encrypted :birthdate,
:key => proc { Encryptor.encrypt(:value => PET_BIRTHDATE_SALT, :key => PET_BIRTHDATE_KEY, insecure_mode: true, algorithm: 'aes-256-cbc') },
:marshal => true
end
def setup
drop_all_tables
create_tables
end
def test_nonmarshalling_backwards_compatibility
pet = NonmarshallingPet.create!(
:name => 'Fido',
:encrypted_nickname => 'E4lJTxFG/EfkfPg5MpnriQ==',
:encrypted_nickname_iv => 'z4Q8deE4h7f6S8NNZcbPNg==',
:encrypted_nickname_salt => 'adcd833001a873db',
:encrypted_birthdate => '6uKEAiFVdJw+N5El+U6Gow==',
:encrypted_birthdate_iv => 'zxtc1XPssL4s2HwA69nORQ==',
:encrypted_birthdate_salt => '4f879270045eaad7'
)
assert_equal 'Fido', pet.name
assert_equal 'Fido the Dog', pet.nickname
assert_equal '2011-07-09', pet.birthdate
end
def test_marshalling_backwards_compatibility
pet = MarshallingPet.create!(
:name => 'Fido',
:encrypted_nickname => 'EsQScJYkPw80vVGvKWkE37Px99HHpXPFjoEPTNa4rbs=',
:encrypted_nickname_iv => 'fNq1OZcGvty4KfcvGTcFSw==',
:encrypted_nickname_salt => '733b459b7d34c217',
:encrypted_birthdate => '+VUlKQGfNWkOgCwI4hv+3qlGIwh9h6cJ/ranJlaxvU+xxQdL3H3cOzTcI2rkYkdR',
:encrypted_birthdate_iv => 'Ka+zF/SwEYZKwVa24lvFfA==',
:encrypted_birthdate_salt => 'd5e892d5bbd81566'
)
assert_equal 'Fido', pet.name
assert_equal 'Mummy\'s little helper', pet.nickname
assert_equal Date.new(2011, 7, 9), pet.birthdate
end
private
def create_tables
ActiveRecord::Schema.define(:version => 1) do
create_table :nonmarshalling_pets do |t|
t.string :name
t.string :encrypted_nickname
t.string :encrypted_nickname_iv
t.string :encrypted_nickname_salt
t.string :encrypted_birthdate
t.string :encrypted_birthdate_iv
t.string :encrypted_birthdate_salt
end
create_table :marshalling_pets do |t|
t.string :name
t.string :encrypted_nickname
t.string :encrypted_nickname_iv
t.string :encrypted_nickname_salt
t.string :encrypted_birthdate
t.string :encrypted_birthdate_iv
t.string :encrypted_birthdate_salt
end
end
end
end
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
ruby-attr-encrypted-3.1.0/test/data_mapper_test.rb 0000664 0000000 0000000 00000002744 13274570231 0022272 0 ustar 00root root 0000000 0000000 require_relative 'test_helper'
DataMapper.setup(:default, 'sqlite3::memory:')
class Client
include DataMapper::Resource
property :id, Serial
property :encrypted_email, String
property :encrypted_email_iv, String
property :encrypted_email_salt, String
property :encrypted_credentials, Text
property :encrypted_credentials_iv, Text
property :encrypted_credentials_salt, Text
self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
attr_encrypted :email, :key => SECRET_KEY
attr_encrypted :credentials, :key => SECRET_KEY, :marshal => true
def initialize(attrs = {})
super attrs
self.credentials ||= { :username => 'example', :password => 'test' }
end
end
DataMapper.auto_migrate!
class DataMapperTest < Minitest::Test
def setup
Client.all.each(&:destroy)
end
def test_should_encrypt_email
@client = Client.new :email => 'test@example.com'
assert @client.save
refute_nil @client.encrypted_email
refute_equal @client.email, @client.encrypted_email
assert_equal @client.email, Client.first.email
end
def test_should_marshal_and_encrypt_credentials
@client = Client.new
assert @client.save
refute_nil @client.encrypted_credentials
refute_equal @client.credentials, @client.encrypted_credentials
assert_equal @client.credentials, Client.first.credentials
assert Client.first.credentials.is_a?(Hash)
end
def test_should_encode_by_default
assert Client.attr_encrypted_options[:encode]
end
end
ruby-attr-encrypted-3.1.0/test/legacy_active_record_test.rb 0000664 0000000 0000000 00000010062 13274570231 0024142 0 ustar 00root root 0000000 0000000 # -*- encoding: utf-8 -*-
require_relative 'test_helper'
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
def create_people_table
ActiveRecord::Schema.define(:version => 1) do
create_table :legacy_people do |t|
t.string :encrypted_email
t.string :password
t.string :encrypted_credentials
t.string :salt
end
end
end
# The table needs to exist before defining the class
create_people_table
ActiveRecord::MissingAttributeError = ActiveModel::MissingAttributeError unless defined?(ActiveRecord::MissingAttributeError)
class LegacyPerson < ActiveRecord::Base
self.attr_encrypted_options[:insecure_mode] = true
self.attr_encrypted_options[:algorithm] = 'aes-256-cbc'
self.attr_encrypted_options[:mode] = :single_iv_and_salt
attr_encrypted :email, :key => 'a secret key'
attr_encrypted :credentials, :key => Proc.new { |user| Encryptor.encrypt(:value => user.salt, :key => 'some private key', insecure_mode: true, algorithm: 'aes-256-cbc') }, :marshal => true
ActiveSupport::Deprecation.silenced = true
def after_initialize; end
ActiveSupport::Deprecation.silenced = false
after_initialize :initialize_salt_and_credentials
protected
def initialize_salt_and_credentials
self.salt ||= Digest::SHA256.hexdigest((Time.now.to_i * rand(5)).to_s)
self.credentials ||= { :username => 'example', :password => 'test' }
rescue ActiveRecord::MissingAttributeError
end
end
class LegacyPersonWithValidation < LegacyPerson
validates_presence_of :email
validates_uniqueness_of :encrypted_email
end
class LegacyActiveRecordTest < Minitest::Test
def setup
drop_all_tables
create_people_table
end
def test_should_decrypt_with_correct_encoding
if defined?(Encoding)
@person = LegacyPerson.create :email => 'test@example.com'
assert_equal 'UTF-8', LegacyPerson.first.email.encoding.name
end
end
def test_should_encrypt_email
@person = LegacyPerson.create :email => 'test@example.com'
refute_nil @person.encrypted_email
refute_equal @person.email, @person.encrypted_email
assert_equal @person.email, LegacyPerson.first.email
end
def test_should_marshal_and_encrypt_credentials
@person = LegacyPerson.create
refute_nil @person.encrypted_credentials
refute_equal @person.credentials, @person.encrypted_credentials
assert_equal @person.credentials, LegacyPerson.first.credentials
end
def test_should_find_by_email
@person = LegacyPerson.create(:email => 'test@example.com')
assert_equal @person, LegacyPerson.find_by_email('test@example.com')
end
def test_should_find_by_email_and_password
LegacyPerson.create(:email => 'test@example.com', :password => 'invalid')
@person = LegacyPerson.create(:email => 'test@example.com', :password => 'test')
assert_equal @person, LegacyPerson.find_by_email_and_password('test@example.com', 'test')
end
def test_should_scope_by_email
@person = LegacyPerson.create(:email => 'test@example.com')
assert_equal @person, LegacyPerson.scoped_by_email('test@example.com').first rescue NoMethodError
end
def test_should_scope_by_email_and_password
LegacyPerson.create(:email => 'test@example.com', :password => 'invalid')
@person = LegacyPerson.create(:email => 'test@example.com', :password => 'test')
assert_equal @person, LegacyPerson.scoped_by_email_and_password('test@example.com', 'test').first rescue NoMethodError
end
def test_should_encode_by_default
assert LegacyPerson.attr_encrypted_options[:encode]
end
def test_should_validate_presence_of_email
@person = LegacyPersonWithValidation.new
assert !@person.valid?
assert !@person.errors[:email].empty? || @person.errors.on(:email)
end
def test_should_validate_uniqueness_of_email
@person = LegacyPersonWithValidation.new :email => 'test@example.com'
assert @person.save
@person2 = LegacyPersonWithValidation.new :email => @person.email
assert !@person2.valid?
assert !@person2.errors[:encrypted_email].empty? || @person2.errors.on(:encrypted_email)
end
end
ruby-attr-encrypted-3.1.0/test/legacy_attr_encrypted_test.rb 0000664 0000000 0000000 00000025745 13274570231 0024376 0 ustar 00root root 0000000 0000000 # -*- encoding: utf-8 -*-
require_relative 'test_helper'
class LegacySillyEncryptor
def self.silly_encrypt(options)
(options[:value] + options[:some_arg]).reverse
end
def self.silly_decrypt(options)
options[:value].reverse.gsub(/#{options[:some_arg]}$/, '')
end
end
class LegacyUser
extend AttrEncrypted
self.attr_encrypted_options[:key] = Proc.new { |user| user.class.to_s } # default key
self.attr_encrypted_options[:insecure_mode] = true
self.attr_encrypted_options[:algorithm] = 'aes-256-cbc'
self.attr_encrypted_options[:mode] = :single_iv_and_salt
attr_encrypted :email, :without_encoding, :key => 'secret key'
attr_encrypted :password, :prefix => 'crypted_', :suffix => '_test'
attr_encrypted :ssn, :key => :salt, :attribute => 'ssn_encrypted'
attr_encrypted :credit_card, :encryptor => LegacySillyEncryptor, :encrypt_method => :silly_encrypt, :decrypt_method => :silly_decrypt, :some_arg => 'test'
attr_encrypted :with_encoding, :key => 'secret key', :encode => true
attr_encrypted :with_custom_encoding, :key => 'secret key', :encode => 'm'
attr_encrypted :with_marshaling, :key => 'secret key', :marshal => true
attr_encrypted :with_true_if, :key => 'secret key', :if => true
attr_encrypted :with_false_if, :key => 'secret key', :if => false
attr_encrypted :with_true_unless, :key => 'secret key', :unless => true
attr_encrypted :with_false_unless, :key => 'secret key', :unless => false
attr_encrypted :with_if_changed, :key => 'secret key', :if => :should_encrypt
attr_encryptor :aliased, :key => 'secret_key'
attr_accessor :salt
attr_accessor :should_encrypt
def initialize
self.salt = Time.now.to_i.to_s
self.should_encrypt = true
end
end
class LegacyAdmin < LegacyUser
attr_encrypted :testing
end
class LegacySomeOtherClass
extend AttrEncrypted
def self.call(object)
object.class
end
end
class LegacyAttrEncryptedTest < Minitest::Test
def test_should_store_email_in_encrypted_attributes
assert LegacyUser.encrypted_attributes.include?(:email)
end
def test_should_not_store_salt_in_encrypted_attributes
assert !LegacyUser.encrypted_attributes.include?(:salt)
end
def test_attr_encrypted_should_return_true_for_email
assert LegacyUser.attr_encrypted?('email')
end
def test_attr_encrypted_should_not_use_the_same_attribute_name_for_two_attributes_in_the_same_line
refute_equal LegacyUser.encrypted_attributes[:email][:attribute], LegacyUser.encrypted_attributes[:without_encoding][:attribute]
end
def test_attr_encrypted_should_return_false_for_salt
assert !LegacyUser.attr_encrypted?('salt')
end
def test_should_generate_an_encrypted_attribute
assert LegacyUser.new.respond_to?(:encrypted_email)
end
def test_should_generate_an_encrypted_attribute_with_a_prefix_and_suffix
assert LegacyUser.new.respond_to?(:crypted_password_test)
end
def test_should_generate_an_encrypted_attribute_with_the_attribute_option
assert LegacyUser.new.respond_to?(:ssn_encrypted)
end
def test_should_not_encrypt_nil_value
assert_nil LegacyUser.encrypt_email(nil)
end
def test_should_not_encrypt_empty_string
assert_equal '', LegacyUser.encrypt_email('')
end
def test_should_encrypt_email
refute_nil LegacyUser.encrypt_email('test@example.com')
refute_equal 'test@example.com', LegacyUser.encrypt_email('test@example.com')
end
def test_should_encrypt_email_when_modifying_the_attr_writer
@user = LegacyUser.new
assert_nil @user.encrypted_email
@user.email = 'test@example.com'
refute_nil @user.encrypted_email
assert_equal LegacyUser.encrypt_email('test@example.com'), @user.encrypted_email
end
def test_should_not_decrypt_nil_value
assert_nil LegacyUser.decrypt_email(nil)
end
def test_should_not_decrypt_empty_string
assert_equal '', LegacyUser.decrypt_email('')
end
def test_should_decrypt_email
encrypted_email = LegacyUser.encrypt_email('test@example.com')
refute_equal 'test@test.com', encrypted_email
assert_equal 'test@example.com', LegacyUser.decrypt_email(encrypted_email)
end
def test_should_decrypt_email_when_reading
@user = LegacyUser.new
assert_nil @user.email
@user.encrypted_email = LegacyUser.encrypt_email('test@example.com')
assert_equal 'test@example.com', @user.email
end
def test_should_encrypt_with_encoding
assert_equal LegacyUser.encrypt_with_encoding('test'), [LegacyUser.encrypt_without_encoding('test')].pack('m')
end
def test_should_decrypt_with_encoding
encrypted = LegacyUser.encrypt_with_encoding('test')
assert_equal 'test', LegacyUser.decrypt_with_encoding(encrypted)
assert_equal LegacyUser.decrypt_with_encoding(encrypted), LegacyUser.decrypt_without_encoding(encrypted.unpack('m').first)
end
def test_should_decrypt_utf8_with_encoding
encrypted = LegacyUser.encrypt_with_encoding("test\xC2\xA0utf-8\xC2\xA0text")
assert_equal "test\xC2\xA0utf-8\xC2\xA0text", LegacyUser.decrypt_with_encoding(encrypted)
assert_equal LegacyUser.decrypt_with_encoding(encrypted), LegacyUser.decrypt_without_encoding(encrypted.unpack('m').first)
end
def test_should_encrypt_with_custom_encoding
assert_equal LegacyUser.encrypt_with_custom_encoding('test'), [LegacyUser.encrypt_without_encoding('test')].pack('m')
end
def test_should_decrypt_with_custom_encoding
encrypted = LegacyUser.encrypt_with_custom_encoding('test')
assert_equal 'test', LegacyUser.decrypt_with_custom_encoding(encrypted)
assert_equal LegacyUser.decrypt_with_custom_encoding(encrypted), LegacyUser.decrypt_without_encoding(encrypted.unpack('m').first)
end
def test_should_encrypt_with_marshaling
@user = LegacyUser.new
@user.with_marshaling = [1, 2, 3]
refute_nil @user.encrypted_with_marshaling
assert_equal LegacyUser.encrypt_with_marshaling([1, 2, 3]), @user.encrypted_with_marshaling
end
def test_should_decrypt_with_marshaling
encrypted = LegacyUser.encrypt_with_marshaling([1, 2, 3])
@user = LegacyUser.new
assert_nil @user.with_marshaling
@user.encrypted_with_marshaling = encrypted
assert_equal [1, 2, 3], @user.with_marshaling
end
def test_should_use_custom_encryptor_and_crypt_method_names_and_arguments
assert_equal LegacySillyEncryptor.silly_encrypt(:value => 'testing', :some_arg => 'test'), LegacyUser.encrypt_credit_card('testing')
end
def test_should_evaluate_a_key_passed_as_a_symbol
@user = LegacyUser.new
assert_nil @user.ssn_encrypted
@user.ssn = 'testing'
refute_nil @user.ssn_encrypted
assert_equal Encryptor.encrypt(:value => 'testing', :key => @user.salt, insecure_mode: true, algorithm: 'aes-256-cbc'), @user.ssn_encrypted
end
def test_should_evaluate_a_key_passed_as_a_proc
@user = LegacyUser.new
assert_nil @user.crypted_password_test
@user.password = 'testing'
refute_nil @user.crypted_password_test
assert_equal Encryptor.encrypt(:value => 'testing', :key => 'LegacyUser', insecure_mode: true, algorithm: 'aes-256-cbc'), @user.crypted_password_test
end
def test_should_use_options_found_in_the_attr_encrypted_options_attribute
@user = LegacyUser.new
assert_nil @user.crypted_password_test
@user.password = 'testing'
refute_nil @user.crypted_password_test
assert_equal Encryptor.encrypt(:value => 'testing', :key => 'LegacyUser', insecure_mode: true, algorithm: 'aes-256-cbc'), @user.crypted_password_test
end
def test_should_inherit_encrypted_attributes
assert_equal [LegacyUser.encrypted_attributes.keys, :testing].flatten.collect { |key| key.to_s }.sort, LegacyAdmin.encrypted_attributes.keys.collect { |key| key.to_s }.sort
end
def test_should_inherit_attr_encrypted_options
assert !LegacyUser.attr_encrypted_options.empty?
assert_equal LegacyUser.attr_encrypted_options, LegacyAdmin.attr_encrypted_options
end
def test_should_not_inherit_unrelated_attributes
assert LegacySomeOtherClass.attr_encrypted_options.empty?
assert LegacySomeOtherClass.encrypted_attributes.empty?
end
def test_should_evaluate_a_symbol_option
assert_equal LegacySomeOtherClass, LegacySomeOtherClass.new.send(:evaluate_attr_encrypted_option, :class)
end
def test_should_evaluate_a_proc_option
assert_equal LegacySomeOtherClass, LegacySomeOtherClass.new.send(:evaluate_attr_encrypted_option, proc { |object| object.class })
end
def test_should_evaluate_a_lambda_option
assert_equal LegacySomeOtherClass, LegacySomeOtherClass.new.send(:evaluate_attr_encrypted_option, lambda { |object| object.class })
end
def test_should_evaluate_a_method_option
assert_equal LegacySomeOtherClass, LegacySomeOtherClass.new.send(:evaluate_attr_encrypted_option, LegacySomeOtherClass.method(:call))
end
def test_should_return_a_string_option
class_string = 'LegacySomeOtherClass'
assert_equal class_string, LegacySomeOtherClass.new.send(:evaluate_attr_encrypted_option, class_string)
end
def test_should_encrypt_with_true_if
@user = LegacyUser.new
assert_nil @user.encrypted_with_true_if
@user.with_true_if = 'testing'
refute_nil @user.encrypted_with_true_if
assert_equal Encryptor.encrypt(:value => 'testing', :key => 'secret key', insecure_mode: true, algorithm: 'aes-256-cbc'), @user.encrypted_with_true_if
end
def test_should_not_encrypt_with_false_if
@user = LegacyUser.new
assert_nil @user.encrypted_with_false_if
@user.with_false_if = 'testing'
refute_nil @user.encrypted_with_false_if
assert_equal 'testing', @user.encrypted_with_false_if
end
def test_should_encrypt_with_false_unless
@user = LegacyUser.new
assert_nil @user.encrypted_with_false_unless
@user.with_false_unless = 'testing'
refute_nil @user.encrypted_with_false_unless
assert_equal Encryptor.encrypt(:value => 'testing', :key => 'secret key', insecure_mode: true, algorithm: 'aes-256-cbc'), @user.encrypted_with_false_unless
end
def test_should_not_encrypt_with_true_unless
@user = LegacyUser.new
assert_nil @user.encrypted_with_true_unless
@user.with_true_unless = 'testing'
refute_nil @user.encrypted_with_true_unless
assert_equal 'testing', @user.encrypted_with_true_unless
end
def test_should_work_with_aliased_attr_encryptor
assert LegacyUser.encrypted_attributes.include?(:aliased)
end
def test_should_always_reset_options
@user = LegacyUser.new
@user.with_if_changed = "encrypt_stuff"
@user = LegacyUser.new
@user.should_encrypt = false
@user.with_if_changed = "not_encrypted_stuff"
assert_equal "not_encrypted_stuff", @user.with_if_changed
assert_equal "not_encrypted_stuff", @user.encrypted_with_if_changed
end
def test_should_cast_values_as_strings_before_encrypting
string_encrypted_email = LegacyUser.encrypt_email('3')
assert_equal string_encrypted_email, LegacyUser.encrypt_email(3)
assert_equal '3', LegacyUser.decrypt_email(string_encrypted_email)
end
def test_should_create_query_accessor
@user = LegacyUser.new
assert !@user.email?
@user.email = ''
assert !@user.email?
@user.email = 'test@example.com'
assert @user.email?
end
end
ruby-attr-encrypted-3.1.0/test/legacy_compatibility_test.rb 0000664 0000000 0000000 00000007151 13274570231 0024207 0 ustar 00root root 0000000 0000000 # -*- encoding: utf-8 -*-
require_relative 'test_helper'
# Test to ensure that existing representations in database do not break on
# migrating to new versions of this gem. This ensures that future versions of
# this gem will retain backwards compatibility with data generated by earlier
# versions.
class LegacyCompatibilityTest < Minitest::Test
class LegacyNonmarshallingPet < ActiveRecord::Base
PET_NICKNAME_SALT = Digest::SHA256.hexdigest('my-really-really-secret-pet-nickname-salt')
PET_NICKNAME_KEY = 'my-really-really-secret-pet-nickname-key'
PET_BIRTHDATE_SALT = Digest::SHA256.hexdigest('my-really-really-secret-pet-birthdate-salt')
PET_BIRTHDATE_KEY = 'my-really-really-secret-pet-birthdate-key'
self.attr_encrypted_options[:insecure_mode] = true
self.attr_encrypted_options[:algorithm] = 'aes-256-cbc'
self.attr_encrypted_options[:mode] = :single_iv_and_salt
attr_encrypted :nickname,
:key => proc { Encryptor.encrypt(:value => PET_NICKNAME_SALT, :key => PET_NICKNAME_KEY, insecure_mode: true, algorithm: 'aes-256-cbc') }
attr_encrypted :birthdate,
:key => proc { Encryptor.encrypt(:value => PET_BIRTHDATE_SALT, :key => PET_BIRTHDATE_KEY, insecure_mode: true, algorithm: 'aes-256-cbc') }
end
class LegacyMarshallingPet < ActiveRecord::Base
PET_NICKNAME_SALT = Digest::SHA256.hexdigest('my-really-really-secret-pet-nickname-salt')
PET_NICKNAME_KEY = 'my-really-really-secret-pet-nickname-key'
PET_BIRTHDATE_SALT = Digest::SHA256.hexdigest('my-really-really-secret-pet-birthdate-salt')
PET_BIRTHDATE_KEY = 'my-really-really-secret-pet-birthdate-key'
self.attr_encrypted_options[:insecure_mode] = true
self.attr_encrypted_options[:algorithm] = 'aes-256-cbc'
self.attr_encrypted_options[:mode] = :single_iv_and_salt
attr_encrypted :nickname,
:key => proc { Encryptor.encrypt(:value => PET_NICKNAME_SALT, :key => PET_NICKNAME_KEY, insecure_mode: true, algorithm: 'aes-256-cbc') },
:marshal => true
attr_encrypted :birthdate,
:key => proc { Encryptor.encrypt(:value => PET_BIRTHDATE_SALT, :key => PET_BIRTHDATE_KEY, insecure_mode: true, algorithm: 'aes-256-cbc') },
:marshal => true
end
def setup
drop_all_tables
create_tables
end
def test_nonmarshalling_backwards_compatibility
pet = LegacyNonmarshallingPet.create!(
:name => 'Fido',
:encrypted_nickname => 'uSUB6KGzta87yxesyVc3DA==',
:encrypted_birthdate => 'I3d691B2PtFXLx15kO067g=='
)
assert_equal 'Fido', pet.name
assert_equal 'Fido the Dog', pet.nickname
assert_equal '2011-07-09', pet.birthdate
end
def test_marshalling_backwards_compatibility
pet = LegacyMarshallingPet.create!(
:name => 'Fido',
:encrypted_nickname => '7RwoT64in4H+fGVBPYtRcN0K4RtriIy1EP4nDojUa8g=',
:encrypted_birthdate => 'bSp9sJhXQSp2QlNZHiujtcK4lRVBE8HQhn1y7moQ63bGJR20hvRSZ73ePAmm+wc5'
)
assert_equal 'Fido', pet.name
assert_equal 'Mummy\'s little helper', pet.nickname
assert_equal Date.new(2011, 7, 9), pet.birthdate
end
private
def create_tables
ActiveRecord::Schema.define(:version => 1) do
create_table :legacy_nonmarshalling_pets do |t|
t.string :name
t.string :encrypted_nickname
t.string :encrypted_birthdate
t.string :salt
end
create_table :legacy_marshalling_pets do |t|
t.string :name
t.string :encrypted_nickname
t.string :encrypted_birthdate
t.string :salt
end
end
end
end
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
ruby-attr-encrypted-3.1.0/test/legacy_data_mapper_test.rb 0000664 0000000 0000000 00000003363 13274570231 0023614 0 ustar 00root root 0000000 0000000 require_relative 'test_helper'
DataMapper.setup(:default, 'sqlite3::memory:')
class LegacyClient
include DataMapper::Resource
self.attr_encrypted_options[:insecure_mode] = true
self.attr_encrypted_options[:algorithm] = 'aes-256-cbc'
self.attr_encrypted_options[:mode] = :single_iv_and_salt
property :id, Serial
property :encrypted_email, String
property :encrypted_credentials, Text
property :salt, String
attr_encrypted :email, :key => 'a secret key', mode: :single_iv_and_salt
attr_encrypted :credentials, :key => Proc.new { |client| Encryptor.encrypt(:value => client.salt, :key => 'some private key', insecure_mode: true, algorithm: 'aes-256-cbc') }, :marshal => true, mode: :single_iv_and_salt
def initialize(attrs = {})
super attrs
self.salt ||= Digest::SHA1.hexdigest((Time.now.to_i * rand(5)).to_s)
self.credentials ||= { :username => 'example', :password => 'test' }
end
end
DataMapper.auto_migrate!
class LegacyDataMapperTest < Minitest::Test
def setup
LegacyClient.all.each(&:destroy)
end
def test_should_encrypt_email
@client = LegacyClient.new :email => 'test@example.com'
assert @client.save
refute_nil @client.encrypted_email
refute_equal @client.email, @client.encrypted_email
assert_equal @client.email, LegacyClient.first.email
end
def test_should_marshal_and_encrypt_credentials
@client = LegacyClient.new
assert @client.save
refute_nil @client.encrypted_credentials
refute_equal @client.credentials, @client.encrypted_credentials
assert_equal @client.credentials, LegacyClient.first.credentials
assert LegacyClient.first.credentials.is_a?(Hash)
end
def test_should_encode_by_default
assert LegacyClient.attr_encrypted_options[:encode]
end
end
ruby-attr-encrypted-3.1.0/test/legacy_sequel_test.rb 0000664 0000000 0000000 00000003302 13274570231 0022626 0 ustar 00root root 0000000 0000000 require_relative 'test_helper'
DB.create_table :legacy_humans do
primary_key :id
column :encrypted_email, :string
column :password, :string
column :encrypted_credentials, :string
column :salt, :string
end
class LegacyHuman < Sequel::Model(:legacy_humans)
self.attr_encrypted_options[:insecure_mode] = true
self.attr_encrypted_options[:algorithm] = 'aes-256-cbc'
self.attr_encrypted_options[:mode] = :single_iv_and_salt
attr_encrypted :email, :key => 'a secret key', mode: :single_iv_and_salt
attr_encrypted :credentials, :key => Proc.new { |human| Encryptor.encrypt(:value => human.salt, :key => 'some private key', insecure_mode: true, algorithm: 'aes-256-cbc') }, :marshal => true, mode: :single_iv_and_salt
def after_initialize(attrs = {})
self.salt ||= Digest::SHA1.hexdigest((Time.now.to_i * rand(5)).to_s)
self.credentials ||= { :username => 'example', :password => 'test' }
end
end
class LegacySequelTest < Minitest::Test
def setup
LegacyHuman.all.each(&:destroy)
end
def test_should_encrypt_email
@human = LegacyHuman.new :email => 'test@example.com'
assert @human.save
refute_nil @human.encrypted_email
refute_equal @human.email, @human.encrypted_email
assert_equal @human.email, LegacyHuman.first.email
end
def test_should_marshal_and_encrypt_credentials
@human = LegacyHuman.new
assert @human.save
refute_nil @human.encrypted_credentials
refute_equal @human.credentials, @human.encrypted_credentials
assert_equal @human.credentials, LegacyHuman.first.credentials
assert LegacyHuman.first.credentials.is_a?(Hash)
end
def test_should_encode_by_default
assert LegacyHuman.attr_encrypted_options[:encode]
end
end
ruby-attr-encrypted-3.1.0/test/run.sh 0000775 0000000 0000000 00000000427 13274570231 0017570 0 ustar 00root root 0000000 0000000 #!/usr/bin/env sh -e
for RUBY in 1.9.3 2.0.0 2.1 2.2
do
for RAILS in 2.3.8 3.0.0 3.1.0 3.2.0 4.0.0 4.1.0 4.2.0
do
if [[ $RUBY -gt 1.9.3 && $RAILS -lt 4.0.0 ]]; then
continue
fi
RBENV_VERSION=$RUBY ACTIVERECORD=$RAILS bundle && bundle exec rake
done
done
ruby-attr-encrypted-3.1.0/test/sequel_test.rb 0000664 0000000 0000000 00000002745 13274570231 0021314 0 ustar 00root root 0000000 0000000 require_relative 'test_helper'
DB.create_table :humans do
primary_key :id
column :encrypted_email, :string
column :encrypted_email_salt, String
column :encrypted_email_iv, :string
column :password, :string
column :encrypted_credentials, :string
column :encrypted_credentials_iv, :string
column :encrypted_credentials_salt, String
end
class Human < Sequel::Model(:humans)
self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
attr_encrypted :email, :key => SECRET_KEY
attr_encrypted :credentials, :key => SECRET_KEY, :marshal => true
def after_initialize(attrs = {})
self.credentials ||= { :username => 'example', :password => 'test' }
end
end
class SequelTest < Minitest::Test
def setup
Human.all.each(&:destroy)
end
def test_should_encrypt_email
@human = Human.new :email => 'test@example.com'
assert @human.save
refute_nil @human.encrypted_email
refute_equal @human.email, @human.encrypted_email
assert_equal @human.email, Human.first.email
end
def test_should_marshal_and_encrypt_credentials
@human = Human.new :credentials => { :username => 'example', :password => 'test' }
assert @human.save
refute_nil @human.encrypted_credentials
refute_equal @human.credentials, @human.encrypted_credentials
assert_equal @human.credentials, Human.first.credentials
assert Human.first.credentials.is_a?(Hash)
end
def test_should_encode_by_default
assert Human.attr_encrypted_options[:encode]
end
end
ruby-attr-encrypted-3.1.0/test/test_helper.rb 0000664 0000000 0000000 00000003007 13274570231 0021265 0 ustar 00root root 0000000 0000000 require 'simplecov'
require 'simplecov-rcov'
require "codeclimate-test-reporter"
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
[
SimpleCov::Formatter::HTMLFormatter,
SimpleCov::Formatter::RcovFormatter,
CodeClimate::TestReporter::Formatter
]
)
SimpleCov.start do
add_filter 'test'
end
CodeClimate::TestReporter.start
require 'minitest/autorun'
# Rails 4.0.x pins to an old minitest
unless defined?(MiniTest::Test)
MiniTest::Test = MiniTest::Unit::TestCase
end
require 'active_record'
require 'data_mapper'
require 'digest/sha2'
require 'sequel'
ActiveSupport::Deprecation.behavior = :raise
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
$:.unshift(File.dirname(__FILE__))
require 'attr_encrypted'
DB = if defined?(RUBY_ENGINE) && RUBY_ENGINE.to_sym == :jruby
Sequel.jdbc('jdbc:sqlite::memory:')
else
Sequel.sqlite
end
# The :after_initialize hook was removed in Sequel 4.0
# and had been deprecated for a while before that:
# http://sequel.rubyforge.org/rdoc-plugins/classes/Sequel/Plugins/AfterInitialize.html
# This plugin re-enables it.
Sequel::Model.plugin :after_initialize
SECRET_KEY = SecureRandom.random_bytes(32)
def base64_encoding_regex
/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$/
end
def drop_all_tables
connection = ActiveRecord::Base.connection
tables = (ActiveRecord::VERSION::MAJOR >= 5 ? connection.data_sources : connection.tables)
tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
end