From cec9a29cfaec955a5135c36b473410e6d88b0c20 Mon Sep 17 00:00:00 2001 From: Bruno Costanzo Date: Tue, 3 Nov 2026 15:15:41 +0100 Subject: [PATCH] fix: with_token sends the original token through memoized API objects Client#with_token copies the client with dup. The copy kept the memoized API objects (@logical, @auth, ...), which still point at the original client, so a request through them went out with the original token. --- CHANGELOG.md | 4 ++++ lib/vault/client.rb | 8 ++++++++ spec/unit/client_spec.rb | 12 ++++++++++++ 3 files changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b742be7..7c6b22a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +BUG FIXES + +- `Client#with_token` now sends the given token from API objects (`#logical`, `#auth`, ...) that the original client had already used. Before, the copy reused them, and their requests went out with the original client's token. + ## v0.20.1 (March 31, 2026) BUG FIXES diff --git a/lib/vault/client.rb b/lib/vault/client.rb index 836aca3..58015ed 100644 --- a/lib/vault/client.rb +++ b/lib/vault/client.rb @@ -96,6 +96,14 @@ def initialize(options = {}) @nhp = nil end + # The API objects (#logical, #auth, ...) are memoized with a reference to + # the client that built them. A copy must build its own, or a request + # through the copy would use the original client's token. + def initialize_copy(other) + super + @logical = @auth = @auth_token = @auth_tls = @approle = @sys = @transform = nil + end + def pool @lock.synchronize do return @nhp if @nhp diff --git a/spec/unit/client_spec.rb b/spec/unit/client_spec.rb index 614f1d9..539153b 100644 --- a/spec/unit/client_spec.rb +++ b/spec/unit/client_spec.rb @@ -38,6 +38,18 @@ module Vault end expect(client.token).to eq("abcd-1234") end + + it "sends the new token from an API the original client already used" do + client = described_class.new(address: "http://vault.test", token: "abcd-1234") + stub_request(:get, "http://vault.test/v1/secret/foo") + .to_return(body: '{"data":{}}', headers: { "Content-Type" => "application/json" }) + client.logical.read("secret/foo") + + client.with_token("defg-5678") { |c| c.logical.read("secret/foo") } + + expect(a_request(:get, "http://vault.test/v1/secret/foo") + .with(headers: { "X-Vault-Token" => "defg-5678" })).to have_been_made.once + end end describe "#get" do