Closes the gap where "Require login" was effectively admin-only: a member with no password can't sign in and can't bootstrap one themselves (can't log in to reach the setting). The set-password endpoint already allowed admin→anyone — this adds the missing UI. Each non-admin row in Manage Profiles gets a lock-icon button that opens an inline form to set / change / remove that member's LOGIN password (separate from the quick-switch PIN), with a confirm field + a hint explaining when it's used. Admin rows don't get it (admin manages their own in Settings → Security, which keeps its anti-lockout). textContent-only rendering, so a profile name can't inject markup. Test: admin sets a member's password → the member can then authenticate (verify_profile_password) and a wrong password fails; admin can clear it back to no-login. 64 script-split integrity tests green.
54 lines
1.8 KiB
Python
54 lines
1.8 KiB
Python
"""Admin sets a member's LOGIN password (the gap behind 'non-admins can't log in
|
|
when Require Login is on'). The endpoint already allowed admin→anyone; this locks
|
|
that the round-trip actually lets the member authenticate."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import tempfile
|
|
|
|
import pytest
|
|
|
|
_TMP = tempfile.mkdtemp(prefix='soulsync-testdb-memberpw-')
|
|
os.environ['DATABASE_PATH'] = os.path.join(_TMP, 'm.db')
|
|
os.environ['SOULSYNC_TEST_DB_READY'] = '1'
|
|
|
|
web_server = pytest.importorskip('web_server')
|
|
|
|
|
|
@pytest.fixture
|
|
def client():
|
|
return web_server.app.test_client()
|
|
|
|
|
|
def _make_member(db, pid=77):
|
|
conn = db._get_connection()
|
|
try:
|
|
conn.execute("INSERT OR REPLACE INTO profiles (id, name, is_admin) VALUES (?,?,0)", (pid, 'Member'))
|
|
conn.commit()
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def test_admin_sets_member_password_then_member_can_authenticate(client):
|
|
db = web_server.get_database()
|
|
_make_member(db)
|
|
assert db.verify_profile_password(77, 'secret123') is False # no password → can't log in
|
|
|
|
r = client.post('/api/profiles/77/set-password', json={'password': 'secret123'})
|
|
assert r.status_code == 200
|
|
body = r.get_json()
|
|
assert body['success'] is True and body['has_password'] is True
|
|
|
|
assert db.verify_profile_password(77, 'secret123') is True # member can now authenticate
|
|
assert db.verify_profile_password(77, 'wrong') is False
|
|
|
|
|
|
def test_admin_can_clear_member_password(client):
|
|
db = web_server.get_database()
|
|
_make_member(db, pid=78)
|
|
client.post('/api/profiles/78/set-password', json={'password': 'pw12345'})
|
|
assert db.verify_profile_password(78, 'pw12345') is True
|
|
r = client.post('/api/profiles/78/set-password', json={'password': ''})
|
|
assert r.status_code == 200
|
|
assert db.verify_profile_password(78, 'pw12345') is False # cleared → no login again
|