| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286 | 
							- // Copyright (C) 2019-2023 Nicola Murino
 
- //
 
- // This program is free software: you can redistribute it and/or modify
 
- // it under the terms of the GNU Affero General Public License as published
 
- // by the Free Software Foundation, version 3.
 
- //
 
- // This program is distributed in the hope that it will be useful,
 
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
 
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 
- // GNU Affero General Public License for more details.
 
- //
 
- // You should have received a copy of the GNU Affero General Public License
 
- // along with this program. If not, see <https://www.gnu.org/licenses/>.
 
- package common
 
- import (
 
- 	"bytes"
 
- 	"crypto/rand"
 
- 	"errors"
 
- 	"fmt"
 
- 	"io"
 
- 	"mime/multipart"
 
- 	"net/http"
 
- 	"net/url"
 
- 	"os"
 
- 	"path"
 
- 	"path/filepath"
 
- 	"runtime"
 
- 	"strings"
 
- 	"testing"
 
- 	"time"
 
- 	"github.com/klauspost/compress/zip"
 
- 	"github.com/rs/xid"
 
- 	"github.com/sftpgo/sdk"
 
- 	sdkkms "github.com/sftpgo/sdk/kms"
 
- 	"github.com/stretchr/testify/assert"
 
- 	"github.com/stretchr/testify/require"
 
- 	"github.com/drakkan/sftpgo/v2/internal/dataprovider"
 
- 	"github.com/drakkan/sftpgo/v2/internal/kms"
 
- 	"github.com/drakkan/sftpgo/v2/internal/util"
 
- 	"github.com/drakkan/sftpgo/v2/internal/vfs"
 
- )
 
