mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
fix(ws): resolve live role in hasChannelPerm to honor mid-session demotions
hasChannelPerm resolved permissions from the connect-time role snapshot (c.user.RoleID), so a user reassigned to a lower role kept the old role's voice privileges (CONNECT_VOICE and the SPEAK/VIDEO grants in the LiveKit token) until reconnect. Resolve the current role via GetRoleForUser(c.userID), matching the V2 handlers. (Security scan F5) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -228,3 +228,8 @@ func (h *Hub) HandleWebhookParticipantLeftForTest(userID int64, channelID int64,
|
||||
func (h *Hub) MustFullResyncForTest(lastSeq uint64) bool {
|
||||
return h.mustFullResync(lastSeq)
|
||||
}
|
||||
|
||||
// HasChannelPermForTest exposes Hub.hasChannelPerm for external tests.
|
||||
func (h *Hub) HasChannelPermForTest(c *Client, channelID, perm int64) bool {
|
||||
return h.hasChannelPerm(c, channelID, perm)
|
||||
}
|
||||
|
||||
@@ -193,11 +193,15 @@ func (h *Hub) handleMessage(c *Client, raw []byte) {
|
||||
|
||||
// hasChannelPerm reports whether the client's role has all the given permission bits.
|
||||
// Delegates to the unified permissions.Checker.
|
||||
//
|
||||
// F5: resolve the user's CURRENT role via GetRoleForUser(c.userID) rather than the
|
||||
// role snapshotted onto c.user at connect time. A mid-session role reassignment
|
||||
// (e.g. stripping CONNECT_VOICE) must take effect immediately for the live
|
||||
// connection — including the SPEAK/VIDEO grants baked into a freshly minted
|
||||
// LiveKit token — instead of persisting until the user reconnects. This mirrors
|
||||
// the V2 handlers, which already resolve the live role (deps.go).
|
||||
func (h *Hub) hasChannelPerm(c *Client, channelID int64, perm int64) bool {
|
||||
if c.user == nil {
|
||||
return false
|
||||
}
|
||||
role, err := h.db.GetRoleByID(c.user.RoleID)
|
||||
role, err := h.db.GetRoleForUser(c.userID)
|
||||
if err != nil || role == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package ws_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/owncord/server/permissions"
|
||||
"github.com/owncord/server/ws"
|
||||
)
|
||||
|
||||
// TestHasChannelPerm_UsesLiveRoleNotConnectSnapshot locks F5: hasChannelPerm must
|
||||
// resolve the user's CURRENT role, not the role snapshotted onto the Client at
|
||||
// connect time. Otherwise a user reassigned to a lower role mid-session keeps the
|
||||
// old role's voice privileges (CONNECT_VOICE / the SPEAK/VIDEO grants baked into
|
||||
// the LiveKit token) until they reconnect.
|
||||
func TestHasChannelPerm_UsesLiveRoleNotConnectSnapshot(t *testing.T) {
|
||||
hub, database := newHandlerHub(t)
|
||||
|
||||
// Connect-time role: Member (id 4), which carries CONNECT_VOICE.
|
||||
user := seedMemberUser(t, database, "demoted")
|
||||
chID := seedTestChannel(t, database, "vc-stale")
|
||||
|
||||
// The client's cached user snapshot still points at the Member role — this
|
||||
// is exactly the stale state the connection holds after a role reassignment.
|
||||
send := make(chan []byte, 4)
|
||||
c := ws.NewTestClientWithUser(hub, user, chID, send)
|
||||
|
||||
// Admin reassigns the user to a role WITHOUT CONNECT_VOICE. The live WS
|
||||
// connection is not refreshed, so c.user.RoleID is now stale.
|
||||
if _, err := database.Exec(
|
||||
`INSERT INTO roles (id, name, color, permissions, position, is_default)
|
||||
VALUES (100, 'novoice', NULL, ?, 5, 0)`,
|
||||
permissions.ReadMessages,
|
||||
); err != nil {
|
||||
t.Fatalf("seed novoice role: %v", err)
|
||||
}
|
||||
if _, err := database.Exec(`UPDATE users SET role_id = 100 WHERE id = ?`, user.ID); err != nil {
|
||||
t.Fatalf("reassign user role: %v", err)
|
||||
}
|
||||
|
||||
if hub.HasChannelPermForTest(c, chID, permissions.ConnectVoice) {
|
||||
t.Fatal("hasChannelPerm granted CONNECT_VOICE from the stale connect-time role; it must use the live role")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user