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 5, 2024
1 parent 0fc9487 commit 1a823ef
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 10 deletions.
61 changes: 53 additions & 8 deletions federation.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,45 @@ import (
const (
initialFederationReconnectInterval = 100 * time.Millisecond
maxFederationReconnectInterval = 8 * time.Second

ActorTypeUsers = "users"
ActorTypeFederatedUsers = "federated_users"
)

var (
ErrFederationNotSupported = NewError("federation_unsupported", "The target server does not support federation.")
)

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
}

func isClosedError(err error) bool {
return errors.Is(err, net.ErrClosed) ||
errors.Is(err, websocket.ErrCloseSent) ||
// Gorilla websocket hides the original net.Error, so also compare error messages
strings.Contains(err.Error(), net.ErrClosed.Error())
}

func getCloudUrl(s string) string {
if strings.HasPrefix(s, "https://") {
s = s[8:]
} else if strings.HasPrefix(s, "http://") {

Check failure on line 75 in federation.go

View workflow job for this annotation

GitHub Actions / golang

S1017: should replace this `if` statement with an unconditional `strings.TrimPrefix` (gosimple)
s = s[7:]
}
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 @@ -568,17 +594,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 1a823ef

Please sign in to comment.