- func TestEventRuleMatch(t *testing.T) {
 
- 	role := "role1"
 
- 	conditions := &dataprovider.EventConditions{
 
- 		ProviderEvents: []string{"add", "update"},
 
- 		Options: dataprovider.ConditionOptions{
 
- 			Names: []dataprovider.ConditionPattern{
 
- 				{
 
- 					Pattern:      "user1",
 
- 					InverseMatch: true,
 
- 				},
 
- 			},
 
- 			RoleNames: []dataprovider.ConditionPattern{
 
- 				{
 
- 					Pattern: role,
 
- 				},
 
- 			},
 
- 		},
 
- 	}
 
- 	res := eventManager.checkProviderEventMatch(conditions, &EventParams{
 
- 		Name:  "user1",
 
- 		Role:  role,
 
- 		Event: "add",
 
- 	})
 
- 	assert.False(t, res)
 
- 	res = eventManager.checkProviderEventMatch(conditions, &EventParams{
 
- 		Name:  "user2",
 
- 		Role:  role,
 
- 		Event: "update",
 
- 	})
 
- 	assert.True(t, res)
 
- 	res = eventManager.checkProviderEventMatch(conditions, &EventParams{
 
- 		Name:  "user2",
 
- 		Role:  role,
 
- 		Event: "delete",
 
- 	})
 
- 	assert.False(t, res)
 
- 	conditions.Options.ProviderObjects = []string{"api_key"}
 
- 	res = eventManager.checkProviderEventMatch(conditions, &EventParams{
 
- 		Name:       "user2",
 
- 		Event:      "update",
 
- 		Role:       role,
 
- 		ObjectType: "share",
 
- 	})
 
- 	assert.False(t, res)
 
- 	res = eventManager.checkProviderEventMatch(conditions, &EventParams{
 
- 		Name:       "user2",
 
- 		Event:      "update",
 
- 		Role:       role,
 
- 		ObjectType: "api_key",
 
- 	})
 
- 	assert.True(t, res)
 
- 	res = eventManager.checkProviderEventMatch(conditions, &EventParams{
 
- 		Name:       "user2",
 
- 		Event:      "update",
 
- 		Role:       role + "1",
 
- 		ObjectType: "api_key",
 
- 	})
 
- 	assert.False(t, res)
 
- 	// now test fs events
 
- 	conditions = &dataprovider.EventConditions{
 
- 		FsEvents: []string{operationUpload, operationDownload},
 
- 		Options: dataprovider.ConditionOptions{
 
- 			Names: []dataprovider.ConditionPattern{
 
- 				{
 
- 					Pattern: "user*",
 
- 				},
 
- 				{
 
- 					Pattern: "tester*",
 
- 				},
 
- 			},
 
- 			RoleNames: []dataprovider.ConditionPattern{
 
- 				{
 
- 					Pattern:      role,
 
- 					InverseMatch: true,
 
- 				},
 
- 			},
 
- 			FsPaths: []dataprovider.ConditionPattern{
 
- 				{
 
- 					Pattern: "/**/*.txt",
 
- 				},
 
- 			},
 
- 			Protocols:   []string{ProtocolSFTP},
 
- 			MinFileSize: 10,
 
- 			MaxFileSize: 30,
 
- 		},
 
- 	}
 
- 	params := EventParams{
 
- 		Name:        "tester4",
 
- 		Event:       operationDelete,
 
- 		VirtualPath: "/path.txt",
 
- 		Protocol:    ProtocolSFTP,
 
- 		ObjectName:  "path.txt",
 
- 		FileSize:    20,
 
- 	}
 
- 	res = eventManager.checkFsEventMatch(conditions, ¶ms)
 
- 	assert.False(t, res)
 
- 	params.Event = operationDownload
 
- 	res = eventManager.checkFsEventMatch(conditions, ¶ms)
 
- 	assert.True(t, res)
 
- 	params.Role = role
 
- 	res = eventManager.checkFsEventMatch(conditions, ¶ms)
 
- 	assert.False(t, res)
 
- 	params.Role = ""
 
- 	params.Name = "name"
 
- 	res = eventManager.checkFsEventMatch(conditions, ¶ms)
 
- 	assert.False(t, res)
 
- 	params.Name = "user5"
 
- 	res = eventManager.checkFsEventMatch(conditions, ¶ms)
 
- 	assert.True(t, res)
 
- 	params.VirtualPath = "/sub/f.jpg"
 
- 	params.ObjectName = path.Base(params.VirtualPath)
 
- 	res = eventManager.checkFsEventMatch(conditions, ¶ms)
 
- 	assert.False(t, res)
 
- 	params.VirtualPath = "/sub/f.txt"
 
- 	params.ObjectName = path.Base(params.VirtualPath)
 
- 	res = eventManager.checkFsEventMatch(conditions, ¶ms)
 
- 	assert.True(t, res)
 
- 	params.Protocol = ProtocolHTTP
 
- 	res = eventManager.checkFsEventMatch(conditions, ¶ms)
 
- 	assert.False(t, res)
 
- 	params.Protocol = ProtocolSFTP
 
- 	params.FileSize = 5
 
- 	res = eventManager.checkFsEventMatch(conditions, ¶ms)
 
- 	assert.False(t, res)
 
- 	params.FileSize = 50
 
- 	res = eventManager.checkFsEventMatch(conditions, ¶ms)
 
- 	assert.False(t, res)
 
- 	params.FileSize = 25
 
- 	res = eventManager.checkFsEventMatch(conditions, ¶ms)
 
- 	assert.True(t, res)
 
- 	// bad pattern
 
- 	conditions.Options.Names = []dataprovider.ConditionPattern{
 
- 		{
 
- 			Pattern: "[-]",
 
- 		},
 
- 	}
 
- 	res = eventManager.checkFsEventMatch(conditions, ¶ms)
 
- 	assert.False(t, res)
 
- 	// check fs events with group name filters
 
- 	conditions = &dataprovider.EventConditions{
 
- 		FsEvents: []string{operationUpload, operationDownload},
 
- 		Options: dataprovider.ConditionOptions{
 
- 			GroupNames: []dataprovider.ConditionPattern{
 
- 				{
 
- 					Pattern: "group*",
 
- 				},
 
- 				{
 
- 					Pattern: "testgroup*",
 
- 				},
 
- 			},
 
- 		},
 
- 	}
 
- 	params = EventParams{
 
- 		Name:  "user1",
 
- 		Event: operationUpload,
 
- 	}
 
- 	res = eventManager.checkFsEventMatch(conditions, ¶ms)
 
- 	assert.False(t, res)
 
- 	params.Groups = []sdk.GroupMapping{
 
- 		{
 
- 			Name: "g1",
 
- 			Type: sdk.GroupTypePrimary,
 
- 		},
 
- 		{
 
- 			Name: "g2",
 
- 			Type: sdk.GroupTypeSecondary,
 
- 		},
 
- 	}
 
- 	res = eventManager.checkFsEventMatch(conditions, ¶ms)
 
- 	assert.False(t, res)
 
- 	params.Groups = []sdk.GroupMapping{
 
- 		{
 
- 			Name: "testgroup2",
 
- 			Type: sdk.GroupTypePrimary,
 
- 		},
 
- 		{
 
- 			Name: "g2",
 
- 			Type: sdk.GroupTypeSecondary,
 
- 		},
 
- 	}
 
- 	res = eventManager.checkFsEventMatch(conditions, ¶ms)
 
- 	assert.True(t, res)
 
- 	// check user conditions
 
- 	user := dataprovider.User{}
 
- 	user.Username = "u1"
 
- 	res = checkUserConditionOptions(&user, &dataprovider.ConditionOptions{})
 
- 	assert.True(t, res)
 
- 	res = checkUserConditionOptions(&user, &dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: "user",
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.False(t, res)
 
- 	res = checkUserConditionOptions(&user, &dataprovider.ConditionOptions{
 
- 		RoleNames: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: role,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.False(t, res)
 
- 	user.Role = role
 
- 	res = checkUserConditionOptions(&user, &dataprovider.ConditionOptions{
 
- 		RoleNames: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: role,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.True(t, res)
 
- 	res = checkUserConditionOptions(&user, &dataprovider.ConditionOptions{
 
- 		GroupNames: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: "group",
 
- 			},
 
- 		},
 
- 		RoleNames: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: role,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.False(t, res)
 
- 	res = eventManager.checkIPDLoginEventMatch(&dataprovider.EventConditions{
 
- 		IDPLoginEvent: 0,
 
- 	}, &EventParams{
 
- 		Event: IDPLoginAdmin,
 
- 	})
 
- 	assert.True(t, res)
 
- 	res = eventManager.checkIPDLoginEventMatch(&dataprovider.EventConditions{
 
- 		IDPLoginEvent: 2,
 
- 	}, &EventParams{
 
- 		Event: IDPLoginAdmin,
 
- 	})
 
- 	assert.True(t, res)
 
- 	res = eventManager.checkIPDLoginEventMatch(&dataprovider.EventConditions{
 
- 		IDPLoginEvent: 1,
 
- 	}, &EventParams{
 
- 		Event: IDPLoginAdmin,
 
- 	})
 
- 	assert.False(t, res)
 
- 	res = eventManager.checkIPDLoginEventMatch(&dataprovider.EventConditions{
 
- 		IDPLoginEvent: 1,
 
- 	}, &EventParams{
 
- 		Event: IDPLoginUser,
 
- 	})
 
- 	assert.True(t, res)
 
- 	res = eventManager.checkIPDLoginEventMatch(&dataprovider.EventConditions{
 
- 		IDPLoginEvent: 1,
 
- 	}, &EventParams{
 
- 		Name:  "user",
 
- 		Event: IDPLoginUser,
 
- 	})
 
- 	assert.True(t, res)
 
- 	res = eventManager.checkIPDLoginEventMatch(&dataprovider.EventConditions{
 
- 		IDPLoginEvent: 1,
 
- 		Options: dataprovider.ConditionOptions{
 
- 			Names: []dataprovider.ConditionPattern{
 
- 				{
 
- 					Pattern: "abc",
 
- 				},
 
- 			},
 
- 		},
 
- 	}, &EventParams{
 
- 		Name:  "user",
 
- 		Event: IDPLoginUser,
 
- 	})
 
- 	assert.False(t, res)
 
- 	res = eventManager.checkIPDLoginEventMatch(&dataprovider.EventConditions{
 
- 		IDPLoginEvent: 2,
 
- 	}, &EventParams{
 
- 		Name:  "user",
 
- 		Event: IDPLoginUser,
 
- 	})
 
- 	assert.False(t, res)
 
- }
 
- func TestDoubleStarMatching(t *testing.T) {
 
- 	c := dataprovider.ConditionPattern{
 
- 		Pattern: "/mydir/**",
 
- 	}
 
- 	res := checkEventConditionPattern(c, "/mydir")
 
- 	assert.True(t, res)
 
- 	res = checkEventConditionPattern(c, "/mydirname")
 
- 	assert.False(t, res)
 
- 	res = checkEventConditionPattern(c, "/mydir/sub")
 
- 	assert.True(t, res)
 
- 	res = checkEventConditionPattern(c, "/mydir/sub/dir")
 
- 	assert.True(t, res)
 
- 	c.Pattern = "/**/*"
 
- 	res = checkEventConditionPattern(c, "/mydir")
 
- 	assert.True(t, res)
 
- 	res = checkEventConditionPattern(c, "/mydirname")
 
- 	assert.True(t, res)
 
- 	res = checkEventConditionPattern(c, "/mydir/sub/dir/file.txt")
 
- 	assert.True(t, res)
 
- 	c.Pattern = "/mydir/**/*.txt"
 
- 	res = checkEventConditionPattern(c, "/mydir")
 
- 	assert.False(t, res)
 
- 	res = checkEventConditionPattern(c, "/mydirname/f.txt")
 
- 	assert.False(t, res)
 
- 	res = checkEventConditionPattern(c, "/mydir/sub")
 
- 	assert.False(t, res)
 
- 	res = checkEventConditionPattern(c, "/mydir/sub/dir")
 
- 	assert.False(t, res)
 
- 	res = checkEventConditionPattern(c, "/mydir/sub/dir/a.txt")
 
- 	assert.True(t, res)
 
- 	c.InverseMatch = true
 
- 	assert.True(t, checkEventConditionPattern(c, "/mydir"))
 
- 	assert.True(t, checkEventConditionPattern(c, "/mydirname/f.txt"))
 
- 	assert.True(t, checkEventConditionPattern(c, "/mydir/sub"))
 
- 	assert.True(t, checkEventConditionPattern(c, "/mydir/sub/dir"))
 
- 	assert.False(t, checkEventConditionPattern(c, "/mydir/sub/dir/a.txt"))
 
- }
 
- func TestMutlipleDoubleStarMatching(t *testing.T) {
 
- 	patterns := []dataprovider.ConditionPattern{
 
- 		{
 
- 			Pattern:      "/**/*.txt",
 
- 			InverseMatch: false,
 
- 		},
 
- 		{
 
- 			Pattern:      "/**/*.tmp",
 
- 			InverseMatch: false,
 
- 		},
 
- 	}
 
- 	assert.False(t, checkEventConditionPatterns("/mydir", patterns))
 
- 	assert.True(t, checkEventConditionPatterns("/mydir/test.tmp", patterns))
 
- 	assert.True(t, checkEventConditionPatterns("/mydir/test.txt", patterns))
 
- 	assert.False(t, checkEventConditionPatterns("/mydir/test.csv", patterns))
 
- 	assert.False(t, checkEventConditionPatterns("/mydir/sub", patterns))
 
- 	assert.True(t, checkEventConditionPatterns("/mydir/sub/test.tmp", patterns))
 
- 	assert.True(t, checkEventConditionPatterns("/mydir/sub/test.txt", patterns))
 
- 	assert.False(t, checkEventConditionPatterns("/mydir/sub/test.csv", patterns))
 
- }
 
- func TestMultipleDoubleStarMatchingInverse(t *testing.T) {
 
- 	patterns := []dataprovider.ConditionPattern{
 
- 		{
 
- 			Pattern:      "/**/*.txt",
 
- 			InverseMatch: true,
 
- 		},
 
- 		{
 
- 			Pattern:      "/**/*.tmp",
 
- 			InverseMatch: true,
 
- 		},
 
- 	}
 
- 	assert.True(t, checkEventConditionPatterns("/mydir", patterns))
 
- 	assert.False(t, checkEventConditionPatterns("/mydir/test.tmp", patterns))
 
- 	assert.False(t, checkEventConditionPatterns("/mydir/test.txt", patterns))
 
- 	assert.True(t, checkEventConditionPatterns("/mydir/test.csv", patterns))
 
- 	assert.True(t, checkEventConditionPatterns("/mydir/sub", patterns))
 
- 	assert.False(t, checkEventConditionPatterns("/mydir/sub/test.tmp", patterns))
 
- 	assert.False(t, checkEventConditionPatterns("/mydir/sub/test.txt", patterns))
 
- 	assert.True(t, checkEventConditionPatterns("/mydir/sub/test.csv", patterns))
 
- }
 
- func TestGroupConditionPatterns(t *testing.T) {
 
- 	group1 := "group1"
 
- 	group2 := "group2"
 
- 	patterns := []dataprovider.ConditionPattern{
 
- 		{
 
- 			Pattern: group1,
 
- 		},
 
- 		{
 
- 			Pattern: group2,
 
- 		},
 
- 	}
 
- 	inversePatterns := []dataprovider.ConditionPattern{
 
- 		{
 
- 			Pattern:      group1,
 
- 			InverseMatch: true,
 
- 		},
 
- 		{
 
- 			Pattern:      group2,
 
- 			InverseMatch: true,
 
- 		},
 
- 	}
 
- 	groups := []sdk.GroupMapping{
 
- 		{
 
- 			Name: "group3",
 
- 			Type: sdk.GroupTypePrimary,
 
- 		},
 
- 	}
 
- 	assert.False(t, checkEventGroupConditionPatterns(groups, patterns))
 
- 	assert.True(t, checkEventGroupConditionPatterns(groups, inversePatterns))
 
- 	groups = []sdk.GroupMapping{
 
- 		{
 
- 			Name: group1,
 
- 			Type: sdk.GroupTypePrimary,
 
- 		},
 
- 		{
 
- 			Name: "group4",
 
- 			Type: sdk.GroupTypePrimary,
 
- 		},
 
- 	}
 
- 	assert.True(t, checkEventGroupConditionPatterns(groups, patterns))
 
- 	assert.False(t, checkEventGroupConditionPatterns(groups, inversePatterns))
 
- 	groups = []sdk.GroupMapping{
 
- 		{
 
- 			Name: group1,
 
- 			Type: sdk.GroupTypePrimary,
 
- 		},
 
- 	}
 
- 	assert.True(t, checkEventGroupConditionPatterns(groups, patterns))
 
- 	assert.False(t, checkEventGroupConditionPatterns(groups, inversePatterns))
 
- 	groups = []sdk.GroupMapping{
 
- 		{
 
- 			Name: "group11",
 
- 			Type: sdk.GroupTypePrimary,
 
- 		},
 
- 	}
 
- 	assert.False(t, checkEventGroupConditionPatterns(groups, patterns))
 
- 	assert.True(t, checkEventGroupConditionPatterns(groups, inversePatterns))
 
- }
 
- func TestEventManager(t *testing.T) {
 
- 	startEventScheduler()
 
- 	action := &dataprovider.BaseEventAction{
 
- 		Name: "test_action",
 
- 		Type: dataprovider.ActionTypeHTTP,
 
- 		Options: dataprovider.BaseEventActionOptions{
 
- 			HTTPConfig: dataprovider.EventActionHTTPConfig{
 
- 				Endpoint: "http://localhost",
 
- 				Timeout:  20,
 
- 				Method:   http.MethodGet,
 
- 			},
 
- 		},
 
- 	}
 
- 	err := dataprovider.AddEventAction(action, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	rule := &dataprovider.EventRule{
 
- 		Name:    "rule",
 
- 		Status:  1,
 
- 		Trigger: dataprovider.EventTriggerFsEvent,
 
- 		Conditions: dataprovider.EventConditions{
 
- 			FsEvents: []string{operationUpload},
 
- 		},
 
- 		Actions: []dataprovider.EventAction{
 
- 			{
 
- 				BaseEventAction: dataprovider.BaseEventAction{
 
- 					Name: action.Name,
 
- 				},
 
- 				Order: 1,
 
- 			},
 
- 		},
 
- 	}
 
- 	err = dataprovider.AddEventRule(rule, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	eventManager.RLock()
 
- 	assert.Len(t, eventManager.FsEvents, 1)
 
- 	assert.Len(t, eventManager.ProviderEvents, 0)
 
- 	assert.Len(t, eventManager.Schedules, 0)
 
- 	assert.Len(t, eventManager.schedulesMapping, 0)
 
- 	eventManager.RUnlock()
 
- 	rule.Trigger = dataprovider.EventTriggerProviderEvent
 
- 	rule.Conditions = dataprovider.EventConditions{
 
- 		ProviderEvents: []string{"add"},
 
- 	}
 
- 	err = dataprovider.UpdateEventRule(rule, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	eventManager.RLock()
 
- 	assert.Len(t, eventManager.FsEvents, 0)
 
- 	assert.Len(t, eventManager.ProviderEvents, 1)
 
- 	assert.Len(t, eventManager.Schedules, 0)
 
- 	assert.Len(t, eventManager.schedulesMapping, 0)
 
- 	eventManager.RUnlock()
 
- 	rule.Trigger = dataprovider.EventTriggerSchedule
 
- 	rule.Conditions = dataprovider.EventConditions{
 
- 		Schedules: []dataprovider.Schedule{
 
- 			{
 
- 				Hours:      "0",
 
- 				DayOfWeek:  "*",
 
- 				DayOfMonth: "*",
 
- 				Month:      "*",
 
- 			},
 
- 		},
 
- 	}
 
- 	rule.DeletedAt = util.GetTimeAsMsSinceEpoch(time.Now().Add(-12 * time.Hour))
 
- 	eventManager.addUpdateRuleInternal(*rule)
 
- 	eventManager.RLock()
 
- 	assert.Len(t, eventManager.FsEvents, 0)
 
- 	assert.Len(t, eventManager.ProviderEvents, 0)
 
- 	assert.Len(t, eventManager.Schedules, 0)
 
- 	assert.Len(t, eventManager.schedulesMapping, 0)
 
- 	eventManager.RUnlock()
 
- 	assert.Eventually(t, func() bool {
 
- 		_, err = dataprovider.EventRuleExists(rule.Name)
 
- 		ok := errors.Is(err, util.ErrNotFound)
 
- 		return ok
 
- 	}, 2*time.Second, 100*time.Millisecond)
 
- 	rule.DeletedAt = 0
 
- 	err = dataprovider.AddEventRule(rule, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	eventManager.RLock()
 
- 	assert.Len(t, eventManager.FsEvents, 0)
 
- 	assert.Len(t, eventManager.ProviderEvents, 0)
 
- 	assert.Len(t, eventManager.Schedules, 1)
 
- 	assert.Len(t, eventManager.schedulesMapping, 1)
 
- 	eventManager.RUnlock()
 
- 	err = dataprovider.DeleteEventRule(rule.Name, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	eventManager.RLock()
 
- 	assert.Len(t, eventManager.FsEvents, 0)
 
- 	assert.Len(t, eventManager.ProviderEvents, 0)
 
- 	assert.Len(t, eventManager.Schedules, 0)
 
- 	assert.Len(t, eventManager.schedulesMapping, 0)
 
- 	eventManager.RUnlock()
 
- 	err = dataprovider.DeleteEventAction(action.Name, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	stopEventScheduler()
 
- }
 
- func TestEventManagerErrors(t *testing.T) {
 
- 	startEventScheduler()
 
- 	providerConf := dataprovider.GetProviderConfig()
 
- 	err := dataprovider.Close()
 
- 	assert.NoError(t, err)
 
- 	params := EventParams{
 
- 		sender: "sender",
 
- 	}
 
- 	_, err = params.getUsers()
 
- 	assert.Error(t, err)
 
- 	_, err = params.getFolders()
 
- 	assert.Error(t, err)
 
- 	err = executeUsersQuotaResetRuleAction(dataprovider.ConditionOptions{}, &EventParams{})
 
- 	assert.Error(t, err)
 
- 	err = executeFoldersQuotaResetRuleAction(dataprovider.ConditionOptions{}, &EventParams{})
 
- 	assert.Error(t, err)
 
- 	err = executeTransferQuotaResetRuleAction(dataprovider.ConditionOptions{}, &EventParams{})
 
- 	assert.Error(t, err)
 
- 	err = executeMetadataCheckRuleAction(dataprovider.ConditionOptions{}, &EventParams{})
 
- 	assert.Error(t, err)
 
- 	err = executeUserExpirationCheckRuleAction(dataprovider.ConditionOptions{}, &EventParams{})
 
- 	assert.Error(t, err)
 
- 	err = executeDeleteFsRuleAction(nil, nil, dataprovider.ConditionOptions{}, &EventParams{})
 
- 	assert.Error(t, err)
 
- 	err = executeMkdirFsRuleAction(nil, nil, dataprovider.ConditionOptions{}, &EventParams{})
 
- 	assert.Error(t, err)
 
- 	err = executeRenameFsRuleAction(nil, nil, dataprovider.ConditionOptions{}, &EventParams{})
 
- 	assert.Error(t, err)
 
- 	err = executeExistFsRuleAction(nil, nil, dataprovider.ConditionOptions{}, &EventParams{})
 
- 	assert.Error(t, err)
 
- 	err = executeCopyFsRuleAction(nil, nil, dataprovider.ConditionOptions{}, &EventParams{})
 
- 	assert.Error(t, err)
 
- 	err = executeCompressFsRuleAction(dataprovider.EventActionFsCompress{}, nil, dataprovider.ConditionOptions{}, &EventParams{})
 
- 	assert.Error(t, err)
 
- 	err = executePwdExpirationCheckRuleAction(dataprovider.EventActionPasswordExpiration{},
 
- 		dataprovider.ConditionOptions{}, &EventParams{})
 
- 	assert.Error(t, err)
 
- 	_, err = executeAdminCheckAction(&dataprovider.EventActionIDPAccountCheck{}, &EventParams{})
 
- 	assert.Error(t, err)
 
- 	_, err = executeUserCheckAction(&dataprovider.EventActionIDPAccountCheck{}, &EventParams{})
 
- 	assert.Error(t, err)
 
- 	groupName := "agroup"
 
- 	err = executeQuotaResetForUser(&dataprovider.User{
 
- 		Groups: []sdk.GroupMapping{
 
- 			{
 
- 				Name: groupName,
 
- 				Type: sdk.GroupTypePrimary,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	err = executeMetadataCheckForUser(&dataprovider.User{
 
- 		Groups: []sdk.GroupMapping{
 
- 			{
 
- 				Name: groupName,
 
- 				Type: sdk.GroupTypePrimary,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	err = executeDataRetentionCheckForUser(dataprovider.User{
 
- 		Groups: []sdk.GroupMapping{
 
- 			{
 
- 				Name: groupName,
 
- 				Type: sdk.GroupTypePrimary,
 
- 			},
 
- 		},
 
- 	}, nil, &EventParams{}, "")
 
- 	assert.Error(t, err)
 
- 	err = executeDeleteFsActionForUser(nil, nil, dataprovider.User{
 
- 		Groups: []sdk.GroupMapping{
 
- 			{
 
- 				Name: groupName,
 
- 				Type: sdk.GroupTypePrimary,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	err = executeMkDirsFsActionForUser(nil, nil, dataprovider.User{
 
- 		Groups: []sdk.GroupMapping{
 
- 			{
 
- 				Name: groupName,
 
- 				Type: sdk.GroupTypePrimary,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	err = executeRenameFsActionForUser(nil, nil, dataprovider.User{
 
- 		Groups: []sdk.GroupMapping{
 
- 			{
 
- 				Name: groupName,
 
- 				Type: sdk.GroupTypePrimary,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	err = executeExistFsActionForUser(nil, nil, dataprovider.User{
 
- 		Groups: []sdk.GroupMapping{
 
- 			{
 
- 				Name: groupName,
 
- 				Type: sdk.GroupTypePrimary,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	err = executeCopyFsActionForUser(nil, nil, dataprovider.User{
 
- 		Groups: []sdk.GroupMapping{
 
- 			{
 
- 				Name: groupName,
 
- 				Type: sdk.GroupTypePrimary,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	err = executeCompressFsActionForUser(dataprovider.EventActionFsCompress{}, nil, dataprovider.User{
 
- 		Groups: []sdk.GroupMapping{
 
- 			{
 
- 				Name: groupName,
 
- 				Type: sdk.GroupTypePrimary,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	err = executePwdExpirationCheckForUser(&dataprovider.User{
 
- 		Groups: []sdk.GroupMapping{
 
- 			{
 
- 				Name: groupName,
 
- 				Type: sdk.GroupTypePrimary,
 
- 			},
 
- 		}}, dataprovider.EventActionPasswordExpiration{})
 
- 	assert.Error(t, err)
 
- 	_, _, err = getHTTPRuleActionBody(&dataprovider.EventActionHTTPConfig{
 
- 		Method: http.MethodPost,
 
- 		Parts: []dataprovider.HTTPPart{
 
- 			{
 
- 				Name: "p1",
 
- 			},
 
- 		},
 
- 	}, nil, nil, dataprovider.User{
 
- 		BaseUser: sdk.BaseUser{
 
- 			Username: "u",
 
- 		},
 
- 		Groups: []sdk.GroupMapping{
 
- 			{
 
- 				Name: groupName,
 
- 				Type: sdk.GroupTypePrimary,
 
- 			},
 
- 		},
 
- 	}, &EventParams{}, false)
 
- 	assert.Error(t, err)
 
- 	dataRetentionAction := dataprovider.BaseEventAction{
 
- 		Type: dataprovider.ActionTypeDataRetentionCheck,
 
- 		Options: dataprovider.BaseEventActionOptions{
 
- 			RetentionConfig: dataprovider.EventActionDataRetentionConfig{
 
- 				Folders: []dataprovider.FolderRetention{
 
- 					{
 
- 						Path:      "/",
 
- 						Retention: 24,
 
- 					},
 
- 				},
 
- 			},
 
- 		},
 
- 	}
 
- 	err = executeRuleAction(dataRetentionAction, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: "username1",
 
- 			},
 
- 		},
 
- 	})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "unable to get users")
 
- 	}
 
- 	eventManager.loadRules()
 
- 	eventManager.RLock()
 
- 	assert.Len(t, eventManager.FsEvents, 0)
 
- 	assert.Len(t, eventManager.ProviderEvents, 0)
 
- 	assert.Len(t, eventManager.Schedules, 0)
 
- 	eventManager.RUnlock()
 
- 	// rule with invalid trigger
 
- 	eventManager.addUpdateRuleInternal(dataprovider.EventRule{
 
- 		Name:    "test rule",
 
- 		Status:  1,
 
- 		Trigger: -1,
 
- 	})
 
- 	eventManager.RLock()
 
- 	assert.Len(t, eventManager.FsEvents, 0)
 
- 	assert.Len(t, eventManager.ProviderEvents, 0)
 
- 	assert.Len(t, eventManager.Schedules, 0)
 
- 	eventManager.RUnlock()
 
- 	// rule with invalid cronspec
 
- 	eventManager.addUpdateRuleInternal(dataprovider.EventRule{
 
- 		Name:    "test rule",
 
- 		Status:  1,
 
- 		Trigger: dataprovider.EventTriggerSchedule,
 
- 		Conditions: dataprovider.EventConditions{
 
- 			Schedules: []dataprovider.Schedule{
 
- 				{
 
- 					Hours: "1000",
 
- 				},
 
- 			},
 
- 		},
 
- 	})
 
- 	eventManager.RLock()
 
- 	assert.Len(t, eventManager.FsEvents, 0)
 
- 	assert.Len(t, eventManager.ProviderEvents, 0)
 
- 	assert.Len(t, eventManager.Schedules, 0)
 
- 	eventManager.RUnlock()
 
- 	err = dataprovider.Initialize(providerConf, configDir, true)
 
- 	assert.NoError(t, err)
 
- 	stopEventScheduler()
 
- }
 
- func TestEventRuleActions(t *testing.T) {
 
- 	actionName := "test rule action"
 
- 	action := dataprovider.BaseEventAction{
 
- 		Name: actionName,
 
- 		Type: dataprovider.ActionTypeBackup,
 
- 	}
 
- 	err := executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{})
 
- 	assert.NoError(t, err)
 
- 	action.Type = -1
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{})
 
- 	assert.Error(t, err)
 
- 	action = dataprovider.BaseEventAction{
 
- 		Name: actionName,
 
- 		Type: dataprovider.ActionTypeHTTP,
 
- 		Options: dataprovider.BaseEventActionOptions{
 
- 			HTTPConfig: dataprovider.EventActionHTTPConfig{
 
- 				Endpoint:      "http://foo\x7f.com/", // invalid URL
 
- 				SkipTLSVerify: true,
 
- 				Body:          `"data": "{{ObjectDataString}}"`,
 
- 				Method:        http.MethodPost,
 
- 				QueryParameters: []dataprovider.KeyValue{
 
- 					{
 
- 						Key:   "param",
 
- 						Value: "value",
 
- 					},
 
- 				},
 
- 				Timeout: 5,
 
- 				Headers: []dataprovider.KeyValue{
 
- 					{
 
- 						Key:   "Content-Type",
 
- 						Value: "application/json",
 
- 					},
 
- 				},
 
- 				Username: "httpuser",
 
- 			},
 
- 		},
 
- 	}
 
- 	action.Options.SetEmptySecretsIfNil()
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "invalid endpoint")
 
- 	}
 
- 	action.Options.HTTPConfig.Endpoint = fmt.Sprintf("http://%v", httpAddr)
 
- 	params := &EventParams{
 
- 		Name: "a",
 
- 		Object: &dataprovider.User{
 
- 			BaseUser: sdk.BaseUser{
 
- 				Username: "test user",
 
- 			},
 
- 		},
 
- 	}
 
- 	err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
 
- 	assert.NoError(t, err)
 
- 	action.Options.HTTPConfig.Method = http.MethodGet
 
- 	err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
 
- 	assert.NoError(t, err)
 
- 	action.Options.HTTPConfig.Endpoint = fmt.Sprintf("http://%v/404", httpAddr)
 
- 	err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "unexpected status code: 404")
 
- 	}
 
- 	action.Options.HTTPConfig.Endpoint = "http://invalid:1234"
 
- 	err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
 
- 	assert.Error(t, err)
 
- 	action.Options.HTTPConfig.QueryParameters = nil
 
- 	action.Options.HTTPConfig.Endpoint = "http://bar\x7f.com/"
 
- 	err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
 
- 	assert.Error(t, err)
 
- 	action.Options.HTTPConfig.Password = kms.NewSecret(sdkkms.SecretStatusSecretBox, "payload", "key", "data")
 
- 	err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "unable to decrypt HTTP password")
 
- 	}
 
- 	action.Options.HTTPConfig.Endpoint = fmt.Sprintf("http://%v", httpAddr)
 
- 	action.Options.HTTPConfig.Password = kms.NewEmptySecret()
 
- 	action.Options.HTTPConfig.Body = ""
 
- 	action.Options.HTTPConfig.Parts = []dataprovider.HTTPPart{
 
- 		{
 
- 			Name:     "p1",
 
- 			Filepath: "path",
 
- 		},
 
- 	}
 
- 	err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
 
- 	assert.Contains(t, getErrorString(err), "error getting user")
 
- 	action.Options.HTTPConfig.Parts = nil
 
- 	action.Options.HTTPConfig.Body = "{{ObjectData}}"
 
- 	// test disk and transfer quota reset
 
- 	username1 := "user1"
 
- 	username2 := "user2"
 
- 	user1 := dataprovider.User{
 
- 		BaseUser: sdk.BaseUser{
 
- 			Username: username1,
 
- 			HomeDir:  filepath.Join(os.TempDir(), username1),
 
- 			Status:   1,
 
- 			Permissions: map[string][]string{
 
- 				"/": {dataprovider.PermAny},
 
- 			},
 
- 		},
 
- 	}
 
- 	user2 := dataprovider.User{
 
- 		BaseUser: sdk.BaseUser{
 
- 			Username: username2,
 
- 			HomeDir:  filepath.Join(os.TempDir(), username2),
 
- 			Status:   1,
 
- 			Permissions: map[string][]string{
 
- 				"/": {dataprovider.PermAny},
 
- 			},
 
- 		},
 
- 	}
 
- 	user2.Filters.PasswordExpiration = 10
 
- 	err = dataprovider.AddUser(&user1, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = dataprovider.AddUser(&user2, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = executePwdExpirationCheckRuleAction(dataprovider.EventActionPasswordExpiration{
 
- 		Threshold: 20,
 
- 	}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: user2.Username,
 
- 			},
 
- 		},
 
- 	}, &EventParams{})
 
- 	// smtp not configured
 
- 	assert.Error(t, err)
 
- 	action = dataprovider.BaseEventAction{
 
- 		Type: dataprovider.ActionTypeUserQuotaReset,
 
- 	}
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: username1,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err) // no home dir
 
- 	// create the home dir
 
- 	err = os.MkdirAll(user1.GetHomeDir(), os.ModePerm)
 
- 	assert.NoError(t, err)
 
- 	err = os.WriteFile(filepath.Join(user1.GetHomeDir(), "file.txt"), []byte("user"), 0666)
 
- 	assert.NoError(t, err)
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: username1,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.NoError(t, err)
 
- 	userGet, err := dataprovider.UserExists(username1, "")
 
- 	assert.NoError(t, err)
 
- 	assert.Equal(t, 1, userGet.UsedQuotaFiles)
 
- 	assert.Equal(t, int64(4), userGet.UsedQuotaSize)
 
- 	// simulate another quota scan in progress
 
- 	assert.True(t, QuotaScans.AddUserQuotaScan(username1, ""))
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: username1,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	assert.True(t, QuotaScans.RemoveUserQuotaScan(username1))
 
- 	// non matching pattern
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: "don't match",
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	assert.Contains(t, getErrorString(err), "no user quota reset executed")
 
- 	action = dataprovider.BaseEventAction{
 
- 		Type: dataprovider.ActionTypeMetadataCheck,
 
- 	}
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: "don't match",
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	assert.Contains(t, getErrorString(err), "no metadata check executed")
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: username1,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.NoError(t, err)
 
- 	// simulate another metadata check in progress
 
- 	assert.True(t, ActiveMetadataChecks.Add(username1, ""))
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: username1,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	assert.True(t, ActiveMetadataChecks.Remove(username1))
 
- 	action = dataprovider.BaseEventAction{
 
- 		Type: dataprovider.ActionTypeUserExpirationCheck,
 
- 	}
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: "don't match",
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	assert.Contains(t, getErrorString(err), "no user expiration check executed")
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: username1,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.NoError(t, err)
 
- 	dataRetentionAction := dataprovider.BaseEventAction{
 
- 		Type: dataprovider.ActionTypeDataRetentionCheck,
 
- 		Options: dataprovider.BaseEventActionOptions{
 
- 			RetentionConfig: dataprovider.EventActionDataRetentionConfig{
 
- 				Folders: []dataprovider.FolderRetention{
 
- 					{
 
- 						Path:      "",
 
- 						Retention: 24,
 
- 					},
 
- 				},
 
- 			},
 
- 		},
 
- 	}
 
- 	err = executeRuleAction(dataRetentionAction, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: username1,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err) // invalid config, no folder path specified
 
- 	retentionDir := "testretention"
 
- 	dataRetentionAction = dataprovider.BaseEventAction{
 
- 		Type: dataprovider.ActionTypeDataRetentionCheck,
 
- 		Options: dataprovider.BaseEventActionOptions{
 
- 			RetentionConfig: dataprovider.EventActionDataRetentionConfig{
 
- 				Folders: []dataprovider.FolderRetention{
 
- 					{
 
- 						Path:            path.Join("/", retentionDir),
 
- 						Retention:       24,
 
- 						DeleteEmptyDirs: true,
 
- 					},
 
- 				},
 
- 			},
 
- 		},
 
- 	}
 
- 	// create some test files
 
- 	file1 := filepath.Join(user1.GetHomeDir(), "file1.txt")
 
- 	file2 := filepath.Join(user1.GetHomeDir(), retentionDir, "file2.txt")
 
- 	file3 := filepath.Join(user1.GetHomeDir(), retentionDir, "file3.txt")
 
- 	file4 := filepath.Join(user1.GetHomeDir(), retentionDir, "sub", "file4.txt")
 
- 	err = os.MkdirAll(filepath.Dir(file4), os.ModePerm)
 
- 	assert.NoError(t, err)
 
- 	for _, f := range []string{file1, file2, file3, file4} {
 
- 		err = os.WriteFile(f, []byte(""), 0666)
 
- 		assert.NoError(t, err)
 
- 	}
 
- 	timeBeforeRetention := time.Now().Add(-48 * time.Hour)
 
- 	err = os.Chtimes(file1, timeBeforeRetention, timeBeforeRetention)
 
- 	assert.NoError(t, err)
 
- 	err = os.Chtimes(file2, timeBeforeRetention, timeBeforeRetention)
 
- 	assert.NoError(t, err)
 
- 	err = os.Chtimes(file4, timeBeforeRetention, timeBeforeRetention)
 
- 	assert.NoError(t, err)
 
- 	err = executeRuleAction(dataRetentionAction, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: username1,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.NoError(t, err)
 
- 	assert.FileExists(t, file1)
 
- 	assert.NoFileExists(t, file2)
 
- 	assert.FileExists(t, file3)
 
- 	assert.NoDirExists(t, filepath.Dir(file4))
 
- 	// simulate another check in progress
 
- 	c := RetentionChecks.Add(RetentionCheck{}, &user1)
 
- 	assert.NotNil(t, c)
 
- 	err = executeRuleAction(dataRetentionAction, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: username1,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	RetentionChecks.remove(user1.Username)
 
- 	err = executeRuleAction(dataRetentionAction, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: "no match",
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	assert.Contains(t, getErrorString(err), "no retention check executed")
 
- 	// test file exists action
 
- 	action = dataprovider.BaseEventAction{
 
- 		Type: dataprovider.ActionTypeFilesystem,
 
- 		Options: dataprovider.BaseEventActionOptions{
 
- 			FsConfig: dataprovider.EventActionFilesystemConfig{
 
- 				Type:  dataprovider.FilesystemActionExist,
 
- 				Exist: []string{"/file1.txt", path.Join("/", retentionDir, "file3.txt")},
 
- 			},
 
- 		},
 
- 	}
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: "no match",
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	assert.Contains(t, getErrorString(err), "no existence check executed")
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: username1,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.NoError(t, err)
 
- 	action.Options.FsConfig.Exist = []string{"/file1.txt", path.Join("/", retentionDir, "file2.txt")}
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: username1,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	err = os.RemoveAll(user1.GetHomeDir())
 
- 	assert.NoError(t, err)
 
- 	err = dataprovider.UpdateUserTransferQuota(&user1, 100, 100, true)
 
- 	assert.NoError(t, err)
 
- 	action.Type = dataprovider.ActionTypeTransferQuotaReset
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: username1,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.NoError(t, err)
 
- 	userGet, err = dataprovider.UserExists(username1, "")
 
- 	assert.NoError(t, err)
 
- 	assert.Equal(t, int64(0), userGet.UsedDownloadDataTransfer)
 
- 	assert.Equal(t, int64(0), userGet.UsedUploadDataTransfer)
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: "no match",
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	assert.Contains(t, getErrorString(err), "no transfer quota reset executed")
 
- 	action.Type = dataprovider.ActionTypeFilesystem
 
- 	action.Options = dataprovider.BaseEventActionOptions{
 
- 		FsConfig: dataprovider.EventActionFilesystemConfig{
 
- 			Type: dataprovider.FilesystemActionRename,
 
- 			Renames: []dataprovider.KeyValue{
 
- 				{
 
- 					Key:   "/source",
 
- 					Value: "/target",
 
- 				},
 
- 			},
 
- 		},
 
- 	}
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: "no match",
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	assert.Contains(t, getErrorString(err), "no rename executed")
 
- 	action.Options = dataprovider.BaseEventActionOptions{
 
- 		FsConfig: dataprovider.EventActionFilesystemConfig{
 
- 			Type:    dataprovider.FilesystemActionDelete,
 
- 			Deletes: []string{"/dir1"},
 
- 		},
 
- 	}
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: "no match",
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	assert.Contains(t, getErrorString(err), "no delete executed")
 
- 	action.Options = dataprovider.BaseEventActionOptions{
 
- 		FsConfig: dataprovider.EventActionFilesystemConfig{
 
- 			Type:    dataprovider.FilesystemActionMkdirs,
 
- 			Deletes: []string{"/dir1"},
 
- 		},
 
- 	}
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: "no match",
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	assert.Contains(t, getErrorString(err), "no mkdir executed")
 
- 	action.Options = dataprovider.BaseEventActionOptions{
 
- 		FsConfig: dataprovider.EventActionFilesystemConfig{
 
- 			Type: dataprovider.FilesystemActionCompress,
 
- 			Compress: dataprovider.EventActionFsCompress{
 
- 				Name:  "test.zip",
 
- 				Paths: []string{"/{{VirtualPath}}"},
 
- 			},
 
- 		},
 
- 	}
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: "no match",
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	assert.Contains(t, getErrorString(err), "no file/folder compressed")
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		GroupNames: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: "no match",
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	assert.Contains(t, getErrorString(err), "no file/folder compressed")
 
- 	err = dataprovider.DeleteUser(username1, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = dataprovider.DeleteUser(username2, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	// test folder quota reset
 
- 	foldername1 := "f1"
 
- 	foldername2 := "f2"
 
- 	folder1 := vfs.BaseVirtualFolder{
 
- 		Name:       foldername1,
 
- 		MappedPath: filepath.Join(os.TempDir(), foldername1),
 
- 	}
 
- 	folder2 := vfs.BaseVirtualFolder{
 
- 		Name:       foldername2,
 
- 		MappedPath: filepath.Join(os.TempDir(), foldername2),
 
- 	}
 
- 	err = dataprovider.AddFolder(&folder1, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = dataprovider.AddFolder(&folder2, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	action = dataprovider.BaseEventAction{
 
- 		Type: dataprovider.ActionTypeFolderQuotaReset,
 
- 	}
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: foldername1,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err) // no home dir
 
- 	err = os.MkdirAll(folder1.MappedPath, os.ModePerm)
 
- 	assert.NoError(t, err)
 
- 	err = os.WriteFile(filepath.Join(folder1.MappedPath, "file.txt"), []byte("folder"), 0666)
 
- 	assert.NoError(t, err)
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: foldername1,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.NoError(t, err)
 
- 	folderGet, err := dataprovider.GetFolderByName(foldername1)
 
- 	assert.NoError(t, err)
 
- 	assert.Equal(t, 1, folderGet.UsedQuotaFiles)
 
- 	assert.Equal(t, int64(6), folderGet.UsedQuotaSize)
 
- 	// simulate another quota scan in progress
 
- 	assert.True(t, QuotaScans.AddVFolderQuotaScan(foldername1))
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: foldername1,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	assert.True(t, QuotaScans.RemoveVFolderQuotaScan(foldername1))
 
- 	err = executeRuleAction(action, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: "no folder match",
 
- 			},
 
- 		},
 
- 	})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "no folder quota reset executed")
 
- 	}
 
- 	body, _, err := getHTTPRuleActionBody(&dataprovider.EventActionHTTPConfig{
 
- 		Method: http.MethodPost,
 
- 	}, nil, nil, dataprovider.User{}, &EventParams{}, true)
 
- 	assert.NoError(t, err)
 
- 	assert.Nil(t, body)
 
- 	body, _, err = getHTTPRuleActionBody(&dataprovider.EventActionHTTPConfig{
 
- 		Method: http.MethodPost,
 
- 		Body:   "test body",
 
- 	}, nil, nil, dataprovider.User{}, &EventParams{}, false)
 
- 	assert.NoError(t, err)
 
- 	assert.NotNil(t, body)
 
- 	err = os.RemoveAll(folder1.MappedPath)
 
- 	assert.NoError(t, err)
 
- 	err = dataprovider.DeleteFolder(foldername1, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = dataprovider.DeleteFolder(foldername2, "", "", "")
 
- 	assert.NoError(t, err)
 
- }
 
- func TestIDPAccountCheckRule(t *testing.T) {
 
- 	_, _, err := executeIDPAccountCheckRule(dataprovider.EventRule{}, EventParams{})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "no action executed")
 
- 	}
 
- 	_, _, err = executeIDPAccountCheckRule(dataprovider.EventRule{
 
- 		Actions: []dataprovider.EventAction{
 
- 			{
 
- 				BaseEventAction: dataprovider.BaseEventAction{
 
- 					Name: "n",
 
- 					Type: dataprovider.ActionTypeIDPAccountCheck,
 
- 				},
 
- 			},
 
- 		},
 
- 	}, EventParams{Event: "invalid"})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "unsupported IDP login event")
 
- 	}
 
- 	// invalid json
 
- 	_, err = executeAdminCheckAction(&dataprovider.EventActionIDPAccountCheck{TemplateAdmin: "{"}, &EventParams{Name: "missing admin"})
 
- 	assert.Error(t, err)
 
- 	_, err = executeUserCheckAction(&dataprovider.EventActionIDPAccountCheck{TemplateUser: "["}, &EventParams{Name: "missing user"})
 
- 	assert.Error(t, err)
 
- 	_, err = executeUserCheckAction(&dataprovider.EventActionIDPAccountCheck{TemplateUser: "{}"}, &EventParams{Name: "invalid user template"})
 
- 	assert.ErrorIs(t, err, util.ErrValidation)
 
- 	username := "u"
 
- 	c := &dataprovider.EventActionIDPAccountCheck{
 
- 		Mode:         1,
 
- 		TemplateUser: `{"username":"` + username + `","status":1,"home_dir":"` + util.JSONEscape(filepath.Join(os.TempDir())) + `","permissions":{"/":["*"]}}`,
 
- 	}
 
- 	params := &EventParams{
 
- 		Name:  username,
 
- 		Event: IDPLoginUser,
 
- 	}
 
- 	user, err := executeUserCheckAction(c, params)
 
- 	assert.NoError(t, err)
 
- 	assert.Equal(t, username, user.Username)
 
- 	assert.Equal(t, 1, user.Status)
 
- 	user.Status = 0
 
- 	err = dataprovider.UpdateUser(user, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	// the user is not changed
 
- 	user, err = executeUserCheckAction(c, params)
 
- 	assert.NoError(t, err)
 
- 	assert.Equal(t, username, user.Username)
 
- 	assert.Equal(t, 0, user.Status)
 
- 	// change the mode, the user is now updated
 
- 	c.Mode = 0
 
- 	user, err = executeUserCheckAction(c, params)
 
- 	assert.NoError(t, err)
 
- 	assert.Equal(t, username, user.Username)
 
- 	assert.Equal(t, 1, user.Status)
 
- 	err = dataprovider.DeleteUser(username, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	// check rule consistency
 
- 	r := dataprovider.EventRule{
 
- 		Actions: []dataprovider.EventAction{
 
- 			{
 
- 				BaseEventAction: dataprovider.BaseEventAction{
 
- 					Type: dataprovider.ActionTypeIDPAccountCheck,
 
- 				},
 
- 				Order: 1,
 
- 			},
 
- 		},
 
- 	}
 
- 	err = r.CheckActionsConsistency("")
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "IDP account check action is only supported for IDP login trigger")
 
- 	}
 
- 	r.Trigger = dataprovider.EventTriggerIDPLogin
 
- 	err = r.CheckActionsConsistency("")
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "IDP account check must be a sync action")
 
- 	}
 
- 	r.Actions[0].Options.ExecuteSync = true
 
- 	err = r.CheckActionsConsistency("")
 
- 	assert.NoError(t, err)
 
- 	r.Actions = append(r.Actions, dataprovider.EventAction{
 
- 		BaseEventAction: dataprovider.BaseEventAction{
 
- 			Type: dataprovider.ActionTypeCommand,
 
- 		},
 
- 		Options: dataprovider.EventActionOptions{
 
- 			ExecuteSync: true,
 
- 		},
 
- 		Order: 2,
 
- 	})
 
- 	err = r.CheckActionsConsistency("")
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "IDP account check must be the only sync action")
 
- 	}
 
- }
 
- func TestUserExpirationCheck(t *testing.T) {
 
- 	username := "test_user_expiration_check"
 
- 	user := dataprovider.User{
 
- 		BaseUser: sdk.BaseUser{
 
- 			Username: username,
 
- 			Permissions: map[string][]string{
 
- 				"/": {dataprovider.PermAny},
 
- 			},
 
- 			HomeDir:        filepath.Join(os.TempDir(), username),
 
- 			ExpirationDate: util.GetTimeAsMsSinceEpoch(time.Now().Add(-24 * time.Hour)),
 
- 		},
 
- 	}
 
- 	user.Filters.PasswordExpiration = 5
 
- 	err := dataprovider.AddUser(&user, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	conditions := dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: username,
 
- 			},
 
- 		},
 
- 	}
 
- 	err = executeUserExpirationCheckRuleAction(conditions, &EventParams{})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "expired users")
 
- 	}
 
- 	// the check will be skipped, the user is expired
 
- 	err = executePwdExpirationCheckRuleAction(dataprovider.EventActionPasswordExpiration{Threshold: 10}, conditions, &EventParams{})
 
- 	assert.NoError(t, err)
 
- 	err = dataprovider.DeleteUser(username, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = os.RemoveAll(user.GetHomeDir())
 
- 	assert.NoError(t, err)
 
- }
 
- func TestEventRuleActionsNoGroupMatching(t *testing.T) {
 
- 	username := "test_user_action_group_matching"
 
- 	user := dataprovider.User{
 
- 		BaseUser: sdk.BaseUser{
 
- 			Username: username,
 
- 			Permissions: map[string][]string{
 
- 				"/": {dataprovider.PermAny},
 
- 			},
 
- 			HomeDir: filepath.Join(os.TempDir(), username),
 
- 		},
 
- 	}
 
- 	err := dataprovider.AddUser(&user, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	conditions := dataprovider.ConditionOptions{
 
- 		GroupNames: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: "agroup",
 
- 			},
 
- 		},
 
- 	}
 
- 	err = executeDeleteFsRuleAction(nil, nil, conditions, &EventParams{})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "no delete executed")
 
- 	}
 
- 	err = executeMkdirFsRuleAction(nil, nil, conditions, &EventParams{})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "no mkdir executed")
 
- 	}
 
- 	err = executeRenameFsRuleAction(nil, nil, conditions, &EventParams{})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "no rename executed")
 
- 	}
 
- 	err = executeExistFsRuleAction(nil, nil, conditions, &EventParams{})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "no existence check executed")
 
- 	}
 
- 	err = executeCopyFsRuleAction(nil, nil, conditions, &EventParams{})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "no copy executed")
 
- 	}
 
- 	err = executeUsersQuotaResetRuleAction(conditions, &EventParams{})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "no user quota reset executed")
 
- 	}
 
- 	err = executeMetadataCheckRuleAction(conditions, &EventParams{})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "no metadata check executed")
 
- 	}
 
- 	err = executeTransferQuotaResetRuleAction(conditions, &EventParams{})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "no transfer quota reset executed")
 
- 	}
 
- 	err = executeDataRetentionCheckRuleAction(dataprovider.EventActionDataRetentionConfig{}, conditions, &EventParams{}, "")
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "no retention check executed")
 
- 	}
 
- 	err = dataprovider.DeleteUser(username, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = os.RemoveAll(user.GetHomeDir())
 
- 	assert.NoError(t, err)
 
- }
 
- func TestGetFileContent(t *testing.T) {
 
- 	username := "test_user_get_file_content"
 
- 	user := dataprovider.User{
 
- 		BaseUser: sdk.BaseUser{
 
- 			Username: username,
 
- 			Permissions: map[string][]string{
 
- 				"/": {dataprovider.PermAny},
 
- 			},
 
- 			HomeDir: filepath.Join(os.TempDir(), username),
 
- 		},
 
- 	}
 
- 	err := dataprovider.AddUser(&user, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = os.MkdirAll(user.GetHomeDir(), os.ModePerm)
 
- 	assert.NoError(t, err)
 
- 	fileContent := []byte("test file content")
 
- 	err = os.WriteFile(filepath.Join(user.GetHomeDir(), "file.txt"), fileContent, 0666)
 
- 	assert.NoError(t, err)
 
- 	conn := NewBaseConnection(xid.New().String(), protocolEventAction, "", "", user)
 
- 	replacer := strings.NewReplacer("old", "new")
 
- 	files, err := getMailAttachments(conn, []string{"/file.txt"}, replacer)
 
- 	assert.NoError(t, err)
 
- 	if assert.Len(t, files, 1) {
 
- 		var b bytes.Buffer
 
- 		_, err = files[0].Writer(&b)
 
- 		assert.NoError(t, err)
 
- 		assert.Equal(t, fileContent, b.Bytes())
 
- 	}
 
- 	// missing file
 
- 	_, err = getMailAttachments(conn, []string{"/file1.txt"}, replacer)
 
- 	assert.Error(t, err)
 
- 	// directory
 
- 	_, err = getMailAttachments(conn, []string{"/"}, replacer)
 
- 	assert.Error(t, err)
 
- 	// files too large
 
- 	content := make([]byte, maxAttachmentsSize/2+1)
 
- 	_, err = rand.Read(content)
 
- 	assert.NoError(t, err)
 
- 	err = os.WriteFile(filepath.Join(user.GetHomeDir(), "file1.txt"), content, 0666)
 
- 	assert.NoError(t, err)
 
- 	err = os.WriteFile(filepath.Join(user.GetHomeDir(), "file2.txt"), content, 0666)
 
- 	assert.NoError(t, err)
 
- 	files, err = getMailAttachments(conn, []string{"/file1.txt"}, replacer)
 
- 	assert.NoError(t, err)
 
- 	if assert.Len(t, files, 1) {
 
- 		var b bytes.Buffer
 
- 		_, err = files[0].Writer(&b)
 
- 		assert.NoError(t, err)
 
- 		assert.Equal(t, content, b.Bytes())
 
- 	}
 
- 	_, err = getMailAttachments(conn, []string{"/file1.txt", "/file2.txt"}, replacer)
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "size too large")
 
- 	}
 
- 	// change the filesystem provider
 
- 	user.FsConfig.Provider = sdk.CryptedFilesystemProvider
 
- 	user.FsConfig.CryptConfig.Passphrase = kms.NewPlainSecret("pwd")
 
- 	err = dataprovider.UpdateUser(&user, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	conn = NewBaseConnection(xid.New().String(), protocolEventAction, "", "", user)
 
- 	// the file is not encrypted so reading the encryption header will fail
 
- 	files, err = getMailAttachments(conn, []string{"/file.txt"}, replacer)
 
- 	assert.NoError(t, err)
 
- 	if assert.Len(t, files, 1) {
 
- 		var b bytes.Buffer
 
- 		_, err = files[0].Writer(&b)
 
- 		assert.Error(t, err)
 
- 	}
 
- 	err = dataprovider.DeleteUser(username, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = os.RemoveAll(user.GetHomeDir())
 
- 	assert.NoError(t, err)
 
- }
 
- func TestFilesystemActionErrors(t *testing.T) {
 
- 	err := executeFsRuleAction(dataprovider.EventActionFilesystemConfig{}, dataprovider.ConditionOptions{}, &EventParams{})
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "unsupported filesystem action")
 
- 	}
 
- 	username := "test_user_for_actions"
 
- 	testReplacer := strings.NewReplacer("old", "new")
 
- 	user := dataprovider.User{
 
- 		BaseUser: sdk.BaseUser{
 
- 			Username: username,
 
- 			Permissions: map[string][]string{
 
- 				"/": {dataprovider.PermAny},
 
- 			},
 
- 			HomeDir: filepath.Join(os.TempDir(), username),
 
- 		},
 
- 		FsConfig: vfs.Filesystem{
 
- 			Provider: sdk.SFTPFilesystemProvider,
 
- 			SFTPConfig: vfs.SFTPFsConfig{
 
- 				BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
 
- 					Endpoint: "127.0.0.1:4022",
 
- 					Username: username,
 
- 				},
 
- 				Password: kms.NewPlainSecret("pwd"),
 
- 			},
 
- 		},
 
- 	}
 
