sidekiq-encryptor 0.0.1 → 0.1.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: fbf6216993306970a0046e788f4b033a8565db1e
4
- data.tar.gz: db90b1555b00991e53aca580cb923872b5c5e99c
3
+ metadata.gz: cc401443775158ce8894d1474e2e68d9f72e0e3d
4
+ data.tar.gz: ad006a7f9a3b5969b524ac67e13b8e5b3657290f
5
5
  SHA512:
6
- metadata.gz: 5dc4b9b01365ad77d9c833bedc238c64c7a5a4a7e6e25d4ba0660f573f891f8082bedc1991b8da88ac1a92ae95f37211d7c8e94f3486c439ffb091ead339853c
7
- data.tar.gz: f7dc81e8b26fcd47c5d244a6fca04f23ae0ae34155140acbdf1a3e12a0a050af09142ce5d3beaad5670757a9791bbae6bcdedc85421d283409a9aa1ce500adff
6
+ metadata.gz: 6d8c9e5439e8223dc1c03ab4b7565520cad982f2707f8971dff14de3c0e748efa0874571c611a2381f1c66c124697ec499c85b432467837a6ffdc24c2a46397f
7
+ data.tar.gz: 1516b7eb16752e7526f4298ba6250cd104033ad777e1cb9edf7b92e8c8fa9d897319a5c8163269c3a3dbb2a665cb020dd166310e59c11986e6666a016d64f81c
data/.pryrc CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  require 'pathname'
6
6
  $LOAD_PATH.unshift(Pathname.getwd.join('lib').to_s)
7
- require 'sidekiq/throttler'
7
+ require 'sidekiq/encryptor'
8
8
 
9
9
  def reload!
10
10
  Dir["#{Dir.pwd}/lib/**/*.rb"].each { |f| load f }
data/.travis.yml CHANGED
@@ -8,9 +8,7 @@ branches:
8
8
  only:
9
9
  - master
10
10
  notifications:
11
- email:
12
- recipients:
13
- - gabe@ga.be
11
+ email: false
14
12
  matrix:
15
13
  allow_failures:
16
14
  - rvm: jruby-19mode
data/CHANGELOG.md ADDED
@@ -0,0 +1,28 @@
1
+ # sidekiq-encryptor changelog
2
+
3
+ ## 0.1.0
4
+
5
+ Backwards incompatible changes:
6
+
7
+ * Protocol version change from 0 to 1. Jobs encrypted by previous
8
+ versions cannot be decrypted by this version.
9
+
10
+ Changes:
11
+
12
+ * Switch to Fernet 2.0
13
+ * Use a distinct protocol version number
14
+ * Support binary, hexadecimal, and base64 keys. Note that many ASCII
15
+ keys will be detected as base64. Base64 assumes RFC 2045.
16
+ * Enforce 256 bit key length
17
+ * Recommend 32 byte keys in README
18
+ * Allow an optional `adapter` to be passed in. The adapter must follow
19
+ the following standards:
20
+ * Define a `encrypt` and `decrypt` class methods that accept two
21
+ arguments: `String key, String data`. `key` will always have length
22
+ 32, `data` will be of arbitrary length.
23
+ * `encrypt` must return a String.
24
+ * `decrypt` must return a String or nil if the decryption fails.
25
+
26
+ ## 0.0.1
27
+
28
+ Initial release
data/README.md CHANGED
@@ -48,9 +48,18 @@ Sidekiq.configure_client do |config|
48
48
  end
