Skip to content

Commit

Permalink
Merge 98f3062 into c73e3a9
Browse files Browse the repository at this point in the history
  • Loading branch information
cqr committed Apr 10, 2020
2 parents c73e3a9 + 98f3062 commit d58cf28
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 32 deletions.
7 changes: 7 additions & 0 deletions lib/prx_auth.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require 'prx_auth/scope_list'
require 'prx_auth/resource_map'
require 'rack/prx_auth/version'

module PrxAuth
VERSION = Rack::PrxAuth::VERSION
end
33 changes: 33 additions & 0 deletions lib/prx_auth/resource_map.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module PrxAuth
class ResourceMap
WILDCARD_KEY = '*'

def initialize(mapped_values)
@map = Hash[mapped_values.map do |(key, values)|
[key, ScopeList.new(values)]
end]
end

def contains?(resource, namespace=nil, scope=nil)
mapped_resource = @map[resource.to_s]

if mapped_resource == wildcard_resource
raise ArgumentError if namespace.nil?

mapped_resource.contains?(namespace, scope)
elsif mapped_resource && !namespace.nil?
mapped_resource.contains?(namespace, scope) || wildcard_resource.contains?(namespace, scope)
elsif !namespace.nil?
wildcard_resource.contains?(namespace, scope)
else
!!mapped_resource
end
end

private

def wildcard_resource
@wildcard_resource ||= @map[WILDCARD_KEY] || ScopeList.new('')
end
end
end
53 changes: 53 additions & 0 deletions lib/prx_auth/scope_list.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module PrxAuth
class ScopeList
SCOPE_SEPARATOR = ' '
NAMESPACE_SEPARATOR = ':'
NO_NAMESPACE = :_

def initialize(list)
@string = list
end

def contains?(namespace, scope=nil)
scope, namespace = namespace, NO_NAMESPACE if scope.nil?

if namespace == NO_NAMESPACE
map[namespace].include?(symbolize(scope))
else
symbolized_scope = symbolize(scope)
map[namespace].include?(symbolized_scope) || map[NO_NAMESPACE].include?(symbolized_scope)
end
end

private

def map
@parsed_map ||= empty_map.tap do |map|
@string.split(SCOPE_SEPARATOR).each do |value|
next if value.length < 1

parts = value.split(NAMESPACE_SEPARATOR, 2)
if parts.length == 2
map[symbolize(parts[0])] << symbolize(parts[1])
else
map[NO_NAMESPACE] << symbolize(parts[0])
end
end
end
end

def empty_map
@empty_map ||= Hash.new do |hash, key|
hash[key] = []
end
end

def symbolize(value)
case value
when Symbol then value
when String then value.downcase.gsub('-', '_').intern
else symbolize value.to_s
end
end
end
end
1 change: 1 addition & 0 deletions lib/rack/prx_auth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require 'rack/prx_auth/version'
require 'rack/prx_auth/certificate'
require 'rack/prx_auth/token_data'
require 'prx_auth'

module Rack
class PrxAuth
Expand Down
2 changes: 1 addition & 1 deletion lib/rack/prx_auth/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module Rack
class PrxAuth
VERSION = "0.3.0"
VERSION = "1.0.0"
end
end
61 changes: 61 additions & 0 deletions test/prx_auth/resource_map_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
require 'test_helper'

describe PrxAuth::ResourceMap do
let(:map) { PrxAuth::ResourceMap.new(resources) }
let(:resources) { {'123' => 'admin one two three ns1:namespaced', '456' => 'member four five six' } }

describe '#authorized?' do
it 'contains scopes in list' do
assert map.contains?(123, :admin)
end

it 'does not include across aur limits' do
assert !map.contains?(123, :member)
end

it 'does not require a scope' do
assert map.contains?(123)
end

it 'does not match if it hasnt seen the resource' do
assert !map.contains?(789)
end

it 'works with namespaced scopes' do
assert map.contains?(123, :ns1, :namespaced)
end

describe 'with wildcard resource' do
let(:resources) do
{
'*' => 'peek',
'123' => 'admin one two three',
'456' => 'member four five six'
}
end

it 'applies wildcard lists to queries with no matching value' do
assert map.contains?(789, :peek)
end

it 'does not scan unscoped for wildcard resources' do
assert !map.contains?(789)
end

it 'allows querying by wildcard resource directly' do
assert map.contains?('*', :peek)
assert !map.contains?('*', :admin)
end

it 'treats wildcard lists as additive to other explicit ones' do
assert map.contains?(123, :peek)
end

it 'refuses to run against wildcard with no scope' do
assert_raises ArgumentError do
map.contains?('*')
end
end
end
end
end
41 changes: 41 additions & 0 deletions test/prx_auth/scope_list_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require 'test_helper'

