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)
|
writeIMError(wrt, req, http.StatusUnauthorized, 30043, imReasonAuthRequired, "invalid internal signature", nil)
|
||||||
return false
|
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)
|
writeIMError(wrt, req, http.StatusUnauthorized, 30043, imReasonAuthRequired, "internal nonce replayed", nil)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -572,23 +573,36 @@ func verifyInternalSignature(secret, signingText, signature string) bool {
|
|||||||
return false
|
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)
|
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 {
|
if err != nil {
|
||||||
logs.Warn.Println("internal api: failed to claim nonce in redis", err)
|
logs.Warn.Println("internal api: failed to claim nonce in redis", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return ok
|
return claimed || sameValue
|
||||||
}
|
}
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
internalAPI.nonces.Range(func(key, value any) bool {
|
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)
|
internalAPI.nonces.Delete(key)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
nonceKey := fmt.Sprintf("%s:%s", serviceName, nonce)
|
if existing, loaded := internalAPI.nonces.LoadOrStore(nonceKey, internalNonceClaim{value: requestClaim, expiresAt: expiresAt}); loaded {
|
||||||
_, loaded := internalAPI.nonces.LoadOrStore(nonceKey, expiresAt)
|
claim, ok := existing.(internalNonceClaim)
|
||||||
return !loaded
|
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)
|
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)
|
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)
|
ClaimShortState(namespace, id string, ttl time.Duration) (bool, error)
|
||||||
|
ClaimShortStateValue(namespace, id, value string, ttl time.Duration) (bool, bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var groupMsgRuntime groupMessageRuntime
|
var groupMsgRuntime groupMessageRuntime
|
||||||
@@ -97,6 +98,14 @@ func ClaimShortState(namespace, id string, ttl time.Duration) (bool, bool, error
|
|||||||
return ok, true, err
|
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 {
|
func RefreshUserPresence(tenantID types.TenantID, uid types.Uid, sid, nodeID string) error {
|
||||||
if groupMsgRuntime == nil || !tenantID.IsValid() || uid.IsZero() || sid == "" {
|
if groupMsgRuntime == nil || !tenantID.IsValid() || uid.IsZero() || sid == "" {
|
||||||
return nil
|
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()
|
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) {
|
func (r *redisGroupMessageRuntime) hotSeqIDs(tenantID types.TenantID, topic string, opts *types.QueryOpt, limit int) ([]int64, bool, error) {
|
||||||
if opts != nil && len(opts.IdRanges) > 0 {
|
if opts != nil && len(opts.IdRanges) > 0 {
|
||||||
seqs := expandRanges(opts.IdRanges, limit)
|
seqs := expandRanges(opts.IdRanges, limit)
|
||||||
|
|||||||
Reference in New Issue
Block a user