github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/secrets/service/manager.go (about)

     1  // Copyright 2016-2022, Pulumi Corporation.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package service implements support for the Pulumi Service secret manager.
    16  package service
    17  
    18  import (
    19  	"context"
    20  	"encoding/base64"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io/ioutil"
    24  
    25  	"github.com/pulumi/pulumi/pkg/v3/backend/httpstate/client"
    26  	"github.com/pulumi/pulumi/pkg/v3/secrets"
    27  	"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
    28  	"github.com/pulumi/pulumi/sdk/v3/go/common/diag/colors"
    29  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource/config"
    30  	"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
    31  	"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
    32  )
    33  
    34  const Type = "service"
    35  
    36  // serviceCrypter is an encrypter/decrypter that uses the Pulumi servce to encrypt/decrypt a stack's secrets.
    37  type serviceCrypter struct {
    38  	client *client.Client
    39  	stack  client.StackIdentifier
    40  }
    41  
    42  func newServiceCrypter(client *client.Client, stack client.StackIdentifier) config.Crypter {
    43  	return &serviceCrypter{client: client, stack: stack}
    44  }
    45  
    46  func (c *serviceCrypter) EncryptValue(ctx context.Context, plaintext string) (string, error) {
    47  	ciphertext, err := c.client.EncryptValue(ctx, c.stack, []byte(plaintext))
    48  	if err != nil {
    49  		return "", err
    50  	}
    51  	return base64.StdEncoding.EncodeToString(ciphertext), nil
    52  }
    53  
    54  func (c *serviceCrypter) DecryptValue(ctx context.Context, cipherstring string) (string, error) {
    55  	ciphertext, err := base64.StdEncoding.DecodeString(cipherstring)
    56  	if err != nil {
    57  		return "", err
    58  	}
    59  
    60  	plaintext, err := c.client.DecryptValue(ctx, c.stack, ciphertext)
    61  	if err != nil {
    62  		return "", err
    63  	}
    64  	return string(plaintext), nil
    65  }
    66  
    67  func (c *serviceCrypter) BulkDecrypt(ctx context.Context, secrets []string) (map[string]string, error) {
    68  	var secretsToDecrypt [][]byte
    69  	for _, val := range secrets {
    70  		ciphertext, err := base64.StdEncoding.DecodeString(val)
    71  		if err != nil {
    72  			return nil, err
    73  		}
    74  		secretsToDecrypt = append(secretsToDecrypt, ciphertext)
    75  	}
    76  
    77  	decryptedList, err := c.client.BulkDecryptValue(ctx, c.stack, secretsToDecrypt)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	decryptedSecrets := make(map[string]string)
    83  	for name, val := range decryptedList {
    84  		decryptedSecrets[name] = string(val)
    85  	}
    86  
    87  	return decryptedSecrets, nil
    88  }
    89  
    90  type serviceSecretsManagerState struct {
    91  	URL     string `json:"url,omitempty"`
    92  	Owner   string `json:"owner"`
    93  	Project string `json:"project"`
    94  	Stack   string `json:"stack"`
    95  }
    96  
    97  var _ secrets.Manager = &serviceSecretsManager{}
    98  
    99  type serviceSecretsManager struct {
   100  	state   serviceSecretsManagerState
   101  	crypter config.Crypter
   102  }
   103  
   104  func (sm *serviceSecretsManager) Type() string {
   105  	return Type
   106  }
   107  
   108  func (sm *serviceSecretsManager) State() interface{} {
   109  	return sm.state
   110  }
   111  
   112  func (sm *serviceSecretsManager) Decrypter() (config.Decrypter, error) {
   113  	contract.Assert(sm.crypter != nil)
   114  	return sm.crypter, nil
   115  }
   116  
   117  func (sm *serviceSecretsManager) Encrypter() (config.Encrypter, error) {
   118  	contract.Assert(sm.crypter != nil)
   119  	return sm.crypter, nil
   120  }
   121  
   122  func NewServiceSecretsManager(c *client.Client, id client.StackIdentifier) (secrets.Manager, error) {
   123  	return &serviceSecretsManager{
   124  		state: serviceSecretsManagerState{
   125  			URL:     c.URL(),
   126  			Owner:   id.Owner,
   127  			Project: id.Project,
   128  			Stack:   id.Stack,
   129  		},
   130  		crypter: newServiceCrypter(c, id),
   131  	}, nil
   132  }
   133  
   134  // NewServiceSecretsManagerFromState returns a Pulumi service-based secrets manager based on the
   135  // existing state.
   136  func NewServiceSecretsManagerFromState(state json.RawMessage) (secrets.Manager, error) {
   137  	var s serviceSecretsManagerState
   138  	if err := json.Unmarshal(state, &s); err != nil {
   139  		return nil, fmt.Errorf("unmarshalling state: %w", err)
   140  	}
   141  
   142  	account, err := workspace.GetAccount(s.URL)
   143  	if err != nil {
   144  		return nil, fmt.Errorf("getting access token: %w", err)
   145  	}
   146  	token := account.AccessToken
   147  
   148  	if token == "" {
   149  		return nil, fmt.Errorf("could not find access token for %s, have you logged in?", s.URL)
   150  	}
   151  
   152  	id := client.StackIdentifier{
   153  		Owner:   s.Owner,
   154  		Project: s.Project,
   155  		Stack:   s.Stack,
   156  	}
   157  	c := client.NewClient(s.URL, token, diag.DefaultSink(ioutil.Discard, ioutil.Discard, diag.FormatOptions{
   158  		Color: colors.Never}))
   159  
   160  	return &serviceSecretsManager{
   161  		state:   s,
   162  		crypter: newServiceCrypter(c, id),
   163  	}, nil
   164  }