-
-
Notifications
You must be signed in to change notification settings - Fork 344
Expand file tree
/
Copy pathauthorization_deprecated.go
More file actions
139 lines (112 loc) 路 4.36 KB
/
Copy pathauthorization_deprecated.go
File metadata and controls
139 lines (112 loc) 路 4.36 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
//go:build deprecated_claim
package mercure
import (
"errors"
"net/http"
)
// ErrTooManyClaimMatchers is returned when the legacy mercure.subscribe or
// mercure.publish claim exceeds maxClaimMatchers.
var ErrTooManyClaimMatchers = errors.New("too many matchers in mercure claim")
// legacyCookieName is the pre-1.0 authorization cookie name, accepted as a
// fallback only in compatibility mode. The modern name is defaultCookieName.
const legacyCookieName = "mercureAuthorization"
// legacyAuthorizationParam is the deprecated "authorization" URI query
// parameter carrying the access token, honored only in compatibility mode.
// Modern mode accepts no query parameter (RFC 9700 搂4.3.2).
const legacyAuthorizationParam = "authorization"
// compatClaimsEnabled reports whether legacy mercure-claim behavior is active:
// the code is compiled in and the operator enabled compatibility mode.
func (h *Hub) compatClaimsEnabled() bool {
return h.isBackwardCompatiblyEnabledWith(8)
}
// requireATJWT reports whether access tokens must carry the at+jwt typ header
// and a matching audience. Relaxed in compatibility mode so legacy tokens
// (which predate RFC 9068) keep working.
func (h *Hub) requireATJWT() bool {
return !h.compatClaimsEnabled()
}
// legacyAuthQueryParam returns the token carried by the deprecated
// "authorization" query parameter when compatibility mode is enabled. Modern
// mode accepts no query parameter (RFC 9700 搂4.3.2).
func (h *Hub) legacyAuthQueryParam(r *http.Request) (string, bool) {
if !h.compatClaimsEnabled() {
return "", false
}
q, ok := r.URL.Query()[legacyAuthorizationParam]
if !ok || len(q) != 1 || len(q[0]) < minCompactJWSLen {
return "", false
}
return q[0], true
}
// readCookie returns the authorization cookie. In compatibility mode, the
// pre-1.0 cookie name is accepted as a fallback when the configured name is
// absent, so subscribers still sending "mercureAuthorization" keep working.
func (h *Hub) readCookie(r *http.Request) (*http.Cookie, error) {
cookie, err := r.Cookie(h.cookieName)
if err == nil || !h.compatClaimsEnabled() {
return cookie, err //nolint:wrapcheck
}
return r.Cookie(legacyCookieName) //nolint:wrapcheck
}
// resolveLegacyClaims converts the legacy mercure claim into the validated
// authorization details shape, so the grant logic is uniform across token
// formats. It runs only in compatibility mode.
func (h *Hub) resolveLegacyClaims(c *claims) error {
if !h.compatClaimsEnabled() {
return nil
}
mc := c.Mercure
if c.MercureNamespaced != nil {
mc = *c.MercureNamespaced
}
if len(mc.Publish) > maxClaimMatchers || len(mc.Subscribe) > maxClaimMatchers {
return ErrTooManyClaimMatchers
}
// Bare-string v8 claims are only meaningful when the deprecated_topic
// matcher code is compiled in. Using isBackwardCompatiblyEnabledWith(8)
// here would be always-true (this function already returned unless compat
// is on), so a "*" string claim would authorize every topic via the
// wildcard short-circuit even in a deprecated_claim-only build where the
// v8 matcher is absent.
deprecated := deprecatedMatcherTypeCompiled()
if err := resolveMatcherClaims(h.topicMatcherStore, mc.Publish, deprecated); err != nil {
return err
}
if err := resolveMatcherClaims(h.topicMatcherStore, mc.Subscribe, deprecated); err != nil {
return err
}
c.Mercure = mc
c.authz = mercureAuthzFromLegacy(mc)
return nil
}
// mercureAuthzFromLegacy builds a mercureAuthz from a resolved legacy claim,
// one subscribe detail per claim so per-claim payloads are preserved.
func mercureAuthzFromLegacy(mc mercureClaim) *mercureAuthz {
authz := &mercureAuthz{}
if len(mc.Publish) > 0 {
authz.details = append(authz.details, validatedDetail{publish: true, topics: matcherClaimTopics(mc.Publish)})
}
for _, sc := range mc.Subscribe {
authz.details = append(authz.details, validatedDetail{
subscribe: true,
topics: []TopicMatcher{sc.TopicMatcher},
payload: sc.Payload,
})
}
return authz
}
func matcherClaimTopics(claims []matcherClaim) []TopicMatcher {
topics := make([]TopicMatcher, len(claims))
for i := range claims {
topics[i] = claims[i].TopicMatcher
}
return topics
}
// legacyPayloadFallback returns the global mercure.payload, used when no
// per-subscription payload matched.
func (s *Subscriber) legacyPayloadFallback() any {
if s.Claims == nil {
return nil
}
return s.Claims.Mercure.Payload
}