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.
This commit is contained in:
Joachim Bauch 2021-09-28 10:46:24 +02:00
parent 3b26003b12
commit 70b37364d3
No known key found for this signature in database
GPG Key ID: 77C1D22D53E15F02
5 changed files with 72 additions and 11 deletions

View File

@ -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.

1
go.mod
View File

@ -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

2
go.sum
View File

@ -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=

62
hub.go
View File

@ -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
} else {
permission = PERMISSION_MAY_PUBLISH_MEDIA
if session.HasPermission(PERMISSION_MAY_PUBLISH_SCREEN) {
return nil
}
return session.HasPermission(permission)
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 {
// 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")
}
}
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
}

View File

@ -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"
)