decode_this 0.1.3 → 0.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 90dca39ac2ebd159e00e009f2701e0d07a9edb78
4
- data.tar.gz: fb4fa59ce7bf405fa56329e86494a45f391d68ca
3
+ metadata.gz: a402dd6e09333fe90e81f7389710c76dde358f21
4
+ data.tar.gz: 8e87ea52fe43b899f8bf348313bb0f041d01442c
5
5
  SHA512:
6
- metadata.gz: cb2f2c1264b8aa3b4a51d9b2a2359ad24357d161b783417ee09afbc116efdfabf3cb2fd2f3a836fe795d210daa1acf63cd261c3cd84a83d3cfd1c1d0504b751e
7
- data.tar.gz: 11c9b17f0537329792a860f4b7328aed97dce3f57786b1eef03ec5ba0af8a99e5c92bbce817900ae0a92fa2ada9d85879b68565fcc9737d2ec96388c9195513a
6
+ metadata.gz: '005134392d22ddb00b5f206a367d9c6f3ea1fd433ae0c38a9a493e28352e427f58f40f5dacea49da9ec562a123d02250cb8215620c7395875cd790443dc76fd9'
7
+ data.tar.gz: 79dd5599461321a3c3b59c9afecd9ac5394201dacc9489def6ad6503e002e19b9183da228cf0ee35cc04fc486193d0f2f9f478aff58542b37012d8558c23960c
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ coverage
data/.travis.yml CHANGED
@@ -1,5 +1,19 @@
1
- sudo: false
1
+ env:
2
+ global:
3
+ - CC_TEST_REPORTER_ID=caf8d649c1f1517bab58f7cbaa98600465446c8d210093e20a5e819b0066eca4
4
+ notifications:
5
+ - false
6
+
2
7
  language: ruby
8
+ sudo: required
3
9
  rvm:
4
10
  - 2.2.5
5
- before_install: gem install bundler
11
+
12
+ before_script:
13
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
14
+ - chmod +x ./cc-test-reporter
15
+ - ./cc-test-reporter before-build
16
+ script:
17
+ - bundle exec rspec
18
+ after_script:
19
+ - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
data/Gemfile CHANGED
@@ -2,3 +2,12 @@
2
2
  source 'https://rubygems.org'
3
3
 
4
4
  gemspec
5
+
6
+ group :test do
7
+ gem 'simplecov', require: false
8
+ gem 'simplecov-console', require: false
9
+ end
10
+
11
+ group :development, :test do
12
+ gem 'pry-byebug'
13
+ end
data/Readme.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # DecodeThis
2
2
 
