fix(filetransfer): allow non-admin file uploads and downloads (#679)

* fix(filetransfer): remove auth.AdminsOnly middleware wrapper from routes

* fix(filetransfer): add config option to allow non-admin users to download and upload files.

* fix(filetransfer): implement user permissions for file download and upload in client.

---------

Co-authored-by: Miroslav Šedivý <sedivy.miro@gmail.com>
This commit is contained in:
Abdellatif Anaflous
2026-08-05 21:33:17 +01:00
committed by GitHub
parent e843961c37
commit 613647c72c
11 changed files with 97 additions and 20 deletions
@@ -109,9 +109,11 @@ type EmoteSend struct {
}
type FileTransferList struct {
Event string `json:"event"`
Cwd string `json:"cwd"`
Files []types.FileListItem `json:"files"`
Event string `json:"event"`
Cwd string `json:"cwd"`
UserDownload bool `json:"user_download"`
UserUpload bool `json:"user_upload"`
Files []types.FileListItem `json:"files"`
}
type Admin struct {
+5 -3
View File
@@ -607,9 +607,11 @@ func (s *session) wsToClient(msg []byte) error {
}
return s.toClient(&oldMessage.FileTransferList{
Event: oldEvent.FILETRANSFER_LIST,
Cwd: request.RootDir,
Files: files,
Event: oldEvent.FILETRANSFER_LIST,
Cwd: request.RootDir,
UserDownload: request.UserDownload,
UserUpload: request.UserUpload,
Files: files,
})
// Screen Events
@@ -12,6 +12,8 @@ type Config struct {
Enabled bool
RootDir string
RefreshInterval time.Duration
UserDownload bool
UserUpload bool
}
func (Config) Init(cmd *cobra.Command) error {
@@ -30,6 +32,16 @@ func (Config) Init(cmd *cobra.Command) error {
return err
}
cmd.PersistentFlags().Bool("filetransfer.user_download", false, "allow non-admin users to download files")
if err := viper.BindPFlag("filetransfer.user_download", cmd.PersistentFlags().Lookup("filetransfer.user_download")); err != nil {
return err
}
cmd.PersistentFlags().Bool("filetransfer.user_upload", false, "allow non-admin users to upload files")
if err := viper.BindPFlag("filetransfer.user_upload", cmd.PersistentFlags().Lookup("filetransfer.user_upload")); err != nil {
return err
}
// v2 config
cmd.PersistentFlags().Bool("file_transfer_enabled", false, "enable file transfer feature")
@@ -50,6 +62,8 @@ func (s *Config) Set() {
rootDir := viper.GetString("filetransfer.dir")
s.RootDir = filepath.Clean(rootDir)
s.RefreshInterval = viper.GetDuration("filetransfer.refresh_interval")
s.UserDownload = viper.GetBool("filetransfer.user_download")
s.UserUpload = viper.GetBool("filetransfer.user_upload")
// v2 config
@@ -103,9 +103,11 @@ func (m *Manager) broadcastUpdate() {
m.mu.RUnlock()
m.sessions.Broadcast(FILETRANSFER_UPDATE, Message{
Enabled: m.config.Enabled,
RootDir: m.config.RootDir,
Files: fileList,
Enabled: m.config.Enabled,
RootDir: m.config.RootDir,
UserDownload: m.config.UserDownload,
UserUpload: m.config.UserUpload,
Files: fileList,
})
}
@@ -115,9 +117,11 @@ func (m *Manager) sendUpdate(session types.Session) {
m.mu.RUnlock()
session.Send(FILETRANSFER_UPDATE, Message{
Enabled: m.config.Enabled,
RootDir: m.config.RootDir,
Files: fileList,
Enabled: m.config.Enabled,
RootDir: m.config.RootDir,
UserDownload: m.config.UserDownload,
UserUpload: m.config.UserUpload,
Files: fileList,
})
}
@@ -208,8 +212,8 @@ func (m *Manager) Shutdown() error {
}
func (m *Manager) Route(r types.Router) {
r.With(auth.AdminsOnly).Get("/", m.downloadFileHandler)
r.With(auth.AdminsOnly).Post("/", m.uploadFileHandler)
r.Get("/", m.downloadFileHandler)
r.Post("/", m.uploadFileHandler)
}
func (m *Manager) WebSocketHandler(session types.Session, msg types.WebSocketMessage) bool {
@@ -251,6 +255,10 @@ func (m *Manager) downloadFileHandler(w http.ResponseWriter, r *http.Request) er
return utils.HttpForbidden("file transfer is disabled")
}
if !session.Profile().IsAdmin && !m.config.UserDownload {
return utils.HttpForbidden("file download is not allowed for non-admin users")
}
filename := r.URL.Query().Get("filename")
badChars, err := regexp.MatchString(`(?m)\.\.(?:\/|$)`, filename)
if filename == "" || badChars || err != nil {
@@ -285,6 +293,10 @@ func (m *Manager) uploadFileHandler(w http.ResponseWriter, r *http.Request) erro
return utils.HttpForbidden("file transfer is disabled")
}
if !session.Profile().IsAdmin && !m.config.UserUpload {
return utils.HttpForbidden("file upload is not allowed for non-admin users")
}
err = r.ParseMultipartForm(multipartFormMaxMemory)
if err != nil || r.MultipartForm == nil {
return utils.HttpBadRequest().
@@ -11,9 +11,11 @@ const (
)
type Message struct {
Enabled bool `json:"enabled"`
RootDir string `json:"root_dir"`
Files []Item `json:"files"`
Enabled bool `json:"enabled"`
RootDir string `json:"root_dir"`
UserDownload bool `json:"user_download"`
UserUpload bool `json:"user_upload"`
Files []Item `json:"files"`
}
type ItemType string