summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/api/groups.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api/groups.py')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/groups.py37
1 files changed, 26 insertions, 11 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/groups.py b/packages/meshbay-hub/src/meshbay_hub/api/groups.py
index 88af764..000f3f7 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/groups.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/groups.py
@@ -6,6 +6,7 @@ from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from meshbay_hub.api.deps import get_current_user, require_user_scope
+from meshbay_hub.api.netutil import client_ip
from meshbay_hub.db.engine import get_db
from meshbay_hub.db.models import (
FederatedGroup, Group, GroupMember,
@@ -14,6 +15,10 @@ from meshbay_hub.db.models import (
router = APIRouter(prefix="/v1/groups", tags=["groups"])
+# Swarm endpoints live at /v1/swarm/*. They were previously declared on the groups
+# router with a full path, which mounted them at /v1/groups/v1/swarm/* (H7).
+swarm_router = APIRouter(prefix="/v1/swarm", tags=["swarm"])
+
@router.get("/mine")
async def my_groups(
@@ -117,13 +122,21 @@ class SwarmRegisterRequest(BaseModel):
endpoint: str # "ip:port"
-@router.post("/v1/swarm/register", status_code=201)
+@swarm_router.post("/register", status_code=201)
async def swarm_register(
body: SwarmRegisterRequest,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
- """Node registers itself as a source for a content hash (public swarm)."""
+ """
+ Node registers itself as a source for a PUBLIC content hash.
+
+ Finding H7: the node registered hashes for every group it hosted, private ones
+ included, and this route was mounted at /v1/groups/v1/swarm/register — so the
+ node's calls 404'd and the leak was masked by a routing bug rather than
+ prevented. Nodes now filter by group visibility before calling, and the path is
+ correct, so the filter has to be right.
+ """
from meshbay_hub.csam import check_content_hash
if check_content_hash(body.content_hash):
raise HTTPException(status_code=451, detail="Content blocked")
@@ -144,12 +157,18 @@ async def swarm_register(
return {"status": "registered", "hash": body.content_hash}
-@router.get("/v1/swarm/{content_hash}")
+@swarm_router.get("/{content_hash}")
async def swarm_sources(
content_hash: str,
+ current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
- """Return list of nodes that can serve a content hash."""
+ """
+ Return nodes that can serve a content hash.
+
+ Authenticated (H7): an open endpoint lets anyone probe whether a given file
+ exists anywhere in the network and which node holds it.
+ """
from datetime import datetime, timezone, timedelta
cutoff = datetime.now(timezone.utc) - timedelta(minutes=30)
result = await db.execute(
@@ -214,7 +233,7 @@ async def join_group(
db.add(GroupMember(group_id=group_id, user_id=current_user.id))
db.add(IPLog(user_id=current_user.id, event="group_join",
- ip_address=_ip(request), detail=group.name))
+ ip_address=client_ip(request), detail=group.name))
await db.commit()
return {"status": "joined", "group_id": group_id, "name": group.name}
@@ -246,7 +265,7 @@ async def create_group(
db.add(GroupMember(group_id=group.id, user_id=current_user.id))
db.add(IPLog(user_id=current_user.id, event="group_create",
- ip_address=_ip(request), detail=body.name))
+ ip_address=client_ip(request), detail=body.name))
await db.commit()
await db.refresh(group)
return {"group_id": group.id, "name": group.name}
@@ -304,13 +323,9 @@ async def delete_group(
from sqlalchemy import delete as sa_delete
await db.execute(sa_delete(GroupMember).where(GroupMember.group_id == group_id))
db.add(IPLog(user_id=current_user.id, event="group_delete",
- ip_address=_ip(request), detail=group.name))
+ ip_address=client_ip(request), detail=group.name))
await db.delete(group)
await db.commit()
return {"status": "deleted", "group_id": group_id}
-def _ip(request: Request) -> str:
- fwd = request.headers.get("X-Forwarded-For")
- return fwd.split(",")[0].strip() if fwd else (
- request.client.host if request.client else "unknown")