fix: tolerate retried internal api nonce
This commit is contained in:
+22
-8
@@ -185,7 +185,8 @@ func authorizeInternalAPI(wrt http.ResponseWriter, req *http.Request, capability
|
||||
writeIMError(wrt, req, http.StatusUnauthorized, 30043, imReasonAuthRequired, "invalid internal signature", nil)
|
||||
return false
|
||||
}
|
||||
if !claimInternalNonce(serviceName, nonce, time.Now().Add(window)) {
|
||||
requestClaim := internalRequestClaimValue(signingText)
|
||||
if !claimInternalNonce(serviceName, nonce, requestClaim, time.Now().Add(window)) {
|
||||
writeIMError(wrt, req, http.StatusUnauthorized, 30043, imReasonAuthRequired, "internal nonce replayed", nil)
|
||||
return false
|
||||
}
|
||||
@@ -572,23 +573,36 @@ func verifyInternalSignature(secret, signingText, signature string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func claimInternalNonce(serviceName, nonce string, expiresAt time.Time) bool {
|
||||
func claimInternalNonce(serviceName, nonce, requestClaim string, expiresAt time.Time) bool {
|
||||
ttl := time.Until(expiresAt)
|
||||
if ok, enabled, err := store.ClaimShortState("internal_api_nonce", fmt.Sprintf("%s:%s", serviceName, nonce), ttl); enabled {
|
||||
nonceKey := fmt.Sprintf("%s:%s", serviceName, nonce)
|
||||
if claimed, sameValue, enabled, err := store.ClaimShortStateValue("internal_api_nonce", nonceKey, requestClaim, ttl); enabled {
|
||||
if err != nil {
|
||||
logs.Warn.Println("internal api: failed to claim nonce in redis", err)
|
||||
return false
|
||||
}
|
||||
return ok
|
||||
return claimed || sameValue
|
||||
}
|
||||
now := time.Now()
|
||||
internalAPI.nonces.Range(func(key, value any) bool {
|
||||
if expires, ok := value.(time.Time); ok && expires.Before(now) {
|
||||
if claim, ok := value.(internalNonceClaim); ok && claim.expiresAt.Before(now) {
|
||||
internalAPI.nonces.Delete(key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
nonceKey := fmt.Sprintf("%s:%s", serviceName, nonce)
|
||||
_, loaded := internalAPI.nonces.LoadOrStore(nonceKey, expiresAt)
|
||||
return !loaded
|
||||
if existing, loaded := internalAPI.nonces.LoadOrStore(nonceKey, internalNonceClaim{value: requestClaim, expiresAt: expiresAt}); loaded {
|
||||
claim, ok := existing.(internalNonceClaim)
|
||||
return ok && claim.value == requestClaim
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type internalNonceClaim struct {
|
||||
value string
|
||||
expiresAt time.Time
|
||||
}
|
||||
|
||||
func internalRequestClaimValue(signingText string) string {
|
||||
sum := sha256.Sum256([]byte(signingText))
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ type groupMessageRuntime interface {
|
||||
CachedSetContains(tenantID types.TenantID, namespace, id, member string) (bool, error)
|
||||
AllowRate(tenantID types.TenantID, scope string, limit int, window time.Duration) (bool, int, time.Duration, error)
|
||||
ClaimShortState(namespace, id string, ttl time.Duration) (bool, error)
|
||||
ClaimShortStateValue(namespace, id, value string, ttl time.Duration) (bool, bool, error)
|
||||
}
|
||||
|
||||
var groupMsgRuntime groupMessageRuntime
|
||||
@@ -97,6 +98,14 @@ func ClaimShortState(namespace, id string, ttl time.Duration) (bool, bool, error
|
||||
return ok, true, err
|
||||
}
|
||||
|
||||
func ClaimShortStateValue(namespace, id, value string, ttl time.Duration) (bool, bool, bool, error) {
|
||||
if groupMsgRuntime == nil || namespace == "" || id == "" || value == "" {
|
||||
return false, false, false, nil
|
||||
}
|
||||
claimed, sameValue, err := groupMsgRuntime.ClaimShortStateValue(namespace, id, value, ttl)
|
||||
return claimed, sameValue, true, err
|
||||
}
|
||||
|
||||
func RefreshUserPresence(tenantID types.TenantID, uid types.Uid, sid, nodeID string) error {
|
||||
if groupMsgRuntime == nil || !tenantID.IsValid() || uid.IsZero() || sid == "" {
|
||||
return nil
|
||||
@@ -753,6 +762,23 @@ func (r *redisGroupMessageRuntime) ClaimShortState(namespace, id string, ttl tim
|
||||
return r.client.SetNX(context.Background(), r.shortStateKey(namespace, id), "1", ttl).Result()
|
||||
}
|
||||
|
||||
func (r *redisGroupMessageRuntime) ClaimShortStateValue(namespace, id, value string, ttl time.Duration) (bool, bool, error) {
|
||||
if ttl <= 0 {
|
||||
ttl = time.Duration(r.cfg.ShortTTL) * time.Second
|
||||
}
|
||||
key := r.shortStateKey(namespace, id)
|
||||
ctx := context.Background()
|
||||
claimed, err := r.client.SetNX(ctx, key, value, ttl).Result()
|
||||
if err != nil || claimed {
|
||||
return claimed, false, err
|
||||
}
|
||||
existing, err := r.client.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
return false, existing == value, nil
|
||||
}
|
||||
|
||||
func (r *redisGroupMessageRuntime) hotSeqIDs(tenantID types.TenantID, topic string, opts *types.QueryOpt, limit int) ([]int64, bool, error) {
|
||||
if opts != nil && len(opts.IdRanges) > 0 {
|
||||
seqs := expandRanges(opts.IdRanges, limit)
|
||||
|
||||
Reference in New Issue
Block a user