Skip to content

Commit

Permalink
Fix up API and docs
Browse files Browse the repository at this point in the history
  • Loading branch information
sethvargo committed Jul 26, 2016
1 parent e1402ae commit c21189f
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 72 deletions.
31 changes: 19 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,22 +158,29 @@ secret.data #=> { :cooktime = >"11", :delicious => true }
```

### Response wrapping

```ruby
# Request new access token as wrapped response where the ttl of the temporary token is 500
wrapped_token_response = Vault.auth_token.create(wrap_ttl: 500)
# Unwrap wrapped response for final token using the initial temporary token
unwrapped_token_response = Vault.logical.unwrap(wrapped_token_response.wrap_info.token)
# Extract final token from response
unwrapped_token = unwrapped_token_response.data.auth.client_token
# Request new access token as wrapped response where the TTL of the temporary
# token is "5s".
wrapped = Vault.auth_token.create(wrap_ttl: "5s")

# Unwrap the wrapped response to get the final token using the initial temporary
# token from the first request.
unwrapped = Vault.logical.unwrap(wrapped.wrap_info.token)

# Extract the final token from the response.
token = unwrapped.data.auth.client_token
```

If the above detail is unnecessary there is a helper method 'unwrap_token' available
A helper function is also provided when unwrapping a token directly:

```ruby
# Request new access token as wrapped response where the ttl of the temporary token is 500
wrapped_token_response = Vault.auth_token.create(wrap_ttl: 500)
# Unwrap wrapped response for final token using the initial temporary token
unwrapped_token = Vault.logical.unwrap_token(wrapped_token_response)
# Request new access token as wrapped response where the TTL of the temporary
# token is "5s".
wrapped = Vault.auth_token.create(wrap_ttl: "5s")

