eventmanager_test.go 48 KB

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