eventrule.go 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358
  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 dataprovider
  15. import (
  16. "context"
  17. "crypto/tls"
  18. "encoding/json"
  19. "errors"
  20. "fmt"
  21. "net/http"
  22. "path"
  23. "path/filepath"
  24. "strings"
  25. "time"
  26. "github.com/robfig/cron/v3"
  27. "github.com/drakkan/sftpgo/v2/internal/kms"
  28. "github.com/drakkan/sftpgo/v2/internal/logger"
  29. "github.com/drakkan/sftpgo/v2/internal/util"
  30. )
  31. // Supported event actions
  32. const (
  33. ActionTypeHTTP = iota + 1
  34. ActionTypeCommand
  35. ActionTypeEmail
  36. ActionTypeBackup
  37. ActionTypeUserQuotaReset
  38. ActionTypeFolderQuotaReset
  39. ActionTypeTransferQuotaReset
  40. ActionTypeDataRetentionCheck
  41. ActionTypeFilesystem
  42. )
  43. var (
  44. supportedEventActions = []int{ActionTypeHTTP, ActionTypeCommand, ActionTypeEmail, ActionTypeBackup,
  45. ActionTypeUserQuotaReset, ActionTypeFolderQuotaReset, ActionTypeTransferQuotaReset,
  46. ActionTypeDataRetentionCheck, ActionTypeFilesystem}
  47. )
  48. func isActionTypeValid(action int) bool {
  49. return util.Contains(supportedEventActions, action)
  50. }
  51. func getActionTypeAsString(action int) string {
  52. switch action {
  53. case ActionTypeHTTP:
  54. return "HTTP"
  55. case ActionTypeEmail:
  56. return "Email"
  57. case ActionTypeBackup:
  58. return "Backup"
  59. case ActionTypeUserQuotaReset:
  60. return "User quota reset"
  61. case ActionTypeFolderQuotaReset:
  62. return "Folder quota reset"
  63. case ActionTypeTransferQuotaReset:
  64. return "Transfer quota reset"
  65. case ActionTypeDataRetentionCheck:
  66. return "Data retention check"
  67. case ActionTypeFilesystem:
  68. return "Filesystem"
  69. default:
  70. return "Command"
  71. }
  72. }
  73. // Supported event triggers
  74. const (
  75. // Filesystem events such as upload, download, mkdir ...
  76. EventTriggerFsEvent = iota + 1
  77. // Provider events such as add, update, delete
  78. EventTriggerProviderEvent
  79. EventTriggerSchedule
  80. EventTriggerIPBlocked
  81. EventTriggerCertificate
  82. )
  83. var (
  84. supportedEventTriggers = []int{EventTriggerFsEvent, EventTriggerProviderEvent, EventTriggerSchedule,
  85. EventTriggerIPBlocked, EventTriggerCertificate}
  86. )
  87. func isEventTriggerValid(trigger int) bool {
  88. return util.Contains(supportedEventTriggers, trigger)
  89. }
  90. func getTriggerTypeAsString(trigger int) string {
  91. switch trigger {
  92. case EventTriggerFsEvent:
  93. return "Filesystem event"
  94. case EventTriggerProviderEvent:
  95. return "Provider event"
  96. case EventTriggerIPBlocked:
  97. return "IP blocked"
  98. case EventTriggerCertificate:
  99. return "Certificate renewal"
  100. default:
  101. return "Schedule"
  102. }
  103. }
  104. // Supported filesystem actions
  105. const (
  106. FilesystemActionRename = iota + 1
  107. FilesystemActionDelete
  108. FilesystemActionMkdirs
  109. FilesystemActionExist
  110. )
  111. var (
  112. supportedFsActions = []int{FilesystemActionRename, FilesystemActionDelete, FilesystemActionMkdirs,
  113. FilesystemActionExist}
  114. )
  115. func isFilesystemActionValid(value int) bool {
  116. return util.Contains(supportedFsActions, value)
  117. }
  118. func getFsActionTypeAsString(value int) string {
  119. switch value {
  120. case FilesystemActionRename:
  121. return "Rename"
  122. case FilesystemActionDelete:
  123. return "Delete"
  124. case FilesystemActionExist:
  125. return "Paths exist"
  126. default:
  127. return "Create directories"
  128. }
  129. }
  130. // TODO: replace the copied strings with shared constants
  131. var (
  132. // SupportedFsEvents defines the supported filesystem events
  133. SupportedFsEvents = []string{"upload", "first-upload", "download", "first-download", "delete", "rename",
  134. "mkdir", "rmdir", "ssh_cmd"}
  135. // SupportedProviderEvents defines the supported provider events
  136. SupportedProviderEvents = []string{operationAdd, operationUpdate, operationDelete}
  137. // SupportedRuleConditionProtocols defines the supported protcols for rule conditions
  138. SupportedRuleConditionProtocols = []string{"SFTP", "SCP", "SSH", "FTP", "DAV", "HTTP", "HTTPShare",
  139. "OIDC"}
  140. // SupporteRuleConditionProviderObjects defines the supported provider objects for rule conditions
  141. SupporteRuleConditionProviderObjects = []string{actionObjectUser, actionObjectFolder, actionObjectGroup,
  142. actionObjectAdmin, actionObjectAPIKey, actionObjectShare, actionObjectEventRule, actionObjectEventAction}
  143. // SupportedHTTPActionMethods defines the supported methods for HTTP actions
  144. SupportedHTTPActionMethods = []string{http.MethodPost, http.MethodGet, http.MethodPut}
  145. )
  146. // enum mappings
  147. var (
  148. EventActionTypes []EnumMapping
  149. EventTriggerTypes []EnumMapping
  150. FsActionTypes []EnumMapping
  151. )
  152. func init() {
  153. for _, t := range supportedEventActions {
  154. EventActionTypes = append(EventActionTypes, EnumMapping{
  155. Value: t,
  156. Name: getActionTypeAsString(t),
  157. })
  158. }
  159. for _, t := range supportedEventTriggers {
  160. EventTriggerTypes = append(EventTriggerTypes, EnumMapping{
  161. Value: t,
  162. Name: getTriggerTypeAsString(t),
  163. })
  164. }
  165. for _, t := range supportedFsActions {
  166. FsActionTypes = append(FsActionTypes, EnumMapping{
  167. Value: t,
  168. Name: getFsActionTypeAsString(t),
  169. })
  170. }
  171. }
  172. // EnumMapping defines a mapping between enum values and names
  173. type EnumMapping struct {
  174. Name string
  175. Value int
  176. }
  177. // KeyValue defines a key/value pair
  178. type KeyValue struct {
  179. Key string `json:"key"`
  180. Value string `json:"value"`
  181. }
  182. func (k *KeyValue) isNotValid() bool {
  183. return k.Key == "" || k.Value == ""
  184. }
  185. // HTTPPart defines a part for HTTP multipart requests
  186. type HTTPPart struct {
  187. Name string `json:"name,omitempty"`
  188. Filepath string `json:"filepath,omitempty"`
  189. Headers []KeyValue `json:"headers,omitempty"`
  190. Body string `json:"body,omitempty"`
  191. Order int `json:"-"`
  192. }
  193. func (p *HTTPPart) validate() error {
  194. if p.Name == "" {
  195. return util.NewValidationError("HTTP part name is required")
  196. }
  197. for _, kv := range p.Headers {
  198. if kv.isNotValid() {
  199. return util.NewValidationError("invalid HTTP part headers")
  200. }
  201. }
  202. if p.Filepath == "" {
  203. if p.Body == "" {
  204. return util.NewValidationError("HTTP part body is required if no file path is provided")
  205. }
  206. } else {
  207. p.Body = ""
  208. p.Filepath = util.CleanPath(p.Filepath)
  209. }
  210. return nil
  211. }
  212. // EventActionHTTPConfig defines the configuration for an HTTP event target
  213. type EventActionHTTPConfig struct {
  214. Endpoint string `json:"endpoint,omitempty"`
  215. Username string `json:"username,omitempty"`
  216. Password *kms.Secret `json:"password,omitempty"`
  217. Headers []KeyValue `json:"headers,omitempty"`
  218. Timeout int `json:"timeout,omitempty"`
  219. SkipTLSVerify bool `json:"skip_tls_verify,omitempty"`
  220. Method string `json:"method,omitempty"`
  221. QueryParameters []KeyValue `json:"query_parameters,omitempty"`
  222. Body string `json:"body,omitempty"`
  223. Parts []HTTPPart `json:"parts,omitempty"`
  224. }
  225. func (c *EventActionHTTPConfig) isTimeoutNotValid() bool {
  226. if c.HasMultipartFile() {
  227. return false
  228. }
  229. return c.Timeout < 1 || c.Timeout > 120
  230. }
  231. func (c *EventActionHTTPConfig) validateMultiparts() error {
  232. for idx := range c.Parts {
  233. if err := c.Parts[idx].validate(); err != nil {
  234. return err
  235. }
  236. }
  237. if len(c.Parts) > 0 {
  238. if c.Body != "" {
  239. return util.NewValidationError("multipart requests require no body. The request body is build from the specified parts")
  240. }
  241. for _, k := range c.Headers {
  242. if strings.ToLower(k.Key) == "content-type" {
  243. return util.NewValidationError("content type is automatically set for multipart requests")
  244. }
  245. }
  246. }
  247. return nil
  248. }
  249. func (c *EventActionHTTPConfig) validate(additionalData string) error {
  250. if c.Endpoint == "" {
  251. return util.NewValidationError("HTTP endpoint is required")
  252. }
  253. if !util.IsStringPrefixInSlice(c.Endpoint, []string{"http://", "https://"}) {
  254. return util.NewValidationError("invalid HTTP endpoint schema: http and https are supported")
  255. }
  256. if c.isTimeoutNotValid() {
  257. return util.NewValidationError(fmt.Sprintf("invalid HTTP timeout %d", c.Timeout))
  258. }
  259. for _, kv := range c.Headers {
  260. if kv.isNotValid() {
  261. return util.NewValidationError("invalid HTTP headers")
  262. }
  263. }
  264. if err := c.validateMultiparts(); err != nil {
  265. return err
  266. }
  267. if c.Password.IsRedacted() {
  268. return util.NewValidationError("cannot save HTTP configuration with a redacted secret")
  269. }
  270. if c.Password.IsPlain() {
  271. c.Password.SetAdditionalData(additionalData)
  272. err := c.Password.Encrypt()
  273. if err != nil {
  274. return util.NewValidationError(fmt.Sprintf("could not encrypt HTTP password: %v", err))
  275. }
  276. }
  277. if !util.Contains(SupportedHTTPActionMethods, c.Method) {
  278. return util.NewValidationError(fmt.Sprintf("unsupported HTTP method: %s", c.Method))
  279. }
  280. for _, kv := range c.QueryParameters {
  281. if kv.isNotValid() {
  282. return util.NewValidationError("invalid HTTP query parameters")
  283. }
  284. }
  285. return nil
  286. }
  287. // GetContext returns the context and the cancel func to use for the HTTP request
  288. func (c *EventActionHTTPConfig) GetContext() (context.Context, context.CancelFunc) {
  289. if c.HasMultipartFile() {
  290. return context.WithCancel(context.Background())
  291. }
  292. return context.WithTimeout(context.Background(), time.Duration(c.Timeout)*time.Second)
  293. }
  294. // HasObjectData returns true if the {{ObjectData}} placeholder is defined
  295. func (c *EventActionHTTPConfig) HasObjectData() bool {
  296. if strings.Contains(c.Body, "{{ObjectData}}") {
  297. return true
  298. }
  299. for _, part := range c.Parts {
  300. if strings.Contains(part.Body, "{{ObjectData}}") {
  301. return true
  302. }
  303. }
  304. return false
  305. }
  306. // HasMultipartFile returns true if a file must be uploaded via a multipart request
  307. func (c *EventActionHTTPConfig) HasMultipartFile() bool {
  308. for _, part := range c.Parts {
  309. if part.Filepath != "" {
  310. return true
  311. }
  312. }
  313. return false
  314. }
  315. // TryDecryptPassword decrypts the password if encryptet
  316. func (c *EventActionHTTPConfig) TryDecryptPassword() error {
  317. if c.Password != nil && !c.Password.IsEmpty() {
  318. if err := c.Password.TryDecrypt(); err != nil {
  319. return fmt.Errorf("unable to decrypt HTTP password: %w", err)
  320. }
  321. }
  322. return nil
  323. }
  324. // GetHTTPClient returns an HTTP client based on the config
  325. func (c *EventActionHTTPConfig) GetHTTPClient() *http.Client {
  326. client := &http.Client{}
  327. if c.SkipTLSVerify {
  328. transport := http.DefaultTransport.(*http.Transport).Clone()
  329. if transport.TLSClientConfig != nil {
  330. transport.TLSClientConfig.InsecureSkipVerify = true
  331. } else {
  332. transport.TLSClientConfig = &tls.Config{
  333. NextProtos: []string{"http/1.1", "h2"},
  334. InsecureSkipVerify: true,
  335. }
  336. }
  337. client.Transport = transport
  338. }
  339. return client
  340. }
  341. // EventActionCommandConfig defines the configuration for a command event target
  342. type EventActionCommandConfig struct {
  343. Cmd string `json:"cmd,omitempty"`
  344. Args []string `json:"args,omitempty"`
  345. Timeout int `json:"timeout,omitempty"`
  346. EnvVars []KeyValue `json:"env_vars,omitempty"`
  347. }
  348. func (c *EventActionCommandConfig) validate() error {
  349. if c.Cmd == "" {
  350. return util.NewValidationError("command is required")
  351. }
  352. if !filepath.IsAbs(c.Cmd) {
  353. return util.NewValidationError("invalid command, it must be an absolute path")
  354. }
  355. if c.Timeout < 1 || c.Timeout > 120 {
  356. return util.NewValidationError(fmt.Sprintf("invalid command action timeout %d", c.Timeout))
  357. }
  358. for _, kv := range c.EnvVars {
  359. if kv.isNotValid() {
  360. return util.NewValidationError("invalid command env vars")
  361. }
  362. }
  363. c.Args = util.RemoveDuplicates(c.Args, true)
  364. for _, arg := range c.Args {
  365. if arg == "" {
  366. return util.NewValidationError("invalid command args")
  367. }
  368. }
  369. return nil
  370. }
  371. // GetArgumentsAsString returns the list of command arguments as comma separated string
  372. func (c EventActionCommandConfig) GetArgumentsAsString() string {
  373. return strings.Join(c.Args, ",")
  374. }
  375. // EventActionEmailConfig defines the configuration options for SMTP event actions
  376. type EventActionEmailConfig struct {
  377. Recipients []string `json:"recipients,omitempty"`
  378. Subject string `json:"subject,omitempty"`
  379. Body string `json:"body,omitempty"`
  380. Attachments []string `json:"attachments,omitempty"`
  381. }
  382. // GetRecipientsAsString returns the list of recipients as comma separated string
  383. func (c EventActionEmailConfig) GetRecipientsAsString() string {
  384. return strings.Join(c.Recipients, ",")
  385. }
  386. // GetAttachmentsAsString returns the list of attachments as comma separated string
  387. func (c EventActionEmailConfig) GetAttachmentsAsString() string {
  388. return strings.Join(c.Attachments, ",")
  389. }
  390. func (c *EventActionEmailConfig) validate() error {
  391. if len(c.Recipients) == 0 {
  392. return util.NewValidationError("at least one email recipient is required")
  393. }
  394. c.Recipients = util.RemoveDuplicates(c.Recipients, false)
  395. for _, r := range c.Recipients {
  396. if r == "" {
  397. return util.NewValidationError("invalid email recipients")
  398. }
  399. }
  400. if c.Subject == "" {
  401. return util.NewValidationError("email subject is required")
  402. }
  403. if c.Body == "" {
  404. return util.NewValidationError("email body is required")
  405. }
  406. for idx, val := range c.Attachments {
  407. val = strings.TrimSpace(val)
  408. if val == "" {
  409. return util.NewValidationError("invalid path to attach")
  410. }
  411. c.Attachments[idx] = util.CleanPath(val)
  412. }
  413. c.Attachments = util.RemoveDuplicates(c.Attachments, false)
  414. return nil
  415. }
  416. // FolderRetention defines a folder retention configuration
  417. type FolderRetention struct {
  418. // Path is the exposed virtual directory path, if no other specific retention is defined,
  419. // the retention applies for sub directories too. For example if retention is defined
  420. // for the paths "/" and "/sub" then the retention for "/" is applied for any file outside
  421. // the "/sub" directory
  422. Path string `json:"path"`
  423. // Retention time in hours. 0 means exclude this path
  424. Retention int `json:"retention"`
  425. // DeleteEmptyDirs defines if empty directories will be deleted.
  426. // The user need the delete permission
  427. DeleteEmptyDirs bool `json:"delete_empty_dirs,omitempty"`
  428. // IgnoreUserPermissions defines whether to delete files even if the user does not have the delete permission.
  429. // The default is "false" which means that files will be skipped if the user does not have the permission
  430. // to delete them. This applies to sub directories too.
  431. IgnoreUserPermissions bool `json:"ignore_user_permissions,omitempty"`
  432. }
  433. // Validate returns an error if the configuration is not valid
  434. func (f *FolderRetention) Validate() error {
  435. f.Path = util.CleanPath(f.Path)
  436. if f.Retention < 0 {
  437. return util.NewValidationError(fmt.Sprintf("invalid folder retention %v, it must be greater or equal to zero",
  438. f.Retention))
  439. }
  440. return nil
  441. }
  442. // EventActionDataRetentionConfig defines the configuration for a data retention check
  443. type EventActionDataRetentionConfig struct {
  444. Folders []FolderRetention `json:"folders,omitempty"`
  445. }
  446. func (c *EventActionDataRetentionConfig) validate() error {
  447. folderPaths := make(map[string]bool)
  448. nothingToDo := true
  449. for idx := range c.Folders {
  450. f := &c.Folders[idx]
  451. if err := f.Validate(); err != nil {
  452. return err
  453. }
  454. if f.Retention > 0 {
  455. nothingToDo = false
  456. }
  457. if _, ok := folderPaths[f.Path]; ok {
  458. return util.NewValidationError(fmt.Sprintf("duplicated folder path %#v", f.Path))
  459. }
  460. folderPaths[f.Path] = true
  461. }
  462. if nothingToDo {
  463. return util.NewValidationError("nothing to delete!")
  464. }
  465. return nil
  466. }
  467. // EventActionFilesystemConfig defines the configuration for filesystem actions
  468. type EventActionFilesystemConfig struct {
  469. // Filesystem actions, see the above enum
  470. Type int `json:"type,omitempty"`
  471. // files/dirs to rename, key is the source and target the value
  472. Renames []KeyValue `json:"renames,omitempty"`
  473. // directories to create
  474. MkDirs []string `json:"mkdirs,omitempty"`
  475. // files/dirs to delete
  476. Deletes []string `json:"deletes,omitempty"`
  477. // file/dirs to check for existence
  478. Exist []string `json:"exist,omitempty"`
  479. }
  480. // GetDeletesAsString returns the list of items to delete as comma separated string.
  481. // Using a pointer receiver will not work in web templates
  482. func (c EventActionFilesystemConfig) GetDeletesAsString() string {
  483. return strings.Join(c.Deletes, ",")
  484. }
  485. // GetMkDirsAsString returns the list of directories to create as comma separated string.
  486. // Using a pointer receiver will not work in web templates
  487. func (c EventActionFilesystemConfig) GetMkDirsAsString() string {
  488. return strings.Join(c.MkDirs, ",")
  489. }
  490. // GetExistAsString returns the list of items to check for existence as comma separated string.
  491. // Using a pointer receiver will not work in web templates
  492. func (c EventActionFilesystemConfig) GetExistAsString() string {
  493. return strings.Join(c.Exist, ",")
  494. }
  495. func (c *EventActionFilesystemConfig) validateRenames() error {
  496. if len(c.Renames) == 0 {
  497. return util.NewValidationError("no path to rename specified")
  498. }
  499. for idx, kv := range c.Renames {
  500. key := strings.TrimSpace(kv.Key)
  501. value := strings.TrimSpace(kv.Value)
  502. if key == "" || value == "" {
  503. return util.NewValidationError("invalid paths to rename")
  504. }
  505. key = util.CleanPath(key)
  506. value = util.CleanPath(value)
  507. if key == value {
  508. return util.NewValidationError("rename source and target cannot be equal")
  509. }
  510. if key == "/" || value == "/" {
  511. return util.NewValidationError("renaming the root directory is not allowed")
  512. }
  513. c.Renames[idx] = KeyValue{
  514. Key: key,
  515. Value: value,
  516. }
  517. }
  518. return nil
  519. }
  520. func (c *EventActionFilesystemConfig) validateDeletes() error {
  521. if len(c.Deletes) == 0 {
  522. return util.NewValidationError("no path to delete specified")
  523. }
  524. for idx, val := range c.Deletes {
  525. val = strings.TrimSpace(val)
  526. if val == "" {
  527. return util.NewValidationError("invalid path to delete")
  528. }
  529. c.Deletes[idx] = util.CleanPath(val)
  530. }
  531. c.Deletes = util.RemoveDuplicates(c.Deletes, false)
  532. return nil
  533. }
  534. func (c *EventActionFilesystemConfig) validateMkdirs() error {
  535. if len(c.MkDirs) == 0 {
  536. return util.NewValidationError("no directory to create specified")
  537. }
  538. for idx, val := range c.MkDirs {
  539. val = strings.TrimSpace(val)
  540. if val == "" {
  541. return util.NewValidationError("invalid directory to create")
  542. }
  543. c.MkDirs[idx] = util.CleanPath(val)
  544. }
  545. c.MkDirs = util.RemoveDuplicates(c.MkDirs, false)
  546. return nil
  547. }
  548. func (c *EventActionFilesystemConfig) validateExist() error {
  549. if len(c.Exist) == 0 {
  550. return util.NewValidationError("no path to check for existence specified")
  551. }
  552. for idx, val := range c.Exist {
  553. val = strings.TrimSpace(val)
  554. if val == "" {
  555. return util.NewValidationError("invalid path to check for existence")
  556. }
  557. c.Exist[idx] = util.CleanPath(val)
  558. }
  559. c.Exist = util.RemoveDuplicates(c.Exist, false)
  560. return nil
  561. }
  562. func (c *EventActionFilesystemConfig) validate() error {
  563. if !isFilesystemActionValid(c.Type) {
  564. return util.NewValidationError(fmt.Sprintf("invalid filesystem action type: %d", c.Type))
  565. }
  566. switch c.Type {
  567. case FilesystemActionRename:
  568. c.MkDirs = nil
  569. c.Deletes = nil
  570. c.Exist = nil
  571. if err := c.validateRenames(); err != nil {
  572. return err
  573. }
  574. case FilesystemActionDelete:
  575. c.Renames = nil
  576. c.MkDirs = nil
  577. c.Exist = nil
  578. if err := c.validateDeletes(); err != nil {
  579. return err
  580. }
  581. case FilesystemActionMkdirs:
  582. c.Renames = nil
  583. c.Deletes = nil
  584. c.Exist = nil
  585. if err := c.validateMkdirs(); err != nil {
  586. return err
  587. }
  588. case FilesystemActionExist:
  589. c.Renames = nil
  590. c.Deletes = nil
  591. c.MkDirs = nil
  592. if err := c.validateExist(); err != nil {
  593. return err
  594. }
  595. }
  596. return nil
  597. }
  598. func (c *EventActionFilesystemConfig) getACopy() EventActionFilesystemConfig {
  599. mkdirs := make([]string, len(c.MkDirs))
  600. copy(mkdirs, c.MkDirs)
  601. deletes := make([]string, len(c.Deletes))
  602. copy(deletes, c.Deletes)
  603. exist := make([]string, len(c.Exist))
  604. copy(exist, c.Exist)
  605. return EventActionFilesystemConfig{
  606. Type: c.Type,
  607. Renames: cloneKeyValues(c.Renames),
  608. MkDirs: mkdirs,
  609. Deletes: deletes,
  610. Exist: exist,
  611. }
  612. }
  613. // BaseEventActionOptions defines the supported configuration options for a base event actions
  614. type BaseEventActionOptions struct {
  615. HTTPConfig EventActionHTTPConfig `json:"http_config"`
  616. CmdConfig EventActionCommandConfig `json:"cmd_config"`
  617. EmailConfig EventActionEmailConfig `json:"email_config"`
  618. RetentionConfig EventActionDataRetentionConfig `json:"retention_config"`
  619. FsConfig EventActionFilesystemConfig `json:"fs_config"`
  620. }
  621. func (o *BaseEventActionOptions) getACopy() BaseEventActionOptions {
  622. o.SetEmptySecretsIfNil()
  623. emailRecipients := make([]string, len(o.EmailConfig.Recipients))
  624. copy(emailRecipients, o.EmailConfig.Recipients)
  625. emailAttachments := make([]string, len(o.EmailConfig.Attachments))
  626. copy(emailAttachments, o.EmailConfig.Attachments)
  627. cmdArgs := make([]string, len(o.CmdConfig.Args))
  628. copy(cmdArgs, o.CmdConfig.Args)
  629. folders := make([]FolderRetention, 0, len(o.RetentionConfig.Folders))
  630. for _, folder := range o.RetentionConfig.Folders {
  631. folders = append(folders, FolderRetention{
  632. Path: folder.Path,
  633. Retention: folder.Retention,
  634. DeleteEmptyDirs: folder.DeleteEmptyDirs,
  635. IgnoreUserPermissions: folder.IgnoreUserPermissions,
  636. })
  637. }
  638. httpParts := make([]HTTPPart, 0, len(o.HTTPConfig.Parts))
  639. for _, part := range o.HTTPConfig.Parts {
  640. httpParts = append(httpParts, HTTPPart{
  641. Name: part.Name,
  642. Filepath: part.Filepath,
  643. Headers: cloneKeyValues(part.Headers),
  644. Body: part.Body,
  645. })
  646. }
  647. return BaseEventActionOptions{
  648. HTTPConfig: EventActionHTTPConfig{
  649. Endpoint: o.HTTPConfig.Endpoint,
  650. Username: o.HTTPConfig.Username,
  651. Password: o.HTTPConfig.Password.Clone(),
  652. Headers: cloneKeyValues(o.HTTPConfig.Headers),
  653. Timeout: o.HTTPConfig.Timeout,
  654. SkipTLSVerify: o.HTTPConfig.SkipTLSVerify,
  655. Method: o.HTTPConfig.Method,
  656. QueryParameters: cloneKeyValues(o.HTTPConfig.QueryParameters),
  657. Body: o.HTTPConfig.Body,
  658. Parts: httpParts,
  659. },
  660. CmdConfig: EventActionCommandConfig{
  661. Cmd: o.CmdConfig.Cmd,
  662. Args: cmdArgs,
  663. Timeout: o.CmdConfig.Timeout,
  664. EnvVars: cloneKeyValues(o.CmdConfig.EnvVars),
  665. },
  666. EmailConfig: EventActionEmailConfig{
  667. Recipients: emailRecipients,
  668. Subject: o.EmailConfig.Subject,
  669. Body: o.EmailConfig.Body,
  670. Attachments: emailAttachments,
  671. },
  672. RetentionConfig: EventActionDataRetentionConfig{
  673. Folders: folders,
  674. },
  675. FsConfig: o.FsConfig.getACopy(),
  676. }
  677. }
  678. // SetEmptySecretsIfNil sets the secrets to empty if nil
  679. func (o *BaseEventActionOptions) SetEmptySecretsIfNil() {
  680. if o.HTTPConfig.Password == nil {
  681. o.HTTPConfig.Password = kms.NewEmptySecret()
  682. }
  683. }
  684. func (o *BaseEventActionOptions) setNilSecretsIfEmpty() {
  685. if o.HTTPConfig.Password != nil && o.HTTPConfig.Password.IsEmpty() {
  686. o.HTTPConfig.Password = nil
  687. }
  688. }
  689. func (o *BaseEventActionOptions) hideConfidentialData() {
  690. if o.HTTPConfig.Password != nil {
  691. o.HTTPConfig.Password.Hide()
  692. }
  693. }
  694. func (o *BaseEventActionOptions) validate(action int, name string) error {
  695. o.SetEmptySecretsIfNil()
  696. switch action {
  697. case ActionTypeHTTP:
  698. o.CmdConfig = EventActionCommandConfig{}
  699. o.EmailConfig = EventActionEmailConfig{}
  700. o.RetentionConfig = EventActionDataRetentionConfig{}
  701. o.FsConfig = EventActionFilesystemConfig{}
  702. return o.HTTPConfig.validate(name)
  703. case ActionTypeCommand:
  704. o.HTTPConfig = EventActionHTTPConfig{}
  705. o.EmailConfig = EventActionEmailConfig{}
  706. o.RetentionConfig = EventActionDataRetentionConfig{}
  707. o.FsConfig = EventActionFilesystemConfig{}
  708. return o.CmdConfig.validate()
  709. case ActionTypeEmail:
  710. o.HTTPConfig = EventActionHTTPConfig{}
  711. o.CmdConfig = EventActionCommandConfig{}
  712. o.RetentionConfig = EventActionDataRetentionConfig{}
  713. o.FsConfig = EventActionFilesystemConfig{}
  714. return o.EmailConfig.validate()
  715. case ActionTypeDataRetentionCheck:
  716. o.HTTPConfig = EventActionHTTPConfig{}
  717. o.CmdConfig = EventActionCommandConfig{}
  718. o.EmailConfig = EventActionEmailConfig{}
  719. o.FsConfig = EventActionFilesystemConfig{}
  720. return o.RetentionConfig.validate()
  721. case ActionTypeFilesystem:
  722. o.HTTPConfig = EventActionHTTPConfig{}
  723. o.CmdConfig = EventActionCommandConfig{}
  724. o.EmailConfig = EventActionEmailConfig{}
  725. o.RetentionConfig = EventActionDataRetentionConfig{}
  726. return o.FsConfig.validate()
  727. default:
  728. o.HTTPConfig = EventActionHTTPConfig{}
  729. o.CmdConfig = EventActionCommandConfig{}
  730. o.EmailConfig = EventActionEmailConfig{}
  731. o.RetentionConfig = EventActionDataRetentionConfig{}
  732. o.FsConfig = EventActionFilesystemConfig{}
  733. }
  734. return nil
  735. }
  736. // BaseEventAction defines the common fields for an event action
  737. type BaseEventAction struct {
  738. // Data provider unique identifier
  739. ID int64 `json:"id"`
  740. // Action name
  741. Name string `json:"name"`
  742. // optional description
  743. Description string `json:"description,omitempty"`
  744. // ActionType, see the above enum
  745. Type int `json:"type"`
  746. // Configuration options specific for the action type
  747. Options BaseEventActionOptions `json:"options"`
  748. // list of rule names associated with this event action
  749. Rules []string `json:"rules,omitempty"`
  750. }
  751. func (a *BaseEventAction) getACopy() BaseEventAction {
  752. rules := make([]string, len(a.Rules))
  753. copy(rules, a.Rules)
  754. return BaseEventAction{
  755. ID: a.ID,
  756. Name: a.Name,
  757. Description: a.Description,
  758. Type: a.Type,
  759. Options: a.Options.getACopy(),
  760. Rules: rules,
  761. }
  762. }
  763. // GetTypeAsString returns the action type as string
  764. func (a *BaseEventAction) GetTypeAsString() string {
  765. return getActionTypeAsString(a.Type)
  766. }
  767. // GetRulesAsString returns the list of rules as comma separated string
  768. func (a *BaseEventAction) GetRulesAsString() string {
  769. return strings.Join(a.Rules, ",")
  770. }
  771. // PrepareForRendering prepares a BaseEventAction for rendering.
  772. // It hides confidential data and set to nil the empty secrets
  773. // so they are not serialized
  774. func (a *BaseEventAction) PrepareForRendering() {
  775. a.Options.setNilSecretsIfEmpty()
  776. a.Options.hideConfidentialData()
  777. }
  778. // RenderAsJSON implements the renderer interface used within plugins
  779. func (a *BaseEventAction) RenderAsJSON(reload bool) ([]byte, error) {
  780. if reload {
  781. action, err := provider.eventActionExists(a.Name)
  782. if err != nil {
  783. providerLog(logger.LevelError, "unable to reload event action before rendering as json: %v", err)
  784. return nil, err
  785. }
  786. action.PrepareForRendering()
  787. return json.Marshal(action)
  788. }
  789. a.PrepareForRendering()
  790. return json.Marshal(a)
  791. }
  792. func (a *BaseEventAction) validate() error {
  793. if a.Name == "" {
  794. return util.NewValidationError("name is mandatory")
  795. }
  796. if !isActionTypeValid(a.Type) {
  797. return util.NewValidationError(fmt.Sprintf("invalid action type: %d", a.Type))
  798. }
  799. return a.Options.validate(a.Type, a.Name)
  800. }
  801. // EventActionOptions defines the supported configuration options for an event action
  802. type EventActionOptions struct {
  803. IsFailureAction bool `json:"is_failure_action"`
  804. StopOnFailure bool `json:"stop_on_failure"`
  805. ExecuteSync bool `json:"execute_sync"`
  806. }
  807. // EventAction defines an event action
  808. type EventAction struct {
  809. BaseEventAction
  810. // Order defines the execution order
  811. Order int `json:"order,omitempty"`
  812. Options EventActionOptions `json:"relation_options"`
  813. }
  814. func (a *EventAction) getACopy() EventAction {
  815. return EventAction{
  816. BaseEventAction: a.BaseEventAction.getACopy(),
  817. Order: a.Order,
  818. Options: EventActionOptions{
  819. IsFailureAction: a.Options.IsFailureAction,
  820. StopOnFailure: a.Options.StopOnFailure,
  821. ExecuteSync: a.Options.ExecuteSync,
  822. },
  823. }
  824. }
  825. func (a *EventAction) validateAssociation(trigger int, fsEvents []string) error {
  826. if a.Options.IsFailureAction {
  827. if a.Options.ExecuteSync {
  828. return util.NewValidationError("sync execution is not supported for failure actions")
  829. }
  830. }
  831. if trigger != EventTriggerFsEvent || !util.Contains(fsEvents, "upload") {
  832. if a.Options.ExecuteSync {
  833. return util.NewValidationError("sync execution is only supported for upload event")
  834. }
  835. }
  836. return nil
  837. }
  838. // ConditionPattern defines a pattern for condition filters
  839. type ConditionPattern struct {
  840. Pattern string `json:"pattern,omitempty"`
  841. InverseMatch bool `json:"inverse_match,omitempty"`
  842. }
  843. func (p *ConditionPattern) validate() error {
  844. if p.Pattern == "" {
  845. return util.NewValidationError("empty condition pattern not allowed")
  846. }
  847. _, err := path.Match(p.Pattern, "abc")
  848. if err != nil {
  849. return util.NewValidationError(fmt.Sprintf("invalid condition pattern %q", p.Pattern))
  850. }
  851. return nil
  852. }
  853. // ConditionOptions defines options for event conditions
  854. type ConditionOptions struct {
  855. // Usernames or folder names
  856. Names []ConditionPattern `json:"names,omitempty"`
  857. // Group names
  858. GroupNames []ConditionPattern `json:"group_names,omitempty"`
  859. // Virtual paths
  860. FsPaths []ConditionPattern `json:"fs_paths,omitempty"`
  861. Protocols []string `json:"protocols,omitempty"`
  862. ProviderObjects []string `json:"provider_objects,omitempty"`
  863. MinFileSize int64 `json:"min_size,omitempty"`
  864. MaxFileSize int64 `json:"max_size,omitempty"`
  865. // allow to execute scheduled tasks concurrently from multiple instances
  866. ConcurrentExecution bool `json:"concurrent_execution,omitempty"`
  867. }
  868. func (f *ConditionOptions) getACopy() ConditionOptions {
  869. protocols := make([]string, len(f.Protocols))
  870. copy(protocols, f.Protocols)
  871. providerObjects := make([]string, len(f.ProviderObjects))
  872. copy(providerObjects, f.ProviderObjects)
  873. return ConditionOptions{
  874. Names: cloneConditionPatterns(f.Names),
  875. GroupNames: cloneConditionPatterns(f.GroupNames),
  876. FsPaths: cloneConditionPatterns(f.FsPaths),
  877. Protocols: protocols,
  878. ProviderObjects: providerObjects,
  879. MinFileSize: f.MinFileSize,
  880. MaxFileSize: f.MaxFileSize,
  881. ConcurrentExecution: f.ConcurrentExecution,
  882. }
  883. }
  884. func (f *ConditionOptions) validate() error {
  885. for _, name := range f.Names {
  886. if err := name.validate(); err != nil {
  887. return err
  888. }
  889. }
  890. for _, name := range f.GroupNames {
  891. if err := name.validate(); err != nil {
  892. return err
  893. }
  894. }
  895. for _, fsPath := range f.FsPaths {
  896. if err := fsPath.validate(); err != nil {
  897. return err
  898. }
  899. }
  900. for _, p := range f.Protocols {
  901. if !util.Contains(SupportedRuleConditionProtocols, p) {
  902. return util.NewValidationError(fmt.Sprintf("unsupported rule condition protocol: %q", p))
  903. }
  904. }
  905. for _, p := range f.ProviderObjects {
  906. if !util.Contains(SupporteRuleConditionProviderObjects, p) {
  907. return util.NewValidationError(fmt.Sprintf("unsupported provider object: %q", p))
  908. }
  909. }
  910. if f.MinFileSize > 0 && f.MaxFileSize > 0 {
  911. if f.MaxFileSize <= f.MinFileSize {
  912. return util.NewValidationError(fmt.Sprintf("invalid max file size %d, it is lesser or equal than min file size %d",
  913. f.MaxFileSize, f.MinFileSize))
  914. }
  915. }
  916. if config.IsShared == 0 {
  917. f.ConcurrentExecution = false
  918. }
  919. return nil
  920. }
  921. // Schedule defines an event schedule
  922. type Schedule struct {
  923. Hours string `json:"hour"`
  924. DayOfWeek string `json:"day_of_week"`
  925. DayOfMonth string `json:"day_of_month"`
  926. Month string `json:"month"`
  927. }
  928. // GetCronSpec returns the cron compatible schedule string
  929. func (s *Schedule) GetCronSpec() string {
  930. return fmt.Sprintf("0 %s %s %s %s", s.Hours, s.DayOfMonth, s.Month, s.DayOfWeek)
  931. }
  932. func (s *Schedule) validate() error {
  933. _, err := cron.ParseStandard(s.GetCronSpec())
  934. if err != nil {
  935. return util.NewValidationError(fmt.Sprintf("invalid schedule, hour: %q, day of month: %q, month: %q, day of week: %q",
  936. s.Hours, s.DayOfMonth, s.Month, s.DayOfWeek))
  937. }
  938. return nil
  939. }
  940. // EventConditions defines the conditions for an event rule
  941. type EventConditions struct {
  942. // Only one between FsEvents, ProviderEvents and Schedule is allowed
  943. FsEvents []string `json:"fs_events,omitempty"`
  944. ProviderEvents []string `json:"provider_events,omitempty"`
  945. Schedules []Schedule `json:"schedules,omitempty"`
  946. Options ConditionOptions `json:"options"`
  947. }
  948. func (c *EventConditions) getACopy() EventConditions {
  949. fsEvents := make([]string, len(c.FsEvents))
  950. copy(fsEvents, c.FsEvents)
  951. providerEvents := make([]string, len(c.ProviderEvents))
  952. copy(providerEvents, c.ProviderEvents)
  953. schedules := make([]Schedule, 0, len(c.Schedules))
  954. for _, schedule := range c.Schedules {
  955. schedules = append(schedules, Schedule{
  956. Hours: schedule.Hours,
  957. DayOfWeek: schedule.DayOfWeek,
  958. DayOfMonth: schedule.DayOfMonth,
  959. Month: schedule.Month,
  960. })
  961. }
  962. return EventConditions{
  963. FsEvents: fsEvents,
  964. ProviderEvents: providerEvents,
  965. Schedules: schedules,
  966. Options: c.Options.getACopy(),
  967. }
  968. }
  969. func (c *EventConditions) validate(trigger int) error {
  970. switch trigger {
  971. case EventTriggerFsEvent:
  972. c.ProviderEvents = nil
  973. c.Schedules = nil
  974. c.Options.ProviderObjects = nil
  975. if len(c.FsEvents) == 0 {
  976. return util.NewValidationError("at least one filesystem event is required")
  977. }
  978. for _, ev := range c.FsEvents {
  979. if !util.Contains(SupportedFsEvents, ev) {
  980. return util.NewValidationError(fmt.Sprintf("unsupported fs event: %q", ev))
  981. }
  982. }
  983. case EventTriggerProviderEvent:
  984. c.FsEvents = nil
  985. c.Schedules = nil
  986. c.Options.GroupNames = nil
  987. c.Options.FsPaths = nil
  988. c.Options.Protocols = nil
  989. c.Options.MinFileSize = 0
  990. c.Options.MaxFileSize = 0
  991. if len(c.ProviderEvents) == 0 {
  992. return util.NewValidationError("at least one provider event is required")
  993. }
  994. for _, ev := range c.ProviderEvents {
  995. if !util.Contains(SupportedProviderEvents, ev) {
  996. return util.NewValidationError(fmt.Sprintf("unsupported provider event: %q", ev))
  997. }
  998. }
  999. case EventTriggerSchedule:
  1000. c.FsEvents = nil
  1001. c.ProviderEvents = nil
  1002. c.Options.FsPaths = nil
  1003. c.Options.Protocols = nil
  1004. c.Options.MinFileSize = 0
  1005. c.Options.MaxFileSize = 0
  1006. c.Options.ProviderObjects = nil
  1007. if len(c.Schedules) == 0 {
  1008. return util.NewValidationError("at least one schedule is required")
  1009. }
  1010. for _, schedule := range c.Schedules {
  1011. if err := schedule.validate(); err != nil {
  1012. return err
  1013. }
  1014. }
  1015. case EventTriggerIPBlocked, EventTriggerCertificate:
  1016. c.FsEvents = nil
  1017. c.ProviderEvents = nil
  1018. c.Options.Names = nil
  1019. c.Options.GroupNames = nil
  1020. c.Options.FsPaths = nil
  1021. c.Options.Protocols = nil
  1022. c.Options.MinFileSize = 0
  1023. c.Options.MaxFileSize = 0
  1024. c.Schedules = nil
  1025. default:
  1026. c.FsEvents = nil
  1027. c.ProviderEvents = nil
  1028. c.Options.GroupNames = nil
  1029. c.Options.FsPaths = nil
  1030. c.Options.Protocols = nil
  1031. c.Options.MinFileSize = 0
  1032. c.Options.MaxFileSize = 0
  1033. c.Schedules = nil
  1034. }
  1035. return c.Options.validate()
  1036. }
  1037. // EventRule defines the trigger, conditions and actions for an event
  1038. type EventRule struct {
  1039. // Data provider unique identifier
  1040. ID int64 `json:"id"`
  1041. // Rule name
  1042. Name string `json:"name"`
  1043. // optional description
  1044. Description string `json:"description,omitempty"`
  1045. // Creation time as unix timestamp in milliseconds
  1046. CreatedAt int64 `json:"created_at"`
  1047. // last update time as unix timestamp in milliseconds
  1048. UpdatedAt int64 `json:"updated_at"`
  1049. // Event trigger
  1050. Trigger int `json:"trigger"`
  1051. // Event conditions
  1052. Conditions EventConditions `json:"conditions"`
  1053. // actions to execute
  1054. Actions []EventAction `json:"actions"`
  1055. // in multi node setups we mark the rule as deleted to be able to update the cache
  1056. DeletedAt int64 `json:"-"`
  1057. }
  1058. func (r *EventRule) getACopy() EventRule {
  1059. actions := make([]EventAction, 0, len(r.Actions))
  1060. for _, action := range r.Actions {
  1061. actions = append(actions, action.getACopy())
  1062. }
  1063. return EventRule{
  1064. ID: r.ID,
  1065. Name: r.Name,
  1066. Description: r.Description,
  1067. CreatedAt: r.CreatedAt,
  1068. UpdatedAt: r.UpdatedAt,
  1069. Trigger: r.Trigger,
  1070. Conditions: r.Conditions.getACopy(),
  1071. Actions: actions,
  1072. DeletedAt: r.DeletedAt,
  1073. }
  1074. }
  1075. // GuardFromConcurrentExecution returns true if the rule cannot be executed concurrently
  1076. // from multiple instances
  1077. func (r *EventRule) GuardFromConcurrentExecution() bool {
  1078. if config.IsShared == 0 {
  1079. return false
  1080. }
  1081. return !r.Conditions.Options.ConcurrentExecution
  1082. }
  1083. // GetTriggerAsString returns the rule trigger as string
  1084. func (r *EventRule) GetTriggerAsString() string {
  1085. return getTriggerTypeAsString(r.Trigger)
  1086. }
  1087. // GetActionsAsString returns the list of action names as comma separated string
  1088. func (r *EventRule) GetActionsAsString() string {
  1089. actions := make([]string, 0, len(r.Actions))
  1090. for _, action := range r.Actions {
  1091. actions = append(actions, action.Name)
  1092. }
  1093. return strings.Join(actions, ",")
  1094. }
  1095. func (r *EventRule) validate() error {
  1096. if r.Name == "" {
  1097. return util.NewValidationError("name is mandatory")
  1098. }
  1099. if !isEventTriggerValid(r.Trigger) {
  1100. return util.NewValidationError(fmt.Sprintf("invalid event rule trigger: %d", r.Trigger))
  1101. }
  1102. if err := r.Conditions.validate(r.Trigger); err != nil {
  1103. return err
  1104. }
  1105. if len(r.Actions) == 0 {
  1106. return util.NewValidationError("at least one action is required")
  1107. }
  1108. actionNames := make(map[string]bool)
  1109. actionOrders := make(map[int]bool)
  1110. failureActions := 0
  1111. for idx := range r.Actions {
  1112. if r.Actions[idx].Name == "" {
  1113. return util.NewValidationError(fmt.Sprintf("invalid action at position %d, name not specified", idx))
  1114. }
  1115. if actionNames[r.Actions[idx].Name] {
  1116. return util.NewValidationError(fmt.Sprintf("duplicated action %q", r.Actions[idx].Name))
  1117. }
  1118. if actionOrders[r.Actions[idx].Order] {
  1119. return util.NewValidationError(fmt.Sprintf("duplicated order %d for action %q",
  1120. r.Actions[idx].Order, r.Actions[idx].Name))
  1121. }
  1122. if err := r.Actions[idx].validateAssociation(r.Trigger, r.Conditions.FsEvents); err != nil {
  1123. return err
  1124. }
  1125. if r.Actions[idx].Options.IsFailureAction {
  1126. failureActions++
  1127. }
  1128. actionNames[r.Actions[idx].Name] = true
  1129. actionOrders[r.Actions[idx].Order] = true
  1130. }
  1131. if len(r.Actions) == failureActions {
  1132. return util.NewValidationError("at least a non-failure action is required")
  1133. }
  1134. return nil
  1135. }
  1136. func (r *EventRule) checkIPBlockedAndCertificateActions() error {
  1137. unavailableActions := []int{ActionTypeUserQuotaReset, ActionTypeFolderQuotaReset, ActionTypeTransferQuotaReset,
  1138. ActionTypeDataRetentionCheck, ActionTypeFilesystem}
  1139. for _, action := range r.Actions {
  1140. if util.Contains(unavailableActions, action.Type) {
  1141. return fmt.Errorf("action %q, type %q is not supported for event trigger %q",
  1142. action.Name, getActionTypeAsString(action.Type), getTriggerTypeAsString(r.Trigger))
  1143. }
  1144. }
  1145. return nil
  1146. }
  1147. func (r *EventRule) checkProviderEventActions(providerObjectType string) error {
  1148. // user quota reset, transfer quota reset, data retention check and filesystem actions
  1149. // can be executed only if we modify a user. They will be executed for the
  1150. // affected user. Folder quota reset can be executed only for folders.
  1151. userSpecificActions := []int{ActionTypeUserQuotaReset, ActionTypeTransferQuotaReset,
  1152. ActionTypeDataRetentionCheck, ActionTypeFilesystem}
  1153. for _, action := range r.Actions {
  1154. if util.Contains(userSpecificActions, action.Type) && providerObjectType != actionObjectUser {
  1155. return fmt.Errorf("action %q, type %q is only supported for provider user events",
  1156. action.Name, getActionTypeAsString(action.Type))
  1157. }
  1158. if action.Type == ActionTypeFolderQuotaReset && providerObjectType != actionObjectFolder {
  1159. return fmt.Errorf("action %q, type %q is only supported for provider folder events",
  1160. action.Name, getActionTypeAsString(action.Type))
  1161. }
  1162. }
  1163. return nil
  1164. }
  1165. func (r *EventRule) hasUserAssociated(providerObjectType string) bool {
  1166. switch r.Trigger {
  1167. case EventTriggerProviderEvent:
  1168. return providerObjectType == actionObjectUser
  1169. case EventTriggerFsEvent:
  1170. return true
  1171. }
  1172. return false
  1173. }
  1174. // CheckActionsConsistency returns an error if the actions cannot be executed
  1175. func (r *EventRule) CheckActionsConsistency(providerObjectType string) error {
  1176. switch r.Trigger {
  1177. case EventTriggerProviderEvent:
  1178. if err := r.checkProviderEventActions(providerObjectType); err != nil {
  1179. return err
  1180. }
  1181. case EventTriggerFsEvent:
  1182. // folder quota reset cannot be executed
  1183. for _, action := range r.Actions {
  1184. if action.Type == ActionTypeFolderQuotaReset {
  1185. return fmt.Errorf("action %q, type %q is not supported for filesystem events",
  1186. action.Name, getActionTypeAsString(action.Type))
  1187. }
  1188. }
  1189. case EventTriggerIPBlocked, EventTriggerCertificate:
  1190. if err := r.checkIPBlockedAndCertificateActions(); err != nil {
  1191. return err
  1192. }
  1193. }
  1194. for _, action := range r.Actions {
  1195. if action.Type == ActionTypeEmail && len(action.BaseEventAction.Options.EmailConfig.Attachments) > 0 {
  1196. if !r.hasUserAssociated(providerObjectType) {
  1197. return errors.New("cannot send an email with attachments for a rule with no user associated")
  1198. }
  1199. }
  1200. if action.Type == ActionTypeHTTP && action.BaseEventAction.Options.HTTPConfig.HasMultipartFile() {
  1201. if !r.hasUserAssociated(providerObjectType) {
  1202. return errors.New("cannot upload file/s for a rule with no user associated")
  1203. }
  1204. }
  1205. }
  1206. return nil
  1207. }
  1208. // PrepareForRendering prepares an EventRule for rendering.
  1209. // It hides confidential data and set to nil the empty secrets
  1210. // so they are not serialized
  1211. func (r *EventRule) PrepareForRendering() {
  1212. for idx := range r.Actions {
  1213. r.Actions[idx].PrepareForRendering()
  1214. }
  1215. }
  1216. // RenderAsJSON implements the renderer interface used within plugins
  1217. func (r *EventRule) RenderAsJSON(reload bool) ([]byte, error) {
  1218. if reload {
  1219. rule, err := provider.eventRuleExists(r.Name)
  1220. if err != nil {
  1221. providerLog(logger.LevelError, "unable to reload event rule before rendering as json: %v", err)
  1222. return nil, err
  1223. }
  1224. rule.PrepareForRendering()
  1225. return json.Marshal(rule)
  1226. }
  1227. r.PrepareForRendering()
  1228. return json.Marshal(r)
  1229. }
  1230. func cloneKeyValues(keyVals []KeyValue) []KeyValue {
  1231. res := make([]KeyValue, 0, len(keyVals))
  1232. for _, kv := range keyVals {
  1233. res = append(res, KeyValue{
  1234. Key: kv.Key,
  1235. Value: kv.Value,
  1236. })
  1237. }
  1238. return res
  1239. }
  1240. func cloneConditionPatterns(patterns []ConditionPattern) []ConditionPattern {
  1241. res := make([]ConditionPattern, 0, len(patterns))
  1242. for _, p := range patterns {
  1243. res = append(res, ConditionPattern{
  1244. Pattern: p.Pattern,
  1245. InverseMatch: p.InverseMatch,
  1246. })
  1247. }
  1248. return res
  1249. }
  1250. // Task stores the state for a scheduled task
  1251. type Task struct {
  1252. Name string `json:"name"`
  1253. UpdateAt int64 `json:"updated_at"`
  1254. Version int64 `json:"version"`
  1255. }