Say accounts cannot be created before the form is filled in

Running without GITEA_ADMIN_TOKEN is the safer configuration and is documented
as such, but the member form did not know: it offered "Crear también su cuenta"
ticked by default and reported "Falta GITEA_ADMIN_TOKEN" only on submit, after
three fields had been filled in.

That is the shape of failure this project has lost the most time to — something
that cannot happen, going unsaid until someone has relied on it. The form now
reads the config when it renders and says so, with a link to Gitea's create-user
page.

The checkbox is disabled rather than hidden, because "you cannot do this here"
is more use than an option that quietly is not there. The refusal itself stays
in the handler: a disabled input is a courtesy, and a hand-crafted POST still
meets the same error.

Verified: 104 checks. The form states the limit with no token and is unchanged
with one, and submitting create_account anyway still creates no member.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NizVpJ2dwzCbjCrTLCjeHn
This commit is contained in:
Claude 2026-09-25 17:17:41 +00:00
parent acf3f745ff
commit 3bd086ab0e
No known key found for this signature in database
4 changed files with 68 additions and 2 deletions

View File

@ -18,7 +18,7 @@ import json
import re
import sqlite3
from flask import (Blueprint, Response, abort, flash, g, redirect,
from flask import (Blueprint, Response, abort, current_app, flash, g, redirect,
render_template, request, url_for)
from . import gitea
@ -121,7 +121,16 @@ def index():
@admin_required
def new():
if request.method == "GET":
return render_template("member_new.html", can_make_admin=g.member["role"] == "owner")
return render_template(
"member_new.html",
can_make_admin=g.member["role"] == "owner",
# Without a site-admin token the server cannot create Gitea accounts,
# which is the documented safer configuration rather than a fault.
# The form says so before it is filled in; offering a ticked checkbox
# and reporting the problem on submit wastes the work of filling it.
can_create_accounts=bool(current_app.config.get("ADMIN_TOKEN")),
gitea_url=current_app.config["GITEA_URL"].rstrip("/"),
)
login = request.form.get("login", "").strip()
display_name = request.form.get("display_name", "").strip()

View File

@ -17,10 +17,25 @@
<label for="email">Correo</label>
<input id="email" name="email" type="email" placeholder="maria@ejemplo.com">
{% if can_create_accounts %}
<label class="check">
<input type="checkbox" name="create_account" checked>
Crear también su cuenta (desmarca si ya tiene una)
</label>
{% else %}
{# Disabled rather than hidden: "you cannot do this here" is more use than
an option that quietly is not there. The handler refuses it either way. #}
<label class="check">
<input type="checkbox" name="create_account" disabled>
Crear también su cuenta
</label>
<p class="muted small">
Este servidor no puede crear cuentas (no tiene un token de administración
de Gitea). Crea la cuenta primero en
<a href="{{ gitea_url }}/admin/users/new">Gitea</a> y luego da de alta
aquí ese mismo usuario.
</p>
{% endif %}
{% if can_make_admin %}
<label for="role">Rol</label>

View File

@ -182,3 +182,40 @@ def test_the_export_is_only_your_own_writing(client, db, make_member, sign_in):
body = client.get("/comunidad/mis-datos").get_data(as_text=True)
assert "Mío" in body
assert "Suyo" not in body
def test_the_form_says_so_when_accounts_cannot_be_created(app, client, owner_id, sign_in):
"""Without a site-admin token the server cannot create Gitea accounts. That
is the documented safer setup, not a fault — but it has to be said before
someone fills the form, not after they submit it."""
app.config["ADMIN_TOKEN"] = ""
sign_in(owner_id)
body = client.get("/comunidad/miembros/nuevo").get_data(as_text=True)
assert "no puede crear cuentas" in body
assert 'name="create_account" disabled' in body
assert 'name="create_account" checked' not in body
def test_with_a_token_the_form_is_unchanged(app, client, owner_id, sign_in):
app.config["ADMIN_TOKEN"] = "admintoken"
sign_in(owner_id)
body = client.get("/comunidad/miembros/nuevo").get_data(as_text=True)
assert 'name="create_account" checked' in body
assert "no puede crear cuentas" not in body
def test_ticking_it_anyway_still_creates_nothing(app, client, db, post, owner_id, sign_in):
"""A disabled input is a courtesy, not a permission — the refusal lives in
the handler, where a hand-crafted POST also meets it."""
app.config["ADMIN_TOKEN"] = ""
sign_in(owner_id)
response = post("/comunidad/miembros/nuevo", {
"login": "maria", "display_name": "María", "email": "m@example.com",
"role": "user", "create_account": "on",
}, follow_redirects=True)
assert "GITEA_ADMIN_TOKEN" in response.get_data(as_text=True)
assert db.execute("SELECT 1 FROM members WHERE gitea_login = 'maria'").fetchone() is None

View File

@ -346,6 +346,11 @@ board's environment — the compose file, `docker inspect`, a shell in the
container — can use it. If you would rather not have that on the box, leave
`GITEA_ADMIN_TOKEN` empty and create accounts in Gitea by hand.
Leaving it empty is a supported configuration, not a half-finished one: the
*Dar de alta* form checks for the token when it renders, says plainly that this
server cannot create accounts, and links to Gitea's own create-user page. You
then add that username here, with the checkbox already off.
### 11.3 Build and run
```sh