H1: HMAC-gate internal endpoints /ticket /order /person (TDD)
Failing tests first (4 red: unsigned POSTs returned 200 and minted objects), then the gate: host/blog--int-verify? checks x-int-sig = sess-sig(fed-secret, request TARGET) (params live in the query, body is empty); host/blog--protect-internal wraps the three routes → 403 unsigned. Secret unset = open (dev/tests). Callers (events→shop /ticket + /order, shop→identity /person) sign via host/blog--int-headers. Closes the live capacity-bypass (anyone could mint tickets directly). blog suite 225/225 (218 + 7 new). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -161,7 +161,9 @@
|
||||
;; domain call, like the RA kernel — not everything is a federated activity; directed reads/writes
|
||||
;; are fine.)
|
||||
(define host/blog--http-order
|
||||
(fn (event) (get (http-request "POST" (str host/blog--shop-base "/order?event=" event) {} "") "body")))
|
||||
(fn (event)
|
||||
(let ((target (str "/order?event=" event)))
|
||||
(get (http-request "POST" (str host/blog--shop-base target) (host/blog--int-headers target) "") "body"))))
|
||||
(define host/blog--allocate-activity
|
||||
(fn (post calendar)
|
||||
{:verb "allocate" :actor host/blog--actor
|
||||
@@ -256,6 +258,21 @@
|
||||
(fn (req body)
|
||||
(or (= host/blog--fed-secret "")
|
||||
(= (str (or (dream-header req "x-fed-sig") "")) (dr/sess-sig host/blog--fed-secret body)))))
|
||||
;; H1: INTERNAL service endpoints (/ticket /order /person) are peer-to-peer only — the caller signs
|
||||
;; the request TARGET (path+query; these carry params in the query, body is empty) with the shared
|
||||
;; fed secret in x-int-sig. Unsigned/miss-signed → 403. Secret unset ("") = open (dev/tests).
|
||||
(define host/blog--int-verify?
|
||||
(fn (req)
|
||||
(or (= host/blog--fed-secret "")
|
||||
(= (str (or (dream-header req "x-int-sig") ""))
|
||||
(dr/sess-sig host/blog--fed-secret (get req :target))))))
|
||||
(define host/blog--protect-internal
|
||||
(fn (handler)
|
||||
(fn (req)
|
||||
(if (host/blog--int-verify? req) (handler req)
|
||||
(dream-response 403 {:content-type "text/plain; charset=utf-8"} "forbidden: internal endpoint")))))
|
||||
;; sign an internal call's target for http-request headers.
|
||||
(define host/blog--int-headers (fn (target) {"x-int-sig" (host/blog--fed-sign target)}))
|
||||
;; FOLLOW another actor: POST a follow to its /inbox announcing OUR actor + base, so it delivers to us.
|
||||
(define host/blog--follow!
|
||||
(fn (target-base)
|
||||
@@ -3253,9 +3270,9 @@
|
||||
(let ((actor (str email "#" (str (len (ev/roster host/blog-store showing))))))
|
||||
(let ((bk (ev/book! host/blog-store showing (host/blog--showing-capacity showing) actor)))
|
||||
(when (= (get bk :status) :booked)
|
||||
(let ((body (get (http-request "POST"
|
||||
(str host/blog--shop-base "/ticket?showing=" showing "&offering=" offering "&email=" email)
|
||||
{} "") "body")))
|
||||
(let ((body (let ((target (str "/ticket?showing=" showing "&offering=" offering "&email=" email)))
|
||||
(get (http-request "POST" (str host/blog--shop-base target)
|
||||
(host/blog--int-headers target) "") "body"))))
|
||||
(when (starts-with? body "ticket:")
|
||||
(let ((tid (substr body 7 (- (len body) 7))))
|
||||
(begin
|
||||
@@ -3315,7 +3332,8 @@
|
||||
(host/blog-relate! tid email "owned-by")
|
||||
(host/blog--set-field-values! tid {"email" email})
|
||||
(when (not (= host/blog--identity-base ""))
|
||||
(http-request "POST" (str host/blog--identity-base "/person?email=" email) {} ""))
|
||||
(let ((target (str "/person?email=" email)))
|
||||
(http-request "POST" (str host/blog--identity-base target) (host/blog--int-headers target) "")))
|
||||
(dream-response 200 {:content-type "text/plain; charset=utf-8"} (str "ticket:" tid)))))))
|
||||
;; identity: find-or-create a Person keyed by a contact-id (email), login-optional → "person:<id>".
|
||||
(define host/blog-person
|
||||
@@ -3521,11 +3539,11 @@
|
||||
(dream-post "/offering-remove" host/blog-offering-remove)
|
||||
(dream-post "/add-poll" host/blog-add-poll)
|
||||
(dream-post "/vote" host/blog-vote)
|
||||
(dream-post "/ticket" host/blog-ticket)
|
||||
(dream-post "/person" host/blog-person)
|
||||
(dream-post "/ticket" (host/blog--protect-internal host/blog-ticket))
|
||||
(dream-post "/person" (host/blog--protect-internal host/blog-person))
|
||||
(dream-post "/new-event" host/blog-new-event)
|
||||
(dream-post "/buy" host/blog-buy)
|
||||
(dream-post "/order" host/blog-order)
|
||||
(dream-post "/order" (host/blog--protect-internal host/blog-order))
|
||||
(dream-get "/activities" host/blog-activities)
|
||||
(dream-get "/:slug/source" host/blog-source)
|
||||
(dream-get "/:slug/relate-options" host/blog-relate-options)
|
||||
|
||||
Reference in New Issue
Block a user