ソースを参照

portable mode: add WebDAV support

Nicola Murino 5 年 前
コミット
bbc8c091e6
5 ファイル変更62 行追加9 行削除
  1. 19 2
      cmd/portable.go
  2. 6 0
      docs/portable-mode.md
  3. 2 0
      httpd/httpd_test.go
  4. 34 7
      service/service_portable.go
  5. 1 0
      webdavd/webdavd_test.go

+ 19 - 2
cmd/portable.go

@@ -53,6 +53,9 @@ var (
 	portableFTPDPort             int
 	portableFTPDPort             int
 	portableFTPSCert             string
 	portableFTPSCert             string
 	portableFTPSKey              string
 	portableFTPSKey              string
+	portableWebDAVPort           int
+	portableWebDAVCert           string
+	portableWebDAVKey            string
 	portableCmd                  = &cobra.Command{
 	portableCmd                  = &cobra.Command{
 		Use:   "portable",
 		Use:   "portable",
 		Short: "Serve a single directory",
 		Short: "Serve a single directory",
@@ -100,6 +103,14 @@ Please take a look at the usage below to customize the serving parameters`,
 					os.Exit(1)
 					os.Exit(1)
 				}
 				}
 			}
 			}
+			if portableWebDAVPort > 0 && len(portableWebDAVCert) > 0 && len(portableWebDAVKey) > 0 {
+				_, err := common.NewCertManager(portableWebDAVCert, portableWebDAVKey, "WebDAV portable")
+				if err != nil {
+					fmt.Printf("Unable to load WebDAV key pair, cert file %#v key file %#v error: %v\n",
+						portableWebDAVCert, portableWebDAVKey, err)
+					os.Exit(1)
+				}
+			}
 			service := service.Service{
 			service := service.Service{
 				ConfigDir:     filepath.Clean(defaultConfigDir),
 				ConfigDir:     filepath.Clean(defaultConfigDir),
 				ConfigFile:    defaultConfigName,
 				ConfigFile:    defaultConfigName,
@@ -145,8 +156,8 @@ Please take a look at the usage below to customize the serving parameters`,
 					},
 					},
 				},
 				},
 			}
 			}
