mirror of
https://github.com/strukturag/nextcloud-spreed-signaling.git
synced 2025-04-14 15:38:29 +00:00
323 lines
9.2 KiB
Go
323 lines
9.2 KiB
Go
/**
|
|
* Standalone signaling server for the Nextcloud Spreed app.
|
|
* Copyright (C) 2017 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 signaling
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"crypto/subtle"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"net/http"
|
|
)
|
|
|
|
const (
|
|
BackendVersion = "1.0"
|
|
|
|
HeaderBackendSignalingRandom = "Spreed-Signaling-Random"
|
|
HeaderBackendSignalingChecksum = "Spreed-Signaling-Checksum"
|
|
HeaderBackendServer = "Spreed-Signaling-Backend"
|
|
)
|
|
|
|
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
|
|
mac.Write(body) // nolint
|
|
return hex.EncodeToString(mac.Sum(nil))
|
|
}
|
|
|
|
func AddBackendChecksum(r *http.Request, body []byte, secret []byte) {
|
|
// Add checksum so the backend can validate the request.
|
|
rnd := newRandomString(64)
|
|
checksum := CalculateBackendChecksum(rnd, body, secret)
|
|
r.Header.Set(HeaderBackendSignalingRandom, rnd)
|
|
r.Header.Set(HeaderBackendSignalingChecksum, checksum)
|
|
}
|
|
|
|
func ValidateBackendChecksum(r *http.Request, body []byte, secret []byte) bool {
|
|
rnd := r.Header.Get(HeaderBackendSignalingRandom)
|
|
checksum := r.Header.Get(HeaderBackendSignalingChecksum)
|
|
return ValidateBackendChecksumValue(checksum, rnd, body, secret)
|
|
}
|
|
|
|
func ValidateBackendChecksumValue(checksum string, random string, body []byte, secret []byte) bool {
|
|
verify := CalculateBackendChecksum(random, body, secret)
|
|
return subtle.ConstantTimeCompare([]byte(verify), []byte(checksum)) == 1
|
|
}
|
|
|
|
// Requests from Nextcloud to the signaling server.
|
|
|
|
type BackendServerRoomRequest struct {
|
|
room *Room
|
|
|
|
Type string `json:"type"`
|
|
|
|
Invite *BackendRoomInviteRequest `json:"invite,omitempty"`
|
|
|
|
Disinvite *BackendRoomDisinviteRequest `json:"disinvite,omitempty"`
|
|
|
|
Update *BackendRoomUpdateRequest `json:"update,omitempty"`
|
|
|
|
Delete *BackendRoomDeleteRequest `json:"delete,omitempty"`
|
|
|
|
InCall *BackendRoomInCallRequest `json:"incall,omitempty"`
|
|
|
|
Participants *BackendRoomParticipantsRequest `json:"participants,omitempty"`
|
|
|
|
Message *BackendRoomMessageRequest `json:"message,omitempty"`
|
|
|
|
// Internal properties
|
|
ReceivedTime int64 `json:"received,omitempty"`
|
|
}
|
|
|
|
type BackendRoomInviteRequest struct {
|
|
UserIds []string `json:"userids,omitempty"`
|
|
// TODO(jojo): We should get rid of "AllUserIds" and find a better way to
|
|
// notify existing users the room has changed and they need to update it.
|
|
AllUserIds []string `json:"alluserids,omitempty"`
|
|
Properties *json.RawMessage `json:"properties,omitempty"`
|
|
}
|
|
|
|
type BackendRoomDisinviteRequest struct {
|
|
UserIds []string `json:"userids,omitempty"`
|
|
SessionIds []string `json:"sessionids,omitempty"`
|
|
// TODO(jojo): We should get rid of "AllUserIds" and find a better way to
|
|
// notify existing users the room has changed and they need to update it.
|
|
AllUserIds []string `json:"alluserids,omitempty"`
|
|
Properties *json.RawMessage `json:"properties,omitempty"`
|
|
}
|
|
|
|
type BackendRoomUpdateRequest struct {
|
|
UserIds []string `json:"userids,omitempty"`
|
|
Properties *json.RawMessage `json:"properties,omitempty"`
|
|
}
|
|
|
|
type BackendRoomDeleteRequest struct {
|
|
UserIds []string `json:"userids,omitempty"`
|
|
}
|
|
|
|
type BackendRoomInCallRequest struct {
|
|
// TODO(jojo): Change "InCall" to "int" when #914 has landed in NC Talk.
|
|
InCall json.RawMessage `json:"incall,omitempty"`
|
|
All bool `json:"all,omitempty"`
|
|
Changed []map[string]interface{} `json:"changed,omitempty"`
|
|
Users []map[string]interface{} `json:"users,omitempty"`
|
|
}
|
|
|
|
type BackendRoomParticipantsRequest struct {
|
|
Changed []map[string]interface{} `json:"changed,omitempty"`
|
|
Users []map[string]interface{} `json:"users,omitempty"`
|
|
}
|
|
|
|
type BackendRoomMessageRequest struct {
|
|
Data *json.RawMessage `json:"data,omitempty"`
|
|
}
|
|
|
|
// Requests from the signaling server to the Nextcloud backend.
|
|
|
|
type BackendClientAuthRequest struct {
|
|
Version string `json:"version"`
|
|
Params *json.RawMessage `json:"params"`
|
|
}
|
|
|
|
type BackendClientRequest struct {
|
|
json.Marshaler
|
|
json.Unmarshaler
|
|
|
|
Type string `json:"type"`
|
|
|
|
Auth *BackendClientAuthRequest `json:"auth,omitempty"`
|
|
|
|
Room *BackendClientRoomRequest `json:"room,omitempty"`
|
|
|
|
Ping *BackendClientPingRequest `json:"ping,omitempty"`
|
|
|
|
Session *BackendClientSessionRequest `json:"session,omitempty"`
|
|
}
|
|
|
|
func NewBackendClientAuthRequest(params *json.RawMessage) *BackendClientRequest {
|
|
return &BackendClientRequest{
|
|
Type: "auth",
|
|
Auth: &BackendClientAuthRequest{
|
|
Version: BackendVersion,
|
|
Params: params,
|
|
},
|
|
}
|
|
}
|
|
|
|
type BackendClientResponse struct {
|
|
json.Marshaler
|
|
json.Unmarshaler
|
|
|
|
Type string `json:"type"`
|
|
|
|
Error *Error `json:"error,omitempty"`
|
|
|
|
Auth *BackendClientAuthResponse `json:"auth,omitempty"`
|
|
|
|
Room *BackendClientRoomResponse `json:"room,omitempty"`
|
|
|
|
Ping *BackendClientRingResponse `json:"ping,omitempty"`
|
|
|
|
Session *BackendClientSessionResponse `json:"session,omitempty"`
|
|
}
|
|
|
|
type BackendClientAuthResponse struct {
|
|
Version string `json:"version"`
|
|
UserId string `json:"userid"`
|
|
User *json.RawMessage `json:"user"`
|
|
}
|
|
|
|
type BackendClientRoomRequest struct {
|
|
Version string `json:"version"`
|
|
RoomId string `json:"roomid"`
|
|
Action string `json:"action,omitempty"`
|
|
UserId string `json:"userid"`
|
|
SessionId string `json:"sessionid"`
|
|
|
|
// For Nextcloud Talk with SIP support.
|
|
ActorId string `json:"actorid,omitempty"`
|
|
ActorType string `json:"actortype,omitempty"`
|
|
InCall int `json:"incall,omitempty"`
|
|
}
|
|
|
|
func NewBackendClientRoomRequest(roomid string, userid string, sessionid string) *BackendClientRequest {
|
|
return &BackendClientRequest{
|
|
Type: "room",
|
|
Room: &BackendClientRoomRequest{
|
|
Version: BackendVersion,
|
|
RoomId: roomid,
|
|
UserId: userid,
|
|
SessionId: sessionid,
|
|
},
|
|
}
|
|
}
|
|
|
|
type BackendClientRoomResponse struct {
|
|
Version string `json:"version"`
|
|
RoomId string `json:"roomid"`
|
|
Properties *json.RawMessage `json:"properties"`
|
|
|
|
// Optional information about the Nextcloud Talk session. Can be used for
|
|
// example to define a "userid" for otherwise anonymous users.
|
|
// See "RoomSessionData" for a possible content.
|
|
Session *json.RawMessage `json:"session,omitempty"`
|
|
|
|
Permissions *[]Permission `json:"permissions,omitempty"`
|
|
}
|
|
|
|
type RoomSessionData struct {
|
|
UserId string `json:"userid,omitempty"`
|
|
}
|
|
|
|
type BackendPingEntry struct {
|
|
UserId string `json:"userid,omitempty"`
|
|
SessionId string `json:"sessionid"`
|
|
}
|
|
|
|
type BackendClientPingRequest struct {
|
|
Version string `json:"version"`
|
|
RoomId string `json:"roomid"`
|
|
Entries []BackendPingEntry `json:"entries"`
|
|
}
|
|
|
|
func NewBackendClientPingRequest(roomid string, entries []BackendPingEntry) *BackendClientRequest {
|
|
return &BackendClientRequest{
|
|
Type: "ping",
|
|
Ping: &BackendClientPingRequest{
|
|
Version: BackendVersion,
|
|
RoomId: roomid,
|
|
Entries: entries,
|
|
},
|
|
}
|
|
}
|
|
|
|
type BackendClientRingResponse struct {
|
|
Version string `json:"version"`
|
|
RoomId string `json:"roomid"`
|
|
}
|
|
|
|
type BackendClientSessionRequest struct {
|
|
Version string `json:"version"`
|
|
RoomId string `json:"roomid"`
|
|
Action string `json:"action"`
|
|
SessionId string `json:"sessionid"`
|
|
UserId string `json:"userid,omitempty"`
|
|
User *json.RawMessage `json:"user,omitempty"`
|
|
}
|
|
|
|
type BackendClientSessionResponse struct {
|
|
Version string `json:"version"`
|
|
RoomId string `json:"roomid"`
|
|
}
|
|
|
|
func NewBackendClientSessionRequest(roomid string, action string, sessionid string, msg *AddSessionInternalClientMessage) *BackendClientRequest {
|
|
request := &BackendClientRequest{
|
|
Type: "session",
|
|
Session: &BackendClientSessionRequest{
|
|
Version: BackendVersion,
|
|
RoomId: roomid,
|
|
Action: action,
|
|
SessionId: sessionid,
|
|
},
|
|
}
|
|
if msg != nil {
|
|
request.Session.UserId = msg.UserId
|
|
request.Session.User = msg.User
|
|
}
|
|
return request
|
|
}
|
|
|
|
type OcsMeta struct {
|
|
Status string `json:"status"`
|
|
StatusCode int `json:"statuscode"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
type OcsBody struct {
|
|
Meta OcsMeta `json:"meta"`
|
|
Data *json.RawMessage `json:"data"`
|
|
}
|
|
|
|
type OcsResponse struct {
|
|
json.Marshaler
|
|
json.Unmarshaler
|
|
|
|
Ocs *OcsBody `json:"ocs"`
|
|
}
|
|
|
|
// See https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00
|
|
type TurnCredentials struct {
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
TTL int64 `json:"ttl"`
|
|
URIs []string `json:"uris"`
|
|
}
|