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 +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +16 -2
- data/Gemfile +9 -0
- data/Readme.md +12 -3
- data/lib/decode_this/decoder.rb +61 -0
- data/lib/decode_this/safe_decoding.rb +42 -0
- data/lib/decode_this/version.rb +2 -2
- data/lib/decode_this.rb +1 -56
- data/spec/decode_this/decoder_spec.rb +48 -0
- data/spec/fixtures/another_config.yml +4 -0
- data/spec/fixtures/another_unsecured.pem +13 -0
- data/spec/fixtures/config.yml +2 -1
- data/spec/fixtures/nonexistent_config.yml +4 -0
- data/spec/simplecov_helper.rb +9 -0
- data/spec/spec_helper.rb +4 -2
- metadata +14 -4
- data/spec/decode_this_spec.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a402dd6e09333fe90e81f7389710c76dde358f21
|
4
|
+
data.tar.gz: 8e87ea52fe43b899f8bf348313bb0f041d01442c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '005134392d22ddb00b5f206a367d9c6f3ea1fd433ae0c38a9a493e28352e427f58f40f5dacea49da9ec562a123d02250cb8215620c7395875cd790443dc76fd9'
|
7
|
+
data.tar.gz: 79dd5599461321a3c3b59c9afecd9ac5394201dacc9489def6ad6503e002e19b9183da228cf0ee35cc04fc486193d0f2f9f478aff58542b37012d8558c23960c
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,5 +1,19 @@
|
|
1
|
-
|
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
|
-
|
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
data/Readme.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# DecodeThis
|
2
2
|
|
3
3
|
[](https://travis-ci.org/myxaluch/decode_this)
|
4
|
+
[](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
|
-
|
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,
|
39
|
+
jwt_token = JWT.encode(payload, private_key, true, algorithm: algorithm)
|
38
40
|
...
|
39
|
-
|
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
|
data/lib/decode_this/version.rb
CHANGED
data/lib/decode_this.rb
CHANGED
@@ -1,58 +1,3 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'decode_this/version'
|
3
|
-
require '
|
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,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-----
|
data/spec/fixtures/config.yml
CHANGED
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.
|
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.
|
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-
|
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/
|
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/
|
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
|
data/spec/decode_this_spec.rb
DELETED
@@ -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
|