internal_test.go 62 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192
  1. package sftpd
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "net"
  9. "os"
  10. "os/exec"
  11. "path"
  12. "path/filepath"
  13. "runtime"
  14. "sync"
  15. "testing"
  16. "time"
  17. "github.com/eikenb/pipeat"
  18. "github.com/pkg/sftp"
  19. "github.com/stretchr/testify/assert"
  20. "golang.org/x/crypto/ssh"
  21. "github.com/drakkan/sftpgo/dataprovider"
  22. "github.com/drakkan/sftpgo/utils"
  23. "github.com/drakkan/sftpgo/vfs"
  24. )
  25. const osWindows = "windows"
  26. type MockChannel struct {
  27. Buffer *bytes.Buffer
  28. StdErrBuffer *bytes.Buffer
  29. ReadError error
  30. WriteError error
  31. ShortWriteErr bool
  32. }
  33. func (c *MockChannel) Read(data []byte) (int, error) {
  34. if c.ReadError != nil {
  35. return 0, c.ReadError
  36. }
  37. return c.Buffer.Read(data)
  38. }
  39. func (c *MockChannel) Write(data []byte) (int, error) {
  40. if c.WriteError != nil {
  41. return 0, c.WriteError
  42. }
  43. if c.ShortWriteErr {
  44. return 0, nil
  45. }
  46. return c.Buffer.Write(data)
  47. }
  48. func (c *MockChannel) Close() error {
  49. return nil
  50. }
  51. func (c *MockChannel) CloseWrite() error {
  52. return nil
  53. }
  54. func (c *MockChannel) SendRequest(name string, wantReply bool, payload []byte) (bool, error) {
  55. return true, nil
  56. }
  57. func (c *MockChannel) Stderr() io.ReadWriter {
  58. return c.StdErrBuffer
  59. }
  60. // MockOsFs mockable OsFs
  61. type MockOsFs struct {
  62. vfs.Fs
  63. err error
  64. statErr error
  65. isAtomicUploadSupported bool
  66. }
  67. // Name returns the name for the Fs implementation
  68. func (fs MockOsFs) Name() string {
  69. return "mockOsFs"
  70. }
  71. // IsUploadResumeSupported returns true if upload resume is supported
  72. func (MockOsFs) IsUploadResumeSupported() bool {
  73. return false
  74. }
  75. // IsAtomicUploadSupported returns true if atomic upload is supported
  76. func (fs MockOsFs) IsAtomicUploadSupported() bool {
  77. return fs.isAtomicUploadSupported
  78. }
  79. // Stat returns a FileInfo describing the named file
  80. func (fs MockOsFs) Stat(name string) (os.FileInfo, error) {
  81. if fs.statErr != nil {
  82. return nil, fs.statErr
  83. }
  84. return os.Stat(name)
  85. }
  86. // Lstat returns a FileInfo describing the named file
  87. func (fs MockOsFs) Lstat(name string) (os.FileInfo, error) {
  88. if fs.statErr != nil {
  89. return nil, fs.statErr
  90. }
  91. return os.Lstat(name)
  92. }
  93. // Remove removes the named file or (empty) directory.
  94. func (fs MockOsFs) Remove(name string, isDir bool) error {
  95. if fs.err != nil {
  96. return fs.err
  97. }
  98. return os.Remove(name)
  99. }
  100. // Rename renames (moves) source to target
  101. func (fs MockOsFs) Rename(source, target string) error {
  102. if fs.err != nil {
  103. return fs.err
  104. }
  105. return os.Rename(source, target)
  106. }
  107. func newMockOsFs(err, statErr error, atomicUpload bool, connectionID, rootDir string) vfs.Fs {
  108. return &MockOsFs{
  109. Fs: vfs.NewOsFs(connectionID, rootDir, nil),
  110. err: err,
  111. statErr: statErr,
  112. isAtomicUploadSupported: atomicUpload,
  113. }
  114. }
  115. func TestNewActionNotification(t *testing.T) {
  116. user := dataprovider.User{
  117. Username: "username",
  118. }
  119. user.FsConfig.Provider = 0
  120. user.FsConfig.S3Config = vfs.S3FsConfig{
  121. Bucket: "s3bucket",
  122. Endpoint: "endpoint",
  123. }
  124. user.FsConfig.GCSConfig = vfs.GCSFsConfig{
  125. Bucket: "gcsbucket",
  126. }
  127. a := newActionNotification(user, operationDownload, "path", "target", "", 123, nil)
  128. assert.Equal(t, user.Username, a.Username)
  129. assert.Equal(t, 0, len(a.Bucket))
  130. assert.Equal(t, 0, len(a.Endpoint))
  131. user.FsConfig.Provider = 1
  132. a = newActionNotification(user, operationDownload, "path", "target", "", 123, nil)
  133. assert.Equal(t, "s3bucket", a.Bucket)
  134. assert.Equal(t, "endpoint", a.Endpoint)
  135. user.FsConfig.Provider = 2
  136. a = newActionNotification(user, operationDownload, "path", "target", "", 123, nil)
  137. assert.Equal(t, "gcsbucket", a.Bucket)
  138. assert.Equal(t, 0, len(a.Endpoint))
  139. }
  140. func TestWrongActions(t *testing.T) {
  141. actionsCopy := actions
  142. badCommand := "/bad/command"
  143. if runtime.GOOS == osWindows {
  144. badCommand = "C:\\bad\\command"
  145. }
  146. actions = Actions{
  147. ExecuteOn: []string{operationDownload},
  148. Hook: badCommand,
  149. }
  150. user := dataprovider.User{
  151. Username: "username",
  152. }
  153. err := executeAction(newActionNotification(user, operationDownload, "path", "", "", 0, nil))
  154. assert.Error(t, err, "action with bad command must fail")
  155. err = executeAction(newActionNotification(user, operationDelete, "path", "", "", 0, nil))
  156. assert.EqualError(t, err, errUnconfiguredAction.Error())
  157. actions.Hook = "http://foo\x7f.com/"
  158. err = executeAction(newActionNotification(user, operationDownload, "path", "", "", 0, nil))
  159. assert.Error(t, err, "action with bad url must fail")
  160. actions.Hook = ""
  161. err = executeAction(newActionNotification(user, operationDownload, "path", "", "", 0, nil))
  162. assert.Error(t, err, errNoHook.Error())
  163. actions.Hook = "relative path"
  164. err = executeNotificationCommand(newActionNotification(user, operationDownload, "path", "", "", 0, nil))
  165. assert.EqualError(t, err, fmt.Sprintf("invalid notification command %#v", actions.Hook))
  166. actions = actionsCopy
  167. }
  168. func TestActionHTTP(t *testing.T) {
  169. actionsCopy := actions
  170. actions = Actions{
  171. ExecuteOn: []string{operationDownload},
  172. Hook: "http://127.0.0.1:8080/",
  173. }
  174. user := dataprovider.User{
  175. Username: "username",
  176. }
  177. err := executeAction(newActionNotification(user, operationDownload, "path", "", "", 0, nil))
  178. assert.EqualError(t, err, errUnexpectedHTTResponse.Error())
  179. actions = actionsCopy
  180. }
  181. func TestPreDeleteAction(t *testing.T) {
  182. if runtime.GOOS == osWindows {
  183. t.Skip("this test is not available on Windows")
  184. }
  185. actionsCopy := actions
  186. hookCmd, err := exec.LookPath("true")
  187. assert.NoError(t, err)
  188. actions = Actions{
  189. ExecuteOn: []string{operationPreDelete},
  190. Hook: hookCmd,
  191. }
  192. homeDir := filepath.Join(os.TempDir(), "test_user")
  193. err = os.MkdirAll(homeDir, os.ModePerm)
  194. assert.NoError(t, err)
  195. user := dataprovider.User{
  196. Username: "username",
  197. HomeDir: homeDir,
  198. }
  199. user.Permissions = make(map[string][]string)
  200. user.Permissions["/"] = []string{dataprovider.PermAny}
  201. c := Connection{
  202. fs: vfs.NewOsFs("id", homeDir, nil),
  203. User: user,
  204. }
  205. testfile := filepath.Join(user.HomeDir, "testfile")
  206. request := sftp.NewRequest("Remove", "/testfile")
  207. err = ioutil.WriteFile(testfile, []byte("test"), 0666)
  208. assert.NoError(t, err)
  209. err = c.handleSFTPRemove(testfile, request)
  210. assert.EqualError(t, err, sftp.ErrSSHFxOk.Error())
  211. assert.FileExists(t, testfile)
  212. os.RemoveAll(homeDir)
  213. actions = actionsCopy
  214. }
  215. func TestRemoveNonexistentTransfer(t *testing.T) {
  216. transfer := Transfer{}
  217. err := removeTransfer(&transfer)
  218. assert.Error(t, err, "remove nonexistent transfer must fail")
  219. }
  220. func TestRemoveNonexistentQuotaScan(t *testing.T) {
  221. err := RemoveQuotaScan("username")
  222. assert.Error(t, err, "remove nonexistent quota scan must fail")
  223. }
  224. func TestGetOSOpenFlags(t *testing.T) {
  225. var flags sftp.FileOpenFlags
  226. flags.Write = true
  227. flags.Excl = true
  228. osFlags := getOSOpenFlags(flags)
  229. assert.NotEqual(t, 0, osFlags&os.O_WRONLY)
  230. assert.NotEqual(t, 0, osFlags&os.O_EXCL)
  231. flags.Append = true
  232. // append flag should be ignored to allow resume
  233. assert.NotEqual(t, 0, osFlags&os.O_WRONLY)
  234. assert.NotEqual(t, 0, osFlags&os.O_EXCL)
  235. }
  236. func TestUploadResumeInvalidOffset(t *testing.T) {
  237. testfile := "testfile" //nolint:goconst
  238. file, err := os.Create(testfile)
  239. assert.NoError(t, err)
  240. transfer := Transfer{
  241. file: file,
  242. path: file.Name(),
  243. start: time.Now(),
  244. bytesSent: 0,
  245. bytesReceived: 0,
  246. user: dataprovider.User{
  247. Username: "testuser",
  248. },
  249. connectionID: "",
  250. transferType: transferUpload,
  251. lastActivity: time.Now(),
  252. isNewFile: false,
  253. protocol: protocolSFTP,
  254. transferError: nil,
  255. isFinished: false,
  256. minWriteOffset: 10,
  257. lock: new(sync.Mutex),
  258. }
  259. _, err = transfer.WriteAt([]byte("test"), 0)
  260. assert.Error(t, err, "upload with invalid offset must fail")
  261. err = transfer.Close()
  262. if assert.Error(t, err) {
  263. assert.Contains(t, err.Error(), "Invalid write offset")
  264. }
  265. err = os.Remove(testfile)
  266. assert.NoError(t, err)
  267. }
  268. func TestReadWriteErrors(t *testing.T) {
  269. testfile := "testfile"
  270. file, err := os.Create(testfile)
  271. assert.NoError(t, err)
  272. transfer := Transfer{
  273. file: file,
  274. path: file.Name(),
  275. start: time.Now(),
  276. bytesSent: 0,
  277. bytesReceived: 0,
  278. user: dataprovider.User{
  279. Username: "testuser",
  280. },
  281. connectionID: "",
  282. transferType: transferDownload,
  283. lastActivity: time.Now(),
  284. isNewFile: false,
  285. protocol: protocolSFTP,
  286. transferError: nil,
  287. isFinished: false,
  288. minWriteOffset: 0,
  289. lock: new(sync.Mutex),
  290. }
  291. err = file.Close()
  292. assert.NoError(t, err)
  293. _, err = transfer.WriteAt([]byte("test"), 0)
  294. assert.Error(t, err, "writing to closed file must fail")
  295. buf := make([]byte, 32768)
  296. _, err = transfer.ReadAt(buf, 0)
  297. assert.Error(t, err, "reading from a closed file must fail")
  298. err = transfer.Close()
  299. assert.Error(t, err)
  300. r, _, err := pipeat.Pipe()
  301. assert.NoError(t, err)
  302. transfer = Transfer{
  303. readerAt: r,
  304. writerAt: nil,
  305. start: time.Now(),
  306. bytesSent: 0,
  307. bytesReceived: 0,
  308. user: dataprovider.User{
  309. Username: "testuser",
  310. },
  311. connectionID: "",
  312. transferType: transferDownload,
  313. lastActivity: time.Now(),
  314. isNewFile: false,
  315. protocol: protocolSFTP,
  316. transferError: nil,
  317. isFinished: false,
  318. lock: new(sync.Mutex),
  319. }
  320. err = transfer.closeIO()
  321. assert.NoError(t, err)
  322. _, err = transfer.ReadAt(buf, 0)
  323. assert.Error(t, err, "reading from a closed pipe must fail")
  324. r, w, err := pipeat.Pipe()
  325. assert.NoError(t, err)
  326. transfer = Transfer{
  327. readerAt: nil,
  328. writerAt: vfs.NewPipeWriter(w),
  329. start: time.Now(),
  330. bytesSent: 0,
  331. bytesReceived: 0,
  332. user: dataprovider.User{
  333. Username: "testuser",
  334. },
  335. connectionID: "",
  336. transferType: transferDownload,
  337. lastActivity: time.Now(),
  338. isNewFile: false,
  339. protocol: protocolSFTP,
  340. transferError: nil,
  341. isFinished: false,
  342. lock: new(sync.Mutex),
  343. }
  344. err = r.Close()
  345. assert.NoError(t, err)
  346. errFake := fmt.Errorf("fake upload error")
  347. go func() {
  348. time.Sleep(100 * time.Millisecond)
  349. transfer.writerAt.Done(errFake)
  350. }()
  351. err = transfer.closeIO()
  352. assert.EqualError(t, err, errFake.Error())
  353. _, err = transfer.WriteAt([]byte("test"), 0)
  354. assert.Error(t, err, "writing to closed pipe must fail")
  355. err = os.Remove(testfile)
  356. assert.NoError(t, err)
  357. }
  358. func TestTransferCancelFn(t *testing.T) {
  359. testfile := "testfile"
  360. file, err := os.Create(testfile)
  361. assert.NoError(t, err)
  362. isCancelled := false
  363. cancelFn := func() {
  364. isCancelled = true
  365. }
  366. transfer := Transfer{
  367. file: file,
  368. cancelFn: cancelFn,
  369. path: file.Name(),
  370. start: time.Now(),
  371. bytesSent: 0,
  372. bytesReceived: 0,
  373. user: dataprovider.User{
  374. Username: "testuser",
  375. },
  376. connectionID: "",
  377. transferType: transferDownload,
  378. lastActivity: time.Now(),
  379. isNewFile: false,
  380. protocol: protocolSFTP,
  381. transferError: nil,
  382. isFinished: false,
  383. minWriteOffset: 0,
  384. lock: new(sync.Mutex),
  385. }
  386. errFake := errors.New("fake error, this will trigger cancelFn")
  387. transfer.TransferError(errFake)
  388. err = transfer.Close()
  389. assert.EqualError(t, err, errFake.Error())
  390. assert.True(t, isCancelled, "cancelFn not called!")
  391. err = os.Remove(testfile)
  392. assert.NoError(t, err)
  393. }
  394. func TestMockFsErrors(t *testing.T) {
  395. errFake := errors.New("fake error")
  396. fs := newMockOsFs(errFake, errFake, false, "123", os.TempDir())
  397. u := dataprovider.User{}
  398. u.Username = "test_username"
  399. u.Permissions = make(map[string][]string)
  400. u.Permissions["/"] = []string{dataprovider.PermAny}
  401. u.HomeDir = os.TempDir()
  402. c := Connection{
  403. fs: fs,
  404. User: u,
  405. }
  406. testfile := filepath.Join(u.HomeDir, "testfile")
  407. request := sftp.NewRequest("Remove", testfile)
  408. err := ioutil.WriteFile(testfile, []byte("test"), 0666)
  409. assert.NoError(t, err)
  410. _, err = c.Filewrite(request)
  411. assert.EqualError(t, err, sftp.ErrSSHFxFailure.Error())
  412. var flags sftp.FileOpenFlags
  413. flags.Write = true
  414. flags.Trunc = false
  415. flags.Append = true
  416. _, err = c.handleSFTPUploadToExistingFile(flags, testfile, testfile, 0, "/testfile")
  417. assert.EqualError(t, err, sftp.ErrSSHFxOpUnsupported.Error())
  418. fs = newMockOsFs(errFake, nil, false, "123", os.TempDir())
  419. c.fs = fs
  420. err = c.handleSFTPRemove(testfile, request)
  421. assert.EqualError(t, err, sftp.ErrSSHFxFailure.Error())
  422. request = sftp.NewRequest("Rename", filepath.Base(testfile))
  423. request.Target = filepath.Base(testfile) + "1"
  424. err = c.handleSFTPRename(testfile, testfile+"1", request)
  425. assert.EqualError(t, err, sftp.ErrSSHFxFailure.Error())
  426. err = os.Remove(testfile)
  427. assert.NoError(t, err)
  428. }
  429. func TestUploadFiles(t *testing.T) {
  430. oldUploadMode := uploadMode
  431. uploadMode = uploadModeAtomic
  432. c := Connection{
  433. fs: vfs.NewOsFs("123", os.TempDir(), nil),
  434. }
  435. var flags sftp.FileOpenFlags
  436. flags.Write = true
  437. flags.Trunc = true
  438. _, err := c.handleSFTPUploadToExistingFile(flags, "missing_path", "other_missing_path", 0, "/missing_path")
  439. assert.Error(t, err, "upload to existing file must fail if one or both paths are invalid")
  440. uploadMode = uploadModeStandard
  441. _, err = c.handleSFTPUploadToExistingFile(flags, "missing_path", "other_missing_path", 0, "/missing_path")
  442. assert.Error(t, err, "upload to existing file must fail if one or both paths are invalid")
  443. missingFile := "missing/relative/file.txt"
  444. if runtime.GOOS == osWindows {
  445. missingFile = "missing\\relative\\file.txt"
  446. }
  447. _, err = c.handleSFTPUploadToNewFile(".", missingFile, "/missing")
  448. assert.Error(t, err, "upload new file in missing path must fail")
  449. c.fs = newMockOsFs(nil, nil, false, "123", os.TempDir())
  450. f, err := ioutil.TempFile("", "temp")
  451. assert.NoError(t, err)
  452. err = f.Close()
  453. assert.NoError(t, err)
  454. _, err = c.handleSFTPUploadToExistingFile(flags, f.Name(), f.Name(), 123, f.Name())
  455. assert.NoError(t, err)
  456. if assert.Equal(t, 1, len(activeTransfers)) {
  457. transfer := activeTransfers[0]
  458. assert.Equal(t, int64(123), transfer.initialSize)
  459. err = transfer.Close()
  460. assert.NoError(t, err)
  461. assert.Equal(t, 0, len(activeTransfers))
  462. }
  463. err = os.Remove(f.Name())
  464. assert.NoError(t, err)
  465. uploadMode = oldUploadMode
  466. }
  467. func TestWithInvalidHome(t *testing.T) {
  468. u := dataprovider.User{}
  469. u.HomeDir = "home_rel_path" //nolint:goconst
  470. _, err := loginUser(u, dataprovider.SSHLoginMethodPassword, "", nil)
  471. assert.Error(t, err, "login a user with an invalid home_dir must fail")
  472. u.HomeDir = os.TempDir()
  473. fs, err := u.GetFilesystem("123")
  474. assert.NoError(t, err)
  475. c := Connection{
  476. User: u,
  477. fs: fs,
  478. }
  479. _, err = c.fs.ResolvePath("../upper_path")
  480. assert.Error(t, err, "tested path is not a home subdir")
  481. }
  482. func TestSFTPCmdTargetPath(t *testing.T) {
  483. u := dataprovider.User{}
  484. if runtime.GOOS == osWindows {
  485. u.HomeDir = "C:\\invalid_home"
  486. } else {
  487. u.HomeDir = "/invalid_home"
  488. }
  489. u.Username = "testuser"
  490. u.Permissions = make(map[string][]string)
  491. u.Permissions["/"] = []string{dataprovider.PermAny}
  492. fs, err := u.GetFilesystem("123")
  493. assert.NoError(t, err)
  494. connection := Connection{
  495. User: u,
  496. fs: fs,
  497. }
  498. _, err = connection.getSFTPCmdTargetPath("invalid_path")
  499. assert.EqualError(t, err, sftp.ErrSSHFxNoSuchFile.Error())
  500. }
  501. func TestGetSFTPErrorFromOSError(t *testing.T) {
  502. err := os.ErrNotExist
  503. fs := vfs.NewOsFs("", os.TempDir(), nil)
  504. err = vfs.GetSFTPError(fs, err)
  505. assert.EqualError(t, err, sftp.ErrSSHFxNoSuchFile.Error())
  506. err = os.ErrPermission
  507. err = vfs.GetSFTPError(fs, err)
  508. assert.EqualError(t, err, sftp.ErrSSHFxPermissionDenied.Error())
  509. err = vfs.GetSFTPError(fs, nil)
  510. assert.NoError(t, err)
  511. }
  512. func TestSetstatModeIgnore(t *testing.T) {
  513. originalMode := setstatMode
  514. setstatMode = 1
  515. connection := Connection{}
  516. err := connection.handleSFTPSetstat("invalid", nil)
  517. assert.NoError(t, err)
  518. setstatMode = originalMode
  519. }
  520. func TestSFTPGetUsedQuota(t *testing.T) {
  521. u := dataprovider.User{}
  522. u.HomeDir = "home_rel_path"
  523. u.Username = "test_invalid_user"
  524. u.QuotaSize = 4096
  525. u.QuotaFiles = 1
  526. u.Permissions = make(map[string][]string)
  527. u.Permissions["/"] = []string{dataprovider.PermAny}
  528. connection := Connection{
  529. User: u,
  530. }
  531. quotaResult := connection.hasSpace(false, "/")
  532. assert.False(t, quotaResult.HasSpace)
  533. }
  534. func TestSupportedSSHCommands(t *testing.T) {
  535. cmds := GetSupportedSSHCommands()
  536. assert.Equal(t, len(supportedSSHCommands), len(cmds))
  537. for _, c := range cmds {
  538. assert.True(t, utils.IsStringInSlice(c, supportedSSHCommands))
  539. }
  540. }
  541. func TestSSHCommandPath(t *testing.T) {
  542. buf := make([]byte, 65535)
  543. stdErrBuf := make([]byte, 65535)
  544. mockSSHChannel := MockChannel{
  545. Buffer: bytes.NewBuffer(buf),
  546. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  547. ReadError: nil,
  548. }
  549. connection := Connection{
  550. channel: &mockSSHChannel,
  551. }
  552. sshCommand := sshCommand{
  553. command: "test",
  554. connection: connection,
  555. args: []string{},
  556. }
  557. assert.Equal(t, "", sshCommand.getDestPath())
  558. sshCommand.args = []string{"-t", "/tmp/../path"}
  559. assert.Equal(t, "/path", sshCommand.getDestPath())
  560. sshCommand.args = []string{"-t", "/tmp/"}
  561. assert.Equal(t, "/tmp/", sshCommand.getDestPath())
  562. sshCommand.args = []string{"-t", "tmp/"}
  563. assert.Equal(t, "/tmp/", sshCommand.getDestPath())
  564. sshCommand.args = []string{"-t", "/tmp/../../../path"}
  565. assert.Equal(t, "/path", sshCommand.getDestPath())
  566. sshCommand.args = []string{"-t", ".."}
  567. assert.Equal(t, "/", sshCommand.getDestPath())
  568. sshCommand.args = []string{"-t", "."}
  569. assert.Equal(t, "/", sshCommand.getDestPath())
  570. sshCommand.args = []string{"-t", "//"}
  571. assert.Equal(t, "/", sshCommand.getDestPath())
  572. sshCommand.args = []string{"-t", "../.."}
  573. assert.Equal(t, "/", sshCommand.getDestPath())
  574. sshCommand.args = []string{"-t", "/.."}
  575. assert.Equal(t, "/", sshCommand.getDestPath())
  576. sshCommand.args = []string{"-f", "/a space.txt"}
  577. assert.Equal(t, "/a space.txt", sshCommand.getDestPath())
  578. }
  579. func TestSSHParseCommandPayload(t *testing.T) {
  580. cmd := "command -a -f /ab\\ à/some\\ spaces\\ \\ \\(\\).txt"
  581. name, args, _ := parseCommandPayload(cmd)
  582. assert.Equal(t, "command", name)
  583. assert.Equal(t, 3, len(args))
  584. assert.Equal(t, "/ab à/some spaces ().txt", args[2])
  585. _, _, err := parseCommandPayload("")
  586. assert.Error(t, err, "parsing invalid command must fail")
  587. }
  588. func TestSSHCommandErrors(t *testing.T) {
  589. buf := make([]byte, 65535)
  590. stdErrBuf := make([]byte, 65535)
  591. readErr := fmt.Errorf("test read error")
  592. mockSSHChannel := MockChannel{
  593. Buffer: bytes.NewBuffer(buf),
  594. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  595. ReadError: readErr,
  596. }
  597. server, client := net.Pipe()
  598. defer func() {
  599. err := server.Close()
  600. assert.NoError(t, err)
  601. }()
  602. defer func() {
  603. err := client.Close()
  604. assert.NoError(t, err)
  605. }()
  606. user := dataprovider.User{}
  607. user.Permissions = make(map[string][]string)
  608. user.Permissions["/"] = []string{dataprovider.PermAny}
  609. fs, err := user.GetFilesystem("123")
  610. assert.NoError(t, err)
  611. connection := Connection{
  612. channel: &mockSSHChannel,
  613. netConn: client,
  614. User: user,
  615. fs: fs,
  616. }
  617. cmd := sshCommand{
  618. command: "md5sum",
  619. connection: connection,
  620. args: []string{},
  621. }
  622. err = cmd.handle()
  623. assert.Error(t, err, "ssh command must fail, we are sending a fake error")
  624. cmd = sshCommand{
  625. command: "md5sum",
  626. connection: connection,
  627. args: []string{"/../../test_file.dat"},
  628. }
  629. err = cmd.handle()
  630. assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
  631. cmd = sshCommand{
  632. command: "git-receive-pack",
  633. connection: connection,
  634. args: []string{"/../../testrepo"},
  635. }
  636. err = cmd.handle()
  637. assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
  638. cmd.connection.User.HomeDir = filepath.Clean(os.TempDir())
  639. cmd.connection.User.QuotaFiles = 1
  640. cmd.connection.User.UsedQuotaFiles = 2
  641. fs, err = cmd.connection.User.GetFilesystem("123")
  642. assert.NoError(t, err)
  643. cmd.connection.fs = fs
  644. err = cmd.handle()
  645. assert.EqualError(t, err, errQuotaExceeded.Error())
  646. cmd.connection.User.QuotaFiles = 0
  647. cmd.connection.User.UsedQuotaFiles = 0
  648. cmd.connection.User.Permissions = make(map[string][]string)
  649. cmd.connection.User.Permissions["/"] = []string{dataprovider.PermListItems}
  650. err = cmd.handle()
  651. assert.EqualError(t, err, errPermissionDenied.Error())
  652. cmd.connection.User.Permissions["/"] = []string{dataprovider.PermAny}
  653. cmd.command = "invalid_command"
  654. command, err := cmd.getSystemCommand()
  655. assert.NoError(t, err)
  656. err = cmd.executeSystemCommand(command)
  657. assert.Error(t, err, "invalid command must fail")
  658. command, err = cmd.getSystemCommand()
  659. assert.NoError(t, err)
  660. _, err = command.cmd.StderrPipe()
  661. assert.NoError(t, err)
  662. err = cmd.executeSystemCommand(command)
  663. assert.Error(t, err, "command must fail, pipe was already assigned")
  664. err = cmd.executeSystemCommand(command)
  665. assert.Error(t, err, "command must fail, pipe was already assigned")
  666. command, err = cmd.getSystemCommand()
  667. assert.NoError(t, err)
  668. _, err = command.cmd.StdoutPipe()
  669. assert.NoError(t, err)
  670. err = cmd.executeSystemCommand(command)
  671. assert.Error(t, err, "command must fail, pipe was already assigned")
  672. cmd = sshCommand{
  673. command: "sftpgo-remove",
  674. connection: connection,
  675. args: []string{"/../../src"},
  676. }
  677. err = cmd.handle()
  678. assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
  679. cmd = sshCommand{
  680. command: "sftpgo-copy",
  681. connection: connection,
  682. args: []string{"/../../test_src", "."},
  683. }
  684. err = cmd.handle()
  685. assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
  686. cmd.connection.fs = fs
  687. _, _, err = cmd.resolveCopyPaths(".", "../adir")
  688. assert.Error(t, err)
  689. cmd = sshCommand{
  690. command: "sftpgo-copy",
  691. connection: connection,
  692. args: []string{"src", "dst"},
  693. }
  694. cmd.connection.User.Permissions = make(map[string][]string)
  695. cmd.connection.User.Permissions["/"] = []string{dataprovider.PermDownload}
  696. src, dst, err := cmd.getCopyPaths()
  697. assert.NoError(t, err)
  698. assert.False(t, cmd.hasCopyPermissions(src, dst, nil))
  699. cmd.connection.User.Permissions = make(map[string][]string)
  700. cmd.connection.User.Permissions["/"] = []string{dataprovider.PermAny}
  701. if runtime.GOOS != osWindows {
  702. aDir := filepath.Join(os.TempDir(), "adir")
  703. err = os.MkdirAll(aDir, os.ModePerm)
  704. assert.NoError(t, err)
  705. tmpFile := filepath.Join(aDir, "testcopy")
  706. err = ioutil.WriteFile(tmpFile, []byte("aaa"), os.ModePerm)
  707. assert.NoError(t, err)
  708. err = os.Chmod(aDir, 0001)
  709. assert.NoError(t, err)
  710. err = cmd.checkCopyDestination(tmpFile)
  711. assert.Error(t, err)
  712. err = os.Chmod(aDir, os.ModePerm)
  713. assert.NoError(t, err)
  714. err = os.Remove(tmpFile)
  715. assert.NoError(t, err)
  716. }
  717. }
  718. func TestCommandsWithExtensionsFilter(t *testing.T) {
  719. buf := make([]byte, 65535)
  720. stdErrBuf := make([]byte, 65535)
  721. mockSSHChannel := MockChannel{
  722. Buffer: bytes.NewBuffer(buf),
  723. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  724. }
  725. server, client := net.Pipe()
  726. defer server.Close()
  727. defer client.Close()
  728. user := dataprovider.User{
  729. Username: "test",
  730. HomeDir: os.TempDir(),
  731. Status: 1,
  732. }
  733. user.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
  734. {
  735. Path: "/subdir",
  736. AllowedExtensions: []string{".jpg"},
  737. DeniedExtensions: []string{},
  738. },
  739. }
  740. fs, err := user.GetFilesystem("123")
  741. assert.NoError(t, err)
  742. connection := Connection{
  743. channel: &mockSSHChannel,
  744. netConn: client,
  745. User: user,
  746. fs: fs,
  747. }
  748. cmd := sshCommand{
  749. command: "md5sum",
  750. connection: connection,
  751. args: []string{"subdir/test.png"},
  752. }
  753. err = cmd.handleHashCommands()
  754. assert.EqualError(t, err, errPermissionDenied.Error())
  755. cmd = sshCommand{
  756. command: "rsync",
  757. connection: connection,
  758. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  759. }
  760. _, err = cmd.getSystemCommand()
  761. assert.EqualError(t, err, errUnsupportedConfig.Error())
  762. cmd = sshCommand{
  763. command: "git-receive-pack",
  764. connection: connection,
  765. args: []string{"/subdir"},
  766. }
  767. _, err = cmd.getSystemCommand()
  768. assert.EqualError(t, err, errUnsupportedConfig.Error())
  769. cmd = sshCommand{
  770. command: "git-receive-pack",
  771. connection: connection,
  772. args: []string{"/subdir/dir"},
  773. }
  774. _, err = cmd.getSystemCommand()
  775. assert.EqualError(t, err, errUnsupportedConfig.Error())
  776. cmd = sshCommand{
  777. command: "git-receive-pack",
  778. connection: connection,
  779. args: []string{"/adir/subdir"},
  780. }
  781. _, err = cmd.getSystemCommand()
  782. assert.NoError(t, err)
  783. }
  784. func TestSSHCommandsRemoteFs(t *testing.T) {
  785. buf := make([]byte, 65535)
  786. stdErrBuf := make([]byte, 65535)
  787. mockSSHChannel := MockChannel{
  788. Buffer: bytes.NewBuffer(buf),
  789. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  790. }
  791. server, client := net.Pipe()
  792. defer func() {
  793. err := server.Close()
  794. assert.NoError(t, err)
  795. }()
  796. defer func() {
  797. err := client.Close()
  798. assert.NoError(t, err)
  799. }()
  800. user := dataprovider.User{}
  801. user.FsConfig = dataprovider.Filesystem{
  802. Provider: 1,
  803. S3Config: vfs.S3FsConfig{
  804. Bucket: "s3bucket",
  805. Endpoint: "endpoint",
  806. Region: "eu-west-1",
  807. },
  808. }
  809. fs, err := user.GetFilesystem("123")
  810. assert.NoError(t, err)
  811. connection := Connection{
  812. channel: &mockSSHChannel,
  813. netConn: client,
  814. User: user,
  815. fs: fs,
  816. }
  817. cmd := sshCommand{
  818. command: "md5sum",
  819. connection: connection,
  820. args: []string{},
  821. }
  822. err = cmd.handleHashCommands()
  823. assert.Error(t, err, "command must fail for a non local filesystem")
  824. command, err := cmd.getSystemCommand()
  825. assert.NoError(t, err)
  826. err = cmd.executeSystemCommand(command)
  827. assert.Error(t, err, "command must fail for a non local filesystem")
  828. cmd = sshCommand{
  829. command: "sftpgo-copy",
  830. connection: connection,
  831. args: []string{},
  832. }
  833. err = cmd.handeSFTPGoCopy()
  834. assert.Error(t, err)
  835. cmd = sshCommand{
  836. command: "sftpgo-remove",
  837. connection: connection,
  838. args: []string{},
  839. }
  840. err = cmd.handeSFTPGoRemove()
  841. assert.Error(t, err)
  842. }
  843. func TestGitVirtualFolders(t *testing.T) {
  844. permissions := make(map[string][]string)
  845. permissions["/"] = []string{dataprovider.PermAny}
  846. user := dataprovider.User{
  847. Permissions: permissions,
  848. HomeDir: os.TempDir(),
  849. }
  850. fs, err := user.GetFilesystem("123")
  851. assert.NoError(t, err)
  852. conn := Connection{
  853. User: user,
  854. fs: fs,
  855. }
  856. cmd := sshCommand{
  857. command: "git-receive-pack",
  858. connection: conn,
  859. args: []string{"/vdir"},
  860. }
  861. cmd.connection.User.VirtualFolders = append(cmd.connection.User.VirtualFolders, vfs.VirtualFolder{
  862. BaseVirtualFolder: vfs.BaseVirtualFolder{
  863. MappedPath: os.TempDir(),
  864. },
  865. VirtualPath: "/vdir",
  866. })
  867. _, err = cmd.getSystemCommand()
  868. assert.NoError(t, err)
  869. cmd.args = []string{"/"}
  870. _, err = cmd.getSystemCommand()
  871. assert.EqualError(t, err, errUnsupportedConfig.Error())
  872. cmd.args = []string{"/vdir1"}
  873. _, err = cmd.getSystemCommand()
  874. assert.NoError(t, err)
  875. cmd.connection.User.VirtualFolders = nil
  876. cmd.connection.User.VirtualFolders = append(cmd.connection.User.VirtualFolders, vfs.VirtualFolder{
  877. BaseVirtualFolder: vfs.BaseVirtualFolder{
  878. MappedPath: os.TempDir(),
  879. },
  880. VirtualPath: "/vdir",
  881. })
  882. cmd.args = []string{"/vdir/subdir"}
  883. _, err = cmd.getSystemCommand()
  884. assert.NoError(t, err)
  885. cmd.args = []string{"/adir/subdir"}
  886. _, err = cmd.getSystemCommand()
  887. assert.NoError(t, err)
  888. }
  889. func TestRsyncOptions(t *testing.T) {
  890. permissions := make(map[string][]string)
  891. permissions["/"] = []string{dataprovider.PermAny}
  892. user := dataprovider.User{
  893. Permissions: permissions,
  894. HomeDir: os.TempDir(),
  895. }
  896. fs, err := user.GetFilesystem("123")
  897. assert.NoError(t, err)
  898. conn := Connection{
  899. User: user,
  900. fs: fs,
  901. }
  902. sshCmd := sshCommand{
  903. command: "rsync",
  904. connection: conn,
  905. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  906. }
  907. cmd, err := sshCmd.getSystemCommand()
  908. assert.NoError(t, err)
  909. assert.True(t, utils.IsStringInSlice("--safe-links", cmd.cmd.Args),
  910. "--safe-links must be added if the user has the create symlinks permission")
  911. permissions["/"] = []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermCreateDirs,
  912. dataprovider.PermListItems, dataprovider.PermOverwrite, dataprovider.PermDelete, dataprovider.PermRename}
  913. user.Permissions = permissions
  914. fs, err = user.GetFilesystem("123")
  915. assert.NoError(t, err)
  916. conn = Connection{
  917. User: user,
  918. fs: fs,
  919. }
  920. sshCmd = sshCommand{
  921. command: "rsync",
  922. connection: conn,
  923. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  924. }
  925. cmd, err = sshCmd.getSystemCommand()
  926. assert.NoError(t, err)
  927. assert.True(t, utils.IsStringInSlice("--munge-links", cmd.cmd.Args),
  928. "--munge-links must be added if the user has the create symlinks permission")
  929. sshCmd.connection.User.VirtualFolders = append(sshCmd.connection.User.VirtualFolders, vfs.VirtualFolder{
  930. BaseVirtualFolder: vfs.BaseVirtualFolder{
  931. MappedPath: os.TempDir(),
  932. },
  933. VirtualPath: "/vdir",
  934. })
  935. _, err = sshCmd.getSystemCommand()
  936. assert.EqualError(t, err, errUnsupportedConfig.Error())
  937. }
  938. func TestSpaceForCrossRename(t *testing.T) {
  939. if runtime.GOOS == osWindows {
  940. t.Skip("this test is not available on Windows")
  941. }
  942. permissions := make(map[string][]string)
  943. permissions["/"] = []string{dataprovider.PermAny}
  944. user := dataprovider.User{
  945. Permissions: permissions,
  946. HomeDir: os.TempDir(),
  947. }
  948. fs, err := user.GetFilesystem("123")
  949. assert.NoError(t, err)
  950. conn := Connection{
  951. User: user,
  952. fs: fs,
  953. }
  954. quotaResult := vfs.QuotaCheckResult{
  955. HasSpace: true,
  956. }
  957. assert.False(t, conn.hasSpaceForCrossRename(quotaResult, -1, filepath.Join(os.TempDir(), "a missing file")))
  958. testDir := filepath.Join(os.TempDir(), "dir")
  959. err = os.MkdirAll(testDir, os.ModePerm)
  960. assert.NoError(t, err)
  961. err = ioutil.WriteFile(filepath.Join(testDir, "afile"), []byte("content"), os.ModePerm)
  962. assert.NoError(t, err)
  963. err = os.Chmod(testDir, 0001)
  964. assert.NoError(t, err)
  965. assert.False(t, conn.hasSpaceForCrossRename(quotaResult, -1, testDir))
  966. err = os.Chmod(testDir, os.ModePerm)
  967. assert.NoError(t, err)
  968. err = os.RemoveAll(testDir)
  969. assert.NoError(t, err)
  970. }
  971. func TestSystemCommandSizeForPath(t *testing.T) {
  972. permissions := make(map[string][]string)
  973. permissions["/"] = []string{dataprovider.PermAny}
  974. user := dataprovider.User{
  975. Permissions: permissions,
  976. HomeDir: os.TempDir(),
  977. }
  978. fs, err := user.GetFilesystem("123")
  979. assert.NoError(t, err)
  980. conn := Connection{
  981. User: user,
  982. fs: fs,
  983. }
  984. sshCmd := sshCommand{
  985. command: "rsync",
  986. connection: conn,
  987. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  988. }
  989. _, _, err = sshCmd.getSizeForPath("missing path")
  990. assert.NoError(t, err)
  991. testDir := filepath.Join(os.TempDir(), "dir")
  992. err = os.MkdirAll(testDir, os.ModePerm)
  993. assert.NoError(t, err)
  994. testFile := filepath.Join(testDir, "testfile")
  995. err = ioutil.WriteFile(testFile, []byte("test content"), os.ModePerm)
  996. assert.NoError(t, err)
  997. err = os.Symlink(testFile, testFile+".link")
  998. assert.NoError(t, err)
  999. numFiles, size, err := sshCmd.getSizeForPath(testFile + ".link")
  1000. assert.NoError(t, err)
  1001. assert.Equal(t, 0, numFiles)
  1002. assert.Equal(t, int64(0), size)
  1003. numFiles, size, err = sshCmd.getSizeForPath(testFile)
  1004. assert.NoError(t, err)
  1005. assert.Equal(t, 1, numFiles)
  1006. assert.Equal(t, int64(12), size)
  1007. if runtime.GOOS != osWindows {
  1008. err = os.Chmod(testDir, 0001)
  1009. assert.NoError(t, err)
  1010. _, _, err = sshCmd.getSizeForPath(testFile)
  1011. assert.Error(t, err)
  1012. err = os.Chmod(testDir, os.ModePerm)
  1013. assert.NoError(t, err)
  1014. }
  1015. err = os.RemoveAll(testDir)
  1016. assert.NoError(t, err)
  1017. }
  1018. func TestSystemCommandErrors(t *testing.T) {
  1019. buf := make([]byte, 65535)
  1020. stdErrBuf := make([]byte, 65535)
  1021. readErr := fmt.Errorf("test read error")
  1022. writeErr := fmt.Errorf("test write error")
  1023. mockSSHChannel := MockChannel{
  1024. Buffer: bytes.NewBuffer(buf),
  1025. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1026. ReadError: nil,
  1027. WriteError: writeErr,
  1028. }
  1029. server, client := net.Pipe()
  1030. defer func() {
  1031. err := server.Close()
  1032. assert.NoError(t, err)
  1033. }()
  1034. defer func() {
  1035. err := client.Close()
  1036. assert.NoError(t, err)
  1037. }()
  1038. permissions := make(map[string][]string)
  1039. permissions["/"] = []string{dataprovider.PermAny}
  1040. homeDir := filepath.Join(os.TempDir(), "adir")
  1041. err := os.MkdirAll(homeDir, os.ModePerm)
  1042. assert.NoError(t, err)
  1043. err = ioutil.WriteFile(filepath.Join(homeDir, "afile"), []byte("content"), os.ModePerm)
  1044. assert.NoError(t, err)
  1045. user := dataprovider.User{
  1046. Permissions: permissions,
  1047. HomeDir: homeDir,
  1048. }
  1049. fs, err := user.GetFilesystem("123")
  1050. assert.NoError(t, err)
  1051. connection := Connection{
  1052. channel: &mockSSHChannel,
  1053. netConn: client,
  1054. User: user,
  1055. fs: fs,
  1056. }
  1057. var sshCmd sshCommand
  1058. if runtime.GOOS == osWindows {
  1059. sshCmd = sshCommand{
  1060. command: "dir",
  1061. connection: connection,
  1062. args: []string{"/"},
  1063. }
  1064. } else {
  1065. sshCmd = sshCommand{
  1066. command: "ls",
  1067. connection: connection,
  1068. args: []string{"/"},
  1069. }
  1070. }
  1071. systemCmd, err := sshCmd.getSystemCommand()
  1072. assert.NoError(t, err)
  1073. systemCmd.cmd.Dir = os.TempDir()
  1074. // FIXME: the command completes but the fake client is unable to read the response
  1075. // no error is reported in this case. We can see that the expected code is executed
  1076. // reading the test coverage
  1077. sshCmd.executeSystemCommand(systemCmd) //nolint:errcheck
  1078. mockSSHChannel = MockChannel{
  1079. Buffer: bytes.NewBuffer(buf),
  1080. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1081. ReadError: readErr,
  1082. WriteError: nil,
  1083. }
  1084. sshCmd.connection.channel = &mockSSHChannel
  1085. transfer := Transfer{
  1086. transferType: transferDownload,
  1087. lock: new(sync.Mutex)}
  1088. destBuff := make([]byte, 65535)
  1089. dst := bytes.NewBuffer(destBuff)
  1090. _, err = transfer.copyFromReaderToWriter(dst, sshCmd.connection.channel)
  1091. assert.EqualError(t, err, readErr.Error())
  1092. mockSSHChannel = MockChannel{
  1093. Buffer: bytes.NewBuffer(buf),
  1094. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1095. ReadError: nil,
  1096. WriteError: nil,
  1097. }
  1098. sshCmd.connection.channel = &mockSSHChannel
  1099. transfer.maxWriteSize = 1
  1100. _, err = transfer.copyFromReaderToWriter(dst, sshCmd.connection.channel)
  1101. assert.EqualError(t, err, errQuotaExceeded.Error())
  1102. mockSSHChannel = MockChannel{
  1103. Buffer: bytes.NewBuffer(buf),
  1104. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1105. ReadError: nil,
  1106. WriteError: nil,
  1107. ShortWriteErr: true,
  1108. }
  1109. sshCmd.connection.channel = &mockSSHChannel
  1110. _, err = transfer.copyFromReaderToWriter(sshCmd.connection.channel, dst)
  1111. assert.EqualError(t, err, io.ErrShortWrite.Error())
  1112. transfer.maxWriteSize = -1
  1113. _, err = transfer.copyFromReaderToWriter(sshCmd.connection.channel, dst)
  1114. assert.EqualError(t, err, errQuotaExceeded.Error())
  1115. err = os.RemoveAll(homeDir)
  1116. assert.NoError(t, err)
  1117. }
  1118. func TestTransferUpdateQuota(t *testing.T) {
  1119. transfer := Transfer{
  1120. transferType: transferUpload,
  1121. bytesReceived: 123,
  1122. lock: new(sync.Mutex)}
  1123. transfer.TransferError(errors.New("fake error"))
  1124. assert.False(t, transfer.updateQuota(1))
  1125. }
  1126. func TestGetConnectionInfo(t *testing.T) {
  1127. c := ConnectionStatus{
  1128. Username: "test_user",
  1129. ConnectionID: "123",
  1130. ClientVersion: "client",
  1131. RemoteAddress: "127.0.0.1:1234",
  1132. Protocol: protocolSSH,
  1133. SSHCommand: "sha1sum /test_file.dat",
  1134. }
  1135. info := c.GetConnectionInfo()
  1136. assert.Contains(t, info, "sha1sum /test_file.dat")
  1137. }
  1138. func TestSCPFileMode(t *testing.T) {
  1139. mode := getFileModeAsString(0, true)
  1140. assert.Equal(t, "0755", mode)
  1141. mode = getFileModeAsString(0700, true)
  1142. assert.Equal(t, "0700", mode)
  1143. mode = getFileModeAsString(0750, true)
  1144. assert.Equal(t, "0750", mode)
  1145. mode = getFileModeAsString(0777, true)
  1146. assert.Equal(t, "0777", mode)
  1147. mode = getFileModeAsString(0640, false)
  1148. assert.Equal(t, "0640", mode)
  1149. mode = getFileModeAsString(0600, false)
  1150. assert.Equal(t, "0600", mode)
  1151. mode = getFileModeAsString(0, false)
  1152. assert.Equal(t, "0644", mode)
  1153. fileMode := uint32(0777)
  1154. fileMode = fileMode | uint32(os.ModeSetgid)
  1155. fileMode = fileMode | uint32(os.ModeSetuid)
  1156. fileMode = fileMode | uint32(os.ModeSticky)
  1157. mode = getFileModeAsString(os.FileMode(fileMode), false)
  1158. assert.Equal(t, "7777", mode)
  1159. fileMode = uint32(0644)
  1160. fileMode = fileMode | uint32(os.ModeSetgid)
  1161. mode = getFileModeAsString(os.FileMode(fileMode), false)
  1162. assert.Equal(t, "4644", mode)
  1163. fileMode = uint32(0600)
  1164. fileMode = fileMode | uint32(os.ModeSetuid)
  1165. mode = getFileModeAsString(os.FileMode(fileMode), false)
  1166. assert.Equal(t, "2600", mode)
  1167. fileMode = uint32(0044)
  1168. fileMode = fileMode | uint32(os.ModeSticky)
  1169. mode = getFileModeAsString(os.FileMode(fileMode), false)
  1170. assert.Equal(t, "1044", mode)
  1171. }
  1172. func TestSCPParseUploadMessage(t *testing.T) {
  1173. buf := make([]byte, 65535)
  1174. stdErrBuf := make([]byte, 65535)
  1175. mockSSHChannel := MockChannel{
  1176. Buffer: bytes.NewBuffer(buf),
  1177. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1178. ReadError: nil,
  1179. }
  1180. connection := Connection{
  1181. channel: &mockSSHChannel,
  1182. fs: vfs.NewOsFs("", os.TempDir(), nil),
  1183. }
  1184. scpCommand := scpCommand{
  1185. sshCommand: sshCommand{
  1186. command: "scp",
  1187. connection: connection,
  1188. args: []string{"-t", "/tmp"},
  1189. },
  1190. }
  1191. _, _, err := scpCommand.parseUploadMessage("invalid")
  1192. assert.Error(t, err, "parsing invalid upload message must fail")
  1193. _, _, err = scpCommand.parseUploadMessage("D0755 0")
  1194. assert.Error(t, err, "parsing incomplete upload message must fail")
  1195. _, _, err = scpCommand.parseUploadMessage("D0755 invalidsize testdir")
  1196. assert.Error(t, err, "parsing upload message with invalid size must fail")
  1197. _, _, err = scpCommand.parseUploadMessage("D0755 0 ")
  1198. assert.Error(t, err, "parsing upload message with invalid name must fail")
  1199. }
  1200. func TestSCPProtocolMessages(t *testing.T) {
  1201. buf := make([]byte, 65535)
  1202. stdErrBuf := make([]byte, 65535)
  1203. readErr := fmt.Errorf("test read error")
  1204. writeErr := fmt.Errorf("test write error")
  1205. mockSSHChannel := MockChannel{
  1206. Buffer: bytes.NewBuffer(buf),
  1207. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1208. ReadError: readErr,
  1209. WriteError: writeErr,
  1210. }
  1211. connection := Connection{
  1212. channel: &mockSSHChannel,
  1213. }
  1214. scpCommand := scpCommand{
  1215. sshCommand: sshCommand{
  1216. command: "scp",
  1217. connection: connection,
  1218. args: []string{"-t", "/tmp"},
  1219. },
  1220. }
  1221. _, err := scpCommand.readProtocolMessage()
  1222. assert.EqualError(t, err, readErr.Error())
  1223. err = scpCommand.sendConfirmationMessage()
  1224. assert.EqualError(t, err, writeErr.Error())
  1225. err = scpCommand.sendProtocolMessage("E\n")
  1226. assert.EqualError(t, err, writeErr.Error())
  1227. _, err = scpCommand.getNextUploadProtocolMessage()
  1228. assert.EqualError(t, err, readErr.Error())
  1229. mockSSHChannel = MockChannel{
  1230. Buffer: bytes.NewBuffer([]byte("T1183832947 0 1183833773 0\n")),
  1231. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1232. ReadError: nil,
  1233. WriteError: writeErr,
  1234. }
  1235. scpCommand.connection.channel = &mockSSHChannel
  1236. _, err = scpCommand.getNextUploadProtocolMessage()
  1237. assert.EqualError(t, err, writeErr.Error())
  1238. respBuffer := []byte{0x02}
  1239. protocolErrorMsg := "protocol error msg"
  1240. respBuffer = append(respBuffer, protocolErrorMsg...)
  1241. respBuffer = append(respBuffer, 0x0A)
  1242. mockSSHChannel = MockChannel{
  1243. Buffer: bytes.NewBuffer(respBuffer),
  1244. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1245. ReadError: nil,
  1246. WriteError: nil,
  1247. }
  1248. scpCommand.connection.channel = &mockSSHChannel
  1249. err = scpCommand.readConfirmationMessage()
  1250. if assert.Error(t, err) {
  1251. assert.Equal(t, protocolErrorMsg, err.Error())
  1252. }
  1253. }
  1254. func TestSCPTestDownloadProtocolMessages(t *testing.T) {
  1255. buf := make([]byte, 65535)
  1256. stdErrBuf := make([]byte, 65535)
  1257. readErr := fmt.Errorf("test read error")
  1258. writeErr := fmt.Errorf("test write error")
  1259. mockSSHChannel := MockChannel{
  1260. Buffer: bytes.NewBuffer(buf),
  1261. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1262. ReadError: readErr,
  1263. WriteError: writeErr,
  1264. }
  1265. connection := Connection{
  1266. channel: &mockSSHChannel,
  1267. }
  1268. scpCommand := scpCommand{
  1269. sshCommand: sshCommand{
  1270. command: "scp",
  1271. connection: connection,
  1272. args: []string{"-f", "-p", "/tmp"},
  1273. },
  1274. }
  1275. path := "testDir"
  1276. err := os.Mkdir(path, os.ModePerm)
  1277. assert.NoError(t, err)
  1278. stat, err := os.Stat(path)
  1279. assert.NoError(t, err)
  1280. err = scpCommand.sendDownloadProtocolMessages(path, stat)
  1281. assert.EqualError(t, err, writeErr.Error())
  1282. mockSSHChannel = MockChannel{
  1283. Buffer: bytes.NewBuffer(buf),
  1284. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1285. ReadError: readErr,
  1286. WriteError: nil,
  1287. }
  1288. err = scpCommand.sendDownloadProtocolMessages(path, stat)
  1289. assert.EqualError(t, err, readErr.Error())
  1290. mockSSHChannel = MockChannel{
  1291. Buffer: bytes.NewBuffer(buf),
  1292. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1293. ReadError: readErr,
  1294. WriteError: writeErr,
  1295. }
  1296. scpCommand.args = []string{"-f", "/tmp"}
  1297. scpCommand.connection.channel = &mockSSHChannel
  1298. err = scpCommand.sendDownloadProtocolMessages(path, stat)
  1299. assert.EqualError(t, err, writeErr.Error())
  1300. mockSSHChannel = MockChannel{
  1301. Buffer: bytes.NewBuffer(buf),
  1302. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1303. ReadError: readErr,
  1304. WriteError: nil,
  1305. }
  1306. scpCommand.connection.channel = &mockSSHChannel
  1307. err = scpCommand.sendDownloadProtocolMessages(path, stat)
  1308. assert.EqualError(t, err, readErr.Error())
  1309. err = os.Remove(path)
  1310. assert.NoError(t, err)
  1311. }
  1312. func TestSCPCommandHandleErrors(t *testing.T) {
  1313. buf := make([]byte, 65535)
  1314. stdErrBuf := make([]byte, 65535)
  1315. readErr := fmt.Errorf("test read error")
  1316. writeErr := fmt.Errorf("test write error")
  1317. mockSSHChannel := MockChannel{
  1318. Buffer: bytes.NewBuffer(buf),
  1319. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1320. ReadError: readErr,
  1321. WriteError: writeErr,
  1322. }
  1323. server, client := net.Pipe()
  1324. defer func() {
  1325. err := server.Close()
  1326. assert.NoError(t, err)
  1327. }()
  1328. defer func() {
  1329. err := client.Close()
  1330. assert.NoError(t, err)
  1331. }()
  1332. connection := Connection{
  1333. channel: &mockSSHChannel,
  1334. netConn: client,
  1335. }
  1336. scpCommand := scpCommand{
  1337. sshCommand: sshCommand{
  1338. command: "scp",
  1339. connection: connection,
  1340. args: []string{"-f", "/tmp"},
  1341. },
  1342. }
  1343. err := scpCommand.handle()
  1344. assert.EqualError(t, err, readErr.Error())
  1345. scpCommand.args = []string{"-i", "/tmp"}
  1346. err = scpCommand.handle()
  1347. assert.Error(t, err, "invalid scp command must fail")
  1348. }
  1349. func TestSCPErrorsMockFs(t *testing.T) {
  1350. errFake := errors.New("fake error")
  1351. fs := newMockOsFs(errFake, errFake, false, "1234", os.TempDir())
  1352. u := dataprovider.User{}
  1353. u.Username = "test"
  1354. u.Permissions = make(map[string][]string)
  1355. u.Permissions["/"] = []string{dataprovider.PermAny}
  1356. u.HomeDir = os.TempDir()
  1357. buf := make([]byte, 65535)
  1358. stdErrBuf := make([]byte, 65535)
  1359. mockSSHChannel := MockChannel{
  1360. Buffer: bytes.NewBuffer(buf),
  1361. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1362. }
  1363. server, client := net.Pipe()
  1364. defer func() {
  1365. err := server.Close()
  1366. assert.NoError(t, err)
  1367. }()
  1368. defer func() {
  1369. err := client.Close()
  1370. assert.NoError(t, err)
  1371. }()
  1372. connection := Connection{
  1373. channel: &mockSSHChannel,
  1374. netConn: client,
  1375. fs: fs,
  1376. User: u,
  1377. }
  1378. scpCommand := scpCommand{
  1379. sshCommand: sshCommand{
  1380. command: "scp",
  1381. connection: connection,
  1382. args: []string{"-r", "-t", "/tmp"},
  1383. },
  1384. }
  1385. err := scpCommand.handleUpload("test", 0)
  1386. assert.EqualError(t, err, errFake.Error())
  1387. testfile := filepath.Join(u.HomeDir, "testfile")
  1388. err = ioutil.WriteFile(testfile, []byte("test"), 0666)
  1389. assert.NoError(t, err)
  1390. stat, err := os.Stat(u.HomeDir)
  1391. assert.NoError(t, err)
  1392. err = scpCommand.handleRecursiveDownload(u.HomeDir, stat)
  1393. assert.EqualError(t, err, errFake.Error())
  1394. scpCommand.sshCommand.connection.fs = newMockOsFs(errFake, nil, true, "123", os.TempDir())
  1395. err = scpCommand.handleUpload(filepath.Base(testfile), 0)
  1396. assert.EqualError(t, err, errFake.Error())
  1397. err = scpCommand.handleUploadFile(testfile, testfile, 0, false, 4, "/testfile")
  1398. assert.NoError(t, err)
  1399. err = os.Remove(testfile)
  1400. assert.NoError(t, err)
  1401. }
  1402. func TestSCPRecursiveDownloadErrors(t *testing.T) {
  1403. buf := make([]byte, 65535)
  1404. stdErrBuf := make([]byte, 65535)
  1405. readErr := fmt.Errorf("test read error")
  1406. writeErr := fmt.Errorf("test write error")
  1407. mockSSHChannel := MockChannel{
  1408. Buffer: bytes.NewBuffer(buf),
  1409. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1410. ReadError: readErr,
  1411. WriteError: writeErr,
  1412. }
  1413. server, client := net.Pipe()
  1414. defer func() {
  1415. err := server.Close()
  1416. assert.NoError(t, err)
  1417. }()
  1418. defer func() {
  1419. err := client.Close()
  1420. assert.NoError(t, err)
  1421. }()
  1422. connection := Connection{
  1423. channel: &mockSSHChannel,
  1424. netConn: client,
  1425. fs: vfs.NewOsFs("123", os.TempDir(), nil),
  1426. }
  1427. scpCommand := scpCommand{
  1428. sshCommand: sshCommand{
  1429. command: "scp",
  1430. connection: connection,
  1431. args: []string{"-r", "-f", "/tmp"},
  1432. },
  1433. }
  1434. path := "testDir"
  1435. err := os.Mkdir(path, os.ModePerm)
  1436. assert.NoError(t, err)
  1437. stat, err := os.Stat(path)
  1438. assert.NoError(t, err)
  1439. err = scpCommand.handleRecursiveDownload("invalid_dir", stat)
  1440. assert.EqualError(t, err, writeErr.Error())
  1441. mockSSHChannel = MockChannel{
  1442. Buffer: bytes.NewBuffer(buf),
  1443. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1444. ReadError: nil,
  1445. WriteError: nil,
  1446. }
  1447. scpCommand.connection.channel = &mockSSHChannel
  1448. err = scpCommand.handleRecursiveDownload("invalid_dir", stat)
  1449. assert.Error(t, err, "recursive upload download must fail for a non existing dir")
  1450. err = os.Remove(path)
  1451. assert.NoError(t, err)
  1452. }
  1453. func TestSCPRecursiveUploadErrors(t *testing.T) {
  1454. buf := make([]byte, 65535)
  1455. stdErrBuf := make([]byte, 65535)
  1456. readErr := fmt.Errorf("test read error")
  1457. writeErr := fmt.Errorf("test write error")
  1458. mockSSHChannel := MockChannel{
  1459. Buffer: bytes.NewBuffer(buf),
  1460. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1461. ReadError: readErr,
  1462. WriteError: writeErr,
  1463. }
  1464. connection := Connection{
  1465. channel: &mockSSHChannel,
  1466. }
  1467. scpCommand := scpCommand{
  1468. sshCommand: sshCommand{
  1469. command: "scp",
  1470. connection: connection,
  1471. args: []string{"-r", "-t", "/tmp"},
  1472. },
  1473. }
  1474. err := scpCommand.handleRecursiveUpload()
  1475. assert.Error(t, err, "recursive upload must fail, we send a fake error message")
  1476. mockSSHChannel = MockChannel{
  1477. Buffer: bytes.NewBuffer(buf),
  1478. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1479. ReadError: readErr,
  1480. WriteError: nil,
  1481. }
  1482. scpCommand.connection.channel = &mockSSHChannel
  1483. err = scpCommand.handleRecursiveUpload()
  1484. assert.Error(t, err, "recursive upload must fail, we send a fake error message")
  1485. }
  1486. func TestSCPCreateDirs(t *testing.T) {
  1487. buf := make([]byte, 65535)
  1488. stdErrBuf := make([]byte, 65535)
  1489. u := dataprovider.User{}
  1490. u.HomeDir = "home_rel_path"
  1491. u.Username = "test"
  1492. u.Permissions = make(map[string][]string)
  1493. u.Permissions["/"] = []string{dataprovider.PermAny}
  1494. mockSSHChannel := MockChannel{
  1495. Buffer: bytes.NewBuffer(buf),
  1496. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1497. ReadError: nil,
  1498. WriteError: nil,
  1499. }
  1500. fs, err := u.GetFilesystem("123")
  1501. assert.NoError(t, err)
  1502. connection := Connection{
  1503. User: u,
  1504. channel: &mockSSHChannel,
  1505. fs: fs,
  1506. }
  1507. scpCommand := scpCommand{
  1508. sshCommand: sshCommand{
  1509. command: "scp",
  1510. connection: connection,
  1511. args: []string{"-r", "-t", "/tmp"},
  1512. },
  1513. }
  1514. err = scpCommand.handleCreateDir("invalid_dir")
  1515. assert.Error(t, err, "create invalid dir must fail")
  1516. }
  1517. func TestSCPDownloadFileData(t *testing.T) {
  1518. testfile := "testfile"
  1519. buf := make([]byte, 65535)
  1520. readErr := fmt.Errorf("test read error")
  1521. writeErr := fmt.Errorf("test write error")
  1522. stdErrBuf := make([]byte, 65535)
  1523. mockSSHChannelReadErr := MockChannel{
  1524. Buffer: bytes.NewBuffer(buf),
  1525. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1526. ReadError: readErr,
  1527. WriteError: nil,
  1528. }
  1529. mockSSHChannelWriteErr := MockChannel{
  1530. Buffer: bytes.NewBuffer(buf),
  1531. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1532. ReadError: nil,
  1533. WriteError: writeErr,
  1534. }
  1535. connection := Connection{
  1536. channel: &mockSSHChannelReadErr,
  1537. }
  1538. scpCommand := scpCommand{
  1539. sshCommand: sshCommand{
  1540. command: "scp",
  1541. connection: connection,
  1542. args: []string{"-r", "-f", "/tmp"},
  1543. },
  1544. }
  1545. err := ioutil.WriteFile(testfile, []byte("test"), 0666)
  1546. assert.NoError(t, err)
  1547. stat, err := os.Stat(testfile)
  1548. assert.NoError(t, err)
  1549. err = scpCommand.sendDownloadFileData(testfile, stat, nil)
  1550. assert.EqualError(t, err, readErr.Error())
  1551. scpCommand.connection.channel = &mockSSHChannelWriteErr
  1552. err = scpCommand.sendDownloadFileData(testfile, stat, nil)
  1553. assert.EqualError(t, err, writeErr.Error())
  1554. scpCommand.args = []string{"-r", "-p", "-f", "/tmp"}
  1555. err = scpCommand.sendDownloadFileData(testfile, stat, nil)
  1556. assert.EqualError(t, err, writeErr.Error())
  1557. scpCommand.connection.channel = &mockSSHChannelReadErr
  1558. err = scpCommand.sendDownloadFileData(testfile, stat, nil)
  1559. assert.EqualError(t, err, readErr.Error())
  1560. err = os.Remove(testfile)
  1561. assert.NoError(t, err)
  1562. }
  1563. func TestSCPUploadFiledata(t *testing.T) {
  1564. testfile := "testfile"
  1565. buf := make([]byte, 65535)
  1566. stdErrBuf := make([]byte, 65535)
  1567. readErr := fmt.Errorf("test read error")
  1568. writeErr := fmt.Errorf("test write error")
  1569. mockSSHChannel := MockChannel{
  1570. Buffer: bytes.NewBuffer(buf),
  1571. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1572. ReadError: readErr,
  1573. WriteError: writeErr,
  1574. }
  1575. connection := Connection{
  1576. User: dataprovider.User{
  1577. Username: "testuser",
  1578. },
  1579. protocol: protocolSCP,
  1580. channel: &mockSSHChannel,
  1581. fs: vfs.NewOsFs("", os.TempDir(), nil),
  1582. }
  1583. scpCommand := scpCommand{
  1584. sshCommand: sshCommand{
  1585. command: "scp",
  1586. connection: connection,
  1587. args: []string{"-r", "-t", "/tmp"},
  1588. },
  1589. }
  1590. file, err := os.Create(testfile)
  1591. assert.NoError(t, err)
  1592. transfer := Transfer{
  1593. file: file,
  1594. path: file.Name(),
  1595. start: time.Now(),
  1596. bytesSent: 0,
  1597. bytesReceived: 0,
  1598. user: scpCommand.connection.User,
  1599. connectionID: "",
  1600. transferType: transferDownload,
  1601. lastActivity: time.Now(),
  1602. isNewFile: true,
  1603. protocol: connection.protocol,
  1604. transferError: nil,
  1605. isFinished: false,
  1606. minWriteOffset: 0,
  1607. lock: new(sync.Mutex),
  1608. }
  1609. addTransfer(&transfer)
  1610. err = scpCommand.getUploadFileData(2, &transfer)
  1611. assert.Error(t, err, "upload must fail, we send a fake write error message")
  1612. mockSSHChannel = MockChannel{
  1613. Buffer: bytes.NewBuffer(buf),
  1614. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1615. ReadError: readErr,
  1616. WriteError: nil,
  1617. }
  1618. scpCommand.connection.channel = &mockSSHChannel
  1619. file, err = os.Create(testfile)
  1620. assert.NoError(t, err)
  1621. transfer.file = file
  1622. transfer.isFinished = false
  1623. addTransfer(&transfer)
  1624. err = scpCommand.getUploadFileData(2, &transfer)
  1625. assert.Error(t, err, "upload must fail, we send a fake read error message")
  1626. respBuffer := []byte("12")
  1627. respBuffer = append(respBuffer, 0x02)
  1628. mockSSHChannel = MockChannel{
  1629. Buffer: bytes.NewBuffer(respBuffer),
  1630. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1631. ReadError: nil,
  1632. WriteError: nil,
  1633. }
  1634. scpCommand.connection.channel = &mockSSHChannel
  1635. file, err = os.Create(testfile)
  1636. assert.NoError(t, err)
  1637. transfer.file = file
  1638. transfer.isFinished = false
  1639. addTransfer(&transfer)
  1640. err = scpCommand.getUploadFileData(2, &transfer)
  1641. assert.Error(t, err, "upload must fail, we have not enough data to read")
  1642. // the file is already closed so we have an error on trasfer closing
  1643. mockSSHChannel = MockChannel{
  1644. Buffer: bytes.NewBuffer(buf),
  1645. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1646. ReadError: nil,
  1647. WriteError: nil,
  1648. }
  1649. addTransfer(&transfer)
  1650. err = scpCommand.getUploadFileData(0, &transfer)
  1651. assert.EqualError(t, err, errTransferClosed.Error())
  1652. mockSSHChannel = MockChannel{
  1653. Buffer: bytes.NewBuffer(buf),
  1654. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1655. ReadError: nil,
  1656. WriteError: nil,
  1657. }
  1658. addTransfer(&transfer)
  1659. err = scpCommand.getUploadFileData(2, &transfer)
  1660. assert.True(t, errors.Is(err, os.ErrClosed))
  1661. err = os.Remove(testfile)
  1662. assert.NoError(t, err)
  1663. }
  1664. func TestUploadError(t *testing.T) {
  1665. oldUploadMode := uploadMode
  1666. uploadMode = uploadModeAtomic
  1667. connection := Connection{
  1668. User: dataprovider.User{
  1669. Username: "testuser",
  1670. },
  1671. protocol: protocolSCP,
  1672. }
  1673. testfile := "testfile"
  1674. fileTempName := "temptestfile"
  1675. file, err := os.Create(fileTempName)
  1676. assert.NoError(t, err)
  1677. transfer := Transfer{
  1678. file: file,
  1679. path: testfile,
  1680. start: time.Now(),
  1681. bytesSent: 0,
  1682. bytesReceived: 100,
  1683. user: connection.User,
  1684. connectionID: "",
  1685. transferType: transferUpload,
  1686. lastActivity: time.Now(),
  1687. isNewFile: true,
  1688. protocol: connection.protocol,
  1689. transferError: nil,
  1690. isFinished: false,
  1691. minWriteOffset: 0,
  1692. lock: new(sync.Mutex),
  1693. }
  1694. addTransfer(&transfer)
  1695. errFake := errors.New("fake error")
  1696. transfer.TransferError(errFake)
  1697. err = transfer.Close()
  1698. assert.EqualError(t, err, errFake.Error())
  1699. assert.Equal(t, int64(0), transfer.bytesReceived)
  1700. assert.NoFileExists(t, testfile)
  1701. assert.NoFileExists(t, fileTempName)
  1702. uploadMode = oldUploadMode
  1703. }
  1704. func TestConnectionStatusStruct(t *testing.T) {
  1705. var transfers []connectionTransfer
  1706. transferUL := connectionTransfer{
  1707. OperationType: operationUpload,
  1708. StartTime: utils.GetTimeAsMsSinceEpoch(time.Now()),
  1709. Size: 123,
  1710. LastActivity: utils.GetTimeAsMsSinceEpoch(time.Now()),
  1711. Path: "/test.upload",
  1712. }
  1713. transferDL := connectionTransfer{
  1714. OperationType: operationDownload,
  1715. StartTime: utils.GetTimeAsMsSinceEpoch(time.Now()),
  1716. Size: 123,
  1717. LastActivity: utils.GetTimeAsMsSinceEpoch(time.Now()),
  1718. Path: "/test.download",
  1719. }
  1720. transfers = append(transfers, transferUL)
  1721. transfers = append(transfers, transferDL)
  1722. c := ConnectionStatus{
  1723. Username: "test",
  1724. ConnectionID: "123",
  1725. ClientVersion: "fakeClient-1.0.0",
  1726. RemoteAddress: "127.0.0.1:1234",
  1727. ConnectionTime: utils.GetTimeAsMsSinceEpoch(time.Now()),
  1728. LastActivity: utils.GetTimeAsMsSinceEpoch(time.Now()),
  1729. Protocol: "SFTP",
  1730. Transfers: transfers,
  1731. }
  1732. durationString := c.GetConnectionDuration()
  1733. assert.NotEqual(t, 0, len(durationString))
  1734. transfersString := c.GetTransfersAsString()
  1735. assert.NotEqual(t, 0, len(transfersString))
  1736. connInfo := c.GetConnectionInfo()
  1737. assert.NotEqual(t, 0, len(connInfo))
  1738. }
  1739. func TestProxyProtocolVersion(t *testing.T) {
  1740. c := Configuration{
  1741. ProxyProtocol: 1,
  1742. }
  1743. proxyListener, err := c.getProxyListener(nil)
  1744. assert.NoError(t, err)
  1745. assert.Nil(t, proxyListener.Policy)
  1746. c.ProxyProtocol = 2
  1747. proxyListener, _ = c.getProxyListener(nil)
  1748. assert.NoError(t, err)
  1749. assert.NotNil(t, proxyListener.Policy)
  1750. c.ProxyProtocol = 1
  1751. c.ProxyAllowed = []string{"invalid"}
  1752. _, err = c.getProxyListener(nil)
  1753. assert.Error(t, err)
  1754. c.ProxyProtocol = 2
  1755. _, err = c.getProxyListener(nil)
  1756. assert.Error(t, err)
  1757. }
  1758. func TestLoadHostKeys(t *testing.T) {
  1759. configDir := ".."
  1760. serverConfig := &ssh.ServerConfig{}
  1761. c := Configuration{}
  1762. c.HostKeys = []string{".", "missing file"}
  1763. err := c.checkAndLoadHostKeys(configDir, serverConfig)
  1764. assert.Error(t, err)
  1765. testfile := filepath.Join(os.TempDir(), "invalidkey")
  1766. err = ioutil.WriteFile(testfile, []byte("some bytes"), 0666)
  1767. assert.NoError(t, err)
  1768. c.HostKeys = []string{testfile}
  1769. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1770. assert.Error(t, err)
  1771. err = os.Remove(testfile)
  1772. assert.NoError(t, err)
  1773. keysDir := filepath.Join(os.TempDir(), "keys")
  1774. err = os.MkdirAll(keysDir, os.ModePerm)
  1775. assert.NoError(t, err)
  1776. rsaKeyName := filepath.Join(keysDir, defaultPrivateRSAKeyName)
  1777. ecdsaKeyName := filepath.Join(keysDir, defaultPrivateECDSAKeyName)
  1778. nonDefaultKeyName := filepath.Join(keysDir, "akey")
  1779. c.HostKeys = []string{nonDefaultKeyName, rsaKeyName, ecdsaKeyName}
  1780. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1781. assert.Error(t, err)
  1782. assert.FileExists(t, rsaKeyName)
  1783. assert.FileExists(t, ecdsaKeyName)
  1784. assert.NoFileExists(t, nonDefaultKeyName)
  1785. err = os.Remove(rsaKeyName)
  1786. assert.NoError(t, err)
  1787. err = os.Remove(ecdsaKeyName)
  1788. assert.NoError(t, err)
  1789. if runtime.GOOS != osWindows {
  1790. err = os.Chmod(keysDir, 0551)
  1791. assert.NoError(t, err)
  1792. c.HostKeys = nil
  1793. err = c.checkAndLoadHostKeys(keysDir, serverConfig)
  1794. assert.Error(t, err)
  1795. c.HostKeys = []string{rsaKeyName, ecdsaKeyName}
  1796. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1797. assert.Error(t, err)
  1798. c.HostKeys = []string{ecdsaKeyName, rsaKeyName}
  1799. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1800. assert.Error(t, err)
  1801. err = os.Chmod(keysDir, 0755)
  1802. assert.NoError(t, err)
  1803. }
  1804. err = os.RemoveAll(keysDir)
  1805. assert.NoError(t, err)
  1806. }
  1807. func TestCertCheckerInitErrors(t *testing.T) {
  1808. c := Configuration{}
  1809. c.TrustedUserCAKeys = []string{".", "missing file"}
  1810. err := c.initializeCertChecker("")
  1811. assert.Error(t, err)
  1812. testfile := filepath.Join(os.TempDir(), "invalidkey")
  1813. err = ioutil.WriteFile(testfile, []byte("some bytes"), 0666)
  1814. assert.NoError(t, err)
  1815. c.TrustedUserCAKeys = []string{testfile}
  1816. err = c.initializeCertChecker("")
  1817. assert.Error(t, err)
  1818. err = os.Remove(testfile)
  1819. assert.NoError(t, err)
  1820. }
  1821. func TestUpdateQuotaAfterRenameMissingFile(t *testing.T) {
  1822. user := dataprovider.User{
  1823. Username: "username",
  1824. HomeDir: filepath.Join(os.TempDir(), "home"),
  1825. }
  1826. mappedPath := filepath.Join(os.TempDir(), "vdir")
  1827. user.Permissions = make(map[string][]string)
  1828. user.Permissions["/"] = []string{dataprovider.PermAny}
  1829. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  1830. BaseVirtualFolder: vfs.BaseVirtualFolder{
  1831. MappedPath: mappedPath,
  1832. },
  1833. VirtualPath: "/vdir",
  1834. })
  1835. c := Connection{
  1836. fs: vfs.NewOsFs("id", os.TempDir(), nil),
  1837. User: user,
  1838. }
  1839. request := sftp.NewRequest("Rename", "/testfile")
  1840. request.Filepath = "/dir"
  1841. request.Target = path.Join("vdir", "dir")
  1842. if runtime.GOOS != osWindows {
  1843. testDirPath := filepath.Join(mappedPath, "dir")
  1844. err := os.MkdirAll(testDirPath, os.ModePerm)
  1845. assert.NoError(t, err)
  1846. err = os.Chmod(testDirPath, 0001)
  1847. assert.NoError(t, err)
  1848. err = c.updateQuotaAfterRename(request, testDirPath, 0)
  1849. assert.Error(t, err)
  1850. err = os.Chmod(testDirPath, os.ModePerm)
  1851. assert.NoError(t, err)
  1852. err = os.RemoveAll(testDirPath)
  1853. assert.NoError(t, err)
  1854. }
  1855. request.Target = "/testfile1"
  1856. request.Filepath = path.Join("vdir", "file")
  1857. err := c.updateQuotaAfterRename(request, filepath.Join(os.TempDir(), "vdir", "file"), 0)
  1858. assert.Error(t, err)
  1859. }
  1860. func TestRenamePermission(t *testing.T) {
  1861. permissions := make(map[string][]string)
  1862. permissions["/"] = []string{dataprovider.PermAny}
  1863. permissions["/dir1"] = []string{dataprovider.PermRename}
  1864. permissions["/dir2"] = []string{dataprovider.PermUpload}
  1865. permissions["/dir3"] = []string{dataprovider.PermDelete}
  1866. permissions["/dir4"] = []string{dataprovider.PermListItems}
  1867. permissions["/dir5"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload}
  1868. permissions["/dir6"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload,
  1869. dataprovider.PermListItems, dataprovider.PermCreateSymlinks}
  1870. user := dataprovider.User{
  1871. Permissions: permissions,
  1872. HomeDir: os.TempDir(),
  1873. }
  1874. fs, err := user.GetFilesystem("123")
  1875. assert.NoError(t, err)
  1876. conn := Connection{
  1877. User: user,
  1878. fs: fs,
  1879. }
  1880. request := sftp.NewRequest("Rename", "/testfile")
  1881. request.Target = "/dir1/testfile"
  1882. // rename is granted on Source and Target
  1883. assert.True(t, conn.isRenamePermitted("", request.Filepath, request.Target, nil))
  1884. request.Target = "/dir4/testfile"
  1885. // rename is not granted on Target
  1886. assert.False(t, conn.isRenamePermitted("", request.Filepath, request.Target, nil))
  1887. request = sftp.NewRequest("Rename", "/dir1/testfile")
  1888. request.Target = "/dir2/testfile" //nolint:goconst
  1889. // rename is granted on Source but not on Target
  1890. assert.False(t, conn.isRenamePermitted("", request.Filepath, request.Target, nil))
  1891. request = sftp.NewRequest("Rename", "/dir4/testfile")
  1892. request.Target = "/dir1/testfile"
  1893. // rename is granted on Target but not on Source
  1894. assert.False(t, conn.isRenamePermitted("", request.Filepath, request.Target, nil))
  1895. request = sftp.NewRequest("Rename", "/dir4/testfile")
  1896. request.Target = "/testfile"
  1897. // rename is granted on Target but not on Source
  1898. assert.False(t, conn.isRenamePermitted("", request.Filepath, request.Target, nil))
  1899. request = sftp.NewRequest("Rename", "/dir3/testfile")
  1900. request.Target = "/dir2/testfile"
  1901. // delete is granted on Source and Upload on Target, the target is a file this is enough
  1902. assert.True(t, conn.isRenamePermitted("", request.Filepath, request.Target, nil))
  1903. request = sftp.NewRequest("Rename", "/dir2/testfile")
  1904. request.Target = "/dir3/testfile"
  1905. assert.False(t, conn.isRenamePermitted("", request.Filepath, request.Target, nil))
  1906. tmpDir := filepath.Join(os.TempDir(), "dir")
  1907. tmpDirLink := filepath.Join(os.TempDir(), "link")
  1908. err = os.Mkdir(tmpDir, os.ModePerm)
  1909. assert.NoError(t, err)
  1910. err = os.Symlink(tmpDir, tmpDirLink)
  1911. assert.NoError(t, err)
  1912. request.Filepath = "/dir"
  1913. request.Target = "/dir2/dir"
  1914. // the source is a dir and the target has no createDirs perm
  1915. info, err := os.Lstat(tmpDir)
  1916. if assert.NoError(t, err) {
  1917. assert.False(t, conn.isRenamePermitted(tmpDir, request.Filepath, request.Target, info))
  1918. conn.User.Permissions["/dir2"] = []string{dataprovider.PermUpload, dataprovider.PermCreateDirs}
  1919. // the source is a dir and the target has createDirs perm
  1920. assert.True(t, conn.isRenamePermitted(tmpDir, request.Filepath, request.Target, info))
  1921. request = sftp.NewRequest("Rename", "/testfile")
  1922. request.Target = "/dir5/testfile"
  1923. // the source is a dir and the target has createDirs and upload perm
  1924. assert.True(t, conn.isRenamePermitted(tmpDir, request.Filepath, request.Target, info))
  1925. }
  1926. info, err = os.Lstat(tmpDirLink)
  1927. if assert.NoError(t, err) {
  1928. assert.True(t, info.Mode()&os.ModeSymlink == os.ModeSymlink)
  1929. // the source is a symlink and the target has createDirs and upload perm
  1930. assert.False(t, conn.isRenamePermitted(tmpDir, request.Filepath, request.Target, info))
  1931. }
  1932. err = os.RemoveAll(tmpDir)
  1933. assert.NoError(t, err)
  1934. err = os.Remove(tmpDirLink)
  1935. assert.NoError(t, err)
  1936. conn.User.VirtualFolders = append(conn.User.VirtualFolders, vfs.VirtualFolder{
  1937. BaseVirtualFolder: vfs.BaseVirtualFolder{
  1938. MappedPath: os.TempDir(),
  1939. },
  1940. VirtualPath: "/dir1",
  1941. })
  1942. request = sftp.NewRequest("Rename", "/dir1")
  1943. request.Target = "/dir2/testfile"
  1944. // renaming a virtual folder is not allowed
  1945. assert.False(t, conn.isRenamePermitted("", request.Filepath, request.Target, nil))
  1946. err = conn.checkRecursiveRenameDirPermissions("invalid", "invalid")
  1947. assert.Error(t, err)
  1948. dir3 := filepath.Join(conn.User.HomeDir, "dir3")
  1949. dir6 := filepath.Join(conn.User.HomeDir, "dir6")
  1950. err = os.MkdirAll(filepath.Join(dir3, "subdir"), os.ModePerm)
  1951. assert.NoError(t, err)
  1952. err = ioutil.WriteFile(filepath.Join(dir3, "subdir", "testfile"), []byte("test"), os.ModePerm)
  1953. assert.NoError(t, err)
  1954. err = conn.checkRecursiveRenameDirPermissions(dir3, dir6)
  1955. assert.NoError(t, err)
  1956. err = os.RemoveAll(dir3)
  1957. assert.NoError(t, err)
  1958. }
  1959. func TestRecursiveCopyErrors(t *testing.T) {
  1960. permissions := make(map[string][]string)
  1961. permissions["/"] = []string{dataprovider.PermAny}
  1962. user := dataprovider.User{
  1963. Permissions: permissions,
  1964. HomeDir: os.TempDir(),
  1965. }
  1966. fs, err := user.GetFilesystem("123")
  1967. assert.NoError(t, err)
  1968. conn := Connection{
  1969. User: user,
  1970. fs: fs,
  1971. }
  1972. sshCmd := sshCommand{
  1973. command: "sftpgo-copy",
  1974. connection: conn,
  1975. args: []string{"adir", "another"},
  1976. }
  1977. // try to copy a missing directory
  1978. err = sshCmd.checkRecursiveCopyPermissions("adir", "another", "/another")
  1979. assert.Error(t, err)
  1980. }
  1981. func TestSSHMappedError(t *testing.T) {
  1982. user := dataprovider.User{
  1983. HomeDir: os.TempDir(),
  1984. }
  1985. fs, err := user.GetFilesystem("123")
  1986. assert.NoError(t, err)
  1987. conn := Connection{
  1988. User: user,
  1989. fs: fs,
  1990. }
  1991. sshCommand := sshCommand{
  1992. command: "test",
  1993. connection: conn,
  1994. args: []string{},
  1995. }
  1996. err = sshCommand.getMappedError(os.ErrNotExist)
  1997. assert.EqualError(t, err, errNotExist.Error())
  1998. err = sshCommand.getMappedError(os.ErrPermission)
  1999. assert.EqualError(t, err, errPermissionDenied.Error())
  2000. err = sshCommand.getMappedError(os.ErrInvalid)
  2001. assert.EqualError(t, err, errGenericFailure.Error())
  2002. err = sshCommand.getMappedError(os.ErrNoDeadline)
  2003. assert.EqualError(t, err, errGenericFailure.Error())
  2004. }