prx_auth 1.4.1 → 1.7.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
  SHA256:
3
- metadata.gz: 765ac2dcac47b990cfb7978891f92fe853d0e8001fc663a0099f90ef064f14a1
4
- data.tar.gz: 8085236902300f65d458a6b66cd2904ff116c340b7c79a08fc17f274856693ce
3
+ metadata.gz: b11c5af8dd4126044aa9d468a50dee909483bc6de84b6fa8f46362e208dac776
4
+ data.tar.gz: 8e308914d3c6f254130bbc02f245ec9a690ea3efbd67c7e03af27d89248e1285
5
5
  SHA512:
6
- metadata.gz: fa5b7a5de59a4de7b0bc974b2ac8932acbf5117c070ea3d2ab602223696a5761eda9a46421d18dfb8c5612331456a42dfeaeae3c63480081c30c90d170de7d5c
7
- data.tar.gz: 66759612cee8f4cf1788d9374934f2a79678e72923fec8a188399fd5f158616ca0a3bc371be66de12e8981cece7c958b0281aaf9a1753826ae816c3e090bd17a
6
+ metadata.gz: 1e41ac8e49eb3bbf6e77a60dbd7a607766dceecc131a221eaefb9d1c6251507cf9b526a7d031dc0a686dd32683ef9113031db1a6de957a86a19738ec9ad5a7d5
7
+ data.tar.gz: 27a2c10518729f37f366ff301835618c959ea7ef5a8446ee0ed0176b6f3046e820fedc58cbce6ce790f114875f98b2c68fbbc06592e8d9685a4eb985e0305d43
@@ -42,7 +42,7 @@ module PrxAuth
42
42
 
43
43
  def initialize(list)
44
44
  @string = list
45
- @string.split(SCOPE_SEPARATOR).each do |value|
45
+ @string.split(SCOPE_SEPARATOR).uniq.each do |value|
46
46
  next if value.length < 1
47
47
 
48
48
  parts = value.split(NAMESPACE_SEPARATOR, 2)
@@ -56,21 +56,21 @@ module PrxAuth
56
56
 
57
57
  def contains?(namespace, scope=nil)
58
58
  entries = if scope.nil?
59
- scope, namespace = namespace, NO_NAMESPACE
59
+ scope, namespace = namespace, NO_NAMESPACE
60
60
  [Entry.new(namespace, symbolize(scope), nil)]
61
61
  else
62
62
  scope = symbolize(scope)
63
63
  namespace = symbolize(namespace)
64
64
  [Entry.new(namespace, scope, nil), Entry.new(NO_NAMESPACE, scope, nil)]
65
65
  end
66
-
66
+
67
67
  entries.any? do |possible_match|
68
68
  include?(possible_match)
69
69
  end
70
70
  end
71
71
 
72
72
  def to_s
73
- @string
73
+ entries.join(SCOPE_SEPARATOR)
74
74
  end
75
75
 
76
76
  def condense
@@ -125,7 +125,7 @@ module PrxAuth
125
125
 
126
126
  def &(other_list)
127
127
  return ScopeList.new('') if other_list.nil?
128
-
128
+
129
129
  self - (self - other_list) + (other_list - (other_list - self))
130
130
  end
131
131
 
@@ -1,3 +1,3 @@
1
1
  module PrxAuth
2
- VERSION = "1.4.1"
2
+ VERSION = "1.7.1"
3
3
  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/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)
30
34
 
31
- if valid?(claims, token)
32
- env['prx.auth'] = TokenData.new(claims)
35
+ return @app.call(env) unless should_validate_token?(auth_validator)
36
+
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,31 +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
- now = Time.now.to_i - 30 # 30 second clock jitter allowance
57
- if claims['iat'] <= claims['exp']
58
- now > claims['exp']
59
- else
60
- now > (claims['iat'] + claims['exp'])
61
- end
62
- end
63
-
64
- def should_validate_token?(claims)
65
- claims['iss'] == @issuer
47
+ def should_validate_token?(auth_validator)
48
+ auth_validator.token_issuer_matches?
66
49
  end
67
50
  end
68
51
  end
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
@@ -12,7 +12,7 @@ describe PrxAuth::ScopeList do
12
12
  it 'looks up successfully for a given scope' do
13
13
  assert list.contains?('write')
14
14
  end
15
-
15
+
16
16
  it 'scans for symbols' do
17
17
  assert list.contains?(:read)
18
18
  end
@@ -27,7 +27,7 @@ describe PrxAuth::ScopeList do
27
27
 
28
28
  describe 'with namespace' do
29
29
  let (:scopes) { 'ns1:hello ns2:goodbye aloha 1:23' }
30
-
30
+
31
31
  it 'works for namespaced lookups' do
32
32
  assert list.contains?(:ns1, :hello)
33
33
  end
