mirror of
https://github.com/m1k1o/neko.git
synced 2024-07-24 14:40:50 +12:00
private mode implementation.
This commit is contained in:
parent
f549171ded
commit
d004ddd68f
@ -4,6 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"gitlab.com/demodesk/neko/server/pkg/auth"
|
"gitlab.com/demodesk/neko/server/pkg/auth"
|
||||||
"gitlab.com/demodesk/neko/server/pkg/types"
|
"gitlab.com/demodesk/neko/server/pkg/types"
|
||||||
"gitlab.com/demodesk/neko/server/pkg/utils"
|
"gitlab.com/demodesk/neko/server/pkg/utils"
|
||||||
@ -13,6 +15,8 @@ type RoomHandler struct {
|
|||||||
sessions types.SessionManager
|
sessions types.SessionManager
|
||||||
desktop types.DesktopManager
|
desktop types.DesktopManager
|
||||||
capture types.CaptureManager
|
capture types.CaptureManager
|
||||||
|
|
||||||
|
privateModeImage []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(
|
func New(
|
||||||
@ -20,13 +24,32 @@ func New(
|
|||||||
desktop types.DesktopManager,
|
desktop types.DesktopManager,
|
||||||
capture types.CaptureManager,
|
capture types.CaptureManager,
|
||||||
) *RoomHandler {
|
) *RoomHandler {
|
||||||
// Init
|
h := &RoomHandler{
|
||||||
|
|
||||||
return &RoomHandler{
|
|
||||||
sessions: sessions,
|
sessions: sessions,
|
||||||
desktop: desktop,
|
desktop: desktop,
|
||||||
capture: capture,
|
capture: capture,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate fallback image for private mode when needed
|
||||||
|
sessions.OnPrivateModeChanged(func(isPrivateMode bool) {
|
||||||
|
if !isPrivateMode {
|
||||||
|
log.Debug().Msg("clearing private mode fallback image")
|
||||||
|
h.privateModeImage = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
img := h.desktop.GetScreenshotImage()
|
||||||
|
bytes, err := utils.CreateJPGImage(img, 90)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("could not generate private mode fallback image")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().Msg("using private mode fallback image")
|
||||||
|
h.privateModeImage = bytes
|
||||||
|
})
|
||||||
|
|
||||||
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *RoomHandler) Route(r types.Router) {
|
func (h *RoomHandler) Route(r types.Router) {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"gitlab.com/demodesk/neko/server/pkg/auth"
|
||||||
"gitlab.com/demodesk/neko/server/pkg/types"
|
"gitlab.com/demodesk/neko/server/pkg/types"
|
||||||
"gitlab.com/demodesk/neko/server/pkg/types/event"
|
"gitlab.com/demodesk/neko/server/pkg/types/event"
|
||||||
"gitlab.com/demodesk/neko/server/pkg/types/message"
|
"gitlab.com/demodesk/neko/server/pkg/types/message"
|
||||||
@ -81,6 +82,19 @@ func (h *RoomHandler) screenShotGet(w http.ResponseWriter, r *http.Request) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *RoomHandler) screenCastGet(w http.ResponseWriter, r *http.Request) error {
|
func (h *RoomHandler) screenCastGet(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
// display fallback image when private mode is enabled even if screencast is not
|
||||||
|
if session, ok := auth.GetSession(r); ok && session.PrivateModeEnabled() {
|
||||||
|
if h.privateModeImage != nil {
|
||||||
|
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||||
|
w.Header().Set("Content-Type", "image/jpeg")
|
||||||
|
|
||||||
|
_, err := w.Write(h.privateModeImage)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils.HttpBadRequest("private mode is enabled but no fallback image available")
|
||||||
|
}
|
||||||
|
|
||||||
screencast := h.capture.Screencast()
|
screencast := h.capture.Screencast()
|
||||||
if !screencast.Enabled() {
|
if !screencast.Enabled() {
|
||||||
return utils.HttpBadRequest("screencast pipeline is not enabled")
|
return utils.HttpBadRequest("screencast pipeline is not enabled")
|
||||||
|
@ -56,6 +56,9 @@ type SessionManagerCtx struct {
|
|||||||
host types.Session
|
host types.Session
|
||||||
hostMu sync.Mutex
|
hostMu sync.Mutex
|
||||||
|
|
||||||
|
privateMode bool
|
||||||
|
privateModeMu sync.Mutex
|
||||||
|
|
||||||
cursors map[types.Session][]types.Cursor
|
cursors map[types.Session][]types.Cursor
|
||||||
cursorsMu sync.Mutex
|
cursorsMu sync.Mutex
|
||||||
|
|
||||||
@ -197,6 +200,39 @@ func (manager *SessionManagerCtx) ClearHost() {
|
|||||||
manager.SetHost(nil)
|
manager.SetHost(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---
|
||||||
|
// private mode
|
||||||
|
// ---
|
||||||
|
|
||||||
|
func (manager *SessionManagerCtx) SetPrivateMode(isPrivateMode bool) {
|
||||||
|
manager.privateModeMu.Lock()
|
||||||
|
|
||||||
|
// only if value changed
|
||||||
|
if manager.privateMode == isPrivateMode {
|
||||||
|
manager.privateModeMu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// update webrtc paused state for all sessions
|
||||||
|
for _, session := range manager.List() {
|
||||||
|
if webrtcPeer := session.GetWebRTCPeer(); webrtcPeer != nil {
|
||||||
|
webrtcPeer.SetPaused(isPrivateMode && !session.Profile().IsAdmin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.privateMode = isPrivateMode
|
||||||
|
manager.privateModeMu.Unlock()
|
||||||
|
|
||||||
|
manager.emmiter.Emit("private_mode_changed", isPrivateMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *SessionManagerCtx) PrivateMode() bool {
|
||||||
|
manager.privateModeMu.Lock()
|
||||||
|
defer manager.privateModeMu.Unlock()
|
||||||
|
|
||||||
|
return manager.privateMode
|
||||||
|
}
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
// cursors
|
// cursors
|
||||||
// ---
|
// ---
|
||||||
@ -326,6 +362,12 @@ func (manager *SessionManagerCtx) OnHostChanged(listener func(session types.Sess
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (manager *SessionManagerCtx) OnPrivateModeChanged(listener func(isPrivateMode bool)) {
|
||||||
|
manager.emmiter.On("private_mode_changed", func(payload ...interface{}) {
|
||||||
|
listener(payload[0].(bool))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
// config
|
// config
|
||||||
// ---
|
// ---
|
||||||
|
@ -44,6 +44,11 @@ func (session *SessionCtx) profileChanged() {
|
|||||||
if (!session.profile.CanConnect || !session.profile.CanLogin) && session.state.IsConnected {
|
if (!session.profile.CanConnect || !session.profile.CanLogin) && session.state.IsConnected {
|
||||||
session.GetWebSocketPeer().Destroy("profile changed")
|
session.GetWebSocketPeer().Destroy("profile changed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update webrtc paused state
|
||||||
|
if webrtcPeer := session.GetWebRTCPeer(); webrtcPeer != nil {
|
||||||
|
webrtcPeer.SetPaused(session.PrivateModeEnabled())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (session *SessionCtx) State() types.SessionState {
|
func (session *SessionCtx) State() types.SessionState {
|
||||||
@ -54,6 +59,10 @@ func (session *SessionCtx) IsHost() bool {
|
|||||||
return session.manager.GetHost() == session
|
return session.manager.GetHost() == session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (session *SessionCtx) PrivateModeEnabled() bool {
|
||||||
|
return session.manager.PrivateMode() && !session.profile.IsAdmin
|
||||||
|
}
|
||||||
|
|
||||||
func (session *SessionCtx) SetCursor(cursor types.Cursor) {
|
func (session *SessionCtx) SetCursor(cursor types.Cursor) {
|
||||||
if session.manager.InactiveCursors() && session.profile.SendsInactiveCursor {
|
if session.manager.InactiveCursors() && session.profile.SendsInactiveCursor {
|
||||||
session.manager.SetCursor(cursor, session)
|
session.manager.SetCursor(cursor, session)
|
||||||
|
@ -200,6 +200,12 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin
|
|||||||
|
|
||||||
return videoTrack.SetStream(videoStream)
|
return videoTrack.SetStream(videoStream)
|
||||||
},
|
},
|
||||||
|
setPaused: func(isPaused bool) {
|
||||||
|
videoTrack.SetPaused(isPaused)
|
||||||
|
audioTrack.SetPaused(isPaused)
|
||||||
|
|
||||||
|
// TODO: Send fresh cursor position & image when unpausing.
|
||||||
|
},
|
||||||
iceTrickle: manager.config.ICETrickle,
|
iceTrickle: manager.config.ICETrickle,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,12 +320,22 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin
|
|||||||
})
|
})
|
||||||
|
|
||||||
cursorImage := func(entry *cursor.ImageEntry) {
|
cursorImage := func(entry *cursor.ImageEntry) {
|
||||||
|
// TODO: Refactor.
|
||||||
|
if videoTrack.paused {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := peer.SendCursorImage(entry.Cursor, entry.Image); err != nil {
|
if err := peer.SendCursorImage(entry.Cursor, entry.Image); err != nil {
|
||||||
logger.Err(err).Msg("could not send cursor image")
|
logger.Err(err).Msg("could not send cursor image")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cursorPosition := func(x, y int) {
|
cursorPosition := func(x, y int) {
|
||||||
|
// TODO: Refactor.
|
||||||
|
if videoTrack.paused {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if session.IsHost() {
|
if session.IsHost() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -352,6 +368,11 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin
|
|||||||
})
|
})
|
||||||
|
|
||||||
dataChannel.OnMessage(func(message webrtc.DataChannelMessage) {
|
dataChannel.OnMessage(func(message webrtc.DataChannelMessage) {
|
||||||
|
// TODO: Refactor.
|
||||||
|
if videoTrack.paused {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := manager.handle(message.Data, session); err != nil {
|
if err := manager.handle(message.Data, session); err != nil {
|
||||||
logger.Err(err).Msg("data handle failed")
|
logger.Err(err).Msg("data handle failed")
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ type WebRTCPeerCtx struct {
|
|||||||
connection *webrtc.PeerConnection
|
connection *webrtc.PeerConnection
|
||||||
dataChannel *webrtc.DataChannel
|
dataChannel *webrtc.DataChannel
|
||||||
changeVideo func(videoID string) error
|
changeVideo func(videoID string) error
|
||||||
|
setPaused func(isPaused bool)
|
||||||
iceTrickle bool
|
iceTrickle bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,6 +123,19 @@ func (peer *WebRTCPeerCtx) SetVideoID(videoID string) error {
|
|||||||
return peer.changeVideo(videoID)
|
return peer.changeVideo(videoID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (peer *WebRTCPeerCtx) SetPaused(isPaused bool) error {
|
||||||
|
peer.mu.Lock()
|
||||||
|
defer peer.mu.Unlock()
|
||||||
|
|
||||||
|
if peer.connection == nil {
|
||||||
|
return types.ErrWebRTCConnectionNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
peer.logger.Info().Bool("is_paused", isPaused).Msg("set paused")
|
||||||
|
peer.setPaused(isPaused)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (peer *WebRTCPeerCtx) Destroy() {
|
func (peer *WebRTCPeerCtx) Destroy() {
|
||||||
peer.mu.Lock()
|
peer.mu.Lock()
|
||||||
defer peer.mu.Unlock()
|
defer peer.mu.Unlock()
|
||||||
|
@ -26,12 +26,17 @@ func (manager *WebRTCManagerCtx) newPeerStreamTrack(stream types.StreamSinkManag
|
|||||||
peer := &PeerStreamTrack{
|
peer := &PeerStreamTrack{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
track: track,
|
track: track,
|
||||||
listener: func(sample types.Sample) {
|
}
|
||||||
|
|
||||||
|
peer.listener = func(sample types.Sample) {
|
||||||
|
if peer.paused {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
err := track.WriteSample(media.Sample(sample))
|
err := track.WriteSample(media.Sample(sample))
|
||||||
if err != nil && errors.Is(err, io.ErrClosedPipe) {
|
if err != nil && errors.Is(err, io.ErrClosedPipe) {
|
||||||
logger.Warn().Err(err).Msg("pipeline failed to write")
|
logger.Warn().Err(err).Msg("pipeline failed to write")
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = peer.SetStream(stream)
|
err = peer.SetStream(stream)
|
||||||
@ -41,6 +46,7 @@ func (manager *WebRTCManagerCtx) newPeerStreamTrack(stream types.StreamSinkManag
|
|||||||
type PeerStreamTrack struct {
|
type PeerStreamTrack struct {
|
||||||
logger zerolog.Logger
|
logger zerolog.Logger
|
||||||
track *webrtc.TrackLocalStaticSample
|
track *webrtc.TrackLocalStaticSample
|
||||||
|
paused bool
|
||||||
listener func(sample types.Sample)
|
listener func(sample types.Sample)
|
||||||
|
|
||||||
stream types.StreamSinkManager
|
stream types.StreamSinkManager
|
||||||
@ -92,3 +98,7 @@ func (peer *PeerStreamTrack) AddToConnection(connection *webrtc.PeerConnection)
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (peer *PeerStreamTrack) SetPaused(paused bool) {
|
||||||
|
peer.paused = paused
|
||||||
|
}
|
||||||
|
@ -17,7 +17,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (h *MessageHandlerCtx) controlRelease(session types.Session) error {
|
func (h *MessageHandlerCtx) controlRelease(session types.Session) error {
|
||||||
if !session.Profile().CanHost {
|
if !session.Profile().CanHost || session.PrivateModeEnabled() {
|
||||||
return ErrIsNotAllowedToHost
|
return ErrIsNotAllowedToHost
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ func (h *MessageHandlerCtx) controlRelease(session types.Session) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *MessageHandlerCtx) controlRequest(session types.Session) error {
|
func (h *MessageHandlerCtx) controlRequest(session types.Session) error {
|
||||||
if !session.Profile().CanHost {
|
if !session.Profile().CanHost || session.PrivateModeEnabled() {
|
||||||
return ErrIsNotAllowedToHost
|
return ErrIsNotAllowedToHost
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,11 @@ func (h *MessageHandlerCtx) signalRequest(session types.Session, payload *messag
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set webrtc as paused if session has private mode enabled
|
||||||
|
if webrtcPeer := session.GetWebRTCPeer(); webrtcPeer != nil && session.PrivateModeEnabled() {
|
||||||
|
webrtcPeer.SetPaused(true)
|
||||||
|
}
|
||||||
|
|
||||||
session.Send(
|
session.Send(
|
||||||
event.SIGNAL_PROVIDE,
|
event.SIGNAL_PROVIDE,
|
||||||
message.SignalProvide{
|
message.SignalProvide{
|
||||||
|
@ -54,6 +54,10 @@ func CanHostOnly(w http.ResponseWriter, r *http.Request) (context.Context, error
|
|||||||
return nil, utils.HttpForbidden("session cannot host")
|
return nil, utils.HttpForbidden("session cannot host")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if session.PrivateModeEnabled() {
|
||||||
|
return nil, utils.HttpUnprocessableEntity("private mode is enabled")
|
||||||
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ type Session interface {
|
|||||||
Profile() MemberProfile
|
Profile() MemberProfile
|
||||||
State() SessionState
|
State() SessionState
|
||||||
IsHost() bool
|
IsHost() bool
|
||||||
|
PrivateModeEnabled() bool
|
||||||
|
|
||||||
// cursor
|
// cursor
|
||||||
SetCursor(cursor Cursor)
|
SetCursor(cursor Cursor)
|
||||||
@ -55,6 +56,9 @@ type SessionManager interface {
|
|||||||
GetHost() Session
|
GetHost() Session
|
||||||
ClearHost()
|
ClearHost()
|
||||||
|
|
||||||
|
SetPrivateMode(isPrivateMode bool)
|
||||||
|
PrivateMode() bool
|
||||||
|
|
||||||
SetCursor(cursor Cursor, session Session)
|
SetCursor(cursor Cursor, session Session)
|
||||||
PopCursors() map[Session][]Cursor
|
PopCursors() map[Session][]Cursor
|
||||||
|
|
||||||
@ -69,6 +73,7 @@ type SessionManager interface {
|
|||||||
OnProfileChanged(listener func(session Session))
|
OnProfileChanged(listener func(session Session))
|
||||||
OnStateChanged(listener func(session Session))
|
OnStateChanged(listener func(session Session))
|
||||||
OnHostChanged(listener func(session Session))
|
OnHostChanged(listener func(session Session))
|
||||||
|
OnPrivateModeChanged(listener func(isPrivateMode bool))
|
||||||
|
|
||||||
ImplicitHosting() bool
|
ImplicitHosting() bool
|
||||||
InactiveCursors() bool
|
InactiveCursors() bool
|
||||||
|
@ -26,6 +26,8 @@ type WebRTCPeer interface {
|
|||||||
SetCandidate(candidate webrtc.ICECandidateInit) error
|
SetCandidate(candidate webrtc.ICECandidateInit) error
|
||||||
|
|
||||||
SetVideoID(videoID string) error
|
SetVideoID(videoID string) error
|
||||||
|
SetPaused(isPaused bool) error
|
||||||
|
|
||||||
SendCursorPosition(x, y int) error
|
SendCursorPosition(x, y int) error
|
||||||
SendCursorImage(cur *CursorImage, img []byte) error
|
SendCursorImage(cur *CursorImage, img []byte) error
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user