From 52eb7392c4ba65387a3a2de2b5018362b510be7e Mon Sep 17 00:00:00 2001 From: Simon Frei Date: Thu, 3 Jun 2021 15:09:35 +0200 Subject: [PATCH] lib/api, lib/config: Apply defaults before deserializing json (#7690) --- .../syncthing/core/syncthingController.js | 2 +- lib/api/confighandler.go | 73 +++++++++++-------- lib/config/config.go | 33 ++++++++- 3 files changed, 74 insertions(+), 34 deletions(-) diff --git a/gui/default/syncthing/core/syncthingController.js b/gui/default/syncthing/core/syncthingController.js index 4b6915879..ababd2c79 100755 --- a/gui/default/syncthing/core/syncthingController.js +++ b/gui/default/syncthing/core/syncthingController.js @@ -2129,7 +2129,7 @@ angular.module('syncthing.core') folderCfg.versioning.params.command = '' + folderCfg._guiVersioning.externalCommand; break; default: - delete folderCfg.versioning; + folderCfg.versioning = {type: ''}; } delete folderCfg._guiVersioning; diff --git a/lib/api/confighandler.go b/lib/api/confighandler.go index 45bc6613d..8f03acfdf 100644 --- a/lib/api/confighandler.go +++ b/lib/api/confighandler.go @@ -17,6 +17,7 @@ import ( "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/protocol" + "github.com/syncthing/syncthing/lib/util" ) type configMuxBuilder struct { @@ -63,10 +64,15 @@ func (c *configMuxBuilder) registerFolders(path string) { }) c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) { - var folders []config.FolderConfiguration - if err := unmarshalTo(r.Body, &folders); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return + data, err := unmarshalToRawMessages(r.Body) + folders := make([]config.FolderConfiguration, len(data)) + defaultFolder := c.cfg.DefaultFolder() + for i, bs := range data { + folders[i] = defaultFolder.Copy() + if err := json.Unmarshal(bs, &folders[i]); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } } waiter, err := c.cfg.Modify(func(cfg *config.Configuration) { cfg.SetFolders(folders) @@ -79,7 +85,7 @@ func (c *configMuxBuilder) registerFolders(path string) { }) c.HandlerFunc(http.MethodPost, path, func(w http.ResponseWriter, r *http.Request) { - c.adjustFolder(w, r, config.FolderConfiguration{}, false) + c.adjustFolder(w, r, c.cfg.DefaultFolder(), false) }) } @@ -89,10 +95,15 @@ func (c *configMuxBuilder) registerDevices(path string) { }) c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) { - var devices []config.DeviceConfiguration - if err := unmarshalTo(r.Body, &devices); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return + data, err := unmarshalToRawMessages(r.Body) + devices := make([]config.DeviceConfiguration, len(data)) + defaultDevice := c.cfg.DefaultDevice() + for i, bs := range data { + devices[i] = defaultDevice.Copy() + if err := json.Unmarshal(bs, &devices[i]); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } } waiter, err := c.cfg.Modify(func(cfg *config.Configuration) { cfg.SetDevices(devices) @@ -105,19 +116,7 @@ func (c *configMuxBuilder) registerDevices(path string) { }) c.HandlerFunc(http.MethodPost, path, func(w http.ResponseWriter, r *http.Request) { - var device config.DeviceConfiguration - if err := unmarshalTo(r.Body, &device); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - waiter, err := c.cfg.Modify(func(cfg *config.Configuration) { - cfg.SetDevice(device) - }) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - c.finish(w, waiter) + c.adjustDevice(w, r, c.cfg.DefaultDevice(), false) }) } @@ -132,7 +131,7 @@ func (c *configMuxBuilder) registerFolder(path string) { }) c.Handle(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { - c.adjustFolder(w, r, config.FolderConfiguration{}, false) + c.adjustFolder(w, r, c.cfg.DefaultFolder(), false) }) c.Handle(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { @@ -176,7 +175,7 @@ func (c *configMuxBuilder) registerDevice(path string) { }) c.Handle(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { - c.adjustDevice(w, r, config.DeviceConfiguration{}, false) + c.adjustDevice(w, r, c.cfg.DefaultDevice(), false) }) c.Handle(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { @@ -206,7 +205,9 @@ func (c *configMuxBuilder) registerDefaultFolder(path string) { }) c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) { - c.adjustFolder(w, r, config.FolderConfiguration{}, true) + var cfg config.FolderConfiguration + util.SetDefaults(&cfg) + c.adjustFolder(w, r, cfg, true) }) c.HandlerFunc(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request) { @@ -220,7 +221,9 @@ func (c *configMuxBuilder) registerDefaultDevice(path string) { }) c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) { - c.adjustDevice(w, r, config.DeviceConfiguration{}, true) + var cfg config.DeviceConfiguration + util.SetDefaults(&cfg) + c.adjustDevice(w, r, cfg, true) }) c.HandlerFunc(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request) { @@ -234,7 +237,9 @@ func (c *configMuxBuilder) registerOptions(path string) { }) c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) { - c.adjustOptions(w, r, config.OptionsConfiguration{}) + var cfg config.OptionsConfiguration + util.SetDefaults(&cfg) + c.adjustOptions(w, r, cfg) }) c.HandlerFunc(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request) { @@ -248,7 +253,9 @@ func (c *configMuxBuilder) registerLDAP(path string) { }) c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) { - c.adjustLDAP(w, r, config.LDAPConfiguration{}) + var cfg config.LDAPConfiguration + util.SetDefaults(&cfg) + c.adjustLDAP(w, r, cfg) }) c.HandlerFunc(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request) { @@ -262,7 +269,9 @@ func (c *configMuxBuilder) registerGUI(path string) { }) c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) { - c.adjustGUI(w, r, config.GUIConfiguration{}) + var cfg config.GUIConfiguration + util.SetDefaults(&cfg) + c.adjustGUI(w, r, cfg) }) c.HandlerFunc(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request) { @@ -403,6 +412,12 @@ func unmarshalTo(body io.ReadCloser, to interface{}) error { return json.Unmarshal(bs, to) } +func unmarshalToRawMessages(body io.ReadCloser) ([]json.RawMessage, error) { + var data []json.RawMessage + err := unmarshalTo(body, &data) + return data, err +} + func checkGUIPassword(oldPassword, newPassword string) (string, error) { if newPassword == oldPassword { return newPassword, nil diff --git a/lib/config/config.go b/lib/config/config.go index 93e52514d..c4db59ebc 100644 --- a/lib/config/config.go +++ b/lib/config/config.go @@ -163,19 +163,44 @@ func ReadXML(r io.Reader, myID protocol.DeviceID) (Configuration, int, error) { } func ReadJSON(r io.Reader, myID protocol.DeviceID) (Configuration, error) { - var cfg Configuration - - util.SetDefaults(&cfg) - bs, err := ioutil.ReadAll(r) if err != nil { return Configuration{}, err } + var cfg Configuration + + util.SetDefaults(&cfg) + if err := json.Unmarshal(bs, &cfg); err != nil { return Configuration{}, err } + // Unmarshal list of devices and folders separately to set defaults + var rawFoldersDevices struct { + Folders []json.RawMessage + Devices []json.RawMessage + } + if err := json.Unmarshal(bs, &rawFoldersDevices); err != nil { + return Configuration{}, err + } + + cfg.Folders = make([]FolderConfiguration, len(rawFoldersDevices.Folders)) + for i, bs := range rawFoldersDevices.Folders { + cfg.Folders[i] = cfg.Defaults.Folder.Copy() + if err := json.Unmarshal(bs, &cfg.Folders[i]); err != nil { + return Configuration{}, err + } + } + + cfg.Devices = make([]DeviceConfiguration, len(rawFoldersDevices.Devices)) + for i, bs := range rawFoldersDevices.Devices { + cfg.Devices[i] = cfg.Defaults.Device.Copy() + if err := json.Unmarshal(bs, &cfg.Devices[i]); err != nil { + return Configuration{}, err + } + } + if err := cfg.prepare(myID); err != nil { return Configuration{}, err }