prx_auth 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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