decode_this 0.1.3 → 0.1.4

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: 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