You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
boundary/api/proxy/credentials.go

136 lines
3.8 KiB

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package proxy
import (
"errors"
"github.com/hashicorp/boundary/api/targets"
"github.com/mitchellh/mapstructure"
)
const (
usernamePasswordCredentialType = "username_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
}
// 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
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)
}
}
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 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 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 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
}