attr_secure 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +7 -3
- data/attr_secure.gemspec +1 -1
- data/lib/attr_secure/adapters/active_record.rb +3 -1
- data/lib/attr_secure/adapters/sequel.rb +3 -1
- data/lib/attr_secure/secure.rb +15 -4
- data/lib/attr_secure/version.rb +1 -1
- data/spec/adapters/active_record_spec.rb +5 -0
- data/spec/adapters/sequel_spec.rb +5 -0
- data/spec/attr_secure_spec.rb +2 -2
- data/spec/secure_spec.rb +37 -13
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 823b15116fee5d14b97ae231f81b48e7cb1b304e
|
4
|
+
data.tar.gz: 49b2aefa2a76a6eb4e6cd466320cba879ac88d20
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19037e5da221dc532f23f733380f1bf0d10f18fb1b18d1877aa648d32b42262ae42622a9491173be0630979bec41d601a13e9861b778d38b933bcd69f3f7213c
|
7
|
+
data.tar.gz: fd893bd6f3858c51e74c74057fbec4a88c99d588b849d1d42228a2996ad516669e29991f3ba6179bc7eef5cca3eb5800e1b3b083b80955552027444d7b32ab87
|
data/README.md
CHANGED
@@ -42,22 +42,26 @@ To make an model attribute secure, first you need a secure key:
|
|
42
42
|
|
43
43
|
dd if=/dev/urandom bs=32 count=1 2>/dev/null | openssl base64
|
44
44
|
|
45
|
-
There's a number of ways of setting a key for a given attribute. The easiest is to default the key via the environment.
|
45
|
+
There's a number of ways of setting a key for a given attribute. The easiest is to default the key via the environment.
|
46
46
|
Setting the environment variable `ATTR_SECURE_SECRET` to a secret value will secure all attributes with the same key.
|
47
47
|
|
48
48
|
Alternatively, if you want to use different keys for different attributes you can do this too:
|
49
49
|
|
50
50
|
attr_secure :my_attribute, :secret => "EKq88AMFeRLqEx5knUcoJ4LOnrv52d7hfAFgEKMoDKzqNei4m7kbu"
|
51
|
-
|
51
|
+
|
52
52
|
If you would like your key dependent on something else, a lambda is OK too:
|
53
53
|
|
54
54
|
attr_secure :my_attribute, :secret => lambda {|record| record.user.secret }
|
55
|
-
|
55
|
+
|
56
56
|
Remember kids, it's not a good idea to hard-code secrets.
|
57
57
|
|
58
58
|
Note: You will want to set your table columns for encrypted values to :text or
|
59
59
|
similar. Encrypted values are long.
|
60
60
|
|
61
|
+
## Key rotation
|
62
|
+
|
63
|
+
You can pass a comma delimited list of keys as your secret. attr_secure will decrypt with each key in turn until it hits a verified value. Encryption always happens with the newest (leftmost) key.
|
64
|
+
|
61
65
|
## Contributing
|
62
66
|
|
63
67
|
1. Fork it
|
data/attr_secure.gemspec
CHANGED
@@ -3,7 +3,9 @@ module AttrSecure
|
|
3
3
|
module ActiveRecord
|
4
4
|
|
5
5
|
def self.valid?(object)
|
6
|
-
object.respond_to?(:<) &&
|
6
|
+
object.respond_to?(:<) && object < ::ActiveRecord::Base
|
7
|
+
rescue NameError
|
8
|
+
false
|
7
9
|
end
|
8
10
|
|
9
11
|
def self.write_attribute(object, attribute, value)
|
@@ -3,7 +3,9 @@ module AttrSecure
|
|
3
3
|
module Sequel
|
4
4
|
|
5
5
|
def self.valid?(object)
|
6
|
-
object.respond_to?(:<) &&
|
6
|
+
object.respond_to?(:<) && object < ::Sequel::Model
|
7
|
+
rescue NameError
|
8
|
+
false
|
7
9
|
end
|
8
10
|
|
9
11
|
def self.write_attribute(object, attribute, value)
|
data/lib/attr_secure/secure.rb
CHANGED
@@ -9,19 +9,30 @@ module AttrSecure
|
|
9
9
|
attr_reader :secret
|
10
10
|
|
11
11
|
def initialize(secret)
|
12
|
-
@secret = secret
|
12
|
+
@secret = secret.split(",")
|
13
|
+
end
|
14
|
+
|
15
|
+
def secret=(val)
|
16
|
+
@secret = secret.split(",")
|
13
17
|
end
|
14
18
|
|
15
19
|
def encrypt(value)
|
16
|
-
Fernet.generate(secret) do |generator|
|
20
|
+
Fernet.generate([secret].flatten.first) do |generator|
|
17
21
|
generator.data = { value: value }
|
18
22
|
end
|
19
23
|
end
|
20
24
|
|
21
25
|
def decrypt(value)
|
22
26
|
return nil if value.nil?
|
23
|
-
|
24
|
-
|
27
|
+
[secret].flatten.each do |_secret|
|
28
|
+
begin
|
29
|
+
verifier = Fernet.verifier(_secret, value)
|
30
|
+
return verifier.data['value'] if verifier.valid?
|
31
|
+
rescue
|
32
|
+
end
|
33
|
+
raise OpenSSL::Cipher::CipherError
|
34
|
+
end
|
25
35
|
end
|
36
|
+
|
26
37
|
end
|
27
38
|
end
|
data/lib/attr_secure/version.rb
CHANGED
@@ -16,6 +16,11 @@ describe AttrSecure::Adapters::ActiveRecord do
|
|
16
16
|
it "should not be valid" do
|
17
17
|
expect(described_class.valid?(String)).to be_false
|
18
18
|
end
|
19
|
+
|
20
|
+
it "should not be valid if activerecord is not loaded" do
|
21
|
+
hide_const('ActiveRecord')
|
22
|
+
expect(described_class.valid?(described)).to be_false
|
23
|
+
end
|
19
24
|
end
|
20
25
|
|
21
26
|
describe "write attribute" do
|
@@ -16,6 +16,11 @@ describe AttrSecure::Adapters::Sequel do
|
|
16
16
|
it "should not be valid" do
|
17
17
|
expect(described_class.valid?(String)).to be_false
|
18
18
|
end
|
19
|
+
|
20
|
+
it "should not be valid if sequel is not loaded" do
|
21
|
+
hide_const('Sequel')
|
22
|
+
expect(described_class.valid?(described)).to be_false
|
23
|
+
end
|
19
24
|
end
|
20
25
|
|
21
26
|
describe "write attribute" do
|
data/spec/attr_secure_spec.rb
CHANGED
@@ -6,8 +6,8 @@ describe AttrSecure do
|
|
6
6
|
let(:described) { Class.new }
|
7
7
|
let(:secure_mock) { double(AttrSecure::Secure) }
|
8
8
|
let(:secret_mock) { double(AttrSecure::Secret) }
|
9
|
-
let(:crypter) {
|
10
|
-
let(:adapter) {
|
9
|
+
let(:crypter) { double(:secure_crypter) }
|
10
|
+
let(:adapter) { double(:adapter) }
|
11
11
|
|
12
12
|
before do
|
13
13
|
described.extend(AttrSecure)
|
data/spec/secure_spec.rb
CHANGED
@@ -1,22 +1,46 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe AttrSecure::Secure do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
context "with a simple key" do
|
5
|
+
|
6
|
+
subject { described_class.new(secret) }
|
7
|
+
let(:secret) { 'fWSvpC6Eh1/FFE1TUgXpcEzMmmGc9IZSqoexzEslzKI=' }
|
8
|
+
|
9
|
+
describe '#encrypt' do
|
10
|
+
it "should encrypt a string" do
|
11
|
+
expect(subject.encrypt('encrypted')).to be_a(String)
|
12
|
+
expect(subject.encrypt('encrypted')).to_not be_empty
|
13
|
+
expect(subject.encrypt('encrypted')).to_not eq(subject.encrypt('encrypted'))
|
14
|
+
end
|
12
15
|
end
|
13
|
-
end
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
+
describe '#decrypt' do
|
18
|
+
let(:encrypted_value) { subject.encrypt('decrypted') }
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
+
it "should decrypt a string" do
|
21
|
+
expect(subject.decrypt(encrypted_value)).to eq('decrypted')
|
22
|
+
end
|
20
23
|
end
|
21
24
|
end
|
25
|
+
|
26
|
+
# context "with an array of keys" do
|
27
|
+
# subject { described_class.new(secret) }
|
28
|
+
# let(:secret) { 'fWSvpC6Eh1/FFE1TUgXpcEzMmmGc9IZSqoexzEslzKI=,d9ssNmUYn7UpMoSc0eM2glVUG2DPYwXveLTDU7j8pBY=' }
|
29
|
+
|
30
|
+
# describe '#encrypt' do
|
31
|
+
# it "should encrypt a string" do
|
32
|
+
# expect(subject.encrypt('encrypted')).to be_a(String)
|
33
|
+
# expect(subject.encrypt('encrypted')).to_not be_empty
|
34
|
+
# expect(subject.encrypt('encrypted')).to_not eq(subject.encrypt('encrypted'))
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
|
38
|
+
# describe '#decrypt' do
|
39
|
+
# let(:encrypted_value) { subject.encrypt('decrypted') }
|
40
|
+
|
41
|
+
# it "should decrypt a string" do
|
42
|
+
# expect(subject.decrypt(encrypted_value)).to eq('decrypted')
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
# end
|
22
46
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attr_secure
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Neil Middleton
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-12-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -84,16 +84,16 @@ dependencies:
|
|
84
84
|
name: fernet
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - '
|
87
|
+
- - '='
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '1.6'
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - '
|
94
|
+
- - '='
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
96
|
+
version: '1.6'
|
97
97
|
description: Securely stores activerecord model attributes
|
98
98
|
email:
|
99
99
|
- neil@neilmiddleton.com
|
@@ -144,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
144
144
|
version: '0'
|
145
145
|
requirements: []
|
146
146
|
rubyforge_project:
|
147
|
-
rubygems_version: 2.0.
|
147
|
+
rubygems_version: 2.0.7
|
148
148
|
signing_key:
|
149
149
|
specification_version: 4
|
150
150
|
summary: Securely stores activerecord model attributes
|