Commit Graph

3 Commits

Author SHA1 Message Date
Claude
0c4a98b0e0
Stop offering a logout button that was never a logout
"Cerrar sesión del todo" linked straight to /user/logout on the account
server. That route is POST-only — verified in Gitea 1.22's router, which
registers m.Post("/logout", auth.SignOut) and no GET at all — so the
click was a GET, the answer was 404, and the session it promised to end
carried on untouched. It shipped in 724dada, in the commit whose message
was about not overstating what Salir does.

It cannot be repaired by turning the link into a form: the POST needs a
CSRF token belonging to that other domain, unreadable from here by
design. So the page stops pretending and gives the instruction — go
there, open the profile menu, choose Cerrar sesión — which is what the
small print underneath already said.

server-setup.md contained the whole answer and contradicted itself: one
paragraph states the logout is POST-only "so a link cannot trigger it",
and two paragraphs later promises "the link that finishes the job". The
code followed the wrong half.

Worse, a test asserted "/user/logout" in page, so the suite was
enforcing the defect rather than catching it. That assertion is now
inverted, and a template guard fails the build if any template links
there again.

199 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NizVpJ2dwzCbjCrTLCjeHn
2026-09-28 15:43:32 +00:00
Claude
a79e04396b
Stop showing members the name of the software behind the login
A member signing in has no idea what Gitea is, and for anyone this
platform is ever sold to it is a competitor's name on their login page.
Fourteen strings named it — a button, a logout page, an account-creation
notice and eleven error messages — plus Gitea's own sign-in and
authorize screens, which every member passes through.

Ours are reworded: "el servidor de cuentas" where the thing has to be
referred to at all, and nothing where it did not. Gitea's own screens
take APP_NAME plus the two footer switches, which are supported settings
rather than a patched template. APP_NAME goes in app.ini's unnamed root
section, spelled DEFAULT in the environment mapping, so the docs carry a
command to confirm it landed — a key written to a section that does not
exist is accepted in silence.

Comments, docstrings, column names and env vars keep the real name. The
code has to stay honest about what it talks to, none of it reaches a
browser, and renaming gitea_login would mean a migration for nothing.

Two guards added, since this is the kind of thing that creeps back one
error message at a time: no template renders the word outside a Jinja
comment, and no string literal outside a docstring contains it. Checked
against the previous commit, where they catch the one message that had
already been missed by hand.

Licence: MIT, no attribution-in-UI clause, and we redistribute nothing —
the official image runs unmodified with its own LICENSE intact. Gitea
ships the "powered by" switch itself. Reasoning recorded in §12.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NizVpJ2dwzCbjCrTLCjeHn
2026-09-25 19:30:06 +00:00
Claude
6ac814475e
Add a members area: roles and an internal board
Everything on this site so far has been a file built from git. This is the
first component that runs code to answer a request and the first whose data
git does not hold, so the trade is stated in the README and the backup script
is not optional.

Roles are owner, admin and user. The owner is seeded once from BOARD_OWNER and
cannot be seeded again, because otherwise editing a compose file would be a
quieter way to take the top role than asking for it; ownership moves only by
transfer, inside the app. There is exactly one owner and a partial unique index
enforces it, so the invariant holds even when a handler is wrong. The owner is
beyond suspension and demotion by everyone, themselves included. Only the owner
makes admins; admins make users.

Sign-in goes through Gitea as a confidential OAuth client — the opposite of
Decap, which has to be public because it runs in the browser. The rule the
whole thing rests on is that a Gitea account is not a membership: entry needs
an active row in `members`, or every account on the instance is a member,
starting with the translations bot.

Admins can delete any post; nobody can edit anyone else's, admins included.
Taking a post down is visible to its author. Quietly rewriting it is not, and
an admin who could do that could leave a sentence attributed to someone who
never wrote it. The plan said admins could do both; this is the one place the
implementation departs from it.

Markdown renders with raw HTML disabled, which is the entire XSS defence and
the reason there is no sanitiser: the renderer emits only its own tags and
escapes the rest. The CSP carries no 'unsafe-inline', which makes an inline
onsubmit silently inert rather than broken, so the confirmation dialogs live in
a static file and a test fails any template that grows an inline handler.

GDPR is in scope rather than deferred: erasure removes the member row and moves
their authorship to a tombstone so the conversations around them still read,
and any member can download their own writing.

Verified: 63 checks pass, covering the membership gate, every role predicate, a
direct insert of a second owner being refused by the index, atomic ownership
transfer, CSRF, an offsite login redirect, script tags rendering as text, soft
deletes leaving both listings and exports, and the member screens rendering for
each role. Smoke-tested live: headers, both static assets, and the bare
/comunidad redirect that the Caddy matcher has to cover.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NizVpJ2dwzCbjCrTLCjeHn
2026-09-22 10:57:18 +00:00