diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api/signaling.py')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/api/signaling.py | 69 |
1 files changed, 43 insertions, 26 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/signaling.py b/packages/meshbay-hub/src/meshbay_hub/api/signaling.py index b4be3f2..bc03b45 100644 --- a/packages/meshbay-hub/src/meshbay_hub/api/signaling.py +++ b/packages/meshbay-hub/src/meshbay_hub/api/signaling.py @@ -103,32 +103,49 @@ async def webrtc_offer( # connection to a node just because it happens to host an open group. Members # of that group are unaffected — they match `shared` below. node_group_ids = set(_node_groups.get(node_id, [])) - if node_group_ids: - result = await db.execute( - select(GroupMember.group_id).where( - GroupMember.user_id == current_user.id, - GroupMember.group_id.in_(node_group_ids), - )) - shared = [gid for (gid,) in result.all()] - if not shared: - has_open = None - if await hub_settings.public_groups_allowed(db): - has_open = (await db.execute( - select(Group.id).where( - Group.id.in_(node_group_ids), - Group.join_policy == "open", - Group.status == "active", - ))).first() - if not has_open: - raise HTTPException(status_code=403, detail="Not a member of any group on this node") - else: - statuses = set((await db.execute( - select(Group.status).where(Group.id.in_(shared)))).scalars().all()) - if "active" not in statuses: - # Report the strongest state present — "revoked" is the signed, - # node-enforced one; "suspended" is the reversible hub flag. - state = "revoked" if "revoked" in statuses else next(iter(statuses), "suspended") - raise HTTPException(status_code=403, detail=f"Group is {state}") + # A node registered for no group shares no group with anybody, which is this + # check's own answer — and `if node_group_ids:` used to skip the whole thing, + # membership, group status and the public-group gate together. Since AV1 made + # an empty claim mean "no groups" rather than "all of my owner's", that is + # the *normal* registration of a node hosting nothing: exactly the + # unconfigured node left running that took a group down on 2026-09-11. So the + # machine least able to defend itself was the one any authenticated account + # could make allocate a peer connection and gather ICE, which is H6 restored + # in the one case AV1 made common. + # + # Nothing legitimate is lost by refusing here: a browser cannot complete a + # handshake with such a node anyway — `group_id` is mandatory (M1) and a node + # holding no group key refuses outright (NS8) — so this only declines work + # the node would decline one step later, at its own expense. + if not node_group_ids: + raise HTTPException(status_code=403, + detail="Not a member of any group on this node") + + result = await db.execute( + select(GroupMember.group_id).where( + GroupMember.user_id == current_user.id, + GroupMember.group_id.in_(node_group_ids), + )) + shared = [gid for (gid,) in result.all()] + if not shared: + has_open = None + if await hub_settings.public_groups_allowed(db): + has_open = (await db.execute( + select(Group.id).where( + Group.id.in_(node_group_ids), + Group.join_policy == "open", + Group.status == "active", + ))).first() + if not has_open: + raise HTTPException(status_code=403, detail="Not a member of any group on this node") + else: + statuses = set((await db.execute( + select(Group.status).where(Group.id.in_(shared)))).scalars().all()) + if "active" not in statuses: + # Report the strongest state present — "revoked" is the signed, + # node-enforced one; "suspended" is the reversible hub flag. + state = "revoked" if "revoked" in statuses else next(iter(statuses), "suspended") + raise HTTPException(status_code=403, detail=f"Group is {state}") if _pending_per_user.get(current_user.id, 0) >= MAX_PENDING_PER_USER: raise HTTPException(status_code=429, detail="Too many pending connections") |