* fix: Cred rotation - fix bug where the rotated service account key was not read. The response needs to be unmarshalled to pull out the values from th fields - fix bug where the time stamp was not getting updated after rotating credentials - fix bug where updates without setting a new secret fails
169 lines
6.0 KiB
Go
169 lines
6.0 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package credential
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/boundary-plugin-gcp/internal/errors"
|
|
"github.com/hashicorp/boundary-plugin-gcp/internal/values"
|
|
"google.golang.org/protobuf/types/known/structpb"
|
|
)
|
|
|
|
// CredentialAttributes contain attributes used for authenticating to GCP
|
|
// and accessing a list of instances
|
|
type CredentialAttributes struct {
|
|
// ProjectId is the project id associated with the GCP credentials
|
|
ProjectId string
|
|
|
|
// Zone is the zone associated with the GCP credentials
|
|
Zone string
|
|
|
|
// DisableCredentialRotation disables the rotation of GCP service account key associated with the plugin
|
|
DisableCredentialRotation bool
|
|
|
|
// ClientEmail is the email associated with the GCP cloud credentials
|
|
ClientEmail string
|
|
|
|
// TargetServiceAccount is the unique identifier for the service account that will be impersonated
|
|
TargetServiceAccountId string
|
|
}
|
|
|
|
// GetCredentialAttributes checks attributes required by GCP to access
|
|
// a list of instances and populate them into Boundary's host catalog
|
|
func GetCredentialAttributes(in *structpb.Struct) (*CredentialAttributes, error) {
|
|
badFields := make(map[string]string)
|
|
|
|
projectId, err := values.GetStringValue(in, ConstProjectId, true)
|
|
if err != nil {
|
|
badFields[fmt.Sprintf("attributes.%s", ConstProjectId)] = err.Error()
|
|
}
|
|
|
|
zone, err := values.GetStringValue(in, ConstZone, true)
|
|
if err != nil {
|
|
badFields[fmt.Sprintf("attributes.%s", ConstZone)] = err.Error()
|
|
}
|
|
|
|
disableCredentialRotation, err := values.GetBoolValue(in, ConstDisableCredentialRotation, false)
|
|
if err != nil {
|
|
badFields[fmt.Sprintf("attributes.%s", ConstDisableCredentialRotation)] = err.Error()
|
|
}
|
|
|
|
clientEmail, err := values.GetStringValue(in, ConstClientEmail, false)
|
|
if err != nil {
|
|
badFields[fmt.Sprintf("attributes.%s", ConstClientEmail)] = err.Error()
|
|
}
|
|
|
|
targetServiceAccountId, err := values.GetStringValue(in, ConstTargetServiceAccountID, false)
|
|
if err != nil {
|
|
badFields[fmt.Sprintf("attributes.%s", ConstTargetServiceAccountID)] = err.Error()
|
|
}
|
|
|
|
if len(badFields) > 0 {
|
|
return nil, errors.InvalidArgumentError("Error in the attributes provider", badFields)
|
|
}
|
|
|
|
return &CredentialAttributes{
|
|
ProjectId: projectId,
|
|
Zone: zone,
|
|
DisableCredentialRotation: disableCredentialRotation,
|
|
ClientEmail: clientEmail,
|
|
TargetServiceAccountId: targetServiceAccountId,
|
|
}, nil
|
|
}
|
|
|
|
// GetCredentialsConfig parses values out of a protobuf struct secrets and returns a
|
|
// Config used for configuring an GCP session. An status error is returned
|
|
// with an InvalidArgument code if any unrecognized fields are found in the protobuf
|
|
// struct input.
|
|
func GetCredentialsConfig(secrets *structpb.Struct, attrs *CredentialAttributes) (*Config, error) {
|
|
// initialize secrets if it is nil
|
|
// secrets can be nil if the authentication type is Application Default Credential(ADC)
|
|
if secrets == nil {
|
|
secrets = &structpb.Struct{
|
|
Fields: make(map[string]*structpb.Value),
|
|
}
|
|
}
|
|
|
|
// initialize attrs if it is nil
|
|
// attributes can be nil if the authentication type is Application Default Credential(ADC)
|
|
// where the credentials are inferred from the environment and no fields are provided
|
|
// https://cloud.google.com/docs/authentication/application-default-credentials
|
|
if attrs == nil {
|
|
attrs = &CredentialAttributes{}
|
|
}
|
|
|
|
unknownFields := values.StructFields(secrets)
|
|
badFields := make(map[string]string)
|
|
|
|
privateKeyId, err := values.GetStringValue(secrets, ConstPrivateKeyId, false)
|
|
if err != nil {
|
|
badFields[fmt.Sprintf("secrets.%s", ConstPrivateKeyId)] = err.Error()
|
|
}
|
|
delete(unknownFields, ConstPrivateKeyId)
|
|
|
|
privateKey, err := values.GetStringValue(secrets, ConstPrivateKey, false)
|
|
if err != nil {
|
|
badFields[fmt.Sprintf("secrets.%s", ConstPrivateKey)] = err.Error()
|
|
}
|
|
delete(unknownFields, ConstPrivateKey)
|
|
|
|
delete(unknownFields, ConstCredsLastRotatedTime)
|
|
for s := range unknownFields {
|
|
badFields[fmt.Sprintf("secrets.%s", s)] = "unrecognized field"
|
|
}
|
|
|
|
switch {
|
|
// service account impersonation authentication requires the target service account id and client_email
|
|
case attrs.TargetServiceAccountId != "" && attrs.ClientEmail == "":
|
|
badFields[fmt.Sprintf("secrets.%s", ConstPrivateKey)] = "must not be empty when target service account id is set"
|
|
// service account impersonation authentication requires the target service account id and private key
|
|
case attrs.TargetServiceAccountId != "" && privateKey == "":
|
|
badFields[fmt.Sprintf("attributes.%s", ConstClientEmail)] = "must not be empty when target service account id is set"
|
|
// service account key credentials requires the private key and client email
|
|
case privateKey != "" && attrs.ClientEmail == "":
|
|
badFields[fmt.Sprintf("attributes.%s", ConstClientEmail)] = "must not be empty when private key is set"
|
|
// service account key credentials requires the client_email
|
|
case attrs.ClientEmail != "" && privateKey == "":
|
|
badFields[fmt.Sprintf("secrets.%s", ConstPrivateKey)] = "must not be empty when client email is set"
|
|
}
|
|
|
|
if len(badFields) > 0 {
|
|
return nil, errors.InvalidArgumentError("Error in the secrets provided", badFields)
|
|
}
|
|
|
|
cfgOpts := []Option{}
|
|
if privateKeyId != "" && privateKey != "" {
|
|
cfgOpts = append(cfgOpts,
|
|
WithPrivateKey(privateKey),
|
|
WithPrivateKeyId(privateKeyId))
|
|
}
|
|
|
|
if attrs.ProjectId != "" {
|
|
cfgOpts = append(cfgOpts, WithProjectId(attrs.ProjectId))
|
|
}
|
|
if attrs.ClientEmail != "" {
|
|
cfgOpts = append(cfgOpts, WithClientEmail(attrs.ClientEmail))
|
|
}
|
|
if attrs.Zone != "" {
|
|
cfgOpts = append(cfgOpts, WithZone(attrs.Zone))
|
|
}
|
|
if privateKey != "" {
|
|
cfgOpts = append(cfgOpts, WithPrivateKey(privateKey))
|
|
}
|
|
if privateKeyId != "" {
|
|
cfgOpts = append(cfgOpts, WithPrivateKeyId(privateKeyId))
|
|
}
|
|
if attrs.TargetServiceAccountId != "" {
|
|
cfgOpts = append(cfgOpts, WithTargetServiceAccountId(attrs.TargetServiceAccountId))
|
|
}
|
|
|
|
credentialsConfig, err := NewConfig(cfgOpts...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating credentials config: %w", err)
|
|
}
|
|
|
|
return credentialsConfig, nil
|
|
}
|