@@ -76,6 +76,11 @@ describe PrxAuth::ScopeList do
76
76
  assert sl.to_s == 'The-Beginning the-end'
77
77
  end
78
78
 
79
+ it 'dedups and condenses' do
80
+ sl = new_list('one ns1:two ns2:two one three three') - new_list('ns1:two three')
81
+ assert_equal sl.length, 2
82
+ assert_equal sl.to_s, 'one ns2:two'
83
+ end
79
84
  end
80
85
 
81
86
  describe '#+' do
@@ -86,6 +91,12 @@ describe PrxAuth::ScopeList do
86
91
  assert sl.contains?(:two)
87
92
  end
88
93
 
94
+ it 'dedups and condenses' do
95
+ sl = new_list('one ns1:one two') + new_list('two three') + new_list('two')
96
+ assert_equal sl.length, 3
97
+ assert_equal sl.to_s, 'one two three'
98
+ end
99
+
89
100
  it 'accepts nil' do
90
101
  sl = new_list('one two') + nil
91
102
  assert sl.contains?(:one) && sl.contains?(:two)
@@ -126,4 +137,4 @@ describe PrxAuth::ScopeList do
126
137
  refute_equal PrxAuth::ScopeList.new("foo bar"), PrxAuth::ScopeList.new("foo:bar bar:foo")
127
138
  end
128
139
  end
129
- end
140
+ end
@@ -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
@@ -34,24 +34,34 @@ describe Rack::PrxAuth do
34
34
  end
35
35
 
36
36
  it 'returns 401 if verification fails' do
37
+ auth_validator = prxauth.build_auth_validator('sometoken')
38
+
37
39
  JSON::JWT.stub(:decode, claims) do
38
- prxauth.stub(:valid?, false) do
39
- 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
40
44
  end
41
45
  end
42
46
  end
43
47
 
44
48
  it 'returns 401 if access token has expired' do
49
+ auth_validator = prxauth.build_auth_validator('sometoken')
50
+
45
51
  JSON::JWT.stub(:decode, claims) do
46
- prxauth.stub(:expired?, true) do
47
- 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
48
56
  end
49
57
  end
50
58
  end
51
59
 
52
60
  it 'attaches claims to request params if verification passes' do
53
- prxauth.stub(:decode_token, claims) do
54
- 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
55
65
  prxauth.call(env)['prx.auth'].tap do |token|
56
66
  assert token.instance_of? Rack::PrxAuth::TokenData
57
67
  assert token.user_id == claims['sub']
@@ -61,52 +71,6 @@ describe Rack::PrxAuth do
61
71
  end
62
72
  end
63
73
 
64
- describe '#expired?' do
65
-
66
- def expired?(claims)
67
- prxauth.send(:expired?, claims)
68
- end
69
-
70
- describe 'with a malformed exp' do
71
- let(:iat) { Time.now.to_i }
72
- let(:exp) { 3600 }
73
-
74
- it 'is expired if iat + exp are in the past' do
75
- claims['iat'] -= 3631
76
-
77
- assert expired?(claims)
78
- end
79
-
80
- it 'is not expired if iat + exp are in the future' do
81
- claims['iat'] = Time.now.to_i - 3599
82
-
83
- refute expired?(claims)
84
- end
85
-
86
- it 'allows a 30s clock jitter' do
87
- claims['iat'] = Time.now.to_i - 3629
88
-
89
- refute expired?(claims)
90
- end
91
- end
92
-
93
- describe 'with a corrected exp' do
94
- let(:iat) { Time.now.to_i - 3600 }
95
- let(:exp) { Time.now.to_i + 1 }
96
-
97
- it 'is not expired if exp is in the future' do
98
- refute expired?(claims)
99
- end
100
-
101
- it 'is expired if exp is in the past (with 30s jitter grace)' do
102
- claims['exp'] = Time.now.to_i - 31
103
- assert expired?(claims)
104
- claims['exp'] = Time.now.to_i - 29
105
- refute expired?(claims)
106
- end
107
- end
108
- end
109
-
110
74
  describe 'initialize' do
111
75
  it 'takes a certificate location as an option' do
112
76
  loc = nil
@@ -116,18 +80,4 @@ describe Rack::PrxAuth do
116
80
  end
117
81
  end
118
82
  end
119
-
120
- describe '#decode_token' do
121
- it 'should return an empty result for a nil token' do
122
- assert prxauth.send(:decode_token, nil) == {}
123
- end
124
-
125
- it 'should return an empty result for an empty token' do
126
- assert prxauth.send(:decode_token, {}) == {}
127
- end
128
-
129
- it 'should return an empty result for a malformed token' do
130
- assert prxauth.send(:decode_token, 'asdfsadfsad') == {}
131
- end
132
- end
133
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.4.1
4
+ version: 1.7.1
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-09-21 00:00:00.000000000 Z
12
+ date: 2022-01-05 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