attr_secure 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a9f970443168e1a32a5bdb022ef5e74acedc5803
4
- data.tar.gz: a247cfc9a807256e6f8003346d28c08ffb4c4b5a
3
+ metadata.gz: 3e6810f8e6b2500a0fd71a8d2cd2755bbab0759f
4
+ data.tar.gz: b9d9017ec05194584f60af544a3564ba1f26cfc1
5
5
  SHA512:
6
- metadata.gz: 7b204d25fcbab4c785c7993d8fb65dade0369d73b10c27cbfbf39020a97020ac3809800148b2039bd586d268c9bac113ee26c755648763728dad23a6279eb438
7
- data.tar.gz: e9c3c0dbb2a835e38ea9e970e0d561ecb41964f8d418522de45469c38e51193c81b3e464b42c9c495fb29d2feb96df23cb76cd6cc693cce81cc46259d74e989f
6
+ metadata.gz: df04a6247b167ff02122d97de4498cf2c89d2b3277799382985bab5e5a97f920a7b4847fb5ff789513da857a56252b5137145f548affed24fa7afcf1516d011f
7
+ data.tar.gz: 05418d708166a33ee050a171cdbe422373f54c1130e4f17334c12d5d93dc3fba9291a4052151136617e99457d6cefb5ebc4faddde5ec0b10c92ac0d483b1e49b
data/README.md CHANGED
@@ -38,16 +38,22 @@ Or install it yourself as:
38
38
 
39
39
  ## Usage
40
40
 
41
- To make an model attribute secure, first create a key:
41
+ 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
- and add it to your environment as `ATTR_SECURE_SECRET`.
46
- Then mark an attribute as secure:
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
+ Setting the environment variable `ATTR_SECURE_SECRET` to a secret value will secure all attributes with the same key.
47
47
 
48
- attr_secure :my_attribute
48
+ Alternatively, if you want to use different keys for different attributes you can do this too:
49
49
 
50
- and read and write as normal (see above example)
50
+ attr_secure :my_attribute, :secret => "EKq88AMFeRLqEx5knUcoJ4LOnrv52d7hfAFgEKMoDKzqNei4m7kbu"
51
+
52
+ If you would like your key dependent on something else, a lambda is OK too:
53
+
54
+ attr_secure :my_attribute, :secret => lambda {|record| record.user.secret }
55
+
56
+ Remember kids, it's not a good idea to hard-code secrets.
51
57
 
52
58
  Note: You will want to set your table columns for encrypted values to :text or
53
59
  similar. Encrypted values are long.
data/lib/attr_secure.rb CHANGED
@@ -1,38 +1,53 @@
1
1
  require "attr_secure/version"
2
- require 'fernet'
2
+ require 'attr_secure/secure'
3
+ require 'attr_secure/secret'
3
4
 
4
5
  require 'attr_secure/adapters/ruby'
5
6
  require 'attr_secure/adapters/active_record'
6
7
  require 'attr_secure/adapters/sequel'
7
8
 
8
- Fernet::Configuration.run do |config|
9
- config.enforce_ttl = false
10
- end
11
-
12
9
  module AttrSecure
13
10
  #
14
11
  # All the available adapters.
15
12
  # The order in this list matters, as only the first valid adapter will be used
16
13
  #
17
14
  ADAPTERS = [
18
- AttrSecure::Adapters::Sequel,
19
15
  AttrSecure::Adapters::ActiveRecord,
16
+ AttrSecure::Adapters::Sequel,
20
17
  AttrSecure::Adapters::Ruby
21
18
  ]
22
19
 
23
- def attr_secure(attribute, encryption_class = Secure.new)
20
+ # Generates attr_accessors that encrypt and decrypt attributes transparently
21
+ def attr_secure(*attributes)
22
+ options = {
23
+ :encryption_class => Secure,
24
+ :secret_class => Secret,
25
+ :env => ENV
26
+ }.merge!(attributes.last.is_a?(Hash) ? attributes.pop : {})
27
+
28
+ attribute = attributes.first
29
+
24
30
  define_method("#{attribute}=") do |value|
