prx_auth 1.3.0 → 1.7.0

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
  SHA256:
3
- metadata.gz: ea45502db577f8550ac1aa9d96187123a7a62d67e7d51998eac6d0eded55e587
4
- data.tar.gz: 1e0cd0290b00484e5bb3dc6979d79c154287c823bf5f284a2657f35addcb6272
3
+ metadata.gz: 9e89ea4feff47dadbac479d38e5a35fa04be2e148cb10ebe4b41d800e2df48c3
4
+ data.tar.gz: b3ae9ed7381ff2765abc01c4126c3b7ede3db2f06a73cbd9da6918c9ac3a41ab
5
5
  SHA512:
6
- metadata.gz: cf7061a2989e205e67f8c908f971b26ebb6d281f3234dcfaef1d29b50ed33369a9256ad7472415e67f13f25f63d24f87298562435c70eb0dd4c489b6ca83dac9
7
- data.tar.gz: ea96bdda24fd8a2a1dba8bea12b9ac793316d021962f0751b3ac3e3496e90b8096db7f38af664fca535e5951a37771761111144154abf4933e10b5f7c538c532
6
+ metadata.gz: 17ffcb56a69a9c5d674f418967a56405a9f44dac40a0359d9bac42bd93421e7273af08632b311718ca10844ac3a37347d1b8753f1cac7f661b62d75752d25875
7
+ data.tar.gz: bb3d2a19cabf47a5f6d3d62edebf9bdb0ac368521b6c6128c1dc73edb00f2a2d353c8e0a17a46c52334e651a3c2926cfaf9215053d4f260d258a75793ee13d14
@@ -126,7 +126,7 @@ 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
130
  end
131
131
 
132
132
  def ==(other)
@@ -1,3 +1,3 @@
1
1
  module PrxAuth
2
- VERSION = "1.3.0"
2
+ VERSION = "1.7.0"
3
3
  end
data/lib/rack/prx_auth.rb CHANGED
@@ -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,58 @@
1
+ require 'json/jwt'
2
+
3
+ module Rack
4
+ class PrxAuth
5
+ class AuthValidator
6
+
7
+ attr_reader :issuer, :token
8
+
9
+ def initialize(token, certificate = nil, issuer = nil)
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
+ (time_to_live + 30) <= 0 # 30 second clock jitter allowance
39
+ end
40
+
41
+ def time_to_live
42
+ now = Time.now.to_i
43
+ if claims['exp'].nil?
44
+ 0
45
+ elsif claims['iat'].nil? || claims['iat'] <= claims['exp']
46
+ claims['exp'] - now
47
+ else
48
+ # malformed - exp is a num-seconds offset from issued-at-time
49
+ (claims['iat'] + claims['exp']) - now
50
+ end
51
+ end
52
+
53
+ def token_issuer_matches?
54
+ claims['iss'] == @issuer
55
+ end
56
+ end
57
+ end
58
+ end
@@ -11,6 +11,7 @@ module Rack
11
11
 
12
12
  def initialize(cert_uri = nil)
13
13
  @cert_location = cert_uri.nil? ? DEFAULT_CERT_LOC : URI(cert_uri)
14
+ @certificate = nil
14
15
  end
15
16
 
16
17
  def valid?(token)
@@ -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)
data/prx_auth.gemspec CHANGED
@@ -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
@@ -104,6 +104,16 @@ 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
107
117
  end
108
118
 
109
119
  describe '==' do
@@ -0,0 +1,141 @@
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) { 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 '#time_to_live' do
93
+ def time_to_live(claims)
94
+ auth_validator.stub(:claims, claims) do
95
+ auth_validator.time_to_live
96
+ end
97
+ end
98
+
99
+ it 'returns the ttl without any clock jitter correction' do
100
+ claims['exp'] = Time.now.to_i + 999
101
+ assert_equal time_to_live(claims), 999
102
+ end
103
+
104
+ it 'handles missing exp' do
105
+ claims['exp'] = nil
106
+ assert_equal time_to_live(claims), 0
107
+ end
108
+
109
+ it 'handles missing iat' do
110
+ claims['iat'] = nil
111
+ claims['exp'] = Time.now.to_i + 999
112
+ assert_equal time_to_live(claims), 999
113
+ end
114
+
115
+ it 'handles malformed exp' do
116
+ claims['iat'] = Time.now.to_i
117
+ claims['exp'] = 999
118
+ assert_equal time_to_live(claims), 999
119
+ end
120
+ end
121
+
122
+ describe '#decode_token' do
123
+ it 'should return an empty result for a nil token' do
124
+ auth_validator.stub(:token, nil) do
125
+ assert auth_validator.decode_token == {}
126
+ end
127
+ end
128
+
129
+ it 'should return an empty result for an empty token' do
130
+ auth_validator.stub(:token, '') do
131
+ assert auth_validator.decode_token == {}
132
+ end
133
+ end
134
+
135
+ it 'should return an empty result for a malformed token' do
136
+ auth_validator.stub(:token, token) do
137
+ assert auth_validator.decode_token == {}
138
+ end
139
+ end
140
+ end
141
+ 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,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prx_auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eve Asher
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-08-28 00:00:00.000000000 Z
12
+ date: 2021-03-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
@@ -172,7 +174,7 @@ 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
177
+ rubygems_version: 3.0.3
176
178
  signing_key:
177
179
  specification_version: 4
178
180
  summary: Utilites for parsing PRX JWTs and Rack middleware that verifies and attaches
@@ -180,6 +182,7 @@ summary: Utilites for parsing PRX JWTs and Rack middleware that verifies and att
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