WarpEngine: let the host say who a browser is
The access policy asks who the caller is, and until now only a bearer token
could answer. That is what a desktop client carries — but a person clicking a
download link on the site carries a session instead, and the engine has no idea
what a session is. So a host that gated its catalog found its own signed-in
visitors refused at /api/download, which is a regression the shadowed route used
to hide.
c.subject_resolver is a callable taking the Rack request and returning the
host's subject: `->(request) { request.env["warden"]&.user }` for a Devise app.
Unset — every deployment today — a non-bearer request stays anonymous, exactly
as before. A resolver that raises is logged and treated as anonymous, because a
broken one turning every read into a 500 is worse than an anonymous request.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -196,4 +196,50 @@ RSpec.describe "Device sign-in", type: :request do
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
# A browser carries a session, not a bearer token, and the engine has no idea what a
|
||||
# session is. A host that wants its signed-in visitors recognised says how.
|
||||
describe "the host's own subject resolver" do
|
||||
let(:seen) { [] }
|
||||
let(:policy) do
|
||||
sink = seen
|
||||
Class.new do
|
||||
def initialize(sink) = @sink = sink
|
||||
def visible_software_scope(subject: nil) = WarpEngine::Software.all
|
||||
def access_for(software:, subject: nil)
|
||||
@sink << subject
|
||||
WarpEngine::Access::OPEN
|
||||
end
|
||||
def authorize_download(asset: nil, subject: nil, request: nil) = WarpEngine::Access::Grant::OPEN
|
||||
end.new(sink)
|
||||
end
|
||||
|
||||
before do
|
||||
create(:software)
|
||||
allow(WarpEngine.config).to receive(:access_policy).and_return(policy)
|
||||
end
|
||||
|
||||
it "is asked when there is no bearer token" do
|
||||
allow(WarpEngine.config).to receive(:subject_resolver).and_return(->(_request) { owner })
|
||||
|
||||
get "/api/software"
|
||||
|
||||
expect(seen).to eq([ owner ])
|
||||
end
|
||||
|
||||
it "leaves the request anonymous when the host configured none" do
|
||||
get "/api/software"
|
||||
|
||||
expect(seen).to eq([ nil ])
|
||||
end
|
||||
|
||||
it "answers anonymously rather than erroring when the resolver breaks" do
|
||||
allow(WarpEngine.config).to receive(:subject_resolver).and_return(->(_request) { raise "boom" })
|
||||
|
||||
get "/api/software"
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(seen).to eq([ nil ])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user