github.com/hashicorp/vault/sdk@v0.13.0/logical/request.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package logical
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"io"
    10  	"net/http"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/mitchellh/copystructure"
    15  )
    16  
    17  // RequestWrapInfo is a struct that stores information about desired response
    18  // and seal wrapping behavior
    19  type RequestWrapInfo struct {
    20  	// Setting to non-zero specifies that the response should be wrapped.
    21  	// Specifies the desired TTL of the wrapping token.
    22  	TTL time.Duration `json:"ttl" structs:"ttl" mapstructure:"ttl" sentinel:""`
    23  
    24  	// The format to use for the wrapped response; if not specified it's a bare
    25  	// token
    26  	Format string `json:"format" structs:"format" mapstructure:"format" sentinel:""`
    27  
    28  	// A flag to conforming backends that data for a given request should be
    29  	// seal wrapped
    30  	SealWrap bool `json:"seal_wrap" structs:"seal_wrap" mapstructure:"seal_wrap" sentinel:""`
    31  }
    32  
    33  func (r *RequestWrapInfo) SentinelGet(key string) (interface{}, error) {
    34  	if r == nil {
    35  		return nil, nil
    36  	}
    37  	switch key {
    38  	case "ttl":
    39  		return r.TTL, nil
    40  	case "ttl_seconds":
    41  		return int64(r.TTL.Seconds()), nil
    42  	}
    43  
    44  	return nil, nil
    45  }
    46  
    47  func (r *RequestWrapInfo) SentinelKeys() []string {
    48  	return []string{
    49  		"ttl",
    50  		"ttl_seconds",
    51  	}
    52  }
    53  
    54  //go:generate enumer -type=ClientTokenSource -trimprefix=ClientTokenFrom -transform=snake
    55  type ClientTokenSource uint32
    56  
    57  const (
    58  	NoClientToken ClientTokenSource = iota
    59  	ClientTokenFromVaultHeader
    60  	ClientTokenFromAuthzHeader
    61  	ClientTokenFromInternalAuth
    62  )
    63  
    64  type WALState struct {
    65  	ClusterID       string
    66  	LocalIndex      uint64
    67  	ReplicatedIndex uint64
    68  }
    69  
    70  const indexStateCtxKey = "index_state"
    71  
    72  // IndexStateContext returns a context with an added value holding the index
    73  // state that should be populated on writes.
    74  func IndexStateContext(ctx context.Context, state *WALState) context.Context {
    75  	return context.WithValue(ctx, indexStateCtxKey, state)
    76  }
    77  
    78  // IndexStateFromContext is a helper to look up if the provided context contains
    79  // an index state pointer.
    80  func IndexStateFromContext(ctx context.Context) *WALState {
    81  	s, ok := ctx.Value(indexStateCtxKey).(*WALState)
    82  	if !ok {
    83  		return nil
    84  	}
    85  	return s
    86  }
    87  
    88  // Request is a struct that stores the parameters and context of a request
    89  // being made to Vault. It is used to abstract the details of the higher level
    90  // request protocol from the handlers.
    91  //
    92  // Note: Many of these have Sentinel disabled because they are values populated
    93  // by the router after policy checks; the token namespace would be the right
    94  // place to access them via Sentinel
    95  type Request struct {
    96  	// Id is the uuid associated with each request
    97  	ID string `json:"id" structs:"id" mapstructure:"id" sentinel:""`
    98  
    99  	// If set, the name given to the replication secondary where this request
   100  	// originated
   101  	ReplicationCluster string `json:"replication_cluster" structs:"replication_cluster" mapstructure:"replication_cluster" sentinel:""`
   102  
   103  	// Operation is the requested operation type
   104  	Operation Operation `json:"operation" structs:"operation" mapstructure:"operation"`
   105  
   106  	// Path is the full path of the request
   107  	Path string `json:"path" structs:"path" mapstructure:"path" sentinel:""`
   108  
   109  	// Request data is an opaque map that must have string keys.
   110  	Data map[string]interface{} `json:"map" structs:"data" mapstructure:"data"`
   111  
   112  	// Storage can be used to durably store and retrieve state.
   113  	Storage Storage `json:"-" sentinel:""`
   114  
   115  	// Secret will be non-nil only for Revoke and Renew operations
   116  	// to represent the secret that was returned prior.
   117  	Secret *Secret `json:"secret" structs:"secret" mapstructure:"secret" sentinel:""`
   118  
   119  	// Auth will be non-nil only for Renew operations
   120  	// to represent the auth that was returned prior.
   121  	Auth *Auth `json:"auth" structs:"auth" mapstructure:"auth" sentinel:""`
   122  
   123  	// Headers will contain the http headers from the request. This value will
   124  	// be used in the audit broker to ensure we are auditing only the allowed
   125  	// headers.
   126  	Headers map[string][]string `json:"headers" structs:"headers" mapstructure:"headers" sentinel:""`
   127  
   128  	// Connection will be non-nil only for credential providers to
   129  	// inspect the connection information and potentially use it for
   130  	// authentication/protection.
   131  	Connection *Connection `json:"connection" structs:"connection" mapstructure:"connection"`
   132  
   133  	// ClientToken is provided to the core so that the identity
   134  	// can be verified and ACLs applied. This value is passed
   135  	// through to the logical backends but after being salted and
   136  	// hashed.
   137  	ClientToken string `json:"client_token" structs:"client_token" mapstructure:"client_token" sentinel:""`
   138  
   139  	// ClientTokenAccessor is provided to the core so that the it can get
   140  	// logged as part of request audit logging.
   141  	ClientTokenAccessor string `json:"client_token_accessor" structs:"client_token_accessor" mapstructure:"client_token_accessor" sentinel:""`
   142  
   143  	// DisplayName is provided to the logical backend to help associate
   144  	// dynamic secrets with the source entity. This is not a sensitive
   145  	// name, but is useful for operators.
   146  	DisplayName string `json:"display_name" structs:"display_name" mapstructure:"display_name" sentinel:""`
   147  
   148  	// MountPoint is provided so that a logical backend can generate
   149  	// paths relative to itself. The `Path` is effectively the client
   150  	// request path with the MountPoint trimmed off.
   151  	MountPoint string `json:"mount_point" structs:"mount_point" mapstructure:"mount_point" sentinel:""`
   152  
   153  	// MountType is provided so that a logical backend can make decisions
   154  	// based on the specific mount type (e.g., if a mount type has different
   155  	// aliases, generating different defaults depending on the alias)
   156  	MountType string `json:"mount_type" structs:"mount_type" mapstructure:"mount_type" sentinel:""`
   157  
   158  	// MountAccessor is provided so that identities returned by the authentication
   159  	// backends can be tied to the mount it belongs to.
   160  	MountAccessor string `json:"mount_accessor" structs:"mount_accessor" mapstructure:"mount_accessor" sentinel:""`
   161  
   162  	// mountRunningVersion is used internally to propagate the semantic version
   163  	// of the mounted plugin as reported by its vault.MountEntry to audit logging
   164  	mountRunningVersion string
   165  
   166  	// mountRunningSha256 is used internally to propagate the encoded sha256
   167  	// of the mounted plugin as reported its vault.MountEntry to audit logging
   168  	mountRunningSha256 string
   169  
   170  	// mountIsExternalPlugin is used internally to propagate whether
   171  	// the backend of the mounted plugin is running externally (i.e., over GRPC)
   172  	// to audit logging
   173  	mountIsExternalPlugin bool
   174  
   175  	// mountClass is used internally to propagate the mount class of the mounted plugin to audit logging
   176  	mountClass string
   177  
   178  	// WrapInfo contains requested response wrapping parameters
   179  	WrapInfo *RequestWrapInfo `json:"wrap_info" structs:"wrap_info" mapstructure:"wrap_info" sentinel:""`
   180  
   181  	// ClientTokenRemainingUses represents the allowed number of uses left on the
   182  	// token supplied
   183  	ClientTokenRemainingUses int `json:"client_token_remaining_uses" structs:"client_token_remaining_uses" mapstructure:"client_token_remaining_uses"`
   184  
   185  	// EntityID is the identity of the caller extracted out of the token used
   186  	// to make this request
   187  	EntityID string `json:"entity_id" structs:"entity_id" mapstructure:"entity_id" sentinel:""`
   188  
   189  	// PolicyOverride indicates that the requestor wishes to override
   190  	// soft-mandatory Sentinel policies
   191  	PolicyOverride bool `json:"policy_override" structs:"policy_override" mapstructure:"policy_override"`
   192  
   193  	// Whether the request is unauthenticated, as in, had no client token
   194  	// attached. Useful in some situations where the client token is not made
   195  	// accessible.
   196  	Unauthenticated bool `json:"unauthenticated" structs:"unauthenticated" mapstructure:"unauthenticated"`
   197  
   198  	// PathLimited indicates that the request path is marked for special-case
   199  	// request limiting.
   200  	PathLimited bool `json:"path_limited" structs:"path_limited" mapstructure:"path_limited"`
   201  
   202  	// MFACreds holds the parsed MFA information supplied over the API as part of
   203  	// X-Vault-MFA header
   204  	MFACreds MFACreds `json:"mfa_creds" structs:"mfa_creds" mapstructure:"mfa_creds" sentinel:""`
   205  
   206  	// Cached token entry. This avoids another lookup in request handling when
   207  	// we've already looked it up at http handling time. Note that this token
   208  	// has not been "used", as in it will not properly take into account use
   209  	// count limitations. As a result this field should only ever be used for
   210  	// transport to a function that would otherwise do a lookup and then
   211  	// properly use the token.
   212  	tokenEntry *TokenEntry
   213  
   214  	// For replication, contains the last WAL on the remote side after handling
   215  	// the request, used for best-effort avoidance of stale read-after-write
   216  	lastRemoteWAL uint64
   217  
   218  	// ControlGroup holds the authorizations that have happened on this
   219  	// request
   220  	ControlGroup *ControlGroup `json:"control_group" structs:"control_group" mapstructure:"control_group" sentinel:""`
   221  
   222  	// ClientTokenSource tells us where the client token was sourced from, so
   223  	// we can delete it before sending off to plugins
   224  	ClientTokenSource ClientTokenSource
   225  
   226  	// HTTPRequest, if set, can be used to access fields from the HTTP request
   227  	// that generated this logical.Request object, such as the request body.
   228  	HTTPRequest *http.Request `json:"-" sentinel:""`
   229  
   230  	// ResponseWriter if set can be used to stream a response value to the http
   231  	// request that generated this logical.Request object.
   232  	ResponseWriter *HTTPResponseWriter `json:"-" sentinel:""`
   233  
   234  	// requiredState is used internally to propagate the X-Vault-Index request
   235  	// header to later levels of request processing that operate only on
   236  	// logical.Request.
   237  	requiredState []string
   238  
   239  	// responseState is used internally to propagate the state that should appear
   240  	// in response headers; it's attached to the request rather than the response
   241  	// because not all requests yields non-nil responses.
   242  	responseState *WALState
   243  
   244  	// ClientID is the identity of the caller. If the token is associated with an
   245  	// entity, it will be the same as the EntityID . If the token has no entity,
   246  	// this will be the sha256(sorted policies + namespace) associated with the
   247  	// client token.
   248  	ClientID string `json:"client_id" structs:"client_id" mapstructure:"client_id" sentinel:""`
   249  
   250  	// InboundSSCToken is the token that arrives on an inbound request, supplied
   251  	// by the vault user.
   252  	InboundSSCToken string
   253  
   254  	// When a request has been forwarded, contains information of the host the request was forwarded 'from'
   255  	ForwardedFrom string `json:"forwarded_from,omitempty"`
   256  
   257  	// Name of the chroot namespace for the listener that the request was made against
   258  	ChrootNamespace string `json:"chroot_namespace,omitempty"`
   259  
   260  	// RequestLimiterDisabled tells whether the request context has Request Limiter applied.
   261  	RequestLimiterDisabled bool `json:"request_limiter_disabled,omitempty"`
   262  }
   263  
   264  // Clone returns a deep copy (almost) of the request.
   265  // It will set unexported fields which were only previously accessible outside
   266  // the package via receiver methods.
   267  // NOTE: Request.Connection is NOT deep-copied, due to issues with the results
   268  // of copystructure on serial numbers within the x509.Certificate objects.
   269  func (r *Request) Clone() (*Request, error) {
   270  	cpy, err := copystructure.Copy(r)
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  
   275  	req := cpy.(*Request)
   276  
   277  	// Add the unexported values that were only retrievable via receivers.
   278  	// copystructure isn't able to do this, which is why we're doing it manually.
   279  	req.mountClass = r.MountClass()
   280  	req.mountRunningVersion = r.MountRunningVersion()
   281  	req.mountRunningSha256 = r.MountRunningSha256()
   282  	req.mountIsExternalPlugin = r.MountIsExternalPlugin()
   283  	// This needs to be overwritten as the internal connection state is not cloned properly
   284  	// mainly the big.Int serial numbers within the x509.Certificate objects get mangled.
   285  	req.Connection = r.Connection
   286  
   287  	return req, nil
   288  }
   289  
   290  // Get returns a data field and guards for nil Data
   291  func (r *Request) Get(key string) interface{} {
   292  	if r.Data == nil {
   293  		return nil
   294  	}
   295  	return r.Data[key]
   296  }
   297  
   298  // GetString returns a data field as a string
   299  func (r *Request) GetString(key string) string {
   300  	raw := r.Get(key)
   301  	s, _ := raw.(string)
   302  	return s
   303  }
   304  
   305  func (r *Request) GoString() string {
   306  	return fmt.Sprintf("*%#v", *r)
   307  }
   308  
   309  func (r *Request) SentinelGet(key string) (interface{}, error) {
   310  	switch key {
   311  	case "path":
   312  		// Sanitize it here so that it's consistent in policies
   313  		return strings.TrimPrefix(r.Path, "/"), nil
   314  
   315  	case "wrapping", "wrap_info":
   316  		// If the pointer is nil accessing the wrap info is considered
   317  		// "undefined" so this allows us to instead discover a TTL of zero
   318  		if r.WrapInfo == nil {
   319  			return &RequestWrapInfo{}, nil
   320  		}
   321  		return r.WrapInfo, nil
   322  	}
   323  
   324  	return nil, nil
   325  }
   326  
   327  func (r *Request) SentinelKeys() []string {
   328  	return []string{
   329  		"path",
   330  		"wrapping",
   331  		"wrap_info",
   332  	}
   333  }
   334  
   335  func (r *Request) MountRunningVersion() string {
   336  	return r.mountRunningVersion
   337  }
   338  
   339  func (r *Request) SetMountRunningVersion(mountRunningVersion string) {
   340  	r.mountRunningVersion = mountRunningVersion
   341  }
   342  
   343  func (r *Request) MountRunningSha256() string {
   344  	return r.mountRunningSha256
   345  }
   346  
   347  func (r *Request) SetMountRunningSha256(mountRunningSha256 string) {
   348  	r.mountRunningSha256 = mountRunningSha256
   349  }
   350  
   351  func (r *Request) MountIsExternalPlugin() bool {
   352  	return r.mountIsExternalPlugin
   353  }
   354  
   355  func (r *Request) SetMountIsExternalPlugin(mountIsExternalPlugin bool) {
   356  	r.mountIsExternalPlugin = mountIsExternalPlugin
   357  }
   358  
   359  func (r *Request) MountClass() string {
   360  	return r.mountClass
   361  }
   362  
   363  func (r *Request) SetMountClass(mountClass string) {
   364  	r.mountClass = mountClass
   365  }
   366  
   367  func (r *Request) LastRemoteWAL() uint64 {
   368  	return r.lastRemoteWAL
   369  }
   370  
   371  func (r *Request) SetLastRemoteWAL(last uint64) {
   372  	r.lastRemoteWAL = last
   373  }
   374  
   375  func (r *Request) RequiredState() []string {
   376  	return r.requiredState
   377  }
   378  
   379  func (r *Request) SetRequiredState(state []string) {
   380  	r.requiredState = state
   381  }
   382  
   383  func (r *Request) ResponseState() *WALState {
   384  	return r.responseState
   385  }
   386  
   387  func (r *Request) SetResponseState(w *WALState) {
   388  	r.responseState = w
   389  }
   390  
   391  func (r *Request) TokenEntry() *TokenEntry {
   392  	return r.tokenEntry
   393  }
   394  
   395  func (r *Request) SetTokenEntry(te *TokenEntry) {
   396  	r.tokenEntry = te
   397  }
   398  
   399  // RenewRequest creates the structure of the renew request.
   400  func RenewRequest(path string, secret *Secret, data map[string]interface{}) *Request {
   401  	return &Request{
   402  		Operation: RenewOperation,
   403  		Path:      path,
   404  		Data:      data,
   405  		Secret:    secret,
   406  	}
   407  }
   408  
   409  // RenewAuthRequest creates the structure of the renew request for an auth.
   410  func RenewAuthRequest(path string, auth *Auth, data map[string]interface{}) *Request {
   411  	return &Request{
   412  		Operation: RenewOperation,
   413  		Path:      path,
   414  		Data:      data,
   415  		Auth:      auth,
   416  	}
   417  }
   418  
   419  // RevokeRequest creates the structure of the revoke request.
   420  func RevokeRequest(path string, secret *Secret, data map[string]interface{}) *Request {
   421  	return &Request{
   422  		Operation: RevokeOperation,
   423  		Path:      path,
   424  		Data:      data,
   425  		Secret:    secret,
   426  	}
   427  }
   428  
   429  // RollbackRequest creates the structure of the revoke request.
   430  func RollbackRequest(path string) *Request {
   431  	return &Request{
   432  		Operation: RollbackOperation,
   433  		Path:      path,
   434  		Data:      make(map[string]interface{}),
   435  	}
   436  }
   437  
   438  // Operation is an enum that is used to specify the type
   439  // of request being made
   440  type Operation string
   441  
   442  const (
   443  	// The operations below are called per path
   444  	CreateOperation         Operation = "create"
   445  	ReadOperation                     = "read"
   446  	UpdateOperation                   = "update"
   447  	PatchOperation                    = "patch"
   448  	DeleteOperation                   = "delete"
   449  	ListOperation                     = "list"
   450  	HelpOperation                     = "help"
   451  	AliasLookaheadOperation           = "alias-lookahead"
   452  	ResolveRoleOperation              = "resolve-role"
   453  	HeaderOperation                   = "header"
   454  
   455  	// The operations below are called globally, the path is less relevant.
   456  	RevokeOperation   Operation = "revoke"
   457  	RenewOperation              = "renew"
   458  	RollbackOperation           = "rollback"
   459  )
   460  
   461  type MFACreds map[string][]string
   462  
   463  // InitializationRequest stores the parameters and context of an Initialize()
   464  // call being made to a logical.Backend.
   465  type InitializationRequest struct {
   466  	// Storage can be used to durably store and retrieve state.
   467  	Storage Storage
   468  }
   469  
   470  type CustomHeader struct {
   471  	Name  string
   472  	Value string
   473  }
   474  
   475  type CtxKeyInFlightRequestID struct{}
   476  
   477  func (c CtxKeyInFlightRequestID) String() string {
   478  	return "in-flight-request-ID"
   479  }
   480  
   481  type CtxKeyInFlightRequestPriority struct{}
   482  
   483  func (c CtxKeyInFlightRequestPriority) String() string {
   484  	return "in-flight-request-priority"
   485  }
   486  
   487  // CtxKeyInFlightTraceID is used for passing a trace ID through request
   488  // forwarding. The CtxKeyInFlightRequestID created at the HTTP layer is
   489  // propagated on through any forwarded requests using this key.
   490  //
   491  // Note that this applies to replication service RPCs (including
   492  // ForwardingRequest from perf standbys or secondaries). The Forwarding RPC
   493  // service may propagate the context but the handling on the active node runs
   494  // back through the `http` package handler which builds a new context from HTTP
   495  // request properties and creates a fresh request ID. Forwarding RPC is used
   496  // exclusively in Community Edition but also in some special cases in Enterprise
   497  // such as when forwarding is forced by an HTTP header.
   498  type CtxKeyInFlightTraceID struct{}
   499  
   500  func (c CtxKeyInFlightTraceID) String() string {
   501  	return "in-flight-trace-ID"
   502  }
   503  
   504  type CtxKeyRequestRole struct{}
   505  
   506  func (c CtxKeyRequestRole) String() string {
   507  	return "request-role"
   508  }
   509  
   510  // ctxKeyDisableReplicationStatusEndpoints is a custom type used as a key in
   511  // context.Context to store the value `true` when the
   512  // disable_replication_status_endpoints configuration parameter is set to true
   513  // for the listener through which a request was received.
   514  type ctxKeyDisableReplicationStatusEndpoints struct{}
   515  
   516  // String returns a string representation of the receiver type.
   517  func (c ctxKeyDisableReplicationStatusEndpoints) String() string {
   518  	return "disable-replication-status-endpoints"
   519  }
   520  
   521  // ContextDisableReplicationStatusEndpointsValue examines the provided
   522  // context.Context for the disable replication status endpoints value and
   523  // returns it as a bool value if it's found along with the ok return value set
   524  // to true; otherwise the ok return value is false.
   525  func ContextDisableReplicationStatusEndpointsValue(ctx context.Context) (value, ok bool) {
   526  	value, ok = ctx.Value(ctxKeyDisableReplicationStatusEndpoints{}).(bool)
   527  
   528  	return
   529  }
   530  
   531  // CreateContextDisableReplicationStatusEndpoints creates a new context.Context
   532  // based on the provided parent that also includes the provided value for the
   533  // ctxKeyDisableReplicationStatusEndpoints key.
   534  func CreateContextDisableReplicationStatusEndpoints(parent context.Context, value bool) context.Context {
   535  	return context.WithValue(parent, ctxKeyDisableReplicationStatusEndpoints{}, value)
   536  }
   537  
   538  // CtxKeyOriginalRequestPath is a custom type used as a key in context.Context
   539  // to store the original request path.
   540  type ctxKeyOriginalRequestPath struct{}
   541  
   542  // String returns a string representation of the receiver type.
   543  func (c ctxKeyOriginalRequestPath) String() string {
   544  	return "original_request_path"
   545  }
   546  
   547  // ContextOriginalRequestPathValue examines the provided context.Context for the
   548  // original request path value and returns it as a string value if it's found
   549  // along with the ok value set to true; otherwise the ok return value is false.
   550  func ContextOriginalRequestPathValue(ctx context.Context) (value string, ok bool) {
   551  	value, ok = ctx.Value(ctxKeyOriginalRequestPath{}).(string)
   552  
   553  	return
   554  }
   555  
   556  // CreateContextOriginalRequestPath creates a new context.Context based on the
   557  // provided parent that also includes the provided original request path value
   558  // for the ctxKeyOriginalRequestPath key.
   559  func CreateContextOriginalRequestPath(parent context.Context, value string) context.Context {
   560  	return context.WithValue(parent, ctxKeyOriginalRequestPath{}, value)
   561  }
   562  
   563  type ctxKeyOriginalBody struct{}
   564  
   565  func ContextOriginalBodyValue(ctx context.Context) (io.ReadCloser, bool) {
   566  	value, ok := ctx.Value(ctxKeyOriginalBody{}).(io.ReadCloser)
   567  	return value, ok
   568  }
   569  
   570  func CreateContextOriginalBody(parent context.Context, body io.ReadCloser) context.Context {
   571  	return context.WithValue(parent, ctxKeyOriginalBody{}, body)
   572  }
   573  
   574  type CtxKeyDisableRequestLimiter struct{}
   575  
   576  func (c CtxKeyDisableRequestLimiter) String() string {
   577  	return "disable_request_limiter"
   578  }
   579  
   580  // ctxKeyRedactionSettings is a custom type used as a key in context.Context to
   581  // store the value the redaction settings for the listener that received the
   582  // request.
   583  type ctxKeyRedactionSettings struct{}
   584  
   585  // String returns a string representation of the receiver type.
   586  func (c ctxKeyRedactionSettings) String() string {
   587  	return "redaction-settings"
   588  }
   589  
   590  // CtxRedactionSettingsValue examines the provided context.Context for the
   591  // redaction settings value and returns them as a tuple of bool values if they
   592  // are found along with the ok return value set to true; otherwise the ok return
   593  // value is false.
   594  func CtxRedactionSettingsValue(ctx context.Context) (redactVersion, redactAddresses, redactClusterName, ok bool) {
   595  	value, ok := ctx.Value(ctxKeyRedactionSettings{}).([]bool)
   596  	if !ok {
   597  		return false, false, false, false
   598  	}
   599  
   600  	return value[0], value[1], value[2], true
   601  }
   602  
   603  // CreatecontextRedactionSettings creates a new context.Context based on the
   604  // provided parent that also includes the provided redaction settings values for
   605  // the ctxKeyRedactionSettings key.
   606  func CreateContextRedactionSettings(parent context.Context, redactVersion, redactAddresses, redactClusterName bool) context.Context {
   607  	return context.WithValue(parent, ctxKeyRedactionSettings{}, []bool{redactVersion, redactAddresses, redactClusterName})
   608  }