From 70b37364d3e674012f2ab71670d23f3f41aae299 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Tue, 28 Sep 2021 10:46:24 +0200 Subject: [PATCH] Support separate permissions for publishing audio / video. Clients with permission "publish-media" can publish any audio/video. Other clients need to have "publish-audio" and/or "publish-video" to publish the corresponding media types. For this the SDP in the offer is parsed to check what will be sent. --- clientsession.go | 18 +++++++++++++++ go.mod | 1 + go.sum | 2 ++ hub.go | 60 +++++++++++++++++++++++++++++++++++++++--------- session.go | 2 ++ 5 files changed, 72 insertions(+), 11 deletions(-) diff --git a/clientsession.go b/clientsession.go index 61c83fe..4727666 100644 --- a/clientsession.go +++ b/clientsession.go @@ -175,6 +175,7 @@ func (s *ClientSession) HasFeature(feature string) bool { return false } +// HasPermission checks if the session has the passed permissions. func (s *ClientSession) HasPermission(permission Permission) bool { s.mu.Lock() defer s.mu.Unlock() @@ -182,6 +183,23 @@ func (s *ClientSession) HasPermission(permission Permission) bool { return s.hasPermissionLocked(permission) } +// HasAnyPermission checks if the session has one of the passed permissions. +func (s *ClientSession) HasAnyPermission(permission ...Permission) bool { + if len(permission) == 0 { + return false + } + + s.mu.Lock() + defer s.mu.Unlock() + + for _, p := range permission { + if s.hasPermissionLocked(p) { + return true + } + } + return false +} + func (s *ClientSession) hasPermissionLocked(permission Permission) bool { if !s.supportsPermissions { // Old-style session that doesn't receive permissions from Nextcloud. diff --git a/go.mod b/go.mod index 01430f4..fd06258 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/nats-io/nats.go v1.13.0 github.com/notedit/janus-go v0.0.0-20200517101215-10eb8b95d1a0 github.com/oschwald/maxminddb-golang v1.8.0 + github.com/pion/sdp v1.3.0 github.com/prometheus/client_golang v1.11.0 go.etcd.io/etcd v0.5.0-alpha.5.0.20210226220824-aa7126864d82 go.uber.org/zap v1.13.0 // indirect diff --git a/go.sum b/go.sum index 759f75e..bee570e 100644 --- a/go.sum +++ b/go.sum @@ -156,6 +156,8 @@ github.com/notedit/janus-go v0.0.0-20200517101215-10eb8b95d1a0/go.mod h1:BN/Txse github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk= github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis= +github.com/pion/sdp v1.3.0 h1:21lpgEILHyolpsIrbCBagZaAPj4o057cFjzaFebkVOs= +github.com/pion/sdp v1.3.0/go.mod h1:ceA2lTyftydQTuCIbUNoH77aAt6CiQJaRpssA4Gee8I= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= diff --git a/hub.go b/hub.go index 9b6e001..0d3afff 100644 --- a/hub.go +++ b/hub.go @@ -42,6 +42,7 @@ import ( "github.com/gorilla/mux" "github.com/gorilla/securecookie" "github.com/gorilla/websocket" + "github.com/pion/sdp" ) var ( @@ -1374,8 +1375,8 @@ func (h *Hub) processMessageMsg(client *Client, message *ClientMessage) { if recipient != nil { // The recipient is connected to this instance, no need to go through NATS. if clientData != nil && clientData.Type == "sendoffer" { - if !isAllowedToSend(session, clientData) { - log.Printf("Session %s is not allowed to send offer for %s, ignoring", session.PublicId(), clientData.RoomType) + if err := isAllowedToSend(session, clientData); err != nil { + log.Printf("Session %s is not allowed to send offer for %s, ignoring (%s)", session.PublicId(), clientData.RoomType, err) sendNotAllowed(session, message, "Not allowed to send offer") return } @@ -1651,14 +1652,51 @@ func (h *Hub) processInternalMsg(client *Client, message *ClientMessage) { } } -func isAllowedToSend(session *ClientSession, data *MessageClientMessageData) bool { - var permission Permission +func isAllowedToSend(session *ClientSession, data *MessageClientMessageData) error { if data.RoomType == "screen" { - permission = PERMISSION_MAY_PUBLISH_SCREEN + if session.HasPermission(PERMISSION_MAY_PUBLISH_SCREEN) { + return nil + } + return fmt.Errorf("permission \"%s\" not found", PERMISSION_MAY_PUBLISH_SCREEN) + } else if session.HasPermission(PERMISSION_MAY_PUBLISH_MEDIA) { + // Client is allowed to publish any media (audio / video). + return nil + } else if data != nil && data.Type == "offer" { + // Parse SDP to check what user is trying to publish and check permissions accordingly. + sdpValue, found := data.Payload["sdp"] + if !found { + return fmt.Errorf("offer does not contain a sdp") + } + sdpText, ok := sdpValue.(string) + if !ok { + return fmt.Errorf("offer does not contain a valid sdp") + } + var s sdp.SessionDescription + if err := s.Unmarshal(sdpText); err != nil { + return fmt.Errorf("could not parse sdp: %w", err) + } + for _, md := range s.MediaDescriptions { + switch md.MediaName.Media { + case "audio": + if !session.HasPermission(PERMISSION_MAY_PUBLISH_AUDIO) { + return fmt.Errorf("permission \"%s\" not found", PERMISSION_MAY_PUBLISH_AUDIO) + } + case "video": + if !session.HasPermission(PERMISSION_MAY_PUBLISH_VIDEO) { + return fmt.Errorf("permission \"%s\" not found", PERMISSION_MAY_PUBLISH_VIDEO) + } + } + } + return nil } else { - permission = PERMISSION_MAY_PUBLISH_MEDIA + // Candidate or unknown event, check if client is allowed to publish any media. + if session.HasAnyPermission(PERMISSION_MAY_PUBLISH_AUDIO, PERMISSION_MAY_PUBLISH_VIDEO) { + return nil + } + + return fmt.Errorf("permission check failed") } - return session.HasPermission(permission) + } func sendNotAllowed(session *ClientSession, message *ClientMessage, reason string) { @@ -1733,8 +1771,8 @@ func (h *Hub) processMcuMessage(senderSession *ClientSession, session *ClientSes clientType = "subscriber" mc, err = session.GetOrCreateSubscriber(ctx, h.mcu, message.Recipient.SessionId, data.RoomType) case "offer": - if !isAllowedToSend(session, data) { - log.Printf("Session %s is not allowed to offer %s, ignoring", session.PublicId(), data.RoomType) + if err := isAllowedToSend(session, data); err != nil { + log.Printf("Session %s is not allowed to offer %s, ignoring (%s)", session.PublicId(), data.RoomType, err) sendNotAllowed(senderSession, client_message, "Not allowed to publish.") return } @@ -1751,8 +1789,8 @@ func (h *Hub) processMcuMessage(senderSession *ClientSession, session *ClientSes mc = session.GetSubscriber(message.Recipient.SessionId, data.RoomType) default: if session.PublicId() == message.Recipient.SessionId { - if !isAllowedToSend(session, data) { - log.Printf("Session %s is not allowed to send candidate for %s, ignoring", session.PublicId(), data.RoomType) + if err := isAllowedToSend(session, data); err != nil { + log.Printf("Session %s is not allowed to send candidate for %s, ignoring (%s)", session.PublicId(), data.RoomType, err) sendNotAllowed(senderSession, client_message, "Not allowed to send candidate.") return } diff --git a/session.go b/session.go index 155cb8b..41434ac 100644 --- a/session.go +++ b/session.go @@ -31,6 +31,8 @@ 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" )