Skip to content
This repository was archived by the owner on Apr 16, 2026. It is now read-only.

Commit d87047c

Browse files
committed
Fix vaultdb DSN escaping and lease cleanup
1 parent 79fd5b5 commit d87047c

2 files changed

Lines changed: 28 additions & 14 deletions

File tree

internal/connectors/vaultdb/connector.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,14 @@ func (c *Connector) Issue(ctx context.Context, req core.IssueRequest) (*core.Iss
113113
if err != nil {
114114
return nil, err
115115
}
116-
lease, leaseExpiresAt, err := c.extendLeaseForGrant(ctx, lease, req.Grant.ExpiresAt)
116+
extendedLease, leaseExpiresAt, err := c.extendLeaseForGrant(ctx, lease, req.Grant.ExpiresAt)
117117
if err != nil {
118+
if lease != nil && lease.LeaseID != "" {
119+
_ = c.client.RevokeLease(ctx, lease.LeaseID)
120+
}
118121
return nil, err
119122
}
123+
lease = extendedLease
120124
dsn, err := renderDSN(c.roleDSNs[req.Resource.Name], lease)
121125
if err != nil {
122126
return nil, err
@@ -192,8 +196,8 @@ func renderDSN(renderPattern string, lease *LeaseCredentials) (string, error) {
192196
}
193197
var builder strings.Builder
194198
if err := tpl.Execute(&builder, map[string]string{
195-
"username": url.QueryEscape(lease.Username),
196-
"password": url.QueryEscape(lease.Password),
199+
"username": url.User(lease.Username).String(),
200+
"password": strings.TrimPrefix(url.UserPassword("", lease.Password).String(), ":"),
197201
}); err != nil {
198202
return "", fmt.Errorf("%w: render dsn template: %v", core.ErrInvalidRequest, err)
199203
}

internal/connectors/vaultdb/connector_test.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ func TestConnector_IssueAndRevokeDynamicCredentials(t *testing.T) {
1515

1616
client := &fakeVaultClient{
1717
lease: &vaultdb.LeaseCredentials{
18-
Username: "v-token-user",
19-
Password: "secret:/?#[]@",
18+
Username: "v token user",
19+
Password: "secret with spaces:/?#[]@",
2020
LeaseID: "database/creds/analytics_ro/123",
2121
LeaseDuration: 10 * time.Minute,
2222
Renewable: true,
@@ -56,9 +56,15 @@ func TestConnector_IssueAndRevokeDynamicCredentials(t *testing.T) {
5656
if issued.Metadata["lease_expires_at"] == "" {
5757
t.Fatalf("issued artifact = %#v, want lease expiry metadata", issued)
5858
}
59-
if strings.Contains(issued.SecretData["dsn"], "secret:/?#[]@") {
59+
if strings.Contains(issued.SecretData["dsn"], "v token user") || strings.Contains(issued.SecretData["dsn"], "secret with spaces:/?#[]@") {
6060
t.Fatalf("dsn = %q, want escaped credentials", issued.SecretData["dsn"])
6161
}
62+
if strings.Contains(issued.SecretData["dsn"], "+") {
63+
t.Fatalf("dsn = %q, want spaces percent-encoded in userinfo", issued.SecretData["dsn"])
64+
}
65+
if want := "postgres://v%20token%20user:secret%20with%20spaces%3A%2F%3F%23%5B%5D%40@db.internal:5432/analytics?sslmode=require"; issued.SecretData["dsn"] != want {
66+
t.Fatalf("dsn = %q, want %q", issued.SecretData["dsn"], want)
67+
}
6268

6369
if err := connector.Revoke(context.Background(), core.RevokeRequest{
6470
Session: &core.Session{ID: "sess_db", TenantID: "t_acme"},
@@ -187,15 +193,16 @@ func TestConnector_IssueRenewsLeaseToMatchGrantTTL(t *testing.T) {
187193
func TestConnector_IssueFailsWhenGrantTTLExceedsNonRenewableLease(t *testing.T) {
188194
t.Parallel()
189195

190-
connector, err := vaultdb.NewConnector(vaultdb.Config{
191-
Client: &fakeVaultClient{
192-
lease: &vaultdb.LeaseCredentials{
193-
Username: "dyn-user",
194-
Password: "dyn-pass",
195-
LeaseID: "database/creds/analytics_ro/123",
196-
LeaseDuration: time.Minute,
197-
},
196+
client := &fakeVaultClient{
197+
lease: &vaultdb.LeaseCredentials{
198+
Username: "dyn-user",
199+
Password: "dyn-pass",
200+
LeaseID: "database/creds/analytics_ro/123",
201+
LeaseDuration: time.Minute,
198202
},
203+
}
204+
connector, err := vaultdb.NewConnector(vaultdb.Config{
205+
Client: client,
199206
RoleDSNs: map[string]string{
200207
"analytics_ro": "postgres://{{username}}:{{password}}@db.internal:5432/analytics?sslmode=require",
201208
},
@@ -219,6 +226,9 @@ func TestConnector_IssueFailsWhenGrantTTLExceedsNonRenewableLease(t *testing.T)
219226
if err == nil || !strings.Contains(err.Error(), "not renewable") {
220227
t.Fatalf("Issue() error = %v, want renewal failure", err)
221228
}
229+
if client.revokedLeaseID != "database/creds/analytics_ro/123" {
230+
t.Fatalf("revoked lease = %q, want original lease revoked on failure", client.revokedLeaseID)
231+
}
222232
}
223233

224234
type fakeVaultClient struct {

0 commit comments

Comments
 (0)