-			if err := service.StartPortableMode(portableSFTPDPort, portableFTPDPort, portableSSHCommands, portableAdvertiseService,
-				portableAdvertiseCredentials, portableFTPSCert, portableFTPSKey); err == nil {
+			if err := service.StartPortableMode(portableSFTPDPort, portableFTPDPort, portableWebDAVPort, portableSSHCommands, portableAdvertiseService,
+				portableAdvertiseCredentials, portableFTPSCert, portableFTPSKey, portableWebDAVCert, portableWebDAVKey); err == nil {
 				service.Wait()
 				service.Wait()
 				if service.Error == nil {
 				if service.Error == nil {
 					os.Exit(0)
 					os.Exit(0)
@@ -166,6 +177,8 @@ relative to the current directory
 `)
 `)
 	portableCmd.Flags().IntVarP(&portableSFTPDPort, "sftpd-port", "s", 0, "0 means a random unprivileged port")
 	portableCmd.Flags().IntVarP(&portableSFTPDPort, "sftpd-port", "s", 0, "0 means a random unprivileged port")
 	portableCmd.Flags().IntVar(&portableFTPDPort, "ftpd-port", -1, `0 means a random unprivileged port,
 	portableCmd.Flags().IntVar(&portableFTPDPort, "ftpd-port", -1, `0 means a random unprivileged port,
+< 0 disabled`)
+	portableCmd.Flags().IntVar(&portableWebDAVPort, "webdav-port", -1, `0 means a random unprivileged port,
 < 0 disabled`)
 < 0 disabled`)
 	portableCmd.Flags().StringSliceVarP(&portableSSHCommands, "ssh-commands", "c", sftpd.GetDefaultSSHCommands(),
 	portableCmd.Flags().StringSliceVarP(&portableSSHCommands, "ssh-commands", "c", sftpd.GetDefaultSSHCommands(),
 		`SSH commands to enable.
 		`SSH commands to enable.
@@ -228,6 +241,10 @@ a JSON credentials file, 1 automatic
 `)
 `)
 	portableCmd.Flags().StringVar(&portableFTPSCert, "ftpd-cert", "", "Path to the certificate file for FTPS")
 	portableCmd.Flags().StringVar(&portableFTPSCert, "ftpd-cert", "", "Path to the certificate file for FTPS")
 	portableCmd.Flags().StringVar(&portableFTPSKey, "ftpd-key", "", "Path to the key file for FTPS")
 	portableCmd.Flags().StringVar(&portableFTPSKey, "ftpd-key", "", "Path to the key file for FTPS")
+	portableCmd.Flags().StringVar(&portableWebDAVCert, "webdav-cert", "", `Path to the certificate file for WebDAV
+over HTTPS`)
+	portableCmd.Flags().StringVar(&portableWebDAVKey, "webdav-key", "", `Path to the key file for WebDAV over
+HTTPS`)
 	rootCmd.AddCommand(portableCmd)
 	rootCmd.AddCommand(portableCmd)
 }
 }
 
 

+ 6 - 0
docs/portable-mode.md

@@ -78,6 +78,12 @@ Flags:
                                           (default [md5sum,sha1sum,cd,pwd,scp])
                                           (default [md5sum,sha1sum,cd,pwd,scp])
   -u, --username string                  Leave empty to use an auto generated
   -u, --username string                  Leave empty to use an auto generated
                                          value
                                          value
+      --webdav-cert string               Path to the certificate file for WebDAV
+                                         over HTTPS
+      --webdav-key string                Path to the key file for WebDAV over
+                                         HTTPS
+      --webdav-port int                  0 means a random unprivileged port,
+                                         < 0 disabled (default -1)
 ```
 ```
 
 
 In portable mode, SFTPGo can advertise the SFTP/FTP services and, optionally, the credentials via multicast DNS, so there is a standard way to discover the service and to automatically connect to it.
 In portable mode, SFTPGo can advertise the SFTP/FTP services and, optionally, the credentials via multicast DNS, so there is a standard way to discover the service and to automatically connect to it.

+ 2 - 0
httpd/httpd_test.go

@@ -355,8 +355,10 @@ func TestAddUserInvalidFilters(t *testing.T) {
 	u.Filters.FileExtensions = nil
 	u.Filters.FileExtensions = nil
 	u.Filters.DeniedProtocols = []string{"invalid"}
 	u.Filters.DeniedProtocols = []string{"invalid"}
 	_, _, err = httpd.AddUser(u, http.StatusBadRequest)
 	_, _, err = httpd.AddUser(u, http.StatusBadRequest)
+	assert.NoError(t, err)
 	u.Filters.DeniedProtocols = dataprovider.ValidProtocols
 	u.Filters.DeniedProtocols = dataprovider.ValidProtocols
 	_, _, err = httpd.AddUser(u, http.StatusBadRequest)
 	_, _, err = httpd.AddUser(u, http.StatusBadRequest)
+	assert.NoError(t, err)
 }
 }
 
 
 func TestAddUserInvalidFsConfig(t *testing.T) {
 func TestAddUserInvalidFsConfig(t *testing.T) {

+ 34 - 7
service/service_portable.go

@@ -23,8 +23,8 @@ import (
 )
 )
 
 
 // StartPortableMode starts the service in portable mode
 // StartPortableMode starts the service in portable mode
-func (s *Service) StartPortableMode(sftpdPort, ftpPort int, enabledSSHCommands []string, advertiseService, advertiseCredentials bool,
-	ftpsCert, ftpsKey string) error {
+func (s *Service) StartPortableMode(sftpdPort, ftpPort, webdavPort int, enabledSSHCommands []string, advertiseService, advertiseCredentials bool,
+	ftpsCert, ftpsKey, webDavCert, webDavKey string) error {
 	if s.PortableMode != 1 {
 	if s.PortableMode != 1 {
 		return fmt.Errorf("service is not configured for portable mode")
 		return fmt.Errorf("service is not configured for portable mode")
 	}
 	}
@@ -81,6 +81,18 @@ func (s *Service) StartPortableMode(sftpdPort, ftpPort int, enabledSSHCommands [
 		config.SetFTPDConfig(ftpConf)
 		config.SetFTPDConfig(ftpConf)
 	}
 	}
 
 
+	if webdavPort > 0 {
+		webDavConf := config.GetWebDAVDConfig()
+		if webdavPort > 0 {
+			webDavConf.BindPort = webdavPort
+		} else {
+			webDavConf.BindPort = 49152 + rand.Intn(15000)
+		}
+		webDavConf.CertificateFile = webDavCert
+		webDavConf.CertificateKeyFile = webDavKey
+		config.SetWebDAVDConfig(webDavConf)
+	}
+
 	err = s.Start()
 	err = s.Start()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -88,17 +100,32 @@ func (s *Service) StartPortableMode(sftpdPort, ftpPort int, enabledSSHCommands [
 
 
 	s.advertiseServices(advertiseService, advertiseCredentials)
 	s.advertiseServices(advertiseService, advertiseCredentials)
 
 
-	var ftpInfo string
-	if config.GetFTPDConfig().BindPort > 0 {
-		ftpInfo = fmt.Sprintf("FTP port: %v", config.GetFTPDConfig().BindPort)
-	}
 	logger.InfoToConsole("Portable mode ready, SFTP port: %v, user: %#v, password: %#v, public keys: %v, directory: %#v, "+
 	logger.InfoToConsole("Portable mode ready, SFTP port: %v, user: %#v, password: %#v, public keys: %v, directory: %#v, "+
 		"permissions: %+v, enabled ssh commands: %v file extensions filters: %+v %v", sftpdConf.BindPort, s.PortableUser.Username,
 		"permissions: %+v, enabled ssh commands: %v file extensions filters: %+v %v", sftpdConf.BindPort, s.PortableUser.Username,
 		printablePassword, s.PortableUser.PublicKeys, s.getPortableDirToServe(), s.PortableUser.Permissions,
 		printablePassword, s.PortableUser.PublicKeys, s.getPortableDirToServe(), s.PortableUser.Permissions,
-		sftpdConf.EnabledSSHCommands, s.PortableUser.Filters.FileExtensions, ftpInfo)
+		sftpdConf.EnabledSSHCommands, s.PortableUser.Filters.FileExtensions, s.getServiceOptionalInfoString())
 	return nil
 	return nil
 }
 }
 
 
+func (s *Service) getServiceOptionalInfoString() string {
+	var info strings.Builder
+	if config.GetFTPDConfig().BindPort > 0 {
+		info.WriteString(fmt.Sprintf("FTP port: %v ", config.GetFTPDConfig().BindPort))
+	}
+	if config.GetWebDAVDConfig().BindPort > 0 {
+		if info.Len() == 0 {
+			info.WriteString(" ")
+		}
+		scheme := "http"
+		if len(config.GetWebDAVDConfig().CertificateFile) > 0 && len(config.GetWebDAVDConfig().CertificateKeyFile) > 0 {
+			scheme = "https"
+		}
+		info.WriteString(fmt.Sprintf("WebDAV URL: %v://<your IP>:%v/%v",
+			scheme, config.GetWebDAVDConfig().BindPort, s.PortableUser.Username))
+	}
+	return info.String()
+}
+
 func (s *Service) advertiseServices(advertiseService, advertiseCredentials bool) {
 func (s *Service) advertiseServices(advertiseService, advertiseCredentials bool) {
 	var mDNSServiceSFTP *zeroconf.Server
 	var mDNSServiceSFTP *zeroconf.Server
 	var mDNSServiceFTP *zeroconf.Server
 	var mDNSServiceFTP *zeroconf.Server

+ 1 - 0
webdavd/webdavd_test.go

@@ -576,6 +576,7 @@ func TestDeniedProtocols(t *testing.T) {
 
 
 	user.Filters.DeniedProtocols = []string{common.ProtocolSSH, common.ProtocolFTP}
 	user.Filters.DeniedProtocols = []string{common.ProtocolSSH, common.ProtocolFTP}
 	user, _, err = httpd.UpdateUser(user, http.StatusOK)
 	user, _, err = httpd.UpdateUser(user, http.StatusOK)
+	assert.NoError(t, err)
 	client = getWebDavClient(user)
 	client = getWebDavClient(user)
 	assert.NoError(t, checkBasicFunc(client))
 	assert.NoError(t, checkBasicFunc(client))