prx_auth 1.1.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.
@@ -0,0 +1,130 @@
1
+ require 'test_helper'
2
+
3
+ describe Rack::PrxAuth::Certificate do
4
+ let(:subject) { Rack::PrxAuth::Certificate.new }
5
+ let(:certificate) { subject }
6
+
7
+ describe '#initialize' do
8
+ it 'allows setting the location of the certificates' do
9
+ cert = Rack::PrxAuth::Certificate.new('http://example.com')
10
+ assert cert.cert_location == URI('http://example.com')
11
+ end
12
+
13
+ it 'defaults to DEFAULT_CERT_LOC' do
14
+ assert certificate.cert_location == Rack::PrxAuth::Certificate::DEFAULT_CERT_LOC
15
+ end
16
+ end
17
+
18
+ describe '#valid?' do
19
+ it 'validates the token with the public key' do
20
+ token, key = nil, nil
21
+ certificate.stub(:public_key, :public_key) do
22
+ JSON::JWT.stub(:decode, Proc.new {|t, k| token, key = t, k }) do
23
+ certificate.valid?(:token)
24
+ end
25
+ end
26
+
27
+ assert token == :token
28
+ assert key == :public_key
29
+ end
30
+
31
+ it 'returns false if verification fails' do
32
+ JSON::JWT.stub(:decode, Proc.new do |t, k|
33
+ raise JSON::JWT::VerificationFailed
34
+ end) do
35
+ certificate.stub(:public_key, :foo) do
36
+ assert !certificate.valid?(:token)
37
+ end
38
+ end
39
+ end
40
+
41
+ it 'returns true if verification passes' do
42
+ JSON::JWT.stub(:decode, {}) do
43
+ certificate.stub(:public_key, :foo) do
44
+ assert certificate.valid?(:token)
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ describe '#certificate' do
51
+ it 'calls fetch if unprimed' do
52
+ def certificate.fetch
53
+ :sigil
54
+ end
55
+
56
+ assert certificate.send(:certificate) == :sigil
57
+ end
58
+ end
59
+
60
+ describe '#public_key' do
61
+ it 'pulls from the certificate' do
62
+ certificate.stub(:certificate, Struct.new(:public_key).new(:key)) do
63
+ assert certificate.send(:public_key) == :key
64
+ end
65
+ end
66
+ end
67
+
68
+ describe '#fetch' do
69
+ it 'pulls from `#cert_location`' do
70
+ Net::HTTP.stub(:get, ->(x) { "{\"certificates\":{\"asdf\":\"#{x}\"}}" }) do
71
+ OpenSSL::X509::Certificate.stub(:new, ->(x) { x }) do
72
+ certificate.stub(:cert_location, "a://fake.url/here") do
73
+ assert certificate.send(:fetch) == "a://fake.url/here"
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ it 'sets the expiration value' do
80
+ Net::HTTP.stub(:get, ->(x) { "{\"certificates\":{\"asdf\":\"#{x}\"}}" }) do
81
+ OpenSSL::X509::Certificate.stub(:new, ->(_) { Struct.new(:not_after).new(Time.now + 10000) }) do
82
+ certificate.send :certificate
83
+ assert !certificate.send(:needs_refresh?)
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ describe '#expired?' do
90
+ let(:stub_cert) { Struct.new(:not_after).new(Time.now + 10000) }
91
+ before(:each) do
92
+ certificate.instance_variable_set :'@certificate', stub_cert
93
+ end
94
+
95
+ it 'is false when the certificate is not expired' do
96
+ assert !certificate.send(:expired?)
97
+ end
98
+
99
+ it 'is true when the certificate is expired' do
100
+ stub_cert.not_after = Time.now - 500
101
+ assert certificate.send(:expired?)
102
+ end
103
+ end
104
+
105
+ describe '#needs_refresh?' do
106
+ def refresh_at=(time)
107
+ certificate.instance_variable_set :'@refresh_at', time
108
+ end
109
+
110
+ it 'is true if certificate is expired' do
111
+ certificate.stub(:expired?, true) do
112
+ assert certificate.send(:needs_refresh?)
113
+ end
114
+ end
115
+
116
+ it 'is true if we are past refresh value' do
117
+ self.refresh_at = Time.now.to_i - 1000
118
+ certificate.stub(:expired?, false) do
119
+ assert certificate.send(:needs_refresh?)
120
+ end
121
+ end
122
+
123
+ it 'is false if certificate is not expired and refresh is in the future' do
124
+ self.refresh_at = Time.now.to_i + 10000
125
+ certificate.stub(:expired?, false) do
126
+ assert !certificate.send(:needs_refresh?)
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,101 @@
1
+ require 'test_helper'
2
+
3
+ describe Rack::PrxAuth::TokenData do
4
+ it 'pulls user_id from sub' do
5
+ token = Rack::PrxAuth::TokenData.new('sub' => 123)
6
+ assert token.user_id == 123
7
+ end
8
+
9
+ it 'pulls resources from aur' do
10
+ token = Rack::PrxAuth::TokenData.new('aur' => {'123' => 'admin'})
11
+ assert token.resources.include?('123')
12
+ end
13
+
14
+ it 'unpacks compressed aur' do
15
+ token = Rack::PrxAuth::TokenData.new('aur' => {
16
+ '123' => 'member',
17
+ '$' => {
18
+ 'admin' => [456, 789, 1011]
19
+ }
20
+ })
21
+ assert !token.resources.include?('$')
22
+ assert token.resources.include?('789')
23
+ assert token.resources.include?('123')
24
+ end
25
+
26
+ describe '#resources' do
27
+ let(:token) { Rack::PrxAuth::TokenData.new('aur' => aur) }
28
+ let(:aur) { {'123' => 'admin ns1:namespaced', '456' => 'member' } }
29
+
30
+ it 'scans for resources by namespace and scope' do
31
+ assert token.resources(:admin) == ['123']
32
+ assert token.resources(:namespaced) == []
33
+ assert token.resources(:member) == ['456']
34
+ assert token.resources(:ns1, :namespaced) == ['123']
35
+ assert token.resources(:ns1, :member) == ['456']
36
+ end
37
+ end
38
+
39
+ describe '#authorized?' do
40
+ let(:token) { Rack::PrxAuth::TokenData.new('aur' => aur, 'scope' => scope) }
41
+ let(:scope) { 'read write purchase sell delete' }
42
+ let(:aur) { {'123' => 'admin ns1:namespaced', '456' => 'member' } }
43
+
44
+ it 'is authorized for scope in aur' do
45
+ assert token.authorized?(123, 'admin')
46
+ end
47
+
48
+ it 'is not authorized across aur limits' do
49
+ assert !token.authorized?(123, :member)
50
+ end
51
+
52
+ it 'does not require a scope' do
53
+ assert token.authorized?(123)
54
+ end
55
+
56
+ it 'is unauthorized if it hasnt seen the resource' do
57
+ assert !token.authorized?(789)
58
+ end
59
+
60
+ it 'works for namespaced scopes' do
61
+ assert token.authorized?(123, :ns1, :namespaced)
62
+ assert !token.authorized?(123, :namespaced)
63
+ assert token.authorized?(123, :ns1, :admin)
64
+ end
65
+
66
+ describe 'with wildcard role' do
67
+ let(:aur) { {'*' => 'peek', '123' => 'admin', '456' => 'member' } }
68
+
69
+ it 'applies wildcard tokens to queries with no matching aur' do
70
+ assert token.authorized?(789, :peek)
71
+ end
72
+
73
+ it 'does not authorize unscoped for wildcard resources' do
74
+ assert !token.authorized?(789)
75
+ end
76
+
77
+ it 'allows querying by wildcard resource directly' do
78
+ assert token.authorized?('*', :peek)
79
+ assert !token.authorized?('*', :admin)
80
+ end
81
+
82
+ it 'has a shorthand `gobally_authorized?` to query wildcard' do
83
+ assert token.globally_authorized?(:peek)
84
+ assert !token.globally_authorized?(:admin)
85
+ end
86
+
87
+ it 'treats global authorizations as additive to other explicit ones' do
88
+ assert token.authorized?(123, :peek)
89
+ end
90
+
91
+ it 'refuses to run `globally_authorized?` with no scope' do
92
+ assert_raises ArgumentError do
93
+ token.globally_authorized?
94
+ end
95
+ assert_raises ArgumentError do
96
+ token.authorized?('*')
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,97 @@
1
+ require 'test_helper'
2
+
3
+ describe Rack::PrxAuth do
4
+ let(:app) { Proc.new {|env| env } }
5
+ let(:prxauth) { Rack::PrxAuth.new(app) }
6
+ let(:fake_token) { 'afawefawefawefawegstgnsrtiohnlijblublwjnvrtoign'}
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'} }
9
+
10
+ describe '#call' do
11
+ it 'does nothing if there is no authorization header' do
12
+ env = {}
13
+
14
+ assert prxauth.call(env.clone) == env
15
+ end
16
+
17
+ it 'does nothing if the token is from another issuer' do
18
+ claims['iss'] = 'auth.elsewhere.org'
19
+
20
+ JSON::JWT.stub(:decode, claims) do
21
+ assert prxauth.call(env.clone) == env
22
+ end
23
+ end
24
+
25
+ it 'does nothing if token is invalid' do
26
+ assert prxauth.call(env.clone) == env
27
+ end
28
+
29
+ it 'does nothing if the token is nil' do
30
+ env = { "HTTP_AUTHORIZATION" => "Bearer "}
31
+ assert prxauth.call(env) == env
32
+ end
33
+
34
+ it 'returns 401 if verification fails' do
35
+ JSON::JWT.stub(:decode, claims) do
36
+ prxauth.stub(:valid?, false) do
37
+ assert prxauth.call(env) == Rack::PrxAuth::INVALID_TOKEN
38
+ end
39
+ end
40
+ end
41
+
42
+ it 'returns 401 if access token has expired' do
43
+ JSON::JWT.stub(:decode, claims) do
44
+ prxauth.stub(:expired?, true) do
45
+ assert prxauth.call(env) == Rack::PrxAuth::INVALID_TOKEN
46
+ end
47
+ end
48
+ end
49
+
50
+ it 'attaches claims to request params if verification passes' do
51
+ prxauth.stub(:decode_token, claims) do
52
+ prxauth.stub(:valid?, true) do
53
+ prxauth.call(env)['prx.auth'].tap do |token|
54
+ assert token.instance_of? Rack::PrxAuth::TokenData
55
+ assert token.user_id == claims['sub']
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+
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
+ describe 'initialize' do
75
+ it 'takes a certificate location as an option' do
76
+ loc = nil
77
+ Rack::PrxAuth::Certificate.stub(:new, Proc.new{|l| loc = l}) do
78
+ Rack::PrxAuth.new(app, cert_location: :location)
79
+ assert loc == :location
80
+ end
81
+ end
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
+ end
@@ -0,0 +1,10 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
5
+ require 'prx_auth'
6
+ require 'rack/prx_auth'
7
+
8
+ require 'minitest/autorun'
9
+ require 'minitest/spec'
10
+ require 'minitest/pride'
metadata ADDED
@@ -0,0 +1,187 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: prx_auth
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Eve Asher
8
+ - Chris Rhoden
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2020-04-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2.0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: coveralls
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: guard
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: guard-minitest
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rack
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 1.5.2
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: 1.5.2
98
+ - !ruby/object:Gem::Dependency
99
+ name: json
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: 1.8.1
105
+ type: :runtime
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: 1.8.1
112
+ - !ruby/object:Gem::Dependency
113
+ name: json-jwt
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: 1.9.4
119
+ type: :runtime
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: 1.9.4
126
+ description: Specific to PRX. Will ignore tokens that were not issued by PRX.
127
+ email:
128
+ - eve@prx.org
129
+ - carhoden@gmail.com
130
+ executables: []
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - ".gitignore"
135
+ - ".travis.yml"
136
+ - CHANGELOG.md
137
+ - Gemfile
138
+ - Guardfile
139
+ - LICENSE
140
+ - README.md
141
+ - Rakefile
142
+ - lib/prx_auth.rb
143
+ - lib/prx_auth/resource_map.rb
144
+ - lib/prx_auth/scope_list.rb
145
+ - lib/prx_auth/version.rb
146
+ - lib/rack/prx_auth.rb
147
+ - lib/rack/prx_auth/certificate.rb
148
+ - lib/rack/prx_auth/token_data.rb
149
+ - lib/rack/prx_auth/version.rb
150
+ - prx_auth.gemspec
151
+ - test/prx_auth/resource_map_test.rb
152
+ - test/prx_auth/scope_list_test.rb
153
+ - test/rack/prx_auth/certificate_test.rb
154
+ - test/rack/prx_auth/token_data_test.rb
155
+ - test/rack/prx_auth_test.rb
156
+ - test/test_helper.rb
157
+ homepage: https://github.com/PRX/prx_auth
158
+ licenses:
159
+ - MIT
160
+ metadata: {}
161
+ post_install_message:
162
+ rdoc_options: []
163
+ require_paths:
164
+ - lib
165
+ required_ruby_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '2.3'
170
+ required_rubygems_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ requirements: []
176
+ rubygems_version: 3.0.1
177
+ signing_key:
178
+ specification_version: 4
179
+ summary: Utilites for parsing PRX JWTs and Rack middleware that verifies and attaches
180
+ the token's claims to env.
181
+ test_files:
182
+ - test/prx_auth/resource_map_test.rb
183
+ - test/prx_auth/scope_list_test.rb
184
+ - test/rack/prx_auth/certificate_test.rb
185
+ - test/rack/prx_auth/token_data_test.rb
186
+ - test/rack/prx_auth_test.rb
187
+ - test/test_helper.rb