vienalatina/apps/board/templates/set_password.html
Claude e11016a8e9
Refuse before the form, not after the password
A member followed his invitation link, chose a password, typed it twice,
pressed save, and was shown the name of an environment variable. The
server knew from the first byte of that request that it could not save
anything: without GITEA_ADMIN_TOKEN it cannot set a password in Gitea.
It asked him to do the work anyway.

Three places had the same shape, all now checked up front through a new
gitea.admin_configured(), mirroring mail.configured():

- the invitation page answers 503 with an explanation and no password
  field, identically for a real and an invented token so it cannot be
  used to probe for live ones
- /recuperar refuses instead of mailing a link to a page that could only
  apologise — and its deliberately identical answer would have hidden
  that from the admin as well as the member
- the sign-in page stops offering recovery it cannot complete

Also: a 404 from admin_set_password now names the real cause. A member
added without "crear también su cuenta" has no Gitea account, so the
password change is aimed at nothing, and "Gitea rechazó el cambio de
contraseña (404)" blames Gitea for an account that was never made.

deploy-board.sh warns about settings that are present but empty. The
previous check looked for missing names, and GITEA_ADMIN_TOKEN= has a
name — which is why the deploy that led to this said nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NizVpJ2dwzCbjCrTLCjeHn
2026-09-25 19:20:44 +00:00

53 lines
2.1 KiB
HTML

{% extends "base.html" %}
{% block title %}Elige tu contraseña{% endblock %}
{% block main %}
{% if unavailable %}
{# The link is fine. The server is not — with no Gitea admin token it cannot
set anybody's password. Said here, before a password field appears, because
the alternative is somebody choosing a password, typing it twice, pressing
the button and only then being shown the name of an environment variable. #}
<article class="card card--center">
<h1>Todavía no podemos guardar tu contraseña</h1>
<p class="muted">
Tu enlace sigue siendo válido, así que guárdalo. Lo que falta está en el
servidor: aún no puede cambiar contraseñas. Avisa a un administrador y
vuelve a abrir este enlace cuando te lo confirme.
</p>
</article>
{% elif member is none %}
{# One message for expired, already used and never existed alike. Which one it
was would tell whoever holds a stale link something about the account. #}
<article class="card card--center">
<h1>Este enlace ya no sirve</h1>
<p class="muted">
Los enlaces caducan y sólo se pueden usar una vez. Pide uno nuevo desde
<a href="{{ url_for('auth.recover') }}">¿olvidaste tu contraseña?</a>,
o a un administrador si aún no habías entrado nunca.
</p>
</article>
{% else %}
<form class="card" method="post" action="{{ url_for('auth.set_password', token=token) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<h1>Hola, {{ member.display_name }}</h1>
<p class="muted">
Elige una contraseña para entrar al área de la comunidad. Tu usuario es
<span class="mono">{{ member.gitea_login }}</span>.
</p>
<label for="password">Contraseña</label>
<input id="password" name="password" type="password" required
minlength="{{ minimum }}" autocomplete="new-password">
<p class="muted small">Al menos {{ minimum }} caracteres.</p>
<label for="confirm">Repite la contraseña</label>
<input id="confirm" name="confirm" type="password" required
minlength="{{ minimum }}" autocomplete="new-password">
<div class="actions">
<button class="btn" type="submit">Guardar contraseña</button>
</div>
</form>
{% endif %}
{% endblock %}