coder_decorator 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f29535d9096afdc19576192bc1f3c8417ab764f0
4
+ data.tar.gz: bcb43020e75dcc1d579ab24108b897d8b1f205bf
5
+ SHA512:
6
+ metadata.gz: 734f600c21637fb446c8188933581d405e955900b4bb75423ab0a9e882e48e846e04ea4b40daac85d73d76169e8ef4b02849ec446128b3a5330762db87a21df1
7
+ data.tar.gz: 54f858ea180c61ef4639926b08dcc86062f8c4734c1e55adb2fb0c558da7eb6bf1482e54fb95ed36c17a28d16fa5c2ea57d99f392ebcd343915de131493adb99
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+ module CoderDecorator; end # :nodoc:
3
+ Dir[File.join(__dir__, 'coder_decorator', '**', '*.rb')].each { |path| require path }
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ require 'coder_decorator/coders/coder'
3
+ module CoderDecorator
4
+ module Coders
5
+ class Base64 < Coder # :nodoc:
6
+ def initialize(coder = nil, strict: true)
7
+ super(coder)
8
+ @template_str = strict ? 'm0' : 'm'
9
+ end
10
+
11
+ def encode(obj)
12
+ [coder.encode(obj)].pack(@template_str)
13
+ end
14
+
15
+ def decode(str)
16
+ coder.decode(str.unpack(@template_str).first)
17
+ rescue ::ArgumentError
18
+ raise InvalidEncoding
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ require 'coder_decorator/coders/coder'
3
+ require 'coder_decorator/coders/base64'
4
+ require 'openssl'
5
+
6
+ module CoderDecorator
7
+ module Coders
8
+ # Encrypt the data into this format:
9
+ #
10
+ # "#{encrypted_data}--#{initial_vector}"
11
+ #
12
+ class Cipher < Coder
13
+ def initialize(coder = nil, secret:, cipher: 'AES-256-CBC')
14
+ super(coder)
15
+ @secret = secret
16
+ @cipher = ::OpenSSL::Cipher.new(cipher)
17
+ @base64 = Coders::Base64.new
18
+ end
19
+
20
+ def encode(obj)
21
+ @cipher.encrypt
22
+ @cipher.key = @secret
23
+ iv = @cipher.random_iv
24
+ encrypted_data = @cipher.update(coder.encode(obj)) << @cipher.final
25
+ blob = "#{@base64.encode(encrypted_data)}--#{@base64.encode(iv)}"
26
+ @base64.encode(blob)
27
+ end
28
+
29
+ def decode(str)
30
+ encrypted_data, iv = @base64.decode(str).split('--').map! { |v| @base64.decode(v) }
31
+ @cipher.decrypt
32
+ @cipher.key = @secret
33
+ @cipher.iv = iv
34
+ begin
35
+ coder.decode(@cipher.update(encrypted_data) << @cipher.final)
36
+ rescue ::OpenSSL::Cipher::CipherError
37
+ raise InvalidEncoding
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+ require 'coder_decorator/errors'
3
+ module CoderDecorator
4
+ module Coders
5
+ # The abstract class of coder, must implement #encode and #decode.
6
+ # It's designed with decorator pattern, which makes it more flexible,
7
+ # and can be wrapped infinitely using Ruby instantiation.
8
+ #
9
+ # To implement a custom coder decorator, inherit from Coders::Coder, and use
10
+ # +coder.encode+, +coder.decode+ to get results from base coder:
11
+ #
12
+ # class Reverse < Coders::Coder
13
+ # def encode(str); coder.encode(str).reverse; end
14
+ # def decode(str); coder.decode(str.reverse); end
15
+ # end
16
+ # coder = Reverse.new(Coder::Base64.new)
17
+ #
18
+ # If you want to customize options, be sure to call super:
19
+ #
20
+ # class MyCoder < Coders::Coder
21
+ # def initialize(gueset_coder, options = {})
22
+ # super(guest_coder)
23
+ # @options = options
24
+ # end
25
+ # end
26
+ #
27
+ class Coder < BasicObject
28
+ # Can optionally pass a base coder which is going to be decorated.
29
+ def initialize(coder = nil)
30
+ @_coder = coder
31
+ end
32
+
33
+ def coder
34
+ @_coder ||= Null.new
35
+ end
36
+
37
+ def encode(_obj)
38
+ ::Kernel.raise ::NotImplementedError
39
+ end
40
+
41
+ # It decodes +_obj+, returning decoded data,
42
+ # or +nil+ if it can't decode.
43
+ def decode(_obj)
44
+ ::Kernel.raise ::NotImplementedError
45
+ end
46
+
47
+ def raise(*args)
48
+ ::Kernel.raise(*args)
49
+ end
50
+ end
51
+
52
+ class Null < BasicObject # :nodoc:
53
+ def encode(obj)
54
+ obj
55
+ end
56
+
57
+ def decode(obj)
58
+ obj
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+ require 'coder_decorator/coders/coder'
3
+ require 'openssl'
4
+
5
+ module CoderDecorator
6
+ module Coders
7
+ # Actuall, it doesn't encode, instead, it appends a hex HMAC to the input data
8
+ # in this format:
9
+ #
10
+ # "#{data}--#{hmac}"
11
+ #
12
+ class HMAC < Coder
13
+ REGEXP = /\A(.*)--(.*)\z/
14
+
15
+ def initialize(coder = nil, secret:, digest: 'SHA1')
16
+ super(coder)
17
+ @secret = secret
18
+ @digest = ::OpenSSL::Digest.new(digest)
19
+ end
20
+
21
+ def encode(str)
22
+ data = coder.encode(str)
23
+ hmac = generate_hmac(data)
24
+ "#{data}--#{hmac}"
25
+ end
26
+
27
+ def decode(str)
28
+ data, hmac = REGEXP.match(str)&.captures
29
+ raise InvalidEncoding unless data && hmac && secure_compare(hmac, generate_hmac(data))
30
+ coder.decode(data)
31
+ end
32
+
33
+ private
34
+
35
+ def generate_hmac(str)
36
+ ::OpenSSL::HMAC.hexdigest(@digest.new, @secret, str)
37
+ end
38
+
39
+ def secure_compare(a, b)
40
+ return false unless a.bytesize == b.bytesize
41
+ l = a.unpack('C*')
42
+ r = 0
43
+ i = -1
44
+ b.each_byte { |v| r |= v ^ l[i += 1] }
45
+ r.zero?
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ require 'coder_decorator/coders/coder'
3
+ module CoderDecorator
4
+ module Coders
5
+ class Identity < Coder # :nodoc:
6
+ def encode(obj)
7
+ coder.encode(obj)
8
+ end
9
+
10
+ def decode(obj)
11
+ coder.decode(obj)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ require 'coder_decorator/coders/coder'
3
+ require 'json'
4
+ module CoderDecorator
5
+ module Coders
6
+ class JSON < Coder # :nodoc:
7
+ def encode(obj)
8
+ ::JSON.dump(coder.encode(obj))
9
+ end
10
+
11
+ def decode(str)
12
+ coder.decode(::JSON.parse(str))
13
+ rescue ::JSON::ParserError
14
+ raise InvalidEncoding
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ require 'coder_decorator/coders/coder'
3
+ module CoderDecorator
4
+ module Coders
5
+ class Marshal < Coder # :nodoc:
6
+ def encode(obj)
7
+ ::Marshal.dump(coder.encode(obj))
8
+ end
9
+
10
+ def decode(str)
11
+ coder.decode(::Marshal.load(str))
12
+ rescue ::TypeError
13
+ raise InvalidEncoding
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ require 'coder_decorator/coders/coder'
3
+ module CoderDecorator
4
+ module Coders
5
+ # When there's exception raised, it rescues and return +nil+.
6
+ class Rescue < Coder # :nodoc:
7
+ def encode(obj)
8
+ coder.encode(obj)
9
+ end
10
+
11
+ def decode(obj)
12
+ coder.decode(obj)
13
+ rescue
14
+ nil
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ require 'coder_decorator/coders/coder'
3
+ require 'zlib'
4
+ module CoderDecorator
5
+ module Coders
6
+ class Zip < Coder # :nodoc:
7
+ def encode(str)
8
+ ::Zlib::Deflate.deflate(coder.encode(str))
9
+ end
10
+
11
+ def decode(str)
12
+ coder.decode(::Zlib::Inflate.inflate(str))
13
+ rescue ::Zlib::DataError
14
+ raise InvalidEncoding
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ module CoderDecorator
3
+ class Error < RuntimeError; end
4
+ class InvalidEncoding < Error; end
5
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: coder_decorator
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jian Weihang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-12-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: An encoding/decoding library with decorator pattern.
56
+ email: tonytonyjan@gmail.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - lib/coder_decorator.rb
62
+ - lib/coder_decorator/coders/base64.rb
63
+ - lib/coder_decorator/coders/cipher.rb
64
+ - lib/coder_decorator/coders/coder.rb
65
+ - lib/coder_decorator/coders/hmac.rb
66
+ - lib/coder_decorator/coders/identity.rb
67
+ - lib/coder_decorator/coders/json.rb
68
+ - lib/coder_decorator/coders/marshal.rb
69
+ - lib/coder_decorator/coders/rescue.rb
70
+ - lib/coder_decorator/coders/zip.rb
71
+ - lib/coder_decorator/errors.rb
72
+ homepage:
73
+ licenses:
74
+ - MIT
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 2.6.8
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: An encoding/decoding library with decorator pattern.
96
+ test_files: []