176 lines
4.8 KiB
Go
176 lines
4.8 KiB
Go
// Copyright IBM Corp. 2020, 2026
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package proxy
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/hashicorp/boundary/api/targets"
|
|
"github.com/mitchellh/mapstructure"
|
|
)
|
|
|
|
const (
|
|
usernamePasswordCredentialType = "username_password"
|
|
passwordCredentialType = "password"
|
|
sshPrivateKeyCredentialType = "ssh_private_key"
|
|
)
|
|
|
|
// UsernamePassword contains username and password credentials
|
|
type UsernamePassword struct {
|
|
Username string `mapstructure:"username"`
|
|
Password string `mapstructure:"password"`
|
|
|
|
Raw *targets.SessionCredential
|
|
// Consumed can be set by the caller to indicate that the credential has
|
|
// been used, e.g. displayed to the user
|
|
Consumed bool
|
|
}
|
|
|
|
// Password contains a password credential
|
|
type Password struct {
|
|
Password string `mapstructure:"password"`
|
|
|
|
Raw *targets.SessionCredential
|
|
// Consumed can be set by the caller to indicate that the credential has
|
|
// been used, e.g. displayed to the user
|
|
Consumed bool
|
|
}
|
|
|
|
// SshPrivateKey contains the username and private key with optional passphrase
|
|
// for the key
|
|
type SshPrivateKey struct {
|
|
Username string `mapstructure:"username"`
|
|
PrivateKey string `mapstructure:"private_key"`
|
|
Passphrase string `mapstructure:"private_key_passphrase"`
|
|
|
|
Raw *targets.SessionCredential
|
|
// Consumed can be set by the caller to indicate that the credential has
|
|
// been used, e.g. displayed to the user
|
|
Consumed bool
|
|
}
|
|
|
|
type Credentials struct {
|
|
UsernamePassword []UsernamePassword
|
|
Password []Password
|
|
SshPrivateKey []SshPrivateKey
|
|
// Unspecified are credentials that do not match one of the types above
|
|
Unspecified []*targets.SessionCredential
|
|
}
|
|
|
|
func (c Credentials) UnconsumedSessionCredentials() []*targets.SessionCredential {
|
|
out := make([]*targets.SessionCredential, 0, len(c.SshPrivateKey)+len(c.UsernamePassword)+len(c.Unspecified))
|
|
|
|
// Unspecified credentials cannot be consumed
|
|
out = append(out, c.Unspecified...)
|
|
|
|
for _, c := range c.SshPrivateKey {
|
|
if !c.Consumed {
|
|
out = append(out, c.Raw)
|
|
}
|
|
}
|
|
for _, c := range c.UsernamePassword {
|
|
if !c.Consumed {
|
|
out = append(out, c.Raw)
|
|
}
|
|
}
|
|
for _, c := range c.Password {
|
|
if !c.Consumed {
|
|
out = append(out, c.Raw)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func ParseCredentials(creds []*targets.SessionCredential) (Credentials, error) {
|
|
if creds == nil {
|
|
return Credentials{}, nil
|
|
}
|
|
var out Credentials
|
|
for _, cred := range creds {
|
|
if cred.CredentialSource == nil {
|
|
return Credentials{}, errors.New("missing credential source")
|
|
}
|
|
|
|
var upCred UsernamePassword
|
|
var pCred Password
|
|
var spkCred SshPrivateKey
|
|
switch cred.CredentialSource.CredentialType {
|
|
case usernamePasswordCredentialType:
|
|
// Decode attributes from credential struct
|
|
if err := mapstructure.Decode(cred.Credential, &upCred); err != nil {
|
|
return Credentials{}, err
|
|
}
|
|
|
|
if upCred.Username != "" && upCred.Password != "" {
|
|
upCred.Raw = cred
|
|
out.UsernamePassword = append(out.UsernamePassword, upCred)
|
|
continue
|
|
}
|
|
|
|
case passwordCredentialType:
|
|
// Decode attributes from credential struct
|
|
if err := mapstructure.Decode(cred.Credential, &pCred); err != nil {
|
|
return Credentials{}, err
|
|
}
|
|
|
|
if pCred.Password != "" {
|
|
pCred.Raw = cred
|
|
out.Password = append(out.Password, pCred)
|
|
continue
|
|
}
|
|
|
|
case sshPrivateKeyCredentialType:
|
|
// Decode attributes from credential struct
|
|
if err := mapstructure.Decode(cred.Credential, &spkCred); err != nil {
|
|
return Credentials{}, err
|
|
}
|
|
|
|
if spkCred.Username != "" && spkCred.PrivateKey != "" {
|
|
spkCred.Raw = cred
|
|
out.SshPrivateKey = append(out.SshPrivateKey, spkCred)
|
|
continue
|
|
}
|
|
}
|
|
|
|
// Credential type is unspecified, make a best effort attempt to parse
|
|
// a credential from the Decoded field if it exists
|
|
if cred.Secret != nil && cred.Secret.Decoded != nil {
|
|
// Attempt unmarshaling into username password creds
|
|
if err := mapstructure.Decode(cred.Secret.Decoded, &upCred); err != nil {
|
|
return Credentials{}, err
|
|
}
|
|
if upCred.Username != "" && upCred.Password != "" {
|
|
upCred.Raw = cred
|
|
out.UsernamePassword = append(out.UsernamePassword, upCred)
|
|
continue
|
|
}
|
|
|
|
// Attempt unmarshaling into password creds
|
|
if err := mapstructure.Decode(cred.Secret.Decoded, &pCred); err != nil {
|
|
return Credentials{}, err
|
|
}
|
|
if pCred.Password != "" {
|
|
pCred.Raw = cred
|
|
out.Password = append(out.Password, pCred)
|
|
continue
|
|
}
|
|
|
|
// Attempt unmarshaling into ssh private key creds
|
|
if err := mapstructure.Decode(cred.Secret.Decoded, &spkCred); err != nil {
|
|
return Credentials{}, err
|
|
}
|
|
if spkCred.Username != "" && spkCred.PrivateKey != "" {
|
|
spkCred.Raw = cred
|
|
out.SshPrivateKey = append(out.SshPrivateKey, spkCred)
|
|
continue
|
|
}
|
|
}
|
|
|
|
// We could not parse the credential
|
|
out.Unspecified = append(out.Unspecified, cred)
|
|
}
|
|
|
|
return out, nil
|
|
}
|