fix: DDNS update token in body, webdav gating, regression tests
Unit Tests / test (push) Successful in 7m25s
Unit Tests / test (push) Successful in 7m25s
- PicNgoDDNS.update(): send token in request body instead of Authorization header; DDNS server validates it from body (was returning HTTP 422 on every heartbeat, leaving IP record stale after fresh install) - peers.py / Peers.jsx: webdav service_access only valid when 'files' store service is installed; was always shown even with no services, confusing users into thinking WebDAV was pre-installed - 10 new regression tests: DDNS update body contract, Caddy always regenerates on startup with no services, peer role allowed on /api/services/active, webdav gating by installed services Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -141,5 +141,81 @@ def test_empty_auth_manager_non_api_path_bypasses_503(
|
||||
assert r.status_code == 200
|
||||
|
||||
|
||||
# ── role-based access: peer vs admin ─────────────────────────────────────────
|
||||
|
||||
@pytest.fixture
|
||||
def peer_client(tmp_path):
|
||||
"""Test client with a peer-role session active."""
|
||||
from app import app
|
||||
from auth_manager import AuthManager
|
||||
data_dir = str(tmp_path / 'data')
|
||||
config_dir = str(tmp_path / 'config')
|
||||
os.makedirs(data_dir, exist_ok=True)
|
||||
os.makedirs(config_dir, exist_ok=True)
|
||||
mgr = AuthManager(data_dir=data_dir, config_dir=config_dir)
|
||||
mgr.create_user('admin', 'AdminPass123!', 'admin')
|
||||
mgr.create_user('alice', 'AlicePass123!', 'peer')
|
||||
|
||||
app.config['TESTING'] = True
|
||||
with patch('app.auth_manager', mgr):
|
||||
try:
|
||||
import auth_routes
|
||||
with patch.object(auth_routes, 'auth_manager', mgr, create=True):
|
||||
with app.test_client() as client:
|
||||
r = client.post('/api/auth/login',
|
||||
data=json.dumps({'username': 'alice', 'password': 'AlicePass123!'}),
|
||||
content_type='application/json')
|
||||
assert r.status_code == 200, f'peer login failed: {r.status_code}'
|
||||
yield client, mgr
|
||||
except ImportError:
|
||||
with app.test_client() as client:
|
||||
with client.session_transaction() as sess:
|
||||
sess['username'] = 'alice'
|
||||
sess['role'] = 'peer'
|
||||
yield client, mgr
|
||||
|
||||
|
||||
def test_peer_role_blocked_from_admin_only_endpoint(peer_client):
|
||||
"""Peer sessions must not access admin-only endpoints like /api/peers."""
|
||||
client, mgr = peer_client
|
||||
with patch('app.auth_manager', mgr):
|
||||
r = client.get('/api/peers')
|
||||
assert r.status_code == 403
|
||||
|
||||
|
||||
def test_peer_role_allowed_services_active(peer_client):
|
||||
"""/api/services/active must be accessible to peer sessions.
|
||||
|
||||
Regression guard: peers saw 'not installed' on My Services because
|
||||
enforce_auth returned 403 for this endpoint.
|
||||
"""
|
||||
client, mgr = peer_client
|
||||
with patch('app.auth_manager', mgr):
|
||||
r = client.get('/api/services/active')
|
||||
# 200 (or whatever the route returns) but NOT 403
|
||||
assert r.status_code != 403, (
|
||||
'/api/services/active returned 403 for peer — peer UI cannot show installed services'
|
||||
)
|
||||
|
||||
|
||||
def test_admin_role_still_allowed_services_active(flask_client, populated_auth_manager):
|
||||
"""/api/services/active must remain accessible to admin sessions."""
|
||||
with patch('app.auth_manager', populated_auth_manager):
|
||||
try:
|
||||
import auth_routes
|
||||
with patch.object(auth_routes, 'auth_manager', populated_auth_manager, create=True):
|
||||
r_login = flask_client.post('/api/auth/login',
|
||||
data=json.dumps({'username': 'admin', 'password': 'AdminPass123!'}),
|
||||
content_type='application/json')
|
||||
assert r_login.status_code == 200
|
||||
r = flask_client.get('/api/services/active')
|
||||
except ImportError:
|
||||
with flask_client.session_transaction() as sess:
|
||||
sess['username'] = 'admin'
|
||||
sess['role'] = 'admin'
|
||||
r = flask_client.get('/api/services/active')
|
||||
assert r.status_code != 403
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__, '-v'])
|
||||
|
||||
Reference in New Issue
Block a user