49
49
  ```
50
50
 
51
+ You should also set `SIDEKIQ_ENCRYPTION_KEY` to something sufficiently
52
+ random. The `openssl` tool is a good choice for this:
53
+
54
+ ```sh
55
+ echo SIDEKIQ_ENCRYPTION_KEY=$(openssl rand -base64 32) >>.env
56
+ heroku config:set SIDEKIQ_ENCRYPTION_KEY=$(openssl rand -base64 32)
57
+ ```
58
+
51
59
  ## Contributing
52
60
 
53
- Pull requests gladly accepted. Please write tests for any code changes.
61
+ Pull requests gladly accepted. Please write tests for any code changes,
62
+ though I'll admit my test coverage is completely awful right now.
54
63
 
55
64
  ## License
56
65
 
@@ -3,11 +3,13 @@ module Sidekiq
3
3
 
4
4
  module Version
5
5
  MAJOR = 0
6
- MINOR = 0
7
- PATCH = 1
6
+ MINOR = 1
7
+ PATCH = 0
8
8
  SUFFIX = ""
9
9
  end
10
10
 
11
+ PROTOCOL_VERSION = 1
12
+
11
13
  VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::PATCH}#{Version::SUFFIX}"
12
14
 
13
15
  end
@@ -1,7 +1,11 @@
1
+ # stdlib
2
+ require 'base64'
3
+
4
+ # gems
1
5
  require 'sidekiq'
2
- require 'active_support/message_encryptor'
3
- require 'active_support/message_verifier'
6
+ require 'fernet'
4
7
 
8
+ # local files
5
9
  require 'sidekiq/encryptor/version'
6
10
 
7
11
  module Sidekiq
@@ -11,35 +15,125 @@ module Sidekiq
11
15
  DecryptionError = Class.new(Error)
12
16
  VersionChangeError = Class.new(DecryptionError)
13
17
 
18
+ module FernetAdapter
19
+ def self.encrypt(key, data)
20
+ Fernet.generate(Base64.urlsafe_encode64(key), data)
21
+ end
22
+
23
+ def self.decrypt(key, data)
24
+ verifier = Fernet::Verifier.new(
25
+ token: data,
26
+ secret: Base64.urlsafe_encode64(key),
27
+ enforce_ttl: false)
28
+ verifier.valid? ? verifier.message : nil
29
+ rescue OpenSSL::Cipher::CipherError
30
+ nil
31
+ end
32
+ end
33
+
14
34
  class Base
15
35
  def initialize(options = {})
16
- @key = options[:key]
17
- @encryptor = ActiveSupport::MessageEncryptor.new(@key) if @key
36
+ @key = validate_key(compact_key(options[:key]))
37
+ @adapter = options[:adapter] || FernetAdapter
38
+ end
39
+
40
+ def inspect
41
+ "#<#{self.class.inspect}> @key=[masked] @adapter=#{@adapter.inspect}>"
42
+ end
43
+ alias to_s inspect
44
+
45
+ def enabled?
46
+ !@key.nil?
47
+ end
48
+
49
+ private
50
+
51
+ def compact_key(key)
52
+ flat_key = key.to_s.delete("\r\n")
53
+ case flat_key
54
+ # empty
55
+ when ""
56
+ nil
57
+ # hexadecimal
58
+ when /^[\da-f]+$/i
59
+ [flat_key].pack('H*')
60
+ # base64
61
+ when /^[A-Za-z\d\+\/=]+$/
62
+ key.unpack('m*').first
63
+ # assume binary otherwise
64
+ else
65
+ key
66
+ end
18
67
  end
68
+
69
+ def validate_key(key)
70
+ if key.nil?
71
+ $stderr.puts '[sidekiq-encryptor] ERROR: no key provided, encryption disabled'
72
+ elsif key.length < 32
73
+ $stderr.puts '[sidekiq-encryptor] ERROR: key length less than 256 bits, encryption disabled'
74
+ else
75
+ key[0,32]
76
+ end
77
+ end
78
+
19
79
  end
20
80
 
21
81
  class Client < Base
22
82
  def call(worker, msg, queue)
23
- return yield unless @key
24
- msg['args'] = ['Sidekiq::Encryptor', Sidekiq::Encryptor::Version::MAJOR, @encryptor.encrypt_and_sign(Sidekiq.dump_json(msg['args']))]
83
+ return yield unless enabled?
84
+ msg['args'] = payload(msg['args'])
25
85
  yield
26
86
  end
87
+
88
+ private
89
+
90
+ def payload(input)
91
+ [
92
+ 'Sidekiq::Encryptor',
93
+ Sidekiq::Encryptor::PROTOCOL_VERSION,
94
+ encrypt(input)
95
+ ]
96
+ end
97
+
98
+ def encrypt(input)
99
+ @adapter.encrypt(@key, Sidekiq.dump_json(input))
100
+ end
101
+
27
102
  end
28
103
 
29
104
  class Server < Base
30
105
  def call(worker, msg, queue)
31
- return yield unless @key
32
- if msg['args'][0] == 'Sidekiq::Encryptor'
33
- if msg['args'][1] != Sidekiq::Encryptor::Version::MAJOR
106
+ return yield unless enabled?
107
+ msg['args'] = validate_and_decrypt(msg['args'])
108
+ yield
109
+ end
110
+
111
+ private
112
+
113
+ def validate_and_decrypt(payload)
114
+ if encrypted?(payload)
115
+ if version_changed?(payload)
34
116
  raise VersionChangeError, 'incompatible change detected'
35
117
  else
36
- msg['args'] = Sidekiq.load_json(@encryptor.decrypt_and_verify(msg['args'][2]))
118
+ data = decrypt(payload) or
119
+ raise DecryptionError, 'key not identical or data was corrupted'
120
+ Sidekiq.load_json(data)
37
121
  end
38
122
  end
39
- yield
40
- rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveSupport::MessageEncryptor::InvalidMessage
41
- raise DecryptionError, 'key not identical or data was corrupted'
42
123
  end
124
+
125
+ def encrypted?(input)
126
+ input.is_a?(Array) && input.size == 3 && input.first == 'Sidekiq::Encryptor'
127
+ end
128
+
129
+ def version_changed?(input)
130
+ input[1] != Sidekiq::Encryptor::PROTOCOL_VERSION
131
+ end
132
+
133
+ def decrypt(input)
134
+ @adapter.decrypt(@key, input[2])
135
+ end
136
+
43
137
  end
44
138
 
45
139
  end # Encryptor
@@ -11,13 +11,14 @@ Gem::Specification.new do |gem|
11
11
  gem.description = %q{Sidekiq middleware that encrypts your job data into and out of Redis.}
12
12
  gem.summary = %q{Sidekiq::Encryptor is a middleware for Sidekiq that keeps your information safe by using 2-way encryption when storing and retrieving jobs from Redis.}
13
13
  gem.homepage = 'https://github.com/wuputah/sidekiq-encryptor'
14
+ gem.license = 'MIT'
14
15
 
15
16
  gem.files = `git ls-files`.split($/)
16
17
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
19
  gem.require_paths = ['lib']
19
20
 
20
- gem.add_dependency 'activesupport'
21
+ gem.add_dependency 'fernet', '>= 2.0rc1', '< 3.0'
21
22
  gem.add_dependency 'sidekiq', '>= 2.5', '< 3.0'
22
23
 
23
24
  gem.add_development_dependency 'rake'
@@ -1,31 +1,59 @@
1
1
  require 'spec_helper'
2
+ require 'securerandom'
2
3
 
3
4
  [Sidekiq::Encryptor::Client, Sidekiq::Encryptor::Server].each do |klass|
4
5
 
5
6
  describe klass do
6
7
 
7
- subject(:middleware) do
8
- described_class.new
9
- end
8
+ raw_key = SecureRandom.random_bytes(32)
10
9
 
11
- let(:worker) do
12
- RegularWorker.new
13
- end
10
+ {
11
+ 'base64' => [raw_key].pack('m*'),
12
+ 'hex' => raw_key.unpack('H*').first,
13
+ 'binary' => raw_key
14
+ }.each_pair do |key_type, key|
14
15
 
15
- let(:message) do
16
- {
17
- args: 'Clint Eastwood'
18
- }
19
- end
16
+ describe "with #{key_type} key" do
20
17
 
21
- let(:queue) do
22
- 'default'
23
- end
18
+ subject(:middleware) do
19
+ described_class.new(key: key)
20
+ end
21
+
22
+ let(:worker) do
23
+ RegularWorker.new
24
+ end
25
+
26
+ let(:data) do
27
+ ['Clint Eastwood']
28
+ end
29
+
30
+ let(:args) do
31
+ {
32
+ Sidekiq::Encryptor::Client => data,
33
+ Sidekiq::Encryptor::Server => [
34
+ 'Sidekiq::Encryptor',
35
+ 1,
36
+ Fernet.generate(Base64.urlsafe_encode64(raw_key), JSON.dump(data))
37
+ ]
38
+ }
39
+ end
40
+
41
+ let(:message) do
42
+ { 'args' => args[klass] }
43
+ end
44
+
45
+ let(:queue) do
46
+ 'default'
47
+ end
48
+
49
+ it { should be_enabled }
24
50
 
25
- describe '#call' do
51
+ describe '#call' do
26
52
 
27
- it 'yields' do
28
- expect { |b| middleware.call(worker, message, queue, &b) }.to yield_with_no_args
53
+ it 'yields' do
54
+ expect { |b| middleware.call(worker, message, queue, &b) }.to yield_with_no_args
55
+ end
56
+ end
29
57
  end
30
58
  end
31
59
  end
data/spec/spec_helper.rb CHANGED
@@ -19,7 +19,7 @@ require 'sidekiq/encryptor'
19
19
  # Autoload every worker for the test suite that sits in spec/app/workers
20
20
  Dir[File.join(WORKERS, '*.rb')].sort.each do |file|
21
21
  name = File.basename(file, '.rb')
22
- autoload name.camelize.to_sym, name
22
+ require name
23
23
  end
24
24
 
25
25
  RSpec.configure do |config|
metadata CHANGED
@@ -1,29 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-encryptor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Dance
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-20 00:00:00.000000000 Z
11
+ date: 2013-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: activesupport
14
+ name: fernet
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - '>='
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 2.0rc1
20
+ - - <
21
+ - !ruby/object:Gem::Version
22
+ version: '3.0'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - '>='
25
28
  - !ruby/object:Gem::Version
26
- version: '0'
29
+ version: 2.0rc1
30
+ - - <
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: sidekiq
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -265,6 +271,7 @@ files:
265
271
  - .pryrc
266
272
  - .travis.yml
267
273
  - .yardopts
274
+ - CHANGELOG.md
268
275
  - Gemfile
269
276
  - Guardfile
270
277
  - LICENSE.txt
@@ -280,7 +287,8 @@ files:
280
287
  - spec/spec_helper.rb
281
288
  - spec/support/sidekiq.rb
282
289
  homepage: https://github.com/wuputah/sidekiq-encryptor
283
- licenses: []
290
+ licenses:
291
+ - MIT
284
292
  metadata: {}
285
293
  post_install_message:
286
294
  rdoc_options: []