eventrule.go 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110
  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. "bytes"
  17. "context"
  18. "crypto/tls"
  19. "encoding/json"
  20. "fmt"
  21. "io"
  22. "net/http"
  23. "net/url"
  24. "os"
  25. "os/exec"
  26. "path"
  27. "path/filepath"
  28. "strings"
  29. "time"
  30. "github.com/robfig/cron/v3"
  31. "github.com/drakkan/sftpgo/v2/internal/kms"
  32. "github.com/drakkan/sftpgo/v2/internal/logger"
  33. "github.com/drakkan/sftpgo/v2/internal/smtp"
  34. "github.com/drakkan/sftpgo/v2/internal/util"
  35. "github.com/drakkan/sftpgo/v2/internal/vfs"
  36. )
  37. // Supported event actions
  38. const (
  39. ActionTypeHTTP = iota + 1
  40. ActionTypeCommand
  41. ActionTypeEmail
  42. ActionTypeBackup
  43. ActionTypeUserQuotaReset
  44. ActionTypeFolderQuotaReset
  45. ActionTypeTransferQuotaReset
  46. )
  47. var (
  48. supportedEventActions = []int{ActionTypeHTTP, ActionTypeCommand, ActionTypeEmail, ActionTypeBackup,
  49. ActionTypeUserQuotaReset, ActionTypeFolderQuotaReset, ActionTypeTransferQuotaReset}
  50. )
  51. func isActionTypeValid(action int) bool {
  52. return util.Contains(supportedEventActions, action)
  53. }
  54. func getActionTypeAsString(action int) string {
  55. switch action {
  56. case ActionTypeHTTP:
  57. return "HTTP"
  58. case ActionTypeEmail:
  59. return "Email"
  60. case ActionTypeBackup:
  61. return "Backup"
  62. case ActionTypeUserQuotaReset:
  63. return "User quota reset"
  64. case ActionTypeFolderQuotaReset:
  65. return "Folder quota reset"
  66. case ActionTypeTransferQuotaReset:
  67. return "Transfer quota reset"
  68. default:
  69. return "Command"
  70. }
  71. }
  72. // Supported event triggers
  73. const (
  74. // Filesystem events such as upload, download, mkdir ...
  75. EventTriggerFsEvent = iota + 1
  76. // Provider events such as add, update, delete
  77. EventTriggerProviderEvent
  78. EventTriggerSchedule
  79. )
  80. var (
  81. supportedEventTriggers = []int{EventTriggerFsEvent, EventTriggerProviderEvent, EventTriggerSchedule}
  82. )
  83. func isEventTriggerValid(trigger int) bool {
  84. return util.Contains(supportedEventTriggers, trigger)
  85. }
  86. func getTriggerTypeAsString(trigger int) string {
  87. switch trigger {
  88. case EventTriggerFsEvent:
  89. return "Filesystem event"
  90. case EventTriggerProviderEvent:
  91. return "Provider event"
  92. default:
  93. return "Schedule"
  94. }
  95. }
  96. // TODO: replace the copied strings with shared constants
  97. var (
  98. // SupportedFsEvents defines the supported filesystem events
  99. SupportedFsEvents = []string{"upload", "download", "delete", "rename", "mkdir", "rmdir", "ssh_cmd"}
  100. // SupportedProviderEvents defines the supported provider events
  101. SupportedProviderEvents = []string{operationAdd, operationUpdate, operationDelete}
  102. // SupportedRuleConditionProtocols defines the supported protcols for rule conditions
  103. SupportedRuleConditionProtocols = []string{"SFTP", "SCP", "SSH", "FTP", "DAV", "HTTP", "HTTPShare",
  104. "OIDC"}
  105. // SupporteRuleConditionProviderObjects defines the supported provider objects for rule conditions
  106. SupporteRuleConditionProviderObjects = []string{actionObjectUser, actionObjectGroup, actionObjectAdmin,
  107. actionObjectAPIKey, actionObjectShare, actionObjectEventRule, actionObjectEventAction}
  108. // SupportedHTTPActionMethods defines the supported methods for HTTP actions
  109. SupportedHTTPActionMethods = []string{http.MethodPost, http.MethodGet, http.MethodPut}
  110. )
  111. // enum mappings
  112. var (
  113. EventActionTypes []EnumMapping
  114. EventTriggerTypes []EnumMapping
  115. )
  116. func init() {
  117. for _, t := range supportedEventActions {
  118. EventActionTypes = append(EventActionTypes, EnumMapping{
  119. Value: t,
  120. Name: getActionTypeAsString(t),
  121. })
  122. }
  123. for _, t := range supportedEventTriggers {
  124. EventTriggerTypes = append(EventTriggerTypes, EnumMapping{
  125. Value: t,
  126. Name: getTriggerTypeAsString(t),
  127. })
  128. }
  129. }
  130. // EnumMapping defines a mapping between enum values and names
  131. type EnumMapping struct {
  132. Name string
  133. Value int
  134. }
  135. // KeyValue defines a key/value pair
  136. type KeyValue struct {
  137. Key string `json:"key"`
  138. Value string `json:"value"`
  139. }
  140. // EventActionHTTPConfig defines the configuration for an HTTP event target
  141. type EventActionHTTPConfig struct {
  142. Endpoint string `json:"endpoint"`
  143. Username string `json:"username,omitempty"`
  144. Password *kms.Secret `json:"password,omitempty"`
  145. Headers []KeyValue `json:"headers,omitempty"`
  146. Timeout int `json:"timeout"`
  147. SkipTLSVerify bool `json:"skip_tls_verify,omitempty"`
  148. Method string `json:"method"`
  149. QueryParameters []KeyValue `json:"query_parameters,omitempty"`
  150. Body string `json:"post_body,omitempty"`
  151. }
  152. func (c *EventActionHTTPConfig) validate(additionalData string) error {
  153. if c.Endpoint == "" {
  154. return util.NewValidationError("HTTP endpoint is required")
  155. }
  156. if !util.IsStringPrefixInSlice(c.Endpoint, []string{"http://", "https://"}) {
  157. return util.NewValidationError("invalid HTTP endpoint schema: http and https are supported")
  158. }
  159. if c.Timeout < 1 || c.Timeout > 120 {
  160. return util.NewValidationError(fmt.Sprintf("invalid HTTP timeout %d", c.Timeout))
  161. }
  162. for _, kv := range c.Headers {
  163. if kv.Key == "" || kv.Value == "" {
  164. return util.NewValidationError("invalid HTTP headers")
  165. }
  166. }
  167. if c.Password.IsRedacted() {
  168. return util.NewValidationError("cannot save HTTP configuration with a redacted secret")
  169. }
  170. if c.Password.IsPlain() {
  171. c.Password.SetAdditionalData(additionalData)
  172. err := c.Password.Encrypt()
  173. if err != nil {
  174. return util.NewValidationError(fmt.Sprintf("could not encrypt HTTP password: %v", err))
  175. }
  176. }
  177. if !util.Contains(SupportedHTTPActionMethods, c.Method) {
  178. return util.NewValidationError(fmt.Sprintf("unsupported HTTP method: %s", c.Method))
  179. }
  180. for _, kv := range c.QueryParameters {
  181. if kv.Key == "" || kv.Value == "" {
  182. return util.NewValidationError("invalid HTTP query parameters")
  183. }
  184. }
  185. return nil
  186. }
  187. func (c *EventActionHTTPConfig) getEndpoint(replacer *strings.Replacer) (string, error) {
  188. if len(c.QueryParameters) > 0 {
  189. u, err := url.Parse(c.Endpoint)
  190. if err != nil {
  191. return "", fmt.Errorf("invalid endpoint: %w", err)
  192. }
  193. q := u.Query()
  194. for _, keyVal := range c.QueryParameters {
  195. q.Add(keyVal.Key, replaceWithReplacer(keyVal.Value, replacer))
  196. }
  197. u.RawQuery = q.Encode()
  198. return u.String(), nil
  199. }
  200. return c.Endpoint, nil
  201. }
  202. func (c *EventActionHTTPConfig) getHTTPClient() *http.Client {
  203. client := &http.Client{
  204. Timeout: time.Duration(c.Timeout) * time.Second,
  205. }
  206. if c.SkipTLSVerify {
  207. transport := http.DefaultTransport.(*http.Transport).Clone()
  208. if transport.TLSClientConfig != nil {
  209. transport.TLSClientConfig.InsecureSkipVerify = true
  210. } else {
  211. transport.TLSClientConfig = &tls.Config{
  212. NextProtos: []string{"http/1.1", "h2"},
  213. InsecureSkipVerify: true,
  214. }
  215. }
  216. client.Transport = transport
  217. }
  218. return client
  219. }
  220. func (c *EventActionHTTPConfig) execute(params EventParams) error {
  221. if !c.Password.IsEmpty() {
  222. if err := c.Password.TryDecrypt(); err != nil {
  223. return fmt.Errorf("unable to decrypt password: %w", err)
  224. }
  225. }
  226. addObjectData := false
  227. if params.Object != nil {
  228. if !addObjectData {
  229. if strings.Contains(c.Body, "{{ObjectData}}") {
  230. addObjectData = true
  231. }
  232. }
  233. }
  234. replacements := params.getStringReplacements(addObjectData)
  235. replacer := strings.NewReplacer(replacements...)
  236. endpoint, err := c.getEndpoint(replacer)
  237. if err != nil {
  238. return err
  239. }
  240. var body io.Reader
  241. if c.Body != "" && c.Method != http.MethodGet {
  242. body = bytes.NewBufferString(replaceWithReplacer(c.Body, replacer))
  243. }
  244. req, err := http.NewRequest(c.Method, endpoint, body)
  245. if err != nil {
  246. return err
  247. }
  248. if c.Username != "" {
  249. req.SetBasicAuth(replaceWithReplacer(c.Username, replacer), c.Password.GetAdditionalData())
  250. }
  251. for _, keyVal := range c.Headers {
  252. req.Header.Set(keyVal.Key, replaceWithReplacer(keyVal.Value, replacer))
  253. }
  254. client := c.getHTTPClient()
  255. defer client.CloseIdleConnections()
  256. startTime := time.Now()
  257. resp, err := client.Do(req)
  258. if err != nil {
  259. eventManagerLog(logger.LevelDebug, "unable to send http notification, endpoint: %s, elapsed: %s, err: %v",
  260. endpoint, time.Since(startTime), err)
  261. return err
  262. }
  263. defer resp.Body.Close()
  264. eventManagerLog(logger.LevelDebug, "http notification sent, endopoint: %s, elapsed: %s, status code: %d",
  265. endpoint, time.Since(startTime), resp.StatusCode)
  266. if resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusNoContent {
  267. return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
  268. }
  269. return nil
  270. }
  271. // EventActionCommandConfig defines the configuration for a command event target
  272. type EventActionCommandConfig struct {
  273. Cmd string `json:"cmd"`
  274. Timeout int `json:"timeout"`
  275. EnvVars []KeyValue `json:"env_vars"`
  276. }
  277. func (c *EventActionCommandConfig) validate() error {
  278. if c.Cmd == "" {
  279. return util.NewValidationError("command is required")
  280. }
  281. if !filepath.IsAbs(c.Cmd) {
  282. return util.NewValidationError("invalid command, it must be an absolute path")
  283. }
  284. if c.Timeout < 1 || c.Timeout > 120 {
  285. return util.NewValidationError(fmt.Sprintf("invalid command action timeout %d", c.Timeout))
  286. }
  287. for _, kv := range c.EnvVars {
  288. if kv.Key == "" || kv.Value == "" {
  289. return util.NewValidationError("invalid command env vars")
  290. }
  291. }
  292. return nil
  293. }
  294. func (c *EventActionCommandConfig) getEnvVars(params EventParams) []string {
  295. envVars := make([]string, 0, len(c.EnvVars))
  296. addObjectData := false
  297. if params.Object != nil {
  298. for _, k := range c.EnvVars {
  299. if strings.Contains(k.Value, "{{ObjectData}}") {
  300. addObjectData = true
  301. break
  302. }
  303. }
  304. }
  305. replacements := params.getStringReplacements(addObjectData)
  306. replacer := strings.NewReplacer(replacements...)
  307. for _, keyVal := range c.EnvVars {
  308. envVars = append(envVars, fmt.Sprintf("%s=%s", keyVal.Key, replaceWithReplacer(keyVal.Value, replacer)))
  309. }
  310. return envVars
  311. }
  312. func (c *EventActionCommandConfig) execute(params EventParams) error {
  313. ctx, cancel := context.WithTimeout(context.Background(), time.Duration(c.Timeout)*time.Second)
  314. defer cancel()
  315. cmd := exec.CommandContext(ctx, c.Cmd)
  316. cmd.Env = append(cmd.Env, os.Environ()...)
  317. cmd.Env = append(cmd.Env, c.getEnvVars(params)...)
  318. startTime := time.Now()
  319. err := cmd.Run()
  320. eventManagerLog(logger.LevelDebug, "executed command %q, elapsed: %s, error: %v",
  321. c.Cmd, time.Since(startTime), err)
  322. return err
  323. }
  324. // EventActionEmailConfig defines the configuration options for SMTP event actions
  325. type EventActionEmailConfig struct {
  326. Recipients []string `json:"recipients"`
  327. Subject string `json:"subject"`
  328. Body string `json:"body"`
  329. }
  330. // GetRecipientsAsString returns the list of recipients as comma separated string
  331. func (o EventActionEmailConfig) GetRecipientsAsString() string {
  332. return strings.Join(o.Recipients, ",")
  333. }
  334. func (o *EventActionEmailConfig) validate() error {
  335. if len(o.Recipients) == 0 {
  336. return util.NewValidationError("at least one email recipient is required")
  337. }
  338. o.Recipients = util.RemoveDuplicates(o.Recipients, false)
  339. for _, r := range o.Recipients {
  340. if r == "" {
  341. return util.NewValidationError("invalid email recipients")
  342. }
  343. }
  344. if o.Subject == "" {
  345. return util.NewValidationError("email subject is required")
  346. }
  347. if o.Body == "" {
  348. return util.NewValidationError("email body is required")
  349. }
  350. return nil
  351. }
  352. func (o *EventActionEmailConfig) execute(params EventParams) error {
  353. addObjectData := false
  354. if params.Object != nil {
  355. if strings.Contains(o.Body, "{{ObjectData}}") {
  356. addObjectData = true
  357. }
  358. }
  359. replacements := params.getStringReplacements(addObjectData)
  360. replacer := strings.NewReplacer(replacements...)
  361. body := replaceWithReplacer(o.Body, replacer)
  362. subject := replaceWithReplacer(o.Subject, replacer)
  363. startTime := time.Now()
  364. err := smtp.SendEmail(o.Recipients, subject, body, smtp.EmailContentTypeTextPlain)
  365. eventManagerLog(logger.LevelDebug, "executed email notification action, elapsed: %s, error: %v",
  366. time.Since(startTime), err)
  367. return err
  368. }
  369. // BaseEventActionOptions defines the supported configuration options for a base event actions
  370. type BaseEventActionOptions struct {
  371. HTTPConfig EventActionHTTPConfig `json:"http_config"`
  372. CmdConfig EventActionCommandConfig `json:"cmd_config"`
  373. EmailConfig EventActionEmailConfig `json:"email_config"`
  374. }
  375. func (o *BaseEventActionOptions) getACopy() BaseEventActionOptions {
  376. o.SetEmptySecretsIfNil()
  377. emailRecipients := make([]string, len(o.EmailConfig.Recipients))
  378. copy(emailRecipients, o.EmailConfig.Recipients)
  379. return BaseEventActionOptions{
  380. HTTPConfig: EventActionHTTPConfig{
  381. Endpoint: o.HTTPConfig.Endpoint,
  382. Username: o.HTTPConfig.Username,
  383. Password: o.HTTPConfig.Password.Clone(),
  384. Headers: cloneKeyValues(o.HTTPConfig.Headers),
  385. Timeout: o.HTTPConfig.Timeout,
  386. SkipTLSVerify: o.HTTPConfig.SkipTLSVerify,
  387. Method: o.HTTPConfig.Method,
  388. QueryParameters: cloneKeyValues(o.HTTPConfig.QueryParameters),
  389. Body: o.HTTPConfig.Body,
  390. },
  391. CmdConfig: EventActionCommandConfig{
  392. Cmd: o.CmdConfig.Cmd,
  393. Timeout: o.CmdConfig.Timeout,
  394. EnvVars: cloneKeyValues(o.CmdConfig.EnvVars),
  395. },
  396. EmailConfig: EventActionEmailConfig{
  397. Recipients: emailRecipients,
  398. Subject: o.EmailConfig.Subject,
  399. Body: o.EmailConfig.Body,
  400. },
  401. }
  402. }
  403. // SetEmptySecretsIfNil sets the secrets to empty if nil
  404. func (o *BaseEventActionOptions) SetEmptySecretsIfNil() {
  405. if o.HTTPConfig.Password == nil {
  406. o.HTTPConfig.Password = kms.NewEmptySecret()
  407. }
  408. }
  409. func (o *BaseEventActionOptions) setNilSecretsIfEmpty() {
  410. if o.HTTPConfig.Password != nil && o.HTTPConfig.Password.IsEmpty() {
  411. o.HTTPConfig.Password = nil
  412. }
  413. }
  414. func (o *BaseEventActionOptions) hideConfidentialData() {
  415. if o.HTTPConfig.Password != nil {
  416. o.HTTPConfig.Password.Hide()
  417. }
  418. }
  419. func (o *BaseEventActionOptions) validate(action int, name string) error {
  420. o.SetEmptySecretsIfNil()
  421. switch action {
  422. case ActionTypeHTTP:
  423. o.CmdConfig = EventActionCommandConfig{}
  424. o.EmailConfig = EventActionEmailConfig{}
  425. return o.HTTPConfig.validate(name)
  426. case ActionTypeCommand:
  427. o.HTTPConfig = EventActionHTTPConfig{}
  428. o.EmailConfig = EventActionEmailConfig{}
  429. return o.CmdConfig.validate()
  430. case ActionTypeEmail:
  431. o.HTTPConfig = EventActionHTTPConfig{}
  432. o.CmdConfig = EventActionCommandConfig{}
  433. return o.EmailConfig.validate()
  434. default:
  435. o.HTTPConfig = EventActionHTTPConfig{}
  436. o.CmdConfig = EventActionCommandConfig{}
  437. o.EmailConfig = EventActionEmailConfig{}
  438. }
  439. return nil
  440. }
  441. // BaseEventAction defines the common fields for an event action
  442. type BaseEventAction struct {
  443. // Data provider unique identifier
  444. ID int64 `json:"id"`
  445. // Action name
  446. Name string `json:"name"`
  447. // optional description
  448. Description string `json:"description,omitempty"`
  449. // ActionType, see the above enum
  450. Type int `json:"type"`
  451. // Configuration options specific for the action type
  452. Options BaseEventActionOptions `json:"options"`
  453. // list of rule names associated with this event action
  454. Rules []string `json:"rules,omitempty"`
  455. }
  456. func (a *BaseEventAction) getACopy() BaseEventAction {
  457. rules := make([]string, len(a.Rules))
  458. copy(rules, a.Rules)
  459. return BaseEventAction{
  460. ID: a.ID,
  461. Name: a.Name,
  462. Description: a.Description,
  463. Type: a.Type,
  464. Options: a.Options.getACopy(),
  465. Rules: rules,
  466. }
  467. }
  468. // GetTypeAsString returns the action type as string
  469. func (a *BaseEventAction) GetTypeAsString() string {
  470. return getActionTypeAsString(a.Type)
  471. }
  472. // GetRulesAsString returns the list of rules as comma separated string
  473. func (a *BaseEventAction) GetRulesAsString() string {
  474. return strings.Join(a.Rules, ",")
  475. }
  476. // PrepareForRendering prepares a BaseEventAction for rendering.
  477. // It hides confidential data and set to nil the empty secrets
  478. // so they are not serialized
  479. func (a *BaseEventAction) PrepareForRendering() {
  480. a.Options.setNilSecretsIfEmpty()
  481. a.Options.hideConfidentialData()
  482. }
  483. // RenderAsJSON implements the renderer interface used within plugins
  484. func (a *BaseEventAction) RenderAsJSON(reload bool) ([]byte, error) {
  485. if reload {
  486. action, err := provider.eventActionExists(a.Name)
  487. if err != nil {
  488. providerLog(logger.LevelError, "unable to reload event action before rendering as json: %v", err)
  489. return nil, err
  490. }
  491. action.PrepareForRendering()
  492. return json.Marshal(action)
  493. }
  494. a.PrepareForRendering()
  495. return json.Marshal(a)
  496. }
  497. func (a *BaseEventAction) validate() error {
  498. if a.Name == "" {
  499. return util.NewValidationError("name is mandatory")
  500. }
  501. if !isActionTypeValid(a.Type) {
  502. return util.NewValidationError(fmt.Sprintf("invalid action type: %d", a.Type))
  503. }
  504. return a.Options.validate(a.Type, a.Name)
  505. }
  506. func (a *BaseEventAction) doUsersQuotaReset(conditions ConditionOptions) error {
  507. users, err := provider.dumpUsers()
  508. if err != nil {
  509. return fmt.Errorf("unable to get users: %w", err)
  510. }
  511. var failedResets []string
  512. for _, user := range users {
  513. if !checkConditionPatterns(user.Username, conditions.Names) {
  514. eventManagerLog(logger.LevelDebug, "skipping scheduled quota reset for user %s, name conditions don't match",
  515. user.Username)
  516. continue
  517. }
  518. if !QuotaScans.AddUserQuotaScan(user.Username) {
  519. eventManagerLog(logger.LevelError, "another quota scan is already in progress for user %s", user.Username)
  520. failedResets = append(failedResets, user.Username)
  521. continue
  522. }
  523. numFiles, size, err := user.ScanQuota()
  524. QuotaScans.RemoveUserQuotaScan(user.Username)
  525. if err != nil {
  526. eventManagerLog(logger.LevelError, "error scanning quota for user %s: %v", user.Username, err)
  527. failedResets = append(failedResets, user.Username)
  528. continue
  529. }
  530. err = UpdateUserQuota(&user, numFiles, size, true)
  531. if err != nil {
  532. eventManagerLog(logger.LevelError, "error updating quota for user %s: %v", user.Username, err)
  533. failedResets = append(failedResets, user.Username)
  534. continue
  535. }
  536. }
  537. if len(failedResets) > 0 {
  538. return fmt.Errorf("quota reset failed for users: %+v", failedResets)
  539. }
  540. return nil
  541. }
  542. func (a *BaseEventAction) doFoldersQuotaReset(conditions ConditionOptions) error {
  543. folders, err := provider.dumpFolders()
  544. if err != nil {
  545. return fmt.Errorf("unable to get folders: %w", err)
  546. }
  547. var failedResets []string
  548. for _, folder := range folders {
  549. if !checkConditionPatterns(folder.Name, conditions.Names) {
  550. eventManagerLog(logger.LevelDebug, "skipping scheduled quota reset for folder %s, name conditions don't match",
  551. folder.Name)
  552. continue
  553. }
  554. if !QuotaScans.AddVFolderQuotaScan(folder.Name) {
  555. eventManagerLog(logger.LevelError, "another quota scan is already in progress for folder %s", folder.Name)
  556. failedResets = append(failedResets, folder.Name)
  557. continue
  558. }
  559. f := vfs.VirtualFolder{
  560. BaseVirtualFolder: folder,
  561. VirtualPath: "/",
  562. }
  563. numFiles, size, err := f.ScanQuota()
  564. QuotaScans.RemoveVFolderQuotaScan(folder.Name)
  565. if err != nil {
  566. eventManagerLog(logger.LevelError, "error scanning quota for folder %s: %v", folder.Name, err)
  567. failedResets = append(failedResets, folder.Name)
  568. continue
  569. }
  570. err = UpdateVirtualFolderQuota(&folder, numFiles, size, true)
  571. if err != nil {
  572. eventManagerLog(logger.LevelError, "error updating quota for folder %s: %v", folder.Name, err)
  573. failedResets = append(failedResets, folder.Name)
  574. continue
  575. }
  576. }
  577. if len(failedResets) > 0 {
  578. return fmt.Errorf("quota reset failed for folders: %+v", failedResets)
  579. }
  580. return nil
  581. }
  582. func (a *BaseEventAction) doTransferQuotaReset(conditions ConditionOptions) error {
  583. users, err := provider.dumpUsers()
  584. if err != nil {
  585. return fmt.Errorf("unable to get users: %w", err)
  586. }
  587. var failedResets []string
  588. for _, user := range users {
  589. if !checkConditionPatterns(user.Username, conditions.Names) {
  590. eventManagerLog(logger.LevelDebug, "skipping scheduled transfer quota reset for user %s, name conditions don't match",
  591. user.Username)
  592. continue
  593. }
  594. err = UpdateUserTransferQuota(&user, 0, 0, true)
  595. if err != nil {
  596. eventManagerLog(logger.LevelError, "error updating transfer quota for user %s: %v", user.Username, err)
  597. failedResets = append(failedResets, user.Username)
  598. continue
  599. }
  600. }
  601. if len(failedResets) > 0 {
  602. return fmt.Errorf("transfer quota reset failed for users: %+v", failedResets)
  603. }
  604. return nil
  605. }
  606. func (a *BaseEventAction) execute(params EventParams, conditions ConditionOptions) error {
  607. switch a.Type {
  608. case ActionTypeHTTP:
  609. return a.Options.HTTPConfig.execute(params)
  610. case ActionTypeCommand:
  611. return a.Options.CmdConfig.execute(params)
  612. case ActionTypeEmail:
  613. return a.Options.EmailConfig.execute(params)
  614. case ActionTypeBackup:
  615. return config.doBackup()
  616. case ActionTypeUserQuotaReset:
  617. return a.doUsersQuotaReset(conditions)
  618. case ActionTypeFolderQuotaReset:
  619. return a.doFoldersQuotaReset(conditions)
  620. case ActionTypeTransferQuotaReset:
  621. return a.doTransferQuotaReset(conditions)
  622. default:
  623. return fmt.Errorf("unsupported action type: %d", a.Type)
  624. }
  625. }
  626. // EventActionOptions defines the supported configuration options for an event action
  627. type EventActionOptions struct {
  628. IsFailureAction bool `json:"is_failure_action"`
  629. StopOnFailure bool `json:"stop_on_failure"`
  630. ExecuteSync bool `json:"execute_sync"`
  631. }
  632. // EventAction defines an event action
  633. type EventAction struct {
  634. BaseEventAction
  635. // Order defines the execution order
  636. Order int `json:"order,omitempty"`
  637. Options EventActionOptions `json:"relation_options"`
  638. }
  639. func (a *EventAction) getACopy() EventAction {
  640. return EventAction{
  641. BaseEventAction: a.BaseEventAction.getACopy(),
  642. Order: a.Order,
  643. Options: EventActionOptions{
  644. IsFailureAction: a.Options.IsFailureAction,
  645. StopOnFailure: a.Options.StopOnFailure,
  646. ExecuteSync: a.Options.ExecuteSync,
  647. },
  648. }
  649. }
  650. func (a *EventAction) validateAssociation(trigger int, fsEvents []string) error {
  651. if a.Options.IsFailureAction {
  652. if a.Options.ExecuteSync {
  653. return util.NewValidationError("sync execution is not supported for failure actions")
  654. }
  655. }
  656. if trigger != EventTriggerFsEvent || !util.Contains(fsEvents, "upload") {
  657. if a.Options.ExecuteSync {
  658. return util.NewValidationError("sync execution is only supported for upload event")
  659. }
  660. }
  661. return nil
  662. }
  663. // ConditionPattern defines a pattern for condition filters
  664. type ConditionPattern struct {
  665. Pattern string `json:"pattern,omitempty"`
  666. InverseMatch bool `json:"inverse_match,omitempty"`
  667. }
  668. func (p *ConditionPattern) match(name string) bool {
  669. matched, err := path.Match(p.Pattern, name)
  670. if err != nil {
  671. eventManagerLog(logger.LevelError, "pattern matching error %q, err: %v", p.Pattern, err)
  672. return false
  673. }
  674. if p.InverseMatch {
  675. return !matched
  676. }
  677. return matched
  678. }
  679. func (p *ConditionPattern) validate() error {
  680. if p.Pattern == "" {
  681. return util.NewValidationError("empty condition pattern not allowed")
  682. }
  683. _, err := path.Match(p.Pattern, "abc")
  684. if err != nil {
  685. return util.NewValidationError(fmt.Sprintf("invalid condition pattern %q", p.Pattern))
  686. }
  687. return nil
  688. }
  689. // ConditionOptions defines options for event conditions
  690. type ConditionOptions struct {
  691. // Usernames or folder names
  692. Names []ConditionPattern `json:"names,omitempty"`
  693. // Virtual paths
  694. FsPaths []ConditionPattern `json:"fs_paths,omitempty"`
  695. Protocols []string `json:"protocols,omitempty"`
  696. ProviderObjects []string `json:"provider_objects,omitempty"`
  697. MinFileSize int64 `json:"min_size,omitempty"`
  698. MaxFileSize int64 `json:"max_size,omitempty"`
  699. // allow to execute scheduled tasks concurrently from multiple instances
  700. ConcurrentExecution bool `json:"concurrent_execution,omitempty"`
  701. }
  702. func (f *ConditionOptions) getACopy() ConditionOptions {
  703. protocols := make([]string, len(f.Protocols))
  704. copy(protocols, f.Protocols)
  705. providerObjects := make([]string, len(f.ProviderObjects))
  706. copy(providerObjects, f.ProviderObjects)
  707. return ConditionOptions{
  708. Names: cloneConditionPatterns(f.Names),
  709. FsPaths: cloneConditionPatterns(f.FsPaths),
  710. Protocols: protocols,
  711. ProviderObjects: providerObjects,
  712. MinFileSize: f.MinFileSize,
  713. MaxFileSize: f.MaxFileSize,
  714. ConcurrentExecution: f.ConcurrentExecution,
  715. }
  716. }
  717. func (f *ConditionOptions) validate() error {
  718. for _, name := range f.Names {
  719. if err := name.validate(); err != nil {
  720. return err
  721. }
  722. }
  723. for _, fsPath := range f.FsPaths {
  724. if err := fsPath.validate(); err != nil {
  725. return err
  726. }
  727. }
  728. for _, p := range f.Protocols {
  729. if !util.Contains(SupportedRuleConditionProtocols, p) {
  730. return util.NewValidationError(fmt.Sprintf("unsupported rule condition protocol: %q", p))
  731. }
  732. }
  733. for _, p := range f.ProviderObjects {
  734. if !util.Contains(SupporteRuleConditionProviderObjects, p) {
  735. return util.NewValidationError(fmt.Sprintf("unsupported provider object: %q", p))
  736. }
  737. }
  738. if f.MinFileSize > 0 && f.MaxFileSize > 0 {
  739. if f.MaxFileSize <= f.MinFileSize {
  740. return util.NewValidationError(fmt.Sprintf("invalid max file size %d, it is lesser or equal than min file size %d",
  741. f.MaxFileSize, f.MinFileSize))
  742. }
  743. }
  744. if config.IsShared == 0 {
  745. f.ConcurrentExecution = false
  746. }
  747. return nil
  748. }
  749. // Schedule defines an event schedule
  750. type Schedule struct {
  751. Hours string `json:"hour"`
  752. DayOfWeek string `json:"day_of_week"`
  753. DayOfMonth string `json:"day_of_month"`
  754. Month string `json:"month"`
  755. }
  756. func (s *Schedule) getCronSpec() string {
  757. return fmt.Sprintf("0 %s %s %s %s", s.Hours, s.DayOfMonth, s.Month, s.DayOfWeek)
  758. }
  759. func (s *Schedule) validate() error {
  760. _, err := cron.ParseStandard(s.getCronSpec())
  761. if err != nil {
  762. return util.NewValidationError(fmt.Sprintf("invalid schedule, hour: %q, day of month: %q, month: %q, day of week: %q",
  763. s.Hours, s.DayOfMonth, s.Month, s.DayOfWeek))
  764. }
  765. return nil
  766. }
  767. // EventConditions defines the conditions for an event rule
  768. type EventConditions struct {
  769. // Only one between FsEvents, ProviderEvents and Schedule is allowed
  770. FsEvents []string `json:"fs_events,omitempty"`
  771. ProviderEvents []string `json:"provider_events,omitempty"`
  772. Schedules []Schedule `json:"schedules,omitempty"`
  773. Options ConditionOptions `json:"options"`
  774. }
  775. func (c *EventConditions) getACopy() EventConditions {
  776. fsEvents := make([]string, len(c.FsEvents))
  777. copy(fsEvents, c.FsEvents)
  778. providerEvents := make([]string, len(c.ProviderEvents))
  779. copy(providerEvents, c.ProviderEvents)
  780. schedules := make([]Schedule, 0, len(c.Schedules))
  781. for _, schedule := range c.Schedules {
  782. schedules = append(schedules, Schedule{
  783. Hours: schedule.Hours,
  784. DayOfWeek: schedule.DayOfWeek,
  785. DayOfMonth: schedule.DayOfMonth,
  786. Month: schedule.Month,
  787. })
  788. }
  789. return EventConditions{
  790. FsEvents: fsEvents,
  791. ProviderEvents: providerEvents,
  792. Schedules: schedules,
  793. Options: c.Options.getACopy(),
  794. }
  795. }
  796. // ProviderEventMatch returns true if the specified provider event match
  797. func (c *EventConditions) ProviderEventMatch(params EventParams) bool {
  798. if !util.Contains(c.ProviderEvents, params.Event) {
  799. return false
  800. }
  801. if !checkConditionPatterns(params.Name, c.Options.Names) {
  802. return false
  803. }
  804. if len(c.Options.ProviderObjects) > 0 && !util.Contains(c.Options.ProviderObjects, params.ObjectType) {
  805. return false
  806. }
  807. return true
  808. }
  809. // FsEventMatch returns true if the specified filesystem event match
  810. func (c *EventConditions) FsEventMatch(params EventParams) bool {
  811. if !util.Contains(c.FsEvents, params.Event) {
  812. return false
  813. }
  814. if !checkConditionPatterns(params.Name, c.Options.Names) {
  815. return false
  816. }
  817. if !checkConditionPatterns(params.VirtualPath, c.Options.FsPaths) {
  818. if !checkConditionPatterns(params.ObjectName, c.Options.FsPaths) {
  819. return false
  820. }
  821. }
  822. if len(c.Options.Protocols) > 0 && !util.Contains(c.Options.Protocols, params.Protocol) {
  823. return false
  824. }
  825. if params.Event == "upload" || params.Event == "download" {
  826. if c.Options.MinFileSize > 0 {
  827. if params.FileSize < c.Options.MinFileSize {
  828. return false
  829. }
  830. }
  831. if c.Options.MaxFileSize > 0 {
  832. if params.FileSize > c.Options.MaxFileSize {
  833. return false
  834. }
  835. }
  836. }
  837. return true
  838. }
  839. func (c *EventConditions) validate(trigger int) error {
  840. switch trigger {
  841. case EventTriggerFsEvent:
  842. c.ProviderEvents = nil
  843. c.Schedules = nil
  844. c.Options.ProviderObjects = nil
  845. if len(c.FsEvents) == 0 {
  846. return util.NewValidationError("at least one filesystem event is required")
  847. }
  848. for _, ev := range c.FsEvents {
  849. if !util.Contains(SupportedFsEvents, ev) {
  850. return util.NewValidationError(fmt.Sprintf("unsupported fs event: %q", ev))
  851. }
  852. }
  853. case EventTriggerProviderEvent:
  854. c.FsEvents = nil
  855. c.Schedules = nil
  856. c.Options.FsPaths = nil
  857. c.Options.Protocols = nil
  858. c.Options.MinFileSize = 0
  859. c.Options.MaxFileSize = 0
  860. if len(c.ProviderEvents) == 0 {
  861. return util.NewValidationError("at least one provider event is required")
  862. }
  863. for _, ev := range c.ProviderEvents {
  864. if !util.Contains(SupportedProviderEvents, ev) {
  865. return util.NewValidationError(fmt.Sprintf("unsupported provider event: %q", ev))
  866. }
  867. }
  868. case EventTriggerSchedule:
  869. c.FsEvents = nil
  870. c.ProviderEvents = nil
  871. c.Options.FsPaths = nil
  872. c.Options.Protocols = nil
  873. c.Options.MinFileSize = 0
  874. c.Options.MaxFileSize = 0
  875. c.Options.ProviderObjects = nil
  876. if len(c.Schedules) == 0 {
  877. return util.NewValidationError("at least one schedule is required")
  878. }
  879. for _, schedule := range c.Schedules {
  880. if err := schedule.validate(); err != nil {
  881. return err
  882. }
  883. }
  884. default:
  885. c.FsEvents = nil
  886. c.ProviderEvents = nil
  887. c.Options.FsPaths = nil
  888. c.Options.Protocols = nil
  889. c.Options.MinFileSize = 0
  890. c.Options.MaxFileSize = 0
  891. c.Schedules = nil
  892. }
  893. return c.Options.validate()
  894. }
  895. // EventRule defines the trigger, conditions and actions for an event
  896. type EventRule struct {
  897. // Data provider unique identifier
  898. ID int64 `json:"id"`
  899. // Rule name
  900. Name string `json:"name"`
  901. // optional description
  902. Description string `json:"description,omitempty"`
  903. // Creation time as unix timestamp in milliseconds
  904. CreatedAt int64 `json:"created_at"`
  905. // last update time as unix timestamp in milliseconds
  906. UpdatedAt int64 `json:"updated_at"`
  907. // Event trigger
  908. Trigger int `json:"trigger"`
  909. // Event conditions
  910. Conditions EventConditions `json:"conditions"`
  911. // actions to execute
  912. Actions []EventAction `json:"actions"`
  913. // in multi node setups we mark the rule as deleted to be able to update the cache
  914. DeletedAt int64 `json:"-"`
  915. }
  916. func (r *EventRule) getACopy() EventRule {
  917. actions := make([]EventAction, 0, len(r.Actions))
  918. for _, action := range r.Actions {
  919. actions = append(actions, action.getACopy())
  920. }
  921. return EventRule{
  922. ID: r.ID,
  923. Name: r.Name,
  924. Description: r.Description,
  925. CreatedAt: r.CreatedAt,
  926. UpdatedAt: r.UpdatedAt,
  927. Trigger: r.Trigger,
  928. Conditions: r.Conditions.getACopy(),
  929. Actions: actions,
  930. DeletedAt: r.DeletedAt,
  931. }
  932. }
  933. func (r *EventRule) guardFromConcurrentExecution() bool {
  934. if config.IsShared == 0 {
  935. return false
  936. }
  937. return !r.Conditions.Options.ConcurrentExecution
  938. }
  939. // GetTriggerAsString returns the rule trigger as string
  940. func (r *EventRule) GetTriggerAsString() string {
  941. return getTriggerTypeAsString(r.Trigger)
  942. }
  943. // GetActionsAsString returns the list of action names as comma separated string
  944. func (r *EventRule) GetActionsAsString() string {
  945. actions := make([]string, 0, len(r.Actions))
  946. for _, action := range r.Actions {
  947. actions = append(actions, action.Name)
  948. }
  949. return strings.Join(actions, ",")
  950. }
  951. func (r *EventRule) validate() error {
  952. if r.Name == "" {
  953. return util.NewValidationError("name is mandatory")
  954. }
  955. if !isEventTriggerValid(r.Trigger) {
  956. return util.NewValidationError(fmt.Sprintf("invalid event rule trigger: %d", r.Trigger))
  957. }
  958. if err := r.Conditions.validate(r.Trigger); err != nil {
  959. return err
  960. }
  961. if len(r.Actions) == 0 {
  962. return util.NewValidationError("at least one action is required")
  963. }
  964. actionNames := make(map[string]bool)
  965. actionOrders := make(map[int]bool)
  966. failureActions := 0
  967. for idx := range r.Actions {
  968. if r.Actions[idx].Name == "" {
  969. return util.NewValidationError(fmt.Sprintf("invalid action at position %d, name not specified", idx))
  970. }
  971. if actionNames[r.Actions[idx].Name] {
  972. return util.NewValidationError(fmt.Sprintf("duplicated action %q", r.Actions[idx].Name))
  973. }
  974. if actionOrders[r.Actions[idx].Order] {
  975. return util.NewValidationError(fmt.Sprintf("duplicated order %d for action %q",
  976. r.Actions[idx].Order, r.Actions[idx].Name))
  977. }
  978. if err := r.Actions[idx].validateAssociation(r.Trigger, r.Conditions.FsEvents); err != nil {
  979. return err
  980. }
  981. if r.Actions[idx].Options.IsFailureAction {
  982. failureActions++
  983. }
  984. actionNames[r.Actions[idx].Name] = true
  985. actionOrders[r.Actions[idx].Order] = true
  986. }
  987. if len(r.Actions) == failureActions {
  988. return util.NewValidationError("at least a non-failure action is required")
  989. }
  990. return nil
  991. }
  992. // PrepareForRendering prepares an EventRule for rendering.
  993. // It hides confidential data and set to nil the empty secrets
  994. // so they are not serialized
  995. func (r *EventRule) PrepareForRendering() {
  996. for idx := range r.Actions {
  997. r.Actions[idx].PrepareForRendering()
  998. }
  999. }
  1000. // RenderAsJSON implements the renderer interface used within plugins
  1001. func (r *EventRule) RenderAsJSON(reload bool) ([]byte, error) {
  1002. if reload {
  1003. rule, err := provider.eventRuleExists(r.Name)
  1004. if err != nil {
  1005. providerLog(logger.LevelError, "unable to reload event rule before rendering as json: %v", err)
  1006. return nil, err
  1007. }
  1008. rule.PrepareForRendering()
  1009. return json.Marshal(rule)
  1010. }
  1011. r.PrepareForRendering()
  1012. return json.Marshal(r)
  1013. }
  1014. // Task stores the state for a scheduled task
  1015. type Task struct {
  1016. Name string `json:"name"`
  1017. UpdateAt int64 `json:"updated_at"`
  1018. Version int64 `json:"version"`
  1019. }