fridge 0.3.0 → 0.3.1

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: f4a824a32d9980601cfa696083b4a7c2765f26e0
4
- data.tar.gz: 27f1e343ab6fdd60a947254f3c9eb428370cb11e
3
+ metadata.gz: 28164c99b4a71e24796559da6b1fc0663d0f42c9
4
+ data.tar.gz: db5603732b2c608dd063cdf0b3c8e6cf33f8ead6
5
5
  SHA512:
6
- metadata.gz: b7f135652cd8d9702f9fd3f5ed32f9b6b31ba702ef107a18f32b73303200b89302659991a666c98f432cb9cb5bc2109444b88f9239e3781dd9378c738cc05567
7
- data.tar.gz: 446fb0d2f82514a9d978159f699fc1f031948ad48c0364b46bae54d285c700fc84f86b6c6d2b6a126fef340f51dd97fdb07a36b250df0018c7f3350b390552fc
6
+ metadata.gz: 44f92c7854c7d359fe5cf9596b165c9ff93815f9f1d0040782f2b230de38a47ea314aed24bc48ed531d4fffb880dacd0cbaa85c83261f8739f0b17b5727245e4
7
+ data.tar.gz: 2fbf80cfddbade814f62fb21e9492f393d155a5df6e55e49ebf310ee5a4c5218382277d473c518f1b93b7978e592a7edaeac213fc7f0e5d57db3aa48309451c1
@@ -1,3 +1,4 @@
1
+ sudo: false
1
2
  rvm:
2
3
  - 2.0.0
3
4
  - jruby
@@ -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(/^spec\//)
19
+ spec.test_files = spec.files.grep(%r{^spec/})
20
20
  spec.require_paths = ['lib']
21
21
 
22
22
  spec.add_dependency 'gem_config'
@@ -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
- [:id, :issuer, :subject, :scope, :expires_at].each do |key|
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.reject { |_, v| v.nil? }
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
- JWT.encode({
39
- id: id,
40
- iss: issuer,
41
- sub: subject,
42
- scope: scope,
43
- exp: expires_at.to_i
44
- }.merge(attributes), private_key, algorithm)
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
- base = {
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
@@ -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|
@@ -1,3 +1,3 @@
1
1
  module Fridge
2
- VERSION = '0.3.0'
2
+ VERSION = '0.3.1'
3
3
  end
@@ -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 be_true
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 be_false
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 be_false
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.0
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: 2015-04-01 00:00:00.000000000 Z
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.2.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