fridge 0.3.0 → 0.3.1
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/.travis.yml +1 -0
- data/fridge.gemspec +1 -1
- data/lib/fridge/access_token.rb +56 -19
- data/lib/fridge/rails_helpers.rb +8 -0
- data/lib/fridge/version.rb +1 -1
- data/spec/fridge/access_token_spec.rb +27 -0
- data/spec/fridge/rails_helpers_spec.rb +3 -3
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 28164c99b4a71e24796559da6b1fc0663d0f42c9
|
4
|
+
data.tar.gz: db5603732b2c608dd063cdf0b3c8e6cf33f8ead6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44f92c7854c7d359fe5cf9596b165c9ff93815f9f1d0040782f2b230de38a47ea314aed24bc48ed531d4fffb880dacd0cbaa85c83261f8739f0b17b5727245e4
|
7
|
+
data.tar.gz: 2fbf80cfddbade814f62fb21e9492f393d155a5df6e55e49ebf310ee5a4c5218382277d473c518f1b93b7978e592a7edaeac213fc7f0e5d57db3aa48309451c1
|
data/.travis.yml
CHANGED
data/fridge.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.license = 'MIT'
|
17
17
|
|
18
18
|
spec.files = `git ls-files`.split($RS)
|
19
|
-
spec.test_files = spec.files.grep(
|
19
|
+
spec.test_files = spec.files.grep(%r{^spec/})
|
20
20
|
spec.require_paths = ['lib']
|
21
21
|
|
22
22
|
spec.add_dependency 'gem_config'
|
data/lib/fridge/access_token.rb
CHANGED
@@ -2,7 +2,7 @@ require 'jwt'
|
|
2
2
|
|
3
3
|
module Fridge
|
4
4
|
class AccessToken
|
5
|
-
attr_accessor :id, :issuer, :subject, :scope, :expires_at,
|
5
|
+
attr_accessor :id, :issuer, :subject, :scope, :expires_at, :actor,
|
6
6
|
:jwt, :attributes
|
7
7
|
|
8
8
|
# rubocop:disable MethodLength
|
@@ -15,11 +15,11 @@ module Fridge
|
|
15
15
|
when Hash then jwt_or_options
|
16
16
|
else {}
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
|
+
[:id, :issuer, :subject, :scope, :expires_at, :actor].each do |key|
|
19
20
|
send "#{key}=", options.delete(key)
|
20
21
|
end
|
21
|
-
self.attributes = options
|
22
|
-
self.attributes = Hash[attributes.map { |k, v| [k.to_sym, v] }]
|
22
|
+
self.attributes = options
|
23
23
|
end
|
24
24
|
# rubocop:enable MethodLength
|
25
25
|
|
@@ -35,13 +35,13 @@ module Fridge
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def encode_and_sign
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
38
|
+
h = {}
|
39
|
+
[:id, :issuer, :subject, :scope, :expires_at, :actor].each do |key|
|
40
|
+
h[key] = send(key)
|
41
|
+
end
|
42
|
+
h.merge!(attributes)
|
43
|
+
h = encode_for_jwt(h)
|
44
|
+
JWT.encode(h, private_key, algorithm)
|
45
45
|
rescue
|
46
46
|
raise SerializationError, 'Invalid private key or signing algorithm'
|
47
47
|
end
|
@@ -49,14 +49,7 @@ module Fridge
|
|
49
49
|
# rubocop:disable MethodLength
|
50
50
|
def decode_and_verify(jwt)
|
51
51
|
hash = JWT.decode(jwt, public_key)
|
52
|
-
|
53
|
-
id: hash.delete('id'),
|
54
|
-
issuer: hash.delete('iss'),
|
55
|
-
subject: hash.delete('sub'),
|
56
|
-
scope: hash.delete('scope'),
|
57
|
-
expires_at: Time.at(hash.delete('exp'))
|
58
|
-
}
|
59
|
-
base.merge(hash)
|
52
|
+
decode_from_jwt(hash)
|
60
53
|
rescue JWT::DecodeError
|
61
54
|
raise InvalidToken, 'Invalid access token'
|
62
55
|
end
|
@@ -123,5 +116,49 @@ module Fridge
|
|
123
116
|
def validate_public_key!
|
124
117
|
fail SerializationError, 'No public key configured' unless public_key
|
125
118
|
end
|
119
|
+
|
120
|
+
# Internally, we use "subject" to refer to "sub", and so on. We also
|
121
|
+
# represent some objects (expiry) differently. These functions do the
|
122
|
+
# mapping from Fridge to JWT and vice-versa.
|
123
|
+
|
124
|
+
def encode_for_jwt(hash)
|
125
|
+
out = {
|
126
|
+
id: hash.delete(:id),
|
127
|
+
iss: hash.delete(:issuer),
|
128
|
+
sub: hash.delete(:subject),
|
129
|
+
scope: hash.delete(:scope)
|
130
|
+
}.delete_if { |_, v| v.nil? }
|
131
|
+
|
132
|
+
# Unfortunately, nil.to_i returns 0, which means we can't
|
133
|
+
# easily clean out exp if we include it although it wasn't passed
|
134
|
+
# in like we do for other keys. So, we only include it if it's
|
135
|
+
# actually passed in and non-nil. Either way, we delete the keys.
|
136
|
+
hash.delete(:expires_at).tap { |e| out[:exp] = e.to_i if e }
|
137
|
+
hash.delete(:actor).tap { |a| out[:act] = encode_for_jwt(a) if a }
|
138
|
+
|
139
|
+
# Extra attributes passed through as-is
|
140
|
+
out.merge!(hash)
|
141
|
+
|
142
|
+
out
|
143
|
+
end
|
144
|
+
|
145
|
+
def decode_from_jwt(hash)
|
146
|
+
out = {
|
147
|
+
id: hash.delete('id'),
|
148
|
+
issuer: hash.delete('iss'),
|
149
|
+
subject: hash.delete('sub'),
|
150
|
+
scope: hash.delete('scope')
|
151
|
+
}.delete_if { |_, v| v.nil? }
|
152
|
+
|
153
|
+
hash.delete('exp').tap { |e| out[:expires_at] = Time.at(e) if e }
|
154
|
+
hash.delete('act').tap { |a| out[:actor] = decode_from_jwt(a) if a }
|
155
|
+
|
156
|
+
# Extra attributes
|
157
|
+
hash.delete_if { |_, v| v.nil? }
|
158
|
+
hash = Hash[hash.map { |k, v| [k.to_sym, v] }]
|
159
|
+
out.merge!(hash)
|
160
|
+
|
161
|
+
out
|
162
|
+
end
|
126
163
|
end
|
127
164
|
end
|
data/lib/fridge/rails_helpers.rb
CHANGED
@@ -15,6 +15,10 @@ module Fridge
|
|
15
15
|
current_token.subject if current_token
|
16
16
|
end
|
17
17
|
|
18
|
+
def token_actor
|
19
|
+
current_token.actor if current_token
|
20
|
+
end
|
21
|
+
|
18
22
|
def current_token
|
19
23
|
return unless bearer_token
|
20
24
|
@current_token ||= AccessToken.new(bearer_token).tap do |token|
|
@@ -31,6 +35,10 @@ module Fridge
|
|
31
35
|
session_token.subject if session_token
|
32
36
|
end
|
33
37
|
|
38
|
+
def session_actor
|
39
|
+
session_token.actor if session_token
|
40
|
+
end
|
41
|
+
|
34
42
|
def session_token
|
35
43
|
return unless session_cookie
|
36
44
|
@session_token ||= AccessToken.new(session_cookie).tap do |token|
|
data/lib/fridge/version.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'json'
|
2
3
|
|
3
4
|
describe Fridge::AccessToken do
|
4
5
|
describe '#initialize' do
|
@@ -26,6 +27,14 @@ describe Fridge::AccessToken do
|
|
26
27
|
jwt = JWT.encode({ id: 'foobar' }, OpenSSL::PKey::RSA.new(1024), 'RS512')
|
27
28
|
expect { described_class.new(jwt) }.to raise_error Fridge::InvalidToken
|
28
29
|
end
|
30
|
+
|
31
|
+
# http://bit.ly/jwt-none-vulnerability
|
32
|
+
it 'should raise an error with { "alg": "none" }' do
|
33
|
+
jwt = "#{Base64.encode64({ typ: 'JWT', alg: 'none' }.to_json).chomp}." \
|
34
|
+
"#{Base64.encode64({ id: 'foobar' }.to_json).chomp}"
|
35
|
+
expect(JWT.decode(jwt, nil, false)).to eq('id' => 'foobar')
|
36
|
+
expect { described_class.new(jwt) }.to raise_error Fridge::InvalidToken
|
37
|
+
end
|
29
38
|
end
|
30
39
|
|
31
40
|
describe '#serialize' do
|
@@ -99,6 +108,24 @@ describe Fridge::AccessToken do
|
|
99
108
|
subject.subject = nil
|
100
109
|
expect { subject.serialize }.to raise_error Fridge::SerializationError
|
101
110
|
end
|
111
|
+
|
112
|
+
it 'should encode and decode :actor as :act' do
|
113
|
+
# The `act` field can recursively encode additional
|
114
|
+
# claims, so we check those too.
|
115
|
+
actor = { subject: 'foo', username: 'test', actor: { subject: 'bar' } }
|
116
|
+
subject = described_class.new(options.merge(actor: actor))
|
117
|
+
|
118
|
+
# The JWT lib will return everything as strings, so we'll
|
119
|
+
# test that, although eventually we'll want to see symbols back.
|
120
|
+
actor_s = { 'sub' => 'foo', 'username' => 'test',
|
121
|
+
'act' => { 'sub' => 'bar' } }
|
122
|
+
hash = JWT.decode(subject.serialize, public_key)
|
123
|
+
expect(hash['act']).to eq(actor_s)
|
124
|
+
|
125
|
+
# Now, check that we properly get symbols back
|
126
|
+
new = described_class.new(subject.serialize)
|
127
|
+
expect(new.actor).to eq(actor)
|
128
|
+
end
|
102
129
|
end
|
103
130
|
|
104
131
|
describe '#expired?' do
|
@@ -104,7 +104,7 @@ describe Controller, type: :controller do
|
|
104
104
|
it 'should delete all cookies on error' do
|
105
105
|
cookies[:fridge_session] = 'foobar'
|
106
106
|
controller.session_token
|
107
|
-
expect(cookies.deleted?(:fridge_session, domain: :all)).to
|
107
|
+
expect(cookies.deleted?(:fridge_session, domain: :all)).to be true
|
108
108
|
end
|
109
109
|
|
110
110
|
it 'should return nil on error' do
|
@@ -135,12 +135,12 @@ describe Controller, type: :controller do
|
|
135
135
|
describe '#validate_token' do
|
136
136
|
it 'should return false if the token is invalid' do
|
137
137
|
Fridge.configuration.validator = ->(_) { false }
|
138
|
-
expect(controller.validate_token(access_token)).to
|
138
|
+
expect(controller.validate_token(access_token)).to be false
|
139
139
|
end
|
140
140
|
|
141
141
|
it 'should return false if the token validator fails' do
|
142
142
|
Fridge.configuration.validator = ->(_) { fail 'Foobar' }
|
143
|
-
expect(controller.validate_token(access_token)).to
|
143
|
+
expect(controller.validate_token(access_token)).to be false
|
144
144
|
end
|
145
145
|
|
146
146
|
it 'should return the token if valid' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fridge
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Frank Macreery
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: gem_config
|
@@ -183,7 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
183
183
|
version: '0'
|
184
184
|
requirements: []
|
185
185
|
rubyforge_project:
|
186
|
-
rubygems_version: 2.
|
186
|
+
rubygems_version: 2.4.5.1
|
187
187
|
signing_key:
|
188
188
|
specification_version: 4
|
189
189
|
summary: Token validation for distributed resource servers
|