mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-05 20:36:26 +02:00
* Add audit log entries for enterprise JWT token fields * Reduce enterprise token field comment detail - simplify enterprise token comments in sdk/logical/request.go - remove verbose wording about issuer/audience/authorization semantics * Fix TestAudit_JWT_DelegationToken permission denied error The test was failing with 'permission denied' when using a delegation token (JWT with act claim) to access cubbyhole. The root causes were: 1. RAR (Rich Authorization Request) check: The JWT contained 'authorization_details' constraints that only allowed access to 'secret/data/users/alice' and 'secret/data/config/general', but the test was attempting to access 'cubbyhole/test'. The RAR check in PerformRARCheck() was correctly denying this mismatch. 2. Missing entity policies for actor ACL: For delegation tokens, the actor's ACL is built solely from entity identity policies (not token policies like 'default'). Without explicit policies on the actor entity, the delegation ACL intersection check would fail. Fixes: - Removed 'authorization_details' from the test JWT since the test is about verifying audit log entries for delegation tokens, not RAR constraints - Added 'default' policy to both subject and actor entities to ensure both ACLs allow cubbyhole access for the delegation token intersection - Updated test assertions to match the simplified JWT (removed authorization_details verification) * Use require.NoError instead of t.Fatalf for error check * Add explicit checks for auth field presence before type assertion Adds separate checks to verify the 'auth' and 'metadata' fields exist in the map before attempting type assertion, preventing potential panics and improving test clarity. * test: tighten request metadata merge assertions * test: simplify enterprise metadata assertions * test: split enterprise metadata merge coverage * style: apply gofumpt to entry formatter tests * test: add godoc for enterprise token metadata test --------- Co-authored-by: Bianca <48203644+biazmoreira@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
681 lines
25 KiB
Go
681 lines
25 KiB
Go
// Copyright IBM Corp. 2016, 2025
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package logical
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hashicorp/vault/sdk/rotation"
|
|
"github.com/mitchellh/copystructure"
|
|
)
|
|
|
|
// RequestWrapInfo is a struct that stores information about desired response
|
|
// and seal wrapping behavior
|
|
type RequestWrapInfo struct {
|
|
// Setting to non-zero specifies that the response should be wrapped.
|
|
// Specifies the desired TTL of the wrapping token.
|
|
TTL time.Duration `json:"ttl" structs:"ttl" mapstructure:"ttl" sentinel:""`
|
|
|
|
// The format to use for the wrapped response; if not specified it's a bare
|
|
// token
|
|
Format string `json:"format" structs:"format" mapstructure:"format" sentinel:""`
|
|
|
|
// A flag to conforming backends that data for a given request should be
|
|
// seal wrapped
|
|
SealWrap bool `json:"seal_wrap" structs:"seal_wrap" mapstructure:"seal_wrap" sentinel:""`
|
|
}
|
|
|
|
func (r *RequestWrapInfo) SentinelGet(key string) (interface{}, error) {
|
|
if r == nil {
|
|
return nil, nil
|
|
}
|
|
switch key {
|
|
case "ttl":
|
|
return r.TTL, nil
|
|
case "ttl_seconds":
|
|
return int64(r.TTL.Seconds()), nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (r *RequestWrapInfo) SentinelKeys() []string {
|
|
return []string{
|
|
"ttl",
|
|
"ttl_seconds",
|
|
}
|
|
}
|
|
|
|
//go:generate enumer -type=ClientTokenSource -trimprefix=ClientTokenFrom -transform=snake
|
|
type ClientTokenSource uint32
|
|
|
|
const (
|
|
NoClientToken ClientTokenSource = iota
|
|
ClientTokenFromVaultHeader
|
|
ClientTokenFromAuthzHeader
|
|
ClientTokenFromInternalAuth
|
|
)
|
|
|
|
type WALState struct {
|
|
ClusterID string
|
|
LocalIndex uint64
|
|
ReplicatedIndex uint64
|
|
}
|
|
|
|
const indexStateCtxKey = "index_state"
|
|
|
|
// IndexStateContext returns a context with an added value holding the index
|
|
// state that should be populated on writes.
|
|
func IndexStateContext(ctx context.Context, state *WALState) context.Context {
|
|
return context.WithValue(ctx, indexStateCtxKey, state)
|
|
}
|
|
|
|
// IndexStateFromContext is a helper to look up if the provided context contains
|
|
// an index state pointer.
|
|
func IndexStateFromContext(ctx context.Context) *WALState {
|
|
s, ok := ctx.Value(indexStateCtxKey).(*WALState)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return s
|
|
}
|
|
|
|
// AuthorizationDetail stores one enterprise token authorization detail object.
|
|
// The "type" field is required.
|
|
type AuthorizationDetail map[string]any
|
|
|
|
// Request is a struct that stores the parameters and context of a request
|
|
// being made to Vault. It is used to abstract the details of the higher level
|
|
// request protocol from the handlers.
|
|
//
|
|
// Note: Many of these have Sentinel disabled because they are values populated
|
|
// by the router after policy checks; the token namespace would be the right
|
|
// place to access them via Sentinel
|
|
type Request struct {
|
|
// Id is the uuid associated with each request
|
|
ID string `json:"id" structs:"id" mapstructure:"id" sentinel:""`
|
|
|
|
// If set, the name given to the replication secondary where this request
|
|
// originated
|
|
ReplicationCluster string `json:"replication_cluster" structs:"replication_cluster" mapstructure:"replication_cluster" sentinel:""`
|
|
|
|
// Operation is the requested operation type
|
|
Operation Operation `json:"operation" structs:"operation" mapstructure:"operation"`
|
|
|
|
// Path is the full path of the request
|
|
Path string `json:"path" structs:"path" mapstructure:"path" sentinel:""`
|
|
|
|
// Request data is an opaque map that must have string keys.
|
|
Data map[string]interface{} `json:"map" structs:"data" mapstructure:"data"`
|
|
|
|
// Storage can be used to durably store and retrieve state.
|
|
Storage Storage `json:"-" sentinel:""`
|
|
|
|
// Secret will be non-nil only for Revoke and Renew operations
|
|
// to represent the secret that was returned prior.
|
|
Secret *Secret `json:"secret" structs:"secret" mapstructure:"secret" sentinel:""`
|
|
|
|
// Auth will be non-nil only for Renew operations
|
|
// to represent the auth that was returned prior.
|
|
Auth *Auth `json:"auth" structs:"auth" mapstructure:"auth" sentinel:""`
|
|
|
|
// Headers will contain the http headers from the request. This value will
|
|
// be used in the audit broker to ensure we are auditing only the allowed
|
|
// headers.
|
|
Headers map[string][]string `json:"headers" structs:"headers" mapstructure:"headers" sentinel:""`
|
|
|
|
// Connection will be non-nil only for credential providers to
|
|
// inspect the connection information and potentially use it for
|
|
// authentication/protection.
|
|
Connection *Connection `json:"connection" structs:"connection" mapstructure:"connection"`
|
|
|
|
// ClientToken is provided to the core so that the identity
|
|
// can be verified and ACLs applied. This value is passed
|
|
// through to the logical backends but after being salted and
|
|
// hashed.
|
|
ClientToken string `json:"client_token" structs:"client_token" mapstructure:"client_token" sentinel:""`
|
|
|
|
// EnterpriseTokenMetadata stores enterprise token metadata.
|
|
EnterpriseTokenMetadata string `json:"enterprise_token_metadata" structs:"enterprise_token_metadata" mapstructure:"enterprise_token_metadata" sentinel:""`
|
|
|
|
// EnterpriseTokenIssuer stores the enterprise token issuer.
|
|
EnterpriseTokenIssuer string `json:"enterprise_token_issuer,omitempty" structs:"enterprise_token_issuer" mapstructure:"enterprise_token_issuer"`
|
|
|
|
// EnterpriseTokenAudience stores enterprise token audience values.
|
|
EnterpriseTokenAudience []string `json:"enterprise_token_audience,omitempty" structs:"enterprise_token_audience" mapstructure:"enterprise_token_audience"`
|
|
|
|
// EnterpriseTokenAuthorizationDetails stores enterprise token authorization details.
|
|
EnterpriseTokenAuthorizationDetails []AuthorizationDetail `json:"enterprise_token_authorization_details,omitempty" structs:"enterprise_token_authorization_details" mapstructure:"enterprise_token_authorization_details"`
|
|
|
|
// ClientTokenAccessor is provided to the core so that the it can get
|
|
// logged as part of request audit logging.
|
|
ClientTokenAccessor string `json:"client_token_accessor" structs:"client_token_accessor" mapstructure:"client_token_accessor" sentinel:""`
|
|
|
|
// DisplayName is provided to the logical backend to help associate
|
|
// dynamic secrets with the source entity. This is not a sensitive
|
|
// name, but is useful for operators.
|
|
DisplayName string `json:"display_name" structs:"display_name" mapstructure:"display_name" sentinel:""`
|
|
|
|
// MountPoint is provided so that a logical backend can generate
|
|
// paths relative to itself. The `Path` is effectively the client
|
|
// request path with the MountPoint trimmed off.
|
|
MountPoint string `json:"mount_point" structs:"mount_point" mapstructure:"mount_point" sentinel:""`
|
|
|
|
// MountType is provided so that a logical backend can make decisions
|
|
// based on the specific mount type (e.g., if a mount type has different
|
|
// aliases, generating different defaults depending on the alias)
|
|
MountType string `json:"mount_type" structs:"mount_type" mapstructure:"mount_type" sentinel:""`
|
|
|
|
// MountAccessor is provided so that identities returned by the authentication
|
|
// backends can be tied to the mount it belongs to.
|
|
MountAccessor string `json:"mount_accessor" structs:"mount_accessor" mapstructure:"mount_accessor" sentinel:""`
|
|
|
|
// mountRunningVersion is used internally to propagate the semantic version
|
|
// of the mounted plugin as reported by its vault.MountEntry to audit logging
|
|
mountRunningVersion string
|
|
|
|
// mountRunningSha256 is used internally to propagate the encoded sha256
|
|
// of the mounted plugin as reported its vault.MountEntry to audit logging
|
|
mountRunningSha256 string
|
|
|
|
// mountIsExternalPlugin is used internally to propagate whether
|
|
// the backend of the mounted plugin is running externally (i.e., over GRPC)
|
|
// to audit logging
|
|
mountIsExternalPlugin bool
|
|
|
|
// mountClass is used internally to propagate the mount class of the mounted plugin to audit logging
|
|
mountClass string
|
|
|
|
// WrapInfo contains requested response wrapping parameters
|
|
WrapInfo *RequestWrapInfo `json:"wrap_info" structs:"wrap_info" mapstructure:"wrap_info" sentinel:""`
|
|
|
|
// ClientTokenRemainingUses represents the allowed number of uses left on the
|
|
// token supplied
|
|
ClientTokenRemainingUses int `json:"client_token_remaining_uses" structs:"client_token_remaining_uses" mapstructure:"client_token_remaining_uses"`
|
|
|
|
// EntityID is the identity of the caller extracted out of the token used
|
|
// to make this request
|
|
EntityID string `json:"entity_id" structs:"entity_id" mapstructure:"entity_id" sentinel:""`
|
|
|
|
// ActorEntityID is the entity ID of the actor in a request.
|
|
ActorEntityID string `json:"actor_entity_id" structs:"actor_entity_id" mapstructure:"actor_entity_id" sentinel:""`
|
|
|
|
// ActorEntityName is the name of the actor entity in a request.
|
|
ActorEntityName string `json:"actor_entity_name" structs:"actor_entity_name" mapstructure:"actor_entity_name" sentinel:""`
|
|
|
|
// PolicyOverride indicates that the requestor wishes to override
|
|
// soft-mandatory Sentinel policies
|
|
PolicyOverride bool `json:"policy_override" structs:"policy_override" mapstructure:"policy_override"`
|
|
|
|
// Whether the request is unauthenticated, as in, had no client token
|
|
// attached. Useful in some situations where the client token is not made
|
|
// accessible.
|
|
Unauthenticated bool `json:"unauthenticated" structs:"unauthenticated" mapstructure:"unauthenticated"`
|
|
|
|
// PathLimited indicates that the request path is marked for special-case
|
|
// request limiting.
|
|
PathLimited bool `json:"path_limited" structs:"path_limited" mapstructure:"path_limited"`
|
|
|
|
// MFACreds holds the parsed MFA information supplied over the API as part of
|
|
// X-Vault-MFA header
|
|
MFACreds MFACreds `json:"mfa_creds" structs:"mfa_creds" mapstructure:"mfa_creds" sentinel:""`
|
|
|
|
// Deprecated: use RotationID in RotationInfo instead
|
|
// RotationID is set by the Rotation Manager
|
|
// when making rotate requests to plugin backends
|
|
RotationID string
|
|
|
|
// RotationInfo is set by the Rotation Manager
|
|
// when making rotate requests to plugin backends
|
|
RotationInfo *rotation.RotationInfo
|
|
|
|
// Cached token entry. This avoids another lookup in request handling when
|
|
// we've already looked it up at http handling time. Note that this token
|
|
// has not been "used", as in it will not properly take into account use
|
|
// count limitations. As a result this field should only ever be used for
|
|
// transport to a function that would otherwise do a lookup and then
|
|
// properly use the token.
|
|
tokenEntry *TokenEntry
|
|
|
|
// For replication, contains the last WAL on the remote side after handling
|
|
// the request, used for best-effort avoidance of stale read-after-write
|
|
lastRemoteWAL uint64
|
|
|
|
// ControlGroup holds the authorizations that have happened on this
|
|
// request
|
|
ControlGroup *ControlGroup `json:"control_group" structs:"control_group" mapstructure:"control_group" sentinel:""`
|
|
|
|
// ClientTokenSource tells us where the client token was sourced from, so
|
|
// we can delete it before sending off to plugins
|
|
ClientTokenSource ClientTokenSource
|
|
|
|
// HTTPRequest, if set, can be used to access fields from the HTTP request
|
|
// that generated this logical.Request object, such as the request body.
|
|
HTTPRequest *http.Request `json:"-" sentinel:""`
|
|
|
|
// ResponseWriter if set can be used to stream a response value to the http
|
|
// request that generated this logical.Request object.
|
|
ResponseWriter *HTTPResponseWriter `json:"-" sentinel:""`
|
|
|
|
// requiredState is used internally to propagate the X-Vault-Index request
|
|
// header to later levels of request processing that operate only on
|
|
// logical.Request.
|
|
requiredState []string
|
|
|
|
// responseState is used internally to propagate the state that should appear
|
|
// in response headers; it's attached to the request rather than the response
|
|
// because not all requests yields non-nil responses.
|
|
responseState *WALState
|
|
|
|
// ClientID is the identity of the caller. If the token is associated with an
|
|
// entity, it will be the same as the EntityID . If the token has no entity,
|
|
// this will be the sha256(sorted policies + namespace) associated with the
|
|
// client token.
|
|
ClientID string `json:"client_id" structs:"client_id" mapstructure:"client_id" sentinel:""`
|
|
|
|
// InboundSSCToken is the token that arrives on an inbound request, supplied
|
|
// by the vault user.
|
|
InboundSSCToken string
|
|
|
|
// When a request has been forwarded, contains information of the host the request was forwarded 'from'
|
|
ForwardedFrom string `json:"forwarded_from,omitempty"`
|
|
|
|
// Name of the chroot namespace for the listener that the request was made against
|
|
ChrootNamespace string `json:"chroot_namespace,omitempty"`
|
|
|
|
// RequestLimiterDisabled tells whether the request context has Request Limiter applied.
|
|
RequestLimiterDisabled bool `json:"request_limiter_disabled,omitempty"`
|
|
|
|
// RequiresSnapshotID holds a loaded snapshot ID that the request will use,
|
|
// for either a read, list, or recover operation
|
|
RequiresSnapshotID string `json:"snapshot_id,omitempty"`
|
|
|
|
// RecoverSourcePath is the path where a recover request should read the data
|
|
// from. This can be empty if the request is not a recover request, or if the
|
|
// request is a recover request where the source path is the same as the
|
|
// destination path
|
|
RecoverSourcePath string `json:"recover_source_path,omitempty"`
|
|
}
|
|
|
|
// Clone returns a deep copy (almost) of the request.
|
|
// It will set unexported fields which were only previously accessible outside
|
|
// the package via receiver methods.
|
|
// NOTE: Request.Connection is NOT deep-copied, due to issues with the results
|
|
// of copystructure on serial numbers within the x509.Certificate objects.
|
|
func (r *Request) Clone() (*Request, error) {
|
|
cpy, err := copystructure.Copy(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req := cpy.(*Request)
|
|
|
|
// Add the unexported values that were only retrievable via receivers.
|
|
// copystructure isn't able to do this, which is why we're doing it manually.
|
|
req.mountClass = r.MountClass()
|
|
req.mountRunningVersion = r.MountRunningVersion()
|
|
req.mountRunningSha256 = r.MountRunningSha256()
|
|
req.mountIsExternalPlugin = r.MountIsExternalPlugin()
|
|
// This needs to be overwritten as the internal connection state is not cloned properly
|
|
// mainly the big.Int serial numbers within the x509.Certificate objects get mangled.
|
|
req.Connection = r.Connection
|
|
|
|
return req, nil
|
|
}
|
|
|
|
// Get returns a data field and guards for nil Data
|
|
func (r *Request) Get(key string) interface{} {
|
|
if r.Data == nil {
|
|
return nil
|
|
}
|
|
return r.Data[key]
|
|
}
|
|
|
|
// GetString returns a data field as a string
|
|
func (r *Request) GetString(key string) string {
|
|
raw := r.Get(key)
|
|
s, _ := raw.(string)
|
|
return s
|
|
}
|
|
|
|
func (r *Request) GoString() string {
|
|
return fmt.Sprintf("*%#v", *r)
|
|
}
|
|
|
|
func (r *Request) SentinelGet(key string) (interface{}, error) {
|
|
switch key {
|
|
case "path":
|
|
// Sanitize it here so that it's consistent in policies
|
|
return strings.TrimPrefix(r.Path, "/"), nil
|
|
|
|
case "wrapping", "wrap_info":
|
|
// If the pointer is nil accessing the wrap info is considered
|
|
// "undefined" so this allows us to instead discover a TTL of zero
|
|
if r.WrapInfo == nil {
|
|
return &RequestWrapInfo{}, nil
|
|
}
|
|
return r.WrapInfo, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (r *Request) SentinelKeys() []string {
|
|
return []string{
|
|
"path",
|
|
"wrapping",
|
|
"wrap_info",
|
|
}
|
|
}
|
|
|
|
func (r *Request) MountRunningVersion() string {
|
|
return r.mountRunningVersion
|
|
}
|
|
|
|
func (r *Request) SetMountRunningVersion(mountRunningVersion string) {
|
|
r.mountRunningVersion = mountRunningVersion
|
|
}
|
|
|
|
func (r *Request) MountRunningSha256() string {
|
|
return r.mountRunningSha256
|
|
}
|
|
|
|
func (r *Request) SetMountRunningSha256(mountRunningSha256 string) {
|
|
r.mountRunningSha256 = mountRunningSha256
|
|
}
|
|
|
|
func (r *Request) MountIsExternalPlugin() bool {
|
|
return r.mountIsExternalPlugin
|
|
}
|
|
|
|
func (r *Request) SetMountIsExternalPlugin(mountIsExternalPlugin bool) {
|
|
r.mountIsExternalPlugin = mountIsExternalPlugin
|
|
}
|
|
|
|
func (r *Request) MountClass() string {
|
|
return r.mountClass
|
|
}
|
|
|
|
func (r *Request) SetMountClass(mountClass string) {
|
|
r.mountClass = mountClass
|
|
}
|
|
|
|
func (r *Request) LastRemoteWAL() uint64 {
|
|
return r.lastRemoteWAL
|
|
}
|
|
|
|
func (r *Request) SetLastRemoteWAL(last uint64) {
|
|
r.lastRemoteWAL = last
|
|
}
|
|
|
|
func (r *Request) RequiredState() []string {
|
|
return r.requiredState
|
|
}
|
|
|
|
func (r *Request) SetRequiredState(state []string) {
|
|
r.requiredState = state
|
|
}
|
|
|
|
func (r *Request) ResponseState() *WALState {
|
|
return r.responseState
|
|
}
|
|
|
|
func (r *Request) SetResponseState(w *WALState) {
|
|
r.responseState = w
|
|
}
|
|
|
|
func (r *Request) TokenEntry() *TokenEntry {
|
|
return r.tokenEntry
|
|
}
|
|
|
|
func (r *Request) SetTokenEntry(te *TokenEntry) {
|
|
r.tokenEntry = te
|
|
}
|
|
|
|
// IsSnapshotReadOrList checks whether the request reads or lists from a
|
|
// snapshot. When this method returns true, handling the request should not
|
|
// modify any internal caches or state
|
|
func (r *Request) IsSnapshotReadOrList() bool {
|
|
return (r.Operation == ReadOperation || r.Operation == ListOperation) && r.RequiresSnapshotID != ""
|
|
}
|
|
|
|
// RenewRequest creates the structure of the renew request.
|
|
func RenewRequest(path string, secret *Secret, data map[string]interface{}) *Request {
|
|
return &Request{
|
|
Operation: RenewOperation,
|
|
Path: path,
|
|
Data: data,
|
|
Secret: secret,
|
|
}
|
|
}
|
|
|
|
// RenewAuthRequest creates the structure of the renew request for an auth.
|
|
func RenewAuthRequest(path string, auth *Auth, data map[string]interface{}) *Request {
|
|
return &Request{
|
|
Operation: RenewOperation,
|
|
Path: path,
|
|
Data: data,
|
|
Auth: auth,
|
|
}
|
|
}
|
|
|
|
// RevokeRequest creates the structure of the revoke request.
|
|
func RevokeRequest(path string, secret *Secret, data map[string]interface{}) *Request {
|
|
return &Request{
|
|
Operation: RevokeOperation,
|
|
Path: path,
|
|
Data: data,
|
|
Secret: secret,
|
|
}
|
|
}
|
|
|
|
// RollbackRequest creates the structure of the revoke request.
|
|
func RollbackRequest(path string) *Request {
|
|
return &Request{
|
|
Operation: RollbackOperation,
|
|
Path: path,
|
|
Data: make(map[string]interface{}),
|
|
}
|
|
}
|
|
|
|
// Operation is an enum that is used to specify the type
|
|
// of request being made
|
|
type Operation string
|
|
|
|
const (
|
|
// The operations below are called per path
|
|
CreateOperation Operation = "create"
|
|
ReadOperation = "read"
|
|
UpdateOperation = "update"
|
|
PatchOperation = "patch"
|
|
DeleteOperation = "delete"
|
|
ListOperation = "list"
|
|
HelpOperation = "help"
|
|
AliasLookaheadOperation = "alias-lookahead"
|
|
ResolveRoleOperation = "resolve-role"
|
|
HeaderOperation = "header"
|
|
RecoverOperation = "recover"
|
|
|
|
// The operations below are called globally, the path is less relevant.
|
|
RevokeOperation Operation = "revoke"
|
|
RenewOperation = "renew"
|
|
RollbackOperation = "rollback"
|
|
RotationOperation = "rotate"
|
|
)
|
|
|
|
type MFACreds map[string][]string
|
|
|
|
// InitializationRequest stores the parameters and context of an Initialize()
|
|
// call being made to a logical.Backend.
|
|
type InitializationRequest struct {
|
|
// Storage can be used to durably store and retrieve state.
|
|
Storage Storage
|
|
}
|
|
|
|
type CustomHeader struct {
|
|
Name string
|
|
Value string
|
|
}
|
|
|
|
type CtxKeyInFlightRequestID struct{}
|
|
|
|
func (c CtxKeyInFlightRequestID) String() string {
|
|
return "in-flight-request-ID"
|
|
}
|
|
|
|
type CtxKeyInFlightRequestPriority struct{}
|
|
|
|
func (c CtxKeyInFlightRequestPriority) String() string {
|
|
return "in-flight-request-priority"
|
|
}
|
|
|
|
// CtxKeyInFlightTraceID is used for passing a trace ID through request
|
|
// forwarding. The CtxKeyInFlightRequestID created at the HTTP layer is
|
|
// propagated on through any forwarded requests using this key.
|
|
//
|
|
// Note that this applies to replication service RPCs (including
|
|
// ForwardingRequest from perf standbys or secondaries). The Forwarding RPC
|
|
// service may propagate the context but the handling on the active node runs
|
|
// back through the `http` package handler which builds a new context from HTTP
|
|
// request properties and creates a fresh request ID. Forwarding RPC is used
|
|
// exclusively in Community Edition but also in some special cases in Enterprise
|
|
// such as when forwarding is forced by an HTTP header.
|
|
type CtxKeyInFlightTraceID struct{}
|
|
|
|
func (c CtxKeyInFlightTraceID) String() string {
|
|
return "in-flight-trace-ID"
|
|
}
|
|
|
|
type CtxKeyRequestRole struct{}
|
|
|
|
func (c CtxKeyRequestRole) String() string {
|
|
return "request-role"
|
|
}
|
|
|
|
// ctxKeyDisableReplicationStatusEndpoints is a custom type used as a key in
|
|
// context.Context to store the value `true` when the
|
|
// disable_replication_status_endpoints configuration parameter is set to true
|
|
// for the listener through which a request was received.
|
|
type ctxKeyDisableReplicationStatusEndpoints struct{}
|
|
|
|
// String returns a string representation of the receiver type.
|
|
func (c ctxKeyDisableReplicationStatusEndpoints) String() string {
|
|
return "disable-replication-status-endpoints"
|
|
}
|
|
|
|
// ContextDisableReplicationStatusEndpointsValue examines the provided
|
|
// context.Context for the disable replication status endpoints value and
|
|
// returns it as a bool value if it's found along with the ok return value set
|
|
// to true; otherwise the ok return value is false.
|
|
func ContextDisableReplicationStatusEndpointsValue(ctx context.Context) (value, ok bool) {
|
|
value, ok = ctx.Value(ctxKeyDisableReplicationStatusEndpoints{}).(bool)
|
|
|
|
return
|
|
}
|
|
|
|
// CreateContextDisableReplicationStatusEndpoints creates a new context.Context
|
|
// based on the provided parent that also includes the provided value for the
|
|
// ctxKeyDisableReplicationStatusEndpoints key.
|
|
func CreateContextDisableReplicationStatusEndpoints(parent context.Context, value bool) context.Context {
|
|
return context.WithValue(parent, ctxKeyDisableReplicationStatusEndpoints{}, value)
|
|
}
|
|
|
|
// CtxKeyOriginalRequestPath is a custom type used as a key in context.Context
|
|
// to store the original request path.
|
|
type ctxKeyOriginalRequestPath struct{}
|
|
|
|
// String returns a string representation of the receiver type.
|
|
func (c ctxKeyOriginalRequestPath) String() string {
|
|
return "original_request_path"
|
|
}
|
|
|
|
// ContextOriginalRequestPathValue examines the provided context.Context for the
|
|
// original request path value and returns it as a string value if it's found
|
|
// along with the ok value set to true; otherwise the ok return value is false.
|
|
func ContextOriginalRequestPathValue(ctx context.Context) (value string, ok bool) {
|
|
value, ok = ctx.Value(ctxKeyOriginalRequestPath{}).(string)
|
|
|
|
return
|
|
}
|
|
|
|
// CreateContextOriginalRequestPath creates a new context.Context based on the
|
|
// provided parent that also includes the provided original request path value
|
|
// for the ctxKeyOriginalRequestPath key.
|
|
func CreateContextOriginalRequestPath(parent context.Context, value string) context.Context {
|
|
return context.WithValue(parent, ctxKeyOriginalRequestPath{}, value)
|
|
}
|
|
|
|
type ctxKeyOriginalBody struct{}
|
|
|
|
func ContextOriginalBodyValue(ctx context.Context) (io.ReadCloser, bool) {
|
|
value, ok := ctx.Value(ctxKeyOriginalBody{}).(io.ReadCloser)
|
|
return value, ok
|
|
}
|
|
|
|
func CreateContextOriginalBody(parent context.Context, body io.ReadCloser) context.Context {
|
|
return context.WithValue(parent, ctxKeyOriginalBody{}, body)
|
|
}
|
|
|
|
type CtxKeyDisableRequestLimiter struct{}
|
|
|
|
func (c CtxKeyDisableRequestLimiter) String() string {
|
|
return "disable_request_limiter"
|
|
}
|
|
|
|
// ctxKeyRedactionSettings is a custom type used as a key in context.Context to
|
|
// store the value the redaction settings for the listener that received the
|
|
// request.
|
|
type ctxKeyRedactionSettings struct{}
|
|
|
|
// String returns a string representation of the receiver type.
|
|
func (c ctxKeyRedactionSettings) String() string {
|
|
return "redaction-settings"
|
|
}
|
|
|
|
// CtxRedactionSettingsValue examines the provided context.Context for the
|
|
// redaction settings value and returns them as a tuple of bool values if they
|
|
// are found along with the ok return value set to true; otherwise the ok return
|
|
// value is false.
|
|
func CtxRedactionSettingsValue(ctx context.Context) (redactVersion, redactAddresses, redactClusterName, ok bool) {
|
|
value, ok := ctx.Value(ctxKeyRedactionSettings{}).([]bool)
|
|
if !ok {
|
|
return false, false, false, false
|
|
}
|
|
|
|
return value[0], value[1], value[2], true
|
|
}
|
|
|
|
// CreatecontextRedactionSettings creates a new context.Context based on the
|
|
// provided parent that also includes the provided redaction settings values for
|
|
// the ctxKeyRedactionSettings key.
|
|
func CreateContextRedactionSettings(parent context.Context, redactVersion, redactAddresses, redactClusterName bool) context.Context {
|
|
return context.WithValue(parent, ctxKeyRedactionSettings{}, []bool{redactVersion, redactAddresses, redactClusterName})
|
|
}
|
|
|
|
type ctxKeySnapshotID struct{}
|
|
|
|
func (c ctxKeySnapshotID) String() string {
|
|
return "snapshot-id"
|
|
}
|
|
|
|
// CreateContextWithSnapshotID creates a new context indicating that any storage
|
|
// operations should be done using the given snapshot ID. If the value is empty,
|
|
// it means that the request should use the normal storage.
|
|
func CreateContextWithSnapshotID(parent context.Context, value string) context.Context {
|
|
return context.WithValue(parent, ctxKeySnapshotID{}, value)
|
|
}
|
|
|
|
// ContextSnapshotIDValue retrieves the snapshot ID value stored in the context.
|
|
// This value can be empty, indicating that the request should use the normal
|
|
// storage.
|
|
func ContextSnapshotIDValue(ctx context.Context) (value string, ok bool) {
|
|
value, ok = ctx.Value(ctxKeySnapshotID{}).(string)
|
|
return
|
|
}
|