25
- encrypted_value = encryption_class.encrypt(value.nil? ? nil : value)
26
- self.class.attr_secure_adapter.write_attribute self, attribute, encrypted_value
31
+ adapter = self.class.attr_secure_adapter
32
+ secret = options[:secret_class].new(options).call(self)
33
+ crypter = options[:encryption_class].new(secret)
34
+ value = crypter.encrypt(value)
35
+
36
+ adapter.write_attribute self, attribute, value
27
37
  end
28
38
 
29
39
  define_method("#{attribute}") do
30
- encrypted_value = self.class.attr_secure_adapter.read_attribute(self, attribute)
31
- encryption_class.decrypt encrypted_value
40
+ adapter = self.class.attr_secure_adapter
41
+ secret = options[:secret_class].new(options).call(self)
42
+ crypter = options[:encryption_class].new(secret)
43
+ value = adapter.read_attribute(self, attribute)
44
+
45
+ crypter.decrypt value
32
46
  end
33
47
  end
34
48
 
35
49
  def attr_secure_adapter
36
50
  ADAPTERS.find {|a| a.valid?(self) }
37
51
  end
52
+
38
53
  end
@@ -1,5 +1,3 @@
1
- require 'attr_secure/secure'
2
-
3
1
  module AttrSecure
4
2
  module Adapters
5
3
  module ActiveRecord
@@ -1,5 +1,3 @@
1
- require 'attr_secure/secure'
2
-
3
1
  module AttrSecure
4
2
  module Adapters
5
3
  module Ruby
@@ -1,5 +1,3 @@
1
- require 'attr_secure/secure'
2
-
3
1
  module AttrSecure
4
2
  module Adapters
5
3
  module Sequel
@@ -9,11 +7,11 @@ module AttrSecure
9
7
  end
10
8
 
11
9
  def self.write_attribute(object, attribute, value)
12
- object[attribute] = value
10
+ object[attribute.to_sym] = value
13
11
  end
14
12
 
15
13
  def self.read_attribute(object, attribute)
16
- object[attribute]
14
+ object[attribute.to_sym]
17
15
  end
18
16
  end
19
17
  end
@@ -0,0 +1,23 @@
1
+ module AttrSecure
2
+ class Secret
3
+
4
+ def initialize(options)
5
+ @secret, @env = options.values_at(:secret, :env)
6
+ end
7
+
8
+ def call(object=nil)
9
+ if secret.respond_to?(:call)
10
+ secret.call(object)
11
+ else
12
+ secret || env!('ATTR_SECURE_SECRET')
13
+ end
14
+ end
15
+
16
+ private
17
+ attr_reader :secret, :env
18
+
19
+ def env!(key)
20
+ env.fetch(key) { raise("Missing ENV(#{key})") }
21
+ end
22
+ end
23
+ end
@@ -1,30 +1,27 @@
1
+ require 'fernet'
2
+
3
+ Fernet::Configuration.run do |config|
4
+ config.enforce_ttl = false
5
+ end
6
+
1
7
  module AttrSecure
2
8
  class Secure
3
- attr_reader :env
9
+ attr_reader :secret
4
10
 
5
- def initialize(env=ENV)
6
- @env = env
11
+ def initialize(secret)
12
+ @secret = secret
7
13
  end
8
14
 
9
15
  def encrypt(value)
10
- Fernet.generate(attr_secure_secret) do |generator|
16
+ Fernet.generate(secret) do |generator|
11
17
  generator.data = { value: value }
12
18
  end
13
19
  end
14
20
 
15
21
  def decrypt(value)
16
22
  return nil if value.nil?
17
- verifier = Fernet.verifier(attr_secure_secret, value)
23
+ verifier = Fernet.verifier(secret, value)
18
24
  verifier.data['value'] if verifier.valid?
