prx_auth 1.3.0 → 1.7.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: 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