Skip to content

Commit

Permalink
Convert actorId / actorType in participants updates for federated users.
Browse files Browse the repository at this point in the history
See nextcloud/spreed#12863 for details.
  • Loading branch information
fancycode committed Aug 6, 2024
1 parent c83c42c commit 11a1f36
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 10 deletions.
14 changes: 14 additions & 0 deletions api_signaling.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ const (

// Version 2.0 validates auth params encoded as JWT.
HelloVersionV2 = "2.0"

ActorTypeUsers = "users"
ActorTypeFederatedUsers = "federated_users"
)

var (
Expand All @@ -52,6 +55,17 @@ func makePtr[T any](v T) *T {
return &v
}

func getStringMapEntry[T any](m map[string]interface{}, key string) (s T, ok bool) {
var defaultValue T
v, found := m[key]
if !found {
return defaultValue, false
}

s, ok = v.(T)
return
}

// ClientMessage is a message that is sent from a client to the server.
type ClientMessage struct {
json.Marshaler
Expand Down
47 changes: 39 additions & 8 deletions federation.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ func isClosedError(err error) bool {
strings.Contains(err.Error(), net.ErrClosed.Error())
}

func getCloudUrl(s string) string {
if strings.HasPrefix(s, "https://") {
s = s[8:]
} else {
s = strings.TrimPrefix(s, "http://")
}
if pos := strings.Index(s, "/ocs/v"); pos != -1 {
s = s[:pos]
}
return s
}

type FederationClient struct {
hub *Hub
session *ClientSession
Expand Down Expand Up @@ -577,17 +589,36 @@ func (c *FederationClient) joinRoom() error {
}

func (c *FederationClient) updateEventUsers(users []map[string]interface{}, localSessionId string, remoteSessionId string) {
localCloudUrl := "@" + getCloudUrl(c.session.BackendUrl())
localCloudUrlLen := len(localCloudUrl)
remoteCloudUrl := "@" + getCloudUrl(c.federation.Load().NextcloudUrl)
checkSessionId := true
for _, u := range users {
key := "sessionId"
sid, found := u[key]
if !found {
key := "sessionid"
sid, found = u[key]
if actorType, found := getStringMapEntry[string](u, "actorType"); found {
if actorId, found := getStringMapEntry[string](u, "actorId"); found {
switch actorType {
case ActorTypeFederatedUsers:
if strings.HasSuffix(actorId, localCloudUrl) {
u["actorId"] = actorId[:len(actorId)-localCloudUrlLen]
u["actorType"] = ActorTypeUsers
}
case ActorTypeUsers:
u["actorId"] = actorId + remoteCloudUrl
u["actorType"] = ActorTypeFederatedUsers
}
}
}
if found {
if sid, ok := sid.(string); ok && sid == remoteSessionId {

if checkSessionId {
key := "sessionId"
sid, found := getStringMapEntry[string](u, key)
if !found {
key := "sessionid"
sid, found = getStringMapEntry[string](u, key)
}
if found && sid == remoteSessionId {
u[key] = localSessionId
break
checkSessionId = false
}
}
}
Expand Down
35 changes: 33 additions & 2 deletions federation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ package signaling
import (
"context"
"encoding/json"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -286,23 +287,53 @@ func Test_Federation(t *testing.T) {
}
}

// Simulate request from the backend that somebody joined the call.
// Simulate request from the backend that a federated user joined the call.
users := []map[string]interface{}{
{
"sessionId": remoteSessionId,
"inCall": 1,
"actorId": "remoteUser@" + strings.TrimPrefix(server2.URL, "http://"),
"actorType": "federated_users",
},
}
room := hub1.getRoom(roomId)
require.NotNil(room)
room.PublishUsersInCallChanged(users, users)
var event *EventServerMessage
// For the local user, it's a federated user on server 2 that joined.
assert.NoError(checkReceiveClientEvent(ctx, client1, "update", &event))
assert.Equal(remoteSessionId, event.Update.Users[0]["sessionId"])
assert.Equal("remoteUser@"+strings.TrimPrefix(server2.URL, "http://"), event.Update.Users[0]["actorId"])
assert.Equal("federated_users", event.Update.Users[0]["actorType"])
assert.Equal(roomId, event.Update.RoomId)

// For the federated user, it's a local user that joined.
assert.NoError(checkReceiveClientEvent(ctx, client2, "update", &event))
assert.Equal(hello2.Hello.SessionId, event.Update.Users[0]["sessionId"])
assert.Equal("remoteUser", event.Update.Users[0]["actorId"])
assert.Equal("users", event.Update.Users[0]["actorType"])
assert.Equal(federatedRoomId, event.Update.RoomId)

// Simulate request from the backend that a local user joined the call.
users = []map[string]interface{}{
{
"sessionId": hello1.Hello.SessionId,
"inCall": 1,
"actorId": "localUser",
"actorType": "users",
},
}
room.PublishUsersInCallChanged(users, users)
// For the local user, it's a local user that joined.
assert.NoError(checkReceiveClientEvent(ctx, client1, "update", &event))
assert.Equal(hello1.Hello.SessionId, event.Update.Users[0]["sessionId"])
assert.Equal("localUser", event.Update.Users[0]["actorId"])
assert.Equal("users", event.Update.Users[0]["actorType"])
assert.Equal(roomId, event.Update.RoomId)
// For the federated user, it's a federated user on server 1 that joined.
assert.NoError(checkReceiveClientEvent(ctx, client2, "update", &event))
assert.Equal(hello1.Hello.SessionId, event.Update.Users[0]["sessionId"])
assert.Equal("localUser@"+strings.TrimPrefix(server1.URL, "http://"), event.Update.Users[0]["actorId"])
assert.Equal("federated_users", event.Update.Users[0]["actorType"])
assert.Equal(federatedRoomId, event.Update.RoomId)

// Joining another "direct" session will trigger correct events.
Expand Down

0 comments on commit 11a1f36

Please sign in to comment.