19
25
  end
20
-
21
- private
22
- def env!(key)
23
- env.fetch(key) { raise("Missing ENV(#{key})") }
24
- end
25
-
26
- def attr_secure_secret
27
- env!('ATTR_SECURE_SECRET')
28
- end
29
26
  end
30
27
  end
@@ -1,3 +1,3 @@
1
1
  module AttrSecure
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,30 +1,34 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe AttrSecure::Adapters::ActiveRecord do
4
- let(:described) { Class.new(ActiveRecord::Base) }
5
- subject { described.new }
6
- let(:secure_mock) { double(AttrSecure::Secure) }
4
+ let(:described) { Class.new(ActiveRecord::Base) }
5
+ subject { described.new }
7
6
 
8
7
  before do
9
8
  described.table_name = 'fake_database'
10
- described.extend(AttrSecure)
11
- described.attr_secure :title, secure_mock
12
9
  end
13
10
 
14
- it 'has active record as it\'s adapter' do
15
- expect(described.attr_secure_adapter).to eq(AttrSecure::Adapters::ActiveRecord)
11
+ describe "valid?" do
12
+ it "should be valid" do
13
+ expect(described_class.valid?(described)).to be_true
14
+ end
15
+
16
+ it "should not be valid" do
17
+ expect(described_class.valid?(String)).to be_false
18
+ end
16
19
  end
17
20
 
18
- it 'encrypts' do
19
- secure_mock.should_receive(:encrypt).with('hello').and_return('encrypted')
20
- subject.title = 'hello'
21
- expect(subject.attributes['title']).to eq('encrypted')
21
+ describe "write attribute" do
22
+ it "should write an attribute" do
23
+ described_class.write_attribute(subject, 'title', 'hello')
24
+ expect(subject.title).to eq('hello')
25
+ end
22
26
  end
23
27
 
24
- it 'decrypts' do
25
- secure_mock.should_receive(:encrypt).with('hello').and_return('encrypted')
26
- subject.title = 'hello'
27
- secure_mock.should_receive(:decrypt).with('encrypted').and_return('decrypted')
28
- expect(subject.title).to eq('decrypted')
28
+ describe "read attribute" do
29
+ it "should read an instance variable" do
30
+ subject.title = 'world'
31
+ expect(described_class.read_attribute(subject, 'title')).to eq('world')
32
+ end
29
33
  end
30
34
  end
@@ -1,29 +1,25 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe AttrSecure::Adapters::Ruby do
4
- let(:described) { Class.new }
5
- subject { described.new }
6
- let(:secure_mock) { double(AttrSecure::Secure) }
4
+ subject { Class.new }
7
5
 
8
- before do
9
- described.extend(AttrSecure)
10
- described.attr_secure :foo, secure_mock
6
+ describe "valid?" do
7
+ it "should be valid all the time" do
8
+ expect(described_class.valid?(String)).to be_true
9
+ end
11
10
  end
12
11
 
13
- it 'has ruby as it\'s adapter' do
14
- expect(described.attr_secure_adapter).to eq(AttrSecure::Adapters::Ruby)
12
+ describe "write attribute" do
13
+ it "should set the instance variable" do
14
+ described_class.write_attribute(subject, 'secure', 'hello')
15
+ expect(subject.instance_variable_get("@secure")).to eql('hello')
16
+ end
15
17
  end
16
18
 
17
- it 'encrypts' do
18
- secure_mock.should_receive(:encrypt).with('hello').and_return('encrypted')
19
- subject.foo = 'hello'
20
- expect(subject.instance_variable_get(:@foo)).to eq('encrypted')
21
- end
22
-
23
- it 'decrypts' do
24
- secure_mock.should_receive(:encrypt).with('hello').and_return('encrypted')
25
- subject.foo = 'hello'
26
- secure_mock.should_receive(:decrypt).with('encrypted').and_return('decrypted')
27
- expect(subject.foo).to eq('decrypted')
19
+ describe "read attribute" do
20
+ it "should read an instance variable" do
21
+ subject.instance_variable_set("@secure", 'world')
22
+ expect(described_class.read_attribute(subject, 'secure')).to eq('world')
23
+ end
28
24
  end
