"""Authenticate privately on the Pi and verify the deployed administration API.""" import json, sqlite3 from pathlib import Path from urllib.error import HTTPError from urllib.request import Request, urlopen from admin_http import AdminHttp root = Path('/mnt/data/ikar_server') settings = dict(line.split('=', 1) for line in (root/'shared/server.env').read_text().splitlines() if '=' in line and not line.startswith('#')) base = 'http://127.0.0.1:5099' for route in ('users/list', 'reports', 'bots', 'audit', 'operations', 'admin-sessions'): try: urlopen(base + '/admin/api/' + route, timeout=15) raise RuntimeError('Unauthenticated admin access is possible') except HTTPError as error: if error.code != 401: raise client = AdminHttp(base) client.login(settings) try: endpoints = {} for route in ('status', 'users/list', 'chats/list', 'attachments/list', 'reports', 'bots', 'audit', 'admin-sessions', 'operations', 'releases', 'push-queue', 'app-updates/compatibility-report'): code, data = client.request('/admin/api/' + route) if code != 200: raise RuntimeError('Admin endpoint failed: ' + route) endpoints[route] = code if route == 'admin-sessions' and not any(item['current'] for item in data): raise RuntimeError('Current admin session is missing') if route == 'operations': if not data['dataProtectionPersistent'] or not data['backups']: raise RuntimeError('Persistent keys or backups are missing') operations = {key: data[key] for key in ('diskFreeBytes', 'diskTotalBytes', 'openReports', 'queuedPush')} if route == 'releases': release = data['release'] # A missing CSRF token must fail without terminating the valid session. cookies = '; '.join(key + '=' + value for key, value in client.cookies.items()) try: urlopen(Request(base + '/admin/api/session/logout', method='POST', headers={'Cookie': cookies, 'X-Forwarded-Proto': 'https'}), timeout=15) raise RuntimeError('A mutation without CSRF was accepted') except HTTPError as error: if error.code != 400: raise if not client.request('/admin/api/session')[1]['authenticated']: raise RuntimeError('CSRF check damaged valid session') finally: client.logout() with sqlite3.connect(f'file:{root}/shared/data/ikar.db?mode=ro', uri=True) as db: if db.execute('PRAGMA integrity_check').fetchone()[0] != 'ok': raise RuntimeError('Database integrity failed') source = db.execute("SELECT COUNT(*) FROM ModerationLogEntries WHERE Action='complaint.submitted'").fetchone()[0] cases = db.execute('SELECT COUNT(*) FROM ModerationCases').fetchone()[0] if cases < source: raise RuntimeError('Complaint migration is incomplete') report = {'passed': True, 'authenticatedEndpoints': endpoints, 'anonymousDenied': True, 'csrfRequired': True, 'logout': True, 'databaseIntegrity': 'ok', 'complaintsMigrated': cases, 'operations': operations, 'androidRelease': {'versionName': release['versionName'], 'versionCode': release['versionCode'], 'sha256': release['sha256']}} (root/'admin-console-verification.json').write_text(json.dumps(report, indent=2)) print(json.dumps(report))