prx_auth 1.2.1 → 1.6.0

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
  SHA256:
3
- metadata.gz: 1a851973aab51bd36533ff3959ab4d494886d0c6803fa1be1359ee1a5e0a68c0
4
- data.tar.gz: 9033b3e43be7f508ec7956df7be578e4b860f3285169ac1de6c5ee80b1e21c89
3
+ metadata.gz: f7d2d71dd1671a97f1ce4f9181852e8cc7e0a651e91370aca09300c468dfbf86
4
+ data.tar.gz: f4fcef9ecc2977321ae3ec193a8be62c0735d08f0a564787ff581d3e65bc869a
5
5
  SHA512:
6
- metadata.gz: 92e74203143a4920470336dd93bedfe07fe03235bbcafc20229f4076bdfabe5240e5908b0765b035c9471c0a8bfbc001435e08e61830dd954f64e65f0d0f2e26
7
- data.tar.gz: 139726fa863102b0e179c63e29df3d42f4fa03082e1208668cc6f9d32062bcab1995921677f22138718449a5b6a1cb67694003e141b829f585f0b6cc141e5dc9
6
+ metadata.gz: 482f861b69e7e05eb6d9b2308b2c0dfc402ac8a8bd01f200e14e04cafd24897adea376de29a6ad1fb6a52eeb08647303c10c344e097a45c1dea6730ace2a4bdf
7
+ data.tar.gz: 10ada294eda678f2d70c4780db32f33063a869d3caf25c481949cd5350f326bbeb6c5a60d96fa797ef3d1f2548c552edde731a28fd9667ee20a7d336ad936656
@@ -37,6 +37,14 @@ module PrxAuth
37
37
  end
38
38
  end
39
39
 
40
+ def [](key)
41
+ super(key.to_s)
42
+ end
43
+
44
+ def []=(key, value)
45
+ super(key.to_s, value)
46
+ end
47
+
40
48
  def condense
41
49
  condensed_wildcard = @wildcard.condense
