mirror of
https://github.com/fluxcd/flux2.git
synced 2026-05-23 09:55:55 +00:00
fix(cli): preserve password-only secret in flux create source git
`sourcesecret.buildGitSecret` previously wrote the Username and Password secret fields only when *both* were set. With the Azure DevOps PAT flow, `flux create source git --password=<pat>` has no username (the token is the credential), so both fields were silently dropped and the resulting secret was empty, breaking authentication. Write the two fields independently, so: - `--username` + `--password` -> username + password keys (unchanged); - `--password` alone -> password key only (fixes #3892); - `--username` alone -> username key only. The SSH-passphrase case also becomes simpler: the duplicated `if options.Password != ""` inside the keypair branch collapses into the top-level write. Adds a `buildGitSecret` unit test covering all four credential shapes, including the Azure DevOps PAT scenario. Closes #3892. Signed-off-by: SAY-5 <SAY-5@users.noreply.github.com>
This commit is contained in:
parent
4e78a9d7e0
commit
f399c72898
2 changed files with 71 additions and 5 deletions
|
|
@ -357,8 +357,14 @@ func buildGitSecret(keypair *ssh.KeyPair, hostKey []byte, options Options) (secr
|
|||
secret.Labels = options.Labels
|
||||
secret.StringData = map[string]string{}
|
||||
|
||||
if options.Username != "" && options.Password != "" {
|
||||
// Username and Password are written independently so that callers can
|
||||
// create a password-only secret (e.g. an Azure DevOps PAT, where the
|
||||
// username is unused). When a keypair is also present, Password acts as
|
||||
// the SSH private-key passphrase.
|
||||
if options.Username != "" {
|
||||
secret.StringData[UsernameSecretKey] = options.Username
|
||||
}
|
||||
if options.Password != "" {
|
||||
secret.StringData[PasswordSecretKey] = options.Password
|
||||
}
|
||||
if options.BearerToken != "" {
|
||||
|
|
@ -374,10 +380,6 @@ func buildGitSecret(keypair *ssh.KeyPair, hostKey []byte, options Options) (secr
|
|||
secret.StringData[PrivateKeySecretKey] = string(keypair.PrivateKey)
|
||||
secret.StringData[PublicKeySecretKey] = string(keypair.PublicKey)
|
||||
secret.StringData[KnownHostsSecretKey] = string(hostKey)
|
||||
// set password if present
|
||||
if options.Password != "" {
|
||||
secret.StringData[PasswordSecretKey] = string(options.Password)
|
||||
}
|
||||
}
|
||||
|
||||
return secret
|
||||
|
|
|
|||
|
|
@ -88,3 +88,67 @@ func Test_PasswordlessLoadKeyPair(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test_buildGitSecret_BasicAuthFields covers the regression reported in
|
||||
// https://github.com/fluxcd/flux2/issues/3892: providing just --password
|
||||
// (e.g. an Azure DevOps PAT, which ignores the username) must still
|
||||
// produce a secret containing the password field.
|
||||
func Test_buildGitSecret_BasicAuthFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
opts Options
|
||||
wantUsername string
|
||||
wantPassword string
|
||||
wantHasUser bool
|
||||
wantHasPass bool
|
||||
}{
|
||||
{
|
||||
name: "username and password",
|
||||
opts: Options{Username: "git", Password: "pw"},
|
||||
wantUsername: "git",
|
||||
wantPassword: "pw",
|
||||
wantHasUser: true,
|
||||
wantHasPass: true,
|
||||
},
|
||||
{
|
||||
name: "password only (Azure DevOps PAT)",
|
||||
opts: Options{Password: "pat-token"},
|
||||
wantPassword: "pat-token",
|
||||
wantHasUser: false,
|
||||
wantHasPass: true,
|
||||
},
|
||||
{
|
||||
name: "username only",
|
||||
opts: Options{Username: "git"},
|
||||
wantUsername: "git",
|
||||
wantHasUser: true,
|
||||
wantHasPass: false,
|
||||
},
|
||||
{
|
||||
name: "no credentials",
|
||||
opts: Options{},
|
||||
wantHasUser: false,
|
||||
wantHasPass: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
secret := buildGitSecret(nil, nil, tt.opts)
|
||||
gotUser, hasUser := secret.StringData[UsernameSecretKey]
|
||||
gotPass, hasPass := secret.StringData[PasswordSecretKey]
|
||||
if hasUser != tt.wantHasUser {
|
||||
t.Errorf("username presence = %v, want %v", hasUser, tt.wantHasUser)
|
||||
}
|
||||
if hasPass != tt.wantHasPass {
|
||||
t.Errorf("password presence = %v, want %v", hasPass, tt.wantHasPass)
|
||||
}
|
||||
if tt.wantHasUser && gotUser != tt.wantUsername {
|
||||
t.Errorf("username = %q, want %q", gotUser, tt.wantUsername)
|
||||
}
|
||||
if tt.wantHasPass && gotPass != tt.wantPassword {
|
||||
t.Errorf("password = %q, want %q", gotPass, tt.wantPassword)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue