github.com/thiagoyeds/go-cloud@v0.26.0/secrets/secrets.go (about)

     1  // Copyright 2019 The Go Cloud Development Kit Authors
     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  //     https://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 secrets provides an easy and portable way to encrypt and decrypt
    16  // messages. Subpackages contain driver implementations of
    17  // secrets for supported services.
    18  //
    19  // See https://gocloud.dev/howto/secrets/ for a detailed how-to guide.
    20  //
    21  //
    22  // OpenCensus Integration
    23  //
    24  // OpenCensus supports tracing and metric collection for multiple languages and
    25  // backend providers. See https://opencensus.io.
    26  //
    27  // This API collects OpenCensus traces and metrics for the following methods:
    28  //  - Encrypt
    29  //  - Decrypt
    30  // All trace and metric names begin with the package import path.
    31  // The traces add the method name.
    32  // For example, "gocloud.dev/secrets/Encrypt".
    33  // The metrics are "completed_calls", a count of completed method calls by driver,
    34  // method and status (error code); and "latency", a distribution of method latency
    35  // by driver and method.
    36  // For example, "gocloud.dev/secrets/latency".
    37  //
    38  // To enable trace collection in your application, see "Configure Exporter" at
    39  // https://opencensus.io/quickstart/go/tracing.
    40  // To enable metric collection in your application, see "Exporting stats" at
    41  // https://opencensus.io/quickstart/go/metrics.
    42  package secrets // import "gocloud.dev/secrets"
    43  
    44  import (
    45  	"context"
    46  	"net/url"
    47  	"sync"
    48  
    49  	"gocloud.dev/internal/gcerr"
    50  	"gocloud.dev/internal/oc"
    51  	"gocloud.dev/internal/openurl"
    52  	"gocloud.dev/secrets/driver"
    53  )
    54  
    55  // Keeper does encryption and decryption. To create a Keeper, use constructors
    56  // found in driver subpackages.
    57  type Keeper struct {
    58  	k      driver.Keeper
    59  	tracer *oc.Tracer
    60  
    61  	// mu protects the closed variable.
    62  	// Read locks are kept to allow holding a read lock for long-running calls,
    63  	// and thereby prevent closing until a call finishes.
    64  	mu     sync.RWMutex
    65  	closed bool
    66  }
    67  
    68  // NewKeeper is intended for use by drivers only. Do not use in application code.
    69  var NewKeeper = newKeeper
    70  
    71  // newKeeper creates a Keeper.
    72  func newKeeper(k driver.Keeper) *Keeper {
    73  	return &Keeper{
    74  		k: k,
    75  		tracer: &oc.Tracer{
    76  			Package:        pkgName,
    77  			Provider:       oc.ProviderName(k),
    78  			LatencyMeasure: latencyMeasure,
    79  		},
    80  	}
    81  }
    82  
    83  const pkgName = "gocloud.dev/secrets"
    84  
    85  var (
    86  	latencyMeasure = oc.LatencyMeasure(pkgName)
    87  
    88  	// OpenCensusViews are predefined views for OpenCensus metrics.
    89  	// The views include counts and latency distributions for API method calls.
    90  	// See the example at https://godoc.org/go.opencensus.io/stats/view for usage.
    91  	OpenCensusViews = oc.Views(pkgName, latencyMeasure)
    92  )
    93  
    94  // Encrypt encrypts the plaintext and returns the cipher message.
    95  func (k *Keeper) Encrypt(ctx context.Context, plaintext []byte) (ciphertext []byte, err error) {
    96  	ctx = k.tracer.Start(ctx, "Encrypt")
    97  	defer func() { k.tracer.End(ctx, err) }()
    98  
    99  	k.mu.RLock()
   100  	defer k.mu.RUnlock()
   101  	if k.closed {
   102  		return nil, errClosed
   103  	}
   104  
   105  	b, err := k.k.Encrypt(ctx, plaintext)
   106  	if err != nil {
   107  		return nil, wrapError(k, err)
   108  	}
   109  	return b, nil
   110  }
   111  
   112  // Decrypt decrypts the ciphertext and returns the plaintext.
   113  func (k *Keeper) Decrypt(ctx context.Context, ciphertext []byte) (plaintext []byte, err error) {
   114  	ctx = k.tracer.Start(ctx, "Decrypt")
   115  	defer func() { k.tracer.End(ctx, err) }()
   116  
   117  	k.mu.RLock()
   118  	defer k.mu.RUnlock()
   119  	if k.closed {
   120  		return nil, errClosed
   121  	}
   122  
   123  	b, err := k.k.Decrypt(ctx, ciphertext)
   124  	if err != nil {
   125  		return nil, wrapError(k, err)
   126  	}
   127  	return b, nil
   128  }
   129  
   130  var errClosed = gcerr.Newf(gcerr.FailedPrecondition, nil, "secrets: Keeper has been closed")
   131  
   132  // Close releases any resources used for the Keeper.
   133  func (k *Keeper) Close() error {
   134  	k.mu.Lock()
   135  	prev := k.closed
   136  	k.closed = true
   137  	k.mu.Unlock()
   138  	if prev {
   139  		return errClosed
   140  	}
   141  	return wrapError(k, k.k.Close())
   142  }
   143  
   144  // ErrorAs converts i to driver-specific types. See
   145  // https://gocloud.dev/concepts/as/ for background information and the
   146  // driver package documentation for the specific types supported for
   147  // that driver.
   148  //
   149  // ErrorAs panics if i is nil or not a pointer.
   150  // ErrorAs returns false if err == nil.
   151  func (k *Keeper) ErrorAs(err error, i interface{}) bool {
   152  	return gcerr.ErrorAs(err, i, k.k.ErrorAs)
   153  }
   154  
   155  func wrapError(k *Keeper, err error) error {
   156  	if err == nil {
   157  		return nil
   158  	}
   159  	if gcerr.DoNotWrap(err) {
   160  		return err
   161  	}
   162  	return gcerr.New(k.k.ErrorCode(err), err, 2, "secrets")
   163  }
   164  
   165  // KeeperURLOpener represents types that can open Keepers based on a URL.
   166  // The opener must not modify the URL argument. OpenKeeperURL must be safe to
   167  // call from multiple goroutines.
   168  //
   169  // This interface is generally implemented by types in driver packages.
   170  type KeeperURLOpener interface {
   171  	OpenKeeperURL(ctx context.Context, u *url.URL) (*Keeper, error)
   172  }
   173  
   174  // URLMux is a URL opener multiplexer. It matches the scheme of the URLs
   175  // against a set of registered schemes and calls the opener that matches the
   176  // URL's scheme.
   177  // See https://gocloud.dev/concepts/urls/ for more information.
   178  //
   179  // The zero value is a multiplexer with no registered schemes.
   180  type URLMux struct {
   181  	schemes openurl.SchemeMap
   182  }
   183  
   184  // KeeperSchemes returns a sorted slice of the registered Keeper schemes.
   185  func (mux *URLMux) KeeperSchemes() []string { return mux.schemes.Schemes() }
   186  
   187  // ValidKeeperScheme returns true iff scheme has been registered for Keepers.
   188  func (mux *URLMux) ValidKeeperScheme(scheme string) bool { return mux.schemes.ValidScheme(scheme) }
   189  
   190  // RegisterKeeper registers the opener with the given scheme. If an opener
   191  // already exists for the scheme, RegisterKeeper panics.
   192  func (mux *URLMux) RegisterKeeper(scheme string, opener KeeperURLOpener) {
   193  	mux.schemes.Register("secrets", "Keeper", scheme, opener)
   194  }
   195  
   196  // OpenKeeper calls OpenKeeperURL with the URL parsed from urlstr.
   197  // OpenKeeper is safe to call from multiple goroutines.
   198  func (mux *URLMux) OpenKeeper(ctx context.Context, urlstr string) (*Keeper, error) {
   199  	opener, u, err := mux.schemes.FromString("Keeper", urlstr)
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  	return opener.(KeeperURLOpener).OpenKeeperURL(ctx, u)
   204  }
   205  
   206  // OpenKeeperURL dispatches the URL to the opener that is registered with the
   207  // URL's scheme. OpenKeeperURL is safe to call from multiple goroutines.
   208  func (mux *URLMux) OpenKeeperURL(ctx context.Context, u *url.URL) (*Keeper, error) {
   209  	opener, err := mux.schemes.FromURL("Keeper", u)
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  	return opener.(KeeperURLOpener).OpenKeeperURL(ctx, u)
   214  }
   215  
   216  var defaultURLMux = new(URLMux)
   217  
   218  // DefaultURLMux returns the URLMux used by OpenKeeper.
   219  //
   220  // Driver packages can use this to register their KeeperURLOpener on the mux.
   221  func DefaultURLMux() *URLMux {
   222  	return defaultURLMux
   223  }
   224  
   225  // OpenKeeper opens the Keeper identified by the URL given.
   226  // See the URLOpener documentation in driver subpackages for
   227  // details on supported URL formats, and https://gocloud.dev/concepts/urls
   228  // for more information.
   229  func OpenKeeper(ctx context.Context, urlstr string) (*Keeper, error) {
   230  	return defaultURLMux.OpenKeeper(ctx, urlstr)
   231  }