Files
security-alert-center/backend/tests/test_mobile_api.py
T
PapaTramp 563b836acc feat: Seaca mobile API, enrollment, FCM push and admin UI (0.9.0)
Adds mobile device registration by admin codes, refresh tokens, push channel
in notification policy, and Settings section for managing Seaca clients.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-10 13:19:14 +10:00

151 lines
4.8 KiB
Python

"""Mobile / Seaca API."""
from datetime import datetime, timedelta, timezone
from app.models.mobile_device import MobileDevice
from app.models.mobile_enrollment_code import MobileEnrollmentCode
from app.models.mobile_settings import MOBILE_SETTINGS_ROW_ID, MobileSettings
from app.services.mobile_tokens import hash_mobile_secret
def _enable_mobile(db_session):
row = db_session.get(MobileSettings, MOBILE_SETTINGS_ROW_ID)
if row is None:
row = MobileSettings(id=MOBILE_SETTINGS_ROW_ID, devices_allowed=True, max_devices_per_user=3)
db_session.add(row)
else:
row.devices_allowed = True
db_session.commit()
def test_mobile_settings_admin(jwt_headers, client, db_session):
_enable_mobile(db_session)
response = client.get("/api/v1/mobile/settings", headers=jwt_headers)
assert response.status_code == 200
body = response.json()
assert body["devices_allowed"] is True
assert body["max_devices_per_user"] == 3
def test_create_enrollment_code_and_enroll(jwt_headers, client, db_session):
_enable_mobile(db_session)
create = client.post(
"/api/v1/mobile/enrollment-codes",
headers=jwt_headers,
json={"label": "test phone", "login_mode": "password", "expires_in_hours": 1},
)
assert create.status_code == 200
created = create.json()
assert created["enrollment_code"].startswith("sacmob_")
assert created["code_prefix"]
enroll = client.post(
"/api/v1/mobile/enroll",
json={
"enrollment_code": created["enrollment_code"],
"username": "test-monitor",
"password": "test-monitor-password",
"device_uuid": "uuid-test-device-001",
"display_name": "Pixel Test",
"fcm_token": "fcm-token-test",
},
)
assert enroll.status_code == 200
body = enroll.json()
assert body["username"] == "test-monitor"
assert body["role"] == "monitor"
assert body["device_id"] > 0
assert body["access_token"]
assert body["refresh_token"]
devices = client.get("/api/v1/mobile/devices", headers=jwt_headers)
assert devices.status_code == 200
assert len(devices.json()) == 1
assert devices.json()[0]["display_name"] == "Pixel Test"
def test_enroll_blocked_when_disabled(client, db_session, jwt_headers):
code_plain = "sacmob_testcode123456"
db_session.add(
MobileEnrollmentCode(
label="x",
code_hash=hash_mobile_secret(code_plain),
code_prefix=code_plain[:12],
login_mode="password",
max_uses=1,
use_count=0,
expires_at=datetime.now(timezone.utc) + timedelta(hours=1),
)
)
db_session.commit()
response = client.post(
"/api/v1/mobile/enroll",
json={
"enrollment_code": code_plain,
"username": "test-monitor",
"password": "test-monitor-password",
"device_uuid": "uuid-disabled-001",
},
)
assert response.status_code == 403
def test_refresh_token_rotation(client, db_session, jwt_headers):
_enable_mobile(db_session)
create = client.post(
"/api/v1/mobile/enrollment-codes",
headers=jwt_headers,
json={"login_mode": "password"},
)
code = create.json()["enrollment_code"]
enroll = client.post(
"/api/v1/mobile/enroll",
json={
"enrollment_code": code,
"username": "test-admin",
"password": "test-admin-password",
"device_uuid": "uuid-refresh-001",
},
)
data = enroll.json()
refresh = client.post(
"/api/v1/mobile/auth/refresh",
json={"refresh_token": data["refresh_token"], "device_uuid": "uuid-refresh-001"},
)
assert refresh.status_code == 200
refreshed = refresh.json()
assert refreshed["refresh_token"] != data["refresh_token"]
assert refreshed["device_id"] == data["device_id"]
def test_revoke_device_blocks_token(client, db_session, jwt_headers):
_enable_mobile(db_session)
create = client.post(
"/api/v1/mobile/enrollment-codes",
headers=jwt_headers,
json={"login_mode": "password"},
)
code = create.json()["enrollment_code"]
enroll = client.post(
"/api/v1/mobile/enroll",
json={
"enrollment_code": code,
"username": "test-monitor",
"password": "test-monitor-password",
"device_uuid": "uuid-revoke-001",
},
)
data = enroll.json()
headers = {"Authorization": f"Bearer {data['access_token']}"}
revoke = client.delete(f"/api/v1/mobile/devices/{data['device_id']}", headers=jwt_headers)
assert revoke.status_code == 200
me = client.put(
"/api/v1/mobile/devices/me/fcm",
headers=headers,
json={"fcm_token": "new-token"},
)
assert me.status_code == 401