diff --git a/lib/host/blog.sx b/lib/host/blog.sx index 5f29cc66..9458496f 100644 --- a/lib/host/blog.sx +++ b/lib/host/blog.sx @@ -3531,17 +3531,12 @@ (dream-get "/cinema" host/blog-cinema) (dream-get "/orders" host/blog-orders) (dream-get "/people" host/blog-people) - (dream-post "/new-film" host/blog-new-film) - (dream-post "/new-showing" host/blog-new-showing) + ;; customer/voter ops — deliberately PUBLIC; internal service ops — HMAC-gated (H1). + ;; Admin ops (new-film/new-showing/offering-*/add-poll/new-event) are in write-routes (H2). (dream-post "/buy-ticket" host/blog-buy-ticket) - (dream-post "/offering-add" host/blog-offering-add) - (dream-post "/offering-update" host/blog-offering-update) - (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--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--protect-internal host/blog-order)) (dream-get "/activities" host/blog-activities) @@ -3577,6 +3572,14 @@ (list (dream-post "/new" (host/blog--protect-html resolve host/blog-form-submit)) (dream-post "/:slug/allocate" (host/blog--protect-html resolve host/blog-allocate)) + ;; H2: cinema/poll ADMIN ops — auth-gated like every other write. + (dream-post "/new-film" (host/blog--protect-html resolve host/blog-new-film)) + (dream-post "/new-showing" (host/blog--protect-html resolve host/blog-new-showing)) + (dream-post "/offering-add" (host/blog--protect-html resolve host/blog-offering-add)) + (dream-post "/offering-update" (host/blog--protect-html resolve host/blog-offering-update)) + (dream-post "/offering-remove" (host/blog--protect-html resolve host/blog-offering-remove)) + (dream-post "/add-poll" (host/blog--protect-html resolve host/blog-add-poll)) + (dream-post "/new-event" (host/blog--protect-html resolve host/blog-new-event)) (dream-get "/:slug/edit" (host/blog--protect-html resolve host/blog-edit-form)) (dream-post "/:slug/edit" (host/blog--protect-html resolve host/blog-edit-submit)) (dream-post "/:slug/blocks/add" (host/blog--protect-html resolve host/blog-block-add-submit)) diff --git a/lib/host/tests/blog.sx b/lib/host/tests/blog.sx index 6b0d8b7f..5644cd8d 100644 --- a/lib/host/tests/blog.sx +++ b/lib/host/tests/blog.sx @@ -1346,6 +1346,39 @@ (starts-with? (dream-resp-body (host-bl-h1-post "/person?email=b@x.com" false)) "person:") true) +;; ── HARDENING H2: cinema/poll ADMIN ops require auth; customer ops stay public ──────────── +;; new-film / new-showing / offering-* / add-poll / new-event live behind protect-html (like /new); +;; /vote and /buy-ticket remain public (voters + customers). +(host/blog-use-store! (persist/open)) +(define host-bl-h2-app + (host/make-app (list (host/blog-write-routes host-bl-resolve) host/blog-routes))) +(define host-bl-h2-loc + (fn (method target auth body) + (or (dream-resp-header + (host-bl-h2-app (host-bl-send method target auth "application/x-www-form-urlencoded" body)) + "location") ""))) + +(host-bl-test "H2: unauth /new-film -> login redirect" + (starts-with? (host-bl-h2-loc "POST" "/new-film" false "title=Sneaky") "/login") true) +(host-bl-test "H2: unauth /new-film creates NO film" + (contains? (host/blog-slugs) "sneaky") false) +(host-bl-test "H2: authed /new-film creates the film" + (begin (host-bl-h2-loc "POST" "/new-film" "Bearer good" "title=Legit Film") + (contains? (host/blog--out-raw "legit-film" "is-a") "film")) + true) +(host-bl-test "H2: unauth /new-showing -> login redirect" + (starts-with? (host-bl-h2-loc "POST" "/new-showing" false "film=legit-film&calendar=c1&time=t") "/login") true) +(host-bl-test "H2: unauth /add-poll -> login redirect" + (starts-with? (host-bl-h2-loc "POST" "/add-poll?post=x" false "question=Q&options=a,b") "/login") true) +(host-bl-test "H2: unauth /offering-add -> login redirect" + (starts-with? (host-bl-h2-loc "POST" "/offering-add?showing=x" false "tickettype=tt&price=1") "/login") true) +(host-bl-test "H2: unauth /new-event -> login redirect" + (starts-with? (host-bl-h2-loc "POST" "/new-event" false "title=E") "/login") true) +(host-bl-test "H2: /vote stays PUBLIC (no login redirect)" + (starts-with? (host-bl-h2-loc "POST" "/vote?poll=p&option=o" false "voter=v@x.com") "/login") false) +(host-bl-test "H2: /buy-ticket stays PUBLIC (no login redirect)" + (starts-with? (host-bl-h2-loc "POST" "/buy-ticket?showing=s&offering=o" false "email=v@x.com") "/login") false) + (define host-bl-tests-run! (fn ()