- 	err = executeEmailRuleAction(dataprovider.EventActionEmailConfig{
 
- 		Recipients:  []string{"[email protected]"},
 
- 		Subject:     "subject",
 
- 		Body:        "body",
 
- 		Attachments: []string{"/file.txt"},
 
- 	}, &EventParams{
 
- 		sender: username,
 
- 	})
 
- 	assert.Error(t, err)
 
- 	conn := NewBaseConnection("", protocolEventAction, "", "", user)
 
- 	err = executeDeleteFileFsAction(conn, "", nil)
 
- 	assert.Error(t, err)
 
- 	err = dataprovider.AddUser(&user, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	// check root fs fails
 
- 	err = executeDeleteFsActionForUser(nil, testReplacer, user)
 
- 	assert.Error(t, err)
 
- 	err = executeMkDirsFsActionForUser(nil, testReplacer, user)
 
- 	assert.Error(t, err)
 
- 	err = executeRenameFsActionForUser(nil, testReplacer, user)
 
- 	assert.Error(t, err)
 
- 	err = executeExistFsActionForUser(nil, testReplacer, user)
 
- 	assert.Error(t, err)
 
- 	err = executeCopyFsActionForUser(nil, testReplacer, user)
 
- 	assert.Error(t, err)
 
- 	err = executeCompressFsActionForUser(dataprovider.EventActionFsCompress{}, testReplacer, user)
 
- 	assert.Error(t, err)
 
- 	_, _, _, _, err = getFileWriter(conn, "/path.txt", -1) //nolint:dogsled
 
- 	assert.Error(t, err)
 
- 	err = executeEmailRuleAction(dataprovider.EventActionEmailConfig{
 
- 		Recipients:  []string{"[email protected]"},
 
- 		Subject:     "subject",
 
- 		Body:        "body",
 
- 		Attachments: []string{"/file1.txt"},
 
- 	}, &EventParams{
 
- 		sender: username,
 
- 	})
 
- 	assert.Error(t, err)
 
- 	fn := getFileContentFn(NewBaseConnection("", protocolEventAction, "", "", user), "/f.txt", 1234)
 
- 	var b bytes.Buffer
 
- 	_, err = fn(&b)
 
- 	assert.Error(t, err)
 
- 	err = executeHTTPRuleAction(dataprovider.EventActionHTTPConfig{
 
- 		Endpoint: "http://127.0.0.1:9999/",
 
- 		Method:   http.MethodPost,
 
- 		Parts: []dataprovider.HTTPPart{
 
- 			{
 
- 				Name:     "p1",
 
- 				Filepath: "/filepath",
 
- 			},
 
- 		},
 
- 	}, &EventParams{
 
- 		sender: username,
 
- 	})
 
- 	assert.Error(t, err)
 
- 	user.FsConfig.Provider = sdk.LocalFilesystemProvider
 
- 	user.Permissions["/"] = []string{dataprovider.PermUpload}
 
- 	err = dataprovider.DeleteUser(username, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = dataprovider.AddUser(&user, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = executeRenameFsActionForUser([]dataprovider.KeyValue{
 
- 		{
 
- 			Key:   "/p1",
 
- 			Value: "/p1",
 
- 		},
 
- 	}, testReplacer, user)
 
- 	if assert.Error(t, err) {
 
- 		assert.Contains(t, err.Error(), "the rename source and target cannot be the same")
 
- 	}
 
- 	err = executeRuleAction(dataprovider.BaseEventAction{
 
- 		Type: dataprovider.ActionTypeFilesystem,
 
- 		Options: dataprovider.BaseEventActionOptions{
 
- 			FsConfig: dataprovider.EventActionFilesystemConfig{
 
- 				Type: dataprovider.FilesystemActionRename,
 
- 				Renames: []dataprovider.KeyValue{
 
- 					{
 
- 						Key:   "/p2",
 
- 						Value: "/p2",
 
- 					},
 
- 				},
 
- 			},
 
- 		},
 
- 	}, &EventParams{}, dataprovider.ConditionOptions{
 
- 		Names: []dataprovider.ConditionPattern{
 
- 			{
 
- 				Pattern: username,
 
- 			},
 
- 		},
 
- 	})
 
- 	assert.Error(t, err)
 
- 	if runtime.GOOS != osWindows {
 
- 		dirPath := filepath.Join(user.HomeDir, "adir", "sub")
 
- 		err := os.MkdirAll(dirPath, os.ModePerm)
 
- 		assert.NoError(t, err)
 
- 		filePath := filepath.Join(dirPath, "f.dat")
 
- 		err = os.WriteFile(filePath, []byte("test file content"), 0666)
 
- 		assert.NoError(t, err)
 
- 		err = os.Chmod(dirPath, 0001)
 
- 		assert.NoError(t, err)
 
- 		err = executeDeleteFsActionForUser([]string{"/adir/sub"}, testReplacer, user)
 
- 		assert.Error(t, err)
 
- 		err = executeDeleteFsActionForUser([]string{"/adir/sub/f.dat"}, testReplacer, user)
 
- 		assert.Error(t, err)
 
- 		err = os.Chmod(dirPath, 0555)
 
- 		assert.NoError(t, err)
 
- 		err = executeDeleteFsActionForUser([]string{"/adir/sub/f.dat"}, testReplacer, user)
 
- 		if assert.Error(t, err) {
 
- 			assert.Contains(t, err.Error(), "unable to remove file")
 
- 		}
 
- 		err = executeRuleAction(dataprovider.BaseEventAction{
 
- 			Type: dataprovider.ActionTypeFilesystem,
 
- 			Options: dataprovider.BaseEventActionOptions{
 
- 				FsConfig: dataprovider.EventActionFilesystemConfig{
 
- 					Type:    dataprovider.FilesystemActionDelete,
 
- 					Deletes: []string{"/adir/sub/f.dat"},
 
- 				},
 
- 			},
 
- 		}, &EventParams{}, dataprovider.ConditionOptions{
 
- 			Names: []dataprovider.ConditionPattern{
 
- 				{
 
- 					Pattern: username,
 
- 				},
 
- 			},
 
- 		})
 
- 		assert.Error(t, err)
 
- 		err = executeMkDirsFsActionForUser([]string{"/adir/sub/sub"}, testReplacer, user)
 
- 		if assert.Error(t, err) {
 
- 			assert.Contains(t, err.Error(), "unable to create dir")
 
- 		}
 
- 		err = executeMkDirsFsActionForUser([]string{"/adir/sub/sub/sub"}, testReplacer, user)
 
- 		if assert.Error(t, err) {
 
- 			assert.Contains(t, err.Error(), "unable to check parent dirs")
 
- 		}
 
- 		err = executeRuleAction(dataprovider.BaseEventAction{
 
- 			Type: dataprovider.ActionTypeFilesystem,
 
- 			Options: dataprovider.BaseEventActionOptions{
 
- 				FsConfig: dataprovider.EventActionFilesystemConfig{
 
- 					Type:   dataprovider.FilesystemActionMkdirs,
 
- 					MkDirs: []string{"/adir/sub/sub1"},
 
- 				},
 
- 			},
 
- 		}, &EventParams{}, dataprovider.ConditionOptions{
 
- 			Names: []dataprovider.ConditionPattern{
 
- 				{
 
- 					Pattern: username,
 
- 				},
 
- 			},
 
- 		})
 
- 		assert.Error(t, err)
 
- 		err = os.Chmod(dirPath, os.ModePerm)
 
- 		assert.NoError(t, err)
 
- 		conn = NewBaseConnection("", protocolEventAction, "", "", user)
 
- 		wr := &zipWriterWrapper{
 
- 			Name:    "test.zip",
 
- 			Writer:  zip.NewWriter(bytes.NewBuffer(nil)),
 
- 			Entries: map[string]bool{},
 
- 		}
 
- 		err = addZipEntry(wr, conn, "/adir/sub/f.dat", "/adir/sub/sub")
 
- 		assert.Error(t, err)
 
- 		assert.Contains(t, getErrorString(err), "is outside base dir")
 
- 	}
 
- 	err = dataprovider.DeleteUser(username, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = os.RemoveAll(user.GetHomeDir())
 
- 	assert.NoError(t, err)
 
- }
 
- func TestQuotaActionsWithQuotaTrackDisabled(t *testing.T) {
 
- 	oldProviderConf := dataprovider.GetProviderConfig()
 
- 	providerConf := dataprovider.GetProviderConfig()
 
- 	providerConf.TrackQuota = 0
 
- 	err := dataprovider.Close()
 
- 	assert.NoError(t, err)
 
- 	err = dataprovider.Initialize(providerConf, configDir, true)
 
- 	assert.NoError(t, err)
 
- 	username := "u1"
 
- 	user := dataprovider.User{
 
- 		BaseUser: sdk.BaseUser{
 
- 			Username: username,
 
- 			HomeDir:  filepath.Join(os.TempDir(), username),
 
- 			Status:   1,
 
- 			Permissions: map[string][]string{
 
- 				"/": {dataprovider.PermAny},
 
- 			},
 
- 		},
 
- 		FsConfig: vfs.Filesystem{
 
- 			Provider: sdk.LocalFilesystemProvider,
 
- 		},
 
- 	}
 
- 	err = dataprovider.AddUser(&user, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = os.MkdirAll(user.GetHomeDir(), os.ModePerm)
 
- 	assert.NoError(t, err)
 
- 	err = executeRuleAction(dataprovider.BaseEventAction{Type: dataprovider.ActionTypeUserQuotaReset},
 
- 		&EventParams{}, dataprovider.ConditionOptions{
 
- 			Names: []dataprovider.ConditionPattern{
 
- 				{
 
- 					Pattern: username,
 
- 				},
 
- 			},
 
- 		})
 
- 	assert.Error(t, err)
 
- 	err = executeRuleAction(dataprovider.BaseEventAction{Type: dataprovider.ActionTypeTransferQuotaReset},
 
- 		&EventParams{}, dataprovider.ConditionOptions{
 
- 			Names: []dataprovider.ConditionPattern{
 
- 				{
 
- 					Pattern: username,
 
- 				},
 
- 			},
 
- 		})
 
- 	assert.Error(t, err)
 
- 	err = os.RemoveAll(user.GetHomeDir())
 
- 	assert.NoError(t, err)
 
- 	err = dataprovider.DeleteUser(username, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	foldername := "f1"
 
- 	folder := vfs.BaseVirtualFolder{
 
- 		Name:       foldername,
 
- 		MappedPath: filepath.Join(os.TempDir(), foldername),
 
- 	}
 
- 	err = dataprovider.AddFolder(&folder, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = os.MkdirAll(folder.MappedPath, os.ModePerm)
 
- 	assert.NoError(t, err)
 
- 	err = executeRuleAction(dataprovider.BaseEventAction{Type: dataprovider.ActionTypeFolderQuotaReset},
 
- 		&EventParams{}, dataprovider.ConditionOptions{
 
- 			Names: []dataprovider.ConditionPattern{
 
- 				{
 
- 					Pattern: foldername,
 
- 				},
 
- 			},
 
- 		})
 
- 	assert.Error(t, err)
 
- 	err = os.RemoveAll(folder.MappedPath)
 
- 	assert.NoError(t, err)
 
- 	err = dataprovider.DeleteFolder(foldername, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = dataprovider.Close()
 
- 	assert.NoError(t, err)
 
- 	err = dataprovider.Initialize(oldProviderConf, configDir, true)
 
- 	assert.NoError(t, err)
 
- }
 
- func TestScheduledActions(t *testing.T) {
 
- 	startEventScheduler()
 
- 	backupsPath := filepath.Join(os.TempDir(), "backups")
 
- 	err := os.RemoveAll(backupsPath)
 
- 	assert.NoError(t, err)
 
- 	action := &dataprovider.BaseEventAction{
 
- 		Name: "action",
 
- 		Type: dataprovider.ActionTypeBackup,
 
- 	}
 
- 	err = dataprovider.AddEventAction(action, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	rule := &dataprovider.EventRule{
 
- 		Name:    "rule",
 
- 		Status:  1,
 
- 		Trigger: dataprovider.EventTriggerSchedule,
 
- 		Conditions: dataprovider.EventConditions{
 
- 			Schedules: []dataprovider.Schedule{
 
- 				{
 
- 					Hours:      "11",
 
- 					DayOfWeek:  "*",
 
- 					DayOfMonth: "*",
 
- 					Month:      "*",
 
- 				},
 
- 			},
 
- 		},
 
- 		Actions: []dataprovider.EventAction{
 
- 			{
 
- 				BaseEventAction: dataprovider.BaseEventAction{
 
- 					Name: action.Name,
 
- 				},
 
- 				Order: 1,
 
- 			},
 
- 		},
 
- 	}
 
- 	job := eventCronJob{
 
- 		ruleName: rule.Name,
 
- 	}
 
- 	job.Run() // rule not found
 
- 	assert.NoDirExists(t, backupsPath)
 
- 	err = dataprovider.AddEventRule(rule, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	job.Run()
 
- 	assert.DirExists(t, backupsPath)
 
- 	action.Type = dataprovider.ActionTypeEmail
 
- 	action.Options = dataprovider.BaseEventActionOptions{
 
- 		EmailConfig: dataprovider.EventActionEmailConfig{
 
- 			Recipients:  []string{"[email protected]"},
 
- 			Subject:     "test with attachments",
 
- 			Body:        "body",
 
- 			Attachments: []string{"/file1.txt"},
 
- 		},
 
- 	}
 
- 	err = dataprovider.UpdateEventAction(action, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	job.Run() // action is not compatible with a scheduled rule
 
- 	err = dataprovider.DeleteEventRule(rule.Name, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = dataprovider.DeleteEventAction(action.Name, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = os.RemoveAll(backupsPath)
 
- 	assert.NoError(t, err)
 
- 	stopEventScheduler()
 
- }
 
- func TestEventParamsCopy(t *testing.T) {
 
- 	params := EventParams{
 
- 		Name:            "name",
 
- 		Event:           "event",
 
- 		Status:          1,
 
- 		errors:          []string{"error1"},
 
- 		retentionChecks: []executedRetentionCheck{},
 
- 	}
 
- 	paramsCopy := params.getACopy()
 
- 	assert.Equal(t, params, *paramsCopy)
 
- 	params.Name = "name mod"
 
- 	paramsCopy.Event = "event mod"
 
- 	paramsCopy.Status = 2
 
- 	params.errors = append(params.errors, "error2")
 
- 	paramsCopy.errors = append(paramsCopy.errors, "error3")
 
- 	assert.Equal(t, []string{"error1", "error3"}, paramsCopy.errors)
 
- 	assert.Equal(t, []string{"error1", "error2"}, params.errors)
 
- 	assert.Equal(t, "name mod", params.Name)
 
- 	assert.Equal(t, "name", paramsCopy.Name)
 
- 	assert.Equal(t, "event", params.Event)
 
- 	assert.Equal(t, "event mod", paramsCopy.Event)
 
- 	assert.Equal(t, 1, params.Status)
 
- 	assert.Equal(t, 2, paramsCopy.Status)
 
- 	params = EventParams{
 
- 		retentionChecks: []executedRetentionCheck{
 
- 			{
 
- 				Username:   "u",
 
- 				ActionName: "a",
 
- 				Results: []folderRetentionCheckResult{
 
- 					{
 
- 						Path:      "p",
 
- 						Retention: 1,
 
- 					},
 
- 				},
 
- 			},
 
- 		},
 
- 	}
 
- 	paramsCopy = params.getACopy()
 
- 	require.Len(t, paramsCopy.retentionChecks, 1)
 
- 	paramsCopy.retentionChecks[0].Username = "u_copy"
 
- 	paramsCopy.retentionChecks[0].ActionName = "a_copy"
 
- 	require.Len(t, paramsCopy.retentionChecks[0].Results, 1)
 
- 	paramsCopy.retentionChecks[0].Results[0].Path = "p_copy"
 
- 	paramsCopy.retentionChecks[0].Results[0].Retention = 2
 
- 	assert.Equal(t, "u", params.retentionChecks[0].Username)
 
- 	assert.Equal(t, "a", params.retentionChecks[0].ActionName)
 
- 	assert.Equal(t, "p", params.retentionChecks[0].Results[0].Path)
 
- 	assert.Equal(t, 1, params.retentionChecks[0].Results[0].Retention)
 
- 	assert.Equal(t, "u_copy", paramsCopy.retentionChecks[0].Username)
 
- 	assert.Equal(t, "a_copy", paramsCopy.retentionChecks[0].ActionName)
 
- 	assert.Equal(t, "p_copy", paramsCopy.retentionChecks[0].Results[0].Path)
 
- 	assert.Equal(t, 2, paramsCopy.retentionChecks[0].Results[0].Retention)
 
- 	assert.Nil(t, params.IDPCustomFields)
 
- 	params.addIDPCustomFields(nil)
 
- 	assert.Nil(t, params.IDPCustomFields)
 
- 	params.IDPCustomFields = &map[string]string{
 
- 		"field1": "val1",
 
- 	}
 
- 	paramsCopy = params.getACopy()
 
- 	for k, v := range *paramsCopy.IDPCustomFields {
 
- 		assert.Equal(t, "field1", k)
 
- 		assert.Equal(t, "val1", v)
 
- 	}
 
- 	assert.Equal(t, params.IDPCustomFields, paramsCopy.IDPCustomFields)
 
- 	(*paramsCopy.IDPCustomFields)["field1"] = "val2"
 
- 	assert.NotEqual(t, params.IDPCustomFields, paramsCopy.IDPCustomFields)
 
- 	params.Metadata = map[string]string{"key": "value"}
 
- 	paramsCopy = params.getACopy()
 
- 	params.Metadata["key1"] = "value1"
 
- 	require.Equal(t, map[string]string{"key": "value"}, paramsCopy.Metadata)
 
- }
 
- func TestEventParamsStatusFromError(t *testing.T) {
 
- 	params := EventParams{Status: 1}
 
- 	params.AddError(os.ErrNotExist)
 
- 	assert.Equal(t, 1, params.Status)
 
- 	params = EventParams{Status: 1, updateStatusFromError: true}
 
- 	params.AddError(os.ErrNotExist)
 
- 	assert.Equal(t, 2, params.Status)
 
- }
 
- type testWriter struct {
 
- 	errTest  error
 
- 	sentinel string
 
- }
 
- func (w *testWriter) Write(p []byte) (int, error) {
 
- 	if w.errTest != nil {
 
- 		return 0, w.errTest
 
- 	}
 
- 	if w.sentinel == string(p) {
 
- 		return 0, io.ErrUnexpectedEOF
 
- 	}
 
- 	return len(p), nil
 
- }
 
- func TestWriteHTTPPartsError(t *testing.T) {
 
- 	m := multipart.NewWriter(&testWriter{
 
- 		errTest: io.ErrShortWrite,
 
- 	})
 
- 	err := writeHTTPPart(m, dataprovider.HTTPPart{}, nil, nil, nil, &EventParams{}, false)
 
- 	assert.ErrorIs(t, err, io.ErrShortWrite)
 
- 	body := "test body"
 
- 	m = multipart.NewWriter(&testWriter{sentinel: body})
 
- 	err = writeHTTPPart(m, dataprovider.HTTPPart{
 
- 		Body: body,
 
- 	}, nil, nil, nil, &EventParams{}, false)
 
- 	assert.ErrorIs(t, err, io.ErrUnexpectedEOF)
 
- }
 
- func TestReplacePathsPlaceholders(t *testing.T) {
 
- 	replacer := strings.NewReplacer("{{VirtualPath}}", "/path1")
 
- 	paths := []string{"{{VirtualPath}}", "/path1"}
 
- 	paths = replacePathsPlaceholders(paths, replacer)
 
- 	assert.Equal(t, []string{"/path1"}, paths)
 
- 	paths = []string{"{{VirtualPath}}", "/path2"}
 
- 	paths = replacePathsPlaceholders(paths, replacer)
 
- 	assert.Equal(t, []string{"/path1", "/path2"}, paths)
 
- }
 
- func TestEstimateZipSizeErrors(t *testing.T) {
 
- 	u := dataprovider.User{
 
- 		BaseUser: sdk.BaseUser{
 
- 			Username: "u",
 
- 			HomeDir:  filepath.Join(os.TempDir(), "u"),
 
- 			Status:   1,
 
- 			Permissions: map[string][]string{
 
- 				"/": {dataprovider.PermAny},
 
- 			},
 
- 			QuotaSize: 1000,
 
- 		},
 
- 	}
 
- 	err := dataprovider.AddUser(&u, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = os.MkdirAll(u.GetHomeDir(), os.ModePerm)
 
- 	assert.NoError(t, err)
 
- 	conn := NewBaseConnection("", ProtocolFTP, "", "", u)
 
- 	_, _, _, _, err = getFileWriter(conn, "/missing/path/file.txt", -1) //nolint:dogsled
 
- 	assert.Error(t, err)
 
- 	_, err = getSizeForPath(conn, "/missing", vfs.NewFileInfo("missing", true, 0, time.Now(), false))
 
- 	assert.True(t, conn.IsNotExistError(err))
 
- 	if runtime.GOOS != osWindows {
 
- 		err = os.MkdirAll(filepath.Join(u.HomeDir, "d1", "d2", "sub"), os.ModePerm)
 
- 		assert.NoError(t, err)
 
- 		err = os.WriteFile(filepath.Join(u.HomeDir, "d1", "d2", "sub", "file.txt"), []byte("data"), 0666)
 
- 		assert.NoError(t, err)
 
- 		err = os.Chmod(filepath.Join(u.HomeDir, "d1", "d2"), 0001)
 
- 		assert.NoError(t, err)
 
- 		size, err := estimateZipSize(conn, "/archive.zip", []string{"/d1"})
 
- 		assert.Error(t, err, "size %d", size)
 
- 		err = os.Chmod(filepath.Join(u.HomeDir, "d1", "d2"), os.ModePerm)
 
- 		assert.NoError(t, err)
 
- 	}
 
- 	err = dataprovider.DeleteUser(u.Username, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = os.RemoveAll(u.GetHomeDir())
 
- 	assert.NoError(t, err)
 
- }
 
- func TestOnDemandRule(t *testing.T) {
 
- 	a := &dataprovider.BaseEventAction{
 
- 		Name:    "a",
 
- 		Type:    dataprovider.ActionTypeBackup,
 
- 		Options: dataprovider.BaseEventActionOptions{},
 
- 	}
 
- 	err := dataprovider.AddEventAction(a, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	r := &dataprovider.EventRule{
 
- 		Name:    "test on demand rule",
 
- 		Status:  1,
 
- 		Trigger: dataprovider.EventTriggerOnDemand,
 
- 		Actions: []dataprovider.EventAction{
 
- 			{
 
- 				BaseEventAction: dataprovider.BaseEventAction{
 
- 					Name: a.Name,
 
- 				},
 
- 			},
 
- 		},
 
- 	}
 
- 	err = dataprovider.AddEventRule(r, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = RunOnDemandRule(r.Name)
 
- 	assert.NoError(t, err)
 
- 	r.Status = 0
 
- 	err = dataprovider.UpdateEventRule(r, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = RunOnDemandRule(r.Name)
 
- 	assert.ErrorIs(t, err, util.ErrValidation)
 
- 	assert.Contains(t, err.Error(), "is inactive")
 
- 	r.Status = 1
 
- 	r.Trigger = dataprovider.EventTriggerCertificate
 
- 	err = dataprovider.UpdateEventRule(r, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = RunOnDemandRule(r.Name)
 
- 	assert.ErrorIs(t, err, util.ErrValidation)
 
- 	assert.Contains(t, err.Error(), "is not defined as on-demand")
 
- 	a1 := &dataprovider.BaseEventAction{
 
- 		Name: "a1",
 
- 		Type: dataprovider.ActionTypeEmail,
 
- 		Options: dataprovider.BaseEventActionOptions{
 
- 			EmailConfig: dataprovider.EventActionEmailConfig{
 
- 				Recipients:  []string{"[email protected]"},
 
- 				Subject:     "subject",
 
- 				Body:        "body",
 
- 				Attachments: []string{"/{{VirtualPath}}"},
 
- 			},
 
- 		},
 
- 	}
 
- 	err = dataprovider.AddEventAction(a1, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	r.Trigger = dataprovider.EventTriggerOnDemand
 
- 	r.Actions = []dataprovider.EventAction{
 
- 		{
 
- 			BaseEventAction: dataprovider.BaseEventAction{
 
- 				Name: a1.Name,
 
- 			},
 
- 		},
 
- 	}
 
- 	err = dataprovider.UpdateEventRule(r, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = RunOnDemandRule(r.Name)
 
- 	assert.ErrorIs(t, err, util.ErrValidation)
 
- 	assert.Contains(t, err.Error(), "incosistent actions")
 
- 	err = dataprovider.DeleteEventRule(r.Name, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = dataprovider.DeleteEventAction(a.Name, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = dataprovider.DeleteEventAction(a1.Name, "", "", "")
 
- 	assert.NoError(t, err)
 
- 	err = RunOnDemandRule(r.Name)
 
- 	assert.ErrorIs(t, err, util.ErrNotFound)
 
- }
 
- func getErrorString(err error) string {
 
- 	if err == nil {
 
- 		return ""
 
- 	}
 
- 	return err.Error()
 
- }
 
- func TestHTTPEndpointWithPlaceholders(t *testing.T) {
 
- 	c := dataprovider.EventActionHTTPConfig{
 
- 		Endpoint: "http://127.0.0.1:8080/base/url/{{Name}}/{{VirtualPath}}/upload",
 
- 		QueryParameters: []dataprovider.KeyValue{
 
- 			{
 
- 				Key:   "u",
 
- 				Value: "{{Name}}",
 
- 			},
 
- 			{
 
- 				Key:   "p",
 
- 				Value: "{{VirtualPath}}",
 
- 			},
 
- 		},
 
- 	}
 
- 	name := "uname"
 
- 	vPath := "/a dir/@ file.txt"
 
- 	replacer := strings.NewReplacer("{{Name}}", name, "{{VirtualPath}}", vPath)
 
- 	u, err := getHTTPRuleActionEndpoint(&c, replacer)
 
- 	assert.NoError(t, err)
 
- 	expected := "http://127.0.0.1:8080/base/url/" + url.PathEscape(name) + "/" + url.PathEscape(vPath) +
 
- 		"/upload?" + "p=" + url.QueryEscape(vPath) + "&u=" + url.QueryEscape(name)
 
- 	assert.Equal(t, expected, u)
 
- 	c.Endpoint = "http://127.0.0.1/upload"
 
- 	u, err = getHTTPRuleActionEndpoint(&c, replacer)
 
- 	assert.NoError(t, err)
 
- 	expected = c.Endpoint + "?p=" + url.QueryEscape(vPath) + "&u=" + url.QueryEscape(name)
 
- 	assert.Equal(t, expected, u)
 
- }
 
- func TestMetadataReplacement(t *testing.T) {
 
- 	params := &EventParams{
 
- 		Metadata: map[string]string{
 
- 			"key": "value",
 
- 		},
 
- 	}
 
- 	replacements := params.getStringReplacements(false, false)
 
- 	replacer := strings.NewReplacer(replacements...)
 
- 	reader, _, err := getHTTPRuleActionBody(&dataprovider.EventActionHTTPConfig{Body: "{{Metadata}} {{MetadataString}}"}, replacer, nil, dataprovider.User{}, params, false)
 
- 	require.NoError(t, err)
 
- 	data, err := io.ReadAll(reader)
 
- 	require.NoError(t, err)
 
- 	assert.Equal(t, `{"key":"value"} {\"key\":\"value\"}`, string(data))
 
- }
 
 
  |