29
25
  end
@@ -1,30 +1,34 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe AttrSecure::Adapters::Sequel do
4
- let(:described) { Class.new(Sequel::Model) }
5
- subject { described.new }
6
- let(:secure_mock) { double(AttrSecure::Secure) }
4
+ let(:described) { Class.new(Sequel::Model) }
5
+ subject { described.new }
7
6
 
8
7
  before do
9
8
  described.set_dataset(:fake_database)
10
- described.extend(AttrSecure)
11
- described.attr_secure :title, secure_mock
12
9
  end
13
10
 
14
- it 'has sequel as it\'s adapter' do
15
- expect(described.attr_secure_adapter).to eq(AttrSecure::Adapters::Sequel)
11
+ describe "valid?" do
12
+ it "should be valid" do
13
+ expect(described_class.valid?(described)).to be_true
14
+ end
15
+
16
+ it "should not be valid" do
17
+ expect(described_class.valid?(String)).to be_false
18
+ end
16
19
  end
17
20
 
18
- it 'encrypts' do
19
- secure_mock.should_receive(:encrypt).with('hello').and_return('encrypted')
20
- subject.title = 'hello'
21
- expect(subject.values[:title]).to eq('encrypted')
21
+ describe "write attribute" do
22
+ it "should write an attribute" do
23
+ described_class.write_attribute(subject, 'title', 'hello')
24
+ expect(subject.title).to eq('hello')
25
+ end
22
26
  end
23
27
 
24
- it 'decrypts' do
25
- secure_mock.should_receive(:encrypt).with('hello').and_return('encrypted')
26
- subject.title = 'hello'
27
- secure_mock.should_receive(:decrypt).with('encrypted').and_return('decrypted')
28
- expect(subject.title).to eq('decrypted')
28
+ describe "read attribute" do
29
+ it "should read an instance variable" do
30
+ subject.title = 'world'
31
+ expect(described_class.read_attribute(subject, 'title')).to eq('world')
32
+ end
29
33
  end
30
34
  end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe AttrSecure do
