eventmanager_test.go 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616
  1. // Copyright (C) 2019-2022 Nicola Murino
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Affero General Public License as published
  5. // by the Free Software Foundation, version 3.
  6. //
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU Affero General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU Affero General Public License
  13. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. package common
  15. import (
  16. "bytes"
  17. "crypto/rand"
  18. "fmt"
  19. "io"
  20. "mime/multipart"
  21. "net/http"
  22. "os"
  23. "path"
  24. "path/filepath"
  25. "runtime"
  26. "strings"
  27. "testing"
  28. "time"
  29. "github.com/klauspost/compress/zip"
  30. "github.com/sftpgo/sdk"
  31. sdkkms "github.com/sftpgo/sdk/kms"
  32. "github.com/stretchr/testify/assert"
  33. "github.com/stretchr/testify/require"
  34. "github.com/drakkan/sftpgo/v2/internal/dataprovider"
  35. "github.com/drakkan/sftpgo/v2/internal/kms"
  36. "github.com/drakkan/sftpgo/v2/internal/util"
  37. "github.com/drakkan/sftpgo/v2/internal/vfs"
  38. )
  39. func TestEventRuleMatch(t *testing.T) {
  40. conditions := dataprovider.EventConditions{
  41. ProviderEvents: []string{"add", "update"},
  42. Options: dataprovider.ConditionOptions{
  43. Names: []dataprovider.ConditionPattern{
  44. {
  45. Pattern: "user1",
  46. InverseMatch: true,
  47. },
  48. },
  49. },
  50. }
  51. res := eventManager.checkProviderEventMatch(conditions, EventParams{
  52. Name: "user1",
  53. Event: "add",
  54. })
  55. assert.False(t, res)
  56. res = eventManager.checkProviderEventMatch(conditions, EventParams{
  57. Name: "user2",
  58. Event: "update",
  59. })
  60. assert.True(t, res)
  61. res = eventManager.checkProviderEventMatch(conditions, EventParams{
  62. Name: "user2",
  63. Event: "delete",
  64. })
  65. assert.False(t, res)
  66. conditions.Options.ProviderObjects = []string{"api_key"}
  67. res = eventManager.checkProviderEventMatch(conditions, EventParams{
  68. Name: "user2",
  69. Event: "update",
  70. ObjectType: "share",
  71. })
  72. assert.False(t, res)
  73. res = eventManager.checkProviderEventMatch(conditions, EventParams{
  74. Name: "user2",
  75. Event: "update",
  76. ObjectType: "api_key",
  77. })
  78. assert.True(t, res)
  79. // now test fs events
  80. conditions = dataprovider.EventConditions{
  81. FsEvents: []string{operationUpload, operationDownload},
  82. Options: dataprovider.ConditionOptions{
  83. Names: []dataprovider.ConditionPattern{
  84. {
  85. Pattern: "user*",
  86. },
  87. {
  88. Pattern: "tester*",
  89. },
  90. },
  91. FsPaths: []dataprovider.ConditionPattern{
  92. {
  93. Pattern: "*.txt",
  94. },
  95. },
  96. Protocols: []string{ProtocolSFTP},
  97. MinFileSize: 10,
  98. MaxFileSize: 30,
  99. },
  100. }
  101. params := EventParams{
  102. Name: "tester4",
  103. Event: operationDelete,
  104. VirtualPath: "/path.txt",
  105. Protocol: ProtocolSFTP,
  106. ObjectName: "path.txt",
  107. FileSize: 20,
  108. }
  109. res = eventManager.checkFsEventMatch(conditions, params)
  110. assert.False(t, res)
  111. params.Event = operationDownload
  112. res = eventManager.checkFsEventMatch(conditions, params)
  113. assert.True(t, res)
  114. params.Name = "name"
  115. res = eventManager.checkFsEventMatch(conditions, params)
  116. assert.False(t, res)
  117. params.Name = "user5"
  118. res = eventManager.checkFsEventMatch(conditions, params)
  119. assert.True(t, res)
  120. params.VirtualPath = "/sub/f.jpg"
  121. params.ObjectName = path.Base(params.VirtualPath)
  122. res = eventManager.checkFsEventMatch(conditions, params)
  123. assert.False(t, res)
  124. params.VirtualPath = "/sub/f.txt"
  125. params.ObjectName = path.Base(params.VirtualPath)
  126. res = eventManager.checkFsEventMatch(conditions, params)
  127. assert.True(t, res)
  128. params.Protocol = ProtocolHTTP
  129. res = eventManager.checkFsEventMatch(conditions, params)
  130. assert.False(t, res)
  131. params.Protocol = ProtocolSFTP
  132. params.FileSize = 5
  133. res = eventManager.checkFsEventMatch(conditions, params)
  134. assert.False(t, res)
  135. params.FileSize = 50
  136. res = eventManager.checkFsEventMatch(conditions, params)
  137. assert.False(t, res)
  138. params.FileSize = 25
  139. res = eventManager.checkFsEventMatch(conditions, params)
  140. assert.True(t, res)
  141. // bad pattern
  142. conditions.Options.Names = []dataprovider.ConditionPattern{
  143. {
  144. Pattern: "[-]",
  145. },
  146. }
  147. res = eventManager.checkFsEventMatch(conditions, params)
  148. assert.False(t, res)
  149. // check fs events with group name filters
  150. conditions = dataprovider.EventConditions{
  151. FsEvents: []string{operationUpload, operationDownload},
  152. Options: dataprovider.ConditionOptions{
  153. GroupNames: []dataprovider.ConditionPattern{
  154. {
  155. Pattern: "group*",
  156. },
  157. {
  158. Pattern: "testgroup*",
  159. },
  160. },
  161. },
  162. }
  163. params = EventParams{
  164. Name: "user1",
  165. Event: operationUpload,
  166. }
  167. res = eventManager.checkFsEventMatch(conditions, params)
  168. assert.False(t, res)
  169. params.Groups = []sdk.GroupMapping{
  170. {
  171. Name: "g1",
  172. Type: sdk.GroupTypePrimary,
  173. },
  174. {
  175. Name: "g2",
  176. Type: sdk.GroupTypeSecondary,
  177. },
  178. }
  179. res = eventManager.checkFsEventMatch(conditions, params)
  180. assert.False(t, res)
  181. params.Groups = []sdk.GroupMapping{
  182. {
  183. Name: "testgroup2",
  184. Type: sdk.GroupTypePrimary,
  185. },
  186. {
  187. Name: "g2",
  188. Type: sdk.GroupTypeSecondary,
  189. },
  190. }
  191. res = eventManager.checkFsEventMatch(conditions, params)
  192. assert.True(t, res)
  193. }
  194. func TestEventManager(t *testing.T) {
  195. startEventScheduler()
  196. action := &dataprovider.BaseEventAction{
  197. Name: "test_action",
  198. Type: dataprovider.ActionTypeHTTP,
  199. Options: dataprovider.BaseEventActionOptions{
  200. HTTPConfig: dataprovider.EventActionHTTPConfig{
  201. Endpoint: "http://localhost",
  202. Timeout: 20,
  203. Method: http.MethodGet,
  204. },
  205. },
  206. }
  207. err := dataprovider.AddEventAction(action, "", "", "")
  208. assert.NoError(t, err)
  209. rule := &dataprovider.EventRule{
  210. Name: "rule",
  211. Trigger: dataprovider.EventTriggerFsEvent,
  212. Conditions: dataprovider.EventConditions{
  213. FsEvents: []string{operationUpload},
  214. },
  215. Actions: []dataprovider.EventAction{
  216. {
  217. BaseEventAction: dataprovider.BaseEventAction{
  218. Name: action.Name,
  219. },
  220. Order: 1,
  221. },
  222. },
  223. }
  224. err = dataprovider.AddEventRule(rule, "", "", "")
  225. assert.NoError(t, err)
  226. eventManager.RLock()
  227. assert.Len(t, eventManager.FsEvents, 1)
  228. assert.Len(t, eventManager.ProviderEvents, 0)
  229. assert.Len(t, eventManager.Schedules, 0)
  230. assert.Len(t, eventManager.schedulesMapping, 0)
  231. eventManager.RUnlock()
  232. rule.Trigger = dataprovider.EventTriggerProviderEvent
  233. rule.Conditions = dataprovider.EventConditions{
  234. ProviderEvents: []string{"add"},
  235. }
  236. err = dataprovider.UpdateEventRule(rule, "", "", "")
  237. assert.NoError(t, err)
  238. eventManager.RLock()
  239. assert.Len(t, eventManager.FsEvents, 0)
  240. assert.Len(t, eventManager.ProviderEvents, 1)
  241. assert.Len(t, eventManager.Schedules, 0)
  242. assert.Len(t, eventManager.schedulesMapping, 0)
  243. eventManager.RUnlock()
  244. rule.Trigger = dataprovider.EventTriggerSchedule
  245. rule.Conditions = dataprovider.EventConditions{
  246. Schedules: []dataprovider.Schedule{
  247. {
  248. Hours: "0",
  249. DayOfWeek: "*",
  250. DayOfMonth: "*",
  251. Month: "*",
  252. },
  253. },
  254. }
  255. rule.DeletedAt = util.GetTimeAsMsSinceEpoch(time.Now().Add(-12 * time.Hour))
  256. eventManager.addUpdateRuleInternal(*rule)
  257. eventManager.RLock()
  258. assert.Len(t, eventManager.FsEvents, 0)
  259. assert.Len(t, eventManager.ProviderEvents, 0)
  260. assert.Len(t, eventManager.Schedules, 0)
  261. assert.Len(t, eventManager.schedulesMapping, 0)
  262. eventManager.RUnlock()
  263. assert.Eventually(t, func() bool {
  264. _, err = dataprovider.EventRuleExists(rule.Name)
  265. _, ok := err.(*util.RecordNotFoundError)
  266. return ok
  267. }, 2*time.Second, 100*time.Millisecond)
  268. rule.DeletedAt = 0
  269. err = dataprovider.AddEventRule(rule, "", "", "")
  270. assert.NoError(t, err)
  271. eventManager.RLock()
  272. assert.Len(t, eventManager.FsEvents, 0)
  273. assert.Len(t, eventManager.ProviderEvents, 0)
  274. assert.Len(t, eventManager.Schedules, 1)
  275. assert.Len(t, eventManager.schedulesMapping, 1)
  276. eventManager.RUnlock()
  277. err = dataprovider.DeleteEventRule(rule.Name, "", "", "")
  278. assert.NoError(t, err)
  279. eventManager.RLock()
  280. assert.Len(t, eventManager.FsEvents, 0)
  281. assert.Len(t, eventManager.ProviderEvents, 0)
  282. assert.Len(t, eventManager.Schedules, 0)
  283. assert.Len(t, eventManager.schedulesMapping, 0)
  284. eventManager.RUnlock()
  285. err = dataprovider.DeleteEventAction(action.Name, "", "", "")
  286. assert.NoError(t, err)
  287. stopEventScheduler()
  288. }
  289. func TestEventManagerErrors(t *testing.T) {
  290. startEventScheduler()
  291. providerConf := dataprovider.GetProviderConfig()
  292. err := dataprovider.Close()
  293. assert.NoError(t, err)
  294. params := EventParams{
  295. sender: "sender",
  296. }
  297. _, err = params.getUsers()
  298. assert.Error(t, err)
  299. _, err = params.getFolders()
  300. assert.Error(t, err)
  301. err = executeUsersQuotaResetRuleAction(dataprovider.ConditionOptions{}, &EventParams{})
  302. assert.Error(t, err)
  303. err = executeFoldersQuotaResetRuleAction(dataprovider.ConditionOptions{}, &EventParams{})
  304. assert.Error(t, err)
  305. err = executeTransferQuotaResetRuleAction(dataprovider.ConditionOptions{}, &EventParams{})
  306. assert.Error(t, err)
  307. err = executeMetadataCheckRuleAction(dataprovider.ConditionOptions{}, &EventParams{})
  308. assert.Error(t, err)
  309. err = executeDeleteFsRuleAction(nil, nil, dataprovider.ConditionOptions{}, &EventParams{})
  310. assert.Error(t, err)
  311. err = executeMkdirFsRuleAction(nil, nil, dataprovider.ConditionOptions{}, &EventParams{})
  312. assert.Error(t, err)
  313. err = executeRenameFsRuleAction(nil, nil, dataprovider.ConditionOptions{}, &EventParams{})
  314. assert.Error(t, err)
  315. err = executeExistFsRuleAction(nil, nil, dataprovider.ConditionOptions{}, &EventParams{})
  316. assert.Error(t, err)
  317. err = executeCompressFsRuleAction(dataprovider.EventActionFsCompress{}, nil, dataprovider.ConditionOptions{}, &EventParams{})
  318. assert.Error(t, err)
  319. groupName := "agroup"
  320. err = executeQuotaResetForUser(dataprovider.User{
  321. Groups: []sdk.GroupMapping{
  322. {
  323. Name: groupName,
  324. Type: sdk.GroupTypePrimary,
  325. },
  326. },
  327. })
  328. assert.Error(t, err)
  329. err = executeMetadataCheckForUser(dataprovider.User{
  330. Groups: []sdk.GroupMapping{
  331. {
  332. Name: groupName,
  333. Type: sdk.GroupTypePrimary,
  334. },
  335. },
  336. })
  337. assert.Error(t, err)
  338. err = executeDataRetentionCheckForUser(dataprovider.User{
  339. Groups: []sdk.GroupMapping{
  340. {
  341. Name: groupName,
  342. Type: sdk.GroupTypePrimary,
  343. },
  344. },
  345. }, nil, &EventParams{}, "")
  346. assert.Error(t, err)
  347. err = executeDeleteFsActionForUser(nil, nil, dataprovider.User{
  348. Groups: []sdk.GroupMapping{
  349. {
  350. Name: groupName,
  351. Type: sdk.GroupTypePrimary,
  352. },
  353. },
  354. })
  355. assert.Error(t, err)
  356. err = executeMkDirsFsActionForUser(nil, nil, dataprovider.User{
  357. Groups: []sdk.GroupMapping{
  358. {
  359. Name: groupName,
  360. Type: sdk.GroupTypePrimary,
  361. },
  362. },
  363. })
  364. assert.Error(t, err)
  365. err = executeRenameFsActionForUser(nil, nil, dataprovider.User{
  366. Groups: []sdk.GroupMapping{
  367. {
  368. Name: groupName,
  369. Type: sdk.GroupTypePrimary,
  370. },
  371. },
  372. })
  373. assert.Error(t, err)
  374. err = executeExistFsActionForUser(nil, nil, dataprovider.User{
  375. Groups: []sdk.GroupMapping{
  376. {
  377. Name: groupName,
  378. Type: sdk.GroupTypePrimary,
  379. },
  380. },
  381. })
  382. assert.Error(t, err)
  383. err = executeCompressFsActionForUser(dataprovider.EventActionFsCompress{}, nil, dataprovider.User{
  384. Groups: []sdk.GroupMapping{
  385. {
  386. Name: groupName,
  387. Type: sdk.GroupTypePrimary,
  388. },
  389. },
  390. })
  391. assert.Error(t, err)
  392. _, err = getMailAttachments(dataprovider.User{
  393. Groups: []sdk.GroupMapping{
  394. {
  395. Name: groupName,
  396. Type: sdk.GroupTypePrimary,
  397. },
  398. }}, []string{"/a", "/b"}, nil)
  399. assert.Error(t, err)
  400. _, _, err = getHTTPRuleActionBody(dataprovider.EventActionHTTPConfig{
  401. Method: http.MethodPost,
  402. Parts: []dataprovider.HTTPPart{
  403. {
  404. Name: "p1",
  405. },
  406. },
  407. }, nil, nil, dataprovider.User{
  408. BaseUser: sdk.BaseUser{
  409. Username: "u",
  410. },
  411. Groups: []sdk.GroupMapping{
  412. {
  413. Name: groupName,
  414. Type: sdk.GroupTypePrimary,
  415. },
  416. },
  417. }, &EventParams{})
  418. assert.Error(t, err)
  419. dataRetentionAction := dataprovider.BaseEventAction{
  420. Type: dataprovider.ActionTypeDataRetentionCheck,
  421. Options: dataprovider.BaseEventActionOptions{
  422. RetentionConfig: dataprovider.EventActionDataRetentionConfig{
  423. Folders: []dataprovider.FolderRetention{
  424. {
  425. Path: "/",
  426. Retention: 24,
  427. },
  428. },
  429. },
  430. },
  431. }
  432. err = executeRuleAction(dataRetentionAction, &EventParams{}, dataprovider.ConditionOptions{
  433. Names: []dataprovider.ConditionPattern{
  434. {
  435. Pattern: "username1",
  436. },
  437. },
  438. })
  439. if assert.Error(t, err) {
  440. assert.Contains(t, err.Error(), "unable to get users")
  441. }
  442. eventManager.loadRules()
  443. eventManager.RLock()
  444. assert.Len(t, eventManager.FsEvents, 0)
  445. assert.Len(t, eventManager.ProviderEvents, 0)
  446. assert.Len(t, eventManager.Schedules, 0)
  447. eventManager.RUnlock()
  448. // rule with invalid trigger
  449. eventManager.addUpdateRuleInternal(dataprovider.EventRule{
  450. Name: "test rule",
  451. Trigger: -1,
  452. })
  453. eventManager.RLock()
  454. assert.Len(t, eventManager.FsEvents, 0)
  455. assert.Len(t, eventManager.ProviderEvents, 0)
  456. assert.Len(t, eventManager.Schedules, 0)
  457. eventManager.RUnlock()
  458. // rule with invalid cronspec
  459. eventManager.addUpdateRuleInternal(dataprovider.EventRule{
  460. Name: "test rule",
  461. Trigger: dataprovider.EventTriggerSchedule,
  462. Conditions: dataprovider.EventConditions{
  463. Schedules: []dataprovider.Schedule{
  464. {
  465. Hours: "1000",
  466. },
  467. },
  468. },
  469. })
  470. eventManager.RLock()
  471. assert.Len(t, eventManager.FsEvents, 0)
  472. assert.Len(t, eventManager.ProviderEvents, 0)
  473. assert.Len(t, eventManager.Schedules, 0)
  474. eventManager.RUnlock()
  475. err = dataprovider.Initialize(providerConf, configDir, true)
  476. assert.NoError(t, err)
  477. stopEventScheduler()
  478. }
  479. func TestEventRuleActions(t *testing.T) {
  480. actionName := "test rule action"
  481. action := dataprovider.BaseEventAction{
  482. Name: actionName,
  483. Type: dataprovider.ActionTypeBackup,
  484. }
  485. err := executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{})
  486. assert.NoError(t, err)
  487. action.Type = -1
  488. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{})
  489. assert.Error(t, err)
  490. action = dataprovider.BaseEventAction{
  491. Name: actionName,
  492. Type: dataprovider.ActionTypeHTTP,
  493. Options: dataprovider.BaseEventActionOptions{
  494. HTTPConfig: dataprovider.EventActionHTTPConfig{
  495. Endpoint: "http://foo\x7f.com/", // invalid URL
  496. SkipTLSVerify: true,
  497. Body: "{{ObjectData}}",
  498. Method: http.MethodPost,
  499. QueryParameters: []dataprovider.KeyValue{
  500. {
  501. Key: "param",
  502. Value: "value",
  503. },
  504. },
  505. Timeout: 5,
  506. Headers: []dataprovider.KeyValue{
  507. {
  508. Key: "Content-Type",
  509. Value: "application/json",
  510. },
  511. },
  512. Username: "httpuser",
  513. },
  514. },
  515. }
  516. action.Options.SetEmptySecretsIfNil()
  517. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{})
  518. if assert.Error(t, err) {
  519. assert.Contains(t, err.Error(), "invalid endpoint")
  520. }
  521. action.Options.HTTPConfig.Endpoint = fmt.Sprintf("http://%v", httpAddr)
  522. params := &EventParams{
  523. Name: "a",
  524. Object: &dataprovider.User{
  525. BaseUser: sdk.BaseUser{
  526. Username: "test user",
  527. },
  528. },
  529. }
  530. err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
  531. assert.NoError(t, err)
  532. action.Options.HTTPConfig.Method = http.MethodGet
  533. err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
  534. assert.NoError(t, err)
  535. action.Options.HTTPConfig.Endpoint = fmt.Sprintf("http://%v/404", httpAddr)
  536. err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
  537. if assert.Error(t, err) {
  538. assert.Contains(t, err.Error(), "unexpected status code: 404")
  539. }
  540. action.Options.HTTPConfig.Endpoint = "http://invalid:1234"
  541. err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
  542. assert.Error(t, err)
  543. action.Options.HTTPConfig.QueryParameters = nil
  544. action.Options.HTTPConfig.Endpoint = "http://bar\x7f.com/"
  545. err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
  546. assert.Error(t, err)
  547. action.Options.HTTPConfig.Password = kms.NewSecret(sdkkms.SecretStatusSecretBox, "payload", "key", "data")
  548. err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
  549. if assert.Error(t, err) {
  550. assert.Contains(t, err.Error(), "unable to decrypt HTTP password")
  551. }
  552. action.Options.HTTPConfig.Password = kms.NewEmptySecret()
  553. action.Options.HTTPConfig.Body = ""
  554. action.Options.HTTPConfig.Parts = []dataprovider.HTTPPart{
  555. {
  556. Name: "p1",
  557. Filepath: "path",
  558. },
  559. }
  560. err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
  561. assert.Contains(t, getErrorString(err), "error getting user")
  562. action.Options.HTTPConfig.Parts = nil
  563. action.Options.HTTPConfig.Body = "{{ObjectData}}"
  564. // test disk and transfer quota reset
  565. username1 := "user1"
  566. username2 := "user2"
  567. user1 := dataprovider.User{
  568. BaseUser: sdk.BaseUser{
  569. Username: username1,
  570. HomeDir: filepath.Join(os.TempDir(), username1),
  571. Status: 1,
  572. Permissions: map[string][]string{
  573. "/": {dataprovider.PermAny},
  574. },
  575. },
  576. }
  577. user2 := dataprovider.User{
  578. BaseUser: sdk.BaseUser{
  579. Username: username2,
  580. HomeDir: filepath.Join(os.TempDir(), username2),
  581. Status: 1,
  582. Permissions: map[string][]string{
  583. "/": {dataprovider.PermAny},
  584. },
  585. },
  586. }
  587. err = dataprovider.AddUser(&user1, "", "", "")
  588. assert.NoError(t, err)
  589. err = dataprovider.AddUser(&user2, "", "", "")
  590. assert.NoError(t, err)
  591. action = dataprovider.BaseEventAction{
  592. Type: dataprovider.ActionTypeUserQuotaReset,
  593. }
  594. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  595. Names: []dataprovider.ConditionPattern{
  596. {
  597. Pattern: username1,
  598. },
  599. },
  600. })
  601. assert.Error(t, err) // no home dir
  602. // create the home dir
  603. err = os.MkdirAll(user1.GetHomeDir(), os.ModePerm)
  604. assert.NoError(t, err)
  605. err = os.WriteFile(filepath.Join(user1.GetHomeDir(), "file.txt"), []byte("user"), 0666)
  606. assert.NoError(t, err)
  607. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  608. Names: []dataprovider.ConditionPattern{
  609. {
  610. Pattern: username1,
  611. },
  612. },
  613. })
  614. assert.NoError(t, err)
  615. userGet, err := dataprovider.UserExists(username1, "")
  616. assert.NoError(t, err)
  617. assert.Equal(t, 1, userGet.UsedQuotaFiles)
  618. assert.Equal(t, int64(4), userGet.UsedQuotaSize)
  619. // simulate another quota scan in progress
  620. assert.True(t, QuotaScans.AddUserQuotaScan(username1, ""))
  621. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  622. Names: []dataprovider.ConditionPattern{
  623. {
  624. Pattern: username1,
  625. },
  626. },
  627. })
  628. assert.Error(t, err)
  629. assert.True(t, QuotaScans.RemoveUserQuotaScan(username1))
  630. // non matching pattern
  631. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  632. Names: []dataprovider.ConditionPattern{
  633. {
  634. Pattern: "don't match",
  635. },
  636. },
  637. })
  638. assert.Error(t, err)
  639. assert.Contains(t, getErrorString(err), "no user quota reset executed")
  640. action = dataprovider.BaseEventAction{
  641. Type: dataprovider.ActionTypeMetadataCheck,
  642. }
  643. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  644. Names: []dataprovider.ConditionPattern{
  645. {
  646. Pattern: "don't match",
  647. },
  648. },
  649. })
  650. assert.Error(t, err)
  651. assert.Contains(t, getErrorString(err), "no metadata check executed")
  652. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  653. Names: []dataprovider.ConditionPattern{
  654. {
  655. Pattern: username1,
  656. },
  657. },
  658. })
  659. assert.NoError(t, err)
  660. // simulate another metadata check in progress
  661. assert.True(t, ActiveMetadataChecks.Add(username1, ""))
  662. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  663. Names: []dataprovider.ConditionPattern{
  664. {
  665. Pattern: username1,
  666. },
  667. },
  668. })
  669. assert.Error(t, err)
  670. assert.True(t, ActiveMetadataChecks.Remove(username1))
  671. dataRetentionAction := dataprovider.BaseEventAction{
  672. Type: dataprovider.ActionTypeDataRetentionCheck,
  673. Options: dataprovider.BaseEventActionOptions{
  674. RetentionConfig: dataprovider.EventActionDataRetentionConfig{
  675. Folders: []dataprovider.FolderRetention{
  676. {
  677. Path: "",
  678. Retention: 24,
  679. },
  680. },
  681. },
  682. },
  683. }
  684. err = executeRuleAction(dataRetentionAction, &EventParams{}, dataprovider.ConditionOptions{
  685. Names: []dataprovider.ConditionPattern{
  686. {
  687. Pattern: username1,
  688. },
  689. },
  690. })
  691. assert.Error(t, err) // invalid config, no folder path specified
  692. retentionDir := "testretention"
  693. dataRetentionAction = dataprovider.BaseEventAction{
  694. Type: dataprovider.ActionTypeDataRetentionCheck,
  695. Options: dataprovider.BaseEventActionOptions{
  696. RetentionConfig: dataprovider.EventActionDataRetentionConfig{
  697. Folders: []dataprovider.FolderRetention{
  698. {
  699. Path: path.Join("/", retentionDir),
  700. Retention: 24,
  701. DeleteEmptyDirs: true,
  702. },
  703. },
  704. },
  705. },
  706. }
  707. // create some test files
  708. file1 := filepath.Join(user1.GetHomeDir(), "file1.txt")
  709. file2 := filepath.Join(user1.GetHomeDir(), retentionDir, "file2.txt")
  710. file3 := filepath.Join(user1.GetHomeDir(), retentionDir, "file3.txt")
  711. file4 := filepath.Join(user1.GetHomeDir(), retentionDir, "sub", "file4.txt")
  712. err = os.MkdirAll(filepath.Dir(file4), os.ModePerm)
  713. assert.NoError(t, err)
  714. for _, f := range []string{file1, file2, file3, file4} {
  715. err = os.WriteFile(f, []byte(""), 0666)
  716. assert.NoError(t, err)
  717. }
  718. timeBeforeRetention := time.Now().Add(-48 * time.Hour)
  719. err = os.Chtimes(file1, timeBeforeRetention, timeBeforeRetention)
  720. assert.NoError(t, err)
  721. err = os.Chtimes(file2, timeBeforeRetention, timeBeforeRetention)
  722. assert.NoError(t, err)
  723. err = os.Chtimes(file4, timeBeforeRetention, timeBeforeRetention)
  724. assert.NoError(t, err)
  725. err = executeRuleAction(dataRetentionAction, &EventParams{}, dataprovider.ConditionOptions{
  726. Names: []dataprovider.ConditionPattern{
  727. {
  728. Pattern: username1,
  729. },
  730. },
  731. })
  732. assert.NoError(t, err)
  733. assert.FileExists(t, file1)
  734. assert.NoFileExists(t, file2)
  735. assert.FileExists(t, file3)
  736. assert.NoDirExists(t, filepath.Dir(file4))
  737. // simulate another check in progress
  738. c := RetentionChecks.Add(RetentionCheck{}, &user1)
  739. assert.NotNil(t, c)
  740. err = executeRuleAction(dataRetentionAction, &EventParams{}, dataprovider.ConditionOptions{
  741. Names: []dataprovider.ConditionPattern{
  742. {
  743. Pattern: username1,
  744. },
  745. },
  746. })
  747. assert.Error(t, err)
  748. RetentionChecks.remove(user1.Username)
  749. err = executeRuleAction(dataRetentionAction, &EventParams{}, dataprovider.ConditionOptions{
  750. Names: []dataprovider.ConditionPattern{
  751. {
  752. Pattern: "no match",
  753. },
  754. },
  755. })
  756. assert.Error(t, err)
  757. assert.Contains(t, getErrorString(err), "no retention check executed")
  758. // test file exists action
  759. action = dataprovider.BaseEventAction{
  760. Type: dataprovider.ActionTypeFilesystem,
  761. Options: dataprovider.BaseEventActionOptions{
  762. FsConfig: dataprovider.EventActionFilesystemConfig{
  763. Type: dataprovider.FilesystemActionExist,
  764. Exist: []string{"/file1.txt", path.Join("/", retentionDir, "file3.txt")},
  765. },
  766. },
  767. }
  768. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  769. Names: []dataprovider.ConditionPattern{
  770. {
  771. Pattern: "no match",
  772. },
  773. },
  774. })
  775. assert.Error(t, err)
  776. assert.Contains(t, getErrorString(err), "no existence check executed")
  777. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  778. Names: []dataprovider.ConditionPattern{
  779. {
  780. Pattern: username1,
  781. },
  782. },
  783. })
  784. assert.NoError(t, err)
  785. action.Options.FsConfig.Exist = []string{"/file1.txt", path.Join("/", retentionDir, "file2.txt")}
  786. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  787. Names: []dataprovider.ConditionPattern{
  788. {
  789. Pattern: username1,
  790. },
  791. },
  792. })
  793. assert.Error(t, err)
  794. err = os.RemoveAll(user1.GetHomeDir())
  795. assert.NoError(t, err)
  796. err = dataprovider.UpdateUserTransferQuota(&user1, 100, 100, true)
  797. assert.NoError(t, err)
  798. action.Type = dataprovider.ActionTypeTransferQuotaReset
  799. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  800. Names: []dataprovider.ConditionPattern{
  801. {
  802. Pattern: username1,
  803. },
  804. },
  805. })
  806. assert.NoError(t, err)
  807. userGet, err = dataprovider.UserExists(username1, "")
  808. assert.NoError(t, err)
  809. assert.Equal(t, int64(0), userGet.UsedDownloadDataTransfer)
  810. assert.Equal(t, int64(0), userGet.UsedUploadDataTransfer)
  811. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  812. Names: []dataprovider.ConditionPattern{
  813. {
  814. Pattern: "no match",
  815. },
  816. },
  817. })
  818. assert.Error(t, err)
  819. assert.Contains(t, getErrorString(err), "no transfer quota reset executed")
  820. action.Type = dataprovider.ActionTypeFilesystem
  821. action.Options = dataprovider.BaseEventActionOptions{
  822. FsConfig: dataprovider.EventActionFilesystemConfig{
  823. Type: dataprovider.FilesystemActionRename,
  824. Renames: []dataprovider.KeyValue{
  825. {
  826. Key: "/source",
  827. Value: "/target",
  828. },
  829. },
  830. },
  831. }
  832. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  833. Names: []dataprovider.ConditionPattern{
  834. {
  835. Pattern: "no match",
  836. },
  837. },
  838. })
  839. assert.Error(t, err)
  840. assert.Contains(t, getErrorString(err), "no rename executed")
  841. action.Options = dataprovider.BaseEventActionOptions{
  842. FsConfig: dataprovider.EventActionFilesystemConfig{
  843. Type: dataprovider.FilesystemActionDelete,
  844. Deletes: []string{"/dir1"},
  845. },
  846. }
  847. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  848. Names: []dataprovider.ConditionPattern{
  849. {
  850. Pattern: "no match",
  851. },
  852. },
  853. })
  854. assert.Error(t, err)
  855. assert.Contains(t, getErrorString(err), "no delete executed")
  856. action.Options = dataprovider.BaseEventActionOptions{
  857. FsConfig: dataprovider.EventActionFilesystemConfig{
  858. Type: dataprovider.FilesystemActionMkdirs,
  859. Deletes: []string{"/dir1"},
  860. },
  861. }
  862. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  863. Names: []dataprovider.ConditionPattern{
  864. {
  865. Pattern: "no match",
  866. },
  867. },
  868. })
  869. assert.Error(t, err)
  870. assert.Contains(t, getErrorString(err), "no mkdir executed")
  871. action.Options = dataprovider.BaseEventActionOptions{
  872. FsConfig: dataprovider.EventActionFilesystemConfig{
  873. Type: dataprovider.FilesystemActionCompress,
  874. Compress: dataprovider.EventActionFsCompress{
  875. Name: "test.zip",
  876. Paths: []string{"/{{VirtualPath}}"},
  877. },
  878. },
  879. }
  880. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  881. Names: []dataprovider.ConditionPattern{
  882. {
  883. Pattern: "no match",
  884. },
  885. },
  886. })
  887. assert.Error(t, err)
  888. assert.Contains(t, getErrorString(err), "no file/folder compressed")
  889. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  890. GroupNames: []dataprovider.ConditionPattern{
  891. {
  892. Pattern: "no match",
  893. },
  894. },
  895. })
  896. assert.Error(t, err)
  897. assert.Contains(t, getErrorString(err), "no file/folder compressed")
  898. err = dataprovider.DeleteUser(username1, "", "", "")
  899. assert.NoError(t, err)
  900. err = dataprovider.DeleteUser(username2, "", "", "")
  901. assert.NoError(t, err)
  902. // test folder quota reset
  903. foldername1 := "f1"
  904. foldername2 := "f2"
  905. folder1 := vfs.BaseVirtualFolder{
  906. Name: foldername1,
  907. MappedPath: filepath.Join(os.TempDir(), foldername1),
  908. }
  909. folder2 := vfs.BaseVirtualFolder{
  910. Name: foldername2,
  911. MappedPath: filepath.Join(os.TempDir(), foldername2),
  912. }
  913. err = dataprovider.AddFolder(&folder1, "", "", "")
  914. assert.NoError(t, err)
  915. err = dataprovider.AddFolder(&folder2, "", "", "")
  916. assert.NoError(t, err)
  917. action = dataprovider.BaseEventAction{
  918. Type: dataprovider.ActionTypeFolderQuotaReset,
  919. }
  920. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  921. Names: []dataprovider.ConditionPattern{
  922. {
  923. Pattern: foldername1,
  924. },
  925. },
  926. })
  927. assert.Error(t, err) // no home dir
  928. err = os.MkdirAll(folder1.MappedPath, os.ModePerm)
  929. assert.NoError(t, err)
  930. err = os.WriteFile(filepath.Join(folder1.MappedPath, "file.txt"), []byte("folder"), 0666)
  931. assert.NoError(t, err)
  932. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  933. Names: []dataprovider.ConditionPattern{
  934. {
  935. Pattern: foldername1,
  936. },
  937. },
  938. })
  939. assert.NoError(t, err)
  940. folderGet, err := dataprovider.GetFolderByName(foldername1)
  941. assert.NoError(t, err)
  942. assert.Equal(t, 1, folderGet.UsedQuotaFiles)
  943. assert.Equal(t, int64(6), folderGet.UsedQuotaSize)
  944. // simulate another quota scan in progress
  945. assert.True(t, QuotaScans.AddVFolderQuotaScan(foldername1))
  946. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  947. Names: []dataprovider.ConditionPattern{
  948. {
  949. Pattern: foldername1,
  950. },
  951. },
  952. })
  953. assert.Error(t, err)
  954. assert.True(t, QuotaScans.RemoveVFolderQuotaScan(foldername1))
  955. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  956. Names: []dataprovider.ConditionPattern{
  957. {
  958. Pattern: "no folder match",
  959. },
  960. },
  961. })
  962. if assert.Error(t, err) {
  963. assert.Contains(t, err.Error(), "no folder quota reset executed")
  964. }
  965. body, _, err := getHTTPRuleActionBody(dataprovider.EventActionHTTPConfig{
  966. Method: http.MethodPost,
  967. }, nil, nil, dataprovider.User{}, &EventParams{})
  968. assert.NoError(t, err)
  969. assert.Nil(t, body)
  970. err = os.RemoveAll(folder1.MappedPath)
  971. assert.NoError(t, err)
  972. err = dataprovider.DeleteFolder(foldername1, "", "", "")
  973. assert.NoError(t, err)
  974. err = dataprovider.DeleteFolder(foldername2, "", "", "")
  975. assert.NoError(t, err)
  976. }
  977. func TestEventRuleActionsNoGroupMatching(t *testing.T) {
  978. username := "test_user_action_group_matching"
  979. user := dataprovider.User{
  980. BaseUser: sdk.BaseUser{
  981. Username: username,
  982. Permissions: map[string][]string{
  983. "/": {dataprovider.PermAny},
  984. },
  985. HomeDir: filepath.Join(os.TempDir(), username),
  986. },
  987. }
  988. err := dataprovider.AddUser(&user, "", "", "")
  989. assert.NoError(t, err)
  990. conditions := dataprovider.ConditionOptions{
  991. GroupNames: []dataprovider.ConditionPattern{
  992. {
  993. Pattern: "agroup",
  994. },
  995. },
  996. }
  997. err = executeDeleteFsRuleAction(nil, nil, conditions, &EventParams{})
  998. if assert.Error(t, err) {
  999. assert.Contains(t, err.Error(), "no delete executed")
  1000. }
  1001. err = executeMkdirFsRuleAction(nil, nil, conditions, &EventParams{})
  1002. if assert.Error(t, err) {
  1003. assert.Contains(t, err.Error(), "no mkdir executed")
  1004. }
  1005. err = executeRenameFsRuleAction(nil, nil, conditions, &EventParams{})
  1006. if assert.Error(t, err) {
  1007. assert.Contains(t, err.Error(), "no rename executed")
  1008. }
  1009. err = executeExistFsRuleAction(nil, nil, conditions, &EventParams{})
  1010. if assert.Error(t, err) {
  1011. assert.Contains(t, err.Error(), "no existence check executed")
  1012. }
  1013. err = executeUsersQuotaResetRuleAction(conditions, &EventParams{})
  1014. if assert.Error(t, err) {
  1015. assert.Contains(t, err.Error(), "no user quota reset executed")
  1016. }
  1017. err = executeMetadataCheckRuleAction(conditions, &EventParams{})
  1018. if assert.Error(t, err) {
  1019. assert.Contains(t, err.Error(), "no metadata check executed")
  1020. }
  1021. err = executeTransferQuotaResetRuleAction(conditions, &EventParams{})
  1022. if assert.Error(t, err) {
  1023. assert.Contains(t, err.Error(), "no transfer quota reset executed")
  1024. }
  1025. err = executeDataRetentionCheckRuleAction(dataprovider.EventActionDataRetentionConfig{}, conditions, &EventParams{}, "")
  1026. if assert.Error(t, err) {
  1027. assert.Contains(t, err.Error(), "no retention check executed")
  1028. }
  1029. err = dataprovider.DeleteUser(username, "", "", "")
  1030. assert.NoError(t, err)
  1031. err = os.RemoveAll(user.GetHomeDir())
  1032. assert.NoError(t, err)
  1033. }
  1034. func TestGetFileContent(t *testing.T) {
  1035. username := "test_user_get_file_content"
  1036. user := dataprovider.User{
  1037. BaseUser: sdk.BaseUser{
  1038. Username: username,
  1039. Permissions: map[string][]string{
  1040. "/": {dataprovider.PermAny},
  1041. },
  1042. HomeDir: filepath.Join(os.TempDir(), username),
  1043. },
  1044. }
  1045. err := dataprovider.AddUser(&user, "", "", "")
  1046. assert.NoError(t, err)
  1047. err = os.MkdirAll(user.GetHomeDir(), os.ModePerm)
  1048. assert.NoError(t, err)
  1049. fileContent := []byte("test file content")
  1050. err = os.WriteFile(filepath.Join(user.GetHomeDir(), "file.txt"), fileContent, 0666)
  1051. assert.NoError(t, err)
  1052. replacer := strings.NewReplacer("old", "new")
  1053. files, err := getMailAttachments(user, []string{"/file.txt"}, replacer)
  1054. assert.NoError(t, err)
  1055. if assert.Len(t, files, 1) {
  1056. assert.Equal(t, fileContent, files[0].Data)
  1057. }
  1058. // missing file
  1059. _, err = getMailAttachments(user, []string{"/file1.txt"}, replacer)
  1060. assert.Error(t, err)
  1061. // directory
  1062. _, err = getMailAttachments(user, []string{"/"}, replacer)
  1063. assert.Error(t, err)
  1064. // files too large
  1065. content := make([]byte, maxAttachmentsSize/2+1)
  1066. _, err = rand.Read(content)
  1067. assert.NoError(t, err)
  1068. err = os.WriteFile(filepath.Join(user.GetHomeDir(), "file1.txt"), content, 0666)
  1069. assert.NoError(t, err)
  1070. err = os.WriteFile(filepath.Join(user.GetHomeDir(), "file2.txt"), content, 0666)
  1071. assert.NoError(t, err)
  1072. files, err = getMailAttachments(user, []string{"/file1.txt"}, replacer)
  1073. assert.NoError(t, err)
  1074. if assert.Len(t, files, 1) {
  1075. assert.Equal(t, content, files[0].Data)
  1076. }
  1077. _, err = getMailAttachments(user, []string{"/file1.txt", "/file2.txt"}, replacer)
  1078. if assert.Error(t, err) {
  1079. assert.Contains(t, err.Error(), "size too large")
  1080. }
  1081. // change the filesystem provider
  1082. user.FsConfig.Provider = sdk.CryptedFilesystemProvider
  1083. user.FsConfig.CryptConfig.Passphrase = kms.NewPlainSecret("pwd")
  1084. err = dataprovider.UpdateUser(&user, "", "", "")
  1085. assert.NoError(t, err)
  1086. // the file is not encrypted so reading the encryption header will fail
  1087. _, err = getMailAttachments(user, []string{"/file.txt"}, replacer)
  1088. assert.Error(t, err)
  1089. err = dataprovider.DeleteUser(username, "", "", "")
  1090. assert.NoError(t, err)
  1091. err = os.RemoveAll(user.GetHomeDir())
  1092. assert.NoError(t, err)
  1093. }
  1094. func TestFilesystemActionErrors(t *testing.T) {
  1095. err := executeFsRuleAction(dataprovider.EventActionFilesystemConfig{}, dataprovider.ConditionOptions{}, &EventParams{})
  1096. if assert.Error(t, err) {
  1097. assert.Contains(t, err.Error(), "unsupported filesystem action")
  1098. }
  1099. username := "test_user_for_actions"
  1100. testReplacer := strings.NewReplacer("old", "new")
  1101. user := dataprovider.User{
  1102. BaseUser: sdk.BaseUser{
  1103. Username: username,
  1104. Permissions: map[string][]string{
  1105. "/": {dataprovider.PermAny},
  1106. },
  1107. HomeDir: filepath.Join(os.TempDir(), username),
  1108. },
  1109. FsConfig: vfs.Filesystem{
  1110. Provider: sdk.SFTPFilesystemProvider,
  1111. SFTPConfig: vfs.SFTPFsConfig{
  1112. BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
  1113. Endpoint: "127.0.0.1:4022",
  1114. Username: username,
  1115. },
  1116. Password: kms.NewPlainSecret("pwd"),
  1117. },
  1118. },
  1119. }
  1120. err = executeEmailRuleAction(dataprovider.EventActionEmailConfig{
  1121. Recipients: []string{"test@example.net"},
  1122. Subject: "subject",
  1123. Body: "body",
  1124. Attachments: []string{"/file.txt"},
  1125. }, &EventParams{
  1126. sender: username,
  1127. })
  1128. assert.Error(t, err)
  1129. conn := NewBaseConnection("", protocolEventAction, "", "", user)
  1130. err = executeDeleteFileFsAction(conn, "", nil)
  1131. assert.Error(t, err)
  1132. err = dataprovider.AddUser(&user, "", "", "")
  1133. assert.NoError(t, err)
  1134. // check root fs fails
  1135. err = executeDeleteFsActionForUser(nil, testReplacer, user)
  1136. assert.Error(t, err)
  1137. err = executeMkDirsFsActionForUser(nil, testReplacer, user)
  1138. assert.Error(t, err)
  1139. err = executeRenameFsActionForUser(nil, testReplacer, user)
  1140. assert.Error(t, err)
  1141. err = executeExistFsActionForUser(nil, testReplacer, user)
  1142. assert.Error(t, err)
  1143. err = executeCompressFsActionForUser(dataprovider.EventActionFsCompress{}, testReplacer, user)
  1144. assert.Error(t, err)
  1145. _, _, _, _, err = getFileWriter(conn, "/path.txt") //nolint:dogsled
  1146. assert.Error(t, err)
  1147. err = executeEmailRuleAction(dataprovider.EventActionEmailConfig{
  1148. Recipients: []string{"test@example.net"},
  1149. Subject: "subject",
  1150. Body: "body",
  1151. Attachments: []string{"/file1.txt"},
  1152. }, &EventParams{
  1153. sender: username,
  1154. })
  1155. assert.Error(t, err)
  1156. _, err = getFileContent(NewBaseConnection("", protocolEventAction, "", "", user), "/f.txt", 1234)
  1157. assert.Error(t, err)
  1158. err = executeHTTPRuleAction(dataprovider.EventActionHTTPConfig{
  1159. Endpoint: "http://127.0.0.1:9999/",
  1160. Method: http.MethodPost,
  1161. Parts: []dataprovider.HTTPPart{
  1162. {
  1163. Name: "p1",
  1164. Filepath: "/filepath",
  1165. },
  1166. },
  1167. }, &EventParams{
  1168. sender: username,
  1169. })
  1170. assert.Error(t, err)
  1171. user.FsConfig.Provider = sdk.LocalFilesystemProvider
  1172. user.Permissions["/"] = []string{dataprovider.PermUpload}
  1173. err = dataprovider.DeleteUser(username, "", "", "")
  1174. assert.NoError(t, err)
  1175. err = dataprovider.AddUser(&user, "", "", "")
  1176. assert.NoError(t, err)
  1177. err = executeRenameFsActionForUser([]dataprovider.KeyValue{
  1178. {
  1179. Key: "/p1",
  1180. Value: "/p1",
  1181. },
  1182. }, testReplacer, user)
  1183. if assert.Error(t, err) {
  1184. assert.Contains(t, err.Error(), "the rename source and target cannot be the same")
  1185. }
  1186. err = executeRuleAction(dataprovider.BaseEventAction{
  1187. Type: dataprovider.ActionTypeFilesystem,
  1188. Options: dataprovider.BaseEventActionOptions{
  1189. FsConfig: dataprovider.EventActionFilesystemConfig{
  1190. Type: dataprovider.FilesystemActionRename,
  1191. Renames: []dataprovider.KeyValue{
  1192. {
  1193. Key: "/p2",
  1194. Value: "/p2",
  1195. },
  1196. },
  1197. },
  1198. },
  1199. }, &EventParams{}, dataprovider.ConditionOptions{
  1200. Names: []dataprovider.ConditionPattern{
  1201. {
  1202. Pattern: username,
  1203. },
  1204. },
  1205. })
  1206. assert.Error(t, err)
  1207. if runtime.GOOS != osWindows {
  1208. dirPath := filepath.Join(user.HomeDir, "adir", "sub")
  1209. err := os.MkdirAll(dirPath, os.ModePerm)
  1210. assert.NoError(t, err)
  1211. filePath := filepath.Join(dirPath, "f.dat")
  1212. err = os.WriteFile(filePath, []byte("test file content"), 0666)
  1213. assert.NoError(t, err)
  1214. err = os.Chmod(dirPath, 0001)
  1215. assert.NoError(t, err)
  1216. err = executeDeleteFsActionForUser([]string{"/adir/sub"}, testReplacer, user)
  1217. assert.Error(t, err)
  1218. err = executeDeleteFsActionForUser([]string{"/adir/sub/f.dat"}, testReplacer, user)
  1219. assert.Error(t, err)
  1220. err = os.Chmod(dirPath, 0555)
  1221. assert.NoError(t, err)
  1222. err = executeDeleteFsActionForUser([]string{"/adir/sub/f.dat"}, testReplacer, user)
  1223. if assert.Error(t, err) {
  1224. assert.Contains(t, err.Error(), "unable to remove file")
  1225. }
  1226. err = executeRuleAction(dataprovider.BaseEventAction{
  1227. Type: dataprovider.ActionTypeFilesystem,
  1228. Options: dataprovider.BaseEventActionOptions{
  1229. FsConfig: dataprovider.EventActionFilesystemConfig{
  1230. Type: dataprovider.FilesystemActionDelete,
  1231. Deletes: []string{"/adir/sub/f.dat"},
  1232. },
  1233. },
  1234. }, &EventParams{}, dataprovider.ConditionOptions{
  1235. Names: []dataprovider.ConditionPattern{
  1236. {
  1237. Pattern: username,
  1238. },
  1239. },
  1240. })
  1241. assert.Error(t, err)
  1242. err = executeMkDirsFsActionForUser([]string{"/adir/sub/sub"}, testReplacer, user)
  1243. if assert.Error(t, err) {
  1244. assert.Contains(t, err.Error(), "unable to create dir")
  1245. }
  1246. err = executeMkDirsFsActionForUser([]string{"/adir/sub/sub/sub"}, testReplacer, user)
  1247. if assert.Error(t, err) {
  1248. assert.Contains(t, err.Error(), "unable to check parent dirs")
  1249. }
  1250. err = executeRuleAction(dataprovider.BaseEventAction{
  1251. Type: dataprovider.ActionTypeFilesystem,
  1252. Options: dataprovider.BaseEventActionOptions{
  1253. FsConfig: dataprovider.EventActionFilesystemConfig{
  1254. Type: dataprovider.FilesystemActionMkdirs,
  1255. MkDirs: []string{"/adir/sub/sub1"},
  1256. },
  1257. },
  1258. }, &EventParams{}, dataprovider.ConditionOptions{
  1259. Names: []dataprovider.ConditionPattern{
  1260. {
  1261. Pattern: username,
  1262. },
  1263. },
  1264. })
  1265. assert.Error(t, err)
  1266. err = os.Chmod(dirPath, os.ModePerm)
  1267. assert.NoError(t, err)
  1268. conn = NewBaseConnection("", protocolEventAction, "", "", user)
  1269. wr := &zipWriterWrapper{
  1270. Name: "test.zip",
  1271. Writer: zip.NewWriter(bytes.NewBuffer(nil)),
  1272. Entries: map[string]bool{},
  1273. }
  1274. err = addZipEntry(wr, conn, "/adir/sub/f.dat", "/adir/sub/sub")
  1275. assert.Error(t, err)
  1276. assert.Contains(t, getErrorString(err), "is outside base dir")
  1277. }
  1278. err = dataprovider.DeleteUser(username, "", "", "")
  1279. assert.NoError(t, err)
  1280. err = os.RemoveAll(user.GetHomeDir())
  1281. assert.NoError(t, err)
  1282. }
  1283. func TestQuotaActionsWithQuotaTrackDisabled(t *testing.T) {
  1284. oldProviderConf := dataprovider.GetProviderConfig()
  1285. providerConf := dataprovider.GetProviderConfig()
  1286. providerConf.TrackQuota = 0
  1287. err := dataprovider.Close()
  1288. assert.NoError(t, err)
  1289. err = dataprovider.Initialize(providerConf, configDir, true)
  1290. assert.NoError(t, err)
  1291. username := "u1"
  1292. user := dataprovider.User{
  1293. BaseUser: sdk.BaseUser{
  1294. Username: username,
  1295. HomeDir: filepath.Join(os.TempDir(), username),
  1296. Status: 1,
  1297. Permissions: map[string][]string{
  1298. "/": {dataprovider.PermAny},
  1299. },
  1300. },
  1301. FsConfig: vfs.Filesystem{
  1302. Provider: sdk.LocalFilesystemProvider,
  1303. },
  1304. }
  1305. err = dataprovider.AddUser(&user, "", "", "")
  1306. assert.NoError(t, err)
  1307. err = os.MkdirAll(user.GetHomeDir(), os.ModePerm)
  1308. assert.NoError(t, err)
  1309. err = executeRuleAction(dataprovider.BaseEventAction{Type: dataprovider.ActionTypeUserQuotaReset},
  1310. &EventParams{}, dataprovider.ConditionOptions{
  1311. Names: []dataprovider.ConditionPattern{
  1312. {
  1313. Pattern: username,
  1314. },
  1315. },
  1316. })
  1317. assert.Error(t, err)
  1318. err = executeRuleAction(dataprovider.BaseEventAction{Type: dataprovider.ActionTypeTransferQuotaReset},
  1319. &EventParams{}, dataprovider.ConditionOptions{
  1320. Names: []dataprovider.ConditionPattern{
  1321. {
  1322. Pattern: username,
  1323. },
  1324. },
  1325. })
  1326. assert.Error(t, err)
  1327. err = os.RemoveAll(user.GetHomeDir())
  1328. assert.NoError(t, err)
  1329. err = dataprovider.DeleteUser(username, "", "", "")
  1330. assert.NoError(t, err)
  1331. foldername := "f1"
  1332. folder := vfs.BaseVirtualFolder{
  1333. Name: foldername,
  1334. MappedPath: filepath.Join(os.TempDir(), foldername),
  1335. }
  1336. err = dataprovider.AddFolder(&folder, "", "", "")
  1337. assert.NoError(t, err)
  1338. err = os.MkdirAll(folder.MappedPath, os.ModePerm)
  1339. assert.NoError(t, err)
  1340. err = executeRuleAction(dataprovider.BaseEventAction{Type: dataprovider.ActionTypeFolderQuotaReset},
  1341. &EventParams{}, dataprovider.ConditionOptions{
  1342. Names: []dataprovider.ConditionPattern{
  1343. {
  1344. Pattern: foldername,
  1345. },
  1346. },
  1347. })
  1348. assert.Error(t, err)
  1349. err = os.RemoveAll(folder.MappedPath)
  1350. assert.NoError(t, err)
  1351. err = dataprovider.DeleteFolder(foldername, "", "", "")
  1352. assert.NoError(t, err)
  1353. err = dataprovider.Close()
  1354. assert.NoError(t, err)
  1355. err = dataprovider.Initialize(oldProviderConf, configDir, true)
  1356. assert.NoError(t, err)
  1357. }
  1358. func TestScheduledActions(t *testing.T) {
  1359. startEventScheduler()
  1360. backupsPath := filepath.Join(os.TempDir(), "backups")
  1361. err := os.RemoveAll(backupsPath)
  1362. assert.NoError(t, err)
  1363. action := &dataprovider.BaseEventAction{
  1364. Name: "action",
  1365. Type: dataprovider.ActionTypeBackup,
  1366. }
  1367. err = dataprovider.AddEventAction(action, "", "", "")
  1368. assert.NoError(t, err)
  1369. rule := &dataprovider.EventRule{
  1370. Name: "rule",
  1371. Trigger: dataprovider.EventTriggerSchedule,
  1372. Conditions: dataprovider.EventConditions{
  1373. Schedules: []dataprovider.Schedule{
  1374. {
  1375. Hours: "11",
  1376. DayOfWeek: "*",
  1377. DayOfMonth: "*",
  1378. Month: "*",
  1379. },
  1380. },
  1381. },
  1382. Actions: []dataprovider.EventAction{
  1383. {
  1384. BaseEventAction: dataprovider.BaseEventAction{
  1385. Name: action.Name,
  1386. },
  1387. Order: 1,
  1388. },
  1389. },
  1390. }
  1391. job := eventCronJob{
  1392. ruleName: rule.Name,
  1393. }
  1394. job.Run() // rule not found
  1395. assert.NoDirExists(t, backupsPath)
  1396. err = dataprovider.AddEventRule(rule, "", "", "")
  1397. assert.NoError(t, err)
  1398. job.Run()
  1399. assert.DirExists(t, backupsPath)
  1400. action.Type = dataprovider.ActionTypeEmail
  1401. action.Options = dataprovider.BaseEventActionOptions{
  1402. EmailConfig: dataprovider.EventActionEmailConfig{
  1403. Recipients: []string{"example@example.com"},
  1404. Subject: "test with attachments",
  1405. Body: "body",
  1406. Attachments: []string{"/file1.txt"},
  1407. },
  1408. }
  1409. err = dataprovider.UpdateEventAction(action, "", "", "")
  1410. assert.NoError(t, err)
  1411. job.Run() // action is not compatible with a scheduled rule
  1412. err = dataprovider.DeleteEventRule(rule.Name, "", "", "")
  1413. assert.NoError(t, err)
  1414. err = dataprovider.DeleteEventAction(action.Name, "", "", "")
  1415. assert.NoError(t, err)
  1416. err = os.RemoveAll(backupsPath)
  1417. assert.NoError(t, err)
  1418. stopEventScheduler()
  1419. }
  1420. func TestEventParamsCopy(t *testing.T) {
  1421. params := EventParams{
  1422. Name: "name",
  1423. Event: "event",
  1424. Status: 1,
  1425. errors: []string{"error1"},
  1426. retentionChecks: []executedRetentionCheck{},
  1427. }
  1428. paramsCopy := params.getACopy()
  1429. assert.Equal(t, params, *paramsCopy)
  1430. params.Name = "name mod"
  1431. paramsCopy.Event = "event mod"
  1432. paramsCopy.Status = 2
  1433. params.errors = append(params.errors, "error2")
  1434. paramsCopy.errors = append(paramsCopy.errors, "error3")
  1435. assert.Equal(t, []string{"error1", "error3"}, paramsCopy.errors)
  1436. assert.Equal(t, []string{"error1", "error2"}, params.errors)
  1437. assert.Equal(t, "name mod", params.Name)
  1438. assert.Equal(t, "name", paramsCopy.Name)
  1439. assert.Equal(t, "event", params.Event)
  1440. assert.Equal(t, "event mod", paramsCopy.Event)
  1441. assert.Equal(t, 1, params.Status)
  1442. assert.Equal(t, 2, paramsCopy.Status)
  1443. params = EventParams{
  1444. retentionChecks: []executedRetentionCheck{
  1445. {
  1446. Username: "u",
  1447. ActionName: "a",
  1448. Results: []folderRetentionCheckResult{
  1449. {
  1450. Path: "p",
  1451. Retention: 1,
  1452. },
  1453. },
  1454. },
  1455. },
  1456. }
  1457. paramsCopy = params.getACopy()
  1458. require.Len(t, paramsCopy.retentionChecks, 1)
  1459. paramsCopy.retentionChecks[0].Username = "u_copy"
  1460. paramsCopy.retentionChecks[0].ActionName = "a_copy"
  1461. require.Len(t, paramsCopy.retentionChecks[0].Results, 1)
  1462. paramsCopy.retentionChecks[0].Results[0].Path = "p_copy"
  1463. paramsCopy.retentionChecks[0].Results[0].Retention = 2
  1464. assert.Equal(t, "u", params.retentionChecks[0].Username)
  1465. assert.Equal(t, "a", params.retentionChecks[0].ActionName)
  1466. assert.Equal(t, "p", params.retentionChecks[0].Results[0].Path)
  1467. assert.Equal(t, 1, params.retentionChecks[0].Results[0].Retention)
  1468. assert.Equal(t, "u_copy", paramsCopy.retentionChecks[0].Username)
  1469. assert.Equal(t, "a_copy", paramsCopy.retentionChecks[0].ActionName)
  1470. assert.Equal(t, "p_copy", paramsCopy.retentionChecks[0].Results[0].Path)
  1471. assert.Equal(t, 2, paramsCopy.retentionChecks[0].Results[0].Retention)
  1472. }
  1473. func TestEventParamsStatusFromError(t *testing.T) {
  1474. params := EventParams{Status: 1}
  1475. params.AddError(os.ErrNotExist)
  1476. assert.Equal(t, 1, params.Status)
  1477. params = EventParams{Status: 1, updateStatusFromError: true}
  1478. params.AddError(os.ErrNotExist)
  1479. assert.Equal(t, 2, params.Status)
  1480. }
  1481. type testWriter struct {
  1482. errTest error
  1483. sentinel string
  1484. }
  1485. func (w *testWriter) Write(p []byte) (int, error) {
  1486. if w.errTest != nil {
  1487. return 0, w.errTest
  1488. }
  1489. if w.sentinel == string(p) {
  1490. return 0, io.ErrUnexpectedEOF
  1491. }
  1492. return len(p), nil
  1493. }
  1494. func TestWriteHTTPPartsError(t *testing.T) {
  1495. m := multipart.NewWriter(&testWriter{
  1496. errTest: io.ErrShortWrite,
  1497. })
  1498. err := writeHTTPPart(m, dataprovider.HTTPPart{}, nil, nil, nil, &EventParams{})
  1499. assert.ErrorIs(t, err, io.ErrShortWrite)
  1500. body := "test body"
  1501. m = multipart.NewWriter(&testWriter{sentinel: body})
  1502. err = writeHTTPPart(m, dataprovider.HTTPPart{
  1503. Body: body,
  1504. }, nil, nil, nil, &EventParams{})
  1505. assert.ErrorIs(t, err, io.ErrUnexpectedEOF)
  1506. }
  1507. func TestReplacePathsPlaceholders(t *testing.T) {
  1508. replacer := strings.NewReplacer("{{VirtualPath}}", "/path1")
  1509. paths := []string{"{{VirtualPath}}", "/path1"}
  1510. paths = replacePathsPlaceholders(paths, replacer)
  1511. assert.Equal(t, []string{"/path1"}, paths)
  1512. paths = []string{"{{VirtualPath}}", "/path2"}
  1513. paths = replacePathsPlaceholders(paths, replacer)
  1514. assert.Equal(t, []string{"/path1", "/path2"}, paths)
  1515. }
  1516. func getErrorString(err error) string {
  1517. if err == nil {
  1518. return ""
  1519. }
  1520. return err.Error()
  1521. }