Repository navigation
Expand file tree
/
Copy pathheaders.go
More file actions
147 lines (122 loc) · 4.59 KB
/
Copy pathheaders.go
File metadata and controls
147 lines (122 loc) · 4.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package dauth
import (
"context"
"maps"
"slices"
"strings"
"sync/atomic"
"google.golang.org/grpc/metadata"
)
const (
// Deprecated: Use [HeaderOrganizationID] instead, we might in the future keep both header,
// but the meaning of both will change. For now, you can assume that user id == organization id
// as it's how we bill and organize things for now.
HeaderUserID string = "x-user-id"
// HeaderOrganizationID is the header carrying the organization id, the actual header is
// named x-user-id for backward compatibility reasons to keep downstream impact minimal for
// now, but it's really populated with the organization id.
HeaderOrganizationID string = "x-user-id"
HeaderApiKeyID string = "x-api-key-id"
HeaderMeta string = "x-meta"
HeaderIP string = "x-real-ip"
HeaderSubstreamsPlanTier string = "x-substreams-plan-tier" // As of August 2025, one of FREE, SCALING, PRO, ENTERPRISE
// Internal: Do not use, only use while transitioning to the new header name,
// will be removed in the future, always use [HeaderOrganizationID]
// instead of this.
HeaderNewOrganizationID string = "x-organization-id"
deprecatedHeaderUserID string = "x-user-id"
deprecatedSfHeaderUserID string = "x-sf-user-id"
deprecatedSfHeaderApiKeyID string = "x-sf-api-key-id"
deprecatedSfHeaderMeta string = "x-sf-meta"
)
type TrustedHeaders map[string]string
type trustedHeadersKeyType int
const trustedHeadersKey trustedHeadersKeyType = iota
type trustedHeadersHolder struct {
headers atomic.Pointer[TrustedHeaders]
}
// WithTrustedHeaders attaches h to a new context through a fresh holder. Calling it again on a
// context that already carries trusted headers shadows them: the returned context and its
// children no longer see a [ReplaceTrustedHeaders] made on the parent context.
func WithTrustedHeaders(ctx context.Context, h TrustedHeaders) context.Context {
holder := &trustedHeadersHolder{}
holder.headers.Store(lowercase(h))
return context.WithValue(ctx, trustedHeadersKey, holder)
}
// FromContext returns a snapshot of the trusted headers, call it again to see a later [ReplaceTrustedHeaders].
//
// The returned map is shared with every other reader of ctx and must not be modified, a
// concurrent [ReplaceTrustedHeaders] (e.g. from continuous authentication) reads it.
func FromContext(ctx context.Context) TrustedHeaders {
holder, ok := ctx.Value(trustedHeadersKey).(*trustedHeadersHolder)
if !ok {
return nil
}
return *holder.headers.Load()
}
// ReplaceTrustedHeaders swaps the trusted headers of ctx and every context derived from it,
// returning false if ctx carries no trusted headers. The swap is atomic but a read-modify-write
// built on [FromContext] is not, callers must ensure a single writer per context.
func ReplaceTrustedHeaders(ctx context.Context, h TrustedHeaders) bool {
holder, ok := ctx.Value(trustedHeadersKey).(*trustedHeadersHolder)
if !ok {
return false
}
holder.headers.Store(lowercase(h))
return true
}
func lowercase(h TrustedHeaders) *TrustedHeaders {
lowercased := make(TrustedHeaders, len(h))
for k, v := range h {
lowercased[strings.ToLower(k)] = v
}
return &lowercased
}
// Deprecated: use [OrganizationID] instead, the [HeaderUserID] now carries the organization id.
// (the [HeaderUserID] is kept for backward compatibility reasons but is also deprecated, use
// [HeaderOrganizationID] instead).
func (h TrustedHeaders) UserID() string {
if u, ok := h[deprecatedHeaderUserID]; ok {
return u
}
return h[deprecatedSfHeaderUserID]
}
// OrganizationID returns the organization id present in the trusted headers,
// returns "" if not present.
func (h TrustedHeaders) OrganizationID() string {
if u, ok := h[HeaderNewOrganizationID]; ok {
return u
}
if u, ok := h[deprecatedHeaderUserID]; ok {
return u
}
return h[deprecatedSfHeaderUserID]
}
func (h TrustedHeaders) APIKeyID() string {
if u, ok := h[HeaderApiKeyID]; ok {
return u
}
return h[deprecatedSfHeaderApiKeyID]
}
func (h TrustedHeaders) Meta() string {
if u, ok := h[HeaderMeta]; ok {
return u
}
return h[deprecatedSfHeaderMeta]
}
func (h TrustedHeaders) RealIP() string {
return h[HeaderIP]
}
func (h TrustedHeaders) SubstreamsPlanTier() string {
return h[HeaderSubstreamsPlanTier]
}
func (h TrustedHeaders) Get(key string) string {
return h[strings.ToLower(key)]
}
func (h TrustedHeaders) ToOutgoingGRPCContext(ctx context.Context) context.Context {
return metadata.NewOutgoingContext(ctx, metadata.New(h))
}
// Names returns the list of header names present in the TrustedHeaders.
func (h TrustedHeaders) Names() []string {
return slices.Collect(maps.Keys(h))
}