4
+ describe "reading and writing attributes" do
5
+ subject { described.new }
6
+ let(:described) { Class.new }
7
+ let(:secure_mock) { double(AttrSecure::Secure) }
8
+ let(:secret_mock) { double(AttrSecure::Secret) }
9
+ let(:crypter) { mock(:secure_crypter) }
10
+ let(:adapter) { mock(:adapter) }
11
+
12
+ before do
13
+ described.extend(AttrSecure)
14
+ described.attr_secure :foo,
15
+ encryption_class: secure_mock,
16
+ secret_class: secret_mock
17
+
18
+ secret_mock.stub_chain(:new, :call).and_return('secret token')
19
+ secure_mock.should_receive(:new).with('secret token').and_return(crypter)
20
+ described.stub(:attr_secure_adapter).and_return(adapter)
21
+ end
22
+
23
+ describe "set the attribute" do
24
+ it "should set the attribute encrypted" do
25
+ crypter.should_receive(:encrypt).with('decrypted').and_return('encrypted')
26
+ adapter.should_receive(:write_attribute).with(subject, :foo, 'encrypted')
27
+ subject.foo = 'decrypted'
28
+ end
29
+ end
30
+
31
+ describe "read the attribute" do
32
+ it "should read an encrypted attribute" do
33
+ adapter.should_receive(:read_attribute).with(subject, :foo).and_return('encrypted')
34
+ crypter.should_receive(:decrypt).with('encrypted').and_return('decrypted')
35
+ expect(subject.foo).to eq('decrypted')
36
+ end
37
+ end
38
+ end
39
+
40
+ describe "retrieving the adapter" do
41
+ describe "ruby" do
42
+ subject { Class.new }
43
+
44
+ before do
45
+ subject.extend(AttrSecure)
46
+ end
47
+
48
+ it "should get the adapter" do
49
+ expect(subject.attr_secure_adapter).to eq(AttrSecure::Adapters::Ruby)
50
+ end
51
+ end
52
+
53
+ describe "active record" do
54
+ subject { Class.new(ActiveRecord::Base) }
55
+
56
+ before do
57
+ subject.extend(AttrSecure)
58
+ end
59
+
60
+ it "should get the adapter" do
61
+ expect(subject.attr_secure_adapter).to eq(AttrSecure::Adapters::ActiveRecord)
62
+ end
63
+ end
64
+
65
+ describe "ruby" do
66
+ subject { Class.new(Sequel::Model) }
67
+
68
+ before do
69
+ subject.extend(AttrSecure)
70
+ end
71
+
72
+ it "should get the adapter" do
73
+ expect(subject.attr_secure_adapter).to eq(AttrSecure::Adapters::Sequel)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe AttrSecure::Secret do
4
+ let(:env) { {'ATTR_SECURE_SECRET' => 'environment secret' } }
5
+ let(:options) { {secret: secret, env: env} }
6
+ subject { described_class.new(options) }
7
+
8
+ describe "with a nil secret" do
9
+ let(:secret) { nil }
10
+
11
+ it "should use the env variable" do
12
+ expect(subject.call).to eq('environment secret')
13
+ end
14
+ end
15
+
16
+ describe "with a string secret" do
17
+ let(:secret) { 'string secret' }
18
+
19
+ it "should use the string" do
20
+ expect(subject.call).to eq('string secret')
21
+ end
22
+ end
23
+
24
+ describe "with a lambda secret" do
25
+ let(:secret) { lambda {|o| o } }
26
+
27
+ it "should use the lambda" do
28
+ expect(subject.call('lambda secret')).to eq('lambda secret')
29
+ end
30
+ end
31
+ end
data/spec/secure_spec.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe AttrSecure::Secure do
4
- subject { described_class.new({'ATTR_SECURE_SECRET' => token}) }
5
- let(:token) { 'fWSvpC6Eh1/FFE1TUgXpcEzMmmGc9IZSqoexzEslzKI=' }
4
+ subject { described_class.new(secret) }
5
+ let(:secret) { 'fWSvpC6Eh1/FFE1TUgXpcEzMmmGc9IZSqoexzEslzKI=' }
6
6
 
7
7
  describe 'encrypt' do
8
8
  it "should encrypt a string" do
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.1.0
4
+ version: 0.2.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-06-04 00:00:00.000000000 Z
11
+ date: 2013-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -112,11 +112,14 @@ files:
112
112
  - lib/attr_secure/adapters/active_record.rb
113
113
  - lib/attr_secure/adapters/ruby.rb
114
114
  - lib/attr_secure/adapters/sequel.rb
115
+ - lib/attr_secure/secret.rb
115
116
  - lib/attr_secure/secure.rb
116
117
  - lib/attr_secure/version.rb
117
118
  - spec/adapters/active_record_spec.rb
118
119
  - spec/adapters/ruby_spec.rb
119
120
  - spec/adapters/sequel_spec.rb
121
+ - spec/attr_secure_spec.rb
122
+ - spec/secret_spec.rb
120
123
  - spec/secure_spec.rb
121
124
  - spec/spec_helper.rb
122
125
  - spec/support/load_active_record.rb
@@ -149,6 +152,8 @@ test_files:
149
152
  - spec/adapters/active_record_spec.rb
150
153
  - spec/adapters/ruby_spec.rb
151
154
  - spec/adapters/sequel_spec.rb
155
+ - spec/attr_secure_spec.rb
156
+ - spec/secret_spec.rb
152
157
  - spec/secure_spec.rb
153
158
  - spec/spec_helper.rb
154
159
  - spec/support/load_active_record.rb