Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ Three Dashboard visuals are now NOC widgets: **Airtime by Band** (average channe

### Widget endpoints now require the suite token

`app/api/widgets.py` previously mounted its router with a bare `APIRouter()`,
`app/api/widgets/` previously mounted its router with a bare `APIRouter()`,
so the server-rendered widget views — which read internal data — answered
anyone who could reach the port. The router now carries
`dependencies=[Depends(require_suite_token)]`, matching the NOC Builder's
Expand Down Expand Up @@ -878,7 +878,7 @@ Point resonance's **READ SPEC** at `/api/resonance/openapi.json`. The published

Every call is made by pktWiFi's own page, same-origin, on the session of the person already signed
in, so nothing here reaches data that person could not already open. Which operations exist is
fixed in `app/api/resonance_data.py`, not configurable per install. Write operations are withheld
fixed in `app/api/resonance_data/`, not configurable per install. Write operations are withheld
from the grant entirely until an administrator sets a role to **Read and write**.

**Never exposed:** a collector's stored controller credentials. Nothing here changes a channel or transmit power, deauthenticates a client, or creates, edits or deletes an access point, SSID, radio or collector. The app has no rule-toggle endpoint, so the assistant's writes are acknowledge-only.
Expand Down
4 changes: 4 additions & 0 deletions app/api/collectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from __future__ import annotations

import json
import logging

import aiosqlite
from fastapi import APIRouter, Depends, HTTPException
Expand All @@ -15,6 +16,8 @@
from app.wifi.collectors.registry import COLLECTOR_TYPES
from app.wifi.collectors.crypto import encrypt_config, decrypt_config

log = logging.getLogger("pktwifi.api.collectors")

router = APIRouter()


Expand All @@ -37,6 +40,7 @@ def _collector_out(r, reveal_config: bool = False) -> dict:
try:
out["config"] = decrypt_config(r["config_json"])
except Exception:
log.warning("collector %s: stored config could not be read", r["id"], exc_info=True)
out["config"] = {}
return out

Expand Down
3 changes: 2 additions & 1 deletion app/api/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from pydantic import BaseModel

from app.database import get_db
from app.errors import describe_exception
from app.dependencies import CurrentUser, AdminUser
from app.wifi.collectors.crypto import encrypt_str, decrypt_config

Expand Down Expand Up @@ -140,7 +141,7 @@ class CredentialTestRequest(BaseModel):

def _exc_detail(exc: Exception) -> str:
# Same convention as poll_now: some httpx exceptions have an empty str().
return f"{type(exc).__name__}: {exc}" if str(exc) else type(exc).__name__
return describe_exception(exc, with_type=True)


@router.post("/{cred_id}/test")
Expand Down
25 changes: 10 additions & 15 deletions app/api/logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from app.database import get_db
from app.dependencies import AdminUser, CurrentUser
from app.sqlutil import Where
from app.wifi.collectors.crypto import decrypt_str

router = APIRouter()
Expand Down Expand Up @@ -48,34 +49,28 @@ async def get_logs(
limit: int = Query(200, ge=1, le=1000),
offset: int = Query(0, ge=0),
):
conditions: list[str] = []
params: list = []
w = Where()

if level:
level_no = _LEVEL_MAP.get(level.upper())
if level_no is not None:
conditions.append("level_no >= ?")
params.append(level_no)
w.add("level_no >= ?", level_no)

if logger:
conditions.append("logger LIKE ?")
params.append(f"{logger}%")
w.add("logger LIKE ?", f"{logger}%")

if search:
conditions.append("message LIKE ?")
params.append(f"%{search}%")
w.add("message LIKE ?", f"%{search}%")

if since:
conditions.append("created_at >= ?")
params.append(since)
w.add("created_at >= ?", since)

if until:
conditions.append("created_at <= ?")
params.append(until)
w.add("created_at <= ?", until)

where = ("WHERE " + " AND ".join(conditions)) if conditions else ""
where = w.sql

async with db.execute(f"SELECT COUNT(*) FROM app_logs {where}", params) as cur:
async with db.execute(f"SELECT COUNT(*) FROM app_logs {where}", w.params) as cur:
total = (await cur.fetchone())[0]

async with db.execute(
Expand All @@ -86,7 +81,7 @@ async def get_logs(
ORDER BY id DESC
LIMIT ? OFFSET ?
""",
params + [limit, offset],
w.with_params(limit, offset),
) as cur:
rows = await cur.fetchall()

Expand Down
2 changes: 1 addition & 1 deletion app/api/nav.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

# Fetched unauthenticated by pktHub's health poller, and it discloses this
# app's page structure — so it carries the same X-Suite-Token gate as the
# widget endpoints in app/api/widgets.py.
# widget endpoints in app/api/widgets/.
router = APIRouter(dependencies=[Depends(require_suite_token)])

# ── Manifest ──────────────────────────────────────────────────────────────────
Expand Down
Loading
Loading