42
50
  condensed_map = Hash[map do |resource, list|
@@ -126,7 +126,11 @@ module PrxAuth
126
126
  def &(other_list)
127
127
  return ScopeList.new('') if other_list.nil?
128
128
 
129
- self - (self - other_list)
129
+ self - (self - other_list) + (other_list - (other_list - self))
130
+ end
131
+
132
+ def ==(other)
133
+ condense.sort_by(&:to_s) == other.condense.sort_by(&:to_s)
130
134
  end
131
135
 
132
136
  private
@@ -1,3 +1,3 @@
1
1
  module PrxAuth
2
- VERSION = "1.2.1"
2
+ VERSION = "1.6.0"
3
3
  end
@@ -1,6 +1,7 @@
1
1
  require 'json/jwt'
2
2
  require 'rack/prx_auth/certificate'
3
3
  require 'rack/prx_auth/token_data'
4
+ require 'rack/prx_auth/auth_validator'
4
5
  require 'prx_auth'
5
6
 
6
7
  module Rack
@@ -20,16 +21,21 @@ module Rack
20
21
  @issuer = options[:issuer] || DEFAULT_ISS
21
22
  end
22
23
 
24
+ def build_auth_validator(token)
25
+ AuthValidator.new(token, @certificate, @issuer)
26
+ end
27
+
23
28
  def call(env)
24
29
  return @app.call(env) unless env['HTTP_AUTHORIZATION']
25
30
 
26
31
  token = env['HTTP_AUTHORIZATION'].split[1]
27
- claims = decode_token(token)
28
32
 
29
- return @app.call(env) unless should_validate_token?(claims)
33
+ auth_validator = build_auth_validator(token)
34
+
35
+ return @app.call(env) unless should_validate_token?(auth_validator)
30
36
 
31
- if valid?(claims, token)
32
- env['prx.auth'] = TokenData.new(claims)
37
+ if auth_validator.valid?
38
+ env['prx.auth'] = TokenData.new(auth_validator.claims)
33
39
  @app.call(env)
34
40
  else
35
41
  INVALID_TOKEN
@@ -38,26 +44,8 @@ module Rack
38
44
 
39
45
  private
40
46
 
41
- def valid?(claims, token)
42
- !expired?(claims) && @certificate.valid?(token)
43
- end
44
-
45
- def decode_token(token)
46
- return {} if token.nil?
47
-
48
- begin
49
- JSON::JWT.decode(token, :skip_verification)
50
- rescue JSON::JWT::InvalidFormat
51
- {}
52
- end
53
- end
54
-
55
- def expired?(claims)
56
- Time.now.to_i > (claims['iat'] + claims['exp'])
57
- end
58
-
59
- def should_validate_token?(claims)
60
- claims['iss'] == @issuer
47
+ def should_validate_token?(auth_validator)
48
+ auth_validator.token_issuer_matches?
61
49
  end
62
50
  end
63
51
  end
@@ -0,0 +1,51 @@
1
+ require 'json/jwt'
2
+
3
+ module Rack
4
+ class PrxAuth
5
+ class AuthValidator
6
+
7
+ attr_reader :issuer, :claims, :token
8
+
9
+ def initialize(token, certificate, issuer)
10
+ @token = token
11
+ @certificate = certificate
12
+ @issuer = issuer
13
+ end
14
+
15
+ def valid?
16
+ valid_token_format? && !expired? && @certificate.valid?(token)
17
+ end
18
+
19
+ def claims
20
+ @claims ||= decode_token
21
+ end
22
+
23
+ def valid_token_format?
24
+ decode_token.present?
25
+ end
26
+
27
+ def decode_token
28
+ return {} if token.nil?
29
+
30
+ begin
31
+ JSON::JWT.decode(token, :skip_verification)
32
+ rescue JSON::JWT::InvalidFormat
33
+ {}
34
+ end
35
+ end
36
+
37
+ def expired?
38
+ now = Time.now.to_i - 30 # 30 second clock jitter allowance
39
+ if claims['iat'] <= claims['exp']
40
+ now > claims['exp']
41
+ else
42
+ now > (claims['iat'] + claims['exp'])
43
+ end
44
+ end
45
+
46
+ def token_issuer_matches?
47
+ claims['iss'] == @issuer
48
+ end
49
+ end
50
+ end
51
+ end
@@ -32,7 +32,11 @@ module Rack
32
32
  def globally_authorized?(namespace, scope=nil)
33
33
  authorized?(::PrxAuth::ResourceMap::WILDCARD_KEY, namespace, scope)
34
34
  end
35
-
35
+
36
+ def authorized_account_ids(scope)
37
+ resources(::PrxAuth::Rails.configuration.namespace, scope).map(&:to_i)
38
+ end
39
+
36
40
  private
37
41
 
38
42
  def unpack_aur(aur)
@@ -21,12 +21,12 @@ Gem::Specification.new do |spec|
21
21
  spec.required_ruby_version = '>= 2.3'
22
22
 
23
23
  spec.add_development_dependency 'bundler', '~> 2.0'
24
- spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'rake', '~> 12.3.3'
25
25
  spec.add_development_dependency 'coveralls', '~> 0'
26
26
  spec.add_development_dependency 'guard'
27
27
  spec.add_development_dependency 'guard-minitest'
28
28
 
29
29
  spec.add_dependency 'rack', '>= 1.5.2'
30
30
  spec.add_dependency 'json', '>= 1.8.1'
31
- spec.add_dependency 'json-jwt', '~> 1.9.4'
31
+ spec.add_dependency 'json-jwt', '~> 1.12.0'
32
32
  end
@@ -169,4 +169,17 @@ describe PrxAuth::ResourceMap do
169
169
  assert map.as_json.has_key?('*')
170
170
  end
171
171
  end
172
+
173
+ describe '#[]' do
174
+ it 'automatically stringifies' do
175
+ refute_nil map[123]
176
+ end
177
+ end
178
+
179
+ describe '#[]=' do
180
+ it 'automatically stringifies' do
181
+ map[789] = PrxAuth::ScopeList.new("")
182
+ refute_nil map["789"]
183
+ end
184
+ end
172
185
  end
@@ -104,5 +104,26 @@ describe PrxAuth::ScopeList do
104
104
  sl = new_list('one') & nil
105
105
  assert !sl.contains?(:one)
106
106
  end
107
+
108
+ it 'works when either side has non-namespaced values correctly' do
109
+ sl = PrxAuth::ScopeList.new('foo:bar') & PrxAuth::ScopeList.new('bar')
110
+ assert sl.contains?(:foo, :bar)
111
+ refute sl.contains?(:bar)
112
+
113
+ sl = PrxAuth::ScopeList.new('bar') & PrxAuth::ScopeList.new('foo:bar')
114
+ assert sl.contains?(:foo, :bar)
115
+ refute sl.contains?(:bar)
116
+ end
117
+ end
118
+
119
+ describe '==' do
120
+
121
+ it 'is equal when they are functionally equal' do
122
+ assert_equal PrxAuth::ScopeList.new("foo ns:foo bar ns2:baz"), PrxAuth::ScopeList.new("ns2:baz bar foo")
123
+ end
124
+
125
+ it 'is not equal when they are not functionally equal' do
126
+ refute_equal PrxAuth::ScopeList.new("foo bar"), PrxAuth::ScopeList.new("foo:bar bar:foo")
127
+ end
107
128
  end
108
129
  end
@@ -0,0 +1,111 @@
1
+ require 'test_helper'
2
+
3
+ describe Rack::PrxAuth::AuthValidator do
4
+ let(:app) { Proc.new {|env| env } }
5
+ let(:auth_validator) { Rack::PrxAuth::AuthValidator.new(token, certificate, 'id.local.test') }
6
+
7
+ let(:token) { 'some.token.foo' }
8
+
9
+ let(:iat) { Time.now.to_i }
10
+ let(:exp) { 3600 }
11
+ let(:claims) { {'sub'=>3, 'exp'=>exp, 'iat'=>iat, 'token_type'=>'bearer', 'scope'=>nil, 'iss'=>'id.prx.org'} }
12
+ let(:certificate) { cert = Rack::PrxAuth::Certificate.new }
13
+
14
+ describe '#token_issuer_matches' do
15
+ it 'false if the token is from another issuer' do
16
+ auth_validator.stub(:claims, claims) do
17
+ refute auth_validator.token_issuer_matches?
18
+ end
19
+ end
20
+
21
+ it 'is false if the issuer in the validator does not match' do
22
+ auth_validator.stub(:issuer, 'id.foo.com') do
23
+ refute auth_validator.token_issuer_matches?
24
+ end
25
+ end
26
+ end
27
+
28
+ describe '#valid?' do
29
+ it 'is false if token is invalid' do
30
+ auth_validator.stub(:claims, claims) do
31
+ refute auth_validator.valid?
32
+ end
33
+ end
34
+
35
+ it 'is false if the token is nil' do
36
+ certificate.stub(:valid?, true) do
37
+ auth_validator.stub(:token, nil) do
38
+ refute auth_validator.valid?
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ describe '#expired?' do
45
+
46
+ def expired?(claims)
47
+ auth_validator.stub(:claims, claims) do
48
+ auth_validator.expired?
49
+ end
50
+ end
51
+
52
+ describe 'with a malformed exp' do
53
+ let(:iat) { Time.now.to_i }
54
+ let(:exp) { 3600 }
55
+
56
+ it 'is expired if iat + exp are in the past' do
57
+ claims['iat'] -= 3631
58
+
59
+ assert expired?(claims)
60
+ end
61
+
62
+ it 'is not expired if iat + exp are in the future' do
63
+ claims['iat'] = Time.now.to_i - 3599
64
+
65
+ refute expired?(claims)
66
+ end
67
+
68
+ it 'allows a 30s clock jitter' do
69
+ claims['iat'] = Time.now.to_i - 3629
70
+
71
+ refute expired?(claims)
72
+ end
73
+ end
74
+
75
+ describe 'with a corrected exp' do
76
+ let(:iat) { Time.now.to_i - 3600 }
77
+ let(:exp) { Time.now.to_i + 1 }
78
+
79
+ it 'is not expired if exp is in the future' do
80
+ refute expired?(claims)
81
+ end
82
+
83
+ it 'is expired if exp is in the past (with 30s jitter grace)' do
84
+ claims['exp'] = Time.now.to_i - 31
85
+ assert expired?(claims)
86
+ claims['exp'] = Time.now.to_i - 29
87
+ refute expired?(claims)
88
+ end
89
+ end
90
+ end
91
+
92
+ describe '#decode_token' do
93
+ it 'should return an empty result for a nil token' do
94
+ auth_validator.stub(:token, nil) do
95
+ assert auth_validator.decode_token == {}
96
+ end
97
+ end
98
+
99
+ it 'should return an empty result for an empty token' do
100
+ auth_validator.stub(:token, '') do
101
+ assert auth_validator.decode_token == {}
102
+ end
103
+ end
104
+
105
+ it 'should return an empty result for a malformed token' do
106
+ auth_validator.stub(:token, token) do
107
+ assert auth_validator.decode_token == {}
108
+ end
109
+ end
110
+ end
111
+ end
@@ -5,7 +5,9 @@ describe Rack::PrxAuth do
5
5
  let(:prxauth) { Rack::PrxAuth.new(app) }
6
6
  let(:fake_token) { 'afawefawefawefawegstgnsrtiohnlijblublwjnvrtoign'}
7
7
  let(:env) { {'HTTP_AUTHORIZATION' => 'Bearer ' + fake_token } }
8
- let(:claims) { {'sub'=>3, 'exp'=>3600, 'iat'=>Time.now.to_i, 'token_type'=>'bearer', 'scope'=>nil, 'iss'=>'id.prx.org'} }
8
+ let(:iat) { Time.now.to_i }
9
+ let(:exp) { 3600 }
10
+ let(:claims) { {'sub'=>3, 'exp'=>exp, 'iat'=>iat, 'token_type'=>'bearer', 'scope'=>nil, 'iss'=>'id.prx.org'} }
9
11
 
10
12
  describe '#call' do
11
13
  it 'does nothing if there is no authorization header' do
@@ -32,24 +34,34 @@ describe Rack::PrxAuth do
32
34
  end
33
35
 
34
36
  it 'returns 401 if verification fails' do
37
+ auth_validator = prxauth.build_auth_validator('sometoken')
38
+
35
39
  JSON::JWT.stub(:decode, claims) do
36
- prxauth.stub(:valid?, false) do
37
- assert prxauth.call(env) == Rack::PrxAuth::INVALID_TOKEN
40
+ prxauth.stub(:build_auth_validator, auth_validator) do
41
+ auth_validator.stub(:valid?, false) do
42
+ assert prxauth.call(env) == Rack::PrxAuth::INVALID_TOKEN
43
+ end
38
44
  end
39
45
  end
40
46
  end
41
47
 
42
48
  it 'returns 401 if access token has expired' do
49
+ auth_validator = prxauth.build_auth_validator('sometoken')
50
+
43
51
  JSON::JWT.stub(:decode, claims) do
44
- prxauth.stub(:expired?, true) do
45
- assert prxauth.call(env) == Rack::PrxAuth::INVALID_TOKEN
52
+ prxauth.stub(:build_auth_validator, auth_validator) do
53
+ auth_validator.stub(:expired?, true) do
54
+ assert prxauth.call(env) == Rack::PrxAuth::INVALID_TOKEN
55
+ end
46
56
  end
47
57
  end
48
58
  end
49
59
 
50
60
  it 'attaches claims to request params if verification passes' do
51
- prxauth.stub(:decode_token, claims) do
52
- prxauth.stub(:valid?, true) do
61
+ auth_validator = prxauth.build_auth_validator('sometoken')
62
+
63
+ JSON::JWT.stub(:decode, claims) do
64
+ prxauth.stub(:build_auth_validator, auth_validator) do
53
65
  prxauth.call(env)['prx.auth'].tap do |token|
54
66
  assert token.instance_of? Rack::PrxAuth::TokenData
55
67
  assert token.user_id == claims['sub']
@@ -59,18 +71,6 @@ describe Rack::PrxAuth do
59
71
  end
60
72
  end
61
73
 
62
- describe '#token_expired?' do
63
- it 'returns true if token is expired' do
64
- claims['iat'] = Time.now.to_i - 4000
65
-
66
- assert prxauth.send(:expired?, claims) == true
67
- end
68
-
69
- it 'returns false if it is valid' do
70
- assert prxauth.send(:expired?, claims) == false
71
- end
72
- end
73
-
74
74
  describe 'initialize' do
75
75
  it 'takes a certificate location as an option' do
76
76
  loc = nil
@@ -80,18 +80,4 @@ describe Rack::PrxAuth do
80
80
  end
81
81
  end
82
82
  end
83
-
84
- describe '#decode_token' do
85
- it 'should return an empty result for a nil token' do
86
- assert prxauth.send(:decode_token, nil) == {}
87
- end
88
-
89
- it 'should return an empty result for an empty token' do
90
- assert prxauth.send(:decode_token, {}) == {}
91
- end
92
-
93
- it 'should return an empty result for a malformed token' do
94
- assert prxauth.send(:decode_token, 'asdfsadfsad') == {}
95
- end
96
- end
97
83
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prx_auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eve Asher
8
8
  - Chris Rhoden
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-08-11 00:00:00.000000000 Z
12
+ date: 2021-01-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -31,14 +31,14 @@ dependencies:
31
31
  requirements:
32
32
  - - "~>"
33
33
  - !ruby/object:Gem::Version
34
- version: '10.0'
34
+ version: 12.3.3
35
35
  type: :development
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - "~>"
40
40
  - !ruby/object:Gem::Version
41
- version: '10.0'
41
+ version: 12.3.3
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: coveralls
44
44
  requirement: !ruby/object:Gem::Requirement
@@ -115,14 +115,14 @@ dependencies:
115
115
  requirements:
116
116
  - - "~>"
117
117
  - !ruby/object:Gem::Version
118
- version: 1.9.4
118
+ version: 1.12.0
119
119
  type: :runtime
120
120
  prerelease: false
121
121
  version_requirements: !ruby/object:Gem::Requirement
122
122
  requirements:
123
123
  - - "~>"
124
124
  - !ruby/object:Gem::Version
125
- version: 1.9.4
125
+ version: 1.12.0
126
126
  description: Specific to PRX. Will ignore tokens that were not issued by PRX.
127
127
  email:
128
128
  - eve@prx.org
@@ -144,11 +144,13 @@ files:
144
144
  - lib/prx_auth/scope_list.rb
145
145
  - lib/prx_auth/version.rb
146
146
  - lib/rack/prx_auth.rb
147
+ - lib/rack/prx_auth/auth_validator.rb
147
148
  - lib/rack/prx_auth/certificate.rb
148
149
  - lib/rack/prx_auth/token_data.rb
149
150
  - prx_auth.gemspec
150
151
  - test/prx_auth/resource_map_test.rb
151
152
  - test/prx_auth/scope_list_test.rb
153
+ - test/rack/prx_auth/auth_validator_test.rb
152
154
  - test/rack/prx_auth/certificate_test.rb
153
155
  - test/rack/prx_auth/token_data_test.rb
154
156
  - test/rack/prx_auth_test.rb
@@ -157,7 +159,7 @@ homepage: https://github.com/PRX/prx_auth
157
159
  licenses:
158
160
  - MIT
159
161
  metadata: {}
160
- post_install_message:
162
+ post_install_message:
161
163
  rdoc_options: []
162
164
  require_paths:
163
165
  - lib
@@ -172,14 +174,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
172
174
  - !ruby/object:Gem::Version
173
175
  version: '0'
174
176
  requirements: []
175
- rubygems_version: 3.0.1
176
- signing_key:
177
+ rubygems_version: 3.0.3
178
+ signing_key:
177
179
  specification_version: 4
178
180
  summary: Utilites for parsing PRX JWTs and Rack middleware that verifies and attaches
179
181
  the token's claims to env.
180
182
  test_files:
181
183
  - test/prx_auth/resource_map_test.rb
182
184
  - test/prx_auth/scope_list_test.rb
185
+ - test/rack/prx_auth/auth_validator_test.rb
183
186
  - test/rack/prx_auth/certificate_test.rb
184
187
  - test/rack/prx_auth/token_data_test.rb
185
188
  - test/rack/prx_auth_test.rb