eventmanager_test.go 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278
  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. "crypto/rand"
  17. "fmt"
  18. "net/http"
  19. "os"
  20. "path"
  21. "path/filepath"
  22. "runtime"
  23. "strings"
  24. "testing"
  25. "time"
  26. "github.com/sftpgo/sdk"
  27. sdkkms "github.com/sftpgo/sdk/kms"
  28. "github.com/stretchr/testify/assert"
  29. "github.com/drakkan/sftpgo/v2/internal/dataprovider"
  30. "github.com/drakkan/sftpgo/v2/internal/kms"
  31. "github.com/drakkan/sftpgo/v2/internal/util"
  32. "github.com/drakkan/sftpgo/v2/internal/vfs"
  33. )
  34. func TestEventRuleMatch(t *testing.T) {
  35. conditions := dataprovider.EventConditions{
  36. ProviderEvents: []string{"add", "update"},
  37. Options: dataprovider.ConditionOptions{
  38. Names: []dataprovider.ConditionPattern{
  39. {
  40. Pattern: "user1",
  41. InverseMatch: true,
  42. },
  43. },
  44. },
  45. }
  46. res := eventManager.checkProviderEventMatch(conditions, EventParams{
  47. Name: "user1",
  48. Event: "add",
  49. })
  50. assert.False(t, res)
  51. res = eventManager.checkProviderEventMatch(conditions, EventParams{
  52. Name: "user2",
  53. Event: "update",
  54. })
  55. assert.True(t, res)
  56. res = eventManager.checkProviderEventMatch(conditions, EventParams{
  57. Name: "user2",
  58. Event: "delete",
  59. })
  60. assert.False(t, res)
  61. conditions.Options.ProviderObjects = []string{"api_key"}
  62. res = eventManager.checkProviderEventMatch(conditions, EventParams{
  63. Name: "user2",
  64. Event: "update",
  65. ObjectType: "share",
  66. })
  67. assert.False(t, res)
  68. res = eventManager.checkProviderEventMatch(conditions, EventParams{
  69. Name: "user2",
  70. Event: "update",
  71. ObjectType: "api_key",
  72. })
  73. assert.True(t, res)
  74. // now test fs events
  75. conditions = dataprovider.EventConditions{
  76. FsEvents: []string{operationUpload, operationDownload},
  77. Options: dataprovider.ConditionOptions{
  78. Names: []dataprovider.ConditionPattern{
  79. {
  80. Pattern: "user*",
  81. },
  82. {
  83. Pattern: "tester*",
  84. },
  85. },
  86. FsPaths: []dataprovider.ConditionPattern{
  87. {
  88. Pattern: "*.txt",
  89. },
  90. },
  91. Protocols: []string{ProtocolSFTP},
  92. MinFileSize: 10,
  93. MaxFileSize: 30,
  94. },
  95. }
  96. params := EventParams{
  97. Name: "tester4",
  98. Event: operationDelete,
  99. VirtualPath: "/path.txt",
  100. Protocol: ProtocolSFTP,
  101. ObjectName: "path.txt",
  102. FileSize: 20,
  103. }
  104. res = eventManager.checkFsEventMatch(conditions, params)
  105. assert.False(t, res)
  106. params.Event = operationDownload
  107. res = eventManager.checkFsEventMatch(conditions, params)
  108. assert.True(t, res)
  109. params.Name = "name"
  110. res = eventManager.checkFsEventMatch(conditions, params)
  111. assert.False(t, res)
  112. params.Name = "user5"
  113. res = eventManager.checkFsEventMatch(conditions, params)
  114. assert.True(t, res)
  115. params.VirtualPath = "/sub/f.jpg"
  116. params.ObjectName = path.Base(params.VirtualPath)
  117. res = eventManager.checkFsEventMatch(conditions, params)
  118. assert.False(t, res)
  119. params.VirtualPath = "/sub/f.txt"
  120. params.ObjectName = path.Base(params.VirtualPath)
  121. res = eventManager.checkFsEventMatch(conditions, params)
  122. assert.True(t, res)
  123. params.Protocol = ProtocolHTTP
  124. res = eventManager.checkFsEventMatch(conditions, params)
  125. assert.False(t, res)
  126. params.Protocol = ProtocolSFTP
  127. params.FileSize = 5
  128. res = eventManager.checkFsEventMatch(conditions, params)
  129. assert.False(t, res)
  130. params.FileSize = 50
  131. res = eventManager.checkFsEventMatch(conditions, params)
  132. assert.False(t, res)
  133. params.FileSize = 25
  134. res = eventManager.checkFsEventMatch(conditions, params)
  135. assert.True(t, res)
  136. // bad pattern
  137. conditions.Options.Names = []dataprovider.ConditionPattern{
  138. {
  139. Pattern: "[-]",
  140. },
  141. }
  142. res = eventManager.checkFsEventMatch(conditions, params)
  143. assert.False(t, res)
  144. }
  145. func TestEventManager(t *testing.T) {
  146. startEventScheduler()
  147. action := &dataprovider.BaseEventAction{
  148. Name: "test_action",
  149. Type: dataprovider.ActionTypeHTTP,
  150. Options: dataprovider.BaseEventActionOptions{
  151. HTTPConfig: dataprovider.EventActionHTTPConfig{
  152. Endpoint: "http://localhost",
  153. Timeout: 20,
  154. Method: http.MethodGet,
  155. },
  156. },
  157. }
  158. err := dataprovider.AddEventAction(action, "", "")
  159. assert.NoError(t, err)
  160. rule := &dataprovider.EventRule{
  161. Name: "rule",
  162. Trigger: dataprovider.EventTriggerFsEvent,
  163. Conditions: dataprovider.EventConditions{
  164. FsEvents: []string{operationUpload},
  165. },
  166. Actions: []dataprovider.EventAction{
  167. {
  168. BaseEventAction: dataprovider.BaseEventAction{
  169. Name: action.Name,
  170. },
  171. Order: 1,
  172. },
  173. },
  174. }
  175. err = dataprovider.AddEventRule(rule, "", "")
  176. assert.NoError(t, err)
  177. eventManager.RLock()
  178. assert.Len(t, eventManager.FsEvents, 1)
  179. assert.Len(t, eventManager.ProviderEvents, 0)
  180. assert.Len(t, eventManager.Schedules, 0)
  181. assert.Len(t, eventManager.schedulesMapping, 0)
  182. eventManager.RUnlock()
  183. rule.Trigger = dataprovider.EventTriggerProviderEvent
  184. rule.Conditions = dataprovider.EventConditions{
  185. ProviderEvents: []string{"add"},
  186. }
  187. err = dataprovider.UpdateEventRule(rule, "", "")
  188. assert.NoError(t, err)
  189. eventManager.RLock()
  190. assert.Len(t, eventManager.FsEvents, 0)
  191. assert.Len(t, eventManager.ProviderEvents, 1)
  192. assert.Len(t, eventManager.Schedules, 0)
  193. assert.Len(t, eventManager.schedulesMapping, 0)
  194. eventManager.RUnlock()
  195. rule.Trigger = dataprovider.EventTriggerSchedule
  196. rule.Conditions = dataprovider.EventConditions{
  197. Schedules: []dataprovider.Schedule{
  198. {
  199. Hours: "0",
  200. DayOfWeek: "*",
  201. DayOfMonth: "*",
  202. Month: "*",
  203. },
  204. },
  205. }
  206. rule.DeletedAt = util.GetTimeAsMsSinceEpoch(time.Now().Add(-12 * time.Hour))
  207. eventManager.addUpdateRuleInternal(*rule)
  208. eventManager.RLock()
  209. assert.Len(t, eventManager.FsEvents, 0)
  210. assert.Len(t, eventManager.ProviderEvents, 0)
  211. assert.Len(t, eventManager.Schedules, 0)
  212. assert.Len(t, eventManager.schedulesMapping, 0)
  213. eventManager.RUnlock()
  214. assert.Eventually(t, func() bool {
  215. _, err = dataprovider.EventRuleExists(rule.Name)
  216. _, ok := err.(*util.RecordNotFoundError)
  217. return ok
  218. }, 2*time.Second, 100*time.Millisecond)
  219. rule.DeletedAt = 0
  220. err = dataprovider.AddEventRule(rule, "", "")
  221. assert.NoError(t, err)
  222. eventManager.RLock()
  223. assert.Len(t, eventManager.FsEvents, 0)
  224. assert.Len(t, eventManager.ProviderEvents, 0)
  225. assert.Len(t, eventManager.Schedules, 1)
  226. assert.Len(t, eventManager.schedulesMapping, 1)
  227. eventManager.RUnlock()
  228. err = dataprovider.DeleteEventRule(rule.Name, "", "")
  229. assert.NoError(t, err)
  230. eventManager.RLock()
  231. assert.Len(t, eventManager.FsEvents, 0)
  232. assert.Len(t, eventManager.ProviderEvents, 0)
  233. assert.Len(t, eventManager.Schedules, 0)
  234. assert.Len(t, eventManager.schedulesMapping, 0)
  235. eventManager.RUnlock()
  236. err = dataprovider.DeleteEventAction(action.Name, "", "")
  237. assert.NoError(t, err)
  238. stopEventScheduler()
  239. }
  240. func TestEventManagerErrors(t *testing.T) {
  241. startEventScheduler()
  242. providerConf := dataprovider.GetProviderConfig()
  243. err := dataprovider.Close()
  244. assert.NoError(t, err)
  245. params := EventParams{
  246. sender: "sender",
  247. }
  248. _, err = params.getUsers()
  249. assert.Error(t, err)
  250. _, err = params.getFolders()
  251. assert.Error(t, err)
  252. err = executeUsersQuotaResetRuleAction(dataprovider.ConditionOptions{}, &EventParams{})
  253. assert.Error(t, err)
  254. err = executeFoldersQuotaResetRuleAction(dataprovider.ConditionOptions{}, &EventParams{})
  255. assert.Error(t, err)
  256. err = executeTransferQuotaResetRuleAction(dataprovider.ConditionOptions{}, &EventParams{})
  257. assert.Error(t, err)
  258. err = executeDeleteFsRuleAction(nil, nil, dataprovider.ConditionOptions{}, &EventParams{})
  259. assert.Error(t, err)
  260. err = executeMkdirFsRuleAction(nil, nil, dataprovider.ConditionOptions{}, &EventParams{})
  261. assert.Error(t, err)
  262. err = executeRenameFsRuleAction(nil, nil, dataprovider.ConditionOptions{}, &EventParams{})
  263. assert.Error(t, err)
  264. err = executeExistFsRuleAction(nil, nil, dataprovider.ConditionOptions{}, &EventParams{})
  265. assert.Error(t, err)
  266. groupName := "agroup"
  267. err = executeQuotaResetForUser(dataprovider.User{
  268. Groups: []sdk.GroupMapping{
  269. {
  270. Name: groupName,
  271. Type: sdk.GroupTypePrimary,
  272. },
  273. },
  274. })
  275. assert.Error(t, err)
  276. err = executeDataRetentionCheckForUser(dataprovider.User{
  277. Groups: []sdk.GroupMapping{
  278. {
  279. Name: groupName,
  280. Type: sdk.GroupTypePrimary,
  281. },
  282. },
  283. }, nil)
  284. assert.Error(t, err)
  285. err = executeDeleteFsActionForUser(nil, nil, dataprovider.User{
  286. Groups: []sdk.GroupMapping{
  287. {
  288. Name: groupName,
  289. Type: sdk.GroupTypePrimary,
  290. },
  291. },
  292. })
  293. assert.Error(t, err)
  294. err = executeMkDirsFsActionForUser(nil, nil, dataprovider.User{
  295. Groups: []sdk.GroupMapping{
  296. {
  297. Name: groupName,
  298. Type: sdk.GroupTypePrimary,
  299. },
  300. },
  301. })
  302. assert.Error(t, err)
  303. err = executeRenameFsActionForUser(nil, nil, dataprovider.User{
  304. Groups: []sdk.GroupMapping{
  305. {
  306. Name: groupName,
  307. Type: sdk.GroupTypePrimary,
  308. },
  309. },
  310. })
  311. assert.Error(t, err)
  312. err = executeExistFsActionForUser(nil, nil, dataprovider.User{
  313. Groups: []sdk.GroupMapping{
  314. {
  315. Name: groupName,
  316. Type: sdk.GroupTypePrimary,
  317. },
  318. },
  319. })
  320. assert.Error(t, err)
  321. _, err = getMailAttachments(dataprovider.User{
  322. Groups: []sdk.GroupMapping{
  323. {
  324. Name: groupName,
  325. Type: sdk.GroupTypePrimary,
  326. },
  327. }}, []string{"/a", "/b"}, nil)
  328. assert.Error(t, err)
  329. dataRetentionAction := dataprovider.BaseEventAction{
  330. Type: dataprovider.ActionTypeDataRetentionCheck,
  331. Options: dataprovider.BaseEventActionOptions{
  332. RetentionConfig: dataprovider.EventActionDataRetentionConfig{
  333. Folders: []dataprovider.FolderRetention{
  334. {
  335. Path: "/",
  336. Retention: 24,
  337. },
  338. },
  339. },
  340. },
  341. }
  342. err = executeRuleAction(dataRetentionAction, &EventParams{}, dataprovider.ConditionOptions{
  343. Names: []dataprovider.ConditionPattern{
  344. {
  345. Pattern: "username1",
  346. },
  347. },
  348. })
  349. if assert.Error(t, err) {
  350. assert.Contains(t, err.Error(), "unable to get users")
  351. }
  352. eventManager.loadRules()
  353. eventManager.RLock()
  354. assert.Len(t, eventManager.FsEvents, 0)
  355. assert.Len(t, eventManager.ProviderEvents, 0)
  356. assert.Len(t, eventManager.Schedules, 0)
  357. eventManager.RUnlock()
  358. // rule with invalid trigger
  359. eventManager.addUpdateRuleInternal(dataprovider.EventRule{
  360. Name: "test rule",
  361. Trigger: -1,
  362. })
  363. eventManager.RLock()
  364. assert.Len(t, eventManager.FsEvents, 0)
  365. assert.Len(t, eventManager.ProviderEvents, 0)
  366. assert.Len(t, eventManager.Schedules, 0)
  367. eventManager.RUnlock()
  368. // rule with invalid cronspec
  369. eventManager.addUpdateRuleInternal(dataprovider.EventRule{
  370. Name: "test rule",
  371. Trigger: dataprovider.EventTriggerSchedule,
  372. Conditions: dataprovider.EventConditions{
  373. Schedules: []dataprovider.Schedule{
  374. {
  375. Hours: "1000",
  376. },
  377. },
  378. },
  379. })
  380. eventManager.RLock()
  381. assert.Len(t, eventManager.FsEvents, 0)
  382. assert.Len(t, eventManager.ProviderEvents, 0)
  383. assert.Len(t, eventManager.Schedules, 0)
  384. eventManager.RUnlock()
  385. err = dataprovider.Initialize(providerConf, configDir, true)
  386. assert.NoError(t, err)
  387. stopEventScheduler()
  388. }
  389. func TestEventRuleActions(t *testing.T) {
  390. actionName := "test rule action"
  391. action := dataprovider.BaseEventAction{
  392. Name: actionName,
  393. Type: dataprovider.ActionTypeBackup,
  394. }
  395. err := executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{})
  396. assert.NoError(t, err)
  397. action.Type = -1
  398. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{})
  399. assert.Error(t, err)
  400. action = dataprovider.BaseEventAction{
  401. Name: actionName,
  402. Type: dataprovider.ActionTypeHTTP,
  403. Options: dataprovider.BaseEventActionOptions{
  404. HTTPConfig: dataprovider.EventActionHTTPConfig{
  405. Endpoint: "http://foo\x7f.com/", // invalid URL
  406. SkipTLSVerify: true,
  407. Body: "{{ObjectData}}",
  408. Method: http.MethodPost,
  409. QueryParameters: []dataprovider.KeyValue{
  410. {
  411. Key: "param",
  412. Value: "value",
  413. },
  414. },
  415. Timeout: 5,
  416. Headers: []dataprovider.KeyValue{
  417. {
  418. Key: "Content-Type",
  419. Value: "application/json",
  420. },
  421. },
  422. Username: "httpuser",
  423. },
  424. },
  425. }
  426. action.Options.SetEmptySecretsIfNil()
  427. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{})
  428. if assert.Error(t, err) {
  429. assert.Contains(t, err.Error(), "invalid endpoint")
  430. }
  431. action.Options.HTTPConfig.Endpoint = fmt.Sprintf("http://%v", httpAddr)
  432. params := &EventParams{
  433. Name: "a",
  434. Object: &dataprovider.User{
  435. BaseUser: sdk.BaseUser{
  436. Username: "test user",
  437. },
  438. },
  439. }
  440. err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
  441. assert.NoError(t, err)
  442. action.Options.HTTPConfig.Endpoint = fmt.Sprintf("http://%v/404", httpAddr)
  443. err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
  444. if assert.Error(t, err) {
  445. assert.Contains(t, err.Error(), "unexpected status code: 404")
  446. }
  447. action.Options.HTTPConfig.Endpoint = "http://invalid:1234"
  448. err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
  449. assert.Error(t, err)
  450. action.Options.HTTPConfig.QueryParameters = nil
  451. action.Options.HTTPConfig.Endpoint = "http://bar\x7f.com/"
  452. err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
  453. assert.Error(t, err)
  454. action.Options.HTTPConfig.Password = kms.NewSecret(sdkkms.SecretStatusSecretBox, "payload", "key", "data")
  455. err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
  456. if assert.Error(t, err) {
  457. assert.Contains(t, err.Error(), "unable to decrypt password")
  458. }
  459. // test disk and transfer quota reset
  460. username1 := "user1"
  461. username2 := "user2"
  462. user1 := dataprovider.User{
  463. BaseUser: sdk.BaseUser{
  464. Username: username1,
  465. HomeDir: filepath.Join(os.TempDir(), username1),
  466. Status: 1,
  467. Permissions: map[string][]string{
  468. "/": {dataprovider.PermAny},
  469. },
  470. },
  471. }
  472. user2 := dataprovider.User{
  473. BaseUser: sdk.BaseUser{
  474. Username: username2,
  475. HomeDir: filepath.Join(os.TempDir(), username2),
  476. Status: 1,
  477. Permissions: map[string][]string{
  478. "/": {dataprovider.PermAny},
  479. },
  480. },
  481. }
  482. err = dataprovider.AddUser(&user1, "", "")
  483. assert.NoError(t, err)
  484. err = dataprovider.AddUser(&user2, "", "")
  485. assert.NoError(t, err)
  486. action = dataprovider.BaseEventAction{
  487. Type: dataprovider.ActionTypeUserQuotaReset,
  488. }
  489. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  490. Names: []dataprovider.ConditionPattern{
  491. {
  492. Pattern: username1,
  493. },
  494. },
  495. })
  496. assert.Error(t, err) // no home dir
  497. // create the home dir
  498. err = os.MkdirAll(user1.GetHomeDir(), os.ModePerm)
  499. assert.NoError(t, err)
  500. err = os.WriteFile(filepath.Join(user1.GetHomeDir(), "file.txt"), []byte("user"), 0666)
  501. assert.NoError(t, err)
  502. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  503. Names: []dataprovider.ConditionPattern{
  504. {
  505. Pattern: username1,
  506. },
  507. },
  508. })
  509. assert.NoError(t, err)
  510. userGet, err := dataprovider.UserExists(username1)
  511. assert.NoError(t, err)
  512. assert.Equal(t, 1, userGet.UsedQuotaFiles)
  513. assert.Equal(t, int64(4), userGet.UsedQuotaSize)
  514. // simulate another quota scan in progress
  515. assert.True(t, QuotaScans.AddUserQuotaScan(username1))
  516. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  517. Names: []dataprovider.ConditionPattern{
  518. {
  519. Pattern: username1,
  520. },
  521. },
  522. })
  523. assert.Error(t, err)
  524. assert.True(t, QuotaScans.RemoveUserQuotaScan(username1))
  525. // non matching pattern
  526. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  527. Names: []dataprovider.ConditionPattern{
  528. {
  529. Pattern: "don't match",
  530. },
  531. },
  532. })
  533. if assert.Error(t, err) {
  534. assert.Contains(t, err.Error(), "no user quota reset executed")
  535. }
  536. dataRetentionAction := dataprovider.BaseEventAction{
  537. Type: dataprovider.ActionTypeDataRetentionCheck,
  538. Options: dataprovider.BaseEventActionOptions{
  539. RetentionConfig: dataprovider.EventActionDataRetentionConfig{
  540. Folders: []dataprovider.FolderRetention{
  541. {
  542. Path: "",
  543. Retention: 24,
  544. },
  545. },
  546. },
  547. },
  548. }
  549. err = executeRuleAction(dataRetentionAction, &EventParams{}, dataprovider.ConditionOptions{
  550. Names: []dataprovider.ConditionPattern{
  551. {
  552. Pattern: username1,
  553. },
  554. },
  555. })
  556. assert.Error(t, err) // invalid config, no folder path specified
  557. retentionDir := "testretention"
  558. dataRetentionAction = dataprovider.BaseEventAction{
  559. Type: dataprovider.ActionTypeDataRetentionCheck,
  560. Options: dataprovider.BaseEventActionOptions{
  561. RetentionConfig: dataprovider.EventActionDataRetentionConfig{
  562. Folders: []dataprovider.FolderRetention{
  563. {
  564. Path: path.Join("/", retentionDir),
  565. Retention: 24,
  566. DeleteEmptyDirs: true,
  567. },
  568. },
  569. },
  570. },
  571. }
  572. // create some test files
  573. file1 := filepath.Join(user1.GetHomeDir(), "file1.txt")
  574. file2 := filepath.Join(user1.GetHomeDir(), retentionDir, "file2.txt")
  575. file3 := filepath.Join(user1.GetHomeDir(), retentionDir, "file3.txt")
  576. file4 := filepath.Join(user1.GetHomeDir(), retentionDir, "sub", "file4.txt")
  577. err = os.MkdirAll(filepath.Dir(file4), os.ModePerm)
  578. assert.NoError(t, err)
  579. for _, f := range []string{file1, file2, file3, file4} {
  580. err = os.WriteFile(f, []byte(""), 0666)
  581. assert.NoError(t, err)
  582. }
  583. timeBeforeRetention := time.Now().Add(-48 * time.Hour)
  584. err = os.Chtimes(file1, timeBeforeRetention, timeBeforeRetention)
  585. assert.NoError(t, err)
  586. err = os.Chtimes(file2, timeBeforeRetention, timeBeforeRetention)
  587. assert.NoError(t, err)
  588. err = os.Chtimes(file4, timeBeforeRetention, timeBeforeRetention)
  589. assert.NoError(t, err)
  590. err = executeRuleAction(dataRetentionAction, &EventParams{}, dataprovider.ConditionOptions{
  591. Names: []dataprovider.ConditionPattern{
  592. {
  593. Pattern: username1,
  594. },
  595. },
  596. })
  597. assert.NoError(t, err)
  598. assert.FileExists(t, file1)
  599. assert.NoFileExists(t, file2)
  600. assert.FileExists(t, file3)
  601. assert.NoDirExists(t, filepath.Dir(file4))
  602. // simulate another check in progress
  603. c := RetentionChecks.Add(RetentionCheck{}, &user1)
  604. assert.NotNil(t, c)
  605. err = executeRuleAction(dataRetentionAction, &EventParams{}, dataprovider.ConditionOptions{
  606. Names: []dataprovider.ConditionPattern{
  607. {
  608. Pattern: username1,
  609. },
  610. },
  611. })
  612. assert.Error(t, err)
  613. RetentionChecks.remove(user1.Username)
  614. err = executeRuleAction(dataRetentionAction, &EventParams{}, dataprovider.ConditionOptions{
  615. Names: []dataprovider.ConditionPattern{
  616. {
  617. Pattern: "no match",
  618. },
  619. },
  620. })
  621. if assert.Error(t, err) {
  622. assert.Contains(t, err.Error(), "no retention check executed")
  623. }
  624. // test file exists action
  625. action = dataprovider.BaseEventAction{
  626. Type: dataprovider.ActionTypeFilesystem,
  627. Options: dataprovider.BaseEventActionOptions{
  628. FsConfig: dataprovider.EventActionFilesystemConfig{
  629. Type: dataprovider.FilesystemActionExist,
  630. Exist: []string{"/file1.txt", path.Join("/", retentionDir, "file3.txt")},
  631. },
  632. },
  633. }
  634. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  635. Names: []dataprovider.ConditionPattern{
  636. {
  637. Pattern: "no match",
  638. },
  639. },
  640. })
  641. if assert.Error(t, err) {
  642. assert.Contains(t, err.Error(), "no existence check executed")
  643. }
  644. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  645. Names: []dataprovider.ConditionPattern{
  646. {
  647. Pattern: username1,
  648. },
  649. },
  650. })
  651. assert.NoError(t, err)
  652. action.Options.FsConfig.Exist = []string{"/file1.txt", path.Join("/", retentionDir, "file2.txt")}
  653. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  654. Names: []dataprovider.ConditionPattern{
  655. {
  656. Pattern: username1,
  657. },
  658. },
  659. })
  660. assert.Error(t, err)
  661. err = os.RemoveAll(user1.GetHomeDir())
  662. assert.NoError(t, err)
  663. err = dataprovider.UpdateUserTransferQuota(&user1, 100, 100, true)
  664. assert.NoError(t, err)
  665. action.Type = dataprovider.ActionTypeTransferQuotaReset
  666. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  667. Names: []dataprovider.ConditionPattern{
  668. {
  669. Pattern: username1,
  670. },
  671. },
  672. })
  673. assert.NoError(t, err)
  674. userGet, err = dataprovider.UserExists(username1)
  675. assert.NoError(t, err)
  676. assert.Equal(t, int64(0), userGet.UsedDownloadDataTransfer)
  677. assert.Equal(t, int64(0), userGet.UsedUploadDataTransfer)
  678. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  679. Names: []dataprovider.ConditionPattern{
  680. {
  681. Pattern: "no match",
  682. },
  683. },
  684. })
  685. if assert.Error(t, err) {
  686. assert.Contains(t, err.Error(), "no transfer quota reset executed")
  687. }
  688. action.Type = dataprovider.ActionTypeFilesystem
  689. action.Options = dataprovider.BaseEventActionOptions{
  690. FsConfig: dataprovider.EventActionFilesystemConfig{
  691. Type: dataprovider.FilesystemActionRename,
  692. Renames: []dataprovider.KeyValue{
  693. {
  694. Key: "/source",
  695. Value: "/target",
  696. },
  697. },
  698. },
  699. }
  700. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  701. Names: []dataprovider.ConditionPattern{
  702. {
  703. Pattern: "no match",
  704. },
  705. },
  706. })
  707. if assert.Error(t, err) {
  708. assert.Contains(t, err.Error(), "no rename executed")
  709. }
  710. action.Options = dataprovider.BaseEventActionOptions{
  711. FsConfig: dataprovider.EventActionFilesystemConfig{
  712. Type: dataprovider.FilesystemActionDelete,
  713. Deletes: []string{"/dir1"},
  714. },
  715. }
  716. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  717. Names: []dataprovider.ConditionPattern{
  718. {
  719. Pattern: "no match",
  720. },
  721. },
  722. })
  723. if assert.Error(t, err) {
  724. assert.Contains(t, err.Error(), "no delete executed")
  725. }
  726. action.Options = dataprovider.BaseEventActionOptions{
  727. FsConfig: dataprovider.EventActionFilesystemConfig{
  728. Type: dataprovider.FilesystemActionMkdirs,
  729. Deletes: []string{"/dir1"},
  730. },
  731. }
  732. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  733. Names: []dataprovider.ConditionPattern{
  734. {
  735. Pattern: "no match",
  736. },
  737. },
  738. })
  739. if assert.Error(t, err) {
  740. assert.Contains(t, err.Error(), "no mkdir executed")
  741. }
  742. err = dataprovider.DeleteUser(username1, "", "")
  743. assert.NoError(t, err)
  744. err = dataprovider.DeleteUser(username2, "", "")
  745. assert.NoError(t, err)
  746. // test folder quota reset
  747. foldername1 := "f1"
  748. foldername2 := "f2"
  749. folder1 := vfs.BaseVirtualFolder{
  750. Name: foldername1,
  751. MappedPath: filepath.Join(os.TempDir(), foldername1),
  752. }
  753. folder2 := vfs.BaseVirtualFolder{
  754. Name: foldername2,
  755. MappedPath: filepath.Join(os.TempDir(), foldername2),
  756. }
  757. err = dataprovider.AddFolder(&folder1, "", "")
  758. assert.NoError(t, err)
  759. err = dataprovider.AddFolder(&folder2, "", "")
  760. assert.NoError(t, err)
  761. action = dataprovider.BaseEventAction{
  762. Type: dataprovider.ActionTypeFolderQuotaReset,
  763. }
  764. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  765. Names: []dataprovider.ConditionPattern{
  766. {
  767. Pattern: foldername1,
  768. },
  769. },
  770. })
  771. assert.Error(t, err) // no home dir
  772. err = os.MkdirAll(folder1.MappedPath, os.ModePerm)
  773. assert.NoError(t, err)
  774. err = os.WriteFile(filepath.Join(folder1.MappedPath, "file.txt"), []byte("folder"), 0666)
  775. assert.NoError(t, err)
  776. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  777. Names: []dataprovider.ConditionPattern{
  778. {
  779. Pattern: foldername1,
  780. },
  781. },
  782. })
  783. assert.NoError(t, err)
  784. folderGet, err := dataprovider.GetFolderByName(foldername1)
  785. assert.NoError(t, err)
  786. assert.Equal(t, 1, folderGet.UsedQuotaFiles)
  787. assert.Equal(t, int64(6), folderGet.UsedQuotaSize)
  788. // simulate another quota scan in progress
  789. assert.True(t, QuotaScans.AddVFolderQuotaScan(foldername1))
  790. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  791. Names: []dataprovider.ConditionPattern{
  792. {
  793. Pattern: foldername1,
  794. },
  795. },
  796. })
  797. assert.Error(t, err)
  798. assert.True(t, QuotaScans.RemoveVFolderQuotaScan(foldername1))
  799. err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
  800. Names: []dataprovider.ConditionPattern{
  801. {
  802. Pattern: "no folder match",
  803. },
  804. },
  805. })
  806. if assert.Error(t, err) {
  807. assert.Contains(t, err.Error(), "no folder quota reset executed")
  808. }
  809. err = os.RemoveAll(folder1.MappedPath)
  810. assert.NoError(t, err)
  811. err = dataprovider.DeleteFolder(foldername1, "", "")
  812. assert.NoError(t, err)
  813. err = dataprovider.DeleteFolder(foldername2, "", "")
  814. assert.NoError(t, err)
  815. }
  816. func TestGetFileContent(t *testing.T) {
  817. username := "test_user_get_file_content"
  818. user := dataprovider.User{
  819. BaseUser: sdk.BaseUser{
  820. Username: username,
  821. Permissions: map[string][]string{
  822. "/": {dataprovider.PermAny},
  823. },
  824. HomeDir: filepath.Join(os.TempDir(), username),
  825. },
  826. }
  827. err := dataprovider.AddUser(&user, "", "")
  828. assert.NoError(t, err)
  829. err = os.MkdirAll(user.GetHomeDir(), os.ModePerm)
  830. assert.NoError(t, err)
  831. fileContent := []byte("test file content")
  832. err = os.WriteFile(filepath.Join(user.GetHomeDir(), "file.txt"), fileContent, 0666)
  833. assert.NoError(t, err)
  834. replacer := strings.NewReplacer("old", "new")
  835. files, err := getMailAttachments(user, []string{"/file.txt"}, replacer)
  836. assert.NoError(t, err)
  837. if assert.Len(t, files, 1) {
  838. assert.Equal(t, fileContent, files[0].Data)
  839. }
  840. // missing file
  841. _, err = getMailAttachments(user, []string{"/file1.txt"}, replacer)
  842. assert.Error(t, err)
  843. // directory
  844. _, err = getMailAttachments(user, []string{"/"}, replacer)
  845. assert.Error(t, err)
  846. // files too large
  847. content := make([]byte, emailAttachmentsMaxSize/2+1)
  848. _, err = rand.Read(content)
  849. assert.NoError(t, err)
  850. err = os.WriteFile(filepath.Join(user.GetHomeDir(), "file1.txt"), content, 0666)
  851. assert.NoError(t, err)
  852. err = os.WriteFile(filepath.Join(user.GetHomeDir(), "file2.txt"), content, 0666)
  853. assert.NoError(t, err)
  854. files, err = getMailAttachments(user, []string{"/file1.txt"}, replacer)
  855. assert.NoError(t, err)
  856. if assert.Len(t, files, 1) {
  857. assert.Equal(t, content, files[0].Data)
  858. }
  859. _, err = getMailAttachments(user, []string{"/file1.txt", "/file2.txt"}, replacer)
  860. if assert.Error(t, err) {
  861. assert.Contains(t, err.Error(), "size too large")
  862. }
  863. // change the filesystem provider
  864. user.FsConfig.Provider = sdk.CryptedFilesystemProvider
  865. user.FsConfig.CryptConfig.Passphrase = kms.NewPlainSecret("pwd")
  866. err = dataprovider.UpdateUser(&user, "", "")
  867. assert.NoError(t, err)
  868. // the file is not encrypted so reading the encryption header will fail
  869. _, err = getMailAttachments(user, []string{"/file.txt"}, replacer)
  870. assert.Error(t, err)
  871. err = dataprovider.DeleteUser(username, "", "")
  872. assert.NoError(t, err)
  873. err = os.RemoveAll(user.GetHomeDir())
  874. assert.NoError(t, err)
  875. }
  876. func TestFilesystemActionErrors(t *testing.T) {
  877. err := executeFsRuleAction(dataprovider.EventActionFilesystemConfig{}, dataprovider.ConditionOptions{}, &EventParams{})
  878. if assert.Error(t, err) {
  879. assert.Contains(t, err.Error(), "unsupported filesystem action")
  880. }
  881. username := "test_user_for_actions"
  882. testReplacer := strings.NewReplacer("old", "new")
  883. user := dataprovider.User{
  884. BaseUser: sdk.BaseUser{
  885. Username: username,
  886. Permissions: map[string][]string{
  887. "/": {dataprovider.PermAny},
  888. },
  889. HomeDir: filepath.Join(os.TempDir(), username),
  890. },
  891. FsConfig: vfs.Filesystem{
  892. Provider: sdk.SFTPFilesystemProvider,
  893. SFTPConfig: vfs.SFTPFsConfig{
  894. BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
  895. Endpoint: "127.0.0.1:4022",
  896. Username: username,
  897. },
  898. Password: kms.NewPlainSecret("pwd"),
  899. },
  900. },
  901. }
  902. err = executeEmailRuleAction(dataprovider.EventActionEmailConfig{
  903. Recipients: []string{"test@example.net"},
  904. Subject: "subject",
  905. Body: "body",
  906. Attachments: []string{"/file.txt"},
  907. }, &EventParams{
  908. sender: username,
  909. })
  910. assert.Error(t, err)
  911. conn := NewBaseConnection("", protocolEventAction, "", "", user)
  912. err = executeDeleteFileFsAction(conn, "", nil)
  913. assert.Error(t, err)
  914. err = dataprovider.AddUser(&user, "", "")
  915. assert.NoError(t, err)
  916. // check root fs fails
  917. err = executeDeleteFsActionForUser(nil, testReplacer, user)
  918. assert.Error(t, err)
  919. err = executeMkDirsFsActionForUser(nil, testReplacer, user)
  920. assert.Error(t, err)
  921. err = executeRenameFsActionForUser(nil, testReplacer, user)
  922. assert.Error(t, err)
  923. err = executeExistFsActionForUser(nil, testReplacer, user)
  924. assert.Error(t, err)
  925. err = executeEmailRuleAction(dataprovider.EventActionEmailConfig{
  926. Recipients: []string{"test@example.net"},
  927. Subject: "subject",
  928. Body: "body",
  929. Attachments: []string{"/file1.txt"},
  930. }, &EventParams{
  931. sender: username,
  932. })
  933. assert.Error(t, err)
  934. _, err = getFileContent(NewBaseConnection("", protocolEventAction, "", "", user), "/f.txt", 1234)
  935. assert.Error(t, err)
  936. user.FsConfig.Provider = sdk.LocalFilesystemProvider
  937. user.Permissions["/"] = []string{dataprovider.PermUpload}
  938. err = dataprovider.DeleteUser(username, "", "")
  939. assert.NoError(t, err)
  940. err = dataprovider.AddUser(&user, "", "")
  941. assert.NoError(t, err)
  942. err = executeRenameFsActionForUser([]dataprovider.KeyValue{
  943. {
  944. Key: "/p1",
  945. Value: "/p1",
  946. },
  947. }, testReplacer, user)
  948. if assert.Error(t, err) {
  949. assert.Contains(t, err.Error(), "the rename source and target cannot be the same")
  950. }
  951. err = executeRuleAction(dataprovider.BaseEventAction{
  952. Type: dataprovider.ActionTypeFilesystem,
  953. Options: dataprovider.BaseEventActionOptions{
  954. FsConfig: dataprovider.EventActionFilesystemConfig{
  955. Type: dataprovider.FilesystemActionRename,
  956. Renames: []dataprovider.KeyValue{
  957. {
  958. Key: "/p2",
  959. Value: "/p2",
  960. },
  961. },
  962. },
  963. },
  964. }, &EventParams{}, dataprovider.ConditionOptions{
  965. Names: []dataprovider.ConditionPattern{
  966. {
  967. Pattern: username,
  968. },
  969. },
  970. })
  971. assert.Error(t, err)
  972. if runtime.GOOS != osWindows {
  973. dirPath := filepath.Join(user.HomeDir, "adir", "sub")
  974. err := os.MkdirAll(dirPath, os.ModePerm)
  975. assert.NoError(t, err)
  976. filePath := filepath.Join(dirPath, "f.dat")
  977. err = os.WriteFile(filePath, nil, 0666)
  978. assert.NoError(t, err)
  979. err = os.Chmod(dirPath, 0001)
  980. assert.NoError(t, err)
  981. err = executeDeleteFsActionForUser([]string{"/adir/sub"}, testReplacer, user)
  982. assert.Error(t, err)
  983. err = executeDeleteFsActionForUser([]string{"/adir/sub/f.dat"}, testReplacer, user)
  984. assert.Error(t, err)
  985. err = os.Chmod(dirPath, 0555)
  986. assert.NoError(t, err)
  987. err = executeDeleteFsActionForUser([]string{"/adir/sub/f.dat"}, testReplacer, user)
  988. if assert.Error(t, err) {
  989. assert.Contains(t, err.Error(), "unable to remove file")
  990. }
  991. err = executeRuleAction(dataprovider.BaseEventAction{
  992. Type: dataprovider.ActionTypeFilesystem,
  993. Options: dataprovider.BaseEventActionOptions{
  994. FsConfig: dataprovider.EventActionFilesystemConfig{
  995. Type: dataprovider.FilesystemActionDelete,
  996. Deletes: []string{"/adir/sub/f.dat"},
  997. },
  998. },
  999. }, &EventParams{}, dataprovider.ConditionOptions{
  1000. Names: []dataprovider.ConditionPattern{
  1001. {
  1002. Pattern: username,
  1003. },
  1004. },
  1005. })
  1006. assert.Error(t, err)
  1007. err = executeMkDirsFsActionForUser([]string{"/adir/sub/sub"}, testReplacer, user)
  1008. if assert.Error(t, err) {
  1009. assert.Contains(t, err.Error(), "unable to create dir")
  1010. }
  1011. err = executeMkDirsFsActionForUser([]string{"/adir/sub/sub/sub"}, testReplacer, user)
  1012. if assert.Error(t, err) {
  1013. assert.Contains(t, err.Error(), "unable to check parent dirs")
  1014. }
  1015. err = executeRuleAction(dataprovider.BaseEventAction{
  1016. Type: dataprovider.ActionTypeFilesystem,
  1017. Options: dataprovider.BaseEventActionOptions{
  1018. FsConfig: dataprovider.EventActionFilesystemConfig{
  1019. Type: dataprovider.FilesystemActionMkdirs,
  1020. MkDirs: []string{"/adir/sub/sub1"},
  1021. },
  1022. },
  1023. }, &EventParams{}, dataprovider.ConditionOptions{
  1024. Names: []dataprovider.ConditionPattern{
  1025. {
  1026. Pattern: username,
  1027. },
  1028. },
  1029. })
  1030. assert.Error(t, err)
  1031. err = os.Chmod(dirPath, os.ModePerm)
  1032. assert.NoError(t, err)
  1033. }
  1034. err = dataprovider.DeleteUser(username, "", "")
  1035. assert.NoError(t, err)
  1036. err = os.RemoveAll(user.GetHomeDir())
  1037. assert.NoError(t, err)
  1038. }
  1039. func TestQuotaActionsWithQuotaTrackDisabled(t *testing.T) {
  1040. oldProviderConf := dataprovider.GetProviderConfig()
  1041. providerConf := dataprovider.GetProviderConfig()
  1042. providerConf.TrackQuota = 0
  1043. err := dataprovider.Close()
  1044. assert.NoError(t, err)
  1045. err = dataprovider.Initialize(providerConf, configDir, true)
  1046. assert.NoError(t, err)
  1047. username := "u1"
  1048. user := dataprovider.User{
  1049. BaseUser: sdk.BaseUser{
  1050. Username: username,
  1051. HomeDir: filepath.Join(os.TempDir(), username),
  1052. Status: 1,
  1053. Permissions: map[string][]string{
  1054. "/": {dataprovider.PermAny},
  1055. },
  1056. },
  1057. FsConfig: vfs.Filesystem{
  1058. Provider: sdk.LocalFilesystemProvider,
  1059. },
  1060. }
  1061. err = dataprovider.AddUser(&user, "", "")
  1062. assert.NoError(t, err)
  1063. err = os.MkdirAll(user.GetHomeDir(), os.ModePerm)
  1064. assert.NoError(t, err)
  1065. err = executeRuleAction(dataprovider.BaseEventAction{Type: dataprovider.ActionTypeUserQuotaReset},
  1066. &EventParams{}, dataprovider.ConditionOptions{
  1067. Names: []dataprovider.ConditionPattern{
  1068. {
  1069. Pattern: username,
  1070. },
  1071. },
  1072. })
  1073. assert.Error(t, err)
  1074. err = executeRuleAction(dataprovider.BaseEventAction{Type: dataprovider.ActionTypeTransferQuotaReset},
  1075. &EventParams{}, dataprovider.ConditionOptions{
  1076. Names: []dataprovider.ConditionPattern{
  1077. {
  1078. Pattern: username,
  1079. },
  1080. },
  1081. })
  1082. assert.Error(t, err)
  1083. err = os.RemoveAll(user.GetHomeDir())
  1084. assert.NoError(t, err)
  1085. err = dataprovider.DeleteUser(username, "", "")
  1086. assert.NoError(t, err)
  1087. foldername := "f1"
  1088. folder := vfs.BaseVirtualFolder{
  1089. Name: foldername,
  1090. MappedPath: filepath.Join(os.TempDir(), foldername),
  1091. }
  1092. err = dataprovider.AddFolder(&folder, "", "")
  1093. assert.NoError(t, err)
  1094. err = os.MkdirAll(folder.MappedPath, os.ModePerm)
  1095. assert.NoError(t, err)
  1096. err = executeRuleAction(dataprovider.BaseEventAction{Type: dataprovider.ActionTypeFolderQuotaReset},
  1097. &EventParams{}, dataprovider.ConditionOptions{
  1098. Names: []dataprovider.ConditionPattern{
  1099. {
  1100. Pattern: foldername,
  1101. },
  1102. },
  1103. })
  1104. assert.Error(t, err)
  1105. err = os.RemoveAll(folder.MappedPath)
  1106. assert.NoError(t, err)
  1107. err = dataprovider.DeleteFolder(foldername, "", "")
  1108. assert.NoError(t, err)
  1109. err = dataprovider.Close()
  1110. assert.NoError(t, err)
  1111. err = dataprovider.Initialize(oldProviderConf, configDir, true)
  1112. assert.NoError(t, err)
  1113. }
  1114. func TestScheduledActions(t *testing.T) {
  1115. startEventScheduler()
  1116. backupsPath := filepath.Join(os.TempDir(), "backups")
  1117. err := os.RemoveAll(backupsPath)
  1118. assert.NoError(t, err)
  1119. action := &dataprovider.BaseEventAction{
  1120. Name: "action",
  1121. Type: dataprovider.ActionTypeBackup,
  1122. }
  1123. err = dataprovider.AddEventAction(action, "", "")
  1124. assert.NoError(t, err)
  1125. rule := &dataprovider.EventRule{
  1126. Name: "rule",
  1127. Trigger: dataprovider.EventTriggerSchedule,
  1128. Conditions: dataprovider.EventConditions{
  1129. Schedules: []dataprovider.Schedule{
  1130. {
  1131. Hours: "11",
  1132. DayOfWeek: "*",
  1133. DayOfMonth: "*",
  1134. Month: "*",
  1135. },
  1136. },
  1137. },
  1138. Actions: []dataprovider.EventAction{
  1139. {
  1140. BaseEventAction: dataprovider.BaseEventAction{
  1141. Name: action.Name,
  1142. },
  1143. Order: 1,
  1144. },
  1145. },
  1146. }
  1147. job := eventCronJob{
  1148. ruleName: rule.Name,
  1149. }
  1150. job.Run() // rule not found
  1151. assert.NoDirExists(t, backupsPath)
  1152. err = dataprovider.AddEventRule(rule, "", "")
  1153. assert.NoError(t, err)
  1154. job.Run()
  1155. assert.DirExists(t, backupsPath)
  1156. action.Type = dataprovider.ActionTypeEmail
  1157. action.Options = dataprovider.BaseEventActionOptions{
  1158. EmailConfig: dataprovider.EventActionEmailConfig{
  1159. Recipients: []string{"example@example.com"},
  1160. Subject: "test with attachments",
  1161. Body: "body",
  1162. Attachments: []string{"/file1.txt"},
  1163. },
  1164. }
  1165. err = dataprovider.UpdateEventAction(action, "", "")
  1166. assert.NoError(t, err)
  1167. job.Run() // action is not compatible with a scheduled rule
  1168. err = dataprovider.DeleteEventRule(rule.Name, "", "")
  1169. assert.NoError(t, err)
  1170. err = dataprovider.DeleteEventAction(action.Name, "", "")
  1171. assert.NoError(t, err)
  1172. err = os.RemoveAll(backupsPath)
  1173. assert.NoError(t, err)
  1174. stopEventScheduler()
  1175. }
  1176. func TestEventParamsCopy(t *testing.T) {
  1177. params := EventParams{
  1178. Name: "name",
  1179. Event: "event",
  1180. Status: 1,
  1181. errors: []string{"error1"},
  1182. }
  1183. paramsCopy := params.getACopy()
  1184. assert.Equal(t, params, *paramsCopy)
  1185. params.Name = "name mod"
  1186. paramsCopy.Event = "event mod"
  1187. paramsCopy.Status = 2
  1188. params.errors = append(params.errors, "error2")
  1189. paramsCopy.errors = append(paramsCopy.errors, "error3")
  1190. assert.Equal(t, []string{"error1", "error3"}, paramsCopy.errors)
  1191. assert.Equal(t, []string{"error1", "error2"}, params.errors)
  1192. assert.Equal(t, "name mod", params.Name)
  1193. assert.Equal(t, "name", paramsCopy.Name)
  1194. assert.Equal(t, "event", params.Event)
  1195. assert.Equal(t, "event mod", paramsCopy.Event)
  1196. assert.Equal(t, 1, params.Status)
  1197. assert.Equal(t, 2, paramsCopy.Status)
  1198. }
  1199. func TestEventParamsStatusFromError(t *testing.T) {
  1200. params := EventParams{Status: 1}
  1201. params.AddError(os.ErrNotExist)
  1202. assert.Equal(t, 1, params.Status)
  1203. params = EventParams{Status: 1, updateStatusFromError: true}
  1204. params.AddError(os.ErrNotExist)
  1205. assert.Equal(t, 2, params.Status)
  1206. }