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