Move Talk-specific API to "talk" package.

This commit is contained in:
Joachim Bauch 2025-12-16 14:14:23 +01:00
commit fbf93dca42
No known key found for this signature in database
GPG key ID: 77C1D22D53E15F02
34 changed files with 529 additions and 402 deletions

View file

@ -83,6 +83,25 @@ func (s RoomSessionId) WithoutFederation() RoomSessionId {
return RoomSessionId(strings.TrimPrefix(string(s), FederatedRoomSessionIdPrefix))
}
type Permission string
var (
PERMISSION_MAY_PUBLISH_MEDIA Permission = "publish-media"
PERMISSION_MAY_PUBLISH_AUDIO Permission = "publish-audio"
PERMISSION_MAY_PUBLISH_VIDEO Permission = "publish-video"
PERMISSION_MAY_PUBLISH_SCREEN Permission = "publish-screen"
PERMISSION_MAY_CONTROL Permission = "control"
PERMISSION_TRANSIENT_DATA Permission = "transient-data"
PERMISSION_HIDE_DISPLAYNAMES Permission = "hide-displaynames"
// DefaultPermissionOverrides contains permission overrides for users where
// no permissions have been set by the server. If a permission is not set in
// this map, it's assumed the user has that permission.
DefaultPermissionOverrides = map[Permission]bool{ // +checklocksignore: Global readonly variable.
PERMISSION_HIDE_DISPLAYNAMES: false,
}
)
// ClientMessage is a message that is sent from a client to the server.
type ClientMessage struct {
json.Marshaler

View file

@ -27,6 +27,7 @@ import (
"time"
"github.com/strukturag/nextcloud-spreed-signaling/api"
"github.com/strukturag/nextcloud-spreed-signaling/talk"
)
type AsyncMessage struct {
@ -36,9 +37,9 @@ type AsyncMessage struct {
Message *api.ServerMessage `json:"message,omitempty"`
Room *BackendServerRoomRequest `json:"room,omitempty"`
Room *talk.BackendServerRoomRequest `json:"room,omitempty"`
Permissions []Permission `json:"permissions,omitempty"`
Permissions []api.Permission `json:"permissions,omitempty"`
AsyncRoom *AsyncRoomMessage `json:"asyncroom,omitempty"`

View file

@ -91,12 +91,12 @@ func NewAsyncEventsNats(logger log.Logger, client nats.Client) (AsyncEvents, err
return events, nil
}
func (e *asyncEventsNats) GetServerInfoNats() *BackendServerInfoNats {
func (e *asyncEventsNats) GetServerInfoNats() *talk.BackendServerInfoNats {
// TODO: This should call a method on "e.client" directly instead of having a type switch.
var result *BackendServerInfoNats
var result *talk.BackendServerInfoNats
switch n := e.client.(type) {
case *nats.NativeClient:
result = &BackendServerInfoNats{
result = &talk.BackendServerInfoNats{
Urls: n.URLs(),
}
if n.IsConnected() {
@ -107,7 +107,7 @@ func (e *asyncEventsNats) GetServerInfoNats() *BackendServerInfoNats {
result.ClusterName = n.ConnectedClusterName()
}
case *nats.LoopbackClient:
result = &BackendServerInfoNats{
result = &talk.BackendServerInfoNats{
Urls: []string{nats.LoopbackUrl},
Connected: true,
ServerUrl: nats.LoopbackUrl,

View file

@ -166,7 +166,7 @@ func (b *BackendClient) PerformJSONRequest(ctx context.Context, u *url.URL, requ
}
// Add checksum so the backend can validate the request.
AddBackendChecksum(req, data.Bytes(), backend.Secret())
talk.AddBackendChecksum(req, data.Bytes(), backend.Secret())
start := time.Now()
resp, err := c.Do(req)

View file

@ -264,11 +264,11 @@ func (b *BackendServer) getTurnCredentials(w http.ResponseWriter, r *http.Reques
if username == "" {
// Make sure to include an actual username in the credentials.
username = newRandomString(randomUsernameLength)
username = internal.RandomString(randomUsernameLength)
}
username, password := calculateTurnSecret(username, b.turnsecret, b.turnvalid)
result := TurnCredentials{
result := talk.TurnCredentials{
Username: username,
Password: password,
TTL: int64(b.turnvalid.Seconds()),
@ -309,8 +309,8 @@ func (b *BackendServer) parseRequestBody(f func(context.Context, http.ResponseWr
return
}
if r.Header.Get(HeaderBackendSignalingRandom) == "" ||
r.Header.Get(HeaderBackendSignalingChecksum) == "" {
if r.Header.Get(talk.HeaderBackendSignalingRandom) == "" ||
r.Header.Get(talk.HeaderBackendSignalingChecksum) == "" {
http.Error(w, "Authentication check failed", http.StatusForbidden)
return
}
@ -500,7 +500,7 @@ func (b *BackendServer) fixupUserSessions(ctx context.Context, cache *container.
return result
}
func (b *BackendServer) sendRoomIncall(roomid string, backend *talk.Backend, request *BackendServerRoomRequest) error {
func (b *BackendServer) sendRoomIncall(roomid string, backend *talk.Backend, request *talk.BackendServerRoomRequest) error {
if !request.InCall.All {
timeout := time.Second
@ -525,7 +525,7 @@ func (b *BackendServer) sendRoomIncall(roomid string, backend *talk.Backend, req
return b.events.PublishBackendRoomMessage(roomid, backend, message)
}
func (b *BackendServer) sendRoomParticipantsUpdate(ctx context.Context, roomid string, backend *talk.Backend, request *BackendServerRoomRequest) error {
func (b *BackendServer) sendRoomParticipantsUpdate(ctx context.Context, roomid string, backend *talk.Backend, request *talk.BackendServerRoomRequest) error {
timeout := time.Second
// Convert (Nextcloud) session ids to signaling session ids.
@ -558,18 +558,18 @@ loop:
b.logger.Printf("Received invalid permissions %+v (%s) for session %s", permissionsInterface, reflect.TypeOf(permissionsInterface), sessionId)
continue
}
var permissions []Permission
var permissions []api.Permission
for idx, ob := range permissionsList {
permission, ok := ob.(string)
if !ok {
b.logger.Printf("Received invalid permission at position %d %+v (%s) for session %s", idx, ob, reflect.TypeOf(ob), sessionId)
continue loop
}
permissions = append(permissions, Permission(permission))
permissions = append(permissions, api.Permission(permission))
}
wg.Add(1)
go func(sessionId api.PublicSessionId, permissions []Permission) {
go func(sessionId api.PublicSessionId, permissions []api.Permission) {
defer wg.Done()
message := &AsyncMessage{
Type: "permissions",
@ -589,7 +589,7 @@ loop:
return b.events.PublishBackendRoomMessage(roomid, backend, message)
}
func (b *BackendServer) sendRoomMessage(roomid string, backend *talk.Backend, request *BackendServerRoomRequest) error {
func (b *BackendServer) sendRoomMessage(roomid string, backend *talk.Backend, request *talk.BackendServerRoomRequest) error {
message := &AsyncMessage{
Type: "room",
Room: request,
@ -597,7 +597,7 @@ func (b *BackendServer) sendRoomMessage(roomid string, backend *talk.Backend, re
return b.events.PublishBackendRoomMessage(roomid, backend, message)
}
func (b *BackendServer) sendRoomSwitchTo(ctx context.Context, roomid string, backend *talk.Backend, request *BackendServerRoomRequest) error {
func (b *BackendServer) sendRoomSwitchTo(ctx context.Context, roomid string, backend *talk.Backend, request *talk.BackendServerRoomRequest) error {
timeout := time.Second
// Convert (Nextcloud) session ids to signaling session ids.
@ -609,7 +609,7 @@ func (b *BackendServer) sendRoomSwitchTo(ctx context.Context, roomid string, bac
if len(request.SwitchTo.Sessions) > 0 {
// We support both a list of sessions or a map with additional details per session.
if request.SwitchTo.Sessions[0] == '[' {
var sessionsList BackendRoomSwitchToSessionsList
var sessionsList talk.BackendRoomSwitchToSessionsList
if err := json.Unmarshal(request.SwitchTo.Sessions, &sessionsList); err != nil {
return err
}
@ -618,7 +618,7 @@ func (b *BackendServer) sendRoomSwitchTo(ctx context.Context, roomid string, bac
return nil
}
var internalSessionsList BackendRoomSwitchToPublicSessionsList
var internalSessionsList talk.BackendRoomSwitchToPublicSessionsList
for _, roomSessionId := range sessionsList {
if roomSessionId == sessionIdNotInMeeting {
continue
@ -647,7 +647,7 @@ func (b *BackendServer) sendRoomSwitchTo(ctx context.Context, roomid string, bac
request.SwitchTo.SessionsList = internalSessionsList
request.SwitchTo.SessionsMap = nil
} else {
var sessionsMap BackendRoomSwitchToSessionsMap
var sessionsMap talk.BackendRoomSwitchToSessionsMap
if err := json.Unmarshal(request.SwitchTo.Sessions, &sessionsMap); err != nil {
return err
}
@ -656,7 +656,7 @@ func (b *BackendServer) sendRoomSwitchTo(ctx context.Context, roomid string, bac
return nil
}
internalSessionsMap := make(BackendRoomSwitchToPublicSessionsMap)
internalSessionsMap := make(talk.BackendRoomSwitchToPublicSessionsMap)
for roomSessionId, details := range sessionsMap {
if roomSessionId == sessionIdNotInMeeting {
continue
@ -700,7 +700,7 @@ type BackendResponseWithStatus interface {
}
type DialoutErrorResponse struct {
BackendServerRoomResponse
talk.BackendServerRoomResponse
status int
}
@ -711,9 +711,9 @@ func (r *DialoutErrorResponse) Status() int {
func returnDialoutError(status int, err *api.Error) (any, error) {
response := &DialoutErrorResponse{
BackendServerRoomResponse: BackendServerRoomResponse{
BackendServerRoomResponse: talk.BackendServerRoomResponse{
Type: "dialout",
Dialout: &BackendRoomDialoutResponse{
Dialout: &talk.BackendRoomDialoutResponse{
Error: err,
},
},
@ -729,7 +729,7 @@ func isNumeric(s string) bool {
return checkNumeric.MatchString(s)
}
func (b *BackendServer) startDialoutInSession(ctx context.Context, session *ClientSession, roomid string, backend *talk.Backend, backendUrl string, request *BackendServerRoomRequest) (any, error) {
func (b *BackendServer) startDialoutInSession(ctx context.Context, session *ClientSession, roomid string, backend *talk.Backend, backendUrl string, request *talk.BackendServerRoomRequest) (any, error) {
url := backendUrl
if url != "" && url[len(url)-1] != '/' {
url += "/"
@ -742,7 +742,7 @@ func (b *BackendServer) startDialoutInSession(ctx context.Context, session *Clie
url = urls[0]
}
}
id := newRandomString(32)
id := internal.RandomString(32)
msg := &api.ServerMessage{
Id: id,
Type: "internal",
@ -799,9 +799,9 @@ func (b *BackendServer) startDialoutInSession(ctx context.Context, session *Clie
return nil, api.NewError("unsupported_status", fmt.Sprintf("Unsupported dialout status received: %+v", dialout))
}
return &BackendServerRoomResponse{
return &talk.BackendServerRoomResponse{
Type: "dialout",
Dialout: &BackendRoomDialoutResponse{
Dialout: &talk.BackendRoomDialoutResponse{
CallId: dialout.Status.CallId,
},
}, nil
@ -810,7 +810,7 @@ func (b *BackendServer) startDialoutInSession(ctx context.Context, session *Clie
}
}
func (b *BackendServer) startDialout(ctx context.Context, roomid string, backend *talk.Backend, backendUrl string, request *BackendServerRoomRequest) (any, error) {
func (b *BackendServer) startDialout(ctx context.Context, roomid string, backend *talk.Backend, backendUrl string, request *talk.BackendServerRoomRequest) (any, error) {
if err := request.Dialout.ValidateNumber(); err != nil {
return returnDialoutError(http.StatusBadRequest, err)
}
@ -862,7 +862,7 @@ func (b *BackendServer) roomHandler(ctx context.Context, w http.ResponseWriter,
roomid := v["roomid"]
var backend *talk.Backend
backendUrl := r.Header.Get(HeaderBackendServer)
backendUrl := r.Header.Get(talk.HeaderBackendServer)
if backendUrl != "" {
if u, err := url.Parse(backendUrl); err == nil {
backend = b.hub.backend.GetBackend(u)
@ -884,7 +884,7 @@ func (b *BackendServer) roomHandler(ctx context.Context, w http.ResponseWriter,
// Old-style Talk, find backend that created the checksum.
// TODO(fancycode): Remove once all supported Talk versions send the backend header.
for _, b := range b.hub.backend.GetBackends() {
if ValidateBackendChecksum(r, body, b.Secret()) {
if talk.ValidateBackendChecksum(r, body, b.Secret()) {
backend = b
break
}
@ -898,13 +898,13 @@ func (b *BackendServer) roomHandler(ctx context.Context, w http.ResponseWriter,
}
}
if !ValidateBackendChecksum(r, body, backend.Secret()) {
if !talk.ValidateBackendChecksum(r, body, backend.Secret()) {
throttle(ctx)
http.Error(w, "Authentication check failed", http.StatusForbidden)
return
}
var request BackendServerRoomRequest
var request talk.BackendServerRoomRequest
if err := json.Unmarshal(body, &request); err != nil {
b.logger.Printf("Error decoding body %s: %s", string(body), err)
http.Error(w, "Could not read body", http.StatusBadRequest)
@ -1017,7 +1017,7 @@ func (b *BackendServer) statsHandler(w http.ResponseWriter, r *http.Request) {
}
func (b *BackendServer) serverinfoHandler(w http.ResponseWriter, r *http.Request) {
info := BackendServerInfo{
info := talk.BackendServerInfo{
Version: b.version,
Features: b.hub.info.Features,

View file

@ -47,8 +47,10 @@ import (
"github.com/stretchr/testify/require"
"github.com/strukturag/nextcloud-spreed-signaling/api"
"github.com/strukturag/nextcloud-spreed-signaling/internal"
"github.com/strukturag/nextcloud-spreed-signaling/log"
"github.com/strukturag/nextcloud-spreed-signaling/nats"
"github.com/strukturag/nextcloud-spreed-signaling/talk"
)
var (
@ -229,8 +231,8 @@ func performBackendRequest(requestUrl string, body []byte) (*http.Response, erro
return nil, err
}
request.Header.Set("Content-Type", "application/json")
rnd := newRandomString(32)
check := CalculateBackendChecksum(rnd, body, testBackendSecret)
rnd := internal.RandomString(32)
check := talk.CalculateBackendChecksum(rnd, body, testBackendSecret)
request.Header.Set("Spreed-Signaling-Random", rnd)
request.Header.Set("Spreed-Signaling-Checksum", check)
u, err := url.Parse(requestUrl)
@ -323,9 +325,9 @@ func TestBackendServer_OldCompatAuth(t *testing.T) {
roomId := "the-room-id"
userid := "the-user-id"
roomProperties := json.RawMessage("{\"foo\":\"bar\"}")
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "invite",
Invite: &BackendRoomInviteRequest{
Invite: &talk.BackendRoomInviteRequest{
UserIds: []string{
userid,
},
@ -342,8 +344,8 @@ func TestBackendServer_OldCompatAuth(t *testing.T) {
request, err := http.NewRequest("POST", server.URL+"/api/v1/room/"+roomId, bytes.NewReader(data))
require.NoError(err)
request.Header.Set("Content-Type", "application/json")
rnd := newRandomString(32)
check := CalculateBackendChecksum(rnd, data, testBackendSecret)
rnd := internal.RandomString(32)
check := talk.CalculateBackendChecksum(rnd, data, testBackendSecret)
request.Header.Set("Spreed-Signaling-Random", rnd)
request.Header.Set("Spreed-Signaling-Checksum", check)
client := &http.Client{}
@ -378,7 +380,7 @@ func TestBackendServer_UnsupportedRequest(t *testing.T) {
assert := assert.New(t)
_, _, _, _, _, server := CreateBackendServerForTest(t)
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "lala",
}
@ -433,9 +435,9 @@ func RunTestBackendServer_RoomInvite(ctx context.Context, t *testing.T) {
defer func() {
assert.NoError(events.UnregisterUserListener(userid, backend, listener))
}()
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "invite",
Invite: &BackendRoomInviteRequest{
Invite: &talk.BackendRoomInviteRequest{
UserIds: []string{
userid,
},
@ -509,9 +511,9 @@ func RunTestBackendServer_RoomDisinvite(ctx context.Context, t *testing.T) {
defer func() {
assert.NoError(events.UnregisterUserListener(testDefaultUserId, backend, listener))
}()
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "disinvite",
Disinvite: &BackendRoomDisinviteRequest{
Disinvite: &talk.BackendRoomDisinviteRequest{
UserIds: []string{
testDefaultUserId,
},
@ -569,9 +571,9 @@ func TestBackendServer_RoomDisinviteDifferentRooms(t *testing.T) {
MustSucceed2(t, client2.JoinRoom, ctx, roomId2)
require.True(client2.RunUntilJoined(ctx, hello2.Hello))
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "disinvite",
Disinvite: &BackendRoomDisinviteRequest{
Disinvite: &talk.BackendRoomDisinviteRequest{
UserIds: []string{
testDefaultUserId,
},
@ -601,9 +603,9 @@ func TestBackendServer_RoomDisinviteDifferentRooms(t *testing.T) {
assert.Equal(roomId1, message.RoomId)
}
msg = &BackendServerRoomRequest{
msg = &talk.BackendServerRoomRequest{
Type: "update",
Update: &BackendRoomUpdateRequest{
Update: &talk.BackendRoomUpdateRequest{
UserIds: []string{
testDefaultUserId,
},
@ -664,9 +666,9 @@ func RunTestBackendServer_RoomUpdate(ctx context.Context, t *testing.T) {
defer func() {
assert.NoError(events.UnregisterUserListener(userid, backend, listener))
}()
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "update",
Update: &BackendRoomUpdateRequest{
Update: &talk.BackendRoomUpdateRequest{
UserIds: []string{
userid,
},
@ -732,9 +734,9 @@ func RunTestBackendServer_RoomDelete(ctx context.Context, t *testing.T) {
defer func() {
assert.NoError(events.UnregisterUserListener(userid, backend, listener))
}()
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "delete",
Delete: &BackendRoomDeleteRequest{
Delete: &talk.BackendRoomDeleteRequest{
UserIds: []string{
userid,
},
@ -801,10 +803,10 @@ func TestBackendServer_ParticipantsUpdatePermissions(t *testing.T) {
require.NotNil(session2, "Session %s does not exist", hello2.Hello.SessionId)
// Sessions have all permissions initially (fallback for old-style sessions).
assertSessionHasPermission(t, session1, PERMISSION_MAY_PUBLISH_MEDIA)
assertSessionHasPermission(t, session1, PERMISSION_MAY_PUBLISH_SCREEN)
assertSessionHasPermission(t, session2, PERMISSION_MAY_PUBLISH_MEDIA)
assertSessionHasPermission(t, session2, PERMISSION_MAY_PUBLISH_SCREEN)
assertSessionHasPermission(t, session1, api.PERMISSION_MAY_PUBLISH_MEDIA)
assertSessionHasPermission(t, session1, api.PERMISSION_MAY_PUBLISH_SCREEN)
assertSessionHasPermission(t, session2, api.PERMISSION_MAY_PUBLISH_MEDIA)
assertSessionHasPermission(t, session2, api.PERMISSION_MAY_PUBLISH_SCREEN)
// Join room by id.
roomId := "test-room"
@ -817,27 +819,27 @@ func TestBackendServer_ParticipantsUpdatePermissions(t *testing.T) {
assert.NoError(client1.DrainMessages(ctx))
assert.NoError(client2.DrainMessages(ctx))
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "participants",
Participants: &BackendRoomParticipantsRequest{
Participants: &talk.BackendRoomParticipantsRequest{
Changed: []api.StringMap{
{
"sessionId": fmt.Sprintf("%s-%s", roomId, hello1.Hello.SessionId),
"permissions": []Permission{PERMISSION_MAY_PUBLISH_MEDIA},
"permissions": []api.Permission{api.PERMISSION_MAY_PUBLISH_MEDIA},
},
{
"sessionId": fmt.Sprintf("%s-%s", roomId, hello2.Hello.SessionId),
"permissions": []Permission{PERMISSION_MAY_PUBLISH_SCREEN},
"permissions": []api.Permission{api.PERMISSION_MAY_PUBLISH_SCREEN},
},
},
Users: []api.StringMap{
{
"sessionId": fmt.Sprintf("%s-%s", roomId, hello1.Hello.SessionId),
"permissions": []Permission{PERMISSION_MAY_PUBLISH_MEDIA},
"permissions": []api.Permission{api.PERMISSION_MAY_PUBLISH_MEDIA},
},
{
"sessionId": fmt.Sprintf("%s-%s", roomId, hello2.Hello.SessionId),
"permissions": []Permission{PERMISSION_MAY_PUBLISH_SCREEN},
"permissions": []api.Permission{api.PERMISSION_MAY_PUBLISH_SCREEN},
},
},
},
@ -856,10 +858,10 @@ func TestBackendServer_ParticipantsUpdatePermissions(t *testing.T) {
// TODO: Use event to wait for asynchronous messages.
time.Sleep(10 * time.Millisecond)
assertSessionHasPermission(t, session1, PERMISSION_MAY_PUBLISH_MEDIA)
assertSessionHasNotPermission(t, session1, PERMISSION_MAY_PUBLISH_SCREEN)
assertSessionHasNotPermission(t, session2, PERMISSION_MAY_PUBLISH_MEDIA)
assertSessionHasPermission(t, session2, PERMISSION_MAY_PUBLISH_SCREEN)
assertSessionHasPermission(t, session1, api.PERMISSION_MAY_PUBLISH_MEDIA)
assertSessionHasNotPermission(t, session1, api.PERMISSION_MAY_PUBLISH_SCREEN)
assertSessionHasNotPermission(t, session2, api.PERMISSION_MAY_PUBLISH_MEDIA)
assertSessionHasPermission(t, session2, api.PERMISSION_MAY_PUBLISH_SCREEN)
})
}
}
@ -882,8 +884,8 @@ func TestBackendServer_ParticipantsUpdateEmptyPermissions(t *testing.T) {
assert.NotNil(session, "Session %s does not exist", hello.Hello.SessionId)
// Sessions have all permissions initially (fallback for old-style sessions).
assertSessionHasPermission(t, session, PERMISSION_MAY_PUBLISH_MEDIA)
assertSessionHasPermission(t, session, PERMISSION_MAY_PUBLISH_SCREEN)
assertSessionHasPermission(t, session, api.PERMISSION_MAY_PUBLISH_MEDIA)
assertSessionHasPermission(t, session, api.PERMISSION_MAY_PUBLISH_SCREEN)
// Join room by id.
roomId := "test-room"
@ -895,19 +897,19 @@ func TestBackendServer_ParticipantsUpdateEmptyPermissions(t *testing.T) {
// Updating with empty permissions upgrades to non-old-style and removes
// all previously available permissions.
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "participants",
Participants: &BackendRoomParticipantsRequest{
Participants: &talk.BackendRoomParticipantsRequest{
Changed: []api.StringMap{
{
"sessionId": fmt.Sprintf("%s-%s", roomId, hello.Hello.SessionId),
"permissions": []Permission{},
"permissions": []api.Permission{},
},
},
Users: []api.StringMap{
{
"sessionId": fmt.Sprintf("%s-%s", roomId, hello.Hello.SessionId),
"permissions": []Permission{},
"permissions": []api.Permission{},
},
},
},
@ -925,8 +927,8 @@ func TestBackendServer_ParticipantsUpdateEmptyPermissions(t *testing.T) {
// TODO: Use event to wait for asynchronous messages.
time.Sleep(10 * time.Millisecond)
assertSessionHasNotPermission(t, session, PERMISSION_MAY_PUBLISH_MEDIA)
assertSessionHasNotPermission(t, session, PERMISSION_MAY_PUBLISH_SCREEN)
assertSessionHasNotPermission(t, session, api.PERMISSION_MAY_PUBLISH_MEDIA)
assertSessionHasNotPermission(t, session, api.PERMISSION_MAY_PUBLISH_SCREEN)
}
func TestBackendServer_ParticipantsUpdateTimeout(t *testing.T) {
@ -963,9 +965,9 @@ func TestBackendServer_ParticipantsUpdateTimeout(t *testing.T) {
wg.Add(1)
go func() {
defer wg.Done()
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "incall",
InCall: &BackendRoomInCallRequest{
InCall: &talk.BackendRoomInCallRequest{
InCall: json.RawMessage("7"),
Changed: []api.StringMap{
{
@ -1010,9 +1012,9 @@ func TestBackendServer_ParticipantsUpdateTimeout(t *testing.T) {
wg.Add(1)
go func() {
defer wg.Done()
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "incall",
InCall: &BackendRoomInCallRequest{
InCall: &talk.BackendRoomInCallRequest{
InCall: json.RawMessage("7"),
Changed: []api.StringMap{
{
@ -1150,9 +1152,9 @@ func TestBackendServer_InCallAll(t *testing.T) {
wg.Add(1)
go func() {
defer wg.Done()
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "incall",
InCall: &BackendRoomInCallRequest{
InCall: &talk.BackendRoomInCallRequest{
InCall: json.RawMessage("7"),
All: true,
},
@ -1207,9 +1209,9 @@ func TestBackendServer_InCallAll(t *testing.T) {
wg.Add(1)
go func() {
defer wg.Done()
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "incall",
InCall: &BackendRoomInCallRequest{
InCall: &talk.BackendRoomInCallRequest{
InCall: json.RawMessage("0"),
All: true,
},
@ -1287,9 +1289,9 @@ func TestBackendServer_RoomMessage(t *testing.T) {
assert.NoError(client.DrainMessages(ctx))
messageData := json.RawMessage("{\"foo\":\"bar\"}")
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "message",
Message: &BackendRoomMessageRequest{
Message: &talk.BackendRoomMessageRequest{
Data: messageData,
},
}
@ -1328,7 +1330,7 @@ func TestBackendServer_TurnCredentials(t *testing.T) {
assert.NoError(err)
assert.Equal(http.StatusOK, res.StatusCode, "Expected successful request, got %s", string(body))
var cred TurnCredentials
var cred talk.TurnCredentials
require.NoError(json.Unmarshal(body, &cred))
m := hmac.New(sha1.New, []byte(turnSecret))
@ -1452,9 +1454,9 @@ func TestBackendServer_DialoutNoSipBridge(t *testing.T) {
MustSucceed1(t, client.RunUntilHello, ctx)
roomId := "12345"
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "dialout",
Dialout: &BackendRoomDialoutRequest{
Dialout: &talk.BackendRoomDialoutRequest{
Number: "+1234567890",
},
}
@ -1468,7 +1470,7 @@ func TestBackendServer_DialoutNoSipBridge(t *testing.T) {
assert.NoError(err)
require.Equal(http.StatusNotFound, res.StatusCode, "Expected error, got %s", string(body))
var response BackendServerRoomResponse
var response talk.BackendServerRoomResponse
if assert.NoError(json.Unmarshal(body, &response)) {
assert.Equal("dialout", response.Type)
if assert.NotNil(response.Dialout) &&
@ -1539,9 +1541,9 @@ func TestBackendServer_DialoutAccepted(t *testing.T) {
<-stopped
}()
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "dialout",
Dialout: &BackendRoomDialoutRequest{
Dialout: &talk.BackendRoomDialoutRequest{
Number: "+1234567890",
},
}
@ -1555,7 +1557,7 @@ func TestBackendServer_DialoutAccepted(t *testing.T) {
assert.NoError(err)
require.Equal(http.StatusOK, res.StatusCode, "Expected success, got %s", string(body))
var response BackendServerRoomResponse
var response talk.BackendServerRoomResponse
if err := json.Unmarshal(body, &response); assert.NoError(err) {
assert.Equal("dialout", response.Type)
if assert.NotNil(response.Dialout) {
@ -1626,9 +1628,9 @@ func TestBackendServer_DialoutAcceptedCompat(t *testing.T) {
<-stopped
}()
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "dialout",
Dialout: &BackendRoomDialoutRequest{
Dialout: &talk.BackendRoomDialoutRequest{
Number: "+1234567890",
},
}
@ -1642,7 +1644,7 @@ func TestBackendServer_DialoutAcceptedCompat(t *testing.T) {
assert.NoError(err)
require.Equal(http.StatusOK, res.StatusCode, "Expected success, got %s", string(body))
var response BackendServerRoomResponse
var response talk.BackendServerRoomResponse
if err := json.Unmarshal(body, &response); assert.NoError(err) {
assert.Equal("dialout", response.Type)
if assert.NotNil(response.Dialout) {
@ -1710,9 +1712,9 @@ func TestBackendServer_DialoutRejected(t *testing.T) {
<-stopped
}()
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "dialout",
Dialout: &BackendRoomDialoutRequest{
Dialout: &talk.BackendRoomDialoutRequest{
Number: "+1234567890",
},
}
@ -1726,7 +1728,7 @@ func TestBackendServer_DialoutRejected(t *testing.T) {
assert.NoError(err)
require.Equal(http.StatusBadGateway, res.StatusCode, "Expected error, got %s", string(body))
var response BackendServerRoomResponse
var response talk.BackendServerRoomResponse
if err := json.Unmarshal(body, &response); assert.NoError(err) {
assert.Equal("dialout", response.Type)
if assert.NotNil(response.Dialout) &&
@ -1824,9 +1826,9 @@ func TestBackendServer_DialoutFirstFailed(t *testing.T) {
wg.Wait()
}()
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "dialout",
Dialout: &BackendRoomDialoutRequest{
Dialout: &talk.BackendRoomDialoutRequest{
Number: "+1234567890",
},
}
@ -1840,7 +1842,7 @@ func TestBackendServer_DialoutFirstFailed(t *testing.T) {
assert.NoError(err)
require.Equal(http.StatusOK, res.StatusCode, "Expected success, got %s", string(body))
var response BackendServerRoomResponse
var response talk.BackendServerRoomResponse
if err := json.Unmarshal(body, &response); assert.NoError(err) {
assert.Equal("dialout", response.Type)
if assert.NotNil(response.Dialout) {

View file

@ -47,7 +47,6 @@ import (
"github.com/mailru/easyjson/jlexer"
"github.com/mailru/easyjson/jwriter"
signaling "github.com/strukturag/nextcloud-spreed-signaling"
"github.com/strukturag/nextcloud-spreed-signaling/api"
"github.com/strukturag/nextcloud-spreed-signaling/config"
"github.com/strukturag/nextcloud-spreed-signaling/internal"
@ -428,28 +427,28 @@ func registerAuthHandler(router *mux.Router) {
return
}
rnd := r.Header.Get(signaling.HeaderBackendSignalingRandom)
checksum := r.Header.Get(signaling.HeaderBackendSignalingChecksum)
rnd := r.Header.Get(talk.HeaderBackendSignalingRandom)
checksum := r.Header.Get(talk.HeaderBackendSignalingChecksum)
if rnd == "" || checksum == "" {
log.Println("No checksum headers found")
return
}
if verify := signaling.CalculateBackendChecksum(rnd, body, backendSecret); verify != checksum {
if verify := talk.CalculateBackendChecksum(rnd, body, backendSecret); verify != checksum {
log.Println("Backend checksum verification failed")
return
}
var request signaling.BackendClientRequest
var request talk.BackendClientRequest
if err := request.UnmarshalJSON(body); err != nil {
log.Println(err)
return
}
response := &signaling.BackendClientResponse{
response := &talk.BackendClientResponse{
Type: "auth",
Auth: &signaling.BackendClientAuthResponse{
Version: signaling.BackendVersion,
Auth: &talk.BackendClientAuthResponse{
Version: talk.BackendVersion,
UserId: "sample-user",
},
}

View file

@ -75,7 +75,7 @@ type ClientSession struct {
// +checklocks:mu
supportsPermissions bool
// +checklocks:mu
permissions map[Permission]bool
permissions map[api.Permission]bool
backend *talk.Backend
backendUrl string
@ -120,7 +120,7 @@ type ClientSession struct {
responseHandlers map[string]ResponseHandlerFunc
}
func NewClientSession(hub *Hub, privateId api.PrivateSessionId, publicId api.PublicSessionId, data *SessionIdData, backend *talk.Backend, hello *api.HelloClientMessage, auth *BackendClientAuthResponse) (*ClientSession, error) {
func NewClientSession(hub *Hub, privateId api.PrivateSessionId, publicId api.PublicSessionId, data *SessionIdData, backend *talk.Backend, hello *api.HelloClientMessage, auth *talk.BackendClientAuthResponse) (*ClientSession, error) {
ctx := log.NewLoggerContext(context.Background(), hub.logger)
ctx, closeFunc := context.WithCancel(ctx)
s := &ClientSession{
@ -208,18 +208,18 @@ func (s *ClientSession) HasFeature(feature string) bool {
}
// HasPermission checks if the session has the passed permissions.
func (s *ClientSession) HasPermission(permission Permission) bool {
func (s *ClientSession) HasPermission(permission api.Permission) bool {
s.mu.Lock()
defer s.mu.Unlock()
return s.hasPermissionLocked(permission)
}
func (s *ClientSession) GetPermissions() []Permission {
func (s *ClientSession) GetPermissions() []api.Permission {
s.mu.Lock()
defer s.mu.Unlock()
result := make([]Permission, len(s.permissions))
result := make([]api.Permission, len(s.permissions))
for p, ok := range s.permissions {
if ok {
result = append(result, p)
@ -229,7 +229,7 @@ func (s *ClientSession) GetPermissions() []Permission {
}
// HasAnyPermission checks if the session has one of the passed permissions.
func (s *ClientSession) HasAnyPermission(permission ...Permission) bool {
func (s *ClientSession) HasAnyPermission(permission ...api.Permission) bool {
if len(permission) == 0 {
return false
}
@ -241,7 +241,7 @@ func (s *ClientSession) HasAnyPermission(permission ...Permission) bool {
}
// +checklocks:s.mu
func (s *ClientSession) hasAnyPermissionLocked(permission ...Permission) bool {
func (s *ClientSession) hasAnyPermissionLocked(permission ...api.Permission) bool {
if len(permission) == 0 {
return false
}
@ -250,10 +250,10 @@ func (s *ClientSession) hasAnyPermissionLocked(permission ...Permission) bool {
}
// +checklocks:s.mu
func (s *ClientSession) hasPermissionLocked(permission Permission) bool {
func (s *ClientSession) hasPermissionLocked(permission api.Permission) bool {
if !s.supportsPermissions {
// Old-style session that doesn't receive permissions from Nextcloud.
if result, found := DefaultPermissionOverrides[permission]; found {
if result, found := api.DefaultPermissionOverrides[permission]; found {
return result
}
return true
@ -265,11 +265,11 @@ func (s *ClientSession) hasPermissionLocked(permission Permission) bool {
return false
}
func (s *ClientSession) SetPermissions(permissions []Permission) {
var p map[Permission]bool
func (s *ClientSession) SetPermissions(permissions []api.Permission) {
var p map[api.Permission]bool
for _, permission := range permissions {
if p == nil {
p = make(map[Permission]bool)
p = make(map[api.Permission]bool)
}
p[permission] = true
}
@ -584,7 +584,7 @@ func (s *ClientSession) doUnsubscribeRoomEvents(notify bool) {
// Notify
go func(sid api.RoomSessionId) {
ctx := log.NewLoggerContext(context.Background(), s.logger)
request := NewBackendClientRoomRequest(room.Id(), s.userId, sid)
request := talk.NewBackendClientRoomRequest(room.Id(), s.userId, sid)
request.Room.UpdateFromSession(s)
request.Room.Action = "leave"
var response api.StringMap
@ -824,10 +824,10 @@ func (s *ClientSession) SubscriberClosed(subscriber McuSubscriber) {
}
type PermissionError struct {
permission Permission
permission api.Permission
}
func (e *PermissionError) Permission() Permission {
func (e *PermissionError) Permission() api.Permission {
return e.permission
}
@ -843,18 +843,18 @@ func (s *ClientSession) isSdpAllowedToSendLocked(sdp *sdp.SessionDescription) (M
}
var mediaTypes MediaType
mayPublishMedia := s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_MEDIA)
mayPublishMedia := s.hasPermissionLocked(api.PERMISSION_MAY_PUBLISH_MEDIA)
for _, md := range sdp.MediaDescriptions {
switch md.MediaName.Media {
case "audio":
if !mayPublishMedia && !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_AUDIO) {
return 0, &PermissionError{PERMISSION_MAY_PUBLISH_AUDIO}
if !mayPublishMedia && !s.hasPermissionLocked(api.PERMISSION_MAY_PUBLISH_AUDIO) {
return 0, &PermissionError{api.PERMISSION_MAY_PUBLISH_AUDIO}
}
mediaTypes |= MediaTypeAudio
case "video":
if !mayPublishMedia && !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_VIDEO) {
return 0, &PermissionError{PERMISSION_MAY_PUBLISH_VIDEO}
if !mayPublishMedia && !s.hasPermissionLocked(api.PERMISSION_MAY_PUBLISH_VIDEO) {
return 0, &PermissionError{api.PERMISSION_MAY_PUBLISH_VIDEO}
}
mediaTypes |= MediaTypeVideo
@ -870,11 +870,11 @@ func (s *ClientSession) IsAllowedToSend(data *api.MessageClientMessageData) erro
switch {
case data != nil && data.RoomType == "screen":
if s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_SCREEN) {
if s.hasPermissionLocked(api.PERMISSION_MAY_PUBLISH_SCREEN) {
return nil
}
return &PermissionError{PERMISSION_MAY_PUBLISH_SCREEN}
case s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_MEDIA):
return &PermissionError{api.PERMISSION_MAY_PUBLISH_SCREEN}
case s.hasPermissionLocked(api.PERMISSION_MAY_PUBLISH_MEDIA):
// Client is allowed to publish any media (audio / video).
return nil
case data != nil && data.Type == "offer":
@ -886,7 +886,7 @@ func (s *ClientSession) IsAllowedToSend(data *api.MessageClientMessageData) erro
return nil
default:
// Candidate or unknown event, check if client is allowed to publish any media.
if s.hasAnyPermissionLocked(PERMISSION_MAY_PUBLISH_AUDIO, PERMISSION_MAY_PUBLISH_VIDEO) {
if s.hasAnyPermissionLocked(api.PERMISSION_MAY_PUBLISH_AUDIO, api.PERMISSION_MAY_PUBLISH_VIDEO) {
return nil
}
@ -904,8 +904,8 @@ func (s *ClientSession) CheckOfferType(streamType StreamType, data *api.MessageC
// +checklocks:s.mu
func (s *ClientSession) checkOfferTypeLocked(streamType StreamType, data *api.MessageClientMessageData) (MediaType, error) {
if streamType == StreamTypeScreen {
if !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_SCREEN) {
return 0, &PermissionError{PERMISSION_MAY_PUBLISH_SCREEN}
if !s.hasPermissionLocked(api.PERMISSION_MAY_PUBLISH_SCREEN) {
return 0, &PermissionError{api.PERMISSION_MAY_PUBLISH_SCREEN}
}
return MediaTypeScreen, nil
@ -1088,10 +1088,10 @@ func (s *ClientSession) processAsyncMessage(message *AsyncMessage) {
s.mu.Lock()
defer s.mu.Unlock()
if !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_MEDIA) {
if !s.hasPermissionLocked(api.PERMISSION_MAY_PUBLISH_MEDIA) {
if publisher, found := s.publishers[StreamTypeVideo]; found {
if (publisher.HasMedia(MediaTypeAudio) && !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_AUDIO)) ||
(publisher.HasMedia(MediaTypeVideo) && !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_VIDEO)) {
if (publisher.HasMedia(MediaTypeAudio) && !s.hasPermissionLocked(api.PERMISSION_MAY_PUBLISH_AUDIO)) ||
(publisher.HasMedia(MediaTypeVideo) && !s.hasPermissionLocked(api.PERMISSION_MAY_PUBLISH_VIDEO)) {
delete(s.publishers, StreamTypeVideo)
s.logger.Printf("Session %s is no longer allowed to publish media, closing publisher %s", s.PublicId(), publisher.Id())
go func() {
@ -1101,7 +1101,7 @@ func (s *ClientSession) processAsyncMessage(message *AsyncMessage) {
}
}
}
if !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_SCREEN) {
if !s.hasPermissionLocked(api.PERMISSION_MAY_PUBLISH_SCREEN) {
if publisher, found := s.publishers[StreamTypeScreen]; found {
delete(s.publishers, StreamTypeScreen)
s.logger.Printf("Session %s is no longer allowed to publish screen, closing publisher %s", s.PublicId(), publisher.Id())
@ -1317,7 +1317,7 @@ func (s *ClientSession) filterMessage(message *api.ServerMessage) *api.ServerMes
}
}
if s.HasPermission(PERMISSION_HIDE_DISPLAYNAMES) {
if s.HasPermission(api.PERMISSION_HIDE_DISPLAYNAMES) {
if copied {
message.Event.Join = filterDisplayNames(message.Event.Join)
} else {
@ -1361,7 +1361,7 @@ func (s *ClientSession) filterMessage(message *api.ServerMessage) *api.ServerMes
update = true
}
if len(data.Chat.Comment) > 0 && s.HasPermission(PERMISSION_HIDE_DISPLAYNAMES) {
if len(data.Chat.Comment) > 0 && s.HasPermission(api.PERMISSION_HIDE_DISPLAYNAMES) {
var comment api.ChatComment
if err := json.Unmarshal(data.Chat.Comment, &comment); err != nil {
return message
@ -1398,7 +1398,7 @@ func (s *ClientSession) filterMessage(message *api.ServerMessage) *api.ServerMes
}
}
case "message":
if message.Message != nil && len(message.Message.Data) > 0 && s.HasPermission(PERMISSION_HIDE_DISPLAYNAMES) {
if message.Message != nil && len(message.Message.Data) > 0 && s.HasPermission(api.PERMISSION_HIDE_DISPLAYNAMES) {
var data api.MessageServerMessageData
if err := json.Unmarshal(message.Message.Data, &data); err != nil {
return message

View file

@ -34,6 +34,7 @@ import (
"github.com/strukturag/nextcloud-spreed-signaling/api"
"github.com/strukturag/nextcloud-spreed-signaling/mock"
"github.com/strukturag/nextcloud-spreed-signaling/talk"
)
func TestBandwidth_Client(t *testing.T) {
@ -219,9 +220,9 @@ func TestFeatureChatRelay(t *testing.T) {
// Simulate request from the backend.
room.processAsyncMessage(&AsyncMessage{
Type: "room",
Room: &BackendServerRoomRequest{
Room: &talk.BackendServerRoomRequest{
Type: "message",
Message: &BackendRoomMessageRequest{
Message: &talk.BackendRoomMessageRequest{
Data: data,
},
},
@ -416,9 +417,9 @@ func TestFeatureChatRelayFederation(t *testing.T) {
// Simulate request from the backend.
room.processAsyncMessage(&AsyncMessage{
Type: "room",
Room: &BackendServerRoomRequest{
Room: &talk.BackendServerRoomRequest{
Type: "message",
Message: &BackendRoomMessageRequest{
Message: &talk.BackendRoomMessageRequest{
Data: data,
},
},
@ -494,7 +495,7 @@ func TestPermissionHideDisplayNames(t *testing.T) {
require.NotNil(session, "Session %s does not exist", hello.Hello.SessionId)
// Client may not receive display names.
session.SetPermissions([]Permission{PERMISSION_HIDE_DISPLAYNAMES})
session.SetPermissions([]api.Permission{api.PERMISSION_HIDE_DISPLAYNAMES})
}
chatComment := api.StringMap{
@ -516,9 +517,9 @@ func TestPermissionHideDisplayNames(t *testing.T) {
// Simulate request from the backend.
room.processAsyncMessage(&AsyncMessage{
Type: "room",
Room: &BackendServerRoomRequest{
Room: &talk.BackendServerRoomRequest{
Type: "message",
Message: &BackendRoomMessageRequest{
Message: &talk.BackendRoomMessageRequest{
Data: data,
},
},

View file

@ -482,7 +482,7 @@ func (c *FederationClient) sendHello(auth *api.FederationAuthParams) error {
// +checklocks:c.helloMu
func (c *FederationClient) sendHelloLocked(auth *api.FederationAuthParams) error {
c.helloMsgId = newRandomString(8)
c.helloMsgId = internal.RandomString(8)
authData, err := json.Marshal(auth)
if err != nil {

View file

@ -50,6 +50,7 @@ import (
"github.com/strukturag/nextcloud-spreed-signaling/geoip"
"github.com/strukturag/nextcloud-spreed-signaling/internal"
"github.com/strukturag/nextcloud-spreed-signaling/log"
"github.com/strukturag/nextcloud-spreed-signaling/talk"
)
const (
@ -504,7 +505,7 @@ func NewGrpcClients(ctx context.Context, config *goconf.ConfigFile, etcdClient e
return result, nil
}
func (c *GrpcClients) GetServerInfoGrpc() (result []BackendServerInfoGrpc) {
func (c *GrpcClients) GetServerInfoGrpc() (result []talk.BackendServerInfoGrpc) {
c.mu.RLock()
defer c.mu.RUnlock()
@ -513,7 +514,7 @@ func (c *GrpcClients) GetServerInfoGrpc() (result []BackendServerInfoGrpc) {
continue
}
grpc := BackendServerInfoGrpc{
grpc := talk.BackendServerInfoGrpc{
Target: client.rawTarget,
}
if len(client.ip) > 0 {

View file

@ -40,6 +40,7 @@ import (
"github.com/strukturag/nextcloud-spreed-signaling/api"
"github.com/strukturag/nextcloud-spreed-signaling/config"
"github.com/strukturag/nextcloud-spreed-signaling/internal"
"github.com/strukturag/nextcloud-spreed-signaling/log"
"github.com/strukturag/nextcloud-spreed-signaling/talk"
)
@ -55,10 +56,10 @@ func init() {
hostname, err := os.Hostname()
if err != nil {
hostname = newRandomString(8)
hostname = internal.RandomString(8)
}
md := sha256.New()
fmt.Fprintf(md, "%s-%s-%d", newRandomString(32), hostname, os.Getpid())
fmt.Fprintf(md, "%s-%s-%d", internal.RandomString(32), hostname, os.Getpid())
GrpcServerId = hex.EncodeToString(md.Sum(nil))
}

114
hub.go
View file

@ -160,10 +160,10 @@ type Hub struct {
shutdown *internal.Closer
shutdownScheduled atomic.Bool
roomUpdated chan *BackendServerRoomRequest
roomDeleted chan *BackendServerRoomRequest
roomInCall chan *BackendServerRoomRequest
roomParticipants chan *BackendServerRoomRequest
roomUpdated chan *talk.BackendServerRoomRequest
roomDeleted chan *talk.BackendServerRoomRequest
roomInCall chan *talk.BackendServerRoomRequest
roomParticipants chan *talk.BackendServerRoomRequest
mu sync.RWMutex
ru sync.RWMutex
@ -379,10 +379,10 @@ func NewHub(ctx context.Context, cfg *goconf.ConfigFile, events AsyncEvents, rpc
closer: internal.NewCloser(),
shutdown: internal.NewCloser(),
roomUpdated: make(chan *BackendServerRoomRequest),
roomDeleted: make(chan *BackendServerRoomRequest),
roomInCall: make(chan *BackendServerRoomRequest),
roomParticipants: make(chan *BackendServerRoomRequest),
roomUpdated: make(chan *talk.BackendServerRoomRequest),
roomDeleted: make(chan *talk.BackendServerRoomRequest),
roomInCall: make(chan *talk.BackendServerRoomRequest),
roomParticipants: make(chan *talk.BackendServerRoomRequest),
clients: make(map[uint64]HandlerClient),
sessions: make(map[uint64]Session),
@ -952,7 +952,7 @@ func (h *Hub) newSessionIdData(backend *talk.Backend) *SessionIdData {
return sessionIdData
}
func (h *Hub) processRegister(c HandlerClient, message *api.ClientMessage, backend *talk.Backend, auth *BackendClientResponse) {
func (h *Hub) processRegister(c HandlerClient, message *api.ClientMessage, backend *talk.Backend, auth *talk.BackendClientResponse) {
if !c.IsConnected() {
// Client disconnected while waiting for "hello" response.
return
@ -1362,7 +1362,7 @@ func (h *Hub) processHello(client HandlerClient, message *api.ClientMessage) {
}
}
func (h *Hub) processHelloV1(ctx context.Context, client HandlerClient, message *api.ClientMessage) (*talk.Backend, *BackendClientResponse, error) {
func (h *Hub) processHelloV1(ctx context.Context, client HandlerClient, message *api.ClientMessage) (*talk.Backend, *talk.BackendClientResponse, error) {
url := message.Hello.Auth.ParsedUrl
backend := h.backend.GetBackend(url)
if backend == nil {
@ -1375,8 +1375,8 @@ func (h *Hub) processHelloV1(ctx context.Context, client HandlerClient, message
ctx, cancel := context.WithTimeout(ctx, h.backendTimeout)
defer cancel()
var auth BackendClientResponse
request := NewBackendClientAuthRequest(message.Hello.Auth.Params)
var auth talk.BackendClientResponse
request := talk.NewBackendClientAuthRequest(message.Hello.Auth.Params)
if err := h.backend.PerformJSONRequest(ctx, url, request, &auth); err != nil {
return nil, nil, err
}
@ -1386,7 +1386,7 @@ func (h *Hub) processHelloV1(ctx context.Context, client HandlerClient, message
return backend, &auth, nil
}
func (h *Hub) processHelloV2(ctx context.Context, client HandlerClient, message *api.ClientMessage) (*talk.Backend, *BackendClientResponse, error) {
func (h *Hub) processHelloV2(ctx context.Context, client HandlerClient, message *api.ClientMessage) (*talk.Backend, *talk.BackendClientResponse, error) {
url := message.Hello.Auth.ParsedUrl
backend := h.backend.GetBackend(url)
if backend == nil {
@ -1452,13 +1452,13 @@ func (h *Hub) processHelloV2(ctx context.Context, client HandlerClient, message
backendCtx, cancel := context.WithTimeout(ctx, h.backendTimeout)
defer cancel()
keyData, cached, found := h.backend.capabilities.GetStringConfig(backendCtx, url, ConfigGroupSignaling, ConfigKeyHelloV2TokenKey)
keyData, cached, found := h.backend.capabilities.GetStringConfig(backendCtx, url, talk.ConfigGroupSignaling, talk.ConfigKeyHelloV2TokenKey)
if !found {
if cached {
// The Nextcloud instance might just have enabled JWT but we probably use
// the cached capabilities without the public key. Make sure to re-fetch.
h.backend.capabilities.InvalidateCapabilities(url)
keyData, _, found = h.backend.capabilities.GetStringConfig(backendCtx, url, ConfigGroupSignaling, ConfigKeyHelloV2TokenKey)
keyData, _, found = h.backend.capabilities.GetStringConfig(backendCtx, url, talk.ConfigGroupSignaling, talk.ConfigKeyHelloV2TokenKey)
}
if !found {
return nil, errors.New("no key found for issuer")
@ -1528,9 +1528,9 @@ func (h *Hub) processHelloV2(ctx context.Context, client HandlerClient, message
return nil, nil, InvalidToken
}
auth := &BackendClientResponse{
auth := &talk.BackendClientResponse{
Type: "auth",
Auth: &BackendClientAuthResponse{
Auth: &talk.BackendClientAuthResponse{
Version: message.Hello.Version,
UserId: subject,
User: authTokenClaims.GetUserData(),
@ -1543,7 +1543,7 @@ func (h *Hub) processHelloClient(client HandlerClient, message *api.ClientMessag
// Make sure the client must send another "hello" in case of errors.
defer h.startExpectHello(client)
var authFunc func(context.Context, HandlerClient, *api.ClientMessage) (*talk.Backend, *BackendClientResponse, error)
var authFunc func(context.Context, HandlerClient, *api.ClientMessage) (*talk.Backend, *talk.BackendClientResponse, error)
switch message.Hello.Version {
case api.HelloVersionV1:
// Auth information contains a ticket that must be validated against the
@ -1606,9 +1606,9 @@ func (h *Hub) processHelloInternal(client HandlerClient, message *api.ClientMess
return
}
auth := &BackendClientResponse{
auth := &talk.BackendClientResponse{
Type: "auth",
Auth: &BackendClientAuthResponse{},
Auth: &talk.BackendClientAuthResponse{},
}
h.processRegister(client, message, backend, auth)
}
@ -1846,12 +1846,12 @@ func (h *Hub) processRoom(sess Session, message *api.ClientMessage) {
return
}
var room BackendClientResponse
var room talk.BackendClientResponse
if session.ClientType() == api.HelloClientTypeInternal {
// Internal clients can join any room.
room = BackendClientResponse{
room = talk.BackendClientResponse{
Type: "room",
Room: &BackendClientRoomResponse{
Room: &talk.BackendClientRoomResponse{
RoomId: roomId,
},
}
@ -1866,7 +1866,7 @@ func (h *Hub) processRoom(sess Session, message *api.ClientMessage) {
h.logger.Printf("User did not send a room session id, assuming session %s", session.PublicId())
sessionId = api.RoomSessionId(session.PublicId())
}
request := NewBackendClientRoomRequest(roomId, session.UserId(), sessionId)
request := talk.NewBackendClientRoomRequest(roomId, session.UserId(), sessionId)
request.Room.UpdateFromSession(session)
if err := h.backend.PerformJSONRequest(ctx, session.ParsedBackendOcsUrl(), request, &room); err != nil {
session.SendMessage(message.NewWrappedErrorServerMessage(err))
@ -1897,7 +1897,7 @@ func (h *Hub) publishFederatedSessions() (int, *sync.WaitGroup) {
return 0, &wg
}
rooms := make(map[string]map[string][]BackendPingEntry)
rooms := make(map[string]map[string][]talk.BackendPingEntry)
urls := make(map[string]*url.URL)
for session := range h.federatedSessions {
u := session.BackendUrl()
@ -1921,7 +1921,7 @@ func (h *Hub) publishFederatedSessions() (int, *sync.WaitGroup) {
roomId := federation.RoomId()
entries, found := rooms[roomId]
if !found {
entries = make(map[string][]BackendPingEntry)
entries = make(map[string][]talk.BackendPingEntry)
rooms[roomId] = entries
}
@ -1935,7 +1935,7 @@ func (h *Hub) publishFederatedSessions() (int, *sync.WaitGroup) {
urls[u] = p
}
entries[u] = append(e, BackendPingEntry{
entries[u] = append(e, talk.BackendPingEntry{
SessionId: sid,
UserId: uid,
})
@ -1950,7 +1950,7 @@ func (h *Hub) publishFederatedSessions() (int, *sync.WaitGroup) {
for u, e := range entries {
wg.Add(1)
count += len(e)
go func(roomId string, url *url.URL, entries []BackendPingEntry) {
go func(roomId string, url *url.URL, entries []talk.BackendPingEntry) {
defer wg.Done()
sendCtx, cancel := context.WithTimeout(ctx, h.backendTimeout)
defer cancel()
@ -2004,7 +2004,7 @@ func (h *Hub) createRoomLocked(id string, properties json.RawMessage, backend *t
return room, nil
}
func (h *Hub) processJoinRoom(session *ClientSession, message *api.ClientMessage, room *BackendClientResponse) {
func (h *Hub) processJoinRoom(session *ClientSession, message *api.ClientMessage, room *talk.BackendClientResponse) {
if room.Type == "error" {
session.SendMessage(message.NewErrorServerMessage(room.Error))
return
@ -2327,7 +2327,7 @@ func isAllowedToControl(session Session) bool {
return true
}
if session.HasPermission(PERMISSION_MAY_CONTROL) {
if session.HasPermission(api.PERMISSION_MAY_CONTROL) {
// Moderator clients are allowed to send any control message.
return true
}
@ -2498,12 +2498,12 @@ func (h *Hub) processInternalMsg(sess Session, message *api.ClientMessage) {
}
if options := msg.Options; options != nil && options.ActorId != "" && options.ActorType != "" {
request := NewBackendClientRoomRequest(room.Id(), msg.UserId, api.RoomSessionId(publicSessionId))
request := talk.NewBackendClientRoomRequest(room.Id(), msg.UserId, api.RoomSessionId(publicSessionId))
request.Room.ActorId = options.ActorId
request.Room.ActorType = options.ActorType
request.Room.InCall = sess.GetInCall()
var response BackendClientResponse
var response talk.BackendClientResponse
if err := h.backend.PerformJSONRequest(ctx, session.ParsedBackendOcsUrl(), request, &response); err != nil {
sess.Close()
h.logger.Printf("Could not join virtual session %s at backend %s: %s", virtualSessionId, session.BackendUrl(), err)
@ -2520,8 +2520,8 @@ func (h *Hub) processInternalMsg(sess Session, message *api.ClientMessage) {
return
}
} else {
request := NewBackendClientSessionRequest(room.Id(), "add", publicSessionId, msg)
var response BackendClientSessionResponse
request := talk.NewBackendClientSessionRequest(room.Id(), "add", publicSessionId, msg)
var response talk.BackendClientSessionResponse
if err := h.backend.PerformJSONRequest(ctx, session.ParsedBackendOcsUrl(), request, &response); err != nil {
sess.Close()
h.logger.Printf("Could not add virtual session %s at backend %s: %s", virtualSessionId, session.BackendUrl(), err)
@ -2620,10 +2620,10 @@ func (h *Hub) processInternalMsg(sess Session, message *api.ClientMessage) {
if msg.Dialout.Type == "status" {
asyncMessage := &AsyncMessage{
Type: "room",
Room: &BackendServerRoomRequest{
Room: &talk.BackendServerRoomRequest{
Type: "transient",
Transient: &BackendRoomTransientRequest{
Action: TransientActionSet,
Transient: &talk.BackendRoomTransientRequest{
Action: talk.TransientActionSet,
Key: "callstatus_" + msg.Dialout.Status.CallId,
Value: msg.Dialout.Status,
},
@ -2658,7 +2658,7 @@ func isAllowedToUpdateTransientData(session Session) bool {
return true
}
if session.HasPermission(PERMISSION_TRANSIENT_DATA) {
if session.HasPermission(api.PERMISSION_TRANSIENT_DATA) {
return true
}
@ -2957,13 +2957,21 @@ func (h *Hub) processByeMsg(client HandlerClient, message *api.ClientMessage) {
}
}
func (h *Hub) processRoomUpdated(message *BackendServerRoomRequest) {
room := message.room
func (h *Hub) processRoomUpdated(message *talk.BackendServerRoomRequest) {
room := h.GetRoomForBackend(message.RoomId, message.Backend)
if room == nil {
return
}
room.UpdateProperties(message.Update.Properties)
}
func (h *Hub) processRoomDeleted(message *BackendServerRoomRequest) {
room := message.room
func (h *Hub) processRoomDeleted(message *talk.BackendServerRoomRequest) {
room := h.GetRoomForBackend(message.RoomId, message.Backend)
if room == nil {
return
}
sessions := room.Close()
for _, session := range sessions {
// The session is no longer in the room
@ -2977,8 +2985,12 @@ func (h *Hub) processRoomDeleted(message *BackendServerRoomRequest) {
}
}
func (h *Hub) processRoomInCallChanged(message *BackendServerRoomRequest) {
room := message.room
func (h *Hub) processRoomInCallChanged(message *talk.BackendServerRoomRequest) {
room := h.GetRoomForBackend(message.RoomId, message.Backend)
if room == nil {
return
}
if message.InCall.All {
var flags int
if err := json.Unmarshal(message.InCall.InCall, &flags); err != nil {
@ -2999,8 +3011,12 @@ func (h *Hub) processRoomInCallChanged(message *BackendServerRoomRequest) {
}
}
func (h *Hub) processRoomParticipants(message *BackendServerRoomRequest) {
room := message.room
func (h *Hub) processRoomParticipants(message *talk.BackendServerRoomRequest) {
room := h.GetRoomForBackend(message.RoomId, message.Backend)
if room == nil {
return
}
room.PublishUsersChanged(message.Participants.Changed, message.Participants.Users)
}
@ -3020,12 +3036,12 @@ func (h *Hub) GetStats() api.StringMap {
return result
}
func (h *Hub) GetServerInfoDialout() (result []BackendServerInfoDialout) {
func (h *Hub) GetServerInfoDialout() (result []talk.BackendServerInfoDialout) {
h.mu.RLock()
defer h.mu.RUnlock()
for session := range h.dialoutSessions {
dialout := BackendServerInfoDialout{
dialout := talk.BackendServerInfoDialout{
SessionId: session.PublicId(),
}
if client := session.GetClient(); client != nil && client.IsConnected() {
@ -3047,7 +3063,7 @@ func (h *Hub) GetServerInfoDialout() (result []BackendServerInfoDialout) {
result = append(result, dialout)
}
slices.SortFunc(result, func(a, b BackendServerInfoDialout) int {
slices.SortFunc(result, func(a, b talk.BackendServerInfoDialout) int {
return strings.Compare(string(a.SessionId), string(b.SessionId))
})
return

View file

@ -348,23 +348,23 @@ func WaitForHub(ctx context.Context, t *testing.T, h *Hub) {
}
}
func validateBackendChecksum(t *testing.T, f func(http.ResponseWriter, *http.Request, *BackendClientRequest) *BackendClientResponse) func(http.ResponseWriter, *http.Request) {
func validateBackendChecksum(t *testing.T, f func(http.ResponseWriter, *http.Request, *talk.BackendClientRequest) *talk.BackendClientResponse) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
assert := assert.New(t)
body, err := io.ReadAll(r.Body)
assert.NoError(err)
rnd := r.Header.Get(HeaderBackendSignalingRandom)
checksum := r.Header.Get(HeaderBackendSignalingChecksum)
rnd := r.Header.Get(talk.HeaderBackendSignalingRandom)
checksum := r.Header.Get(talk.HeaderBackendSignalingChecksum)
if rnd == "" || checksum == "" {
assert.Fail("No checksum headers found", "request to %s", r.URL)
}
if verify := CalculateBackendChecksum(rnd, body, testBackendSecret); verify != checksum {
if verify := talk.CalculateBackendChecksum(rnd, body, testBackendSecret); verify != checksum {
assert.Fail("Backend checksum verification failed", "request to %s", r.URL)
}
var request BackendClientRequest
var request talk.BackendClientRequest
assert.NoError(json.Unmarshal(body, &request))
response := f(w, r, &request)
@ -396,7 +396,7 @@ func validateBackendChecksum(t *testing.T, f func(http.ResponseWriter, *http.Req
}
}
func processAuthRequest(t *testing.T, w http.ResponseWriter, r *http.Request, request *BackendClientRequest) *BackendClientResponse {
func processAuthRequest(t *testing.T, w http.ResponseWriter, r *http.Request, request *talk.BackendClientRequest) *talk.BackendClientResponse {
require := require.New(t)
if request.Type != "auth" || request.Auth == nil {
require.Fail("Expected an auth backend request", "received %+v", request)
@ -413,10 +413,10 @@ func processAuthRequest(t *testing.T, w http.ResponseWriter, r *http.Request, re
params.UserId = ""
}
response := &BackendClientResponse{
response := &talk.BackendClientResponse{
Type: "auth",
Auth: &BackendClientAuthResponse{
Version: BackendVersion,
Auth: &talk.BackendClientAuthResponse{
Version: talk.BackendVersion,
UserId: params.UserId,
},
}
@ -428,7 +428,7 @@ func processAuthRequest(t *testing.T, w http.ResponseWriter, r *http.Request, re
return response
}
func processRoomRequest(t *testing.T, w http.ResponseWriter, r *http.Request, request *BackendClientRequest) *BackendClientResponse {
func processRoomRequest(t *testing.T, w http.ResponseWriter, r *http.Request, request *talk.BackendClientRequest) *talk.BackendClientResponse {
require := require.New(t)
assert := assert.New(t)
if request.Type != "room" || request.Room == nil {
@ -444,7 +444,7 @@ func processRoomRequest(t *testing.T, w http.ResponseWriter, r *http.Request, re
assert.Fail("Should not receive \"leave\" event for first user", "received %+v", request.Room)
}
case "test-invalid-room":
response := &BackendClientResponse{
response := &talk.BackendClientResponse{
Type: "error",
Error: &api.Error{
Code: "no_such_room",
@ -471,10 +471,10 @@ func processRoomRequest(t *testing.T, w http.ResponseWriter, r *http.Request, re
}
// Allow joining any room.
response := &BackendClientResponse{
response := &talk.BackendClientResponse{
Type: "room",
Room: &BackendClientRoomResponse{
Version: BackendVersion,
Room: &talk.BackendClientRoomResponse{
Version: talk.BackendVersion,
RoomId: request.Room.RoomId,
Properties: testRoomProperties,
},
@ -487,7 +487,7 @@ func processRoomRequest(t *testing.T, w http.ResponseWriter, r *http.Request, re
tmp, _ := json.Marshal(data)
response.Room.Session = tmp
case "test-room-initial-permissions":
permissions := []Permission{PERMISSION_MAY_PUBLISH_AUDIO}
permissions := []api.Permission{api.PERMISSION_MAY_PUBLISH_AUDIO}
response.Room.Permissions = &permissions
}
return response
@ -497,15 +497,15 @@ var (
sessionRequestHander struct {
sync.Mutex
// +checklocks:Mutex
handlers map[*testing.T]func(*BackendClientSessionRequest)
handlers map[*testing.T]func(*talk.BackendClientSessionRequest)
}
)
func setSessionRequestHandler(t *testing.T, f func(*BackendClientSessionRequest)) {
func setSessionRequestHandler(t *testing.T, f func(*talk.BackendClientSessionRequest)) {
sessionRequestHander.Lock()
defer sessionRequestHander.Unlock()
if sessionRequestHander.handlers == nil {
sessionRequestHander.handlers = make(map[*testing.T]func(*BackendClientSessionRequest))
sessionRequestHander.handlers = make(map[*testing.T]func(*talk.BackendClientSessionRequest))
}
if _, found := sessionRequestHander.handlers[t]; !found {
t.Cleanup(func() {
@ -525,7 +525,7 @@ func clearSessionRequestHandler(t *testing.T) { // nolint
delete(sessionRequestHander.handlers, t)
}
func processSessionRequest(t *testing.T, w http.ResponseWriter, r *http.Request, request *BackendClientRequest) *BackendClientResponse {
func processSessionRequest(t *testing.T, w http.ResponseWriter, r *http.Request, request *talk.BackendClientRequest) *talk.BackendClientResponse {
if request.Type != "session" || request.Session == nil {
require.Fail(t, "Expected an session backend request", "received %+v", request)
}
@ -536,10 +536,10 @@ func processSessionRequest(t *testing.T, w http.ResponseWriter, r *http.Request,
f(request.Session)
}
response := &BackendClientResponse{
response := &talk.BackendClientResponse{
Type: "session",
Session: &BackendClientSessionResponse{
Version: BackendVersion,
Session: &talk.BackendClientSessionResponse{
Version: talk.BackendVersion,
RoomId: request.Session.RoomId,
},
}
@ -547,10 +547,10 @@ func processSessionRequest(t *testing.T, w http.ResponseWriter, r *http.Request,
}
var (
pingRequests internal.TestStorage[[]*BackendClientRequest]
pingRequests internal.TestStorage[[]*talk.BackendClientRequest]
)
func getPingRequests(t *testing.T) []*BackendClientRequest {
func getPingRequests(t *testing.T) []*talk.BackendClientRequest {
entries, _ := pingRequests.Get(t)
return entries
}
@ -559,12 +559,12 @@ func clearPingRequests(t *testing.T) {
pingRequests.Del(t)
}
func storePingRequest(t *testing.T, request *BackendClientRequest) {
func storePingRequest(t *testing.T, request *talk.BackendClientRequest) {
entries, _ := pingRequests.Get(t)
pingRequests.Set(t, append(entries, request))
}
func processPingRequest(t *testing.T, w http.ResponseWriter, r *http.Request, request *BackendClientRequest) *BackendClientResponse {
func processPingRequest(t *testing.T, w http.ResponseWriter, r *http.Request, request *talk.BackendClientRequest) *talk.BackendClientResponse {
if request.Type != "ping" || request.Ping == nil {
require.Fail(t, "Expected an ping backend request", "received %+v", request)
}
@ -577,10 +577,10 @@ func processPingRequest(t *testing.T, w http.ResponseWriter, r *http.Request, re
storePingRequest(t, request)
response := &BackendClientResponse{
response := &talk.BackendClientResponse{
Type: "ping",
Ping: &BackendClientRingResponse{
Version: BackendVersion,
Ping: &talk.BackendClientRingResponse{
Version: talk.BackendVersion,
RoomId: request.Ping.RoomId,
},
}
@ -706,7 +706,7 @@ var (
)
func registerBackendHandlerUrl(t *testing.T, router *mux.Router, url string) {
handleFunc := validateBackendChecksum(t, func(w http.ResponseWriter, r *http.Request, request *BackendClientRequest) *BackendClientResponse {
handleFunc := validateBackendChecksum(t, func(w http.ResponseWriter, r *http.Request, request *talk.BackendClientRequest) *talk.BackendClientResponse {
assert.Regexp(t, "/ocs/v2\\.php/apps/spreed/api/v[\\d]/signaling/backend$", r.URL.Path, "invalid url for backend request %+v", request)
switch request.Type {
@ -748,7 +748,7 @@ func registerBackendHandlerUrl(t *testing.T, router *mux.Router, url string) {
"signaling": signaling,
}
if strings.Contains(t.Name(), "MultiRoom") {
signaling[ConfigKeySessionPingLimit] = 2
signaling[talk.ConfigKeySessionPingLimit] = 2
}
skipV2, _ := skipV2Capabilities.Get(t)
if (strings.Contains(t.Name(), "V2") && !skipV2) || strings.Contains(t.Name(), "Federation") {
@ -771,9 +771,9 @@ func registerBackendHandlerUrl(t *testing.T, router *mux.Router, url string) {
if strings.Contains(t.Name(), "Ed25519_Nextcloud") {
// Simulate Nextcloud which returns the Ed25519 key as base64-encoded data.
encoded := base64.StdEncoding.EncodeToString(key.(ed25519.PublicKey))
signaling[ConfigKeyHelloV2TokenKey] = encoded
signaling[talk.ConfigKeyHelloV2TokenKey] = encoded
} else {
signaling[ConfigKeyHelloV2TokenKey] = string(public)
signaling[talk.ConfigKeyHelloV2TokenKey] = string(public)
}
}
spreedCapa, err := json.Marshal(api.StringMap{
@ -2113,15 +2113,15 @@ func TestClientControlMissingPermissions(t *testing.T) {
require.NotNil(session2, "Session %s does not exist", hello2.Hello.SessionId)
// Client 1 may not send control messages (will be ignored).
session1.SetPermissions([]Permission{
PERMISSION_MAY_PUBLISH_AUDIO,
PERMISSION_MAY_PUBLISH_VIDEO,
session1.SetPermissions([]api.Permission{
api.PERMISSION_MAY_PUBLISH_AUDIO,
api.PERMISSION_MAY_PUBLISH_VIDEO,
})
// Client 2 may send control messages.
session2.SetPermissions([]Permission{
PERMISSION_MAY_PUBLISH_AUDIO,
PERMISSION_MAY_PUBLISH_VIDEO,
PERMISSION_MAY_CONTROL,
session2.SetPermissions([]api.Permission{
api.PERMISSION_MAY_PUBLISH_AUDIO,
api.PERMISSION_MAY_PUBLISH_VIDEO,
api.PERMISSION_MAY_CONTROL,
})
recipient1 := api.MessageClientMessageRecipient{
@ -2972,7 +2972,7 @@ func TestJoinDisplaynamesPermission(t *testing.T) {
require.NotNil(session2, "Session %s does not exist", hello2.Hello.SessionId)
// Client 2 may not receive display names.
session2.SetPermissions([]Permission{PERMISSION_HIDE_DISPLAYNAMES})
session2.SetPermissions([]api.Permission{api.PERMISSION_HIDE_DISPLAYNAMES})
// Join room by id (first client).
roomId := "test-room"
@ -3024,8 +3024,8 @@ func TestInitialRoomPermissions(t *testing.T) {
session := hub.GetSessionByPublicId(hello.Hello.SessionId).(*ClientSession)
require.NotNil(session, "Session %s does not exist", hello.Hello.SessionId)
assert.True(session.HasPermission(PERMISSION_MAY_PUBLISH_AUDIO), "Session %s should have %s, got %+v", session.PublicId(), PERMISSION_MAY_PUBLISH_AUDIO, session.GetPermissions())
assert.False(session.HasPermission(PERMISSION_MAY_PUBLISH_VIDEO), "Session %s should not have %s, got %+v", session.PublicId(), PERMISSION_MAY_PUBLISH_VIDEO, session.GetPermissions())
assert.True(session.HasPermission(api.PERMISSION_MAY_PUBLISH_AUDIO), "Session %s should have %s, got %+v", session.PublicId(), api.PERMISSION_MAY_PUBLISH_AUDIO, session.GetPermissions())
assert.False(session.HasPermission(api.PERMISSION_MAY_PUBLISH_VIDEO), "Session %s should not have %s, got %+v", session.PublicId(), api.PERMISSION_MAY_PUBLISH_VIDEO, session.GetPermissions())
}
func TestJoinRoomSwitchClient(t *testing.T) {
@ -3382,18 +3382,18 @@ func TestCombineChatRefreshWhileDisconnected(t *testing.T) {
// Simulate requests from the backend.
room.processAsyncMessage(&AsyncMessage{
Type: "room",
Room: &BackendServerRoomRequest{
Room: &talk.BackendServerRoomRequest{
Type: "message",
Message: &BackendRoomMessageRequest{
Message: &talk.BackendRoomMessageRequest{
Data: json.RawMessage(chat_refresh),
},
},
})
room.processAsyncMessage(&AsyncMessage{
Type: "room",
Room: &BackendServerRoomRequest{
Room: &talk.BackendServerRoomRequest{
Type: "message",
Message: &BackendRoomMessageRequest{
Message: &talk.BackendRoomMessageRequest{
Data: json.RawMessage(chat_refresh),
},
},
@ -3628,9 +3628,9 @@ func TestClientSendOfferPermissions(t *testing.T) {
require.NotNil(session2, "Session %s does not exist", hello2.Hello.SessionId)
// Client 1 is the moderator
session1.SetPermissions([]Permission{PERMISSION_MAY_PUBLISH_MEDIA, PERMISSION_MAY_PUBLISH_SCREEN})
session1.SetPermissions([]api.Permission{api.PERMISSION_MAY_PUBLISH_MEDIA, api.PERMISSION_MAY_PUBLISH_SCREEN})
// Client 2 is a guest participant.
session2.SetPermissions([]Permission{})
session2.SetPermissions([]api.Permission{})
// Client 2 may not send an offer (he doesn't have the necessary permissions).
require.NoError(client2.SendMessage(api.MessageClientMessageRecipient{
@ -3706,7 +3706,7 @@ func TestClientSendOfferPermissionsAudioOnly(t *testing.T) {
require.NotNil(session, "Session %s does not exist", hello.Hello.SessionId)
// Client is allowed to send audio only.
session.SetPermissions([]Permission{PERMISSION_MAY_PUBLISH_AUDIO})
session.SetPermissions([]api.Permission{api.PERMISSION_MAY_PUBLISH_AUDIO})
// Client may not send an offer with audio and video.
require.NoError(client.SendMessage(api.MessageClientMessageRecipient{
@ -3768,7 +3768,7 @@ func TestClientSendOfferPermissionsAudioVideo(t *testing.T) {
require.NotNil(session, "Session %s does not exist", hello.Hello.SessionId)
// Client is allowed to send audio and video.
session.SetPermissions([]Permission{PERMISSION_MAY_PUBLISH_AUDIO, PERMISSION_MAY_PUBLISH_VIDEO})
session.SetPermissions([]api.Permission{api.PERMISSION_MAY_PUBLISH_AUDIO, api.PERMISSION_MAY_PUBLISH_VIDEO})
require.NoError(client.SendMessage(api.MessageClientMessageRecipient{
Type: "session",
@ -3785,19 +3785,19 @@ func TestClientSendOfferPermissionsAudioVideo(t *testing.T) {
require.True(client.RunUntilAnswer(ctx, mock.MockSdpAnswerAudioAndVideo))
// Client is no longer allowed to send video, this will stop the publisher.
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "participants",
Participants: &BackendRoomParticipantsRequest{
Participants: &talk.BackendRoomParticipantsRequest{
Changed: []api.StringMap{
{
"sessionId": fmt.Sprintf("%s-%s", roomId, hello.Hello.SessionId),
"permissions": []Permission{PERMISSION_MAY_PUBLISH_AUDIO},
"permissions": []api.Permission{api.PERMISSION_MAY_PUBLISH_AUDIO},
},
},
Users: []api.StringMap{
{
"sessionId": fmt.Sprintf("%s-%s", roomId, hello.Hello.SessionId),
"permissions": []Permission{PERMISSION_MAY_PUBLISH_AUDIO},
"permissions": []api.Permission{api.PERMISSION_MAY_PUBLISH_AUDIO},
},
},
},
@ -3864,7 +3864,7 @@ func TestClientSendOfferPermissionsAudioVideoMedia(t *testing.T) {
require.NotNil(session, "Session %s does not exist", hello.Hello.SessionId)
// Client is allowed to send audio and video.
session.SetPermissions([]Permission{PERMISSION_MAY_PUBLISH_MEDIA})
session.SetPermissions([]api.Permission{api.PERMISSION_MAY_PUBLISH_MEDIA})
// Client may send an offer (audio and video).
require.NoError(client.SendMessage(api.MessageClientMessageRecipient{
@ -3882,19 +3882,19 @@ func TestClientSendOfferPermissionsAudioVideoMedia(t *testing.T) {
require.True(client.RunUntilAnswer(ctx, mock.MockSdpAnswerAudioAndVideo))
// Client is no longer allowed to send video, this will stop the publisher.
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "participants",
Participants: &BackendRoomParticipantsRequest{
Participants: &talk.BackendRoomParticipantsRequest{
Changed: []api.StringMap{
{
"sessionId": fmt.Sprintf("%s-%s", roomId, hello.Hello.SessionId),
"permissions": []Permission{PERMISSION_MAY_PUBLISH_MEDIA, PERMISSION_MAY_CONTROL},
"permissions": []api.Permission{api.PERMISSION_MAY_PUBLISH_MEDIA, api.PERMISSION_MAY_CONTROL},
},
},
Users: []api.StringMap{
{
"sessionId": fmt.Sprintf("%s-%s", roomId, hello.Hello.SessionId),
"permissions": []Permission{PERMISSION_MAY_PUBLISH_MEDIA, PERMISSION_MAY_CONTROL},
"permissions": []api.Permission{api.PERMISSION_MAY_PUBLISH_MEDIA, api.PERMISSION_MAY_CONTROL},
},
},
},
@ -4552,7 +4552,7 @@ func TestVirtualClientSessions(t *testing.T) {
virtualUserId := "virtual-user-id"
generatedSessionId := GetVirtualSessionId(session2, virtualSessionId)
setSessionRequestHandler(t, func(request *BackendClientSessionRequest) {
setSessionRequestHandler(t, func(request *talk.BackendClientSessionRequest) {
defer calledCancel()
assert.Equal("add", request.Action, "%+v", request)
assert.Equal(roomId, request.RoomId, "%+v", request)
@ -4661,7 +4661,7 @@ func TestVirtualClientSessions(t *testing.T) {
calledCtx, calledCancel = context.WithTimeout(ctx, time.Second)
setSessionRequestHandler(t, func(request *BackendClientSessionRequest) {
setSessionRequestHandler(t, func(request *talk.BackendClientSessionRequest) {
defer calledCancel()
assert.Equal("remove", request.Action, "%+v", request)
assert.Equal(roomId, request.RoomId, "%+v", request)
@ -4794,7 +4794,7 @@ func TestDuplicateVirtualSessions(t *testing.T) {
virtualUserId := "virtual-user-id"
generatedSessionId := GetVirtualSessionId(session2, virtualSessionId)
setSessionRequestHandler(t, func(request *BackendClientSessionRequest) {
setSessionRequestHandler(t, func(request *talk.BackendClientSessionRequest) {
defer calledCancel()
assert.Equal("add", request.Action, "%+v", request)
assert.Equal(roomId, request.RoomId, "%+v", request)
@ -4874,9 +4874,9 @@ func TestDuplicateVirtualSessions(t *testing.T) {
}
}
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "incall",
InCall: &BackendRoomInCallRequest{
InCall: &talk.BackendRoomInCallRequest{
InCall: []byte("0"),
Users: []api.StringMap{
{
@ -4981,7 +4981,7 @@ func TestDuplicateVirtualSessions(t *testing.T) {
}
}
setSessionRequestHandler(t, func(request *BackendClientSessionRequest) {
setSessionRequestHandler(t, func(request *talk.BackendClientSessionRequest) {
defer calledCancel()
assert.Equal("remove", request.Action, "%+v", request)
assert.Equal(roomId, request.RoomId, "%+v", request)
@ -5045,9 +5045,9 @@ func DoTestSwitchToOne(t *testing.T, details api.StringMap) {
}
// Notify first client to switch to different room.
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "switchto",
SwitchTo: &BackendRoomSwitchToMessageRequest{
SwitchTo: &talk.BackendRoomSwitchToMessageRequest{
RoomId: roomId2,
Sessions: sessions,
},
@ -5146,9 +5146,9 @@ func DoTestSwitchToMultiple(t *testing.T, details1 api.StringMap, details2 api.S
require.NoError(err)
}
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "switchto",
SwitchTo: &BackendRoomSwitchToMessageRequest{
SwitchTo: &talk.BackendRoomSwitchToMessageRequest{
RoomId: roomId2,
Sessions: sessions,
},
@ -5291,9 +5291,9 @@ func TestDialoutStatus(t *testing.T) {
<-stopped
}()
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "dialout",
Dialout: &BackendRoomDialoutRequest{
Dialout: &talk.BackendRoomDialoutRequest{
Number: "+1234567890",
},
}
@ -5307,7 +5307,7 @@ func TestDialoutStatus(t *testing.T) {
assert.NoError(err)
require.Equal(http.StatusOK, res.StatusCode, "Expected success, got %s", string(body))
var response BackendServerRoomResponse
var response talk.BackendServerRoomResponse
if assert.NoError(json.Unmarshal(body, &response)) {
assert.Equal("dialout", response.Type)
if assert.NotNil(response.Dialout) {

34
internal/random_string.go Normal file
View file

@ -0,0 +1,34 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2025 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package internal
import (
"crypto/rand"
)
func RandomString(size int) string {
s := rand.Text()
for len(s) < size {
s += rand.Text()
}
return s[:size]
}

View file

@ -0,0 +1,41 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2025 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package internal
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestRandomString(t *testing.T) {
t.Parallel()
assert := assert.New(t)
s1 := RandomString(10)
assert.Len(s1, 10)
assert.NotEqual(s1, RandomString(10))
s2 := RandomString(123)
assert.Len(s2, 123)
assert.NotEqual(s2, RandomString(123))
}

View file

@ -32,6 +32,7 @@ import (
"github.com/strukturag/nextcloud-spreed-signaling/api"
"github.com/strukturag/nextcloud-spreed-signaling/geoip"
"github.com/strukturag/nextcloud-spreed-signaling/log"
"github.com/strukturag/nextcloud-spreed-signaling/talk"
)
const (
@ -135,7 +136,7 @@ type Mcu interface {
SetOnDisconnected(func())
GetStats() any
GetServerInfoSfu() *BackendServerInfoSfu
GetServerInfoSfu() *talk.BackendServerInfoSfu
GetBandwidthLimits() (api.Bandwidth, api.Bandwidth)
NewPublisher(ctx context.Context, listener McuListener, id api.PublicSessionId, sid string, streamType StreamType, settings NewPublisherSettings, initiator McuInitiator) (McuPublisher, error)

View file

@ -39,6 +39,7 @@ import (
"github.com/strukturag/nextcloud-spreed-signaling/container"
"github.com/strukturag/nextcloud-spreed-signaling/internal"
"github.com/strukturag/nextcloud-spreed-signaling/log"
"github.com/strukturag/nextcloud-spreed-signaling/talk"
)
const (
@ -581,8 +582,8 @@ func (m *mcuJanus) Info() *InfoMsg {
return m.info.Load()
}
func (m *mcuJanus) GetServerInfoSfu() *BackendServerInfoSfu {
janus := &BackendServerInfoSfuJanus{
func (m *mcuJanus) GetServerInfoSfu() *talk.BackendServerInfoSfu {
janus := &talk.BackendServerInfoSfuJanus{
Url: m.url,
}
if m.IsConnected() {
@ -597,7 +598,7 @@ func (m *mcuJanus) GetServerInfoSfu() *BackendServerInfoSfu {
janus.IPv6 = internal.MakePtr(info.IPv6)
if plugin, found := info.Plugins[pluginVideoRoom]; found {
janus.VideoRoom = &BackendServerInfoVideoRoom{
janus.VideoRoom = &talk.BackendServerInfoVideoRoom{
Name: plugin.Name,
Version: plugin.VersionString,
Author: plugin.Author,
@ -606,8 +607,8 @@ func (m *mcuJanus) GetServerInfoSfu() *BackendServerInfoSfu {
}
}
sfu := &BackendServerInfoSfu{
Mode: SfuModeJanus,
sfu := &talk.BackendServerInfoSfu{
Mode: talk.SfuModeJanus,
Janus: janus,
}
return sfu

View file

@ -53,6 +53,7 @@ import (
"github.com/strukturag/nextcloud-spreed-signaling/geoip"
"github.com/strukturag/nextcloud-spreed-signaling/internal"
"github.com/strukturag/nextcloud-spreed-signaling/log"
"github.com/strukturag/nextcloud-spreed-signaling/talk"
)
const (
@ -1878,15 +1879,15 @@ func (m *mcuProxy) GetStats() any {
return result
}
func (m *mcuProxy) GetServerInfoSfu() *BackendServerInfoSfu {
func (m *mcuProxy) GetServerInfoSfu() *talk.BackendServerInfoSfu {
m.connectionsMu.RLock()
defer m.connectionsMu.RUnlock()
sfu := &BackendServerInfoSfu{
Mode: SfuModeProxy,
sfu := &talk.BackendServerInfoSfu{
Mode: talk.SfuModeProxy,
}
for _, c := range m.connections {
proxy := BackendServerInfoSfuProxy{
proxy := talk.BackendServerInfoSfuProxy{
Url: c.rawUrl,
Temporary: c.IsTemporary(),
@ -1905,11 +1906,18 @@ func (m *mcuProxy) GetServerInfoSfu() *BackendServerInfoSfu {
proxy.Features = c.Features()
proxy.Country = c.Country()
proxy.Load = internal.MakePtr(c.Load())
proxy.Bandwidth = c.Bandwidth()
if bw := c.Bandwidth(); bw != nil {
proxy.Bandwidth = &talk.BackendServerInfoSfuProxyBandwidth{
Incoming: bw.Incoming,
Outgoing: bw.Outgoing,
Received: bw.Received,
Sent: bw.Sent,
}
}
}
sfu.Proxies = append(sfu.Proxies, proxy)
}
slices.SortFunc(sfu.Proxies, func(a, b BackendServerInfoSfuProxy) int {
slices.SortFunc(sfu.Proxies, func(a, b talk.BackendServerInfoSfuProxy) int {
c := strings.Compare(a.Url, b.Url)
if c == 0 {
c = strings.Compare(a.IP, b.IP)

View file

@ -582,7 +582,7 @@ func (h *TestProxyServerHandler) createPublisher() *testProxyServerPublisher {
h.mu.Lock()
defer h.mu.Unlock()
pub := &testProxyServerPublisher{
id: api.PublicSessionId(newRandomString(32)),
id: api.PublicSessionId(internal.RandomString(32)),
}
for {
@ -590,7 +590,7 @@ func (h *TestProxyServerHandler) createPublisher() *testProxyServerPublisher {
break
}
pub.id = api.PublicSessionId(newRandomString(32))
pub.id = api.PublicSessionId(internal.RandomString(32))
}
h.publishers[pub.id] = pub
return pub
@ -621,8 +621,8 @@ func (h *TestProxyServerHandler) createSubscriber(pub *testProxyServerPublisher)
defer h.mu.Unlock()
sub := &testProxyServerSubscriber{
id: newRandomString(32),
sid: newRandomString(8),
id: internal.RandomString(32),
sid: internal.RandomString(8),
pub: pub,
}
@ -631,7 +631,7 @@ func (h *TestProxyServerHandler) createSubscriber(pub *testProxyServerPublisher)
break
}
sub.id = newRandomString(32)
sub.id = internal.RandomString(32)
}
h.subscribers[sub.id] = sub
return sub
@ -753,7 +753,7 @@ func (h *TestProxyServerHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
t: h.t,
server: h,
ws: ws,
sessionId: api.PublicSessionId(newRandomString(32)),
sessionId: api.PublicSessionId(internal.RandomString(32)),
}
h.setClient(client.sessionId, client)

View file

@ -33,8 +33,10 @@ import (
"github.com/dlintw/goconf"
"github.com/strukturag/nextcloud-spreed-signaling/api"
"github.com/strukturag/nextcloud-spreed-signaling/internal"
"github.com/strukturag/nextcloud-spreed-signaling/log"
"github.com/strukturag/nextcloud-spreed-signaling/mock"
"github.com/strukturag/nextcloud-spreed-signaling/talk"
)
var (
@ -92,7 +94,7 @@ func (m *TestMCU) GetStats() any {
return nil
}
func (m *TestMCU) GetServerInfoSfu() *BackendServerInfoSfu {
func (m *TestMCU) GetServerInfoSfu() *talk.BackendServerInfoSfu {
return nil
}
@ -150,7 +152,7 @@ func (m *TestMCU) NewSubscriber(ctx context.Context, listener McuListener, publi
return nil, errors.New("Waiting for publisher not implemented yet")
}
id := newRandomString(8)
id := internal.RandomString(8)
sub := &TestMCUSubscriber{
TestMCUClient: TestMCUClient{
t: m.t,

View file

@ -48,6 +48,7 @@ import (
"github.com/strukturag/nextcloud-spreed-signaling/api"
"github.com/strukturag/nextcloud-spreed-signaling/internal"
"github.com/strukturag/nextcloud-spreed-signaling/log"
"github.com/strukturag/nextcloud-spreed-signaling/talk"
)
const (
@ -387,7 +388,7 @@ func (m *TestMCU) GetStats() any {
return nil
}
func (m *TestMCU) GetServerInfoSfu() *signaling.BackendServerInfoSfu {
func (m *TestMCU) GetServerInfoSfu() *talk.BackendServerInfoSfu {
return nil
}

49
room.go
View file

@ -88,7 +88,7 @@ type Room struct {
// +checklocks:mu
inCallSessions map[Session]bool
// +checklocks:mu
roomSessionData map[api.PublicSessionId]*RoomSessionData
roomSessionData map[api.PublicSessionId]*talk.RoomSessionData
// +checklocks:mu
statsRoomSessionsCurrent *prometheus.GaugeVec
@ -128,7 +128,7 @@ func NewRoom(roomId string, properties json.RawMessage, hub *Hub, events AsyncEv
internalSessions: make(map[*ClientSession]bool),
virtualSessions: make(map[*VirtualSession]bool),
inCallSessions: make(map[Session]bool),
roomSessionData: make(map[api.PublicSessionId]*RoomSessionData),
roomSessionData: make(map[api.PublicSessionId]*talk.RoomSessionData),
statsRoomSessionsCurrent: statsRoomSessionsCurrent.MustCurryWith(prometheus.Labels{
"backend": backend.Id(),
@ -255,7 +255,7 @@ func (r *Room) processAsyncMessage(message *AsyncMessage) {
}
}
func (r *Room) processBackendRoomRequestRoom(message *BackendServerRoomRequest) {
func (r *Room) processBackendRoomRequestRoom(message *talk.BackendServerRoomRequest) {
received := message.ReceivedTime
if last, found := r.lastRoomRequests[message.Type]; found && last > received {
if msg, err := json.Marshal(message); err == nil {
@ -267,7 +267,8 @@ func (r *Room) processBackendRoomRequestRoom(message *BackendServerRoomRequest)
}
r.lastRoomRequests[message.Type] = received
message.room = r
message.RoomId = r.Id()
message.Backend = r.Backend()
switch message.Type {
case "update":
r.hub.roomUpdated <- message
@ -284,13 +285,13 @@ func (r *Room) processBackendRoomRequestRoom(message *BackendServerRoomRequest)
r.publishSwitchTo(message.SwitchTo)
case "transient":
switch message.Transient.Action {
case TransientActionSet:
case talk.TransientActionSet:
if message.Transient.TTL == 0 {
r.doSetTransientData(message.Transient.Key, message.Transient.Value)
} else {
r.doSetTransientDataTTL(message.Transient.Key, message.Transient.Value, message.Transient.TTL)
}
case TransientActionDelete:
case talk.TransientActionDelete:
r.doRemoveTransientData(message.Transient.Key)
default:
r.logger.Printf("Unsupported transient action in room %s: %+v", r.Id(), message.Transient)
@ -313,9 +314,9 @@ func (r *Room) processBackendRoomRequestAsyncRoom(message *AsyncRoomMessage) {
}
func (r *Room) AddSession(session Session, sessionData json.RawMessage) {
var roomSessionData *RoomSessionData
var roomSessionData *talk.RoomSessionData
if len(sessionData) > 0 {
roomSessionData = &RoomSessionData{}
roomSessionData = &talk.RoomSessionData{}
if err := json.Unmarshal(sessionData, roomSessionData); err != nil {
r.logger.Printf("Error decoding room session data \"%s\": %s", string(sessionData), err)
roomSessionData = nil
@ -572,13 +573,13 @@ func (r *Room) UpdateProperties(properties json.RawMessage) {
}
}
func (r *Room) GetRoomSessionData(session Session) *RoomSessionData {
func (r *Room) GetRoomSessionData(session Session) *talk.RoomSessionData {
r.mu.RLock()
defer r.mu.RUnlock()
return r.roomSessionData[session.PublicId()]
}
func (r *Room) PublishSessionJoined(session Session, sessionData *RoomSessionData) {
func (r *Room) PublishSessionJoined(session Session, sessionData *talk.RoomSessionData) {
sessionId := session.PublicId()
if sessionId == "" {
return
@ -1072,7 +1073,7 @@ func (r *Room) publishActiveSessions() (int, *sync.WaitGroup) {
r.mu.RLock()
defer r.mu.RUnlock()
entries := make(map[string][]BackendPingEntry)
entries := make(map[string][]talk.BackendPingEntry)
urls := make(map[string]*url.URL)
for _, session := range r.sessions {
u := session.BackendUrl()
@ -1120,7 +1121,7 @@ func (r *Room) publishActiveSessions() (int, *sync.WaitGroup) {
urls[u] = parsedBackendUrl
}
entries[u] = append(e, BackendPingEntry{
entries[u] = append(e, talk.BackendPingEntry{
SessionId: sid,
UserId: uid,
})
@ -1134,7 +1135,7 @@ func (r *Room) publishActiveSessions() (int, *sync.WaitGroup) {
for u, e := range entries {
wg.Add(1)
count += len(e)
go func(url *url.URL, entries []BackendPingEntry) {
go func(url *url.URL, entries []talk.BackendPingEntry) {
defer wg.Done()
sendCtx, cancel := context.WithTimeout(ctx, r.hub.backendTimeout)
defer cancel()
@ -1147,7 +1148,7 @@ func (r *Room) publishActiveSessions() (int, *sync.WaitGroup) {
return count, &wg
}
func (r *Room) publishRoomMessage(message *BackendRoomMessageRequest) {
func (r *Room) publishRoomMessage(message *talk.BackendRoomMessageRequest) {
if message == nil || len(message.Data) == 0 {
return
}
@ -1168,7 +1169,7 @@ func (r *Room) publishRoomMessage(message *BackendRoomMessageRequest) {
}
}
func (r *Room) publishSwitchTo(message *BackendRoomSwitchToMessageRequest) {
func (r *Room) publishSwitchTo(message *talk.BackendRoomSwitchToMessageRequest) {
var wg sync.WaitGroup
if len(message.SessionsList) > 0 {
msg := &api.ServerMessage{
@ -1250,10 +1251,10 @@ func (r *Room) SetTransientData(key string, value any) error {
return r.events.PublishBackendRoomMessage(r.Id(), r.Backend(), &AsyncMessage{
Type: "room",
Room: &BackendServerRoomRequest{
Room: &talk.BackendServerRoomRequest{
Type: "transient",
Transient: &BackendRoomTransientRequest{
Action: TransientActionSet,
Transient: &talk.BackendRoomTransientRequest{
Action: talk.TransientActionSet,
Key: key,
Value: value,
},
@ -1274,10 +1275,10 @@ func (r *Room) SetTransientDataTTL(key string, value any, ttl time.Duration) err
return r.events.PublishBackendRoomMessage(r.Id(), r.Backend(), &AsyncMessage{
Type: "room",
Room: &BackendServerRoomRequest{
Room: &talk.BackendServerRoomRequest{
Type: "transient",
Transient: &BackendRoomTransientRequest{
Action: TransientActionSet,
Transient: &talk.BackendRoomTransientRequest{
Action: talk.TransientActionSet,
Key: key,
Value: value,
TTL: ttl,
@ -1293,10 +1294,10 @@ func (r *Room) doSetTransientDataTTL(key string, value any, ttl time.Duration) {
func (r *Room) RemoveTransientData(key string) error {
return r.events.PublishBackendRoomMessage(r.Id(), r.Backend(), &AsyncMessage{
Type: "room",
Room: &BackendServerRoomRequest{
Room: &talk.BackendServerRoomRequest{
Type: "transient",
Transient: &BackendRoomTransientRequest{
Action: TransientActionDelete,
Transient: &talk.BackendRoomTransientRequest{
Action: talk.TransientActionDelete,
Key: key,
},
},

View file

@ -36,19 +36,19 @@ import (
type pingEntries struct {
url *url.URL
entries map[string][]BackendPingEntry
entries map[string][]talk.BackendPingEntry
}
func newPingEntries(url *url.URL, roomId string, entries []BackendPingEntry) *pingEntries {
func newPingEntries(url *url.URL, roomId string, entries []talk.BackendPingEntry) *pingEntries {
return &pingEntries{
url: url,
entries: map[string][]BackendPingEntry{
entries: map[string][]talk.BackendPingEntry{
roomId: entries,
},
}
}
func (e *pingEntries) Add(roomId string, entries []BackendPingEntry) {
func (e *pingEntries) Add(roomId string, entries []talk.BackendPingEntry) {
if existing, found := e.entries[roomId]; found {
e.entries[roomId] = append(existing, entries...)
} else {
@ -121,7 +121,7 @@ func (p *RoomPing) publishEntries(ctx context.Context, entries *pingEntries, tim
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
limit, _, found := p.capabilities.GetIntegerConfig(ctx, entries.url, ConfigGroupSignaling, ConfigKeySessionPingLimit)
limit, _, found := p.capabilities.GetIntegerConfig(ctx, entries.url, talk.ConfigGroupSignaling, talk.ConfigKeySessionPingLimit)
if !found || limit <= 0 {
// Limit disabled while waiting for the next iteration, fallback to sending
// one request per room.
@ -137,7 +137,7 @@ func (p *RoomPing) publishEntries(ctx context.Context, entries *pingEntries, tim
return
}
var allEntries []BackendPingEntry
var allEntries []talk.BackendPingEntry
for _, e := range entries.entries {
allEntries = append(allEntries, e...)
}
@ -164,28 +164,28 @@ func (p *RoomPing) publishActiveSessions(ctx context.Context) {
wg.Wait()
}
func (p *RoomPing) sendPingsDirect(ctx context.Context, roomId string, url *url.URL, entries []BackendPingEntry) error {
request := NewBackendClientPingRequest(roomId, entries)
var response BackendClientResponse
func (p *RoomPing) sendPingsDirect(ctx context.Context, roomId string, url *url.URL, entries []talk.BackendPingEntry) error {
request := talk.NewBackendClientPingRequest(roomId, entries)
var response talk.BackendClientResponse
return p.backend.PerformJSONRequest(ctx, url, request, &response)
}
func (p *RoomPing) sendPingsCombined(ctx context.Context, url *url.URL, entries []BackendPingEntry, limit int, timeout time.Duration) {
func (p *RoomPing) sendPingsCombined(ctx context.Context, url *url.URL, entries []talk.BackendPingEntry, limit int, timeout time.Duration) {
logger := log.LoggerFromContext(ctx)
for tosend := range slices.Chunk(entries, limit) {
subCtx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
request := NewBackendClientPingRequest("", tosend)
var response BackendClientResponse
request := talk.NewBackendClientPingRequest("", tosend)
var response talk.BackendClientResponse
if err := p.backend.PerformJSONRequest(subCtx, url, request, &response); err != nil {
logger.Printf("Error sending combined ping session entries %+v to %s: %s", tosend, url, err)
}
}
}
func (p *RoomPing) SendPings(ctx context.Context, roomId string, url *url.URL, entries []BackendPingEntry) error {
limit, _, found := p.capabilities.GetIntegerConfig(ctx, url, ConfigGroupSignaling, ConfigKeySessionPingLimit)
func (p *RoomPing) SendPings(ctx context.Context, roomId string, url *url.URL, entries []talk.BackendPingEntry) error {
limit, _, found := p.capabilities.GetIntegerConfig(ctx, url, talk.ConfigGroupSignaling, talk.ConfigKeySessionPingLimit)
if !found || limit <= 0 {
// Old-style Nextcloud or session limit not configured. Perform one request
// per room. Don't queue to avoid sending all ping requests to old-style

View file

@ -32,6 +32,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/strukturag/nextcloud-spreed-signaling/log"
"github.com/strukturag/nextcloud-spreed-signaling/talk"
)
func NewRoomPingForTest(ctx context.Context, t *testing.T) (*url.URL, *RoomPing) {
@ -72,7 +73,7 @@ func TestSingleRoomPing(t *testing.T) {
room1 := &Room{
id: "sample-room-1",
}
entries1 := []BackendPingEntry{
entries1 := []talk.BackendPingEntry{
{
UserId: "foo",
SessionId: "123",
@ -87,7 +88,7 @@ func TestSingleRoomPing(t *testing.T) {
room2 := &Room{
id: "sample-room-2",
}
entries2 := []BackendPingEntry{
entries2 := []talk.BackendPingEntry{
{
UserId: "bar",
SessionId: "456",
@ -116,7 +117,7 @@ func TestMultiRoomPing(t *testing.T) {
room1 := &Room{
id: "sample-room-1",
}
entries1 := []BackendPingEntry{
entries1 := []talk.BackendPingEntry{
{
UserId: "foo",
SessionId: "123",
@ -128,7 +129,7 @@ func TestMultiRoomPing(t *testing.T) {
room2 := &Room{
id: "sample-room-2",
}
entries2 := []BackendPingEntry{
entries2 := []talk.BackendPingEntry{
{
UserId: "bar",
SessionId: "456",
@ -156,7 +157,7 @@ func TestMultiRoomPing_Separate(t *testing.T) {
room1 := &Room{
id: "sample-room-1",
}
entries1 := []BackendPingEntry{
entries1 := []talk.BackendPingEntry{
{
UserId: "foo",
SessionId: "123",
@ -164,7 +165,7 @@ func TestMultiRoomPing_Separate(t *testing.T) {
}
assert.NoError(ping.SendPings(ctx, room1.Id(), u, entries1))
assert.Empty(getPingRequests(t))
entries2 := []BackendPingEntry{
entries2 := []talk.BackendPingEntry{
{
UserId: "bar",
SessionId: "456",
@ -192,7 +193,7 @@ func TestMultiRoomPing_DeleteRoom(t *testing.T) {
room1 := &Room{
id: "sample-room-1",
}
entries1 := []BackendPingEntry{
entries1 := []talk.BackendPingEntry{
{
UserId: "foo",
SessionId: "123",
@ -204,7 +205,7 @@ func TestMultiRoomPing_DeleteRoom(t *testing.T) {
room2 := &Room{
id: "sample-room-2",
}
entries2 := []BackendPingEntry{
entries2 := []talk.BackendPingEntry{
{
UserId: "bar",
SessionId: "456",

View file

@ -36,6 +36,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/strukturag/nextcloud-spreed-signaling/log"
"github.com/strukturag/nextcloud-spreed-signaling/talk"
)
func TestRoom_InCall(t *testing.T) {
@ -106,9 +107,9 @@ func TestRoom_Update(t *testing.T) {
// Simulate backend request from Nextcloud to update the room.
roomProperties := json.RawMessage("{\"foo\":\"bar\"}")
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "update",
Update: &BackendRoomUpdateRequest{
Update: &talk.BackendRoomUpdateRequest{
UserIds: []string{
testDefaultUserId,
},
@ -200,9 +201,9 @@ func TestRoom_Delete(t *testing.T) {
assert.True(client.RunUntilJoined(ctx, hello.Hello))
// Simulate backend request from Nextcloud to update the room.
msg := &BackendServerRoomRequest{
msg := &talk.BackendServerRoomRequest{
Type: "delete",
Delete: &BackendRoomDeleteRequest{
Delete: &talk.BackendRoomDeleteRequest{
UserIds: []string{
testDefaultUserId,
},
@ -387,9 +388,9 @@ func TestRoom_InCallAll(t *testing.T) {
client1.RunUntilJoined(ctx, hello2.Hello)
// Simulate backend request from Nextcloud to update the "inCall" flag of all participants.
msg1 := &BackendServerRoomRequest{
msg1 := &talk.BackendServerRoomRequest{
Type: "incall",
InCall: &BackendRoomInCallRequest{
InCall: &talk.BackendRoomInCallRequest{
All: true,
InCall: json.RawMessage(strconv.FormatInt(FlagInCall, 10)),
},
@ -413,9 +414,9 @@ func TestRoom_InCallAll(t *testing.T) {
}
// Simulate backend request from Nextcloud to update the "inCall" flag of all participants.
msg2 := &BackendServerRoomRequest{
msg2 := &talk.BackendServerRoomRequest{
Type: "incall",
InCall: &BackendRoomInCallRequest{
InCall: &talk.BackendRoomInCallRequest{
All: true,
InCall: json.RawMessage(strconv.FormatInt(0, 10)),
},

View file

@ -100,7 +100,7 @@ func (s *DummySession) LeaveRoom(notify bool) *Room {
func (s *DummySession) Close() {
}
func (s *DummySession) HasPermission(permission Permission) bool {
func (s *DummySession) HasPermission(permission api.Permission) bool {
return false
}

View file

@ -31,25 +31,6 @@ import (
"github.com/strukturag/nextcloud-spreed-signaling/talk"
)
type Permission string
var (
PERMISSION_MAY_PUBLISH_MEDIA Permission = "publish-media"
PERMISSION_MAY_PUBLISH_AUDIO Permission = "publish-audio"
PERMISSION_MAY_PUBLISH_VIDEO Permission = "publish-video"
PERMISSION_MAY_PUBLISH_SCREEN Permission = "publish-screen"
PERMISSION_MAY_CONTROL Permission = "control"
PERMISSION_TRANSIENT_DATA Permission = "transient-data"
PERMISSION_HIDE_DISPLAYNAMES Permission = "hide-displaynames"
// DefaultPermissionOverrides contains permission overrides for users where
// no permissions have been set by the server. If a permission is not set in
// this map, it's assumed the user has that permission.
DefaultPermissionOverrides = map[Permission]bool{ // +checklocksignore: Global readonly variable.
PERMISSION_HIDE_DISPLAYNAMES: false,
}
)
type Session interface {
Context() context.Context
PrivateId() api.PrivateSessionId
@ -72,7 +53,7 @@ type Session interface {
Close()
HasPermission(permission Permission) bool
HasPermission(permission api.Permission) bool
SendError(e *api.Error) bool
SendMessage(message *api.ServerMessage) bool

View file

@ -25,9 +25,11 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/strukturag/nextcloud-spreed-signaling/api"
)
func assertSessionHasPermission(t *testing.T, session Session, permission Permission) {
func assertSessionHasPermission(t *testing.T, session Session, permission api.Permission) {
t.Helper()
assert.True(t, session.HasPermission(permission), "Session %s doesn't have permission %s", session.PublicId(), permission)
if cs, ok := session.(*ClientSession); ok {
@ -35,7 +37,7 @@ func assertSessionHasPermission(t *testing.T, session Session, permission Permis
}
}
func assertSessionHasNotPermission(t *testing.T, session Session, permission Permission) {
func assertSessionHasNotPermission(t *testing.T, session Session, permission api.Permission) {
t.Helper()
assert.False(t, session.HasPermission(permission), "Session %s has permission %s but shouldn't", session.PublicId(), permission)
if cs, ok := session.(*ClientSession); ok {

View file

@ -19,11 +19,10 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
package talk
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"crypto/subtle"
"encoding/hex"
@ -36,6 +35,7 @@ import (
"github.com/strukturag/nextcloud-spreed-signaling/api"
"github.com/strukturag/nextcloud-spreed-signaling/etcd"
"github.com/strukturag/nextcloud-spreed-signaling/geoip"
"github.com/strukturag/nextcloud-spreed-signaling/internal"
)
const (
@ -51,14 +51,6 @@ const (
ConfigKeySessionPingLimit = "session-ping-limit"
)
func newRandomString(length int) string {
b := make([]byte, length/2)
if _, err := rand.Read(b); err != nil {
panic(err)
}
return hex.EncodeToString(b)
}
func CalculateBackendChecksum(random string, body []byte, secret []byte) string {
mac := hmac.New(sha256.New, secret)
mac.Write([]byte(random)) // nolint
@ -68,7 +60,7 @@ func CalculateBackendChecksum(random string, body []byte, secret []byte) string
func AddBackendChecksum(r *http.Request, body []byte, secret []byte) {
// Add checksum so the backend can validate the request.
rnd := newRandomString(64)
rnd := internal.RandomString(64)
checksum := CalculateBackendChecksum(rnd, body, secret)
r.Header.Set(HeaderBackendSignalingRandom, rnd)
r.Header.Set(HeaderBackendSignalingChecksum, checksum)
@ -88,7 +80,8 @@ func ValidateBackendChecksumValue(checksum string, random string, body []byte, s
// Requests from Nextcloud to the signaling server.
type BackendServerRoomRequest struct {
room *Room
RoomId string `json:"-"`
Backend *Backend `json:"-"`
Type string `json:"type"`
@ -315,7 +308,12 @@ type BackendClientRoomRequest struct {
InCall int `json:"incall,omitempty"`
}
func (r *BackendClientRoomRequest) UpdateFromSession(s Session) {
type SessionWithUserData interface {
ClientType() api.ClientType
ParsedUserData() (api.StringMap, error)
}
func (r *BackendClientRoomRequest) UpdateFromSession(s SessionWithUserData) {
if s.ClientType() == api.HelloClientTypeFederation {
// Need to send additional data for requests of federated users.
if u, err := s.ParsedUserData(); err == nil && len(u) > 0 {
@ -351,7 +349,7 @@ type BackendClientRoomResponse struct {
// See "RoomSessionData" for a possible content.
Session json.RawMessage `json:"session,omitempty"`
Permissions *[]Permission `json:"permissions,omitempty"`
Permissions *[]api.Permission `json:"permissions,omitempty"`
}
type RoomSessionData struct {
@ -447,6 +445,18 @@ type BackendServerInfoSfuJanus struct {
VideoRoom *BackendServerInfoVideoRoom `json:"videoroom,omitempty"`
}
type BackendServerInfoSfuProxyBandwidth struct {
// Incoming is the bandwidth utilization for publishers in percent.
Incoming *float64 `json:"incoming,omitempty"`
// Outgoing is the bandwidth utilization for subscribers in percent.
Outgoing *float64 `json:"outgoing,omitempty"`
// Received is the incoming bandwidth.
Received api.Bandwidth `json:"received,omitempty"`
// Sent is the outgoing bandwidth.
Sent api.Bandwidth `json:"sent,omitempty"`
}
type BackendServerInfoSfuProxy struct {
Url string `json:"url"`
IP string `json:"ip,omitempty"`
@ -459,9 +469,9 @@ type BackendServerInfoSfuProxy struct {
Version string `json:"version,omitempty"`
Features []string `json:"features,omitempty"`
Country geoip.Country `json:"country,omitempty"`
Load *uint64 `json:"load,omitempty"`
Bandwidth *EventProxyServerBandwidth `json:"bandwidth,omitempty"`
Country geoip.Country `json:"country,omitempty"`
Load *uint64 `json:"load,omitempty"`
Bandwidth *BackendServerInfoSfuProxyBandwidth `json:"bandwidth,omitempty"`
}
type SfuMode string

View file

@ -19,19 +19,21 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
package talk
import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/strukturag/nextcloud-spreed-signaling/internal"
)
func TestBackendChecksum(t *testing.T) {
t.Parallel()
assert := assert.New(t)
rnd := newRandomString(32)
rnd := internal.RandomString(32)
body := []byte{1, 2, 3, 4, 5}
secret := []byte("shared-secret")

View file

@ -43,6 +43,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/strukturag/nextcloud-spreed-signaling/api"
"github.com/strukturag/nextcloud-spreed-signaling/internal"
)
var (
@ -492,7 +493,7 @@ func (c *TestClient) SendHelloInternal() error {
}
func (c *TestClient) SendHelloInternalWithFeatures(features []string) error {
random := newRandomString(48)
random := internal.RandomString(48)
mac := hmac.New(sha256.New, testInternalSecret)
mac.Write([]byte(random)) // nolint
token := hex.EncodeToString(mac.Sum(nil))

View file

@ -197,9 +197,9 @@ func Test_TransientMessages(t *testing.T) {
require.NotNil(session2, "Session %s does not exist", hello2.Hello.SessionId)
// Client 1 may modify transient data.
session1.SetPermissions([]Permission{PERMISSION_TRANSIENT_DATA})
session1.SetPermissions([]api.Permission{api.PERMISSION_TRANSIENT_DATA})
// Client 2 may not modify transient data.
session2.SetPermissions([]Permission{})
session2.SetPermissions([]api.Permission{})
require.NoError(client2.SetTransientData("foo", "bar", 0))
if msg, ok := client2.RunUntilMessage(ctx); ok {

View file

@ -240,12 +240,12 @@ func (s *VirtualSession) notifyBackendRemoved(room *Room, session Session, messa
defer cancel()
if options := s.Options(); options != nil && options.ActorId != "" && options.ActorType != "" {
request := NewBackendClientRoomRequest(room.Id(), s.UserId(), api.RoomSessionId(s.PublicId()))
request := talk.NewBackendClientRoomRequest(room.Id(), s.UserId(), api.RoomSessionId(s.PublicId()))
request.Room.Action = "leave"
request.Room.ActorId = options.ActorId
request.Room.ActorType = options.ActorType
var response BackendClientResponse
var response talk.BackendClientResponse
if err := s.hub.backend.PerformJSONRequest(ctx, s.ParsedBackendOcsUrl(), request, &response); err != nil {
virtualSessionId := GetVirtualSessionId(s.session, s.PublicId())
s.logger.Printf("Could not leave virtual session %s at backend %s: %s", virtualSessionId, s.BackendUrl(), err)
@ -266,11 +266,11 @@ func (s *VirtualSession) notifyBackendRemoved(room *Room, session Session, messa
return
}
} else {
request := NewBackendClientSessionRequest(room.Id(), "remove", s.PublicId(), &api.AddSessionInternalClientMessage{
request := talk.NewBackendClientSessionRequest(room.Id(), "remove", s.PublicId(), &api.AddSessionInternalClientMessage{
UserId: s.userId,
User: s.userData,
})
var response BackendClientSessionResponse
var response talk.BackendClientSessionResponse
err := s.hub.backend.PerformJSONRequest(ctx, s.ParsedBackendOcsUrl(), request, &response)
if err != nil {
s.logger.Printf("Could not remove virtual session %s from backend %s: %s", s.PublicId(), s.BackendUrl(), err)
@ -282,7 +282,7 @@ func (s *VirtualSession) notifyBackendRemoved(room *Room, session Session, messa
}
}
func (s *VirtualSession) HasPermission(permission Permission) bool {
func (s *VirtualSession) HasPermission(permission api.Permission) bool {
return true
}