Portal sign-in with the Ecare office login

Ecare staff sign in with their office username and password, checked by
the Ecare sign-in service from the backend (AUTH_TOKEN_API_URL); a good
Ecare login still needs a Portal User. Service down = "try again
shortly", never a wrong password. /api/auth/login, me, logout; sign-in
tries limited per address. Users page: Ecare office login or Portal
password per user. Context.md: 27/10 runs, the login retry bug.
71 test files, 1239 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ManikandanandClaude Opus 5.5 committed 2026-10-27 17:55:50 +05:30
1 parent 83a0cf65ae
commit 64efb68ea1
10 files changed
+556 -48

No files matched your search

+6
View File
@@ -8,6 +8,11 @@
{% block content %}
<div class="pw">
<h1>Change password</h1>
{% if user.ecare %}
<p class="sub">You sign in with your Ecare office login, so this Portal
keeps no password for you. Your password is your Ecare office password —
change it in Ecare.</p>
{% else %}
{% if user.must_change %}<p class="sub">Choose your own password before
going on (an Admin set the one you signed in with).</p>
{% else %}<p class="sub">At least 10 characters, not your username.</p>{% endif %}
@@ -19,5 +24,6 @@
<label>New password again <input name="confirm" type="password" autocomplete="new-password" minlength="10" required></label>
<button type="submit">Change password</button>
</form>
{% endif %}
</div>
{% endblock %}
+3 -1
View File
@@ -105,6 +105,7 @@
</section>
<section class="auth-form">
<h2>Sign in</h2>
{% if ecare_on %}<p class="note">Ecare staff: your Ecare office username and password.</p>{% endif %}
{% if error %}<div class="msg bad" role="alert">{{ error }}</div>{% endif %}
{% if no_users %}<div class="msg bad">No Portal Users yet. On this PC run
<code>python tools\add_portal_user.py</code> to add the first Admin.</div>{% endif %}
@@ -124,7 +125,8 @@
</form>
<p class="note rules">Signed out after {{ idle_minutes }} minutes idle.
{{ lock_after }} wrong passwords lock the account for
{{ lock_minutes }} minutes. Forgot it? An Admin can reset it.</p>
{{ lock_minutes }} minutes. Forgot a Portal password? An Admin can
reset it.</p>
</section>
</div>
{% endblock %}
+12 -3
View File
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block title %}Portal Users{% endblock %}
{% block style %}
form.add { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)) auto;
form.add { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)) auto;
gap: 10px; align-items: end; }
form.add label { font-weight: 600; font-size: 13px; }
form.add input, form.add select { display: block; width: 100%; margin-top: 4px; }
@@ -38,10 +38,16 @@
<label>Role
<select name="role">{% for r in roles %}<option{% if r == "Viewer" %} selected{% endif %}>{{ r }}</option>{% endfor %}</select>
</label>
<label>First password <input name="password" type="password" autocomplete="new-password" minlength="10" required></label>
<label>Sign-in
<select name="signin"><option value="ecare" selected>Ecare office login</option><option value="portal">Portal password</option></select>
</label>
<label>First password <input name="password" type="password" autocomplete="new-password" placeholder="Portal password only"></label>
<button type="submit">Add</button>
</form>
<p class="note">They change the first password at their first Sign-in.</p>
<p class="note">Ecare office login: their own Ecare username and password,
nothing to hand over — use their Ecare username here. Portal password
(clients, a backup): give a first password; they change it at their
first Sign-in.</p>
</div>
<div class="card">
@@ -55,6 +61,7 @@
<td>
{% if u.disabled %}<span class="tag off">Disabled</span>
{% else %}<span class="tag on">Active</span>{% endif %}
<span class="tag">{% if u.ecare %}Ecare login{% else %}Portal password{% endif %}</span>
{% if u.must_change %}<span class="tag change">Must change password</span>{% endif %}
{% if u.locked_until %}<span class="tag lock">Locked until {{ u.locked_until | usdate }}</span>{% endif %}
</td>
@@ -68,11 +75,13 @@
<button type="submit" class="plain">Set role</button>
</form>
{% endif %}
{% if not u.ecare %}
<form method="post" action="/admin/users/{{ u.username }}/reset" autocomplete="off">
<input type="hidden" name="token" value="{{ run_token }}">
<input name="password" type="password" placeholder="New first password" autocomplete="new-password" minlength="10" required>
<button type="submit" class="plain">Reset</button>
</form>
{% endif %}
{% if u.disabled %}
<form method="post" action="/admin/users/{{ u.username }}/enable">
<input type="hidden" name="token" value="{{ run_token }}">