move filetransfer to locks.
This commit is contained in:
@ -19,7 +19,12 @@ func (h *MessageHandler) adminLock(id string, session types.Session, payload *me
|
||||
return nil
|
||||
}
|
||||
|
||||
if payload.Resource != "login" && payload.Resource != "control" {
|
||||
// allow only known resources
|
||||
switch payload.Resource {
|
||||
case "login":
|
||||
case "control":
|
||||
case "file_transfer":
|
||||
default:
|
||||
h.logger.Debug().Msg("unknown lock resource")
|
||||
return nil
|
||||
}
|
||||
|
@ -1,62 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"m1k1o/neko/internal/types"
|
||||
"m1k1o/neko/internal/types/event"
|
||||
"m1k1o/neko/internal/types/message"
|
||||
"m1k1o/neko/internal/utils"
|
||||
)
|
||||
|
||||
func (h *MessageHandler) setFileTransferStatus(session types.Session, payload *message.FileTransferStatus) error {
|
||||
if !session.Admin() {
|
||||
h.logger.Debug().Msg("user not admin")
|
||||
return nil
|
||||
}
|
||||
|
||||
h.state.SetFileTransferState(payload.Admin, payload.Unpriv)
|
||||
|
||||
err := h.sessions.Broadcast(message.FileTransferStatus{
|
||||
Event: event.FILETRANSFER_STATUS,
|
||||
Admin: payload.Admin,
|
||||
Unpriv: payload.Admin && payload.Unpriv,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
files, err := utils.ListFiles(h.state.FileTransferPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := message.FileList{
|
||||
Event: event.FILETRANSFER_LIST,
|
||||
Cwd: h.state.FileTransferPath(),
|
||||
Files: files,
|
||||
}
|
||||
|
||||
if payload.Unpriv {
|
||||
return h.sessions.Broadcast(msg, nil)
|
||||
} else {
|
||||
return h.sessions.AdminBroadcast(msg, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *MessageHandler) refresh(session types.Session) error {
|
||||
if !(h.state.FileTransferEnabled() && session.Admin() || h.state.UnprivFileTransferEnabled()) {
|
||||
return errors.New(session.Member().Name + " tried to refresh file list when they can't")
|
||||
}
|
||||
|
||||
files, err := utils.ListFiles(h.state.FileTransferPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return session.Send(
|
||||
message.FileList{
|
||||
Event: event.FILETRANSFER_LIST,
|
||||
Cwd: h.state.FileTransferPath(),
|
||||
Files: files,
|
||||
})
|
||||
}
|
42
server/internal/websocket/handler/filetransfer.go
Normal file
42
server/internal/websocket/handler/filetransfer.go
Normal file
@ -0,0 +1,42 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"m1k1o/neko/internal/types"
|
||||
"m1k1o/neko/internal/types/event"
|
||||
"m1k1o/neko/internal/types/message"
|
||||
"m1k1o/neko/internal/utils"
|
||||
)
|
||||
|
||||
func (h *MessageHandler) FileTransferRefresh(session types.Session) error {
|
||||
fileTransferPath := h.state.FileTransferPath("") // root
|
||||
|
||||
// allow users only if file transfer is not locked
|
||||
if session != nil && !(session.Admin() || !h.state.IsLocked("file_transfer")) {
|
||||
h.logger.Debug().Msg("file transfer is locked for users")
|
||||
return nil
|
||||
}
|
||||
|
||||
files, err := utils.ListFiles(fileTransferPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
message := message.FileTransferList{
|
||||
Event: event.FILETRANSFER_LIST,
|
||||
Cwd: fileTransferPath,
|
||||
Files: files,
|
||||
}
|
||||
|
||||
// send to just one user
|
||||
if session != nil {
|
||||
return session.Send(message)
|
||||
}
|
||||
|
||||
// broadcast to all admins
|
||||
if h.state.IsLocked("file_transfer") {
|
||||
return h.sessions.AdminBroadcast(message, nil)
|
||||
}
|
||||
|
||||
// broadcast to all users
|
||||
return h.sessions.Broadcast(message, nil)
|
||||
}
|
@ -127,14 +127,8 @@ func (h *MessageHandler) Message(id string, raw []byte) error {
|
||||
}), "%s failed", header.Event)
|
||||
|
||||
// File Transfer Events
|
||||
case event.FILETRANSFER_STATUS:
|
||||
payload := &message.FileTransferStatus{}
|
||||
return errors.Wrapf(
|
||||
utils.Unmarshal(payload, raw, func() error {
|
||||
return h.setFileTransferStatus(session, payload)
|
||||
}), "%s failed", header.Event)
|
||||
case event.FILETRANSFER_REFRESH:
|
||||
return errors.Wrapf(h.refresh(session), "%s failed", header.Event)
|
||||
return errors.Wrapf(h.FileTransferRefresh(session), "%s failed", header.Event)
|
||||
|
||||
// Screen Events
|
||||
case event.SCREEN_RESOLUTION:
|
||||
|
@ -17,6 +17,7 @@ func (h *MessageHandler) SessionCreated(id string, session types.Session) error
|
||||
Event: event.SYSTEM_INIT,
|
||||
ImplicitHosting: h.webrtc.ImplicitControl(),
|
||||
Locks: h.state.AllLocked(),
|
||||
FileTransfer: h.state.FileTransferEnabled(),
|
||||
}); err != nil {
|
||||
h.logger.Warn().Str("id", id).Err(err).Msgf("sending event %s has failed", event.SYSTEM_INIT)
|
||||
return err
|
||||
@ -34,14 +35,21 @@ func (h *MessageHandler) SessionCreated(id string, session types.Session) error
|
||||
}
|
||||
}
|
||||
|
||||
// send file list if file transfer is enabled
|
||||
if h.state.FileTransferEnabled() && (session.Admin() || !h.state.IsLocked("file_transfer")) {
|
||||
if err := h.FileTransferRefresh(session); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *MessageHandler) SessionConnected(id string, session types.Session) error {
|
||||
// send list of members to session
|
||||
if err := session.Send(message.MembersList{
|
||||
Event: event.MEMBER_LIST,
|
||||
Memebers: h.sessions.Members(),
|
||||
Event: event.MEMBER_LIST,
|
||||
Members: h.sessions.Members(),
|
||||
}); err != nil {
|
||||
h.logger.Warn().Str("id", id).Err(err).Msgf("sending event %s has failed", event.MEMBER_LIST)
|
||||
return err
|
||||
|
@ -1,22 +1,22 @@
|
||||
package state
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
type State struct {
|
||||
banned map[string]string // IP -> session ID (that banned it)
|
||||
locked map[string]string // resource name -> session ID (that locked it)
|
||||
|
||||
fileTransferEnabled bool // admins can transfer files
|
||||
fileTransferUnprivEnabled bool // all users can transfer files
|
||||
fileTransferPath string // path where files are located
|
||||
fileTransferEnabled bool
|
||||
fileTransferPath string // path where files are located
|
||||
}
|
||||
|
||||
func New(fileTransferEnabled bool, fileTransferUnprivEnabled bool, fileTransferPath string) *State {
|
||||
func New(fileTransferEnabled bool, fileTransferPath string) *State {
|
||||
return &State{
|
||||
banned: make(map[string]string),
|
||||
locked: make(map[string]string),
|
||||
|
||||
fileTransferEnabled: fileTransferEnabled,
|
||||
fileTransferUnprivEnabled: fileTransferUnprivEnabled,
|
||||
fileTransferPath: fileTransferPath,
|
||||
fileTransferEnabled: fileTransferEnabled,
|
||||
fileTransferPath: fileTransferPath,
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,21 +68,17 @@ func (s *State) AllLocked() map[string]string {
|
||||
return s.locked
|
||||
}
|
||||
|
||||
// File Transfer
|
||||
// File transfer
|
||||
|
||||
func (s *State) FileTransferPath(filename string) string {
|
||||
if filename == "" {
|
||||
return s.fileTransferPath
|
||||
}
|
||||
|
||||
cleanPath := filepath.Clean(filename)
|
||||
return filepath.Join(s.fileTransferPath, cleanPath)
|
||||
}
|
||||
|
||||
func (s *State) FileTransferEnabled() bool {
|
||||
return s.fileTransferEnabled
|
||||
}
|
||||
|
||||
func (s *State) UnprivFileTransferEnabled() bool {
|
||||
return s.fileTransferUnprivEnabled
|
||||
}
|
||||
|
||||
func (s *State) SetFileTransferState(admin bool, unpriv bool) {
|
||||
s.fileTransferEnabled = admin
|
||||
s.fileTransferUnprivEnabled = unpriv
|
||||
}
|
||||
|
||||
func (s *State) FileTransferPath() string {
|
||||
return s.fileTransferPath
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@ -28,7 +27,7 @@ const CONTROL_PROTECTION_SESSION = "by_control_protection"
|
||||
func New(sessions types.SessionManager, desktop types.DesktopManager, capture types.CaptureManager, webrtc types.WebRTCManager, conf *config.WebSocket) *WebSocketHandler {
|
||||
logger := log.With().Str("module", "websocket").Logger()
|
||||
|
||||
state := state.New(conf.FileTransfer, conf.UnprivFileTransfer, conf.FileTransferPath)
|
||||
state := state.New(conf.FileTransferEnabled, conf.FileTransferPath)
|
||||
|
||||
// if control protection is enabled
|
||||
if conf.ControlProtection {
|
||||
@ -36,9 +35,12 @@ func New(sessions types.SessionManager, desktop types.DesktopManager, capture ty
|
||||
logger.Info().Msgf("control locked on behalf of control protection")
|
||||
}
|
||||
|
||||
if _, err := os.Stat(conf.FileTransferPath); os.IsNotExist(err) {
|
||||
err = os.Mkdir(conf.FileTransferPath, os.ModePerm)
|
||||
logger.Err(err).Msg("creating file transfer directory")
|
||||
// create file transfer directory if not exists
|
||||
if conf.FileTransferEnabled {
|
||||
if _, err := os.Stat(conf.FileTransferPath); os.IsNotExist(err) {
|
||||
err = os.Mkdir(conf.FileTransferPath, os.ModePerm)
|
||||
logger.Err(err).Msg("creating file transfer directory")
|
||||
}
|
||||
}
|
||||
|
||||
// apply default locks
|
||||
@ -132,32 +134,6 @@ func (ws *WebSocketHandler) Start() {
|
||||
}
|
||||
}
|
||||
|
||||
// send file list if necessary
|
||||
if ws.state.FileTransferEnabled() && (session.Admin() || ws.state.UnprivFileTransferEnabled()) {
|
||||
err := session.Send(
|
||||
message.FileTransferStatus{
|
||||
Event: event.FILETRANSFER_STATUS,
|
||||
Admin: ws.state.FileTransferEnabled(),
|
||||
Unpriv: ws.state.UnprivFileTransferEnabled(),
|
||||
})
|
||||
if err != nil {
|
||||
ws.logger.Warn().Err(err).Msgf("file transfer status event has failed")
|
||||
return
|
||||
}
|
||||
|
||||
files, err := utils.ListFiles(ws.conf.FileTransferPath)
|
||||
if err == nil {
|
||||
if err := session.Send(
|
||||
message.FileList{
|
||||
Event: event.FILETRANSFER_LIST,
|
||||
Cwd: ws.conf.FileTransferPath,
|
||||
Files: files,
|
||||
}); err != nil {
|
||||
ws.logger.Warn().Err(err).Msg("file list event has failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove outdated stats
|
||||
if session.Admin() {
|
||||
ws.lastAdminLeftAt = nil
|
||||
@ -222,32 +198,35 @@ func (ws *WebSocketHandler) Start() {
|
||||
ws.logger.Err(err).Msg("sync clipboard")
|
||||
})
|
||||
|
||||
// watch for file changes
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
ws.logger.Err(err).Msg("unable to start file transfer dir watcher")
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case e, ok := <-watcher.Events:
|
||||
if !ok {
|
||||
ws.logger.Info().Msg("file transfer dir watcher closed")
|
||||
return
|
||||
}
|
||||
if e.Has(fsnotify.Create) || e.Has(fsnotify.Remove) || e.Has(fsnotify.Rename) {
|
||||
ws.sendFileTransferUpdate()
|
||||
}
|
||||
case err := <-watcher.Errors:
|
||||
ws.logger.Err(err).Msg("error in file transfer dir watcher")
|
||||
}
|
||||
// watch for file changes and send file list if file transfer is enabled
|
||||
if ws.conf.FileTransferEnabled {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
ws.logger.Err(err).Msg("unable to start file transfer dir watcher")
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
if err := watcher.Add(ws.conf.FileTransferPath); err != nil {
|
||||
ws.logger.Err(err).Msg("unable to add file transfer path to watcher")
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case e, ok := <-watcher.Events:
|
||||
if !ok {
|
||||
ws.logger.Info().Msg("file transfer dir watcher closed")
|
||||
return
|
||||
}
|
||||
if e.Has(fsnotify.Create) || e.Has(fsnotify.Remove) || e.Has(fsnotify.Rename) {
|
||||
ws.logger.Debug().Str("event", e.String()).Msg("file transfer dir watcher event")
|
||||
ws.handler.FileTransferRefresh(nil)
|
||||
}
|
||||
case err := <-watcher.Errors:
|
||||
ws.logger.Err(err).Msg("error in file transfer dir watcher")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err := watcher.Add(ws.conf.FileTransferPath); err != nil {
|
||||
ws.logger.Err(err).Msg("unable to add file transfer path to watcher")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -376,52 +355,6 @@ func (ws *WebSocketHandler) IsAdmin(password string) (bool, error) {
|
||||
return false, fmt.Errorf("invalid password")
|
||||
}
|
||||
|
||||
func (ws *WebSocketHandler) CanTransferFiles(password string) (bool, error) {
|
||||
if !ws.state.FileTransferEnabled() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
isAdmin, err := ws.IsAdmin(password)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return isAdmin || ws.state.UnprivFileTransferEnabled(), nil
|
||||
}
|
||||
|
||||
func (ws *WebSocketHandler) MakeFilePath(filename string) string {
|
||||
cleanPath := filepath.Clean(filename)
|
||||
return filepath.Join(ws.conf.FileTransferPath, cleanPath)
|
||||
}
|
||||
|
||||
func (ws *WebSocketHandler) sendFileTransferUpdate() {
|
||||
if !ws.state.FileTransferEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
files, err := utils.ListFiles(ws.conf.FileTransferPath)
|
||||
if err != nil {
|
||||
ws.logger.Err(err).Msg("unable to ls file transfer path")
|
||||
return
|
||||
}
|
||||
|
||||
message := message.FileList{
|
||||
Event: event.FILETRANSFER_LIST,
|
||||
Cwd: ws.conf.FileTransferPath,
|
||||
Files: files,
|
||||
}
|
||||
|
||||
if ws.state.UnprivFileTransferEnabled() {
|
||||
err = ws.sessions.Broadcast(message, nil)
|
||||
} else {
|
||||
err = ws.sessions.AdminBroadcast(message, nil)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
ws.logger.Err(err).Msg("unable to broadcast file list")
|
||||
}
|
||||
}
|
||||
|
||||
func (ws *WebSocketHandler) authenticate(r *http.Request) (bool, error) {
|
||||
passwords, ok := r.URL.Query()["password"]
|
||||
if !ok || len(passwords[0]) < 1 {
|
||||
@ -492,3 +425,28 @@ func (ws *WebSocketHandler) handle(connection *websocket.Conn, id string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// File transfer
|
||||
//
|
||||
|
||||
func (ws *WebSocketHandler) CanTransferFiles(password string) (bool, error) {
|
||||
if !ws.conf.FileTransferEnabled {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
isAdmin, err := ws.IsAdmin(password)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return isAdmin || !ws.state.IsLocked("file_transfer"), nil
|
||||
}
|
||||
|
||||
func (ws *WebSocketHandler) FileTransferPath(filename string) string {
|
||||
return ws.state.FileTransferPath(filename)
|
||||
}
|
||||
|
||||
func (ws *WebSocketHandler) FileTransferEnabled() bool {
|
||||
return ws.conf.FileTransferEnabled
|
||||
}
|
||||
|
Reference in New Issue
Block a user