# Unwrap wrapped response for final token using the initial temporary token.
token = Vault.logical.unwrap_token(wrapped)
```


Expand All @@ -188,4 +195,4 @@ Important Notes:
- **All new features must include test coverage.** At a bare minimum, Unit tests are required. It is preferred if you include acceptance tests as well.
- **The tests must be be idempotent.** The HTTP calls made during a test should be able to be run over and over.
- **Tests are order independent.** The default RSpec configuration randomizes the test order, so this should not be a problem.
- **Integration tests require Vault** Vault must be available in the path for the integration tests to pass.
- **Integration tests require Vault** Vault must be available in the path for the integration tests to pass.
56 changes: 39 additions & 17 deletions lib/vault/api/logical.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,21 +85,31 @@ def delete(path)
return true
end

# Unwrap the data stored against the given token. If the secret does not exist, +nil+
# will be returned.
# Unwrap the data stored against the given token. If the secret does not
# exist, `nil` will be returned.
#
# @example
# Vault.logical.unwrap("f363dba8-25a7-08c5-430c-00b2367124e6") #=> #<Vault::Secret lease_id="">
#
# @param [String] wrapper_token
# the token to unwrap
# @param [String] wrapper
# the token to use when unwrapping the value
#
# @return [Secret, nil]
def unwrap(wrapper_token)
json = client.get("/v1/cubbyhole/response", {}, { Vault::Client::TOKEN_HEADER => wrapper_token })
secret = Secret.decode(json)
secret.instance_variable_set("@data", Vault::Secret.new(JSON.parse(secret.data[:response], symbolize_names: true))) if secret.data
return secret
def unwrap(wrapper)
client.with_token(wrapper) do |client|
json = client.get("/v1/cubbyhole/response")
secret = Secret.decode(json)

# If there is nothing in the cubbyhole, return early.
if secret.nil? || secret.data.nil? || secret.data[:response].nil?
return nil
end

# Extract the response and parse it into a new secret.
json = JSON.parse(secret.data[:response], symbolize_names: true)
secret = Secret.decode(json)
return secret
end
rescue HTTPError => e
return nil if e.code == 404
raise
Expand All @@ -108,16 +118,28 @@ def unwrap(wrapper_token)
# Unwrap a token in a wrapped response given the temporary token.
#
# @example
# Vault.logical.unwrap("f363dba8-25a7-08c5-430c-00b2367124e6") #=> '0f0f40fd-06ce-4af1-61cb-cdc12796f42b'
# Vault.logical.unwrap("f363dba8-25a7-08c5-430c-00b2367124e6") #=> "0f0f40fd-06ce-4af1-61cb-cdc12796f42b"
#
# @param [String, Secret] wrapper_token
# the token to unwrap as a string or Vault::Secret response
# @param [String, Secret] wrapper
# the token to unwrap
#
# @return [String]
def unwrap_token(wrapper_token)
wrapper_token = wrapper_token.wrap_info.token if wrapper_token.is_a?(Vault::Secret) && wrapper_token.wrap_info
unwrapped_token_response = unwrap(wrapper_token)
return unwrapped_token_response.data.auth.client_token
# @return [String, nil]
def unwrap_token(wrapper)
# If provided a secret, grab the token. This is really just to make the
# API a bit nicer.
if wrapper.is_a?(Secret)
wrapper = wrapper.wrap_info.token
end

# Unwrap
response = unwrap(wrapper)

# If nothing was there, return nil
if response.nil? || response.auth.nil?
return nil
end

return response.auth.client_token
rescue HTTPError => e
raise
end
Expand Down
12 changes: 12 additions & 0 deletions lib/vault/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@ def initialize(options = {})
end
end

# Creates and yields a new client object with the given token. This may be
# used safely in a threadsafe manner because the original client remains
# unchanged. The value of the block is returned.
#
# @yield [Vault::Client]
def with_token(token)
client = self.dup
client.token = token
return yield client if block_given?
return nil
end

# Determine if the given options are the same as ours.
# @return [true, false]
def same_options?(opts)
Expand Down
71 changes: 28 additions & 43 deletions spec/integration/api/logical_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,62 +87,47 @@ module Vault
end

describe "#unwrap" do
it "preserves the original access token" do
original_token = Vault.token
expect { subject.unwrap('some token')}.to raise_error
expect(Vault.token).to eq(original_token)
end

it "returns the wrapped secret when it exists" do
original_token = vault_test_client.token
subject.write("secret/test-read", foo: "bar")
expect(subject.read("secret/test-read").data).to eq(foo: "bar")
wrapped = vault_test_client.auth_token.create(wrap_ttl: "5s")
unwrapped = subject.unwrap(wrapped.wrap_info.token)

expect {
wrapped_token_response = vault_test_client.auth_token.create({"display_name" => "", "num_uses" => 0, "renewable" => true, :wrap_ttl => 500})
unwrapped_token_response = subject.unwrap(wrapped_token_response.wrap_info.token)
# Verify quality of unwrapped token response
vault_test_client.token = unwrapped_token_response.data.auth.client_token
expect(subject.read("secret/test-read").data).to eq(foo: "bar")
}.to_not raise_error
vault_test_client.token = original_token
expect(unwrapped.auth).to be
expect(unwrapped.auth.client_token).to be

vault_test_client.with_token(unwrapped.auth.client_token) do |client|
expect { client.logical.read("secret/test") }.to_not raise_error
end
end
end

describe "#unwrap_token" do
it "preserves the original access token" do
original_token = Vault.token
expect { subject.unwrap_token('some token')}.to raise_error
expect(Vault.token).to eq(original_token)
end
it "returns the wrapped token when given a string" do
wrapped = vault_test_client.auth_token.create(wrap_ttl: "5s")
unwrapped = subject.unwrap_token(wrapped.wrap_info.token)

it "returns the wrapped token (as a string) when it exists" do
original_token = vault_test_client.token
subject.write("secret/test-read", foo: "bar")
expect(subject.read("secret/test-read").data).to eq(foo: "bar")
expect(unwrapped).to be

expect {
wrapped_token_response = vault_test_client.auth_token.create({"display_name" => "", "num_uses" => 0, "renewable" => true, :wrap_ttl => 500})
vault_test_client.token = subject.unwrap_token(wrapped_token_response.wrap_info.token)
expect(subject.read("secret/test-read").data).to eq(foo: "bar")
}.to_not raise_error
vault_test_client.token = original_token
vault_test_client.with_token(unwrapped) do |client|
expect { client.logical.read("secret/test") }.to_not raise_error
end
end

it "returns the wrapped token (as a Vault::Secret) when it exists" do
original_token = vault_test_client.token
subject.write("secret/test-read", foo: "bar")
expect(subject.read("secret/test-read").data).to eq(foo: "bar")
it "returns the wrapped token when given a Vault::Secret" do
wrapped = vault_test_client.auth_token.create(wrap_ttl: "5s")
unwrapped = subject.unwrap_token(wrapped)

expect {
wrapped_token_response = vault_test_client.auth_token.create({"display_name" => "", "num_uses" => 0, "renewable" => true, :wrap_ttl => 500})
vault_test_client.token = subject.unwrap_token(wrapped_token_response)
expect(subject.read("secret/test-read").data).to eq(foo: "bar")
}.to_not raise_error
vault_test_client.token = original_token
expect(unwrapped).to be

vault_test_client.with_token(unwrapped) do |client|
expect { client.logical.read("secret/test") }.to_not raise_error
end
end

it "returns nil when the response is empty" do
token = vault_test_client.auth_token.create # Note no wrap-ttl here
unwrapped = subject.unwrap_token(token.auth.client_token)
expect(unwrapped).to be(nil)
end
end

end
end
10 changes: 10 additions & 0 deletions spec/unit/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ module Vault
end
end

describe "#with_token" do
it "does not change the original client" do
token = client.token.dup
client.with_token("abcd-1234") do |c|
expect(c.token).to_not eq(token)
end
expect(client.token).to eq(token)
end
end

describe "#get" do
it "delegates to the #request method" do
expect(subject).to receive(:request).with(:get, "/foo", {}, {})
Expand Down

0 comments on commit c21189f

Please sign in to comment.