describe PrxAuth::ScopeList do
let (:scopes) { 'read write sell top-up' }
let (:list) { PrxAuth::ScopeList.new(scopes) }

it 'looks up successfully for a given scope' do
assert list.contains?('write')
end

it 'scans for symbols' do
assert list.contains?(:read)
end

it 'handles hyphen to underscore conversions' do
assert list.contains?(:top_up)
end

it 'fails for contents not in the list' do
assert !list.contains?(:buy)
end

describe 'with namespace' do
let (:scopes) { 'ns1:hello ns2:goodbye aloha' }

it 'works for namespaced lookups' do
assert list.contains?(:ns1, :hello)
end

it 'fails when the wrong namespace is passed' do
assert !list.contains?(:ns1, :goodbye)
end

it 'looks up global scopes when namespaced fails' do
assert list.contains?(:ns1, :aloha)
assert list.contains?(:ns3, :aloha)
end

end

end
30 changes: 15 additions & 15 deletions test/rack/prx_auth/certificate_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
describe '#initialize' do
it 'allows setting the location of the certificates' do
cert = Rack::PrxAuth::Certificate.new('http://example.com')
cert.cert_location.must_equal URI('http://example.com')
assert cert.cert_location == URI('http://example.com')
end

it 'defaults to DEFAULT_CERT_LOC' do
certificate.cert_location.must_equal Rack::PrxAuth::Certificate::DEFAULT_CERT_LOC
assert certificate.cert_location == Rack::PrxAuth::Certificate::DEFAULT_CERT_LOC
end
end

Expand All @@ -24,24 +24,24 @@
end
end

token.must_equal :token
key.must_equal :public_key
assert token == :token
assert key == :public_key
end

it 'returns false if verification fails' do
JSON::JWT.stub(:decode, Proc.new do |t, k|
raise JSON::JWT::VerificationFailed
end) do
certificate.stub(:public_key, :foo) do
certificate.wont_be :valid?, :token
assert !certificate.valid?(:token)
end
end
end

it 'returns true if verification passes' do
JSON::JWT.stub(:decode, {}) do
certificate.stub(:public_key, :foo) do
certificate.must_be :valid?, :token
assert certificate.valid?(:token)
end
end
end
Expand All @@ -53,14 +53,14 @@ def certificate.fetch
:sigil
end

certificate.send(:certificate).must_equal :sigil
assert certificate.send(:certificate) == :sigil
end
end

describe '#public_key' do
it 'pulls from the certificate' do
certificate.stub(:certificate, Struct.new(:public_key).new(:key)) do
certificate.send(:public_key).must_equal :key
assert certificate.send(:public_key) == :key
end
end
end
Expand All @@ -70,7 +70,7 @@ def certificate.fetch
Net::HTTP.stub(:get, ->(x) { "{\"certificates\":{\"asdf\":\"#{x}\"}}" }) do
OpenSSL::X509::Certificate.stub(:new, ->(x) { x }) do
certificate.stub(:cert_location, "a://fake.url/here") do
certificate.send(:fetch).must_equal "a://fake.url/here"
assert certificate.send(:fetch) == "a://fake.url/here"
end
end
end
Expand All @@ -80,7 +80,7 @@ def certificate.fetch
Net::HTTP.stub(:get, ->(x) { "{\"certificates\":{\"asdf\":\"#{x}\"}}" }) do
OpenSSL::X509::Certificate.stub(:new, ->(_) { Struct.new(:not_after).new(Time.now + 10000) }) do
certificate.send :certificate
certificate.wont_be :needs_refresh?
assert !certificate.send(:needs_refresh?)
end
end
end
Expand All @@ -93,12 +93,12 @@ def certificate.fetch
end

it 'is false when the certificate is not expired' do
certificate.wont_be :expired?
assert !certificate.send(:expired?)
end

it 'is true when the certificate is expired' do
stub_cert.not_after = Time.now - 500
certificate.must_be :expired?
assert certificate.send(:expired?)
end
end

Expand All @@ -109,21 +109,21 @@ def refresh_at=(time)

it 'is true if certificate is expired' do
certificate.stub(:expired?, true) do
certificate.must_be :needs_refresh?
assert certificate.send(:needs_refresh?)
end
end

it 'is true if we are past refresh value' do
self.refresh_at = Time.now.to_i - 1000
certificate.stub(:expired?, false) do
certificate.must_be :needs_refresh?
assert certificate.send(:needs_refresh?)
end
end

it 'is false if certificate is not expired and refresh is in the future' do
self.refresh_at = Time.now.to_i + 10000
certificate.stub(:expired?, false) do
certificate.wont_be :needs_refresh?
assert !certificate.send(:needs_refresh?)
end
end
end
Expand Down
Loading

0 comments on commit d58cf28

Please sign in to comment.