handleInvalidateLimits busts the in-process limits cache for the given user. Called by the external provisioner (billing sidecar) immediately after it writes account_limits, so the next request from that user sees the new caps without waiting ~60s for natural TTL expiry. Authentication is a shared
(w http.ResponseWriter, r *http.Request)
| 61 | // provisioner. Self-hosters who don't run a provisioner simply leave |
| 62 | // InternalAPISecret empty and the endpoint 503s. |
| 63 | func (a *API) handleInvalidateLimits(w http.ResponseWriter, r *http.Request) { |
| 64 | if a.internalAPISecret == "" { |
| 65 | http.Error(w, "internal api not configured", http.StatusServiceUnavailable) |
| 66 | return |
| 67 | } |
| 68 | if a.enforcer == nil { |
| 69 | http.Error(w, "limits subsystem not configured", http.StatusServiceUnavailable) |
| 70 | return |
| 71 | } |
| 72 | |
| 73 | body, err := io.ReadAll(http.MaxBytesReader(w, r.Body, 1024)) |
| 74 | if err != nil { |
| 75 | http.Error(w, "invalid request body", http.StatusBadRequest) |
| 76 | return |
| 77 | } |
| 78 | |
| 79 | sig := r.Header.Get("X-E2A-Internal-Signature") |
| 80 | if sig == "" { |
| 81 | http.Error(w, "missing signature", http.StatusUnauthorized) |
| 82 | return |
| 83 | } |
| 84 | expected := hmacHexSHA256([]byte(a.internalAPISecret), body) |
| 85 | if subtle.ConstantTimeCompare([]byte(sig), []byte(expected)) != 1 { |
| 86 | http.Error(w, "invalid signature", http.StatusUnauthorized) |
| 87 | return |
| 88 | } |
| 89 | |
| 90 | var req invalidateLimitsRequest |
| 91 | if err := json.Unmarshal(body, &req); err != nil { |
| 92 | http.Error(w, "invalid request body", http.StatusBadRequest) |
| 93 | return |
| 94 | } |
| 95 | if req.UserID == "" { |
| 96 | http.Error(w, "user_id is required", http.StatusBadRequest) |
| 97 | return |
| 98 | } |
| 99 | |
| 100 | a.enforcer.Invalidate(req.UserID) |
| 101 | w.WriteHeader(http.StatusNoContent) |
| 102 | } |
| 103 | |
| 104 | func hmacHexSHA256(key, body []byte) string { |
| 105 | h := hmac.New(sha256.New, key) |
nothing calls this directly
no test coverage detected