voice-limit-guard: live revoke never fired — LiveKit's JSON uses snake_case permissions
livekit-server (verified on 1.13.7) serialises ParticipantPermission as can_publish / can_publish_sources; the reconciler read canPublish / canPublishSources, saw 'publishes nothing' for everyone and never called UpdateParticipant, so turning Allow Screen Sharing off did not stop an in-progress share. Normalise the permission keys before deciding. Verified on a local Synapse + LiveKit + guard stack: share track gone from the SFU 2 s after the policy flip. Tests added (44 pass). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -362,3 +362,30 @@ class TestRoomStateParsing(unittest.TestCase):
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
|
||||
class NormalizePermissionTest(unittest.TestCase):
|
||||
"""livekit-server's JSON uses proto names (snake_case); the reconciler must
|
||||
not read camelCase and conclude nobody publishes (which silently disabled
|
||||
the live screenshare kill)."""
|
||||
|
||||
def test_snake_case_permission_is_reconciled(self):
|
||||
calls = []
|
||||
guard.livekit_update_participant = lambda alias, identity, perm: calls.append((identity, perm))
|
||||
participant = {
|
||||
"identity": "@a:x:DEV",
|
||||
"permission": {"can_subscribe": True, "can_publish": True, "can_publish_data": True, "can_publish_sources": []},
|
||||
}
|
||||
changed = guard.reconcile_participant("!r:x", participant, {"SCREEN_SHARE", "SCREEN_SHARE_AUDIO"})
|
||||
self.assertTrue(changed)
|
||||
identity, perm = calls[0]
|
||||
self.assertEqual(identity, "@a:x:DEV")
|
||||
self.assertTrue(perm["canPublish"])
|
||||
self.assertNotIn("SCREEN_SHARE", perm["canPublishSources"])
|
||||
self.assertIn("MICROPHONE", perm["canPublishSources"])
|
||||
self.assertTrue(perm["canSubscribe"]) # preserved
|
||||
|
||||
def test_camel_case_still_works(self):
|
||||
guard.livekit_update_participant = lambda *a: None
|
||||
participant = {"identity": "@a:x:DEV", "permission": {"canPublish": True, "canPublishSources": ["CAMERA"]}}
|
||||
self.assertFalse(guard.reconcile_participant("!r:x", participant, {"SCREEN_SHARE"}))
|
||||
|
||||
Reference in New Issue
Block a user