internal_test.go 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088
  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. err = os.Remove(testfile)
  423. assert.NoError(t, err)
  424. }
  425. func TestUploadFiles(t *testing.T) {
  426. oldUploadMode := uploadMode
  427. uploadMode = uploadModeAtomic
  428. c := Connection{
  429. fs: vfs.NewOsFs("123", os.TempDir(), nil),
  430. }
  431. var flags sftp.FileOpenFlags
  432. flags.Write = true
  433. flags.Trunc = true
  434. _, err := c.handleSFTPUploadToExistingFile(flags, "missing_path", "other_missing_path", 0, "/missing_path")
  435. assert.Error(t, err, "upload to existing file must fail if one or both paths are invalid")
  436. uploadMode = uploadModeStandard
  437. _, err = c.handleSFTPUploadToExistingFile(flags, "missing_path", "other_missing_path", 0, "/missing_path")
  438. assert.Error(t, err, "upload to existing file must fail if one or both paths are invalid")
  439. missingFile := "missing/relative/file.txt"
  440. if runtime.GOOS == osWindows {
  441. missingFile = "missing\\relative\\file.txt"
  442. }
  443. _, err = c.handleSFTPUploadToNewFile(".", missingFile, "/missing")
  444. assert.Error(t, err, "upload new file in missing path must fail")
  445. c.fs = newMockOsFs(nil, nil, false, "123", os.TempDir())
  446. f, err := ioutil.TempFile("", "temp")
  447. assert.NoError(t, err)
  448. err = f.Close()
  449. assert.NoError(t, err)
  450. _, err = c.handleSFTPUploadToExistingFile(flags, f.Name(), f.Name(), 123, f.Name())
  451. assert.NoError(t, err)
  452. if assert.Equal(t, 1, len(activeTransfers)) {
  453. transfer := activeTransfers[0]
  454. assert.Equal(t, int64(123), transfer.initialSize)
  455. err = transfer.Close()
  456. assert.NoError(t, err)
  457. assert.Equal(t, 0, len(activeTransfers))
  458. }
  459. err = os.Remove(f.Name())
  460. assert.NoError(t, err)
  461. uploadMode = oldUploadMode
  462. }
  463. func TestWithInvalidHome(t *testing.T) {
  464. u := dataprovider.User{}
  465. u.HomeDir = "home_rel_path" //nolint:goconst
  466. _, err := loginUser(u, dataprovider.SSHLoginMethodPassword, "", nil)
  467. assert.Error(t, err, "login a user with an invalid home_dir must fail")
  468. u.HomeDir = os.TempDir()
  469. fs, err := u.GetFilesystem("123")
  470. assert.NoError(t, err)
  471. c := Connection{
  472. User: u,
  473. fs: fs,
  474. }
  475. _, err = c.fs.ResolvePath("../upper_path")
  476. assert.Error(t, err, "tested path is not a home subdir")
  477. }
  478. func TestSFTPCmdTargetPath(t *testing.T) {
  479. u := dataprovider.User{}
  480. if runtime.GOOS == osWindows {
  481. u.HomeDir = "C:\\invalid_home"
  482. } else {
  483. u.HomeDir = "/invalid_home"
  484. }
  485. u.Username = "testuser"
  486. u.Permissions = make(map[string][]string)
  487. u.Permissions["/"] = []string{dataprovider.PermAny}
  488. fs, err := u.GetFilesystem("123")
  489. assert.NoError(t, err)
  490. connection := Connection{
  491. User: u,
  492. fs: fs,
  493. }
  494. _, err = connection.getSFTPCmdTargetPath("invalid_path")
  495. assert.EqualError(t, err, sftp.ErrSSHFxNoSuchFile.Error())
  496. }
  497. func TestGetSFTPErrorFromOSError(t *testing.T) {
  498. err := os.ErrNotExist
  499. fs := vfs.NewOsFs("", os.TempDir(), nil)
  500. err = vfs.GetSFTPError(fs, err)
  501. assert.EqualError(t, err, sftp.ErrSSHFxNoSuchFile.Error())
  502. err = os.ErrPermission
  503. err = vfs.GetSFTPError(fs, err)
  504. assert.EqualError(t, err, sftp.ErrSSHFxPermissionDenied.Error())
  505. err = vfs.GetSFTPError(fs, nil)
  506. assert.NoError(t, err)
  507. }
  508. func TestSetstatModeIgnore(t *testing.T) {
  509. originalMode := setstatMode
  510. setstatMode = 1
  511. connection := Connection{}
  512. err := connection.handleSFTPSetstat("invalid", nil)
  513. assert.NoError(t, err)
  514. setstatMode = originalMode
  515. }
  516. func TestSFTPGetUsedQuota(t *testing.T) {
  517. u := dataprovider.User{}
  518. u.HomeDir = "home_rel_path"
  519. u.Username = "test_invalid_user"
  520. u.QuotaSize = 4096
  521. u.QuotaFiles = 1
  522. u.Permissions = make(map[string][]string)
  523. u.Permissions["/"] = []string{dataprovider.PermAny}
  524. connection := Connection{
  525. User: u,
  526. }
  527. quotaResult := connection.hasSpace(false, "/")
  528. assert.False(t, quotaResult.HasSpace)
  529. }
  530. func TestSupportedSSHCommands(t *testing.T) {
  531. cmds := GetSupportedSSHCommands()
  532. assert.Equal(t, len(supportedSSHCommands), len(cmds))
  533. for _, c := range cmds {
  534. assert.True(t, utils.IsStringInSlice(c, supportedSSHCommands))
  535. }
  536. }
  537. func TestSSHCommandPath(t *testing.T) {
  538. buf := make([]byte, 65535)
  539. stdErrBuf := make([]byte, 65535)
  540. mockSSHChannel := MockChannel{
  541. Buffer: bytes.NewBuffer(buf),
  542. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  543. ReadError: nil,
  544. }
  545. connection := Connection{
  546. channel: &mockSSHChannel,
  547. }
  548. sshCommand := sshCommand{
  549. command: "test",
  550. connection: connection,
  551. args: []string{},
  552. }
  553. assert.Equal(t, "", sshCommand.getDestPath())
  554. sshCommand.args = []string{"-t", "/tmp/../path"}
  555. assert.Equal(t, "/path", sshCommand.getDestPath())
  556. sshCommand.args = []string{"-t", "/tmp/"}
  557. assert.Equal(t, "/tmp/", sshCommand.getDestPath())
  558. sshCommand.args = []string{"-t", "tmp/"}
  559. assert.Equal(t, "/tmp/", sshCommand.getDestPath())
  560. sshCommand.args = []string{"-t", "/tmp/../../../path"}
  561. assert.Equal(t, "/path", sshCommand.getDestPath())
  562. sshCommand.args = []string{"-t", ".."}
  563. assert.Equal(t, "/", sshCommand.getDestPath())
  564. sshCommand.args = []string{"-t", "."}
  565. assert.Equal(t, "/", 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{"-f", "/a space.txt"}
  573. assert.Equal(t, "/a space.txt", sshCommand.getDestPath())
  574. }
  575. func TestSSHParseCommandPayload(t *testing.T) {
  576. cmd := "command -a -f /ab\\ à/some\\ spaces\\ \\ \\(\\).txt"
  577. name, args, _ := parseCommandPayload(cmd)
  578. assert.Equal(t, "command", name)
  579. assert.Equal(t, 3, len(args))
  580. assert.Equal(t, "/ab à/some spaces ().txt", args[2])
  581. _, _, err := parseCommandPayload("")
  582. assert.Error(t, err, "parsing invalid command must fail")
  583. }
  584. func TestSSHCommandErrors(t *testing.T) {
  585. buf := make([]byte, 65535)
  586. stdErrBuf := make([]byte, 65535)
  587. readErr := fmt.Errorf("test read error")
  588. mockSSHChannel := MockChannel{
  589. Buffer: bytes.NewBuffer(buf),
  590. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  591. ReadError: readErr,
  592. }
  593. server, client := net.Pipe()
  594. defer func() {
  595. err := server.Close()
  596. assert.NoError(t, err)
  597. }()
  598. defer func() {
  599. err := client.Close()
  600. assert.NoError(t, err)
  601. }()
  602. user := dataprovider.User{}
  603. user.Permissions = make(map[string][]string)
  604. user.Permissions["/"] = []string{dataprovider.PermAny}
  605. fs, err := user.GetFilesystem("123")
  606. assert.NoError(t, err)
  607. connection := Connection{
  608. channel: &mockSSHChannel,
  609. netConn: client,
  610. User: user,
  611. fs: fs,
  612. }
  613. cmd := sshCommand{
  614. command: "md5sum",
  615. connection: connection,
  616. args: []string{},
  617. }
  618. err = cmd.handle()
  619. assert.Error(t, err, "ssh command must fail, we are sending a fake error")
  620. cmd = sshCommand{
  621. command: "md5sum",
  622. connection: connection,
  623. args: []string{"/../../test_file.dat"},
  624. }
  625. err = cmd.handle()
  626. assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
  627. cmd = sshCommand{
  628. command: "git-receive-pack",
  629. connection: connection,
  630. args: []string{"/../../testrepo"},
  631. }
  632. err = cmd.handle()
  633. assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
  634. cmd.connection.User.HomeDir = filepath.Clean(os.TempDir())
  635. cmd.connection.User.QuotaFiles = 1
  636. cmd.connection.User.UsedQuotaFiles = 2
  637. fs, err = cmd.connection.User.GetFilesystem("123")
  638. assert.NoError(t, err)
  639. cmd.connection.fs = fs
  640. err = cmd.handle()
  641. assert.EqualError(t, err, errQuotaExceeded.Error())
  642. cmd.connection.User.QuotaFiles = 0
  643. cmd.connection.User.UsedQuotaFiles = 0
  644. cmd.connection.User.Permissions = make(map[string][]string)
  645. cmd.connection.User.Permissions["/"] = []string{dataprovider.PermListItems}
  646. err = cmd.handle()
  647. assert.EqualError(t, err, errPermissionDenied.Error())
  648. cmd.connection.User.Permissions["/"] = []string{dataprovider.PermAny}
  649. cmd.command = "invalid_command"
  650. command, err := cmd.getSystemCommand()
  651. assert.NoError(t, err)
  652. err = cmd.executeSystemCommand(command)
  653. assert.Error(t, err, "invalid command must fail")
  654. command, err = cmd.getSystemCommand()
  655. assert.NoError(t, err)
  656. _, err = command.cmd.StderrPipe()
  657. assert.NoError(t, err)
  658. err = cmd.executeSystemCommand(command)
  659. assert.Error(t, err, "command must fail, pipe was already assigned")
  660. err = cmd.executeSystemCommand(command)
  661. assert.Error(t, err, "command must fail, pipe was already assigned")
  662. command, err = cmd.getSystemCommand()
  663. assert.NoError(t, err)
  664. _, err = command.cmd.StdoutPipe()
  665. assert.NoError(t, err)
  666. err = cmd.executeSystemCommand(command)
  667. assert.Error(t, err, "command must fail, pipe was already assigned")
  668. cmd = sshCommand{
  669. command: "sftpgo-remove",
  670. connection: connection,
  671. args: []string{"/../../src"},
  672. }
  673. err = cmd.handle()
  674. assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
  675. cmd = sshCommand{
  676. command: "sftpgo-copy",
  677. connection: connection,
  678. args: []string{"/../../test_src", "."},
  679. }
  680. err = cmd.handle()
  681. assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
  682. cmd.connection.fs = fs
  683. _, _, err = cmd.resolveCopyPaths(".", "../adir")
  684. assert.Error(t, err)
  685. cmd = sshCommand{
  686. command: "sftpgo-copy",
  687. connection: connection,
  688. args: []string{"src", "dst"},
  689. }
  690. cmd.connection.User.Permissions = make(map[string][]string)
  691. cmd.connection.User.Permissions["/"] = []string{dataprovider.PermDownload}
  692. _, _, err = cmd.getCopyPaths()
  693. if assert.Error(t, err) {
  694. assert.EqualError(t, err, errPermissionDenied.Error())
  695. }
  696. cmd.connection.User.Permissions = make(map[string][]string)
  697. cmd.connection.User.Permissions["/"] = []string{dataprovider.PermAny}
  698. if runtime.GOOS != osWindows {
  699. aDir := filepath.Join(os.TempDir(), "adir")
  700. err = os.MkdirAll(aDir, os.ModePerm)
  701. assert.NoError(t, err)
  702. tmpFile := filepath.Join(aDir, "testcopy")
  703. err = ioutil.WriteFile(tmpFile, []byte("aaa"), os.ModePerm)
  704. assert.NoError(t, err)
  705. err = os.Chmod(aDir, 0001)
  706. assert.NoError(t, err)
  707. err = cmd.checkCopyDestination(tmpFile)
  708. assert.Error(t, err)
  709. err = os.Chmod(aDir, os.ModePerm)
  710. assert.NoError(t, err)
  711. err = os.Remove(tmpFile)
  712. assert.NoError(t, err)
  713. }
  714. }
  715. func TestCommandsWithExtensionsFilter(t *testing.T) {
  716. buf := make([]byte, 65535)
  717. stdErrBuf := make([]byte, 65535)
  718. mockSSHChannel := MockChannel{
  719. Buffer: bytes.NewBuffer(buf),
  720. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  721. }
  722. server, client := net.Pipe()
  723. defer server.Close()
  724. defer client.Close()
  725. user := dataprovider.User{
  726. Username: "test",
  727. HomeDir: os.TempDir(),
  728. Status: 1,
  729. }
  730. user.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
  731. {
  732. Path: "/subdir",
  733. AllowedExtensions: []string{".jpg"},
  734. DeniedExtensions: []string{},
  735. },
  736. }
  737. fs, err := user.GetFilesystem("123")
  738. assert.NoError(t, err)
  739. connection := Connection{
  740. channel: &mockSSHChannel,
  741. netConn: client,
  742. User: user,
  743. fs: fs,
  744. }
  745. cmd := sshCommand{
  746. command: "md5sum",
  747. connection: connection,
  748. args: []string{"subdir/test.png"},
  749. }
  750. err = cmd.handleHashCommands()
  751. assert.EqualError(t, err, errPermissionDenied.Error())
  752. cmd = sshCommand{
  753. command: "rsync",
  754. connection: connection,
  755. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  756. }
  757. _, err = cmd.getSystemCommand()
  758. assert.EqualError(t, err, errUnsupportedConfig.Error())
  759. cmd = sshCommand{
  760. command: "git-receive-pack",
  761. connection: connection,
  762. args: []string{"/subdir"},
  763. }
  764. _, err = cmd.getSystemCommand()
  765. assert.EqualError(t, err, errUnsupportedConfig.Error())
  766. cmd = sshCommand{
  767. command: "git-receive-pack",
  768. connection: connection,
  769. args: []string{"/subdir/dir"},
  770. }
  771. _, err = cmd.getSystemCommand()
  772. assert.EqualError(t, err, errUnsupportedConfig.Error())
  773. cmd = sshCommand{
  774. command: "git-receive-pack",
  775. connection: connection,
  776. args: []string{"/adir/subdir"},
  777. }
  778. _, err = cmd.getSystemCommand()
  779. assert.NoError(t, err)
  780. }
  781. func TestSSHCommandsRemoteFs(t *testing.T) {
  782. buf := make([]byte, 65535)
  783. stdErrBuf := make([]byte, 65535)
  784. mockSSHChannel := MockChannel{
  785. Buffer: bytes.NewBuffer(buf),
  786. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  787. }
  788. server, client := net.Pipe()
  789. defer func() {
  790. err := server.Close()
  791. assert.NoError(t, err)
  792. }()
  793. defer func() {
  794. err := client.Close()
  795. assert.NoError(t, err)
  796. }()
  797. user := dataprovider.User{}
  798. user.FsConfig = dataprovider.Filesystem{
  799. Provider: 1,
  800. S3Config: vfs.S3FsConfig{
  801. Bucket: "s3bucket",
  802. Endpoint: "endpoint",
  803. Region: "eu-west-1",
  804. },
  805. }
  806. fs, err := user.GetFilesystem("123")
  807. assert.NoError(t, err)
  808. connection := Connection{
  809. channel: &mockSSHChannel,
  810. netConn: client,
  811. User: user,
  812. fs: fs,
  813. }
  814. cmd := sshCommand{
  815. command: "md5sum",
  816. connection: connection,
  817. args: []string{},
  818. }
  819. err = cmd.handleHashCommands()
  820. assert.Error(t, err, "command must fail for a non local filesystem")
  821. command, err := cmd.getSystemCommand()
  822. assert.NoError(t, err)
  823. err = cmd.executeSystemCommand(command)
  824. assert.Error(t, err, "command must fail for a non local filesystem")
  825. cmd = sshCommand{
  826. command: "sftpgo-copy",
  827. connection: connection,
  828. args: []string{},
  829. }
  830. err = cmd.handeSFTPGoCopy()
  831. assert.Error(t, err)
  832. cmd = sshCommand{
  833. command: "sftpgo-remove",
  834. connection: connection,
  835. args: []string{},
  836. }
  837. err = cmd.handeSFTPGoRemove()
  838. assert.Error(t, err)
  839. }
  840. func TestGitVirtualFolders(t *testing.T) {
  841. permissions := make(map[string][]string)
  842. permissions["/"] = []string{dataprovider.PermAny}
  843. user := dataprovider.User{
  844. Permissions: permissions,
  845. HomeDir: os.TempDir(),
  846. }
  847. fs, err := user.GetFilesystem("123")
  848. assert.NoError(t, err)
  849. conn := Connection{
  850. User: user,
  851. fs: fs,
  852. }
  853. cmd := sshCommand{
  854. command: "git-receive-pack",
  855. connection: conn,
  856. args: []string{"/vdir"},
  857. }
  858. cmd.connection.User.VirtualFolders = append(cmd.connection.User.VirtualFolders, vfs.VirtualFolder{
  859. BaseVirtualFolder: vfs.BaseVirtualFolder{
  860. MappedPath: os.TempDir(),
  861. },
  862. VirtualPath: "/vdir",
  863. })
  864. _, err = cmd.getSystemCommand()
  865. assert.NoError(t, err)
  866. cmd.args = []string{"/"}
  867. _, err = cmd.getSystemCommand()
  868. assert.EqualError(t, err, errUnsupportedConfig.Error())
  869. cmd.args = []string{"/vdir1"}
  870. _, err = cmd.getSystemCommand()
  871. assert.NoError(t, err)
  872. cmd.connection.User.VirtualFolders = nil
  873. cmd.connection.User.VirtualFolders = append(cmd.connection.User.VirtualFolders, vfs.VirtualFolder{
  874. BaseVirtualFolder: vfs.BaseVirtualFolder{
  875. MappedPath: os.TempDir(),
  876. },
  877. VirtualPath: "/vdir",
  878. })
  879. cmd.args = []string{"/vdir/subdir"}
  880. _, err = cmd.getSystemCommand()
  881. assert.NoError(t, err)
  882. cmd.args = []string{"/adir/subdir"}
  883. _, err = cmd.getSystemCommand()
  884. assert.NoError(t, err)
  885. }
  886. func TestRsyncOptions(t *testing.T) {
  887. permissions := make(map[string][]string)
  888. permissions["/"] = []string{dataprovider.PermAny}
  889. user := dataprovider.User{
  890. Permissions: permissions,
  891. HomeDir: os.TempDir(),
  892. }
  893. fs, err := user.GetFilesystem("123")
  894. assert.NoError(t, err)
  895. conn := Connection{
  896. User: user,
  897. fs: fs,
  898. }
  899. sshCmd := sshCommand{
  900. command: "rsync",
  901. connection: conn,
  902. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  903. }
  904. cmd, err := sshCmd.getSystemCommand()
  905. assert.NoError(t, err)
  906. assert.True(t, utils.IsStringInSlice("--safe-links", cmd.cmd.Args),
  907. "--safe-links must be added if the user has the create symlinks permission")
  908. permissions["/"] = []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermCreateDirs,
  909. dataprovider.PermListItems, dataprovider.PermOverwrite, dataprovider.PermDelete, dataprovider.PermRename}
  910. user.Permissions = permissions
  911. fs, err = user.GetFilesystem("123")
  912. assert.NoError(t, err)
  913. conn = Connection{
  914. User: user,
  915. fs: fs,
  916. }
  917. sshCmd = sshCommand{
  918. command: "rsync",
  919. connection: conn,
  920. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  921. }
  922. cmd, err = sshCmd.getSystemCommand()
  923. assert.NoError(t, err)
  924. assert.True(t, utils.IsStringInSlice("--munge-links", cmd.cmd.Args),
  925. "--munge-links must be added if the user has the create symlinks permission")
  926. sshCmd.connection.User.VirtualFolders = append(sshCmd.connection.User.VirtualFolders, vfs.VirtualFolder{
  927. BaseVirtualFolder: vfs.BaseVirtualFolder{
  928. MappedPath: os.TempDir(),
  929. },
  930. VirtualPath: "/vdir",
  931. })
  932. _, err = sshCmd.getSystemCommand()
  933. assert.EqualError(t, err, errUnsupportedConfig.Error())
  934. }
  935. func TestSpaceForCrossRename(t *testing.T) {
  936. if runtime.GOOS == osWindows {
  937. t.Skip("this test is not available on Windows")
  938. }
  939. permissions := make(map[string][]string)
  940. permissions["/"] = []string{dataprovider.PermAny}
  941. user := dataprovider.User{
  942. Permissions: permissions,
  943. HomeDir: os.TempDir(),
  944. }
  945. fs, err := user.GetFilesystem("123")
  946. assert.NoError(t, err)
  947. conn := Connection{
  948. User: user,
  949. fs: fs,
  950. }
  951. quotaResult := vfs.QuotaCheckResult{
  952. HasSpace: true,
  953. }
  954. assert.False(t, conn.hasSpaceForCrossRename(quotaResult, -1, filepath.Join(os.TempDir(), "a missing file")))
  955. testDir := filepath.Join(os.TempDir(), "dir")
  956. err = os.MkdirAll(testDir, os.ModePerm)
  957. assert.NoError(t, err)
  958. err = ioutil.WriteFile(filepath.Join(testDir, "afile"), []byte("content"), os.ModePerm)
  959. assert.NoError(t, err)
  960. err = os.Chmod(testDir, 0001)
  961. assert.NoError(t, err)
  962. assert.False(t, conn.hasSpaceForCrossRename(quotaResult, -1, testDir))
  963. err = os.Chmod(testDir, os.ModePerm)
  964. assert.NoError(t, err)
  965. err = os.RemoveAll(testDir)
  966. assert.NoError(t, err)
  967. }
  968. func TestSystemCommandSizeForPath(t *testing.T) {
  969. permissions := make(map[string][]string)
  970. permissions["/"] = []string{dataprovider.PermAny}
  971. user := dataprovider.User{
  972. Permissions: permissions,
  973. HomeDir: os.TempDir(),
  974. }
  975. fs, err := user.GetFilesystem("123")
  976. assert.NoError(t, err)
  977. conn := Connection{
  978. User: user,
  979. fs: fs,
  980. }
  981. sshCmd := sshCommand{
  982. command: "rsync",
  983. connection: conn,
  984. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  985. }
  986. _, _, err = sshCmd.getSizeForPath("missing path")
  987. assert.NoError(t, err)
  988. testDir := filepath.Join(os.TempDir(), "dir")
  989. err = os.MkdirAll(testDir, os.ModePerm)
  990. assert.NoError(t, err)
  991. testFile := filepath.Join(testDir, "testfile")
  992. err = ioutil.WriteFile(testFile, []byte("test content"), os.ModePerm)
  993. assert.NoError(t, err)
  994. err = os.Symlink(testFile, testFile+".link")
  995. assert.NoError(t, err)
  996. numFiles, size, err := sshCmd.getSizeForPath(testFile + ".link")
  997. assert.NoError(t, err)
  998. assert.Equal(t, 0, numFiles)
  999. assert.Equal(t, int64(0), size)
  1000. numFiles, size, err = sshCmd.getSizeForPath(testFile)
  1001. assert.NoError(t, err)
  1002. assert.Equal(t, 1, numFiles)
  1003. assert.Equal(t, int64(12), size)
  1004. if runtime.GOOS != osWindows {
  1005. err = os.Chmod(testDir, 0001)
  1006. assert.NoError(t, err)
  1007. _, _, err = sshCmd.getSizeForPath(testFile)
  1008. assert.Error(t, err)
  1009. err = os.Chmod(testDir, os.ModePerm)
  1010. assert.NoError(t, err)
  1011. }
  1012. err = os.RemoveAll(testDir)
  1013. assert.NoError(t, err)
  1014. }
  1015. func TestSystemCommandErrors(t *testing.T) {
  1016. buf := make([]byte, 65535)
  1017. stdErrBuf := make([]byte, 65535)
  1018. readErr := fmt.Errorf("test read error")
  1019. writeErr := fmt.Errorf("test write error")
  1020. mockSSHChannel := MockChannel{
  1021. Buffer: bytes.NewBuffer(buf),
  1022. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1023. ReadError: nil,
  1024. WriteError: writeErr,
  1025. }
  1026. server, client := net.Pipe()
  1027. defer func() {
  1028. err := server.Close()
  1029. assert.NoError(t, err)
  1030. }()
  1031. defer func() {
  1032. err := client.Close()
  1033. assert.NoError(t, err)
  1034. }()
  1035. permissions := make(map[string][]string)
  1036. permissions["/"] = []string{dataprovider.PermAny}
  1037. homeDir := filepath.Join(os.TempDir(), "adir")
  1038. err := os.MkdirAll(homeDir, os.ModePerm)
  1039. assert.NoError(t, err)
  1040. err = ioutil.WriteFile(filepath.Join(homeDir, "afile"), []byte("content"), os.ModePerm)
  1041. assert.NoError(t, err)
  1042. user := dataprovider.User{
  1043. Permissions: permissions,
  1044. HomeDir: homeDir,
  1045. }
  1046. fs, err := user.GetFilesystem("123")
  1047. assert.NoError(t, err)
  1048. connection := Connection{
  1049. channel: &mockSSHChannel,
  1050. netConn: client,
  1051. User: user,
  1052. fs: fs,
  1053. }
  1054. var sshCmd sshCommand
  1055. if runtime.GOOS == osWindows {
  1056. sshCmd = sshCommand{
  1057. command: "dir",
  1058. connection: connection,
  1059. args: []string{"/"},
  1060. }
  1061. } else {
  1062. sshCmd = sshCommand{
  1063. command: "ls",
  1064. connection: connection,
  1065. args: []string{"/"},
  1066. }
  1067. }
  1068. systemCmd, err := sshCmd.getSystemCommand()
  1069. assert.NoError(t, err)
  1070. systemCmd.cmd.Dir = os.TempDir()
  1071. // FIXME: the command completes but the fake client is unable to read the response
  1072. // no error is reported in this case. We can see that the expected code is executed
  1073. // reading the test coverage
  1074. sshCmd.executeSystemCommand(systemCmd) //nolint:errcheck
  1075. mockSSHChannel = MockChannel{
  1076. Buffer: bytes.NewBuffer(buf),
  1077. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1078. ReadError: readErr,
  1079. WriteError: nil,
  1080. }
  1081. sshCmd.connection.channel = &mockSSHChannel
  1082. transfer := Transfer{
  1083. transferType: transferDownload,
  1084. lock: new(sync.Mutex)}
  1085. destBuff := make([]byte, 65535)
  1086. dst := bytes.NewBuffer(destBuff)
  1087. _, err = transfer.copyFromReaderToWriter(dst, sshCmd.connection.channel)
  1088. assert.EqualError(t, err, readErr.Error())
  1089. mockSSHChannel = MockChannel{
  1090. Buffer: bytes.NewBuffer(buf),
  1091. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1092. ReadError: nil,
  1093. WriteError: nil,
  1094. }
  1095. sshCmd.connection.channel = &mockSSHChannel
  1096. transfer.maxWriteSize = 1
  1097. _, err = transfer.copyFromReaderToWriter(dst, sshCmd.connection.channel)
  1098. assert.EqualError(t, err, errQuotaExceeded.Error())
  1099. mockSSHChannel = MockChannel{
  1100. Buffer: bytes.NewBuffer(buf),
  1101. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1102. ReadError: nil,
  1103. WriteError: nil,
  1104. ShortWriteErr: true,
  1105. }
  1106. sshCmd.connection.channel = &mockSSHChannel
  1107. _, err = transfer.copyFromReaderToWriter(sshCmd.connection.channel, dst)
  1108. assert.EqualError(t, err, io.ErrShortWrite.Error())
  1109. transfer.maxWriteSize = -1
  1110. _, err = transfer.copyFromReaderToWriter(sshCmd.connection.channel, dst)
  1111. assert.EqualError(t, err, errQuotaExceeded.Error())
  1112. err = os.RemoveAll(homeDir)
  1113. assert.NoError(t, err)
  1114. }
  1115. func TestTransferUpdateQuota(t *testing.T) {
  1116. transfer := Transfer{
  1117. transferType: transferUpload,
  1118. bytesReceived: 123,
  1119. lock: new(sync.Mutex)}
  1120. transfer.TransferError(errors.New("fake error"))
  1121. assert.False(t, transfer.updateQuota(1))
  1122. }
  1123. func TestGetConnectionInfo(t *testing.T) {
  1124. c := ConnectionStatus{
  1125. Username: "test_user",
  1126. ConnectionID: "123",
  1127. ClientVersion: "client",
  1128. RemoteAddress: "127.0.0.1:1234",
  1129. Protocol: protocolSSH,
  1130. SSHCommand: "sha1sum /test_file.dat",
  1131. }
  1132. info := c.GetConnectionInfo()
  1133. assert.Contains(t, info, "sha1sum /test_file.dat")
  1134. }
  1135. func TestSCPFileMode(t *testing.T) {
  1136. mode := getFileModeAsString(0, true)
  1137. assert.Equal(t, "0755", mode)
  1138. mode = getFileModeAsString(0700, true)
  1139. assert.Equal(t, "0700", mode)
  1140. mode = getFileModeAsString(0750, true)
  1141. assert.Equal(t, "0750", mode)
  1142. mode = getFileModeAsString(0777, true)
  1143. assert.Equal(t, "0777", mode)
  1144. mode = getFileModeAsString(0640, false)
  1145. assert.Equal(t, "0640", mode)
  1146. mode = getFileModeAsString(0600, false)
  1147. assert.Equal(t, "0600", mode)
  1148. mode = getFileModeAsString(0, false)
  1149. assert.Equal(t, "0644", mode)
  1150. fileMode := uint32(0777)
  1151. fileMode = fileMode | uint32(os.ModeSetgid)
  1152. fileMode = fileMode | uint32(os.ModeSetuid)
  1153. fileMode = fileMode | uint32(os.ModeSticky)
  1154. mode = getFileModeAsString(os.FileMode(fileMode), false)
  1155. assert.Equal(t, "7777", mode)
  1156. fileMode = uint32(0644)
  1157. fileMode = fileMode | uint32(os.ModeSetgid)
  1158. mode = getFileModeAsString(os.FileMode(fileMode), false)
  1159. assert.Equal(t, "4644", mode)
  1160. fileMode = uint32(0600)
  1161. fileMode = fileMode | uint32(os.ModeSetuid)
  1162. mode = getFileModeAsString(os.FileMode(fileMode), false)
  1163. assert.Equal(t, "2600", mode)
  1164. fileMode = uint32(0044)
  1165. fileMode = fileMode | uint32(os.ModeSticky)
  1166. mode = getFileModeAsString(os.FileMode(fileMode), false)
  1167. assert.Equal(t, "1044", mode)
  1168. }
  1169. func TestSCPParseUploadMessage(t *testing.T) {
  1170. buf := make([]byte, 65535)
  1171. stdErrBuf := make([]byte, 65535)
  1172. mockSSHChannel := MockChannel{
  1173. Buffer: bytes.NewBuffer(buf),
  1174. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1175. ReadError: nil,
  1176. }
  1177. connection := Connection{
  1178. channel: &mockSSHChannel,
  1179. fs: vfs.NewOsFs("", os.TempDir(), nil),
  1180. }
  1181. scpCommand := scpCommand{
  1182. sshCommand: sshCommand{
  1183. command: "scp",
  1184. connection: connection,
  1185. args: []string{"-t", "/tmp"},
  1186. },
  1187. }
  1188. _, _, err := scpCommand.parseUploadMessage("invalid")
  1189. assert.Error(t, err, "parsing invalid upload message must fail")
  1190. _, _, err = scpCommand.parseUploadMessage("D0755 0")
  1191. assert.Error(t, err, "parsing incomplete upload message must fail")
  1192. _, _, err = scpCommand.parseUploadMessage("D0755 invalidsize testdir")
  1193. assert.Error(t, err, "parsing upload message with invalid size must fail")
  1194. _, _, err = scpCommand.parseUploadMessage("D0755 0 ")
  1195. assert.Error(t, err, "parsing upload message with invalid name must fail")
  1196. }
  1197. func TestSCPProtocolMessages(t *testing.T) {
  1198. buf := make([]byte, 65535)
  1199. stdErrBuf := make([]byte, 65535)
  1200. readErr := fmt.Errorf("test read error")
  1201. writeErr := fmt.Errorf("test write error")
  1202. mockSSHChannel := MockChannel{
  1203. Buffer: bytes.NewBuffer(buf),
  1204. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1205. ReadError: readErr,
  1206. WriteError: writeErr,
  1207. }
  1208. connection := Connection{
  1209. channel: &mockSSHChannel,
  1210. }
  1211. scpCommand := scpCommand{
  1212. sshCommand: sshCommand{
  1213. command: "scp",
  1214. connection: connection,
  1215. args: []string{"-t", "/tmp"},
  1216. },
  1217. }
  1218. _, err := scpCommand.readProtocolMessage()
  1219. assert.EqualError(t, err, readErr.Error())
  1220. err = scpCommand.sendConfirmationMessage()
  1221. assert.EqualError(t, err, writeErr.Error())
  1222. err = scpCommand.sendProtocolMessage("E\n")
  1223. assert.EqualError(t, err, writeErr.Error())
  1224. _, err = scpCommand.getNextUploadProtocolMessage()
  1225. assert.EqualError(t, err, readErr.Error())
  1226. mockSSHChannel = MockChannel{
  1227. Buffer: bytes.NewBuffer([]byte("T1183832947 0 1183833773 0\n")),
  1228. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1229. ReadError: nil,
  1230. WriteError: writeErr,
  1231. }
  1232. scpCommand.connection.channel = &mockSSHChannel
  1233. _, err = scpCommand.getNextUploadProtocolMessage()
  1234. assert.EqualError(t, err, writeErr.Error())
  1235. respBuffer := []byte{0x02}
  1236. protocolErrorMsg := "protocol error msg"
  1237. respBuffer = append(respBuffer, protocolErrorMsg...)
  1238. respBuffer = append(respBuffer, 0x0A)
  1239. mockSSHChannel = MockChannel{
  1240. Buffer: bytes.NewBuffer(respBuffer),
  1241. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1242. ReadError: nil,
  1243. WriteError: nil,
  1244. }
  1245. scpCommand.connection.channel = &mockSSHChannel
  1246. err = scpCommand.readConfirmationMessage()
  1247. if assert.Error(t, err) {
  1248. assert.Equal(t, protocolErrorMsg, err.Error())
  1249. }
  1250. }
  1251. func TestSCPTestDownloadProtocolMessages(t *testing.T) {
  1252. buf := make([]byte, 65535)
  1253. stdErrBuf := make([]byte, 65535)
  1254. readErr := fmt.Errorf("test read error")
  1255. writeErr := fmt.Errorf("test write error")
  1256. mockSSHChannel := MockChannel{
  1257. Buffer: bytes.NewBuffer(buf),
  1258. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1259. ReadError: readErr,
  1260. WriteError: writeErr,
  1261. }
  1262. connection := Connection{
  1263. channel: &mockSSHChannel,
  1264. }
  1265. scpCommand := scpCommand{
  1266. sshCommand: sshCommand{
  1267. command: "scp",
  1268. connection: connection,
  1269. args: []string{"-f", "-p", "/tmp"},
  1270. },
  1271. }
  1272. path := "testDir"
  1273. err := os.Mkdir(path, os.ModePerm)
  1274. assert.NoError(t, err)
  1275. stat, err := os.Stat(path)
  1276. assert.NoError(t, err)
  1277. err = scpCommand.sendDownloadProtocolMessages(path, stat)
  1278. assert.EqualError(t, err, writeErr.Error())
  1279. mockSSHChannel = MockChannel{
  1280. Buffer: bytes.NewBuffer(buf),
  1281. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1282. ReadError: readErr,
  1283. WriteError: nil,
  1284. }
  1285. err = scpCommand.sendDownloadProtocolMessages(path, stat)
  1286. assert.EqualError(t, err, readErr.Error())
  1287. mockSSHChannel = MockChannel{
  1288. Buffer: bytes.NewBuffer(buf),
  1289. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1290. ReadError: readErr,
  1291. WriteError: writeErr,
  1292. }
  1293. scpCommand.args = []string{"-f", "/tmp"}
  1294. scpCommand.connection.channel = &mockSSHChannel
  1295. err = scpCommand.sendDownloadProtocolMessages(path, stat)
  1296. assert.EqualError(t, err, writeErr.Error())
  1297. mockSSHChannel = MockChannel{
  1298. Buffer: bytes.NewBuffer(buf),
  1299. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1300. ReadError: readErr,
  1301. WriteError: nil,
  1302. }
  1303. scpCommand.connection.channel = &mockSSHChannel
  1304. err = scpCommand.sendDownloadProtocolMessages(path, stat)
  1305. assert.EqualError(t, err, readErr.Error())
  1306. err = os.Remove(path)
  1307. assert.NoError(t, err)
  1308. }
  1309. func TestSCPCommandHandleErrors(t *testing.T) {
  1310. buf := make([]byte, 65535)
  1311. stdErrBuf := make([]byte, 65535)
  1312. readErr := fmt.Errorf("test read error")
  1313. writeErr := fmt.Errorf("test write error")
  1314. mockSSHChannel := MockChannel{
  1315. Buffer: bytes.NewBuffer(buf),
  1316. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1317. ReadError: readErr,
  1318. WriteError: writeErr,
  1319. }
  1320. server, client := net.Pipe()
  1321. defer func() {
  1322. err := server.Close()
  1323. assert.NoError(t, err)
  1324. }()
  1325. defer func() {
  1326. err := client.Close()
  1327. assert.NoError(t, err)
  1328. }()
  1329. connection := Connection{
  1330. channel: &mockSSHChannel,
  1331. netConn: client,
  1332. }
  1333. scpCommand := scpCommand{
  1334. sshCommand: sshCommand{
  1335. command: "scp",
  1336. connection: connection,
  1337. args: []string{"-f", "/tmp"},
  1338. },
  1339. }
  1340. err := scpCommand.handle()
  1341. assert.EqualError(t, err, readErr.Error())
  1342. scpCommand.args = []string{"-i", "/tmp"}
  1343. err = scpCommand.handle()
  1344. assert.Error(t, err, "invalid scp command must fail")
  1345. }
  1346. func TestSCPErrorsMockFs(t *testing.T) {
  1347. errFake := errors.New("fake error")
  1348. fs := newMockOsFs(errFake, errFake, false, "1234", os.TempDir())
  1349. u := dataprovider.User{}
  1350. u.Username = "test"
  1351. u.Permissions = make(map[string][]string)
  1352. u.Permissions["/"] = []string{dataprovider.PermAny}
  1353. u.HomeDir = os.TempDir()
  1354. buf := make([]byte, 65535)
  1355. stdErrBuf := make([]byte, 65535)
  1356. mockSSHChannel := MockChannel{
  1357. Buffer: bytes.NewBuffer(buf),
  1358. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1359. }
  1360. server, client := net.Pipe()
  1361. defer func() {
  1362. err := server.Close()
  1363. assert.NoError(t, err)
  1364. }()
  1365. defer func() {
  1366. err := client.Close()
  1367. assert.NoError(t, err)
  1368. }()
  1369. connection := Connection{
  1370. channel: &mockSSHChannel,
  1371. netConn: client,
  1372. fs: fs,
  1373. User: u,
  1374. }
  1375. scpCommand := scpCommand{
  1376. sshCommand: sshCommand{
  1377. command: "scp",
  1378. connection: connection,
  1379. args: []string{"-r", "-t", "/tmp"},
  1380. },
  1381. }
  1382. err := scpCommand.handleUpload("test", 0)
  1383. assert.EqualError(t, err, errFake.Error())
  1384. testfile := filepath.Join(u.HomeDir, "testfile")
  1385. err = ioutil.WriteFile(testfile, []byte("test"), 0666)
  1386. assert.NoError(t, err)
  1387. stat, err := os.Stat(u.HomeDir)
  1388. assert.NoError(t, err)
  1389. err = scpCommand.handleRecursiveDownload(u.HomeDir, stat)
  1390. assert.EqualError(t, err, errFake.Error())
  1391. scpCommand.sshCommand.connection.fs = newMockOsFs(errFake, nil, true, "123", os.TempDir())
  1392. err = scpCommand.handleUpload(filepath.Base(testfile), 0)
  1393. assert.EqualError(t, err, errFake.Error())
  1394. err = scpCommand.handleUploadFile(testfile, testfile, 0, false, 4, "/testfile")
  1395. assert.NoError(t, err)
  1396. err = os.Remove(testfile)
  1397. assert.NoError(t, err)
  1398. }
  1399. func TestSCPRecursiveDownloadErrors(t *testing.T) {
  1400. buf := make([]byte, 65535)
  1401. stdErrBuf := make([]byte, 65535)
  1402. readErr := fmt.Errorf("test read error")
  1403. writeErr := fmt.Errorf("test write error")
  1404. mockSSHChannel := MockChannel{
  1405. Buffer: bytes.NewBuffer(buf),
  1406. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1407. ReadError: readErr,
  1408. WriteError: writeErr,
  1409. }
  1410. server, client := net.Pipe()
  1411. defer func() {
  1412. err := server.Close()
  1413. assert.NoError(t, err)
  1414. }()
  1415. defer func() {
  1416. err := client.Close()
  1417. assert.NoError(t, err)
  1418. }()
  1419. connection := Connection{
  1420. channel: &mockSSHChannel,
  1421. netConn: client,
  1422. fs: vfs.NewOsFs("123", os.TempDir(), nil),
  1423. }
  1424. scpCommand := scpCommand{
  1425. sshCommand: sshCommand{
  1426. command: "scp",
  1427. connection: connection,
  1428. args: []string{"-r", "-f", "/tmp"},
  1429. },
  1430. }
  1431. path := "testDir"
  1432. err := os.Mkdir(path, os.ModePerm)
  1433. assert.NoError(t, err)
  1434. stat, err := os.Stat(path)
  1435. assert.NoError(t, err)
  1436. err = scpCommand.handleRecursiveDownload("invalid_dir", stat)
  1437. assert.EqualError(t, err, writeErr.Error())
  1438. mockSSHChannel = MockChannel{
  1439. Buffer: bytes.NewBuffer(buf),
  1440. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1441. ReadError: nil,
  1442. WriteError: nil,
  1443. }
  1444. scpCommand.connection.channel = &mockSSHChannel
  1445. err = scpCommand.handleRecursiveDownload("invalid_dir", stat)
  1446. assert.Error(t, err, "recursive upload download must fail for a non existing dir")
  1447. err = os.Remove(path)
  1448. assert.NoError(t, err)
  1449. }
  1450. func TestSCPRecursiveUploadErrors(t *testing.T) {
  1451. buf := make([]byte, 65535)
  1452. stdErrBuf := make([]byte, 65535)
  1453. readErr := fmt.Errorf("test read error")
  1454. writeErr := fmt.Errorf("test write error")
  1455. mockSSHChannel := MockChannel{
  1456. Buffer: bytes.NewBuffer(buf),
  1457. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1458. ReadError: readErr,
  1459. WriteError: writeErr,
  1460. }
  1461. connection := Connection{
  1462. channel: &mockSSHChannel,
  1463. }
  1464. scpCommand := scpCommand{
  1465. sshCommand: sshCommand{
  1466. command: "scp",
  1467. connection: connection,
  1468. args: []string{"-r", "-t", "/tmp"},
  1469. },
  1470. }
  1471. err := scpCommand.handleRecursiveUpload()
  1472. assert.Error(t, err, "recursive upload must fail, we send a fake error message")
  1473. mockSSHChannel = MockChannel{
  1474. Buffer: bytes.NewBuffer(buf),
  1475. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1476. ReadError: readErr,
  1477. WriteError: nil,
  1478. }
  1479. scpCommand.connection.channel = &mockSSHChannel
  1480. err = scpCommand.handleRecursiveUpload()
  1481. assert.Error(t, err, "recursive upload must fail, we send a fake error message")
  1482. }
  1483. func TestSCPCreateDirs(t *testing.T) {
  1484. buf := make([]byte, 65535)
  1485. stdErrBuf := make([]byte, 65535)
  1486. u := dataprovider.User{}
  1487. u.HomeDir = "home_rel_path"
  1488. u.Username = "test"
  1489. u.Permissions = make(map[string][]string)
  1490. u.Permissions["/"] = []string{dataprovider.PermAny}
  1491. mockSSHChannel := MockChannel{
  1492. Buffer: bytes.NewBuffer(buf),
  1493. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1494. ReadError: nil,
  1495. WriteError: nil,
  1496. }
  1497. fs, err := u.GetFilesystem("123")
  1498. assert.NoError(t, err)
  1499. connection := Connection{
  1500. User: u,
  1501. channel: &mockSSHChannel,
  1502. fs: fs,
  1503. }
  1504. scpCommand := scpCommand{
  1505. sshCommand: sshCommand{
  1506. command: "scp",
  1507. connection: connection,
  1508. args: []string{"-r", "-t", "/tmp"},
  1509. },
  1510. }
  1511. err = scpCommand.handleCreateDir("invalid_dir")
  1512. assert.Error(t, err, "create invalid dir must fail")
  1513. }
  1514. func TestSCPDownloadFileData(t *testing.T) {
  1515. testfile := "testfile"
  1516. buf := make([]byte, 65535)
  1517. readErr := fmt.Errorf("test read error")
  1518. writeErr := fmt.Errorf("test write error")
  1519. stdErrBuf := make([]byte, 65535)
  1520. mockSSHChannelReadErr := MockChannel{
  1521. Buffer: bytes.NewBuffer(buf),
  1522. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1523. ReadError: readErr,
  1524. WriteError: nil,
  1525. }
  1526. mockSSHChannelWriteErr := MockChannel{
  1527. Buffer: bytes.NewBuffer(buf),
  1528. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1529. ReadError: nil,
  1530. WriteError: writeErr,
  1531. }
  1532. connection := Connection{
  1533. channel: &mockSSHChannelReadErr,
  1534. }
  1535. scpCommand := scpCommand{
  1536. sshCommand: sshCommand{
  1537. command: "scp",
  1538. connection: connection,
  1539. args: []string{"-r", "-f", "/tmp"},
  1540. },
  1541. }
  1542. err := ioutil.WriteFile(testfile, []byte("test"), 0666)
  1543. assert.NoError(t, err)
  1544. stat, err := os.Stat(testfile)
  1545. assert.NoError(t, err)
  1546. err = scpCommand.sendDownloadFileData(testfile, stat, nil)
  1547. assert.EqualError(t, err, readErr.Error())
  1548. scpCommand.connection.channel = &mockSSHChannelWriteErr
  1549. err = scpCommand.sendDownloadFileData(testfile, stat, nil)
  1550. assert.EqualError(t, err, writeErr.Error())
  1551. scpCommand.args = []string{"-r", "-p", "-f", "/tmp"}
  1552. err = scpCommand.sendDownloadFileData(testfile, stat, nil)
  1553. assert.EqualError(t, err, writeErr.Error())
  1554. scpCommand.connection.channel = &mockSSHChannelReadErr
  1555. err = scpCommand.sendDownloadFileData(testfile, stat, nil)
  1556. assert.EqualError(t, err, readErr.Error())
  1557. err = os.Remove(testfile)
  1558. assert.NoError(t, err)
  1559. }
  1560. func TestSCPUploadFiledata(t *testing.T) {
  1561. testfile := "testfile"
  1562. buf := make([]byte, 65535)
  1563. stdErrBuf := make([]byte, 65535)
  1564. readErr := fmt.Errorf("test read error")
  1565. writeErr := fmt.Errorf("test write error")
  1566. mockSSHChannel := MockChannel{
  1567. Buffer: bytes.NewBuffer(buf),
  1568. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1569. ReadError: readErr,
  1570. WriteError: writeErr,
  1571. }
  1572. connection := Connection{
  1573. User: dataprovider.User{
  1574. Username: "testuser",
  1575. },
  1576. protocol: protocolSCP,
  1577. channel: &mockSSHChannel,
  1578. fs: vfs.NewOsFs("", os.TempDir(), nil),
  1579. }
  1580. scpCommand := scpCommand{
  1581. sshCommand: sshCommand{
  1582. command: "scp",
  1583. connection: connection,
  1584. args: []string{"-r", "-t", "/tmp"},
  1585. },
  1586. }
  1587. file, err := os.Create(testfile)
  1588. assert.NoError(t, err)
  1589. transfer := Transfer{
  1590. file: file,
  1591. path: file.Name(),
  1592. start: time.Now(),
  1593. bytesSent: 0,
  1594. bytesReceived: 0,
  1595. user: scpCommand.connection.User,
  1596. connectionID: "",
  1597. transferType: transferDownload,
  1598. lastActivity: time.Now(),
  1599. isNewFile: true,
  1600. protocol: connection.protocol,
  1601. transferError: nil,
  1602. isFinished: false,
  1603. minWriteOffset: 0,
  1604. lock: new(sync.Mutex),
  1605. }
  1606. addTransfer(&transfer)
  1607. err = scpCommand.getUploadFileData(2, &transfer)
  1608. assert.Error(t, err, "upload must fail, we send a fake write error message")
  1609. mockSSHChannel = MockChannel{
  1610. Buffer: bytes.NewBuffer(buf),
  1611. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1612. ReadError: readErr,
  1613. WriteError: nil,
  1614. }
  1615. scpCommand.connection.channel = &mockSSHChannel
  1616. file, err = os.Create(testfile)
  1617. assert.NoError(t, err)
  1618. transfer.file = file
  1619. transfer.isFinished = false
  1620. addTransfer(&transfer)
  1621. err = scpCommand.getUploadFileData(2, &transfer)
  1622. assert.Error(t, err, "upload must fail, we send a fake read error message")
  1623. respBuffer := []byte("12")
  1624. respBuffer = append(respBuffer, 0x02)
  1625. mockSSHChannel = MockChannel{
  1626. Buffer: bytes.NewBuffer(respBuffer),
  1627. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1628. ReadError: nil,
  1629. WriteError: nil,
  1630. }
  1631. scpCommand.connection.channel = &mockSSHChannel
  1632. file, err = os.Create(testfile)
  1633. assert.NoError(t, err)
  1634. transfer.file = file
  1635. transfer.isFinished = false
  1636. addTransfer(&transfer)
  1637. err = scpCommand.getUploadFileData(2, &transfer)
  1638. assert.Error(t, err, "upload must fail, we have not enough data to read")
  1639. // the file is already closed so we have an error on trasfer closing
  1640. mockSSHChannel = MockChannel{
  1641. Buffer: bytes.NewBuffer(buf),
  1642. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1643. ReadError: nil,
  1644. WriteError: nil,
  1645. }
  1646. addTransfer(&transfer)
  1647. err = scpCommand.getUploadFileData(0, &transfer)
  1648. assert.EqualError(t, err, errTransferClosed.Error())
  1649. mockSSHChannel = MockChannel{
  1650. Buffer: bytes.NewBuffer(buf),
  1651. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1652. ReadError: nil,
  1653. WriteError: nil,
  1654. }
  1655. addTransfer(&transfer)
  1656. err = scpCommand.getUploadFileData(2, &transfer)
  1657. assert.True(t, errors.Is(err, os.ErrClosed))
  1658. err = os.Remove(testfile)
  1659. assert.NoError(t, err)
  1660. }
  1661. func TestUploadError(t *testing.T) {
  1662. oldUploadMode := uploadMode
  1663. uploadMode = uploadModeAtomic
  1664. connection := Connection{
  1665. User: dataprovider.User{
  1666. Username: "testuser",
  1667. },
  1668. protocol: protocolSCP,
  1669. }
  1670. testfile := "testfile"
  1671. fileTempName := "temptestfile"
  1672. file, err := os.Create(fileTempName)
  1673. assert.NoError(t, err)
  1674. transfer := Transfer{
  1675. file: file,
  1676. path: testfile,
  1677. start: time.Now(),
  1678. bytesSent: 0,
  1679. bytesReceived: 100,
  1680. user: connection.User,
  1681. connectionID: "",
  1682. transferType: transferUpload,
  1683. lastActivity: time.Now(),
  1684. isNewFile: true,
  1685. protocol: connection.protocol,
  1686. transferError: nil,
  1687. isFinished: false,
  1688. minWriteOffset: 0,
  1689. lock: new(sync.Mutex),
  1690. }
  1691. addTransfer(&transfer)
  1692. errFake := errors.New("fake error")
  1693. transfer.TransferError(errFake)
  1694. err = transfer.Close()
  1695. assert.EqualError(t, err, errFake.Error())
  1696. assert.Equal(t, int64(0), transfer.bytesReceived)
  1697. assert.NoFileExists(t, testfile)
  1698. assert.NoFileExists(t, fileTempName)
  1699. uploadMode = oldUploadMode
  1700. }
  1701. func TestConnectionStatusStruct(t *testing.T) {
  1702. var transfers []connectionTransfer
  1703. transferUL := connectionTransfer{
  1704. OperationType: operationUpload,
  1705. StartTime: utils.GetTimeAsMsSinceEpoch(time.Now()),
  1706. Size: 123,
  1707. LastActivity: utils.GetTimeAsMsSinceEpoch(time.Now()),
  1708. Path: "/test.upload",
  1709. }
  1710. transferDL := connectionTransfer{
  1711. OperationType: operationDownload,
  1712. StartTime: utils.GetTimeAsMsSinceEpoch(time.Now()),
  1713. Size: 123,
  1714. LastActivity: utils.GetTimeAsMsSinceEpoch(time.Now()),
  1715. Path: "/test.download",
  1716. }
  1717. transfers = append(transfers, transferUL)
  1718. transfers = append(transfers, transferDL)
  1719. c := ConnectionStatus{
  1720. Username: "test",
  1721. ConnectionID: "123",
  1722. ClientVersion: "fakeClient-1.0.0",
  1723. RemoteAddress: "127.0.0.1:1234",
  1724. ConnectionTime: utils.GetTimeAsMsSinceEpoch(time.Now()),
  1725. LastActivity: utils.GetTimeAsMsSinceEpoch(time.Now()),
  1726. Protocol: "SFTP",
  1727. Transfers: transfers,
  1728. }
  1729. durationString := c.GetConnectionDuration()
  1730. assert.NotEqual(t, 0, len(durationString))
  1731. transfersString := c.GetTransfersAsString()
  1732. assert.NotEqual(t, 0, len(transfersString))
  1733. connInfo := c.GetConnectionInfo()
  1734. assert.NotEqual(t, 0, len(connInfo))
  1735. }
  1736. func TestProxyProtocolVersion(t *testing.T) {
  1737. c := Configuration{
  1738. ProxyProtocol: 1,
  1739. }
  1740. proxyListener, err := c.getProxyListener(nil)
  1741. assert.NoError(t, err)
  1742. assert.Nil(t, proxyListener.Policy)
  1743. c.ProxyProtocol = 2
  1744. proxyListener, _ = c.getProxyListener(nil)
  1745. assert.NoError(t, err)
  1746. assert.NotNil(t, proxyListener.Policy)
  1747. c.ProxyProtocol = 1
  1748. c.ProxyAllowed = []string{"invalid"}
  1749. _, err = c.getProxyListener(nil)
  1750. assert.Error(t, err)
  1751. c.ProxyProtocol = 2
  1752. _, err = c.getProxyListener(nil)
  1753. assert.Error(t, err)
  1754. }
  1755. func TestLoadHostKeys(t *testing.T) {
  1756. configDir := ".."
  1757. serverConfig := &ssh.ServerConfig{}
  1758. c := Configuration{}
  1759. c.HostKeys = []string{".", "missing file"}
  1760. err := c.checkAndLoadHostKeys(configDir, serverConfig)
  1761. assert.Error(t, err)
  1762. testfile := filepath.Join(os.TempDir(), "invalidkey")
  1763. err = ioutil.WriteFile(testfile, []byte("some bytes"), 0666)
  1764. assert.NoError(t, err)
  1765. c.HostKeys = []string{testfile}
  1766. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1767. assert.Error(t, err)
  1768. err = os.Remove(testfile)
  1769. assert.NoError(t, err)
  1770. keysDir := filepath.Join(os.TempDir(), "keys")
  1771. err = os.MkdirAll(keysDir, os.ModePerm)
  1772. assert.NoError(t, err)
  1773. rsaKeyName := filepath.Join(keysDir, defaultPrivateRSAKeyName)
  1774. ecdsaKeyName := filepath.Join(keysDir, defaultPrivateECDSAKeyName)
  1775. nonDefaultKeyName := filepath.Join(keysDir, "akey")
  1776. c.HostKeys = []string{nonDefaultKeyName, rsaKeyName, ecdsaKeyName}
  1777. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1778. assert.Error(t, err)
  1779. assert.FileExists(t, rsaKeyName)
  1780. assert.FileExists(t, ecdsaKeyName)
  1781. assert.NoFileExists(t, nonDefaultKeyName)
  1782. err = os.Remove(rsaKeyName)
  1783. assert.NoError(t, err)
  1784. err = os.Remove(ecdsaKeyName)
  1785. assert.NoError(t, err)
  1786. if runtime.GOOS != osWindows {
  1787. err = os.Chmod(keysDir, 0551)
  1788. assert.NoError(t, err)
  1789. c.HostKeys = nil
  1790. err = c.checkAndLoadHostKeys(keysDir, serverConfig)
  1791. assert.Error(t, err)
  1792. c.HostKeys = []string{rsaKeyName, ecdsaKeyName}
  1793. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1794. assert.Error(t, err)
  1795. c.HostKeys = []string{ecdsaKeyName, rsaKeyName}
  1796. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1797. assert.Error(t, err)
  1798. err = os.Chmod(keysDir, 0755)
  1799. assert.NoError(t, err)
  1800. }
  1801. err = os.RemoveAll(keysDir)
  1802. assert.NoError(t, err)
  1803. }
  1804. func TestCertCheckerInitErrors(t *testing.T) {
  1805. c := Configuration{}
  1806. c.TrustedUserCAKeys = []string{".", "missing file"}
  1807. err := c.initializeCertChecker("")
  1808. assert.Error(t, err)
  1809. testfile := filepath.Join(os.TempDir(), "invalidkey")
  1810. err = ioutil.WriteFile(testfile, []byte("some bytes"), 0666)
  1811. assert.NoError(t, err)
  1812. c.TrustedUserCAKeys = []string{testfile}
  1813. err = c.initializeCertChecker("")
  1814. assert.Error(t, err)
  1815. err = os.Remove(testfile)
  1816. assert.NoError(t, err)
  1817. }
  1818. func TestUpdateQuotaAfterRenameMissingFile(t *testing.T) {
  1819. user := dataprovider.User{
  1820. Username: "username",
  1821. HomeDir: filepath.Join(os.TempDir(), "home"),
  1822. }
  1823. mappedPath := filepath.Join(os.TempDir(), "vdir")
  1824. user.Permissions = make(map[string][]string)
  1825. user.Permissions["/"] = []string{dataprovider.PermAny}
  1826. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  1827. BaseVirtualFolder: vfs.BaseVirtualFolder{
  1828. MappedPath: mappedPath,
  1829. },
  1830. VirtualPath: "/vdir",
  1831. })
  1832. c := Connection{
  1833. fs: vfs.NewOsFs("id", os.TempDir(), nil),
  1834. User: user,
  1835. }
  1836. request := sftp.NewRequest("Rename", "/testfile")
  1837. request.Filepath = "/dir"
  1838. request.Target = path.Join("vdir", "dir")
  1839. if runtime.GOOS != osWindows {
  1840. testDirPath := filepath.Join(mappedPath, "dir")
  1841. err := os.MkdirAll(testDirPath, os.ModePerm)
  1842. assert.NoError(t, err)
  1843. err = os.Chmod(testDirPath, 0001)
  1844. assert.NoError(t, err)
  1845. err = c.updateQuotaAfterRename(request, testDirPath, 0)
  1846. assert.Error(t, err)
  1847. err = os.Chmod(testDirPath, os.ModePerm)
  1848. assert.NoError(t, err)
  1849. err = os.RemoveAll(testDirPath)
  1850. assert.NoError(t, err)
  1851. }
  1852. request.Target = "/testfile1"
  1853. request.Filepath = path.Join("vdir", "file")
  1854. err := c.updateQuotaAfterRename(request, filepath.Join(os.TempDir(), "vdir", "file"), 0)
  1855. assert.Error(t, err)
  1856. }
  1857. func TestRenamePermission(t *testing.T) {
  1858. permissions := make(map[string][]string)
  1859. permissions["/"] = []string{dataprovider.PermAny}
  1860. permissions["/dir1"] = []string{dataprovider.PermRename}
  1861. permissions["/dir2"] = []string{dataprovider.PermUpload}
  1862. permissions["/dir3"] = []string{dataprovider.PermDelete}
  1863. permissions["/dir4"] = []string{dataprovider.PermListItems}
  1864. user := dataprovider.User{
  1865. Permissions: permissions,
  1866. HomeDir: os.TempDir(),
  1867. }
  1868. fs, err := user.GetFilesystem("123")
  1869. assert.NoError(t, err)
  1870. conn := Connection{
  1871. User: user,
  1872. fs: fs,
  1873. }
  1874. request := sftp.NewRequest("Rename", "/testfile")
  1875. request.Target = "/dir4/testfile"
  1876. // rename is not granted on Target
  1877. assert.False(t, conn.isRenamePermitted("", request))
  1878. request = sftp.NewRequest("Rename", "/dir4/testfile")
  1879. request.Target = "/dir1/testfile"
  1880. // rename is granted on Target, this is enough
  1881. assert.True(t, conn.isRenamePermitted("", request))
  1882. request = sftp.NewRequest("Rename", "/dir4/testfile")
  1883. request.Target = "/testfile"
  1884. // rename is granted on Target, this is enough
  1885. assert.True(t, conn.isRenamePermitted("", request))
  1886. request = sftp.NewRequest("Rename", "/dir3/testfile")
  1887. request.Target = "/dir2/testfile"
  1888. // delete is granted on Source and Upload on Target, the target is a file this is enough
  1889. assert.True(t, conn.isRenamePermitted("", request))
  1890. request = sftp.NewRequest("Rename", "/dir2/testfile")
  1891. request.Target = "/dir3/testfile"
  1892. assert.False(t, conn.isRenamePermitted("", request))
  1893. tmpDir := filepath.Join(os.TempDir(), "dir")
  1894. err = os.Mkdir(tmpDir, os.ModePerm)
  1895. assert.NoError(t, err)
  1896. request.Filepath = "/dir"
  1897. request.Target = "/dir2/dir"
  1898. // the source is a dir and the target has no createDirs perm
  1899. assert.False(t, conn.isRenamePermitted(tmpDir, request))
  1900. conn.User.Permissions["/dir2"] = []string{dataprovider.PermUpload, dataprovider.PermCreateDirs}
  1901. // the source is a dir and the target has createDirs perm
  1902. assert.True(t, conn.isRenamePermitted(tmpDir, request))
  1903. err = os.RemoveAll(tmpDir)
  1904. assert.NoError(t, err)
  1905. }