232 lines
7.3 KiB
Go
232 lines
7.3 KiB
Go
|
// Copyright 2019 The Go Cloud Development Kit Authors
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
// Package secrets provides an easy and portable way to encrypt and decrypt
|
||
|
// messages. Subpackages contain driver implementations of
|
||
|
// secrets for supported services.
|
||
|
//
|
||
|
// See https://gocloud.dev/howto/secrets/ for a detailed how-to guide.
|
||
|
//
|
||
|
//
|
||
|
// OpenCensus Integration
|
||
|
//
|
||
|
// OpenCensus supports tracing and metric collection for multiple languages and
|
||
|
// backend providers. See https://opencensus.io.
|
||
|
//
|
||
|
// This API collects OpenCensus traces and metrics for the following methods:
|
||
|
// - Encrypt
|
||
|
// - Decrypt
|
||
|
// All trace and metric names begin with the package import path.
|
||
|
// The traces add the method name.
|
||
|
// For example, "gocloud.dev/secrets/Encrypt".
|
||
|
// The metrics are "completed_calls", a count of completed method calls by driver,
|
||
|
// method and status (error code); and "latency", a distribution of method latency
|
||
|
// by driver and method.
|
||
|
// For example, "gocloud.dev/secrets/latency".
|
||
|
//
|
||
|
// To enable trace collection in your application, see "Configure Exporter" at
|
||
|
// https://opencensus.io/quickstart/go/tracing.
|
||
|
// To enable metric collection in your application, see "Exporting stats" at
|
||
|
// https://opencensus.io/quickstart/go/metrics.
|
||
|
package secrets // import "gocloud.dev/secrets"
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"net/url"
|
||
|
"sync"
|
||
|
|
||
|
"gocloud.dev/internal/gcerr"
|
||
|
"gocloud.dev/internal/oc"
|
||
|
"gocloud.dev/internal/openurl"
|
||
|
"gocloud.dev/secrets/driver"
|
||
|
)
|
||
|
|
||
|
// Keeper does encryption and decryption. To create a Keeper, use constructors
|
||
|
// found in driver subpackages.
|
||
|
type Keeper struct {
|
||
|
k driver.Keeper
|
||
|
tracer *oc.Tracer
|
||
|
|
||
|
// mu protects the closed variable.
|
||
|
// Read locks are kept to allow holding a read lock for long-running calls,
|
||
|
// and thereby prevent closing until a call finishes.
|
||
|
mu sync.RWMutex
|
||
|
closed bool
|
||
|
}
|
||
|
|
||
|
// NewKeeper is intended for use by drivers only. Do not use in application code.
|
||
|
var NewKeeper = newKeeper
|
||
|
|
||
|
// newKeeper creates a Keeper.
|
||
|
func newKeeper(k driver.Keeper) *Keeper {
|
||
|
return &Keeper{
|
||
|
k: k,
|
||
|
tracer: &oc.Tracer{
|
||
|
Package: pkgName,
|
||
|
Provider: oc.ProviderName(k),
|
||
|
LatencyMeasure: latencyMeasure,
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const pkgName = "gocloud.dev/secrets"
|
||
|
|
||
|
var (
|
||
|
latencyMeasure = oc.LatencyMeasure(pkgName)
|
||
|
|
||
|
// OpenCensusViews are predefined views for OpenCensus metrics.
|
||
|
// The views include counts and latency distributions for API method calls.
|
||
|
// See the example at https://godoc.org/go.opencensus.io/stats/view for usage.
|
||
|
OpenCensusViews = oc.Views(pkgName, latencyMeasure)
|
||
|
)
|
||
|
|
||
|
// Encrypt encrypts the plaintext and returns the cipher message.
|
||
|
func (k *Keeper) Encrypt(ctx context.Context, plaintext []byte) (ciphertext []byte, err error) {
|
||
|
ctx = k.tracer.Start(ctx, "Encrypt")
|
||
|
defer func() { k.tracer.End(ctx, err) }()
|
||
|
|
||
|
k.mu.RLock()
|
||
|
defer k.mu.RUnlock()
|
||
|
if k.closed {
|
||
|
return nil, errClosed
|
||
|
}
|
||
|
|
||
|
b, err := k.k.Encrypt(ctx, plaintext)
|
||
|
if err != nil {
|
||
|
return nil, wrapError(k, err)
|
||
|
}
|
||
|
return b, nil
|
||
|
}
|
||
|
|
||
|
// Decrypt decrypts the ciphertext and returns the plaintext.
|
||
|
func (k *Keeper) Decrypt(ctx context.Context, ciphertext []byte) (plaintext []byte, err error) {
|
||
|
ctx = k.tracer.Start(ctx, "Decrypt")
|
||
|
defer func() { k.tracer.End(ctx, err) }()
|
||
|
|
||
|
k.mu.RLock()
|
||
|
defer k.mu.RUnlock()
|
||
|
if k.closed {
|
||
|
return nil, errClosed
|
||
|
}
|
||
|
|
||
|
b, err := k.k.Decrypt(ctx, ciphertext)
|
||
|
if err != nil {
|
||
|
return nil, wrapError(k, err)
|
||
|
}
|
||
|
return b, nil
|
||
|
}
|
||
|
|
||
|
var errClosed = gcerr.Newf(gcerr.FailedPrecondition, nil, "secrets: Keeper has been closed")
|
||
|
|
||
|
// Close releases any resources used for the Keeper.
|
||
|
func (k *Keeper) Close() error {
|
||
|
k.mu.Lock()
|
||
|
prev := k.closed
|
||
|
k.closed = true
|
||
|
k.mu.Unlock()
|
||
|
if prev {
|
||
|
return errClosed
|
||
|
}
|
||
|
return wrapError(k, k.k.Close())
|
||
|
}
|
||
|
|
||
|
// ErrorAs converts i to driver-specific types. See
|
||
|
// https://gocloud.dev/concepts/as/ for background information and the
|
||
|
// driver package documentation for the specific types supported for
|
||
|
// that driver.
|
||
|
//
|
||
|
// ErrorAs panics if i is nil or not a pointer.
|
||
|
// ErrorAs returns false if err == nil.
|
||
|
func (k *Keeper) ErrorAs(err error, i interface{}) bool {
|
||
|
return gcerr.ErrorAs(err, i, k.k.ErrorAs)
|
||
|
}
|
||
|
|
||
|
func wrapError(k *Keeper, err error) error {
|
||
|
if err == nil {
|
||
|
return nil
|
||
|
}
|
||
|
if gcerr.DoNotWrap(err) {
|
||
|
return err
|
||
|
}
|
||
|
return gcerr.New(k.k.ErrorCode(err), err, 2, "secrets")
|
||
|
}
|
||
|
|
||
|
// KeeperURLOpener represents types that can open Keepers based on a URL.
|
||
|
// The opener must not modify the URL argument. OpenKeeperURL must be safe to
|
||
|
// call from multiple goroutines.
|
||
|
//
|
||
|
// This interface is generally implemented by types in driver packages.
|
||
|
type KeeperURLOpener interface {
|
||
|
OpenKeeperURL(ctx context.Context, u *url.URL) (*Keeper, error)
|
||
|
}
|
||
|
|
||
|
// URLMux is a URL opener multiplexer. It matches the scheme of the URLs
|
||
|
// against a set of registered schemes and calls the opener that matches the
|
||
|
// URL's scheme.
|
||
|
// See https://gocloud.dev/concepts/urls/ for more information.
|
||
|
//
|
||
|
// The zero value is a multiplexer with no registered schemes.
|
||
|
type URLMux struct {
|
||
|
schemes openurl.SchemeMap
|
||
|
}
|
||
|
|
||
|
// KeeperSchemes returns a sorted slice of the registered Keeper schemes.
|
||
|
func (mux *URLMux) KeeperSchemes() []string { return mux.schemes.Schemes() }
|
||
|
|
||
|
// ValidKeeperScheme returns true iff scheme has been registered for Keepers.
|
||
|
func (mux *URLMux) ValidKeeperScheme(scheme string) bool { return mux.schemes.ValidScheme(scheme) }
|
||
|
|
||
|
// RegisterKeeper registers the opener with the given scheme. If an opener
|
||
|
// already exists for the scheme, RegisterKeeper panics.
|
||
|
func (mux *URLMux) RegisterKeeper(scheme string, opener KeeperURLOpener) {
|
||
|
mux.schemes.Register("secrets", "Keeper", scheme, opener)
|
||
|
}
|
||
|
|
||
|
// OpenKeeper calls OpenKeeperURL with the URL parsed from urlstr.
|
||
|
// OpenKeeper is safe to call from multiple goroutines.
|
||
|
func (mux *URLMux) OpenKeeper(ctx context.Context, urlstr string) (*Keeper, error) {
|
||
|
opener, u, err := mux.schemes.FromString("Keeper", urlstr)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return opener.(KeeperURLOpener).OpenKeeperURL(ctx, u)
|
||
|
}
|
||
|
|
||
|
// OpenKeeperURL dispatches the URL to the opener that is registered with the
|
||
|
// URL's scheme. OpenKeeperURL is safe to call from multiple goroutines.
|
||
|
func (mux *URLMux) OpenKeeperURL(ctx context.Context, u *url.URL) (*Keeper, error) {
|
||
|
opener, err := mux.schemes.FromURL("Keeper", u)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return opener.(KeeperURLOpener).OpenKeeperURL(ctx, u)
|
||
|
}
|
||
|
|
||
|
var defaultURLMux = new(URLMux)
|
||
|
|
||
|
// DefaultURLMux returns the URLMux used by OpenKeeper.
|
||
|
//
|
||
|
// Driver packages can use this to register their KeeperURLOpener on the mux.
|
||
|
func DefaultURLMux() *URLMux {
|
||
|
return defaultURLMux
|
||
|
}
|
||
|
|
||
|
// OpenKeeper opens the Keeper identified by the URL given.
|
||
|
// See the URLOpener documentation in driver subpackages for
|
||
|
// details on supported URL formats, and https://gocloud.dev/concepts/urls
|
||
|
// for more information.
|
||
|
func OpenKeeper(ctx context.Context, urlstr string) (*Keeper, error) {
|
||
|
return defaultURLMux.OpenKeeper(ctx, urlstr)
|
||
|
}
|