prx_auth 1.4.1 → 1.7.1

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