connection_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. package common
  2. import (
  3. "os"
  4. "path"
  5. "path/filepath"
  6. "runtime"
  7. "testing"
  8. "time"
  9. "github.com/pkg/sftp"
  10. "github.com/stretchr/testify/assert"
  11. "github.com/drakkan/sftpgo/v2/dataprovider"
  12. "github.com/drakkan/sftpgo/v2/sdk"
  13. "github.com/drakkan/sftpgo/v2/vfs"
  14. )
  15. // MockOsFs mockable OsFs
  16. type MockOsFs struct {
  17. vfs.Fs
  18. hasVirtualFolders bool
  19. }
  20. // Name returns the name for the Fs implementation
  21. func (fs MockOsFs) Name() string {
  22. return "mockOsFs"
  23. }
  24. // HasVirtualFolders returns true if folders are emulated
  25. func (fs MockOsFs) HasVirtualFolders() bool {
  26. return fs.hasVirtualFolders
  27. }
  28. func (fs MockOsFs) IsUploadResumeSupported() bool {
  29. return !fs.hasVirtualFolders
  30. }
  31. func newMockOsFs(hasVirtualFolders bool, connectionID, rootDir string) vfs.Fs {
  32. return &MockOsFs{
  33. Fs: vfs.NewOsFs(connectionID, rootDir, ""),
  34. hasVirtualFolders: hasVirtualFolders,
  35. }
  36. }
  37. func TestRemoveErrors(t *testing.T) {
  38. mappedPath := filepath.Join(os.TempDir(), "map")
  39. homePath := filepath.Join(os.TempDir(), "home")
  40. user := dataprovider.User{
  41. BaseUser: sdk.BaseUser{
  42. Username: "remove_errors_user",
  43. HomeDir: homePath,
  44. },
  45. VirtualFolders: []vfs.VirtualFolder{
  46. {
  47. BaseVirtualFolder: vfs.BaseVirtualFolder{
  48. Name: filepath.Base(mappedPath),
  49. MappedPath: mappedPath,
  50. },
  51. VirtualPath: "/virtualpath",
  52. },
  53. },
  54. }
  55. user.Permissions = make(map[string][]string)
  56. user.Permissions["/"] = []string{dataprovider.PermAny}
  57. fs := vfs.NewOsFs("", os.TempDir(), "")
  58. conn := NewBaseConnection("", ProtocolFTP, "", "", user)
  59. err := conn.IsRemoveDirAllowed(fs, mappedPath, "/virtualpath1")
  60. if assert.Error(t, err) {
  61. assert.Contains(t, err.Error(), "permission denied")
  62. }
  63. err = conn.RemoveFile(fs, filepath.Join(homePath, "missing_file"), "/missing_file",
  64. vfs.NewFileInfo("info", false, 100, time.Now(), false))
  65. assert.Error(t, err)
  66. }
  67. func TestSetStatMode(t *testing.T) {
  68. oldSetStatMode := Config.SetstatMode
  69. Config.SetstatMode = 1
  70. fakePath := "fake path"
  71. user := dataprovider.User{
  72. BaseUser: sdk.BaseUser{
  73. HomeDir: os.TempDir(),
  74. },
  75. }
  76. user.Permissions = make(map[string][]string)
  77. user.Permissions["/"] = []string{dataprovider.PermAny}
  78. fs := newMockOsFs(true, "", user.GetHomeDir())
  79. conn := NewBaseConnection("", ProtocolWebDAV, "", "", user)
  80. err := conn.handleChmod(fs, fakePath, fakePath, nil)
  81. assert.NoError(t, err)
  82. err = conn.handleChown(fs, fakePath, fakePath, nil)
  83. assert.NoError(t, err)
  84. err = conn.handleChtimes(fs, fakePath, fakePath, nil)
  85. assert.NoError(t, err)
  86. Config.SetstatMode = 2
  87. err = conn.handleChmod(fs, fakePath, fakePath, nil)
  88. assert.NoError(t, err)
  89. Config.SetstatMode = oldSetStatMode
  90. }
  91. func TestRecursiveRenameWalkError(t *testing.T) {
  92. fs := vfs.NewOsFs("", os.TempDir(), "")
  93. conn := NewBaseConnection("", ProtocolWebDAV, "", "", dataprovider.User{})
  94. err := conn.checkRecursiveRenameDirPermissions(fs, fs, "/source", "/target")
  95. assert.ErrorIs(t, err, os.ErrNotExist)
  96. }
  97. func TestCrossRenameFsErrors(t *testing.T) {
  98. fs := vfs.NewOsFs("", os.TempDir(), "")
  99. conn := NewBaseConnection("", ProtocolWebDAV, "", "", dataprovider.User{})
  100. res := conn.hasSpaceForCrossRename(fs, vfs.QuotaCheckResult{}, 1, "missingsource")
  101. assert.False(t, res)
  102. if runtime.GOOS != osWindows {
  103. dirPath := filepath.Join(os.TempDir(), "d")
  104. err := os.Mkdir(dirPath, os.ModePerm)
  105. assert.NoError(t, err)
  106. err = os.Chmod(dirPath, 0001)
  107. assert.NoError(t, err)
  108. res = conn.hasSpaceForCrossRename(fs, vfs.QuotaCheckResult{}, 1, dirPath)
  109. assert.False(t, res)
  110. err = os.Chmod(dirPath, os.ModePerm)
  111. assert.NoError(t, err)
  112. err = os.Remove(dirPath)
  113. assert.NoError(t, err)
  114. }
  115. }
  116. func TestRenameVirtualFolders(t *testing.T) {
  117. vdir := "/avdir"
  118. u := dataprovider.User{}
  119. u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
  120. BaseVirtualFolder: vfs.BaseVirtualFolder{
  121. Name: "name",
  122. MappedPath: "mappedPath",
  123. },
  124. VirtualPath: vdir,
  125. })
  126. fs := vfs.NewOsFs("", os.TempDir(), "")
  127. conn := NewBaseConnection("", ProtocolFTP, "", "", u)
  128. res := conn.isRenamePermitted(fs, fs, "source", "target", vdir, "vdirtarget", nil)
  129. assert.False(t, res)
  130. }
  131. func TestRenamePerms(t *testing.T) {
  132. src := "source"
  133. target := "target"
  134. u := dataprovider.User{}
  135. u.Permissions = map[string][]string{}
  136. u.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermCreateSymlinks,
  137. dataprovider.PermDeleteFiles}
  138. conn := NewBaseConnection("", ProtocolSFTP, "", "", u)
  139. assert.False(t, conn.hasRenamePerms(src, target, nil))
  140. u.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermCreateSymlinks,
  141. dataprovider.PermDeleteFiles, dataprovider.PermDeleteDirs}
  142. assert.True(t, conn.hasRenamePerms(src, target, nil))
  143. u.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermDeleteFiles,
  144. dataprovider.PermDeleteDirs}
  145. assert.False(t, conn.hasRenamePerms(src, target, nil))
  146. info := vfs.NewFileInfo(src, true, 0, time.Now(), false)
  147. u.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermDeleteFiles}
  148. assert.False(t, conn.hasRenamePerms(src, target, info))
  149. u.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermDeleteDirs}
  150. assert.True(t, conn.hasRenamePerms(src, target, info))
  151. u.Permissions["/"] = []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDeleteDirs}
  152. assert.False(t, conn.hasRenamePerms(src, target, info))
  153. }
  154. func TestUpdateQuotaAfterRename(t *testing.T) {
  155. user := dataprovider.User{
  156. BaseUser: sdk.BaseUser{
  157. Username: userTestUsername,
  158. HomeDir: filepath.Join(os.TempDir(), "home"),
  159. },
  160. }
  161. mappedPath := filepath.Join(os.TempDir(), "vdir")
  162. user.Permissions = make(map[string][]string)
  163. user.Permissions["/"] = []string{dataprovider.PermAny}
  164. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  165. BaseVirtualFolder: vfs.BaseVirtualFolder{
  166. MappedPath: mappedPath,
  167. },
  168. VirtualPath: "/vdir",
  169. QuotaFiles: -1,
  170. QuotaSize: -1,
  171. })
  172. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  173. BaseVirtualFolder: vfs.BaseVirtualFolder{
  174. MappedPath: mappedPath,
  175. },
  176. VirtualPath: "/vdir1",
  177. QuotaFiles: -1,
  178. QuotaSize: -1,
  179. })
  180. err := os.MkdirAll(user.GetHomeDir(), os.ModePerm)
  181. assert.NoError(t, err)
  182. err = os.MkdirAll(mappedPath, os.ModePerm)
  183. assert.NoError(t, err)
  184. fs, err := user.GetFilesystem("id")
  185. assert.NoError(t, err)
  186. c := NewBaseConnection("", ProtocolSFTP, "", "", user)
  187. request := sftp.NewRequest("Rename", "/testfile")
  188. if runtime.GOOS != osWindows {
  189. request.Filepath = "/dir"
  190. request.Target = path.Join("/vdir", "dir")
  191. testDirPath := filepath.Join(mappedPath, "dir")
  192. err := os.MkdirAll(testDirPath, os.ModePerm)
  193. assert.NoError(t, err)
  194. err = os.Chmod(testDirPath, 0001)
  195. assert.NoError(t, err)
  196. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, testDirPath, 0)
  197. assert.Error(t, err)
  198. err = os.Chmod(testDirPath, os.ModePerm)
  199. assert.NoError(t, err)
  200. }
  201. testFile1 := "/testfile1"
  202. request.Target = testFile1
  203. request.Filepath = path.Join("/vdir", "file")
  204. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 0)
  205. assert.Error(t, err)
  206. err = os.WriteFile(filepath.Join(mappedPath, "file"), []byte("test content"), os.ModePerm)
  207. assert.NoError(t, err)
  208. request.Filepath = testFile1
  209. request.Target = path.Join("/vdir", "file")
  210. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 12)
  211. assert.NoError(t, err)
  212. err = os.WriteFile(filepath.Join(user.GetHomeDir(), "testfile1"), []byte("test content"), os.ModePerm)
  213. assert.NoError(t, err)
  214. request.Target = testFile1
  215. request.Filepath = path.Join("/vdir", "file")
  216. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 12)
  217. assert.NoError(t, err)
  218. request.Target = path.Join("/vdir1", "file")
  219. request.Filepath = path.Join("/vdir", "file")
  220. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 12)
  221. assert.NoError(t, err)
  222. err = os.RemoveAll(mappedPath)
  223. assert.NoError(t, err)
  224. err = os.RemoveAll(user.GetHomeDir())
  225. assert.NoError(t, err)
  226. }
  227. func TestErrorsMapping(t *testing.T) {
  228. fs := vfs.NewOsFs("", os.TempDir(), "")
  229. conn := NewBaseConnection("", ProtocolSFTP, "", "", dataprovider.User{BaseUser: sdk.BaseUser{HomeDir: os.TempDir()}})
  230. for _, protocol := range supportedProtocols {
  231. conn.SetProtocol(protocol)
  232. err := conn.GetFsError(fs, os.ErrNotExist)
  233. if protocol == ProtocolSFTP {
  234. assert.ErrorIs(t, err, sftp.ErrSSHFxNoSuchFile)
  235. } else if protocol == ProtocolWebDAV || protocol == ProtocolFTP || protocol == ProtocolHTTP {
  236. assert.EqualError(t, err, os.ErrNotExist.Error())
  237. } else {
  238. assert.EqualError(t, err, ErrNotExist.Error())
  239. }
  240. err = conn.GetFsError(fs, os.ErrPermission)
  241. if protocol == ProtocolSFTP {
  242. assert.EqualError(t, err, sftp.ErrSSHFxPermissionDenied.Error())
  243. } else {
  244. assert.EqualError(t, err, ErrPermissionDenied.Error())
  245. }
  246. err = conn.GetFsError(fs, os.ErrClosed)
  247. if protocol == ProtocolSFTP {
  248. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  249. assert.Contains(t, err.Error(), os.ErrClosed.Error())
  250. } else {
  251. assert.EqualError(t, err, ErrGenericFailure.Error())
  252. }
  253. err = conn.GetFsError(fs, ErrPermissionDenied)
  254. if protocol == ProtocolSFTP {
  255. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  256. assert.Contains(t, err.Error(), ErrPermissionDenied.Error())
  257. } else {
  258. assert.EqualError(t, err, ErrPermissionDenied.Error())
  259. }
  260. err = conn.GetFsError(fs, vfs.ErrVfsUnsupported)
  261. if protocol == ProtocolSFTP {
  262. assert.EqualError(t, err, sftp.ErrSSHFxOpUnsupported.Error())
  263. } else {
  264. assert.EqualError(t, err, ErrOpUnsupported.Error())
  265. }
  266. err = conn.GetFsError(fs, vfs.ErrStorageSizeUnavailable)
  267. if protocol == ProtocolSFTP {
  268. assert.ErrorIs(t, err, sftp.ErrSSHFxOpUnsupported)
  269. assert.Contains(t, err.Error(), vfs.ErrStorageSizeUnavailable.Error())
  270. } else {
  271. assert.EqualError(t, err, vfs.ErrStorageSizeUnavailable.Error())
  272. }
  273. err = conn.GetQuotaExceededError()
  274. assert.True(t, conn.IsQuotaExceededError(err))
  275. err = conn.GetFsError(fs, nil)
  276. assert.NoError(t, err)
  277. err = conn.GetOpUnsupportedError()
  278. if protocol == ProtocolSFTP {
  279. assert.EqualError(t, err, sftp.ErrSSHFxOpUnsupported.Error())
  280. } else {
  281. assert.EqualError(t, err, ErrOpUnsupported.Error())
  282. }
  283. }
  284. }
  285. func TestMaxWriteSize(t *testing.T) {
  286. permissions := make(map[string][]string)
  287. permissions["/"] = []string{dataprovider.PermAny}
  288. user := dataprovider.User{
  289. BaseUser: sdk.BaseUser{
  290. Username: userTestUsername,
  291. Permissions: permissions,
  292. HomeDir: filepath.Clean(os.TempDir()),
  293. },
  294. }
  295. fs, err := user.GetFilesystem("123")
  296. assert.NoError(t, err)
  297. conn := NewBaseConnection("", ProtocolFTP, "", "", user)
  298. quotaResult := vfs.QuotaCheckResult{
  299. HasSpace: true,
  300. }
  301. size, err := conn.GetMaxWriteSize(quotaResult, false, 0, fs.IsUploadResumeSupported())
  302. assert.NoError(t, err)
  303. assert.Equal(t, int64(0), size)
  304. conn.User.Filters.MaxUploadFileSize = 100
  305. size, err = conn.GetMaxWriteSize(quotaResult, false, 0, fs.IsUploadResumeSupported())
  306. assert.NoError(t, err)
  307. assert.Equal(t, int64(100), size)
  308. quotaResult.QuotaSize = 1000
  309. size, err = conn.GetMaxWriteSize(quotaResult, false, 50, fs.IsUploadResumeSupported())
  310. assert.NoError(t, err)
  311. assert.Equal(t, int64(100), size)
  312. quotaResult.QuotaSize = 1000
  313. quotaResult.UsedSize = 990
  314. size, err = conn.GetMaxWriteSize(quotaResult, false, 50, fs.IsUploadResumeSupported())
  315. assert.NoError(t, err)
  316. assert.Equal(t, int64(60), size)
  317. quotaResult.QuotaSize = 0
  318. quotaResult.UsedSize = 0
  319. size, err = conn.GetMaxWriteSize(quotaResult, true, 100, fs.IsUploadResumeSupported())
  320. assert.True(t, conn.IsQuotaExceededError(err))
  321. assert.Equal(t, int64(0), size)
  322. size, err = conn.GetMaxWriteSize(quotaResult, true, 10, fs.IsUploadResumeSupported())
  323. assert.NoError(t, err)
  324. assert.Equal(t, int64(90), size)
  325. fs = newMockOsFs(true, fs.ConnectionID(), user.GetHomeDir())
  326. size, err = conn.GetMaxWriteSize(quotaResult, true, 100, fs.IsUploadResumeSupported())
  327. assert.EqualError(t, err, ErrOpUnsupported.Error())
  328. assert.Equal(t, int64(0), size)
  329. }