3
3
  [![Build Status](https://travis-ci.org/myxaluch/decode_this.svg?branch=master)](https://travis-ci.org/myxaluch/decode_this)
4
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/182d13aea55106ba87a4/test_coverage)](https://codeclimate.com/github/myxaluch/decode_this)
4
5
 
5
6
  Simple decoder JWT token by given key
6
7
 
@@ -26,7 +27,8 @@ Configuration file scheme:
26
27
  ```
27
28
  test:
28
29
  algorithm: 'RS256'
29
- path: 'paht/to/keys'
30
+ authorization_scheme: 'Bearer'
31
+ key_path: 'paht/to/keys'
30
32
  ```
31
33
 
32
34
  ```ruby
@@ -34,9 +36,16 @@ payload = {
34
36
  'field1' => 'foo',
35
37
  'field2' => 'bar'
36
38
  }
37
- jwt_token = JWT.encode(payload, private_token, true, algorithm: algorithm)
39
+ jwt_token = JWT.encode(payload, private_key, true, algorithm: algorithm)
38
40
  ...
39
- decoded_token = DecodeThis.decode(jwt_token, config_file: '/path/to/config.yml', env: :my_env, logger: logger)
41
+ header_value = request.env['HTTP_AUTHENTICATION'] // "Bearer fgjsgkjsfslfjg.."
42
+ decoded_token = DecodeThis::Decoder.call(
43
+ header_value,
44
+ config_file: '/path/to/config.yml',
45
+ env: :my_env,
46
+ logger: logger
47
+ )
48
+
40
49
  token['field1'] = 'foo'
41
50
  token['field2'] = 'bar'
42
51
  ```
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+ require 'decode_this/safe_decoding'
3
+ require 'openssl'
4
+ require 'jwt'
5
+ require 'huyettings'
6
+
7
+ module DecodeThis
8
+ class Decoder
9
+ attr_reader :header_value, :config_file, :env, :logger
10
+
11
+ def initialize(header_value, config_file:, env:, logger: nil)
12
+ @header_value = header_value
13
+ @config_file = config_file
14
+ @env = env
15
+ @logger = logger
16
+ end
17
+
18
+ def call
19
+ safe_decode { JWT.decode(token, public_key, true, algorithm: algorithm).first }
20
+ end
21
+
22
+ private
23
+
24
+ def config
25
+ @config ||= Huyettings.new(config_file, env)
26
+ end
27
+
28
+ def algorithm
29
+ config.algorithm
30
+ end
31
+
32
+ def token
33
+ return unless header_value
34
+
35
+ token = header_value.match(/^#{config.authorization_scheme} (.+)/)
36
+ token[1] if token
37
+ end
38
+
39
+ def public_key
40
+ private_key.public_key
41
+ end
42
+
43
+ def safe_decode(&block)
44
+ DecodeThis::SafeDecoding.call(logger, &block)
45
+ end
46
+
47
+ def private_key
48
+ OpenSSL::PKey::RSA.new(pem)
49
+ end
50
+
51
+ def pem
52
+ keys_absolute_path = File.expand_path(config.key_path)
53
+
54
+ if !File.readable?(keys_absolute_path)
55
+ raise DecodeThis::KeyFileNotFoundError.new("Cannot found file in #{config.key_path}")
56
+ end
57
+
58
+ File.read(config.key_path)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ module DecodeThis
3
+ KeyFileNotFoundError = Class.new(RuntimeError)
4
+ DecodeError = Class.new(RuntimeError)
5
+
6
+ class SafeDecoding
7
+ def self.call(logger = nil, &block)
8
+ block.call
9
+
10
+ rescue JWT::ExpiredSignature => err
11
+ handle_and_log_error(
12
+ DecodeThis::DecodeError,
13
+ "Expired token #{err.class} - #{err.message}",
14
+ logger
15
+ )
16
+ rescue JWT::VerificationError => err
17
+ handle_and_log_error(
18
+ DecodeThis::DecodeError,
19
+ "Can't verify token #{err.class} - #{err.message}",
20
+ logger
21
+ )
22
+ rescue JWT::DecodeError => err
23
+ handle_and_log_error(
24
+ DecodeThis::DecodeError,
25
+ "Can't decode token '#{jwt_token}' #{err.class} - #{err.message}",
26
+ logger
27
+ )
28
+ rescue DecodeThis::KeyFileNotFoundError => err
29
+ handle_and_log_error(
30
+ DecodeThis::KeyFileNotFoundError,
31
+ err.message,
32
+ logger
33
+ )
34
+ end
35
+
36
+ def self.handle_and_log_error(raising_error, message, logger = nil)
37
+ logger.warn(message) if logger
38
+
39
+ raise raising_error.new(message)
40
+ end
41
+ end
42
+ end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
- class DecodeThis
3
- VERSION = '0.1.3'
2
+ module DecodeThis
3
+ VERSION = '0.1.4'
4
4
  end
data/lib/decode_this.rb CHANGED
@@ -1,58 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
  require 'decode_this/version'
3
- require 'huyettings'
4
- require 'openssl'
5
- require 'jwt'
6
-
7
- class DecodeThis
8
- ConfigFileNotFoundError = Class.new(RuntimeError)
9
- DecodeError = Class.new(RuntimeError)
10
-
11
- attr_reader :token, :config_file, :env, :logger
12
-
13
- def initialize(token, config_file:, env:, logger: nil)
14
- @token = token
15
- @config_file = config_file
16
- @env = env
17
- @logger = logger
18
- end
19
-
20
- def decode
21
- JWT.decode(token, public_key, true, algorithm: algorithm).first
22
-
23
- rescue JWT::ExpiredSignature => err
24
- logger.warn("Expired JWT token #{err.class} - #{err.message}") if logger
25
- raise DecodeError
26
- rescue JWT::VerificationError => err
27
- logger.warn("Can't verify JWT token #{err.class} - #{err.message}") if logger
28
- raise DecodeError
29
- rescue JWT::DecodeError => err
30
- logger.warn("Can't decode JWT token '#{jwt_token}' #{err.class} - #{err.message}") if logger
31
- raise DecodeError
32
- end
33
-
34
- private
35
-
36
- def jwt_config
37
- @jwt_config ||= Huyettings.new(config_file, env)
38
- end
39
-
40
- def algorithm
41
- jwt_config.algorithm
42
- end
43
-
44
- def public_key
45
- private_key.public_key
46
- end
47
-
48
- def private_key
49
- OpenSSL::PKey::RSA.new(pem)
50
- end
51
-
52
- def pem
53
- keys_absolute_path = File.expand_path(jwt_config.path)
54
-
55
- raise ConfigFileNotFoundError.new("Cannot found file in #{jwt_config.path}") unless File.readable?(keys_absolute_path)
56
- File.read(jwt_config.path)
57
- end
58
- end
3
+ require 'decode_this/decoder'
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+
4
+ RSpec.describe DecodeThis::Decoder do
5
+ let(:config_path) { File.expand_path('spec/fixtures/config.yml') }
6
+ let(:payload) { { field: 'foobar' } }
7
+ let(:header_value) { encode(payload) }
8
+ let(:logger) { Logger.new(STDOUT) }
9
+
10
+ subject(:decoded_token) do
11
+ described_class.new(header_value, config_file: config_path, env: :test, logger: logger).call
12
+ end
13
+
14
+ it 'decodes given token correctly' do
15
+ payload.keys.each do |key|
16
+ expect(decoded_token[key.to_s]).to eq(payload[key])
17
+ end
18
+ end
19
+
20
+ context 'when check correct error raising' do
21
+ context 'when raise error when config file not present' do
22
+ let(:config_path) { 'spec/fixtures/nonexistent_config.yml' }
23
+
24
+ it 'raises ConfigFileNotFoundError' do
25
+ expect(logger).to receive(:warn).and_call_original
26
+ expect { decoded_token }.to raise_error { DecodeThis::KeyFileNotFoundError }
27
+ end
28
+ end
29
+
30
+ context 'when raise error when token is expired' do
31
+ let(:payload) { { field: 'foobar', exp: -1 } }
32
+
33
+ it 'raises DecodeError' do
34
+ expect(logger).to receive(:warn).and_call_original
35
+ expect { decoded_token }.to raise_error { DecodeThis::DecodeError }
36
+ end
37
+ end
38
+
39
+ context 'when raise error when try to decode by another key' do
40
+ let(:config_path) { File.expand_path('spec/fixtures/another_config.yml') }
41
+
42
+ it 'raises DecodeError' do
43
+ expect(logger).to receive(:warn).and_call_original
44
+ expect { decoded_token }.to raise_error { DecodeThis::DecodeError }
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,4 @@
1
+ test:
2
+ algorithm: 'RS256'
3
+ authorization_scheme: 'Bearer'
4
+ key_path: './spec/fixtures/another_unsecured.pem'
@@ -0,0 +1,13 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
3
+ wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
4
+ 1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh
5
+ 3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2
6
+ pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX
7
+ GukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il
8
+ AkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF
9
+ L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k
10
+ X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
11
+ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
12
+ 37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
13
+ -----END RSA PRIVATE KEY-----
@@ -1,3 +1,4 @@
1
1
  test:
2
2
  algorithm: 'RS256'
3
- path: './spec/fixtures/unsecured.pem'
3
+ authorization_scheme: 'Bearer'
4
+ key_path: './spec/fixtures/unsecured.pem'
@@ -0,0 +1,4 @@
1
+ test:
2
+ algorithm: 'RS256'
3
+ authorization_scheme: 'Bearer'
4
+ key_path: './spec/fixtures/notexistent.pem'
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ require 'simplecov'
3
+ require 'simplecov-console'
4
+
5
+ SimpleCov.formatters = [
6
+ SimpleCov::Formatter::HTMLFormatter,
7
+ SimpleCov::Formatter::Console
8
+ ]
9
+ SimpleCov.start
data/spec/spec_helper.rb CHANGED
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
+ require_relative 'simplecov_helper'
2
3
  require 'bundler/setup'
4
+ require 'logger'
3
5
  require 'decode_this'
4
6
 
5
7
  def encode(payload)
6
8
  config = Huyettings.new(File.expand_path('spec/fixtures/config.yml'), :test)
7
- private_key = OpenSSL::PKey::RSA.new(File.read(config.path))
8
- JWT.encode(payload, private_key, config.algorithm)
9
+ private_key = OpenSSL::PKey::RSA.new(File.read(config.key_path))
10
+ "#{config.authorization_scheme} " + JWT.encode(payload, private_key, config.algorithm)
9
11
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: decode_this
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sasha Kotov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-07 00:00:00.000000000 Z
11
+ date: 2018-03-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jwt
@@ -94,10 +94,16 @@ files:
94
94
  - Readme.md
95
95
  - decode_this.gemspec
96
96
  - lib/decode_this.rb
97
+ - lib/decode_this/decoder.rb
98
+ - lib/decode_this/safe_decoding.rb
97
99
  - lib/decode_this/version.rb
98
- - spec/decode_this_spec.rb
100
+ - spec/decode_this/decoder_spec.rb
101
+ - spec/fixtures/another_config.yml
102
+ - spec/fixtures/another_unsecured.pem
99
103
  - spec/fixtures/config.yml
104
+ - spec/fixtures/nonexistent_config.yml
100
105
  - spec/fixtures/unsecured.pem
106
+ - spec/simplecov_helper.rb
101
107
  - spec/spec_helper.rb
102
108
  homepage:
103
109
  licenses: []
@@ -123,7 +129,11 @@ signing_key:
123
129
  specification_version: 4
124
130
  summary: Decode token. This token
125
131
  test_files:
126
- - spec/decode_this_spec.rb
132
+ - spec/decode_this/decoder_spec.rb
133
+ - spec/fixtures/another_config.yml
134
+ - spec/fixtures/another_unsecured.pem
127
135
  - spec/fixtures/config.yml
136
+ - spec/fixtures/nonexistent_config.yml
128
137
  - spec/fixtures/unsecured.pem
138
+ - spec/simplecov_helper.rb
129
139
  - spec/spec_helper.rb
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'spec_helper'
3
-
4
- RSpec.describe DecodeThis do
5
- let(:config_path) { File.expand_path('spec/fixtures/config.yml') }
6
- let(:payload) { { field: 'foobar' } }
7
- let(:token) { encode(payload) }
8
-
9
- subject(:decoded_token) { described_class.new(token, config_file: config_path, env: :test).decode }
10
-
11
- it 'decodes given token correctly' do
12
- payload.keys.each do |key|
13
- expect(decoded_token[key.to_s]).to eq(payload[key])
14
- end
15
- end
16
- end