1package httpd_test
2
3import (
4	"bytes"
5	"crypto/rand"
6	"encoding/json"
7	"errors"
8	"fmt"
9	"io"
10	"math"
11	"mime/multipart"
12	"net"
13	"net/http"
14	"net/http/httptest"
15	"net/url"
16	"os"
17	"path"
18	"path/filepath"
19	"regexp"
20	"runtime"
21	"strconv"
22	"strings"
23	"sync"
24	"testing"
25	"time"
26
27	"github.com/go-chi/render"
28	_ "github.com/go-sql-driver/mysql"
29	_ "github.com/lib/pq"
30	"github.com/lithammer/shortuuid/v3"
31	_ "github.com/mattn/go-sqlite3"
32	"github.com/mhale/smtpd"
33	"github.com/pquerna/otp"
34	"github.com/pquerna/otp/totp"
35	"github.com/rs/xid"
36	"github.com/rs/zerolog"
37	"github.com/stretchr/testify/assert"
38	"github.com/stretchr/testify/require"
39	"golang.org/x/crypto/bcrypt"
40	"golang.org/x/net/html"
41
42	"github.com/drakkan/sftpgo/v2/common"
43	"github.com/drakkan/sftpgo/v2/config"
44	"github.com/drakkan/sftpgo/v2/dataprovider"
45	"github.com/drakkan/sftpgo/v2/httpclient"
46	"github.com/drakkan/sftpgo/v2/httpd"
47	"github.com/drakkan/sftpgo/v2/httpdtest"
48	"github.com/drakkan/sftpgo/v2/kms"
49	"github.com/drakkan/sftpgo/v2/logger"
50	"github.com/drakkan/sftpgo/v2/mfa"
51	"github.com/drakkan/sftpgo/v2/sdk"
52	"github.com/drakkan/sftpgo/v2/sdk/plugin"
53	"github.com/drakkan/sftpgo/v2/sftpd"
54	"github.com/drakkan/sftpgo/v2/smtp"
55	"github.com/drakkan/sftpgo/v2/util"
56	"github.com/drakkan/sftpgo/v2/vfs"
57)
58
59const (
60	defaultUsername                 = "test_user"
61	defaultPassword                 = "test_password"
62	testPubKey                      = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC03jj0D+djk7pxIf/0OhrxrchJTRZklofJ1NoIu4752Sq02mdXmarMVsqJ1cAjV5LBVy3D1F5U6XW4rppkXeVtd04Pxb09ehtH0pRRPaoHHlALiJt8CoMpbKYMA8b3KXPPriGxgGomvtU2T2RMURSwOZbMtpsugfjYSWenyYX+VORYhylWnSXL961LTyC21ehd6d6QnW9G7E5hYMITMY9TuQZz3bROYzXiTsgN0+g6Hn7exFQp50p45StUMfV/SftCMdCxlxuyGny2CrN/vfjO7xxOo2uv7q1qm10Q46KPWJQv+pgZ/OfL+EDjy07n5QVSKHlbx+2nT4Q0EgOSQaCTYwn3YjtABfIxWwgAFdyj6YlPulCL22qU4MYhDcA6PSBwDdf8hvxBfvsiHdM+JcSHvv8/VeJhk6CmnZxGY0fxBupov27z3yEO8nAg8k+6PaUiW1MSUfuGMF/ktB8LOstXsEPXSszuyXiOv4DaryOXUiSn7bmRqKcEFlJusO6aZP0= nicola@p1"
63	testPubKey1                     = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCd60+/j+y8f0tLftihWV1YN9RSahMI9btQMDIMqts/jeNbD8jgoogM3nhF7KxfcaMKURuD47KC4Ey6iAJUJ0sWkSNNxOcIYuvA+5MlspfZDsa8Ag76Fe1vyz72WeHMHMeh/hwFo2TeIeIXg480T1VI6mzfDrVp2GzUx0SS0dMsQBjftXkuVR8YOiOwMCAH2a//M1OrvV7d/NBk6kBN0WnuIBb2jKm15PAA7+jQQG7tzwk2HedNH3jeL5GH31xkSRwlBczRK0xsCQXehAlx6cT/e/s44iJcJTHfpPKoSk6UAhPJYe7Z1QnuoawY9P9jQaxpyeImBZxxUEowhjpj2avBxKdRGBVK8R7EL8tSOeLbhdyWe5Mwc1+foEbq9Zz5j5Kd+hn3Wm1UnsGCrXUUUoZp1jnlNl0NakCto+5KmqnT9cHxaY+ix2RLUWAZyVFlRq71OYux1UHJnEJPiEI1/tr4jFBSL46qhQZv/TfpkfVW8FLz0lErfqu0gQEZnNHr3Fc= nicola@p1"
64	defaultTokenAuthUser            = "admin"
65	defaultTokenAuthPass            = "password"
66	altAdminUsername                = "newTestAdmin"
67	altAdminPassword                = "password1"
68	csrfFormToken                   = "_form_token"
69	tokenPath                       = "/api/v2/token"
70	userTokenPath                   = "/api/v2/user/token"
71	userLogoutPath                  = "/api/v2/user/logout"
72	userPath                        = "/api/v2/users"
73	adminPath                       = "/api/v2/admins"
74	adminPwdPath                    = "/api/v2/admin/changepwd"
75	folderPath                      = "/api/v2/folders"
76	activeConnectionsPath           = "/api/v2/connections"
77	serverStatusPath                = "/api/v2/status"
78	quotasBasePath                  = "/api/v2/quotas"
79	quotaScanPath                   = "/api/v2/quotas/users/scans"
80	quotaScanVFolderPath            = "/api/v2/quotas/folders/scans"
81	quotaScanCompatPath             = "/api/v2/quota-scans"
82	quotaScanVFolderCompatPath      = "/api/v2/folder-quota-scans"
83	updateUsedQuotaCompatPath       = "/api/v2/quota-update"
84	updateFolderUsedQuotaCompatPath = "/api/v2/folder-quota-update"
85	defenderHosts                   = "/api/v2/defender/hosts"
86	defenderUnban                   = "/api/v2/defender/unban"
87	versionPath                     = "/api/v2/version"
88	logoutPath                      = "/api/v2/logout"
89	userPwdPath                     = "/api/v2/user/changepwd"
90	userPublicKeysPath              = "/api/v2/user/publickeys"
91	userDirsPath                    = "/api/v2/user/dirs"
92	userFilesPath                   = "/api/v2/user/files"
93	userStreamZipPath               = "/api/v2/user/streamzip"
94	apiKeysPath                     = "/api/v2/apikeys"
95	adminTOTPConfigsPath            = "/api/v2/admin/totp/configs"
96	adminTOTPGeneratePath           = "/api/v2/admin/totp/generate"
97	adminTOTPValidatePath           = "/api/v2/admin/totp/validate"
98	adminTOTPSavePath               = "/api/v2/admin/totp/save"
99	admin2FARecoveryCodesPath       = "/api/v2/admin/2fa/recoverycodes"
100	adminProfilePath                = "/api/v2/admin/profile"
101	userTOTPConfigsPath             = "/api/v2/user/totp/configs"
102	userTOTPGeneratePath            = "/api/v2/user/totp/generate"
103	userTOTPValidatePath            = "/api/v2/user/totp/validate"
104	userTOTPSavePath                = "/api/v2/user/totp/save"
105	user2FARecoveryCodesPath        = "/api/v2/user/2fa/recoverycodes"
106	userProfilePath                 = "/api/v2/user/profile"
107	userSharesPath                  = "/api/v2/user/shares"
108	retentionBasePath               = "/api/v2/retention/users"
109	fsEventsPath                    = "/api/v2/events/fs"
110	providerEventsPath              = "/api/v2/events/provider"
111	sharesPath                      = "/api/v2/shares"
112	healthzPath                     = "/healthz"
113	webBasePath                     = "/web"
114	webBasePathAdmin                = "/web/admin"
115	webAdminSetupPath               = "/web/admin/setup"
116	webLoginPath                    = "/web/admin/login"
117	webLogoutPath                   = "/web/admin/logout"
118	webUsersPath                    = "/web/admin/users"
119	webUserPath                     = "/web/admin/user"
120	webFoldersPath                  = "/web/admin/folders"
121	webFolderPath                   = "/web/admin/folder"
122	webConnectionsPath              = "/web/admin/connections"
123	webStatusPath                   = "/web/admin/status"
124	webAdminsPath                   = "/web/admin/managers"
125	webAdminPath                    = "/web/admin/manager"
126	webMaintenancePath              = "/web/admin/maintenance"
127	webRestorePath                  = "/web/admin/restore"
128	webChangeAdminPwdPath           = "/web/admin/changepwd"
129	webAdminProfilePath             = "/web/admin/profile"
130	webTemplateUser                 = "/web/admin/template/user"
131	webTemplateFolder               = "/web/admin/template/folder"
132	webDefenderPath                 = "/web/admin/defender"
133	webAdminTwoFactorPath           = "/web/admin/twofactor"
134	webAdminTwoFactorRecoveryPath   = "/web/admin/twofactor-recovery"
135	webAdminMFAPath                 = "/web/admin/mfa"
136	webAdminTOTPSavePath            = "/web/admin/totp/save"
137	webAdminForgotPwdPath           = "/web/admin/forgot-password"
138	webAdminResetPwdPath            = "/web/admin/reset-password"
139	webBasePathClient               = "/web/client"
140	webClientLoginPath              = "/web/client/login"
141	webClientFilesPath              = "/web/client/files"
142	webClientEditFilePath           = "/web/client/editfile"
143	webClientDirsPath               = "/web/client/dirs"
144	webClientDownloadZipPath        = "/web/client/downloadzip"
145	webChangeClientPwdPath          = "/web/client/changepwd"
146	webClientProfilePath            = "/web/client/profile"
147	webClientTwoFactorPath          = "/web/client/twofactor"
148	webClientTwoFactorRecoveryPath  = "/web/client/twofactor-recovery"
149	webClientLogoutPath             = "/web/client/logout"
150	webClientMFAPath                = "/web/client/mfa"
151	webClientTOTPSavePath           = "/web/client/totp/save"
152	webClientSharesPath             = "/web/client/shares"
153	webClientSharePath              = "/web/client/share"
154	webClientPubSharesPath          = "/web/client/pubshares"
155	webClientForgotPwdPath          = "/web/client/forgot-password"
156	webClientResetPwdPath           = "/web/client/reset-password"
157	webClientViewPDFPath            = "/web/client/viewpdf"
158	httpBaseURL                     = "http://127.0.0.1:8081"
159	sftpServerAddr                  = "127.0.0.1:8022"
160	smtpServerAddr                  = "127.0.0.1:3525"
161	configDir                       = ".."
162	httpsCert                       = `-----BEGIN CERTIFICATE-----
163MIICHTCCAaKgAwIBAgIUHnqw7QnB1Bj9oUsNpdb+ZkFPOxMwCgYIKoZIzj0EAwIw
164RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
165dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDAyMDQwOTUzMDRaFw0zMDAyMDEw
166OTUzMDRaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD
167VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwdjAQBgcqhkjOPQIBBgUrgQQA
168IgNiAARCjRMqJ85rzMC998X5z761nJ+xL3bkmGVqWvrJ51t5OxV0v25NsOgR82CA
169NXUgvhVYs7vNFN+jxtb2aj6Xg+/2G/BNxkaFspIVCzgWkxiz7XE4lgUwX44FCXZM
1703+JeUbKjUzBRMB0GA1UdDgQWBBRhLw+/o3+Z02MI/d4tmaMui9W16jAfBgNVHSME
171GDAWgBRhLw+/o3+Z02MI/d4tmaMui9W16jAPBgNVHRMBAf8EBTADAQH/MAoGCCqG
172SM49BAMCA2kAMGYCMQDqLt2lm8mE+tGgtjDmtFgdOcI72HSbRQ74D5rYTzgST1rY
173/8wTi5xl8TiFUyLMUsICMQC5ViVxdXbhuG7gX6yEqSkMKZICHpO8hqFwOD/uaFVI
174dV4vKmHUzwK/eIx+8Ay3neE=
175-----END CERTIFICATE-----`
176	httpsKey = `-----BEGIN EC PARAMETERS-----
177BgUrgQQAIg==
178-----END EC PARAMETERS-----
179-----BEGIN EC PRIVATE KEY-----
180MIGkAgEBBDCfMNsN6miEE3rVyUPwElfiJSWaR5huPCzUenZOfJT04GAcQdWvEju3
181UM2lmBLIXpGgBwYFK4EEACKhZANiAARCjRMqJ85rzMC998X5z761nJ+xL3bkmGVq
182WvrJ51t5OxV0v25NsOgR82CANXUgvhVYs7vNFN+jxtb2aj6Xg+/2G/BNxkaFspIV
183CzgWkxiz7XE4lgUwX44FCXZM3+JeUbI=
184-----END EC PRIVATE KEY-----`
185	sftpPrivateKey = `-----BEGIN OPENSSH PRIVATE KEY-----
186b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
187QyNTUxOQAAACB+RB4yNTZz9mHOkawwUibNdemijVV3ErMeLxWUBlCN/gAAAJA7DjpfOw46
188XwAAAAtzc2gtZWQyNTUxOQAAACB+RB4yNTZz9mHOkawwUibNdemijVV3ErMeLxWUBlCN/g
189AAAEA0E24gi8ab/XRSvJ85TGZJMe6HVmwxSG4ExPfTMwwe2n5EHjI1NnP2Yc6RrDBSJs11
1906aKNVXcSsx4vFZQGUI3+AAAACW5pY29sYUBwMQECAwQ=
191-----END OPENSSH PRIVATE KEY-----`
192	sftpPkeyFingerprint = "SHA256:QVQ06XHZZbYZzqfrsZcf3Yozy2WTnqQPeLOkcJCdbP0"
193	redactedSecret      = "[**redacted**]"
194	osWindows           = "windows"
195)
196
197var (
198	defaultPerms       = []string{dataprovider.PermAny}
199	homeBasePath       string
200	backupsPath        string
201	credentialsPath    string
202	testServer         *httptest.Server
203	providerDriverName string
204	postConnectPath    string
205	preActionPath      string
206	lastResetCode      string
207)
208
209type fakeConnection struct {
210	*common.BaseConnection
211	command string
212}
213
214func (c *fakeConnection) Disconnect() error {
215	common.Connections.Remove(c.GetID())
216	return nil
217}
218
219func (c *fakeConnection) GetClientVersion() string {
220	return ""
221}
222
223func (c *fakeConnection) GetCommand() string {
224	return c.command
225}
226
227func (c *fakeConnection) GetLocalAddress() string {
228	return ""
229}
230
231func (c *fakeConnection) GetRemoteAddress() string {
232	return ""
233}
234
235type generateTOTPRequest struct {
236	ConfigName string `json:"config_name"`
237}
238
239type generateTOTPResponse struct {
240	ConfigName string `json:"config_name"`
241	Issuer     string `json:"issuer"`
242	Secret     string `json:"secret"`
243	QRCode     []byte `json:"qr_code"`
244}
245
246type validateTOTPRequest struct {
247	ConfigName string `json:"config_name"`
248	Passcode   string `json:"passcode"`
249	Secret     string `json:"secret"`
250}
251
252type recoveryCode struct {
253	Code string `json:"code"`
254	Used bool   `json:"used"`
255}
256
257func TestMain(m *testing.M) {
258	homeBasePath = os.TempDir()
259	logfilePath := filepath.Join(configDir, "sftpgo_api_test.log")
260	logger.InitLogger(logfilePath, 5, 1, 28, false, false, zerolog.DebugLevel)
261	os.Setenv("SFTPGO_COMMON__UPLOAD_MODE", "2")
262	os.Setenv("SFTPGO_DATA_PROVIDER__CREATE_DEFAULT_ADMIN", "1")
263	os.Setenv("SFTPGO_DEFAULT_ADMIN_USERNAME", "admin")
264	os.Setenv("SFTPGO_DEFAULT_ADMIN_PASSWORD", "password")
265	err := config.LoadConfig(configDir, "")
266	if err != nil {
267		logger.WarnToConsole("error loading configuration: %v", err)
268		os.Exit(1)
269	}
270	wdPath, err := os.Getwd()
271	if err != nil {
272		logger.WarnToConsole("error getting exe path: %v", err)
273		os.Exit(1)
274	}
275	pluginsConfig := []plugin.Config{
276		{
277			Type:     "eventsearcher",
278			Cmd:      filepath.Join(wdPath, "..", "tests", "eventsearcher", "eventsearcher"),
279			AutoMTLS: true,
280		},
281	}
282	if runtime.GOOS == osWindows {
283		pluginsConfig[0].Cmd += ".exe"
284	}
285	providerConf := config.GetProviderConf()
286	credentialsPath = filepath.Join(os.TempDir(), "test_credentials")
287	providerConf.CredentialsPath = credentialsPath
288	providerDriverName = providerConf.Driver
289	os.RemoveAll(credentialsPath) //nolint:errcheck
290	logger.InfoToConsole("Starting HTTPD tests, provider: %v", providerConf.Driver)
291
292	err = common.Initialize(config.GetCommonConfig())
293	if err != nil {
294		logger.WarnToConsole("error initializing common: %v", err)
295		os.Exit(1)
296	}
297
298	err = dataprovider.Initialize(providerConf, configDir, true)
299	if err != nil {
300		logger.WarnToConsole("error initializing data provider: %v", err)
301		os.Exit(1)
302	}
303
304	postConnectPath = filepath.Join(homeBasePath, "postconnect.sh")
305	preActionPath = filepath.Join(homeBasePath, "preaction.sh")
306
307	httpConfig := config.GetHTTPConfig()
308	httpConfig.Initialize(configDir) //nolint:errcheck
309	kmsConfig := config.GetKMSConfig()
310	err = kmsConfig.Initialize()
311	if err != nil {
312		logger.ErrorToConsole("error initializing kms: %v", err)
313		os.Exit(1)
314	}
315	mfaConfig := config.GetMFAConfig()
316	err = mfaConfig.Initialize()
317	if err != nil {
318		logger.ErrorToConsole("error initializing MFA: %v", err)
319		os.Exit(1)
320	}
321	err = plugin.Initialize(pluginsConfig, true)
322	if err != nil {
323		logger.ErrorToConsole("error initializing plugin: %v", err)
324		os.Exit(1)
325	}
326
327	httpdConf := config.GetHTTPDConfig()
328
329	httpdConf.Bindings[0].Port = 8081
330	httpdtest.SetBaseURL(httpBaseURL)
331	backupsPath = filepath.Join(os.TempDir(), "test_backups")
332	httpdConf.BackupsPath = backupsPath
333	err = os.MkdirAll(backupsPath, os.ModePerm)
334	if err != nil {
335		logger.ErrorToConsole("error creating backups path: %v", err)
336		os.Exit(1)
337	}
338
339	// required to test sftpfs
340	sftpdConf := config.GetSFTPDConfig()
341	sftpdConf.Bindings = []sftpd.Binding{
342		{
343			Port: 8022,
344		},
345	}
346	hostKeyPath := filepath.Join(os.TempDir(), "id_rsa")
347	sftpdConf.HostKeys = []string{hostKeyPath}
348
349	go func() {
350		if err := httpdConf.Initialize(configDir); err != nil {
351			logger.ErrorToConsole("could not start HTTP server: %v", err)
352			os.Exit(1)
353		}
354	}()
355
356	go func() {
357		if err := sftpdConf.Initialize(configDir); err != nil {
358			logger.ErrorToConsole("could not start SFTP server: %v", err)
359			os.Exit(1)
360		}
361	}()
362
363	startSMTPServer()
364
365	waitTCPListening(httpdConf.Bindings[0].GetAddress())
366	waitTCPListening(sftpdConf.Bindings[0].GetAddress())
367	httpd.ReloadCertificateMgr() //nolint:errcheck
368	// now start an https server
369	certPath := filepath.Join(os.TempDir(), "test.crt")
370	keyPath := filepath.Join(os.TempDir(), "test.key")
371	err = os.WriteFile(certPath, []byte(httpsCert), os.ModePerm)
372	if err != nil {
373		logger.ErrorToConsole("error writing HTTPS certificate: %v", err)
374		os.Exit(1)
375	}
376	err = os.WriteFile(keyPath, []byte(httpsKey), os.ModePerm)
377	if err != nil {
378		logger.ErrorToConsole("error writing HTTPS private key: %v", err)
379		os.Exit(1)
380	}
381	httpdConf.Bindings[0].Port = 8443
382	httpdConf.Bindings[0].EnableHTTPS = true
383	httpdConf.CertificateFile = certPath
384	httpdConf.CertificateKeyFile = keyPath
385	httpdConf.Bindings = append(httpdConf.Bindings, httpd.Binding{})
386
387	go func() {
388		if err := httpdConf.Initialize(configDir); err != nil {
389			logger.ErrorToConsole("could not start HTTPS server: %v", err)
390			os.Exit(1)
391		}
392	}()
393	waitTCPListening(httpdConf.Bindings[0].GetAddress())
394	httpd.ReloadCertificateMgr() //nolint:errcheck
395
396	testServer = httptest.NewServer(httpd.GetHTTPRouter())
397	defer testServer.Close()
398
399	exitCode := m.Run()
400	os.Remove(logfilePath)
401	os.RemoveAll(backupsPath)
402	os.RemoveAll(credentialsPath)
403	os.Remove(certPath)
404	os.Remove(keyPath)
405	os.Remove(hostKeyPath)
406	os.Remove(hostKeyPath + ".pub")
407	os.Remove(postConnectPath)
408	os.Remove(preActionPath)
409	os.Exit(exitCode)
410}
411
412func TestInitialization(t *testing.T) {
413	err := config.LoadConfig(configDir, "")
414	assert.NoError(t, err)
415	invalidFile := "invalid file"
416	httpdConf := config.GetHTTPDConfig()
417	defaultTemplatesPath := httpdConf.TemplatesPath
418	defaultStaticPath := httpdConf.StaticFilesPath
419	httpdConf.BackupsPath = backupsPath
420	httpdConf.CertificateFile = invalidFile
421	httpdConf.CertificateKeyFile = invalidFile
422	err = httpdConf.Initialize(configDir)
423	assert.Error(t, err)
424	httpdConf.CertificateFile = ""
425	httpdConf.CertificateKeyFile = ""
426	httpdConf.TemplatesPath = "."
427	err = httpdConf.Initialize(configDir)
428	assert.Error(t, err)
429	httpdConf = config.GetHTTPDConfig()
430	httpdConf.TemplatesPath = defaultTemplatesPath
431	httpdConf.BackupsPath = ".."
432	err = httpdConf.Initialize(configDir)
433	assert.Error(t, err)
434	httpdConf.BackupsPath = backupsPath
435	httpdConf.CertificateFile = invalidFile
436	httpdConf.CertificateKeyFile = invalidFile
437	httpdConf.StaticFilesPath = ""
438	httpdConf.TemplatesPath = ""
439	err = httpdConf.Initialize(configDir)
440	assert.Error(t, err)
441	httpdConf.StaticFilesPath = defaultStaticPath
442	httpdConf.TemplatesPath = defaultTemplatesPath
443	httpdConf.CertificateFile = filepath.Join(os.TempDir(), "test.crt")
444	httpdConf.CertificateKeyFile = filepath.Join(os.TempDir(), "test.key")
445	httpdConf.CACertificates = append(httpdConf.CACertificates, invalidFile)
446	err = httpdConf.Initialize(configDir)
447	assert.Error(t, err)
448	httpdConf.CACertificates = nil
449	httpdConf.CARevocationLists = append(httpdConf.CARevocationLists, invalidFile)
450	err = httpdConf.Initialize(configDir)
451	assert.Error(t, err)
452	httpdConf.CARevocationLists = nil
453	httpdConf.Bindings[0].ProxyAllowed = []string{"invalid ip/network"}
454	err = httpdConf.Initialize(configDir)
455	if assert.Error(t, err) {
456		assert.Contains(t, err.Error(), "is not a valid IP range")
457	}
458	httpdConf.Bindings[0].ProxyAllowed = nil
459	httpdConf.Bindings[0].EnableWebAdmin = false
460	httpdConf.Bindings[0].EnableWebClient = false
461	httpdConf.Bindings[0].Port = 8081
462	httpdConf.Bindings[0].EnableHTTPS = true
463	httpdConf.Bindings[0].ClientAuthType = 1
464	err = httpdConf.Initialize(configDir)
465	assert.Error(t, err)
466}
467
468func TestBasicUserHandling(t *testing.T) {
469	u := getTestUser()
470	u.Email = "user@user.com"
471	user, resp, err := httpdtest.AddUser(u, http.StatusCreated)
472	assert.NoError(t, err, string(resp))
473	user.MaxSessions = 10
474	user.QuotaSize = 4096
475	user.QuotaFiles = 2
476	user.UploadBandwidth = 128
477	user.DownloadBandwidth = 64
478	user.ExpirationDate = util.GetTimeAsMsSinceEpoch(time.Now())
479	user.AdditionalInfo = "some free text"
480	user.Filters.TLSUsername = sdk.TLSUsernameCN
481	user.Email = "user@example.net"
482	user.Filters.WebClient = append(user.Filters.WebClient, sdk.WebClientPubKeyChangeDisabled,
483		sdk.WebClientWriteDisabled)
484	originalUser := user
485	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
486	assert.NoError(t, err)
487	assert.Equal(t, originalUser.ID, user.ID)
488
489	user, _, err = httpdtest.GetUserByUsername(defaultUsername, http.StatusOK)
490	assert.NoError(t, err)
491
492	user.Email = "invalid@email"
493	_, body, err := httpdtest.UpdateUser(user, http.StatusBadRequest, "")
494	assert.NoError(t, err)
495	assert.Contains(t, string(body), "Validation error: email")
496
497	_, err = httpdtest.RemoveUser(user, http.StatusOK)
498	assert.NoError(t, err)
499}
500
501func TestUserTimestamps(t *testing.T) {
502	user, resp, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
503	assert.NoError(t, err, string(resp))
504	createdAt := user.CreatedAt
505	updatedAt := user.UpdatedAt
506	assert.Equal(t, int64(0), user.LastLogin)
507	assert.Greater(t, createdAt, int64(0))
508	assert.Greater(t, updatedAt, int64(0))
509	mappedPath := filepath.Join(os.TempDir(), "mapped_dir")
510	folderName := filepath.Base(mappedPath)
511	user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
512		BaseVirtualFolder: vfs.BaseVirtualFolder{
513			Name:       folderName,
514			MappedPath: mappedPath,
515		},
516		VirtualPath: "/vdir",
517	})
518	time.Sleep(10 * time.Millisecond)
519	user, resp, err = httpdtest.UpdateUser(user, http.StatusOK, "")
520	assert.NoError(t, err, string(resp))
521	assert.Equal(t, int64(0), user.LastLogin)
522	assert.Equal(t, createdAt, user.CreatedAt)
523	assert.Greater(t, user.UpdatedAt, updatedAt)
524	updatedAt = user.UpdatedAt
525	// after a folder update or delete the user updated_at field should change
526	folder, _, err := httpdtest.GetFolderByName(folderName, http.StatusOK)
527	assert.NoError(t, err)
528	assert.Len(t, folder.Users, 1)
529	time.Sleep(10 * time.Millisecond)
530	_, _, err = httpdtest.UpdateFolder(folder, http.StatusOK)
531	assert.NoError(t, err)
532	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
533	assert.NoError(t, err)
534	assert.Equal(t, int64(0), user.LastLogin)
535	assert.Equal(t, createdAt, user.CreatedAt)
536	assert.Greater(t, user.UpdatedAt, updatedAt)
537	updatedAt = user.UpdatedAt
538	time.Sleep(10 * time.Millisecond)
539	_, err = httpdtest.RemoveFolder(folder, http.StatusOK)
540	assert.NoError(t, err)
541	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
542	assert.NoError(t, err)
543	assert.Equal(t, int64(0), user.LastLogin)
544	assert.Equal(t, createdAt, user.CreatedAt)
545	assert.Greater(t, user.UpdatedAt, updatedAt)
546
547	err = os.RemoveAll(user.GetHomeDir())
548	assert.NoError(t, err)
549	_, err = httpdtest.RemoveUser(user, http.StatusOK)
550	assert.NoError(t, err)
551}
552
553func TestAdminTimestamps(t *testing.T) {
554	admin := getTestAdmin()
555	admin.Username = altAdminUsername
556	admin, _, err := httpdtest.AddAdmin(admin, http.StatusCreated)
557	assert.NoError(t, err)
558	createdAt := admin.CreatedAt
559	updatedAt := admin.UpdatedAt
560	assert.Equal(t, int64(0), admin.LastLogin)
561	assert.Greater(t, createdAt, int64(0))
562	assert.Greater(t, updatedAt, int64(0))
563	time.Sleep(10 * time.Millisecond)
564	admin, _, err = httpdtest.UpdateAdmin(admin, http.StatusOK)
565	assert.NoError(t, err)
566	assert.Equal(t, int64(0), admin.LastLogin)
567	assert.Equal(t, createdAt, admin.CreatedAt)
568	assert.Greater(t, admin.UpdatedAt, updatedAt)
569
570	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
571	assert.NoError(t, err)
572}
573
574func TestHTTPUserAuthentication(t *testing.T) {
575	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
576	assert.NoError(t, err)
577	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userTokenPath), nil)
578	assert.NoError(t, err)
579	req.SetBasicAuth(defaultUsername, defaultPassword)
580	c := httpclient.GetHTTPClient()
581	resp, err := c.Do(req)
582	c.CloseIdleConnections()
583	assert.NoError(t, err)
584	assert.Equal(t, http.StatusOK, resp.StatusCode)
585	responseHolder := make(map[string]interface{})
586	err = render.DecodeJSON(resp.Body, &responseHolder)
587	assert.NoError(t, err)
588	userToken := responseHolder["access_token"].(string)
589	assert.NotEmpty(t, userToken)
590	err = resp.Body.Close()
591	assert.NoError(t, err)
592	// login with wrong credentials
593	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userTokenPath), nil)
594	assert.NoError(t, err)
595	req.SetBasicAuth(defaultUsername, "")
596	resp, err = httpclient.GetHTTPClient().Do(req)
597	assert.NoError(t, err)
598	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
599	err = resp.Body.Close()
600	assert.NoError(t, err)
601
602	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userTokenPath), nil)
603	assert.NoError(t, err)
604	resp, err = httpclient.GetHTTPClient().Do(req)
605	assert.NoError(t, err)
606	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
607	err = resp.Body.Close()
608	assert.NoError(t, err)
609
610	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userTokenPath), nil)
611	assert.NoError(t, err)
612	req.SetBasicAuth(defaultUsername, "wrong pwd")
613	resp, err = httpclient.GetHTTPClient().Do(req)
614	assert.NoError(t, err)
615	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
616	respBody, err := io.ReadAll(resp.Body)
617	assert.NoError(t, err)
618	assert.Contains(t, string(respBody), "invalid credentials")
619	err = resp.Body.Close()
620	assert.NoError(t, err)
621
622	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userTokenPath), nil)
623	assert.NoError(t, err)
624	req.SetBasicAuth("wrong username", defaultPassword)
625	resp, err = httpclient.GetHTTPClient().Do(req)
626	assert.NoError(t, err)
627	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
628	respBody, err = io.ReadAll(resp.Body)
629	assert.NoError(t, err)
630	assert.Contains(t, string(respBody), "invalid credentials")
631	err = resp.Body.Close()
632	assert.NoError(t, err)
633
634	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, tokenPath), nil)
635	assert.NoError(t, err)
636	req.SetBasicAuth(defaultTokenAuthUser, defaultTokenAuthPass)
637	resp, err = httpclient.GetHTTPClient().Do(req)
638	assert.NoError(t, err)
639	assert.Equal(t, http.StatusOK, resp.StatusCode)
640	responseHolder = make(map[string]interface{})
641	err = render.DecodeJSON(resp.Body, &responseHolder)
642	assert.NoError(t, err)
643	adminToken := responseHolder["access_token"].(string)
644	assert.NotEmpty(t, adminToken)
645	err = resp.Body.Close()
646	assert.NoError(t, err)
647
648	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, versionPath), nil)
649	assert.NoError(t, err)
650	req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", adminToken))
651	resp, err = httpclient.GetHTTPClient().Do(req)
652	assert.NoError(t, err)
653	assert.Equal(t, http.StatusOK, resp.StatusCode)
654	err = resp.Body.Close()
655	assert.NoError(t, err)
656	// using the user token should not work
657	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, versionPath), nil)
658	assert.NoError(t, err)
659	req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", userToken))
660	resp, err = httpclient.GetHTTPClient().Do(req)
661	assert.NoError(t, err)
662	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
663	err = resp.Body.Close()
664	assert.NoError(t, err)
665
666	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userPublicKeysPath), nil)
667	assert.NoError(t, err)
668	req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", userToken))
669	resp, err = httpclient.GetHTTPClient().Do(req)
670	assert.NoError(t, err)
671	assert.Equal(t, http.StatusOK, resp.StatusCode)
672	err = resp.Body.Close()
673	assert.NoError(t, err)
674	// using the admin token should not work
675	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userPublicKeysPath), nil)
676	assert.NoError(t, err)
677	req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", adminToken))
678	resp, err = httpclient.GetHTTPClient().Do(req)
679	assert.NoError(t, err)
680	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
681	err = resp.Body.Close()
682	assert.NoError(t, err)
683
684	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userLogoutPath), nil)
685	assert.NoError(t, err)
686	req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", adminToken))
687	resp, err = httpclient.GetHTTPClient().Do(req)
688	assert.NoError(t, err)
689	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
690	err = resp.Body.Close()
691	assert.NoError(t, err)
692
693	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userLogoutPath), nil)
694	assert.NoError(t, err)
695	req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", userToken))
696	resp, err = httpclient.GetHTTPClient().Do(req)
697	assert.NoError(t, err)
698	assert.Equal(t, http.StatusOK, resp.StatusCode)
699	err = resp.Body.Close()
700	assert.NoError(t, err)
701
702	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userPublicKeysPath), nil)
703	assert.NoError(t, err)
704	req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", userToken))
705	resp, err = httpclient.GetHTTPClient().Do(req)
706	assert.NoError(t, err)
707	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
708	err = resp.Body.Close()
709	assert.NoError(t, err)
710
711	_, err = httpdtest.RemoveUser(user, http.StatusOK)
712	assert.NoError(t, err)
713	err = os.RemoveAll(user.GetHomeDir())
714	assert.NoError(t, err)
715}
716
717func TestPermMFADisabled(t *testing.T) {
718	u := getTestUser()
719	u.Filters.WebClient = []string{sdk.WebClientMFADisabled}
720	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
721	assert.NoError(t, err)
722
723	configName, _, secret, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], user.Username)
724	assert.NoError(t, err)
725	token, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
726	assert.NoError(t, err)
727	userTOTPConfig := sdk.TOTPConfig{
728		Enabled:    true,
729		ConfigName: configName,
730		Secret:     kms.NewPlainSecret(secret),
731		Protocols:  []string{common.ProtocolSSH},
732	}
733	asJSON, err := json.Marshal(userTOTPConfig)
734	assert.NoError(t, err)
735	req, err := http.NewRequest(http.MethodPost, userTOTPSavePath, bytes.NewBuffer(asJSON))
736	assert.NoError(t, err)
737	setBearerForReq(req, token)
738	rr := executeRequest(req)
739	checkResponseCode(t, http.StatusForbidden, rr) // MFA is disabled for this user
740
741	user.Filters.WebClient = []string{sdk.WebClientWriteDisabled}
742	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
743	assert.NoError(t, err)
744
745	token, err = getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
746	assert.NoError(t, err)
747	req, err = http.NewRequest(http.MethodPost, userTOTPSavePath, bytes.NewBuffer(asJSON))
748	assert.NoError(t, err)
749	setBearerForReq(req, token)
750	rr = executeRequest(req)
751	checkResponseCode(t, http.StatusOK, rr)
752	// now we cannot disable MFA for this user
753	user.Filters.WebClient = []string{sdk.WebClientMFADisabled}
754	_, resp, err := httpdtest.UpdateUser(user, http.StatusBadRequest, "")
755	assert.NoError(t, err)
756	assert.Contains(t, string(resp), "multi-factor authentication cannot be disabled for a user with an active configuration")
757
758	saveReq := make(map[string]bool)
759	saveReq["enabled"] = false
760	asJSON, err = json.Marshal(saveReq)
761	assert.NoError(t, err)
762	req, err = http.NewRequest(http.MethodPost, userTOTPSavePath, bytes.NewBuffer(asJSON))
763	assert.NoError(t, err)
764	setBearerForReq(req, token)
765	rr = executeRequest(req)
766	checkResponseCode(t, http.StatusOK, rr)
767
768	user.Filters.RecoveryCodes = []sdk.RecoveryCode{
769		{
770			Secret: kms.NewPlainSecret(util.GenerateUniqueID()),
771		},
772	}
773	user, resp, err = httpdtest.UpdateUser(user, http.StatusOK, "")
774	assert.NoError(t, err, string(resp))
775	assert.Contains(t, user.Filters.WebClient, sdk.WebClientMFADisabled)
776	assert.Len(t, user.Filters.RecoveryCodes, 12)
777
778	req, err = http.NewRequest(http.MethodGet, user2FARecoveryCodesPath, nil)
779	assert.NoError(t, err)
780	setBearerForReq(req, token)
781	rr = executeRequest(req)
782	checkResponseCode(t, http.StatusOK, rr)
783	var recCodes []recoveryCode
784	err = json.Unmarshal(rr.Body.Bytes(), &recCodes)
785	assert.NoError(t, err)
786	assert.Len(t, recCodes, 12)
787
788	_, err = httpdtest.RemoveUser(user, http.StatusOK)
789	assert.NoError(t, err)
790	err = os.RemoveAll(user.GetHomeDir())
791	assert.NoError(t, err)
792}
793
794func TestLoginUserAPITOTP(t *testing.T) {
795	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
796	assert.NoError(t, err)
797
798	configName, _, secret, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], user.Username)
799	assert.NoError(t, err)
800	token, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
801	assert.NoError(t, err)
802	userTOTPConfig := sdk.TOTPConfig{
803		Enabled:    true,
804		ConfigName: configName,
805		Secret:     kms.NewPlainSecret(secret),
806		Protocols:  []string{common.ProtocolHTTP},
807	}
808	asJSON, err := json.Marshal(userTOTPConfig)
809	assert.NoError(t, err)
810	req, err := http.NewRequest(http.MethodPost, userTOTPSavePath, bytes.NewBuffer(asJSON))
811	assert.NoError(t, err)
812	setBearerForReq(req, token)
813	rr := executeRequest(req)
814	checkResponseCode(t, http.StatusOK, rr)
815
816	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userTokenPath), nil)
817	assert.NoError(t, err)
818	req.SetBasicAuth(defaultUsername, defaultPassword)
819	resp, err := httpclient.GetHTTPClient().Do(req)
820	assert.NoError(t, err)
821	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
822	err = resp.Body.Close()
823	assert.NoError(t, err)
824
825	passcode, err := generateTOTPPasscode(secret)
826	assert.NoError(t, err)
827	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userTokenPath), nil)
828	assert.NoError(t, err)
829	req.Header.Set("X-SFTPGO-OTP", passcode)
830	req.SetBasicAuth(defaultUsername, defaultPassword)
831	resp, err = httpclient.GetHTTPClient().Do(req)
832	assert.NoError(t, err)
833	assert.Equal(t, http.StatusOK, resp.StatusCode)
834	responseHolder := make(map[string]interface{})
835	err = render.DecodeJSON(resp.Body, &responseHolder)
836	assert.NoError(t, err)
837	adminToken := responseHolder["access_token"].(string)
838	assert.NotEmpty(t, adminToken)
839	err = resp.Body.Close()
840	assert.NoError(t, err)
841
842	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userTokenPath), nil)
843	assert.NoError(t, err)
844	req.Header.Set("X-SFTPGO-OTP", passcode)
845	req.SetBasicAuth(defaultUsername, defaultPassword)
846	resp, err = httpclient.GetHTTPClient().Do(req)
847	assert.NoError(t, err)
848	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
849	err = resp.Body.Close()
850	assert.NoError(t, err)
851
852	_, err = httpdtest.RemoveUser(user, http.StatusOK)
853	assert.NoError(t, err)
854	err = os.RemoveAll(user.GetHomeDir())
855	assert.NoError(t, err)
856}
857
858func TestLoginAdminAPITOTP(t *testing.T) {
859	admin := getTestAdmin()
860	admin.Username = altAdminUsername
861	admin.Password = altAdminPassword
862	admin, _, err := httpdtest.AddAdmin(admin, http.StatusCreated)
863	assert.NoError(t, err)
864
865	configName, _, secret, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], admin.Username)
866	assert.NoError(t, err)
867	altToken, err := getJWTAPITokenFromTestServer(altAdminUsername, altAdminPassword)
868	assert.NoError(t, err)
869	adminTOTPConfig := dataprovider.TOTPConfig{
870		Enabled:    true,
871		ConfigName: configName,
872		Secret:     kms.NewPlainSecret(secret),
873	}
874	asJSON, err := json.Marshal(adminTOTPConfig)
875	assert.NoError(t, err)
876	req, err := http.NewRequest(http.MethodPost, adminTOTPSavePath, bytes.NewBuffer(asJSON))
877	assert.NoError(t, err)
878	setBearerForReq(req, altToken)
879	rr := executeRequest(req)
880	checkResponseCode(t, http.StatusOK, rr)
881
882	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, tokenPath), nil)
883	assert.NoError(t, err)
884	req.SetBasicAuth(altAdminUsername, altAdminPassword)
885	resp, err := httpclient.GetHTTPClient().Do(req)
886	assert.NoError(t, err)
887	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
888	err = resp.Body.Close()
889	assert.NoError(t, err)
890
891	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, tokenPath), nil)
892	assert.NoError(t, err)
893	req.Header.Set("X-SFTPGO-OTP", "passcode")
894	req.SetBasicAuth(altAdminUsername, altAdminPassword)
895	resp, err = httpclient.GetHTTPClient().Do(req)
896	assert.NoError(t, err)
897	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
898	err = resp.Body.Close()
899	assert.NoError(t, err)
900
901	passcode, err := generateTOTPPasscode(secret)
902	assert.NoError(t, err)
903	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, tokenPath), nil)
904	assert.NoError(t, err)
905	req.Header.Set("X-SFTPGO-OTP", passcode)
906	req.SetBasicAuth(altAdminUsername, altAdminPassword)
907	resp, err = httpclient.GetHTTPClient().Do(req)
908	assert.NoError(t, err)
909	assert.Equal(t, http.StatusOK, resp.StatusCode)
910	responseHolder := make(map[string]interface{})
911	err = render.DecodeJSON(resp.Body, &responseHolder)
912	assert.NoError(t, err)
913	adminToken := responseHolder["access_token"].(string)
914	assert.NotEmpty(t, adminToken)
915	err = resp.Body.Close()
916	assert.NoError(t, err)
917
918	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, versionPath), nil)
919	assert.NoError(t, err)
920	setBearerForReq(req, adminToken)
921	resp, err = httpclient.GetHTTPClient().Do(req)
922	assert.NoError(t, err)
923	assert.Equal(t, http.StatusOK, resp.StatusCode)
924	err = resp.Body.Close()
925	assert.NoError(t, err)
926
927	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
928	assert.NoError(t, err)
929}
930
931func TestHTTPStreamZipError(t *testing.T) {
932	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
933	assert.NoError(t, err)
934
935	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userTokenPath), nil)
936	assert.NoError(t, err)
937	req.SetBasicAuth(defaultUsername, defaultPassword)
938	resp, err := httpclient.GetHTTPClient().Do(req)
939	assert.NoError(t, err)
940	assert.Equal(t, http.StatusOK, resp.StatusCode)
941	responseHolder := make(map[string]interface{})
942	err = render.DecodeJSON(resp.Body, &responseHolder)
943	assert.NoError(t, err)
944	userToken := responseHolder["access_token"].(string)
945	assert.NotEmpty(t, userToken)
946	err = resp.Body.Close()
947	assert.NoError(t, err)
948
949	filesList := []string{"missing"}
950	asJSON, err := json.Marshal(filesList)
951	assert.NoError(t, err)
952	req, err = http.NewRequest(http.MethodPost, fmt.Sprintf("%v%v", httpBaseURL, userStreamZipPath), bytes.NewBuffer(asJSON))
953	assert.NoError(t, err)
954	req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", userToken))
955	resp, err = httpclient.GetHTTPClient().Do(req)
956	if !assert.Error(t, err) { // the connection will be closed
957		err = resp.Body.Close()
958		assert.NoError(t, err)
959	}
960	_, err = httpdtest.RemoveUser(user, http.StatusOK)
961	assert.NoError(t, err)
962	err = os.RemoveAll(user.GetHomeDir())
963	assert.NoError(t, err)
964}
965
966func TestBasicAdminHandling(t *testing.T) {
967	// we have one admin by default
968	admins, _, err := httpdtest.GetAdmins(0, 0, http.StatusOK)
969	assert.NoError(t, err)
970	assert.GreaterOrEqual(t, len(admins), 1)
971	admin := getTestAdmin()
972	// the default admin already exists
973	_, _, err = httpdtest.AddAdmin(admin, http.StatusInternalServerError)
974	assert.NoError(t, err)
975
976	admin.Username = altAdminUsername
977	admin, _, err = httpdtest.AddAdmin(admin, http.StatusCreated)
978	assert.NoError(t, err)
979
980	admin, _, err = httpdtest.GetAdminByUsername(admin.Username, http.StatusOK)
981	assert.NoError(t, err)
982
983	admin.AdditionalInfo = "test info"
984	admin, _, err = httpdtest.UpdateAdmin(admin, http.StatusOK)
985	assert.NoError(t, err)
986	assert.Equal(t, "test info", admin.AdditionalInfo)
987
988	admins, _, err = httpdtest.GetAdmins(1, 0, http.StatusOK)
989	assert.NoError(t, err)
990	assert.Len(t, admins, 1)
991	assert.NotEqual(t, admin.Username, admins[0].Username)
992
993	admins, _, err = httpdtest.GetAdmins(1, 1, http.StatusOK)
994	assert.NoError(t, err)
995	assert.Len(t, admins, 1)
996	assert.Equal(t, admin.Username, admins[0].Username)
997
998	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
999	assert.NoError(t, err)
1000
1001	_, err = httpdtest.RemoveAdmin(admin, http.StatusNotFound)
1002	assert.NoError(t, err)
1003
1004	admin, _, err = httpdtest.GetAdminByUsername(admin.Username+"123", http.StatusNotFound)
1005	assert.NoError(t, err)
1006
1007	admin.Username = defaultTokenAuthUser
1008	_, err = httpdtest.RemoveAdmin(admin, http.StatusBadRequest)
1009	assert.NoError(t, err)
1010}
1011
1012func TestChangeAdminPassword(t *testing.T) {
1013	_, err := httpdtest.ChangeAdminPassword("wrong", defaultTokenAuthPass, http.StatusBadRequest)
1014	assert.NoError(t, err)
1015	_, err = httpdtest.ChangeAdminPassword(defaultTokenAuthPass, defaultTokenAuthPass, http.StatusBadRequest)
1016	assert.NoError(t, err)
1017	_, err = httpdtest.ChangeAdminPassword(defaultTokenAuthPass, defaultTokenAuthPass+"1", http.StatusOK)
1018	assert.NoError(t, err)
1019	_, err = httpdtest.ChangeAdminPassword(defaultTokenAuthPass+"1", defaultTokenAuthPass, http.StatusUnauthorized)
1020	assert.NoError(t, err)
1021	admin, err := dataprovider.AdminExists(defaultTokenAuthUser)
1022	assert.NoError(t, err)
1023	admin.Password = defaultTokenAuthPass
1024	err = dataprovider.UpdateAdmin(&admin, "", "")
1025	assert.NoError(t, err)
1026}
1027
1028func TestPasswordValidations(t *testing.T) {
1029	if config.GetProviderConf().Driver == dataprovider.MemoryDataProviderName {
1030		t.Skip("this test is not supported with the memory provider")
1031	}
1032	err := dataprovider.Close()
1033	assert.NoError(t, err)
1034	err = config.LoadConfig(configDir, "")
1035	providerConf := config.GetProviderConf()
1036	assert.NoError(t, err)
1037	providerConf.PasswordValidation.Admins.MinEntropy = 50
1038	providerConf.PasswordValidation.Users.MinEntropy = 70
1039	err = dataprovider.Initialize(providerConf, configDir, true)
1040	assert.NoError(t, err)
1041
1042	a := getTestAdmin()
1043	a.Username = altAdminUsername
1044	a.Password = altAdminPassword
1045
1046	_, resp, err := httpdtest.AddAdmin(a, http.StatusBadRequest)
1047	assert.NoError(t, err, string(resp))
1048	assert.Contains(t, string(resp), "insecure password")
1049
1050	_, resp, err = httpdtest.AddUser(getTestUser(), http.StatusBadRequest)
1051	assert.NoError(t, err, string(resp))
1052	assert.Contains(t, string(resp), "insecure password")
1053
1054	err = dataprovider.Close()
1055	assert.NoError(t, err)
1056	err = config.LoadConfig(configDir, "")
1057	assert.NoError(t, err)
1058	providerConf = config.GetProviderConf()
1059	providerConf.CredentialsPath = credentialsPath
1060	err = os.RemoveAll(credentialsPath)
1061	assert.NoError(t, err)
1062	err = dataprovider.Initialize(providerConf, configDir, true)
1063	assert.NoError(t, err)
1064}
1065
1066func TestAdminPasswordHashing(t *testing.T) {
1067	if config.GetProviderConf().Driver == dataprovider.MemoryDataProviderName {
1068		t.Skip("this test is not supported with the memory provider")
1069	}
1070	err := dataprovider.Close()
1071	assert.NoError(t, err)
1072	err = config.LoadConfig(configDir, "")
1073	providerConf := config.GetProviderConf()
1074	assert.NoError(t, err)
1075	providerConf.PasswordHashing.Algo = dataprovider.HashingAlgoArgon2ID
1076	err = dataprovider.Initialize(providerConf, configDir, true)
1077	assert.NoError(t, err)
1078
1079	currentAdmin, err := dataprovider.AdminExists(defaultTokenAuthUser)
1080	assert.NoError(t, err)
1081	assert.True(t, strings.HasPrefix(currentAdmin.Password, "$2a$"))
1082
1083	a := getTestAdmin()
1084	a.Username = altAdminUsername
1085	a.Password = altAdminPassword
1086
1087	admin, _, err := httpdtest.AddAdmin(a, http.StatusCreated)
1088	assert.NoError(t, err)
1089
1090	newAdmin, err := dataprovider.AdminExists(altAdminUsername)
1091	assert.NoError(t, err)
1092	assert.True(t, strings.HasPrefix(newAdmin.Password, "$argon2id$"))
1093
1094	token, _, err := httpdtest.GetToken(altAdminUsername, altAdminPassword)
1095	assert.NoError(t, err)
1096	httpdtest.SetJWTToken(token)
1097	_, _, err = httpdtest.GetStatus(http.StatusOK)
1098	assert.NoError(t, err)
1099
1100	httpdtest.SetJWTToken("")
1101	_, _, err = httpdtest.GetStatus(http.StatusOK)
1102	assert.NoError(t, err)
1103
1104	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
1105	assert.NoError(t, err)
1106
1107	err = dataprovider.Close()
1108	assert.NoError(t, err)
1109	err = config.LoadConfig(configDir, "")
1110	assert.NoError(t, err)
1111	providerConf = config.GetProviderConf()
1112	providerConf.CredentialsPath = credentialsPath
1113	err = os.RemoveAll(credentialsPath)
1114	assert.NoError(t, err)
1115	err = dataprovider.Initialize(providerConf, configDir, true)
1116	assert.NoError(t, err)
1117}
1118
1119func TestAdminInvalidCredentials(t *testing.T) {
1120	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, tokenPath), nil)
1121	assert.NoError(t, err)
1122	req.SetBasicAuth(defaultTokenAuthUser, defaultTokenAuthPass)
1123	resp, err := httpclient.GetHTTPClient().Do(req)
1124	assert.NoError(t, err)
1125	assert.Equal(t, http.StatusOK, resp.StatusCode)
1126	err = resp.Body.Close()
1127	assert.NoError(t, err)
1128	// wrong password
1129	req.SetBasicAuth(defaultTokenAuthUser, "wrong pwd")
1130	resp, err = httpclient.GetHTTPClient().Do(req)
1131	assert.NoError(t, err)
1132	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
1133	responseHolder := make(map[string]interface{})
1134	err = render.DecodeJSON(resp.Body, &responseHolder)
1135	assert.NoError(t, err)
1136	err = resp.Body.Close()
1137	assert.NoError(t, err)
1138	assert.Equal(t, dataprovider.ErrInvalidCredentials.Error(), responseHolder["error"].(string))
1139	// wrong username
1140	req.SetBasicAuth("wrong username", defaultTokenAuthPass)
1141	resp, err = httpclient.GetHTTPClient().Do(req)
1142	assert.NoError(t, err)
1143	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
1144	responseHolder = make(map[string]interface{})
1145	err = render.DecodeJSON(resp.Body, &responseHolder)
1146	assert.NoError(t, err)
1147	err = resp.Body.Close()
1148	assert.NoError(t, err)
1149	assert.Equal(t, dataprovider.ErrInvalidCredentials.Error(), responseHolder["error"].(string))
1150}
1151
1152func TestAdminLastLogin(t *testing.T) {
1153	a := getTestAdmin()
1154	a.Username = altAdminUsername
1155	a.Password = altAdminPassword
1156
1157	admin, _, err := httpdtest.AddAdmin(a, http.StatusCreated)
1158	assert.NoError(t, err)
1159	assert.Equal(t, int64(0), admin.LastLogin)
1160
1161	_, _, err = httpdtest.GetToken(altAdminUsername, altAdminPassword)
1162	assert.NoError(t, err)
1163
1164	admin, _, err = httpdtest.GetAdminByUsername(altAdminUsername, http.StatusOK)
1165	assert.NoError(t, err)
1166	assert.Greater(t, admin.LastLogin, int64(0))
1167
1168	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
1169	assert.NoError(t, err)
1170}
1171
1172func TestAdminAllowList(t *testing.T) {
1173	a := getTestAdmin()
1174	a.Username = altAdminUsername
1175	a.Password = altAdminPassword
1176
1177	admin, _, err := httpdtest.AddAdmin(a, http.StatusCreated)
1178	assert.NoError(t, err)
1179
1180	token, _, err := httpdtest.GetToken(altAdminUsername, altAdminPassword)
1181	assert.NoError(t, err)
1182	httpdtest.SetJWTToken(token)
1183	_, _, err = httpdtest.GetStatus(http.StatusOK)
1184	assert.NoError(t, err)
1185
1186	httpdtest.SetJWTToken("")
1187
1188	admin.Password = altAdminPassword
1189	admin.Filters.AllowList = []string{"10.6.6.0/32"}
1190	admin, _, err = httpdtest.UpdateAdmin(admin, http.StatusOK)
1191	assert.NoError(t, err)
1192
1193	_, _, err = httpdtest.GetToken(altAdminUsername, altAdminPassword)
1194	assert.EqualError(t, err, "wrong status code: got 401 want 200")
1195
1196	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
1197	assert.NoError(t, err)
1198}
1199
1200func TestUserStatus(t *testing.T) {
1201	u := getTestUser()
1202	u.Status = 3
1203	_, _, err := httpdtest.AddUser(u, http.StatusBadRequest)
1204	assert.NoError(t, err)
1205	u.Status = 0
1206	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
1207	assert.NoError(t, err)
1208	user.Status = 2
1209	_, _, err = httpdtest.UpdateUser(user, http.StatusBadRequest, "")
1210	assert.NoError(t, err)
1211	user.Status = 1
1212	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
1213	assert.NoError(t, err)
1214	_, err = httpdtest.RemoveUser(user, http.StatusOK)
1215	assert.NoError(t, err)
1216}
1217
1218func TestUidGidLimits(t *testing.T) {
1219	u := getTestUser()
1220	u.UID = math.MaxInt32
1221	u.GID = math.MaxInt32
1222	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
1223	assert.NoError(t, err)
1224	assert.Equal(t, math.MaxInt32, user.GetUID())
1225	assert.Equal(t, math.MaxInt32, user.GetGID())
1226
1227	_, err = httpdtest.RemoveUser(user, http.StatusOK)
1228	assert.NoError(t, err)
1229}
1230
1231func TestAddUserNoCredentials(t *testing.T) {
1232	u := getTestUser()
1233	u.Password = ""
1234	u.PublicKeys = []string{}
1235	_, _, err := httpdtest.AddUser(u, http.StatusBadRequest)
1236	assert.NoError(t, err)
1237}
1238
1239func TestAddUserNoUsername(t *testing.T) {
1240	u := getTestUser()
1241	u.Username = ""
1242	_, _, err := httpdtest.AddUser(u, http.StatusBadRequest)
1243	assert.NoError(t, err)
1244}
1245
1246func TestAddUserNoHomeDir(t *testing.T) {
1247	u := getTestUser()
1248	u.HomeDir = ""
1249	_, _, err := httpdtest.AddUser(u, http.StatusBadRequest)
1250	assert.NoError(t, err)
1251}
1252
1253func TestAddUserInvalidHomeDir(t *testing.T) {
1254	u := getTestUser()
1255	u.HomeDir = "relative_path" //nolint:goconst
1256	_, _, err := httpdtest.AddUser(u, http.StatusBadRequest)
1257	assert.NoError(t, err)
1258}
1259
1260func TestAddUserNoPerms(t *testing.T) {
1261	u := getTestUser()
1262	u.Permissions = make(map[string][]string)
1263	_, _, err := httpdtest.AddUser(u, http.StatusBadRequest)
1264	assert.NoError(t, err)
1265	u.Permissions["/"] = []string{}
1266	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1267	assert.NoError(t, err)
1268}
1269
1270func TestAddUserInvalidEmail(t *testing.T) {
1271	u := getTestUser()
1272	u.Email = "invalid_email"
1273	_, body, err := httpdtest.AddUser(u, http.StatusBadRequest)
1274	assert.NoError(t, err)
1275	assert.Contains(t, string(body), "Validation error: email")
1276}
1277
1278func TestAddUserInvalidPerms(t *testing.T) {
1279	u := getTestUser()
1280	u.Permissions["/"] = []string{"invalidPerm"}
1281	_, _, err := httpdtest.AddUser(u, http.StatusBadRequest)
1282	assert.NoError(t, err)
1283	// permissions for root dir are mandatory
1284	u.Permissions["/"] = []string{}
1285	u.Permissions["/somedir"] = []string{dataprovider.PermAny}
1286	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1287	assert.NoError(t, err)
1288	u.Permissions["/"] = []string{dataprovider.PermAny}
1289	u.Permissions["/subdir/.."] = []string{dataprovider.PermAny}
1290	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1291	assert.NoError(t, err)
1292}
1293
1294func TestAddUserInvalidFilters(t *testing.T) {
1295	u := getTestUser()
1296	u.Filters.AllowedIP = []string{"192.168.1.0/24", "192.168.2.0"}
1297	_, _, err := httpdtest.AddUser(u, http.StatusBadRequest)
1298	assert.NoError(t, err)
1299	u.Filters.AllowedIP = []string{}
1300	u.Filters.DeniedIP = []string{"192.168.3.0/16", "invalid"}
1301	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1302	assert.NoError(t, err)
1303	u.Filters.DeniedIP = []string{}
1304	u.Filters.DeniedLoginMethods = []string{"invalid"}
1305	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1306	assert.NoError(t, err)
1307	u.Filters.DeniedLoginMethods = dataprovider.ValidLoginMethods
1308	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1309	assert.NoError(t, err)
1310	u.Filters.DeniedLoginMethods = []string{}
1311	u.Filters.FilePatterns = []sdk.PatternsFilter{
1312		{
1313			Path:            "relative",
1314			AllowedPatterns: []string{},
1315			DeniedPatterns:  []string{},
1316		},
1317	}
1318	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1319	assert.NoError(t, err)
1320	u.Filters.FilePatterns = []sdk.PatternsFilter{
1321		{
1322			Path:            "/",
1323			AllowedPatterns: []string{},
1324			DeniedPatterns:  []string{},
1325		},
1326	}
1327	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1328	assert.NoError(t, err)
1329	u.Filters.FilePatterns = []sdk.PatternsFilter{
1330		{
1331			Path:            "/subdir",
1332			AllowedPatterns: []string{"*.zip"},
1333			DeniedPatterns:  []string{},
1334		},
1335		{
1336			Path:            "/subdir",
1337			AllowedPatterns: []string{"*.rar"},
1338			DeniedPatterns:  []string{"*.jpg"},
1339		},
1340	}
1341	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1342	assert.NoError(t, err)
1343	u.Filters.FilePatterns = []sdk.PatternsFilter{
1344		{
1345			Path:            "relative",
1346			AllowedPatterns: []string{},
1347			DeniedPatterns:  []string{},
1348		},
1349	}
1350	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1351	assert.NoError(t, err)
1352	u.Filters.FilePatterns = []sdk.PatternsFilter{
1353		{
1354			Path:            "/",
1355			AllowedPatterns: []string{},
1356			DeniedPatterns:  []string{},
1357		},
1358	}
1359	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1360	assert.NoError(t, err)
1361	u.Filters.FilePatterns = []sdk.PatternsFilter{
1362		{
1363			Path:            "/subdir",
1364			AllowedPatterns: []string{"*.zip"},
1365		},
1366		{
1367			Path:            "/subdir",
1368			AllowedPatterns: []string{"*.rar"},
1369			DeniedPatterns:  []string{"*.jpg"},
1370		},
1371	}
1372	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1373	assert.NoError(t, err)
1374	u.Filters.FilePatterns = []sdk.PatternsFilter{
1375		{
1376			Path:            "/subdir",
1377			AllowedPatterns: []string{"a\\"},
1378		},
1379	}
1380	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1381	assert.NoError(t, err)
1382	u.Filters.DeniedProtocols = []string{"invalid"}
1383	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1384	assert.NoError(t, err)
1385	u.Filters.DeniedProtocols = dataprovider.ValidProtocols
1386	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1387	assert.NoError(t, err)
1388	u.Filters.DeniedProtocols = nil
1389	u.Filters.TLSUsername = "not a supported attribute"
1390	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1391	assert.NoError(t, err)
1392	u.Filters.TLSUsername = ""
1393	u.Filters.WebClient = []string{"not a valid web client options"}
1394	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1395	assert.NoError(t, err)
1396}
1397
1398func TestAddUserInvalidFsConfig(t *testing.T) {
1399	u := getTestUser()
1400	u.FsConfig.Provider = sdk.S3FilesystemProvider
1401	u.FsConfig.S3Config.Bucket = ""
1402	_, _, err := httpdtest.AddUser(u, http.StatusBadRequest)
1403	assert.NoError(t, err)
1404	err = os.RemoveAll(credentialsPath)
1405	assert.NoError(t, err)
1406	err = os.MkdirAll(credentialsPath, 0700)
1407	assert.NoError(t, err)
1408	u.FsConfig.S3Config.Bucket = "testbucket"
1409	u.FsConfig.S3Config.Region = "eu-west-1"     //nolint:goconst
1410	u.FsConfig.S3Config.AccessKey = "access-key" //nolint:goconst
1411	u.FsConfig.S3Config.AccessSecret = kms.NewSecret(kms.SecretStatusRedacted, "access-secret", "", "")
1412	u.FsConfig.S3Config.Endpoint = "http://127.0.0.1:9000/path?a=b"
1413	u.FsConfig.S3Config.StorageClass = "Standard" //nolint:goconst
1414	u.FsConfig.S3Config.KeyPrefix = "/adir/subdir/"
1415	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1416	assert.NoError(t, err)
1417	u.FsConfig.S3Config.AccessSecret.SetStatus(kms.SecretStatusPlain)
1418	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1419	assert.NoError(t, err)
1420	u.FsConfig.S3Config.KeyPrefix = ""
1421	u.FsConfig.S3Config.UploadPartSize = 3
1422	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1423	assert.NoError(t, err)
1424	u.FsConfig.S3Config.UploadPartSize = 5001
1425	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1426	assert.NoError(t, err)
1427	u.FsConfig.S3Config.UploadPartSize = 0
1428	u.FsConfig.S3Config.UploadConcurrency = -1
1429	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1430	assert.NoError(t, err)
1431	u.FsConfig.S3Config.UploadConcurrency = 0
1432	u.FsConfig.S3Config.DownloadPartSize = -1
1433	_, resp, err := httpdtest.AddUser(u, http.StatusBadRequest)
1434	if assert.NoError(t, err) {
1435		assert.Contains(t, string(resp), "download_part_size cannot be")
1436	}
1437	u.FsConfig.S3Config.DownloadPartSize = 5001
1438	_, resp, err = httpdtest.AddUser(u, http.StatusBadRequest)
1439	if assert.NoError(t, err) {
1440		assert.Contains(t, string(resp), "download_part_size cannot be")
1441	}
1442	u.FsConfig.S3Config.DownloadPartSize = 0
1443	u.FsConfig.S3Config.DownloadConcurrency = 100
1444	_, resp, err = httpdtest.AddUser(u, http.StatusBadRequest)
1445	if assert.NoError(t, err) {
1446		assert.Contains(t, string(resp), "invalid download concurrency")
1447	}
1448	u.FsConfig.S3Config.DownloadConcurrency = -1
1449	_, resp, err = httpdtest.AddUser(u, http.StatusBadRequest)
1450	if assert.NoError(t, err) {
1451		assert.Contains(t, string(resp), "invalid download concurrency")
1452	}
1453	u = getTestUser()
1454	u.FsConfig.Provider = sdk.GCSFilesystemProvider
1455	u.FsConfig.GCSConfig.Bucket = ""
1456	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1457	assert.NoError(t, err)
1458	u.FsConfig.GCSConfig.Bucket = "abucket"
1459	u.FsConfig.GCSConfig.StorageClass = "Standard"
1460	u.FsConfig.GCSConfig.KeyPrefix = "/somedir/subdir/"
1461	u.FsConfig.GCSConfig.Credentials = kms.NewSecret(kms.SecretStatusRedacted, "test", "", "") //nolint:goconst
1462	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1463	assert.NoError(t, err)
1464	u.FsConfig.GCSConfig.Credentials.SetStatus(kms.SecretStatusPlain)
1465	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1466	assert.NoError(t, err)
1467	u.FsConfig.GCSConfig.KeyPrefix = "somedir/subdir/" //nolint:goconst
1468	u.FsConfig.GCSConfig.Credentials = kms.NewEmptySecret()
1469	u.FsConfig.GCSConfig.AutomaticCredentials = 0
1470	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1471	assert.NoError(t, err)
1472	u.FsConfig.GCSConfig.Credentials = kms.NewSecret(kms.SecretStatusSecretBox, "invalid", "", "")
1473	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1474	assert.NoError(t, err)
1475
1476	u = getTestUser()
1477	u.FsConfig.Provider = sdk.AzureBlobFilesystemProvider
1478	u.FsConfig.AzBlobConfig.SASURL = kms.NewPlainSecret("http://foo\x7f.com/")
1479	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1480	assert.NoError(t, err)
1481	u.FsConfig.AzBlobConfig.SASURL = kms.NewSecret(kms.SecretStatusRedacted, "key", "", "")
1482	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1483	assert.NoError(t, err)
1484	u.FsConfig.AzBlobConfig.SASURL = kms.NewEmptySecret()
1485	u.FsConfig.AzBlobConfig.AccountName = "name"
1486	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1487	assert.NoError(t, err)
1488	u.FsConfig.AzBlobConfig.Container = "container"
1489	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1490	assert.NoError(t, err)
1491	u.FsConfig.AzBlobConfig.AccountKey = kms.NewSecret(kms.SecretStatusRedacted, "key", "", "")
1492	u.FsConfig.AzBlobConfig.KeyPrefix = "/amedir/subdir/"
1493	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1494	assert.NoError(t, err)
1495	u.FsConfig.AzBlobConfig.AccountKey.SetStatus(kms.SecretStatusPlain)
1496	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1497	assert.NoError(t, err)
1498	u.FsConfig.AzBlobConfig.KeyPrefix = "amedir/subdir/"
1499	u.FsConfig.AzBlobConfig.UploadPartSize = -1
1500	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1501	assert.NoError(t, err)
1502	u.FsConfig.AzBlobConfig.UploadPartSize = 101
1503	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1504	assert.NoError(t, err)
1505
1506	u = getTestUser()
1507	u.FsConfig.Provider = sdk.CryptedFilesystemProvider
1508	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1509	assert.NoError(t, err)
1510	u.FsConfig.CryptConfig.Passphrase = kms.NewSecret(kms.SecretStatusRedacted, "akey", "", "")
1511	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1512	assert.NoError(t, err)
1513	u = getTestUser()
1514	u.FsConfig.Provider = sdk.SFTPFilesystemProvider
1515	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1516	assert.NoError(t, err)
1517	u.FsConfig.SFTPConfig.Password = kms.NewSecret(kms.SecretStatusRedacted, "randompkey", "", "")
1518	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1519	assert.NoError(t, err)
1520	u.FsConfig.SFTPConfig.Password = kms.NewEmptySecret()
1521	u.FsConfig.SFTPConfig.PrivateKey = kms.NewSecret(kms.SecretStatusRedacted, "keyforpkey", "", "")
1522	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1523	assert.NoError(t, err)
1524	u.FsConfig.SFTPConfig.PrivateKey = kms.NewPlainSecret("pk")
1525	u.FsConfig.SFTPConfig.Endpoint = "127.1.1.1:22"
1526	u.FsConfig.SFTPConfig.Username = defaultUsername
1527	u.FsConfig.SFTPConfig.BufferSize = -1
1528	_, resp, err = httpdtest.AddUser(u, http.StatusBadRequest)
1529	if assert.NoError(t, err) {
1530		assert.Contains(t, string(resp), "invalid buffer_size")
1531	}
1532	u.FsConfig.SFTPConfig.BufferSize = 1000
1533	_, resp, err = httpdtest.AddUser(u, http.StatusBadRequest)
1534	if assert.NoError(t, err) {
1535		assert.Contains(t, string(resp), "invalid buffer_size")
1536	}
1537}
1538
1539func TestUserRedactedPassword(t *testing.T) {
1540	u := getTestUser()
1541	u.FsConfig.Provider = sdk.S3FilesystemProvider
1542	u.FsConfig.S3Config.Bucket = "b"
1543	u.FsConfig.S3Config.Region = "eu-west-1"
1544	u.FsConfig.S3Config.AccessKey = "access-key"
1545	u.FsConfig.S3Config.AccessSecret = kms.NewSecret(kms.SecretStatusRedacted, "access-secret", "", "")
1546	u.FsConfig.S3Config.Endpoint = "http://127.0.0.1:9000/path?k=m"
1547	u.FsConfig.S3Config.StorageClass = "Standard"
1548	u.FsConfig.S3Config.ACL = "bucket-owner-full-control"
1549	_, resp, err := httpdtest.AddUser(u, http.StatusBadRequest)
1550	assert.NoError(t, err, string(resp))
1551	assert.Contains(t, string(resp), "cannot save a user with a redacted secret")
1552	err = dataprovider.AddUser(&u, "", "")
1553	if assert.Error(t, err) {
1554		assert.Contains(t, err.Error(), "cannot save a user with a redacted secret")
1555	}
1556	u.FsConfig.S3Config.AccessSecret = kms.NewPlainSecret("secret")
1557	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
1558	assert.NoError(t, err)
1559
1560	folderName := "folderName"
1561	vfolder := vfs.VirtualFolder{
1562		BaseVirtualFolder: vfs.BaseVirtualFolder{
1563			Name:       folderName,
1564			MappedPath: filepath.Join(os.TempDir(), "crypted"),
1565			FsConfig: vfs.Filesystem{
1566				Provider: sdk.CryptedFilesystemProvider,
1567				CryptConfig: vfs.CryptFsConfig{
1568					CryptFsConfig: sdk.CryptFsConfig{
1569						Passphrase: kms.NewSecret(kms.SecretStatusRedacted, "crypted-secret", "", ""),
1570					},
1571				},
1572			},
1573		},
1574		VirtualPath: "/avpath",
1575	}
1576
1577	user.Password = defaultPassword
1578	user.VirtualFolders = append(user.VirtualFolders, vfolder)
1579	err = dataprovider.UpdateUser(&user, "", "")
1580	if assert.Error(t, err) {
1581		assert.Contains(t, err.Error(), "cannot save a user with a redacted secret")
1582	}
1583
1584	_, err = httpdtest.RemoveUser(user, http.StatusOK)
1585	assert.NoError(t, err)
1586}
1587
1588func TestUserType(t *testing.T) {
1589	u := getTestUser()
1590	u.Filters.UserType = string(sdk.UserTypeLDAP)
1591	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
1592	assert.NoError(t, err)
1593	assert.Equal(t, string(sdk.UserTypeLDAP), user.Filters.UserType)
1594	user.Filters.UserType = string(sdk.UserTypeOS)
1595	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
1596	assert.NoError(t, err)
1597	assert.Equal(t, string(sdk.UserTypeOS), user.Filters.UserType)
1598
1599	_, err = httpdtest.RemoveUser(user, http.StatusOK)
1600	assert.NoError(t, err)
1601}
1602
1603func TestRetentionAPI(t *testing.T) {
1604	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
1605	assert.NoError(t, err)
1606
1607	checks, _, err := httpdtest.GetRetentionChecks(http.StatusOK)
1608	assert.NoError(t, err)
1609	assert.Len(t, checks, 0)
1610
1611	localFilePath := filepath.Join(user.HomeDir, "testdir", "testfile")
1612	err = os.MkdirAll(filepath.Dir(localFilePath), os.ModePerm)
1613	assert.NoError(t, err)
1614	err = os.WriteFile(localFilePath, []byte("test data"), os.ModePerm)
1615	assert.NoError(t, err)
1616
1617	folderRetention := []common.FolderRetention{
1618		{
1619			Path:            "/",
1620			Retention:       0,
1621			DeleteEmptyDirs: true,
1622		},
1623	}
1624
1625	_, err = httpdtest.StartRetentionCheck(altAdminUsername, folderRetention, http.StatusNotFound)
1626	assert.NoError(t, err)
1627
1628	resp, err := httpdtest.StartRetentionCheck(user.Username, folderRetention, http.StatusBadRequest)
1629	assert.NoError(t, err)
1630	assert.Contains(t, string(resp), "Invalid retention check")
1631
1632	folderRetention[0].Retention = 24
1633	_, err = httpdtest.StartRetentionCheck(user.Username, folderRetention, http.StatusAccepted)
1634	assert.NoError(t, err)
1635
1636	assert.Eventually(t, func() bool {
1637		return len(common.RetentionChecks.Get()) == 0
1638	}, 1000*time.Millisecond, 50*time.Millisecond)
1639
1640	assert.FileExists(t, localFilePath)
1641
1642	err = os.Chtimes(localFilePath, time.Now().Add(-48*time.Hour), time.Now().Add(-48*time.Hour))
1643	assert.NoError(t, err)
1644
1645	_, err = httpdtest.StartRetentionCheck(user.Username, folderRetention, http.StatusAccepted)
1646	assert.NoError(t, err)
1647
1648	assert.Eventually(t, func() bool {
1649		return len(common.RetentionChecks.Get()) == 0
1650	}, 1000*time.Millisecond, 50*time.Millisecond)
1651
1652	assert.NoFileExists(t, localFilePath)
1653	assert.NoDirExists(t, filepath.Dir(localFilePath))
1654
1655	check := common.RetentionCheck{
1656		Folders: folderRetention,
1657	}
1658	c := common.RetentionChecks.Add(check, &user)
1659	assert.NotNil(t, c)
1660
1661	_, err = httpdtest.StartRetentionCheck(user.Username, folderRetention, http.StatusConflict)
1662	assert.NoError(t, err)
1663
1664	c.Start()
1665	assert.Len(t, common.RetentionChecks.Get(), 0)
1666
1667	admin := getTestAdmin()
1668	admin.Username = altAdminUsername
1669	admin.Password = altAdminPassword
1670	admin, _, err = httpdtest.AddAdmin(admin, http.StatusCreated)
1671	assert.NoError(t, err)
1672
1673	token, err := getJWTAPITokenFromTestServer(altAdminUsername, altAdminPassword)
1674	assert.NoError(t, err)
1675	req, _ := http.NewRequest(http.MethodPost, retentionBasePath+"/"+user.Username+"/check",
1676		bytes.NewBuffer([]byte("invalid json")))
1677	setBearerForReq(req, token)
1678	rr := executeRequest(req)
1679	checkResponseCode(t, http.StatusBadRequest, rr)
1680
1681	asJSON, err := json.Marshal(folderRetention)
1682	assert.NoError(t, err)
1683	req, _ = http.NewRequest(http.MethodPost, retentionBasePath+"/"+user.Username+"/check?notifications=Email,",
1684		bytes.NewBuffer(asJSON))
1685	setBearerForReq(req, token)
1686	rr = executeRequest(req)
1687	checkResponseCode(t, http.StatusBadRequest, rr)
1688	assert.Contains(t, rr.Body.String(), "to notify results via email")
1689
1690	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
1691	assert.NoError(t, err)
1692	req, _ = http.NewRequest(http.MethodPost, retentionBasePath+"/"+user.Username+"/check?notifications=Email",
1693		bytes.NewBuffer(asJSON))
1694	setBearerForReq(req, token)
1695	rr = executeRequest(req)
1696	checkResponseCode(t, http.StatusNotFound, rr)
1697
1698	_, err = httpdtest.RemoveUser(user, http.StatusOK)
1699	assert.NoError(t, err)
1700	err = os.RemoveAll(user.GetHomeDir())
1701	assert.NoError(t, err)
1702}
1703
1704func TestAddUserInvalidVirtualFolders(t *testing.T) {
1705	u := getTestUser()
1706	folderName := "fname"
1707	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
1708		BaseVirtualFolder: vfs.BaseVirtualFolder{
1709			MappedPath: filepath.Join(os.TempDir(), "mapped_dir"),
1710			Name:       folderName,
1711		},
1712		VirtualPath: "vdir", // invalid
1713	})
1714	_, _, err := httpdtest.AddUser(u, http.StatusBadRequest)
1715	assert.NoError(t, err)
1716	u.VirtualFolders = nil
1717	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
1718		BaseVirtualFolder: vfs.BaseVirtualFolder{
1719			MappedPath: filepath.Join(os.TempDir(), "mapped_dir"),
1720			Name:       folderName,
1721		},
1722		VirtualPath: "/", // invalid
1723	})
1724	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1725	assert.NoError(t, err)
1726	u.VirtualFolders = nil
1727	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
1728		BaseVirtualFolder: vfs.BaseVirtualFolder{
1729			MappedPath: filepath.Join(u.GetHomeDir(), "mapped_dir"), // invalid, inside home dir
1730			Name:       folderName,
1731		},
1732		VirtualPath: "/vdir",
1733	})
1734	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1735	assert.NoError(t, err)
1736	u.VirtualFolders = nil
1737	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
1738		BaseVirtualFolder: vfs.BaseVirtualFolder{
1739			MappedPath: u.GetHomeDir(), // invalid
1740			Name:       folderName,
1741		},
1742		VirtualPath: "/vdir",
1743	})
1744	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1745	assert.NoError(t, err)
1746	u.VirtualFolders = nil
1747	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
1748		BaseVirtualFolder: vfs.BaseVirtualFolder{
1749			MappedPath: filepath.Join(u.GetHomeDir(), ".."), // invalid, contains home dir
1750			Name:       "tmp",
1751		},
1752		VirtualPath: "/vdir",
1753	})
1754	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1755	assert.NoError(t, err)
1756	u.VirtualFolders = nil
1757	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
1758		BaseVirtualFolder: vfs.BaseVirtualFolder{
1759			MappedPath: filepath.Join(os.TempDir(), "mapped_dir"),
1760			Name:       folderName,
1761		},
1762		VirtualPath: "/vdir",
1763	})
1764	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
1765		BaseVirtualFolder: vfs.BaseVirtualFolder{
1766			MappedPath: filepath.Join(os.TempDir(), "mapped_dir1"),
1767			Name:       folderName + "1",
1768		},
1769		VirtualPath: "/vdir", // invalid, already defined
1770	})
1771	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1772	assert.NoError(t, err)
1773	u.VirtualFolders = nil
1774	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
1775		BaseVirtualFolder: vfs.BaseVirtualFolder{
1776			MappedPath: filepath.Join(os.TempDir(), "mapped_dir"),
1777			Name:       folderName,
1778		},
1779		VirtualPath: "/vdir1",
1780	})
1781	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
1782		BaseVirtualFolder: vfs.BaseVirtualFolder{
1783			MappedPath: filepath.Join(os.TempDir(), "mapped_dir"), // invalid, already defined
1784			Name:       folderName,
1785		},
1786		VirtualPath: "/vdir2",
1787	})
1788	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1789	assert.NoError(t, err)
1790	u.VirtualFolders = nil
1791	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
1792		BaseVirtualFolder: vfs.BaseVirtualFolder{
1793			MappedPath: filepath.Join(os.TempDir(), "mapped_dir1"),
1794			Name:       folderName + "1",
1795		},
1796		VirtualPath: "/vdir1/",
1797		QuotaSize:   -1,
1798		QuotaFiles:  1, // invvalid, we cannot have -1 and > 0
1799	})
1800	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1801	assert.NoError(t, err)
1802	u.VirtualFolders = nil
1803	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
1804		BaseVirtualFolder: vfs.BaseVirtualFolder{
1805			MappedPath: filepath.Join(os.TempDir(), "mapped_dir1"),
1806			Name:       folderName + "1",
1807		},
1808		VirtualPath: "/vdir1/",
1809		QuotaSize:   1,
1810		QuotaFiles:  -1,
1811	})
1812	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1813	assert.NoError(t, err)
1814	u.VirtualFolders = nil
1815	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
1816		BaseVirtualFolder: vfs.BaseVirtualFolder{
1817			MappedPath: filepath.Join(os.TempDir(), "mapped_dir1"),
1818			Name:       folderName + "1",
1819		},
1820		VirtualPath: "/vdir1/",
1821		QuotaSize:   -2, // invalid
1822		QuotaFiles:  0,
1823	})
1824	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1825	assert.NoError(t, err)
1826	u.VirtualFolders = nil
1827	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
1828		BaseVirtualFolder: vfs.BaseVirtualFolder{
1829			MappedPath: filepath.Join(os.TempDir(), "mapped_dir1"),
1830			Name:       folderName + "1",
1831		},
1832		VirtualPath: "/vdir1/",
1833		QuotaSize:   0,
1834		QuotaFiles:  -2, // invalid
1835	})
1836	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1837	assert.NoError(t, err)
1838	u.VirtualFolders = nil
1839	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
1840		BaseVirtualFolder: vfs.BaseVirtualFolder{
1841			MappedPath: filepath.Join(os.TempDir(), "mapped_dir"),
1842		},
1843		VirtualPath: "/vdir1",
1844	})
1845	// folder name is mandatory
1846	_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
1847	assert.NoError(t, err)
1848}
1849
1850func TestUserPublicKey(t *testing.T) {
1851	u := getTestUser()
1852	u.Password = ""
1853	invalidPubKey := "invalid"
1854	u.PublicKeys = []string{invalidPubKey}
1855	_, _, err := httpdtest.AddUser(u, http.StatusBadRequest)
1856	assert.NoError(t, err)
1857	u.PublicKeys = []string{testPubKey}
1858	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
1859	assert.NoError(t, err)
1860
1861	dbUser, err := dataprovider.UserExists(u.Username)
1862	assert.NoError(t, err)
1863	assert.Empty(t, dbUser.Password)
1864	assert.False(t, dbUser.IsPasswordHashed())
1865
1866	user.PublicKeys = []string{testPubKey, invalidPubKey}
1867	_, _, err = httpdtest.UpdateUser(user, http.StatusBadRequest, "")
1868	assert.NoError(t, err)
1869	user.PublicKeys = []string{testPubKey, testPubKey, testPubKey}
1870	user.Password = defaultPassword
1871	_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
1872	assert.NoError(t, err)
1873
1874	dbUser, err = dataprovider.UserExists(u.Username)
1875	assert.NoError(t, err)
1876	assert.NotEmpty(t, dbUser.Password)
1877	assert.True(t, dbUser.IsPasswordHashed())
1878
1879	_, err = httpdtest.RemoveUser(user, http.StatusOK)
1880	assert.NoError(t, err)
1881}
1882
1883func TestUpdateUserEmptyPassword(t *testing.T) {
1884	u := getTestUser()
1885	u.PublicKeys = []string{testPubKey}
1886	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
1887	assert.NoError(t, err)
1888
1889	// the password is not empty
1890	dbUser, err := dataprovider.UserExists(u.Username)
1891	assert.NoError(t, err)
1892	assert.NotEmpty(t, dbUser.Password)
1893	assert.True(t, dbUser.IsPasswordHashed())
1894	// now update the user and set an empty password
1895	customUser := make(map[string]interface{})
1896	customUser["password"] = ""
1897	asJSON, err := json.Marshal(customUser)
1898	assert.NoError(t, err)
1899	userNoPwd, _, err := httpdtest.UpdateUserWithJSON(user, http.StatusOK, "", asJSON)
1900	assert.NoError(t, err)
1901	assert.Equal(t, user.Password, userNoPwd.Password) // the password is hidden
1902	// check the password within the data provider
1903	dbUser, err = dataprovider.UserExists(u.Username)
1904	assert.NoError(t, err)
1905	assert.Empty(t, dbUser.Password)
1906	assert.False(t, dbUser.IsPasswordHashed())
1907
1908	_, err = httpdtest.RemoveUser(user, http.StatusOK)
1909	assert.NoError(t, err)
1910}
1911
1912func TestUpdateUser(t *testing.T) {
1913	u := getTestUser()
1914	u.UsedQuotaFiles = 1
1915	u.UsedQuotaSize = 2
1916	u.Filters.TLSUsername = sdk.TLSUsernameCN
1917	u.Filters.Hooks.CheckPasswordDisabled = true
1918	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
1919	assert.NoError(t, err)
1920	assert.Equal(t, 0, user.UsedQuotaFiles)
1921	assert.Equal(t, int64(0), user.UsedQuotaSize)
1922	user.HomeDir = filepath.Join(homeBasePath, "testmod")
1923	user.UID = 33
1924	user.GID = 101
1925	user.MaxSessions = 10
1926	user.QuotaSize = 4096
1927	user.QuotaFiles = 2
1928	user.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermDelete, dataprovider.PermDownload}
1929	user.Permissions["/subdir"] = []string{dataprovider.PermListItems, dataprovider.PermUpload}
1930	user.Filters.AllowedIP = []string{"192.168.1.0/24", "192.168.2.0/24"}
1931	user.Filters.DeniedIP = []string{"192.168.3.0/24", "192.168.4.0/24"}
1932	user.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodPassword}
1933	user.Filters.DeniedProtocols = []string{common.ProtocolWebDAV}
1934	user.Filters.TLSUsername = sdk.TLSUsernameNone
1935	user.Filters.Hooks.ExternalAuthDisabled = true
1936	user.Filters.Hooks.PreLoginDisabled = true
1937	user.Filters.Hooks.CheckPasswordDisabled = false
1938	user.Filters.DisableFsChecks = true
1939	user.Filters.FilePatterns = append(user.Filters.FilePatterns, sdk.PatternsFilter{
1940		Path:            "/subdir",
1941		AllowedPatterns: []string{"*.zip", "*.rar"},
1942		DeniedPatterns:  []string{"*.jpg", "*.png"},
1943	})
1944	user.Filters.MaxUploadFileSize = 4096
1945	user.UploadBandwidth = 1024
1946	user.DownloadBandwidth = 512
1947	user.VirtualFolders = nil
1948	mappedPath1 := filepath.Join(os.TempDir(), "mapped_dir1")
1949	mappedPath2 := filepath.Join(os.TempDir(), "mapped_dir2")
1950	folderName1 := filepath.Base(mappedPath1)
1951	folderName2 := filepath.Base(mappedPath2)
1952	user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
1953		BaseVirtualFolder: vfs.BaseVirtualFolder{
1954			Name:       folderName1,
1955			MappedPath: mappedPath1,
1956		},
1957		VirtualPath: "/vdir1",
1958	})
1959	user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
1960		BaseVirtualFolder: vfs.BaseVirtualFolder{
1961			Name:       folderName2,
1962			MappedPath: mappedPath2,
1963		},
1964		VirtualPath: "/vdir12/subdir",
1965		QuotaSize:   123,
1966		QuotaFiles:  2,
1967	})
1968	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
1969	assert.NoError(t, err)
1970
1971	_, _, err = httpdtest.UpdateUser(user, http.StatusBadRequest, "invalid")
1972	assert.NoError(t, err)
1973	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "0")
1974	assert.NoError(t, err)
1975	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "1")
1976	assert.NoError(t, err)
1977	user.Permissions["/subdir"] = []string{}
1978	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
1979	assert.NoError(t, err)
1980	assert.Len(t, user.Permissions["/subdir"], 0)
1981	assert.Len(t, user.VirtualFolders, 2)
1982	for _, folder := range user.VirtualFolders {
1983		assert.Greater(t, folder.ID, int64(0))
1984		if folder.VirtualPath == "/vdir12/subdir" {
1985			assert.Equal(t, int64(123), folder.QuotaSize)
1986			assert.Equal(t, 2, folder.QuotaFiles)
1987		}
1988	}
1989	folder, _, err := httpdtest.GetFolderByName(folderName1, http.StatusOK)
1990	assert.NoError(t, err)
1991	assert.Len(t, folder.Users, 1)
1992	assert.Contains(t, folder.Users, user.Username)
1993
1994	_, err = httpdtest.RemoveUser(user, http.StatusOK)
1995	assert.NoError(t, err)
1996	// removing the user must remove folder mapping
1997	folder, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK)
1998	assert.NoError(t, err)
1999	assert.Len(t, folder.Users, 0)
2000	_, err = httpdtest.RemoveFolder(folder, http.StatusOK)
2001	assert.NoError(t, err)
2002	folder, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK)
2003	assert.NoError(t, err)
2004	assert.Len(t, folder.Users, 0)
2005	_, err = httpdtest.RemoveFolder(folder, http.StatusOK)
2006	assert.NoError(t, err)
2007}
2008
2009func TestUpdateUserQuotaUsage(t *testing.T) {
2010	u := getTestUser()
2011	usedQuotaFiles := 1
2012	usedQuotaSize := int64(65535)
2013	u.UsedQuotaFiles = usedQuotaFiles
2014	u.UsedQuotaSize = usedQuotaSize
2015	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
2016	assert.NoError(t, err)
2017	_, err = httpdtest.UpdateQuotaUsage(u, "invalid_mode", http.StatusBadRequest)
2018	assert.NoError(t, err)
2019	_, err = httpdtest.UpdateQuotaUsage(u, "", http.StatusOK)
2020	assert.NoError(t, err)
2021	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
2022	assert.NoError(t, err)
2023	assert.Equal(t, usedQuotaFiles, user.UsedQuotaFiles)
2024	assert.Equal(t, usedQuotaSize, user.UsedQuotaSize)
2025	_, err = httpdtest.UpdateQuotaUsage(u, "add", http.StatusBadRequest)
2026	assert.NoError(t, err, "user has no quota restrictions add mode should fail")
2027	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
2028	assert.NoError(t, err)
2029	assert.Equal(t, usedQuotaFiles, user.UsedQuotaFiles)
2030	assert.Equal(t, usedQuotaSize, user.UsedQuotaSize)
2031	user.QuotaFiles = 100
2032	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
2033	assert.NoError(t, err)
2034	_, err = httpdtest.UpdateQuotaUsage(u, "add", http.StatusOK)
2035	assert.NoError(t, err)
2036	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
2037	assert.NoError(t, err)
2038	assert.Equal(t, 2*usedQuotaFiles, user.UsedQuotaFiles)
2039	assert.Equal(t, 2*usedQuotaSize, user.UsedQuotaSize)
2040	u.UsedQuotaFiles = -1
2041	_, err = httpdtest.UpdateQuotaUsage(u, "", http.StatusBadRequest)
2042	assert.NoError(t, err)
2043	u.UsedQuotaFiles = usedQuotaFiles
2044	u.Username = u.Username + "1"
2045	_, err = httpdtest.UpdateQuotaUsage(u, "", http.StatusNotFound)
2046	assert.NoError(t, err)
2047	_, err = httpdtest.RemoveUser(user, http.StatusOK)
2048	assert.NoError(t, err)
2049}
2050
2051func TestUserFolderMapping(t *testing.T) {
2052	mappedPath1 := filepath.Join(os.TempDir(), "mapped_dir1")
2053	mappedPath2 := filepath.Join(os.TempDir(), "mapped_dir2")
2054	folderName1 := filepath.Base(mappedPath1)
2055	folderName2 := filepath.Base(mappedPath2)
2056	u1 := getTestUser()
2057	u1.VirtualFolders = append(u1.VirtualFolders, vfs.VirtualFolder{
2058		BaseVirtualFolder: vfs.BaseVirtualFolder{
2059			Name:            folderName1,
2060			MappedPath:      mappedPath1,
2061			UsedQuotaFiles:  2,
2062			UsedQuotaSize:   123,
2063			LastQuotaUpdate: 456,
2064		},
2065		VirtualPath: "/vdir",
2066		QuotaSize:   -1,
2067		QuotaFiles:  -1,
2068	})
2069	user1, _, err := httpdtest.AddUser(u1, http.StatusCreated)
2070	assert.NoError(t, err)
2071	// virtual folder must be auto created
2072	folder, _, err := httpdtest.GetFolderByName(folderName1, http.StatusOK)
2073	assert.NoError(t, err)
2074	assert.Len(t, folder.Users, 1)
2075	assert.Contains(t, folder.Users, user1.Username)
2076	assert.Equal(t, 0, folder.UsedQuotaFiles)
2077	assert.Equal(t, int64(0), folder.UsedQuotaSize)
2078	assert.Equal(t, int64(0), folder.LastQuotaUpdate)
2079	assert.Equal(t, 0, user1.VirtualFolders[0].UsedQuotaFiles)
2080	assert.Equal(t, int64(0), user1.VirtualFolders[0].UsedQuotaSize)
2081	assert.Equal(t, int64(0), user1.VirtualFolders[0].LastQuotaUpdate)
2082
2083	u2 := getTestUser()
2084	u2.Username = defaultUsername + "2"
2085	u2.VirtualFolders = append(u2.VirtualFolders, vfs.VirtualFolder{
2086		BaseVirtualFolder: vfs.BaseVirtualFolder{
2087			Name:       folderName1,
2088			MappedPath: mappedPath1,
2089		},
2090		VirtualPath: "/vdir1",
2091		QuotaSize:   0,
2092		QuotaFiles:  0,
2093	})
2094	u2.VirtualFolders = append(u2.VirtualFolders, vfs.VirtualFolder{
2095		BaseVirtualFolder: vfs.BaseVirtualFolder{
2096			Name:       folderName2,
2097			MappedPath: mappedPath2,
2098		},
2099		VirtualPath: "/vdir2",
2100		QuotaSize:   -1,
2101		QuotaFiles:  -1,
2102	})
2103	user2, _, err := httpdtest.AddUser(u2, http.StatusCreated)
2104	assert.NoError(t, err)
2105	folder, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK)
2106	assert.NoError(t, err)
2107	assert.Len(t, folder.Users, 1)
2108	assert.Contains(t, folder.Users, user2.Username)
2109	folder, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK)
2110	assert.NoError(t, err)
2111	assert.Len(t, folder.Users, 2)
2112	assert.Contains(t, folder.Users, user1.Username)
2113	assert.Contains(t, folder.Users, user2.Username)
2114	// now update user2 removing mappedPath1
2115	user2.VirtualFolders = nil
2116	user2.VirtualFolders = append(user2.VirtualFolders, vfs.VirtualFolder{
2117		BaseVirtualFolder: vfs.BaseVirtualFolder{
2118			Name:           folderName2,
2119			MappedPath:     mappedPath2,
2120			UsedQuotaFiles: 2,
2121			UsedQuotaSize:  123,
2122		},
2123		VirtualPath: "/vdir",
2124		QuotaSize:   0,
2125		QuotaFiles:  0,
2126	})
2127	user2, _, err = httpdtest.UpdateUser(user2, http.StatusOK, "")
2128	assert.NoError(t, err)
2129	folder, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK)
2130	assert.NoError(t, err)
2131	assert.Len(t, folder.Users, 1)
2132	assert.Contains(t, folder.Users, user2.Username)
2133	assert.Equal(t, 0, folder.UsedQuotaFiles)
2134	assert.Equal(t, int64(0), folder.UsedQuotaSize)
2135	folder, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK)
2136	assert.NoError(t, err)
2137	assert.Len(t, folder.Users, 1)
2138	assert.Contains(t, folder.Users, user1.Username)
2139	// add mappedPath1 again to user2
2140	user2.VirtualFolders = append(user2.VirtualFolders, vfs.VirtualFolder{
2141		BaseVirtualFolder: vfs.BaseVirtualFolder{
2142			Name:       folderName1,
2143			MappedPath: mappedPath1,
2144		},
2145		VirtualPath: "/vdir1",
2146	})
2147	user2, _, err = httpdtest.UpdateUser(user2, http.StatusOK, "")
2148	assert.NoError(t, err)
2149	folder, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK)
2150	assert.NoError(t, err)
2151	assert.Len(t, folder.Users, 1)
2152	assert.Contains(t, folder.Users, user2.Username)
2153	// removing virtual folders should clear relations on both side
2154	_, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName2}, http.StatusOK)
2155	assert.NoError(t, err)
2156	user2, _, err = httpdtest.GetUserByUsername(user2.Username, http.StatusOK)
2157	assert.NoError(t, err)
2158	if assert.Len(t, user2.VirtualFolders, 1) {
2159		folder := user2.VirtualFolders[0]
2160		assert.Equal(t, mappedPath1, folder.MappedPath)
2161		assert.Equal(t, folderName1, folder.Name)
2162	}
2163	user1, _, err = httpdtest.GetUserByUsername(user1.Username, http.StatusOK)
2164	assert.NoError(t, err)
2165	if assert.Len(t, user2.VirtualFolders, 1) {
2166		folder := user2.VirtualFolders[0]
2167		assert.Equal(t, mappedPath1, folder.MappedPath)
2168	}
2169
2170	folder, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK)
2171	assert.NoError(t, err)
2172	assert.Len(t, folder.Users, 2)
2173	// removing a user should clear virtual folder mapping
2174	_, err = httpdtest.RemoveUser(user1, http.StatusOK)
2175	assert.NoError(t, err)
2176	folder, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK)
2177	assert.NoError(t, err)
2178	assert.Len(t, folder.Users, 1)
2179	assert.Contains(t, folder.Users, user2.Username)
2180	// removing a folder should clear mapping on the user side too
2181	_, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName1}, http.StatusOK)
2182	assert.NoError(t, err)
2183	user2, _, err = httpdtest.GetUserByUsername(user2.Username, http.StatusOK)
2184	assert.NoError(t, err)
2185	assert.Len(t, user2.VirtualFolders, 0)
2186	_, err = httpdtest.RemoveUser(user2, http.StatusOK)
2187	assert.NoError(t, err)
2188}
2189
2190func TestUserS3Config(t *testing.T) {
2191	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
2192	assert.NoError(t, err)
2193	user.FsConfig.Provider = sdk.S3FilesystemProvider
2194	user.FsConfig.S3Config.Bucket = "test"      //nolint:goconst
2195	user.FsConfig.S3Config.Region = "us-east-1" //nolint:goconst
2196	user.FsConfig.S3Config.AccessKey = "Server-Access-Key"
2197	user.FsConfig.S3Config.AccessSecret = kms.NewPlainSecret("Server-Access-Secret")
2198	user.FsConfig.S3Config.Endpoint = "http://127.0.0.1:9000"
2199	user.FsConfig.S3Config.UploadPartSize = 8
2200	user.FsConfig.S3Config.DownloadPartMaxTime = 60
2201	user.FsConfig.S3Config.ForcePathStyle = true
2202	user.FsConfig.S3Config.DownloadPartSize = 6
2203	folderName := "vfolderName"
2204	user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
2205		BaseVirtualFolder: vfs.BaseVirtualFolder{
2206			Name:       folderName,
2207			MappedPath: filepath.Join(os.TempDir(), "folderName"),
2208			FsConfig: vfs.Filesystem{
2209				Provider: sdk.CryptedFilesystemProvider,
2210				CryptConfig: vfs.CryptFsConfig{
2211					CryptFsConfig: sdk.CryptFsConfig{
2212						Passphrase: kms.NewPlainSecret("Crypted-Secret"),
2213					},
2214				},
2215			},
2216		},
2217		VirtualPath: "/folderPath",
2218	})
2219	user, body, err := httpdtest.UpdateUser(user, http.StatusOK, "")
2220	assert.NoError(t, err, string(body))
2221	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.S3Config.AccessSecret.GetStatus())
2222	assert.NotEmpty(t, user.FsConfig.S3Config.AccessSecret.GetPayload())
2223	assert.Empty(t, user.FsConfig.S3Config.AccessSecret.GetAdditionalData())
2224	assert.Empty(t, user.FsConfig.S3Config.AccessSecret.GetKey())
2225	assert.Equal(t, 60, user.FsConfig.S3Config.DownloadPartMaxTime)
2226	if assert.Len(t, user.VirtualFolders, 1) {
2227		folder := user.VirtualFolders[0]
2228		assert.Equal(t, kms.SecretStatusSecretBox, folder.FsConfig.CryptConfig.Passphrase.GetStatus())
2229		assert.NotEmpty(t, folder.FsConfig.CryptConfig.Passphrase.GetPayload())
2230		assert.Empty(t, folder.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
2231		assert.Empty(t, folder.FsConfig.CryptConfig.Passphrase.GetKey())
2232	}
2233	_, err = httpdtest.RemoveUser(user, http.StatusOK)
2234	assert.NoError(t, err)
2235	folder, _, err := httpdtest.GetFolderByName(folderName, http.StatusOK)
2236	assert.NoError(t, err)
2237	assert.Equal(t, kms.SecretStatusSecretBox, folder.FsConfig.CryptConfig.Passphrase.GetStatus())
2238	assert.NotEmpty(t, folder.FsConfig.CryptConfig.Passphrase.GetPayload())
2239	assert.Empty(t, folder.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
2240	assert.Empty(t, folder.FsConfig.CryptConfig.Passphrase.GetKey())
2241	_, err = httpdtest.RemoveFolder(folder, http.StatusOK)
2242	assert.NoError(t, err)
2243	user.Password = defaultPassword
2244	user.ID = 0
2245	user.CreatedAt = 0
2246	user.VirtualFolders = nil
2247	secret := kms.NewSecret(kms.SecretStatusSecretBox, "Server-Access-Secret", "", "")
2248	user.FsConfig.S3Config.AccessSecret = secret
2249	_, _, err = httpdtest.AddUser(user, http.StatusCreated)
2250	assert.Error(t, err)
2251	user.FsConfig.S3Config.AccessSecret.SetStatus(kms.SecretStatusPlain)
2252	user, _, err = httpdtest.AddUser(user, http.StatusCreated)
2253	assert.NoError(t, err)
2254	initialSecretPayload := user.FsConfig.S3Config.AccessSecret.GetPayload()
2255	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.S3Config.AccessSecret.GetStatus())
2256	assert.NotEmpty(t, initialSecretPayload)
2257	assert.Empty(t, user.FsConfig.S3Config.AccessSecret.GetAdditionalData())
2258	assert.Empty(t, user.FsConfig.S3Config.AccessSecret.GetKey())
2259	user.FsConfig.Provider = sdk.S3FilesystemProvider
2260	user.FsConfig.S3Config.Bucket = "test-bucket"
2261	user.FsConfig.S3Config.Region = "us-east-1" //nolint:goconst
2262	user.FsConfig.S3Config.AccessKey = "Server-Access-Key1"
2263	user.FsConfig.S3Config.Endpoint = "http://localhost:9000"
2264	user.FsConfig.S3Config.KeyPrefix = "somedir/subdir" //nolint:goconst
2265	user.FsConfig.S3Config.UploadConcurrency = 5
2266	user.FsConfig.S3Config.DownloadConcurrency = 4
2267	user, bb, err := httpdtest.UpdateUser(user, http.StatusOK, "")
2268	assert.NoError(t, err, string(bb))
2269	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.S3Config.AccessSecret.GetStatus())
2270	assert.Equal(t, initialSecretPayload, user.FsConfig.S3Config.AccessSecret.GetPayload())
2271	assert.Empty(t, user.FsConfig.S3Config.AccessSecret.GetAdditionalData())
2272	assert.Empty(t, user.FsConfig.S3Config.AccessSecret.GetKey())
2273	// test user without access key and access secret (shared config state)
2274	user.FsConfig.Provider = sdk.S3FilesystemProvider
2275	user.FsConfig.S3Config.Bucket = "testbucket"
2276	user.FsConfig.S3Config.Region = "us-east-1"
2277	user.FsConfig.S3Config.AccessKey = ""
2278	user.FsConfig.S3Config.AccessSecret = kms.NewEmptySecret()
2279	user.FsConfig.S3Config.Endpoint = ""
2280	user.FsConfig.S3Config.KeyPrefix = "somedir/subdir"
2281	user.FsConfig.S3Config.UploadPartSize = 6
2282	user.FsConfig.S3Config.UploadConcurrency = 4
2283	user, body, err = httpdtest.UpdateUser(user, http.StatusOK, "")
2284	assert.NoError(t, err, string(body))
2285	assert.Nil(t, user.FsConfig.S3Config.AccessSecret)
2286	_, err = httpdtest.RemoveUser(user, http.StatusOK)
2287	assert.NoError(t, err)
2288	user.Password = defaultPassword
2289	user.ID = 0
2290	user.CreatedAt = 0
2291	// shared credential test for add instead of update
2292	user, _, err = httpdtest.AddUser(user, http.StatusCreated)
2293	assert.NoError(t, err)
2294	assert.Nil(t, user.FsConfig.S3Config.AccessSecret)
2295	_, err = httpdtest.RemoveUser(user, http.StatusOK)
2296	assert.NoError(t, err)
2297}
2298
2299func TestUserGCSConfig(t *testing.T) {
2300	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
2301	assert.NoError(t, err)
2302	err = os.RemoveAll(credentialsPath)
2303	assert.NoError(t, err)
2304	err = os.MkdirAll(credentialsPath, 0700)
2305	assert.NoError(t, err)
2306	user.FsConfig.Provider = sdk.GCSFilesystemProvider
2307	user.FsConfig.GCSConfig.Bucket = "test"
2308	user.FsConfig.GCSConfig.Credentials = kms.NewPlainSecret("fake credentials") //nolint:goconst
2309	user, bb, err := httpdtest.UpdateUser(user, http.StatusOK, "")
2310	assert.NoError(t, err, string(bb))
2311	credentialFile := filepath.Join(credentialsPath, fmt.Sprintf("%v_gcs_credentials.json", user.Username))
2312	assert.FileExists(t, credentialFile)
2313	creds, err := os.ReadFile(credentialFile)
2314	assert.NoError(t, err)
2315	secret := kms.NewEmptySecret()
2316	err = json.Unmarshal(creds, secret)
2317	assert.NoError(t, err)
2318	err = secret.Decrypt()
2319	assert.NoError(t, err)
2320	assert.Equal(t, "fake credentials", secret.GetPayload())
2321	user.FsConfig.GCSConfig.Credentials = kms.NewSecret(kms.SecretStatusSecretBox, "fake encrypted credentials", "", "")
2322	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
2323	assert.NoError(t, err)
2324	assert.FileExists(t, credentialFile)
2325	creds, err = os.ReadFile(credentialFile)
2326	assert.NoError(t, err)
2327	secret = kms.NewEmptySecret()
2328	err = json.Unmarshal(creds, secret)
2329	assert.NoError(t, err)
2330	err = secret.Decrypt()
2331	assert.NoError(t, err)
2332	assert.Equal(t, "fake credentials", secret.GetPayload())
2333	_, err = httpdtest.RemoveUser(user, http.StatusOK)
2334	assert.NoError(t, err)
2335	user.Password = defaultPassword
2336	user.ID = 0
2337	user.CreatedAt = 0
2338	user.FsConfig.GCSConfig.Credentials = kms.NewSecret(kms.SecretStatusSecretBox, "fake credentials", "", "")
2339	_, _, err = httpdtest.AddUser(user, http.StatusCreated)
2340	assert.Error(t, err)
2341	user.FsConfig.GCSConfig.Credentials.SetStatus(kms.SecretStatusPlain)
2342	user, body, err := httpdtest.AddUser(user, http.StatusCreated)
2343	assert.NoError(t, err, string(body))
2344	err = os.RemoveAll(credentialsPath)
2345	assert.NoError(t, err)
2346	err = os.MkdirAll(credentialsPath, 0700)
2347	assert.NoError(t, err)
2348	user.FsConfig.GCSConfig.Credentials = kms.NewEmptySecret()
2349	user.FsConfig.GCSConfig.AutomaticCredentials = 1
2350	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
2351	assert.NoError(t, err)
2352	assert.NoFileExists(t, credentialFile)
2353	user.FsConfig.GCSConfig = vfs.GCSFsConfig{}
2354	user.FsConfig.Provider = sdk.S3FilesystemProvider
2355	user.FsConfig.S3Config.Bucket = "test1"
2356	user.FsConfig.S3Config.Region = "us-east-1"
2357	user.FsConfig.S3Config.AccessKey = "Server-Access-Key1"
2358	user.FsConfig.S3Config.AccessSecret = kms.NewPlainSecret("secret")
2359	user.FsConfig.S3Config.Endpoint = "http://localhost:9000"
2360	user.FsConfig.S3Config.KeyPrefix = "somedir/subdir"
2361	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
2362	assert.NoError(t, err)
2363	user.FsConfig.S3Config = vfs.S3FsConfig{}
2364	user.FsConfig.Provider = sdk.GCSFilesystemProvider
2365	user.FsConfig.GCSConfig.Bucket = "test1"
2366	user.FsConfig.GCSConfig.Credentials = kms.NewPlainSecret("fake credentials")
2367	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
2368	assert.NoError(t, err)
2369
2370	_, err = httpdtest.RemoveUser(user, http.StatusOK)
2371	assert.NoError(t, err)
2372}
2373
2374func TestUserAzureBlobConfig(t *testing.T) {
2375	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
2376	assert.NoError(t, err)
2377	user.FsConfig.Provider = sdk.AzureBlobFilesystemProvider
2378	user.FsConfig.AzBlobConfig.Container = "test"
2379	user.FsConfig.AzBlobConfig.AccountName = "Server-Account-Name"
2380	user.FsConfig.AzBlobConfig.AccountKey = kms.NewPlainSecret("Server-Account-Key")
2381	user.FsConfig.AzBlobConfig.Endpoint = "http://127.0.0.1:9000"
2382	user.FsConfig.AzBlobConfig.UploadPartSize = 8
2383	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
2384	assert.NoError(t, err)
2385	initialPayload := user.FsConfig.AzBlobConfig.AccountKey.GetPayload()
2386	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.AccountKey.GetStatus())
2387	assert.NotEmpty(t, initialPayload)
2388	assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
2389	assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetKey())
2390	user.FsConfig.AzBlobConfig.AccountKey.SetStatus(kms.SecretStatusSecretBox)
2391	user.FsConfig.AzBlobConfig.AccountKey.SetAdditionalData("data")
2392	user.FsConfig.AzBlobConfig.AccountKey.SetKey("fake key")
2393	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
2394	assert.NoError(t, err)
2395	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.AccountKey.GetStatus())
2396	assert.Equal(t, initialPayload, user.FsConfig.AzBlobConfig.AccountKey.GetPayload())
2397	assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
2398	assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetKey())
2399
2400	_, err = httpdtest.RemoveUser(user, http.StatusOK)
2401	assert.NoError(t, err)
2402	user.Password = defaultPassword
2403	user.ID = 0
2404	user.CreatedAt = 0
2405	secret := kms.NewSecret(kms.SecretStatusSecretBox, "Server-Account-Key", "", "")
2406	user.FsConfig.AzBlobConfig.AccountKey = secret
2407	_, _, err = httpdtest.AddUser(user, http.StatusCreated)
2408	assert.Error(t, err)
2409	user.FsConfig.AzBlobConfig.AccountKey = kms.NewPlainSecret("Server-Account-Key-Test")
2410	user, _, err = httpdtest.AddUser(user, http.StatusCreated)
2411	assert.NoError(t, err)
2412	initialPayload = user.FsConfig.AzBlobConfig.AccountKey.GetPayload()
2413	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.AccountKey.GetStatus())
2414	assert.NotEmpty(t, initialPayload)
2415	assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
2416	assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetKey())
2417	user.FsConfig.Provider = sdk.AzureBlobFilesystemProvider
2418	user.FsConfig.AzBlobConfig.Container = "test-container"
2419	user.FsConfig.AzBlobConfig.Endpoint = "http://localhost:9001"
2420	user.FsConfig.AzBlobConfig.KeyPrefix = "somedir/subdir"
2421	user.FsConfig.AzBlobConfig.UploadConcurrency = 5
2422	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
2423	assert.NoError(t, err)
2424	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.AccountKey.GetStatus())
2425	assert.NotEmpty(t, initialPayload)
2426	assert.Equal(t, initialPayload, user.FsConfig.AzBlobConfig.AccountKey.GetPayload())
2427	assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
2428	assert.Empty(t, user.FsConfig.AzBlobConfig.AccountKey.GetKey())
2429	// test user without access key and access secret (SAS)
2430	user.FsConfig.Provider = sdk.AzureBlobFilesystemProvider
2431	user.FsConfig.AzBlobConfig.SASURL = kms.NewPlainSecret("https://myaccount.blob.core.windows.net/pictures/profile.jpg?sv=2012-02-12&st=2009-02-09&se=2009-02-10&sr=c&sp=r&si=YWJjZGVmZw%3d%3d&sig=dD80ihBh5jfNpymO5Hg1IdiJIEvHcJpCMiCMnN%2fRnbI%3d")
2432	user.FsConfig.AzBlobConfig.KeyPrefix = "somedir/subdir"
2433	user.FsConfig.AzBlobConfig.AccountName = ""
2434	user.FsConfig.AzBlobConfig.AccountKey = kms.NewEmptySecret()
2435	user.FsConfig.AzBlobConfig.UploadPartSize = 6
2436	user.FsConfig.AzBlobConfig.UploadConcurrency = 4
2437	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
2438	assert.NoError(t, err)
2439	assert.Nil(t, user.FsConfig.AzBlobConfig.AccountKey)
2440	assert.NotNil(t, user.FsConfig.AzBlobConfig.SASURL)
2441	_, err = httpdtest.RemoveUser(user, http.StatusOK)
2442	assert.NoError(t, err)
2443	user.Password = defaultPassword
2444	user.ID = 0
2445	user.CreatedAt = 0
2446	// sas test for add instead of update
2447	user.FsConfig.AzBlobConfig = vfs.AzBlobFsConfig{
2448		AzBlobFsConfig: sdk.AzBlobFsConfig{
2449			Container: user.FsConfig.AzBlobConfig.Container,
2450			SASURL:    kms.NewPlainSecret("http://127.0.0.1/fake/sass/url"),
2451		},
2452	}
2453	user, _, err = httpdtest.AddUser(user, http.StatusCreated)
2454	assert.NoError(t, err)
2455	assert.Nil(t, user.FsConfig.AzBlobConfig.AccountKey)
2456	initialPayload = user.FsConfig.AzBlobConfig.SASURL.GetPayload()
2457	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.SASURL.GetStatus())
2458	assert.NotEmpty(t, initialPayload)
2459	assert.Empty(t, user.FsConfig.AzBlobConfig.SASURL.GetAdditionalData())
2460	assert.Empty(t, user.FsConfig.AzBlobConfig.SASURL.GetKey())
2461	user.FsConfig.AzBlobConfig.SASURL.SetStatus(kms.SecretStatusSecretBox)
2462	user.FsConfig.AzBlobConfig.SASURL.SetAdditionalData("data")
2463	user.FsConfig.AzBlobConfig.SASURL.SetKey("fake key")
2464	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
2465	assert.NoError(t, err)
2466	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.AzBlobConfig.SASURL.GetStatus())
2467	assert.Equal(t, initialPayload, user.FsConfig.AzBlobConfig.SASURL.GetPayload())
2468	assert.Empty(t, user.FsConfig.AzBlobConfig.SASURL.GetAdditionalData())
2469	assert.Empty(t, user.FsConfig.AzBlobConfig.SASURL.GetKey())
2470
2471	_, err = httpdtest.RemoveUser(user, http.StatusOK)
2472	assert.NoError(t, err)
2473}
2474
2475func TestUserCryptFs(t *testing.T) {
2476	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
2477	assert.NoError(t, err)
2478	user.FsConfig.Provider = sdk.CryptedFilesystemProvider
2479	user.FsConfig.CryptConfig.Passphrase = kms.NewPlainSecret("crypt passphrase")
2480	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
2481	assert.NoError(t, err)
2482	initialPayload := user.FsConfig.CryptConfig.Passphrase.GetPayload()
2483	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.CryptConfig.Passphrase.GetStatus())
2484	assert.NotEmpty(t, initialPayload)
2485	assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
2486	assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetKey())
2487	user.FsConfig.CryptConfig.Passphrase.SetStatus(kms.SecretStatusSecretBox)
2488	user.FsConfig.CryptConfig.Passphrase.SetAdditionalData("data")
2489	user.FsConfig.CryptConfig.Passphrase.SetKey("fake pass key")
2490	user, bb, err := httpdtest.UpdateUser(user, http.StatusOK, "")
2491	assert.NoError(t, err, string(bb))
2492	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.CryptConfig.Passphrase.GetStatus())
2493	assert.Equal(t, initialPayload, user.FsConfig.CryptConfig.Passphrase.GetPayload())
2494	assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
2495	assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetKey())
2496
2497	_, err = httpdtest.RemoveUser(user, http.StatusOK)
2498	assert.NoError(t, err)
2499	user.Password = defaultPassword
2500	user.ID = 0
2501	user.CreatedAt = 0
2502	secret := kms.NewSecret(kms.SecretStatusSecretBox, "invalid encrypted payload", "", "")
2503	user.FsConfig.CryptConfig.Passphrase = secret
2504	_, _, err = httpdtest.AddUser(user, http.StatusCreated)
2505	assert.Error(t, err)
2506	user.FsConfig.CryptConfig.Passphrase = kms.NewPlainSecret("passphrase test")
2507	user, _, err = httpdtest.AddUser(user, http.StatusCreated)
2508	assert.NoError(t, err)
2509	initialPayload = user.FsConfig.CryptConfig.Passphrase.GetPayload()
2510	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.CryptConfig.Passphrase.GetStatus())
2511	assert.NotEmpty(t, initialPayload)
2512	assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
2513	assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetKey())
2514	user.FsConfig.Provider = sdk.CryptedFilesystemProvider
2515	user.FsConfig.CryptConfig.Passphrase.SetKey("pass")
2516	user, bb, err = httpdtest.UpdateUser(user, http.StatusOK, "")
2517	assert.NoError(t, err, string(bb))
2518	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.CryptConfig.Passphrase.GetStatus())
2519	assert.NotEmpty(t, initialPayload)
2520	assert.Equal(t, initialPayload, user.FsConfig.CryptConfig.Passphrase.GetPayload())
2521	assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
2522	assert.Empty(t, user.FsConfig.CryptConfig.Passphrase.GetKey())
2523
2524	_, err = httpdtest.RemoveUser(user, http.StatusOK)
2525	assert.NoError(t, err)
2526}
2527
2528func TestUserSFTPFs(t *testing.T) {
2529	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
2530	assert.NoError(t, err)
2531	user.FsConfig.Provider = sdk.SFTPFilesystemProvider
2532	user.FsConfig.SFTPConfig.Endpoint = "127.0.0.1" // missing port
2533	user.FsConfig.SFTPConfig.Username = "sftp_user"
2534	user.FsConfig.SFTPConfig.Password = kms.NewPlainSecret("sftp_pwd")
2535	user.FsConfig.SFTPConfig.PrivateKey = kms.NewPlainSecret(sftpPrivateKey)
2536	user.FsConfig.SFTPConfig.Fingerprints = []string{sftpPkeyFingerprint}
2537	user.FsConfig.SFTPConfig.BufferSize = 2
2538	_, resp, err := httpdtest.UpdateUser(user, http.StatusBadRequest, "")
2539	assert.NoError(t, err)
2540	assert.Contains(t, string(resp), "invalid endpoint")
2541
2542	user.FsConfig.SFTPConfig.Endpoint = "127.0.0.1:2022"
2543	user.FsConfig.SFTPConfig.DisableCouncurrentReads = true
2544	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
2545	assert.NoError(t, err)
2546	assert.Equal(t, "/", user.FsConfig.SFTPConfig.Prefix)
2547	assert.True(t, user.FsConfig.SFTPConfig.DisableCouncurrentReads)
2548	assert.Equal(t, int64(2), user.FsConfig.SFTPConfig.BufferSize)
2549	initialPwdPayload := user.FsConfig.SFTPConfig.Password.GetPayload()
2550	initialPkeyPayload := user.FsConfig.SFTPConfig.PrivateKey.GetPayload()
2551	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.Password.GetStatus())
2552	assert.NotEmpty(t, initialPwdPayload)
2553	assert.Empty(t, user.FsConfig.SFTPConfig.Password.GetAdditionalData())
2554	assert.Empty(t, user.FsConfig.SFTPConfig.Password.GetKey())
2555	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.PrivateKey.GetStatus())
2556	assert.NotEmpty(t, initialPkeyPayload)
2557	assert.Empty(t, user.FsConfig.SFTPConfig.PrivateKey.GetAdditionalData())
2558	assert.Empty(t, user.FsConfig.SFTPConfig.PrivateKey.GetKey())
2559	user.FsConfig.SFTPConfig.Password.SetStatus(kms.SecretStatusSecretBox)
2560	user.FsConfig.SFTPConfig.Password.SetAdditionalData("adata")
2561	user.FsConfig.SFTPConfig.Password.SetKey("fake pwd key")
2562	user.FsConfig.SFTPConfig.PrivateKey.SetStatus(kms.SecretStatusSecretBox)
2563	user.FsConfig.SFTPConfig.PrivateKey.SetAdditionalData("adata")
2564	user.FsConfig.SFTPConfig.PrivateKey.SetKey("fake key")
2565	user.FsConfig.SFTPConfig.DisableCouncurrentReads = false
2566	user, bb, err := httpdtest.UpdateUser(user, http.StatusOK, "")
2567	assert.NoError(t, err, string(bb))
2568	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.Password.GetStatus())
2569	assert.Equal(t, initialPwdPayload, user.FsConfig.SFTPConfig.Password.GetPayload())
2570	assert.Empty(t, user.FsConfig.SFTPConfig.Password.GetAdditionalData())
2571	assert.Empty(t, user.FsConfig.SFTPConfig.Password.GetKey())
2572	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.PrivateKey.GetStatus())
2573	assert.Equal(t, initialPkeyPayload, user.FsConfig.SFTPConfig.PrivateKey.GetPayload())
2574	assert.Empty(t, user.FsConfig.SFTPConfig.PrivateKey.GetAdditionalData())
2575	assert.Empty(t, user.FsConfig.SFTPConfig.PrivateKey.GetKey())
2576	assert.False(t, user.FsConfig.SFTPConfig.DisableCouncurrentReads)
2577
2578	_, err = httpdtest.RemoveUser(user, http.StatusOK)
2579	assert.NoError(t, err)
2580	user.Password = defaultPassword
2581	user.ID = 0
2582	user.CreatedAt = 0
2583	secret := kms.NewSecret(kms.SecretStatusSecretBox, "invalid encrypted payload", "", "")
2584	user.FsConfig.SFTPConfig.Password = secret
2585	_, _, err = httpdtest.AddUser(user, http.StatusCreated)
2586	assert.Error(t, err)
2587	user.FsConfig.SFTPConfig.Password = kms.NewEmptySecret()
2588	user.FsConfig.SFTPConfig.PrivateKey = secret
2589	_, _, err = httpdtest.AddUser(user, http.StatusCreated)
2590	assert.Error(t, err)
2591
2592	user.FsConfig.SFTPConfig.PrivateKey = kms.NewPlainSecret(sftpPrivateKey)
2593	user, _, err = httpdtest.AddUser(user, http.StatusCreated)
2594	assert.NoError(t, err)
2595	initialPkeyPayload = user.FsConfig.SFTPConfig.PrivateKey.GetPayload()
2596	assert.Nil(t, user.FsConfig.SFTPConfig.Password)
2597	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.PrivateKey.GetStatus())
2598	assert.NotEmpty(t, initialPkeyPayload)
2599	assert.Empty(t, user.FsConfig.SFTPConfig.PrivateKey.GetAdditionalData())
2600	assert.Empty(t, user.FsConfig.SFTPConfig.PrivateKey.GetKey())
2601	user.FsConfig.Provider = sdk.SFTPFilesystemProvider
2602	user.FsConfig.SFTPConfig.PrivateKey.SetKey("k")
2603	user, bb, err = httpdtest.UpdateUser(user, http.StatusOK, "")
2604	assert.NoError(t, err, string(bb))
2605	assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.SFTPConfig.PrivateKey.GetStatus())
2606	assert.NotEmpty(t, initialPkeyPayload)
2607	assert.Equal(t, initialPkeyPayload, user.FsConfig.SFTPConfig.PrivateKey.GetPayload())
2608	assert.Empty(t, user.FsConfig.SFTPConfig.PrivateKey.GetAdditionalData())
2609	assert.Empty(t, user.FsConfig.SFTPConfig.PrivateKey.GetKey())
2610
2611	_, err = httpdtest.RemoveUser(user, http.StatusOK)
2612	assert.NoError(t, err)
2613}
2614
2615func TestUserHiddenFields(t *testing.T) {
2616	err := dataprovider.Close()
2617	assert.NoError(t, err)
2618	err = config.LoadConfig(configDir, "")
2619	assert.NoError(t, err)
2620	providerConf := config.GetProviderConf()
2621	providerConf.PreferDatabaseCredentials = true
2622	err = dataprovider.Initialize(providerConf, configDir, true)
2623	assert.NoError(t, err)
2624
2625	// sensitive data must be hidden but not deleted from the dataprovider
2626	usernames := []string{"user1", "user2", "user3", "user4", "user5"}
2627	u1 := getTestUser()
2628	u1.Username = usernames[0]
2629	u1.FsConfig.Provider = sdk.S3FilesystemProvider
2630	u1.FsConfig.S3Config.Bucket = "test"
2631	u1.FsConfig.S3Config.Region = "us-east-1"
2632	u1.FsConfig.S3Config.AccessKey = "S3-Access-Key"
2633	u1.FsConfig.S3Config.AccessSecret = kms.NewPlainSecret("S3-Access-Secret")
2634	user1, _, err := httpdtest.AddUser(u1, http.StatusCreated)
2635	assert.NoError(t, err)
2636
2637	u2 := getTestUser()
2638	u2.Username = usernames[1]
2639	u2.FsConfig.Provider = sdk.GCSFilesystemProvider
2640	u2.FsConfig.GCSConfig.Bucket = "test"
2641	u2.FsConfig.GCSConfig.Credentials = kms.NewPlainSecret("fake credentials")
2642	u2.FsConfig.GCSConfig.ACL = "bucketOwnerRead"
2643	user2, _, err := httpdtest.AddUser(u2, http.StatusCreated)
2644	assert.NoError(t, err)
2645
2646	u3 := getTestUser()
2647	u3.Username = usernames[2]
2648	u3.FsConfig.Provider = sdk.AzureBlobFilesystemProvider
2649	u3.FsConfig.AzBlobConfig.Container = "test"
2650	u3.FsConfig.AzBlobConfig.AccountName = "Server-Account-Name"
2651	u3.FsConfig.AzBlobConfig.AccountKey = kms.NewPlainSecret("Server-Account-Key")
2652	user3, _, err := httpdtest.AddUser(u3, http.StatusCreated)
2653	assert.NoError(t, err)
2654
2655	u4 := getTestUser()
2656	u4.Username = usernames[3]
2657	u4.FsConfig.Provider = sdk.CryptedFilesystemProvider
2658	u4.FsConfig.CryptConfig.Passphrase = kms.NewPlainSecret("test passphrase")
2659	user4, _, err := httpdtest.AddUser(u4, http.StatusCreated)
2660	assert.NoError(t, err)
2661
2662	u5 := getTestUser()
2663	u5.Username = usernames[4]
2664	u5.FsConfig.Provider = sdk.SFTPFilesystemProvider
2665	u5.FsConfig.SFTPConfig.Endpoint = "127.0.0.1:2022"
2666	u5.FsConfig.SFTPConfig.Username = "sftp_user"
2667	u5.FsConfig.SFTPConfig.Password = kms.NewPlainSecret("apassword")
2668	u5.FsConfig.SFTPConfig.PrivateKey = kms.NewPlainSecret(sftpPrivateKey)
2669	u5.FsConfig.SFTPConfig.Fingerprints = []string{sftpPkeyFingerprint}
2670	u5.FsConfig.SFTPConfig.Prefix = "/prefix"
2671	user5, _, err := httpdtest.AddUser(u5, http.StatusCreated)
2672	assert.NoError(t, err)
2673
2674	users, _, err := httpdtest.GetUsers(0, 0, http.StatusOK)
2675	assert.NoError(t, err)
2676	assert.GreaterOrEqual(t, len(users), 5)
2677	for _, username := range usernames {
2678		user, _, err := httpdtest.GetUserByUsername(username, http.StatusOK)
2679		assert.NoError(t, err)
2680		assert.Empty(t, user.Password)
2681	}
2682	user1, _, err = httpdtest.GetUserByUsername(user1.Username, http.StatusOK)
2683	assert.NoError(t, err)
2684	assert.Empty(t, user1.Password)
2685	assert.Empty(t, user1.FsConfig.S3Config.AccessSecret.GetKey())
2686	assert.Empty(t, user1.FsConfig.S3Config.AccessSecret.GetAdditionalData())
2687	assert.NotEmpty(t, user1.FsConfig.S3Config.AccessSecret.GetStatus())
2688	assert.NotEmpty(t, user1.FsConfig.S3Config.AccessSecret.GetPayload())
2689
2690	user2, _, err = httpdtest.GetUserByUsername(user2.Username, http.StatusOK)
2691	assert.NoError(t, err)
2692	assert.Empty(t, user2.Password)
2693	assert.Empty(t, user2.FsConfig.GCSConfig.Credentials.GetKey())
2694	assert.Empty(t, user2.FsConfig.GCSConfig.Credentials.GetAdditionalData())
2695	assert.NotEmpty(t, user2.FsConfig.GCSConfig.Credentials.GetStatus())
2696	assert.NotEmpty(t, user2.FsConfig.GCSConfig.Credentials.GetPayload())
2697
2698	user3, _, err = httpdtest.GetUserByUsername(user3.Username, http.StatusOK)
2699	assert.NoError(t, err)
2700	assert.Empty(t, user3.Password)
2701	assert.Empty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetKey())
2702	assert.Empty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
2703	assert.NotEmpty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetStatus())
2704	assert.NotEmpty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetPayload())
2705
2706	user4, _, err = httpdtest.GetUserByUsername(user4.Username, http.StatusOK)
2707	assert.NoError(t, err)
2708	assert.Empty(t, user4.Password)
2709	assert.Empty(t, user4.FsConfig.CryptConfig.Passphrase.GetKey())
2710	assert.Empty(t, user4.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
2711	assert.NotEmpty(t, user4.FsConfig.CryptConfig.Passphrase.GetStatus())
2712	assert.NotEmpty(t, user4.FsConfig.CryptConfig.Passphrase.GetPayload())
2713
2714	user5, _, err = httpdtest.GetUserByUsername(user5.Username, http.StatusOK)
2715	assert.NoError(t, err)
2716	assert.Empty(t, user5.Password)
2717	assert.Empty(t, user5.FsConfig.SFTPConfig.Password.GetKey())
2718	assert.Empty(t, user5.FsConfig.SFTPConfig.Password.GetAdditionalData())
2719	assert.NotEmpty(t, user5.FsConfig.SFTPConfig.Password.GetStatus())
2720	assert.NotEmpty(t, user5.FsConfig.SFTPConfig.Password.GetPayload())
2721	assert.Empty(t, user5.FsConfig.SFTPConfig.PrivateKey.GetKey())
2722	assert.Empty(t, user5.FsConfig.SFTPConfig.PrivateKey.GetAdditionalData())
2723	assert.NotEmpty(t, user5.FsConfig.SFTPConfig.PrivateKey.GetStatus())
2724	assert.NotEmpty(t, user5.FsConfig.SFTPConfig.PrivateKey.GetPayload())
2725	assert.Equal(t, "/prefix", user5.FsConfig.SFTPConfig.Prefix)
2726
2727	// finally check that we have all the data inside the data provider
2728	user1, err = dataprovider.UserExists(user1.Username)
2729	assert.NoError(t, err)
2730	assert.NotEmpty(t, user1.Password)
2731	assert.NotEmpty(t, user1.FsConfig.S3Config.AccessSecret.GetKey())
2732	assert.NotEmpty(t, user1.FsConfig.S3Config.AccessSecret.GetAdditionalData())
2733	assert.NotEmpty(t, user1.FsConfig.S3Config.AccessSecret.GetStatus())
2734	assert.NotEmpty(t, user1.FsConfig.S3Config.AccessSecret.GetPayload())
2735	err = user1.FsConfig.S3Config.AccessSecret.Decrypt()
2736	assert.NoError(t, err)
2737	assert.Equal(t, kms.SecretStatusPlain, user1.FsConfig.S3Config.AccessSecret.GetStatus())
2738	assert.Equal(t, u1.FsConfig.S3Config.AccessSecret.GetPayload(), user1.FsConfig.S3Config.AccessSecret.GetPayload())
2739	assert.Empty(t, user1.FsConfig.S3Config.AccessSecret.GetKey())
2740	assert.Empty(t, user1.FsConfig.S3Config.AccessSecret.GetAdditionalData())
2741
2742	user2, err = dataprovider.UserExists(user2.Username)
2743	assert.NoError(t, err)
2744	assert.NotEmpty(t, user2.Password)
2745	assert.NotEmpty(t, user2.FsConfig.GCSConfig.Credentials.GetKey())
2746	assert.NotEmpty(t, user2.FsConfig.GCSConfig.Credentials.GetAdditionalData())
2747	assert.NotEmpty(t, user2.FsConfig.GCSConfig.Credentials.GetStatus())
2748	assert.NotEmpty(t, user2.FsConfig.GCSConfig.Credentials.GetPayload())
2749	err = user2.FsConfig.GCSConfig.Credentials.Decrypt()
2750	assert.NoError(t, err)
2751	assert.Equal(t, kms.SecretStatusPlain, user2.FsConfig.GCSConfig.Credentials.GetStatus())
2752	assert.Equal(t, u2.FsConfig.GCSConfig.Credentials.GetPayload(), user2.FsConfig.GCSConfig.Credentials.GetPayload())
2753	assert.Empty(t, user2.FsConfig.GCSConfig.Credentials.GetKey())
2754	assert.Empty(t, user2.FsConfig.GCSConfig.Credentials.GetAdditionalData())
2755
2756	user3, err = dataprovider.UserExists(user3.Username)
2757	assert.NoError(t, err)
2758	assert.NotEmpty(t, user3.Password)
2759	assert.NotEmpty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetKey())
2760	assert.NotEmpty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
2761	assert.NotEmpty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetStatus())
2762	assert.NotEmpty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetPayload())
2763	err = user3.FsConfig.AzBlobConfig.AccountKey.Decrypt()
2764	assert.NoError(t, err)
2765	assert.Equal(t, kms.SecretStatusPlain, user3.FsConfig.AzBlobConfig.AccountKey.GetStatus())
2766	assert.Equal(t, u3.FsConfig.AzBlobConfig.AccountKey.GetPayload(), user3.FsConfig.AzBlobConfig.AccountKey.GetPayload())
2767	assert.Empty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetKey())
2768	assert.Empty(t, user3.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
2769
2770	user4, err = dataprovider.UserExists(user4.Username)
2771	assert.NoError(t, err)
2772	assert.NotEmpty(t, user4.Password)
2773	assert.NotEmpty(t, user4.FsConfig.CryptConfig.Passphrase.GetKey())
2774	assert.NotEmpty(t, user4.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
2775	assert.NotEmpty(t, user4.FsConfig.CryptConfig.Passphrase.GetStatus())
2776	assert.NotEmpty(t, user4.FsConfig.CryptConfig.Passphrase.GetPayload())
2777	err = user4.FsConfig.CryptConfig.Passphrase.Decrypt()
2778	assert.NoError(t, err)
2779	assert.Equal(t, kms.SecretStatusPlain, user4.FsConfig.CryptConfig.Passphrase.GetStatus())
2780	assert.Equal(t, u4.FsConfig.CryptConfig.Passphrase.GetPayload(), user4.FsConfig.CryptConfig.Passphrase.GetPayload())
2781	assert.Empty(t, user4.FsConfig.CryptConfig.Passphrase.GetKey())
2782	assert.Empty(t, user4.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
2783
2784	user5, err = dataprovider.UserExists(user5.Username)
2785	assert.NoError(t, err)
2786	assert.NotEmpty(t, user5.Password)
2787	assert.NotEmpty(t, user5.FsConfig.SFTPConfig.Password.GetKey())
2788	assert.NotEmpty(t, user5.FsConfig.SFTPConfig.Password.GetAdditionalData())
2789	assert.NotEmpty(t, user5.FsConfig.SFTPConfig.Password.GetStatus())
2790	assert.NotEmpty(t, user5.FsConfig.SFTPConfig.Password.GetPayload())
2791	err = user5.FsConfig.SFTPConfig.Password.Decrypt()
2792	assert.NoError(t, err)
2793	assert.Equal(t, kms.SecretStatusPlain, user5.FsConfig.SFTPConfig.Password.GetStatus())
2794	assert.Equal(t, u5.FsConfig.SFTPConfig.Password.GetPayload(), user5.FsConfig.SFTPConfig.Password.GetPayload())
2795	assert.Empty(t, user5.FsConfig.SFTPConfig.Password.GetKey())
2796	assert.Empty(t, user5.FsConfig.SFTPConfig.Password.GetAdditionalData())
2797	assert.NotEmpty(t, user5.FsConfig.SFTPConfig.PrivateKey.GetKey())
2798	assert.NotEmpty(t, user5.FsConfig.SFTPConfig.PrivateKey.GetAdditionalData())
2799	assert.NotEmpty(t, user5.FsConfig.SFTPConfig.PrivateKey.GetStatus())
2800	assert.NotEmpty(t, user5.FsConfig.SFTPConfig.PrivateKey.GetPayload())
2801	err = user5.FsConfig.SFTPConfig.PrivateKey.Decrypt()
2802	assert.NoError(t, err)
2803	assert.Equal(t, kms.SecretStatusPlain, user5.FsConfig.SFTPConfig.PrivateKey.GetStatus())
2804	assert.Equal(t, u5.FsConfig.SFTPConfig.PrivateKey.GetPayload(), user5.FsConfig.SFTPConfig.PrivateKey.GetPayload())
2805	assert.Empty(t, user5.FsConfig.SFTPConfig.PrivateKey.GetKey())
2806	assert.Empty(t, user5.FsConfig.SFTPConfig.PrivateKey.GetAdditionalData())
2807
2808	// update the GCS user and check that the credentials are preserved
2809	user2.FsConfig.GCSConfig.Credentials = kms.NewEmptySecret()
2810	user2.FsConfig.GCSConfig.ACL = "private"
2811	_, _, err = httpdtest.UpdateUser(user2, http.StatusOK, "")
2812	assert.NoError(t, err)
2813
2814	user2, _, err = httpdtest.GetUserByUsername(user2.Username, http.StatusOK)
2815	assert.NoError(t, err)
2816	assert.Empty(t, user2.Password)
2817	assert.Empty(t, user2.FsConfig.GCSConfig.Credentials.GetKey())
2818	assert.Empty(t, user2.FsConfig.GCSConfig.Credentials.GetAdditionalData())
2819	assert.NotEmpty(t, user2.FsConfig.GCSConfig.Credentials.GetStatus())
2820	assert.NotEmpty(t, user2.FsConfig.GCSConfig.Credentials.GetPayload())
2821
2822	_, err = httpdtest.RemoveUser(user1, http.StatusOK)
2823	assert.NoError(t, err)
2824	_, err = httpdtest.RemoveUser(user2, http.StatusOK)
2825	assert.NoError(t, err)
2826	_, err = httpdtest.RemoveUser(user3, http.StatusOK)
2827	assert.NoError(t, err)
2828	_, err = httpdtest.RemoveUser(user4, http.StatusOK)
2829	assert.NoError(t, err)
2830	_, err = httpdtest.RemoveUser(user5, http.StatusOK)
2831	assert.NoError(t, err)
2832
2833	err = dataprovider.Close()
2834	assert.NoError(t, err)
2835	err = config.LoadConfig(configDir, "")
2836	assert.NoError(t, err)
2837	providerConf = config.GetProviderConf()
2838	providerConf.CredentialsPath = credentialsPath
2839	err = os.RemoveAll(credentialsPath)
2840	assert.NoError(t, err)
2841	err = dataprovider.Initialize(providerConf, configDir, true)
2842	assert.NoError(t, err)
2843}
2844
2845func TestSecretObject(t *testing.T) {
2846	s := kms.NewPlainSecret("test data")
2847	s.SetAdditionalData("username")
2848	require.True(t, s.IsValid())
2849	err := s.Encrypt()
2850	require.NoError(t, err)
2851	require.Equal(t, kms.SecretStatusSecretBox, s.GetStatus())
2852	require.NotEmpty(t, s.GetPayload())
2853	require.NotEmpty(t, s.GetKey())
2854	require.True(t, s.IsValid())
2855	err = s.Decrypt()
2856	require.NoError(t, err)
2857	require.Equal(t, kms.SecretStatusPlain, s.GetStatus())
2858	require.Equal(t, "test data", s.GetPayload())
2859	require.Empty(t, s.GetKey())
2860
2861	oldFormat := "$aes$5b97e3a3324a2f53e2357483383367c0$0ed3132b584742ab217866219da633266782b69b13e50ebc6ddfb7c4fbf2f2a414c6d5f813"
2862	s, err = kms.GetSecretFromCompatString(oldFormat)
2863	require.NoError(t, err)
2864	require.True(t, s.IsValid())
2865	require.Equal(t, kms.SecretStatusPlain, s.GetStatus())
2866	require.Equal(t, "test data", s.GetPayload())
2867	require.Empty(t, s.GetKey())
2868}
2869
2870func TestSecretObjectCompatibility(t *testing.T) {
2871	// this is manually tested against vault too
2872	testPayload := "test payload"
2873	s := kms.NewPlainSecret(testPayload)
2874	require.True(t, s.IsValid())
2875	err := s.Encrypt()
2876	require.NoError(t, err)
2877	localAsJSON, err := json.Marshal(s)
2878	assert.NoError(t, err)
2879
2880	for _, secretStatus := range []string{kms.SecretStatusSecretBox} {
2881		kmsConfig := config.GetKMSConfig()
2882		assert.Empty(t, kmsConfig.Secrets.MasterKeyPath)
2883		if secretStatus == kms.SecretStatusVaultTransit {
2884			os.Setenv("VAULT_SERVER_URL", "http://127.0.0.1:8200")
2885			os.Setenv("VAULT_SERVER_TOKEN", "s.9lYGq83MbgG5KR5kfebXVyhJ")
2886			kmsConfig.Secrets.URL = "hashivault://mykey"
2887		}
2888		err := kmsConfig.Initialize()
2889		assert.NoError(t, err)
2890		// encrypt without a master key
2891		secret := kms.NewPlainSecret(testPayload)
2892		secret.SetAdditionalData("add data")
2893		err = secret.Encrypt()
2894		assert.NoError(t, err)
2895		assert.Equal(t, 0, secret.GetMode())
2896		secretClone := secret.Clone()
2897		err = secretClone.Decrypt()
2898		assert.NoError(t, err)
2899		assert.Equal(t, testPayload, secretClone.GetPayload())
2900		if secretStatus == kms.SecretStatusVaultTransit {
2901			// decrypt the local secret now that the provider is vault
2902			secretLocal := kms.NewEmptySecret()
2903			err = json.Unmarshal(localAsJSON, secretLocal)
2904			assert.NoError(t, err)
2905			assert.Equal(t, kms.SecretStatusSecretBox, secretLocal.GetStatus())
2906			assert.Equal(t, 0, secretLocal.GetMode())
2907			err = secretLocal.Decrypt()
2908			assert.NoError(t, err)
2909			assert.Equal(t, testPayload, secretLocal.GetPayload())
2910			assert.Equal(t, kms.SecretStatusPlain, secretLocal.GetStatus())
2911			err = secretLocal.Encrypt()
2912			assert.NoError(t, err)
2913			assert.Equal(t, kms.SecretStatusSecretBox, secretLocal.GetStatus())
2914			assert.Equal(t, 0, secretLocal.GetMode())
2915		}
2916
2917		asJSON, err := json.Marshal(secret)
2918		assert.NoError(t, err)
2919
2920		masterKeyPath := filepath.Join(os.TempDir(), "mkey")
2921		err = os.WriteFile(masterKeyPath, []byte("test key"), os.ModePerm)
2922		assert.NoError(t, err)
2923		config := kms.Configuration{
2924			Secrets: kms.Secrets{
2925				MasterKeyPath: masterKeyPath,
2926			},
2927		}
2928		if secretStatus == kms.SecretStatusVaultTransit {
2929			config.Secrets.URL = "hashivault://mykey"
2930		}
2931		err = config.Initialize()
2932		assert.NoError(t, err)
2933
2934		// now build the secret from JSON
2935		secret = kms.NewEmptySecret()
2936		err = json.Unmarshal(asJSON, secret)
2937		assert.NoError(t, err)
2938		assert.Equal(t, 0, secret.GetMode())
2939		err = secret.Decrypt()
2940		assert.NoError(t, err)
2941		assert.Equal(t, testPayload, secret.GetPayload())
2942		err = secret.Encrypt()
2943		assert.NoError(t, err)
2944		assert.Equal(t, 1, secret.GetMode())
2945		err = secret.Decrypt()
2946		assert.NoError(t, err)
2947		assert.Equal(t, testPayload, secret.GetPayload())
2948		if secretStatus == kms.SecretStatusVaultTransit {
2949			// decrypt the local secret encryped without a master key now that
2950			// the provider is vault and a master key is set.
2951			// The provider will not change, the master key will be used
2952			secretLocal := kms.NewEmptySecret()
2953			err = json.Unmarshal(localAsJSON, secretLocal)
2954			assert.NoError(t, err)
2955			assert.Equal(t, kms.SecretStatusSecretBox, secretLocal.GetStatus())
2956			assert.Equal(t, 0, secretLocal.GetMode())
2957			err = secretLocal.Decrypt()
2958			assert.NoError(t, err)
2959			assert.Equal(t, testPayload, secretLocal.GetPayload())
2960			assert.Equal(t, kms.SecretStatusPlain, secretLocal.GetStatus())
2961			err = secretLocal.Encrypt()
2962			assert.NoError(t, err)
2963			assert.Equal(t, kms.SecretStatusSecretBox, secretLocal.GetStatus())
2964			assert.Equal(t, 1, secretLocal.GetMode())
2965		}
2966
2967		err = kmsConfig.Initialize()
2968		assert.NoError(t, err)
2969		err = os.Remove(masterKeyPath)
2970		assert.NoError(t, err)
2971		if secretStatus == kms.SecretStatusVaultTransit {
2972			os.Unsetenv("VAULT_SERVER_URL")
2973			os.Unsetenv("VAULT_SERVER_TOKEN")
2974		}
2975	}
2976}
2977
2978func TestUpdateUserNoCredentials(t *testing.T) {
2979	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
2980	assert.NoError(t, err)
2981	user.Password = ""
2982	user.PublicKeys = []string{}
2983	// password and public key will be omitted from json serialization if empty and so they will remain unchanged
2984	// and no validation error will be raised
2985	_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
2986	assert.NoError(t, err)
2987	_, err = httpdtest.RemoveUser(user, http.StatusOK)
2988	assert.NoError(t, err)
2989}
2990
2991func TestUpdateUserEmptyHomeDir(t *testing.T) {
2992	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
2993	assert.NoError(t, err)
2994	user.HomeDir = ""
2995	_, _, err = httpdtest.UpdateUser(user, http.StatusBadRequest, "")
2996	assert.NoError(t, err)
2997	_, err = httpdtest.RemoveUser(user, http.StatusOK)
2998	assert.NoError(t, err)
2999}
3000
3001func TestUpdateUserInvalidHomeDir(t *testing.T) {
3002	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
3003	assert.NoError(t, err)
3004	user.HomeDir = "relative_path"
3005	_, _, err = httpdtest.UpdateUser(user, http.StatusBadRequest, "")
3006	assert.NoError(t, err)
3007	_, err = httpdtest.RemoveUser(user, http.StatusOK)
3008	assert.NoError(t, err)
3009}
3010
3011func TestUpdateNonExistentUser(t *testing.T) {
3012	_, _, err := httpdtest.UpdateUser(getTestUser(), http.StatusNotFound, "")
3013	assert.NoError(t, err)
3014}
3015
3016func TestGetNonExistentUser(t *testing.T) {
3017	_, _, err := httpdtest.GetUserByUsername("na", http.StatusNotFound)
3018	assert.NoError(t, err)
3019}
3020
3021func TestDeleteNonExistentUser(t *testing.T) {
3022	_, err := httpdtest.RemoveUser(getTestUser(), http.StatusNotFound)
3023	assert.NoError(t, err)
3024}
3025
3026func TestAddDuplicateUser(t *testing.T) {
3027	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
3028	assert.NoError(t, err)
3029	_, _, err = httpdtest.AddUser(getTestUser(), http.StatusInternalServerError)
3030	assert.NoError(t, err)
3031	_, _, err = httpdtest.AddUser(getTestUser(), http.StatusCreated)
3032	assert.Error(t, err, "adding a duplicate user must fail")
3033	_, err = httpdtest.RemoveUser(user, http.StatusOK)
3034	assert.NoError(t, err)
3035}
3036
3037func TestGetUsers(t *testing.T) {
3038	user1, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
3039	assert.NoError(t, err)
3040	u := getTestUser()
3041	u.Username = defaultUsername + "1"
3042	user2, _, err := httpdtest.AddUser(u, http.StatusCreated)
3043	assert.NoError(t, err)
3044	users, _, err := httpdtest.GetUsers(0, 0, http.StatusOK)
3045	assert.NoError(t, err)
3046	assert.GreaterOrEqual(t, len(users), 2)
3047	users, _, err = httpdtest.GetUsers(1, 0, http.StatusOK)
3048	assert.NoError(t, err)
3049	assert.Equal(t, 1, len(users))
3050	users, _, err = httpdtest.GetUsers(1, 1, http.StatusOK)
3051	assert.NoError(t, err)
3052	assert.Equal(t, 1, len(users))
3053	_, _, err = httpdtest.GetUsers(1, 1, http.StatusInternalServerError)
3054	assert.Error(t, err)
3055	_, err = httpdtest.RemoveUser(user1, http.StatusOK)
3056	assert.NoError(t, err)
3057	_, err = httpdtest.RemoveUser(user2, http.StatusOK)
3058	assert.NoError(t, err)
3059}
3060
3061func TestGetQuotaScans(t *testing.T) {
3062	_, _, err := httpdtest.GetQuotaScans(http.StatusOK)
3063	assert.NoError(t, err)
3064	_, _, err = httpdtest.GetQuotaScans(http.StatusInternalServerError)
3065	assert.Error(t, err)
3066	_, _, err = httpdtest.GetFoldersQuotaScans(http.StatusOK)
3067	assert.NoError(t, err)
3068	_, _, err = httpdtest.GetFoldersQuotaScans(http.StatusInternalServerError)
3069	assert.Error(t, err)
3070}
3071
3072func TestStartQuotaScan(t *testing.T) {
3073	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
3074	assert.NoError(t, err)
3075	_, err = httpdtest.StartQuotaScan(user, http.StatusAccepted)
3076	assert.NoError(t, err)
3077	_, err = httpdtest.RemoveUser(user, http.StatusOK)
3078	assert.NoError(t, err)
3079	folder := vfs.BaseVirtualFolder{
3080		Name:        "vfolder",
3081		MappedPath:  filepath.Join(os.TempDir(), "folder"),
3082		Description: "virtual folder",
3083	}
3084	_, _, err = httpdtest.AddFolder(folder, http.StatusCreated)
3085	assert.NoError(t, err)
3086	_, err = httpdtest.StartFolderQuotaScan(folder, http.StatusAccepted)
3087	assert.NoError(t, err)
3088	for {
3089		quotaScan, _, err := httpdtest.GetFoldersQuotaScans(http.StatusOK)
3090		if !assert.NoError(t, err, "Error getting active scans") {
3091			break
3092		}
3093		if len(quotaScan) == 0 {
3094			break
3095		}
3096		time.Sleep(100 * time.Millisecond)
3097	}
3098	_, err = httpdtest.RemoveFolder(folder, http.StatusOK)
3099	assert.NoError(t, err)
3100}
3101
3102func TestEmbeddedFolders(t *testing.T) {
3103	u := getTestUser()
3104	mappedPath := filepath.Join(os.TempDir(), "mapped_path")
3105	name := filepath.Base(mappedPath)
3106	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
3107		BaseVirtualFolder: vfs.BaseVirtualFolder{
3108			Name:            name,
3109			UsedQuotaFiles:  1000,
3110			UsedQuotaSize:   8192,
3111			LastQuotaUpdate: 123,
3112		},
3113		VirtualPath: "/vdir",
3114		QuotaSize:   4096,
3115		QuotaFiles:  1,
3116	})
3117	_, _, err := httpdtest.AddUser(u, http.StatusBadRequest)
3118	assert.NoError(t, err)
3119	u.VirtualFolders[0].MappedPath = mappedPath
3120	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
3121	assert.NoError(t, err)
3122	// check that the folder was created
3123	folder, _, err := httpdtest.GetFolderByName(name, http.StatusOK)
3124	assert.NoError(t, err)
3125	assert.Equal(t, mappedPath, folder.MappedPath)
3126	assert.Equal(t, 0, folder.UsedQuotaFiles)
3127	assert.Equal(t, int64(0), folder.UsedQuotaSize)
3128	assert.Equal(t, int64(0), folder.LastQuotaUpdate)
3129	if assert.Len(t, user.VirtualFolders, 1) {
3130		assert.Equal(t, mappedPath, user.VirtualFolders[0].MappedPath)
3131		assert.Equal(t, u.VirtualFolders[0].VirtualPath, user.VirtualFolders[0].VirtualPath)
3132		assert.Equal(t, u.VirtualFolders[0].QuotaFiles, user.VirtualFolders[0].QuotaFiles)
3133		assert.Equal(t, u.VirtualFolders[0].QuotaSize, user.VirtualFolders[0].QuotaSize)
3134	}
3135	// if the folder already exists we can just reference it by name while adding/updating a user
3136	u.Username = u.Username + "1"
3137	u.VirtualFolders[0].MappedPath = ""
3138	user1, _, err := httpdtest.AddUser(u, http.StatusCreated)
3139	assert.EqualError(t, err, "mapped path mismatch")
3140	if assert.Len(t, user1.VirtualFolders, 1) {
3141		assert.Equal(t, mappedPath, user1.VirtualFolders[0].MappedPath)
3142		assert.Equal(t, u.VirtualFolders[0].VirtualPath, user1.VirtualFolders[0].VirtualPath)
3143		assert.Equal(t, u.VirtualFolders[0].QuotaFiles, user1.VirtualFolders[0].QuotaFiles)
3144		assert.Equal(t, u.VirtualFolders[0].QuotaSize, user1.VirtualFolders[0].QuotaSize)
3145	}
3146	user1.VirtualFolders = u.VirtualFolders
3147	user1, _, err = httpdtest.UpdateUser(user1, http.StatusOK, "")
3148	assert.EqualError(t, err, "mapped path mismatch")
3149	if assert.Len(t, user1.VirtualFolders, 1) {
3150		assert.Equal(t, mappedPath, user1.VirtualFolders[0].MappedPath)
3151		assert.Equal(t, u.VirtualFolders[0].VirtualPath, user1.VirtualFolders[0].VirtualPath)
3152		assert.Equal(t, u.VirtualFolders[0].QuotaFiles, user1.VirtualFolders[0].QuotaFiles)
3153		assert.Equal(t, u.VirtualFolders[0].QuotaSize, user1.VirtualFolders[0].QuotaSize)
3154	}
3155	// now the virtual folder contains all the required paths
3156	user1, _, err = httpdtest.UpdateUser(user1, http.StatusOK, "")
3157	assert.NoError(t, err)
3158	if assert.Len(t, user1.VirtualFolders, 1) {
3159		assert.Equal(t, mappedPath, user1.VirtualFolders[0].MappedPath)
3160		assert.Equal(t, u.VirtualFolders[0].VirtualPath, user1.VirtualFolders[0].VirtualPath)
3161		assert.Equal(t, u.VirtualFolders[0].QuotaFiles, user1.VirtualFolders[0].QuotaFiles)
3162		assert.Equal(t, u.VirtualFolders[0].QuotaSize, user1.VirtualFolders[0].QuotaSize)
3163	}
3164
3165	_, err = httpdtest.RemoveUser(user, http.StatusOK)
3166	assert.NoError(t, err)
3167	_, err = httpdtest.RemoveUser(user1, http.StatusOK)
3168	assert.NoError(t, err)
3169
3170	_, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: name}, http.StatusOK)
3171	assert.NoError(t, err)
3172}
3173
3174func TestEmbeddedFoldersUpdate(t *testing.T) {
3175	u := getTestUser()
3176	mappedPath := filepath.Join(os.TempDir(), "mapped_path")
3177	name := filepath.Base(mappedPath)
3178	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
3179		BaseVirtualFolder: vfs.BaseVirtualFolder{
3180			Name:            name,
3181			MappedPath:      mappedPath,
3182			UsedQuotaFiles:  1000,
3183			UsedQuotaSize:   8192,
3184			LastQuotaUpdate: 123,
3185		},
3186		VirtualPath: "/vdir",
3187		QuotaSize:   4096,
3188		QuotaFiles:  1,
3189	})
3190	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
3191	assert.NoError(t, err)
3192	folder, _, err := httpdtest.GetFolderByName(name, http.StatusOK)
3193	assert.NoError(t, err)
3194	assert.Equal(t, mappedPath, folder.MappedPath)
3195	assert.Equal(t, 0, folder.UsedQuotaFiles)
3196	assert.Equal(t, int64(0), folder.UsedQuotaSize)
3197	assert.Equal(t, int64(0), folder.LastQuotaUpdate)
3198	assert.Empty(t, folder.Description)
3199	assert.Equal(t, sdk.LocalFilesystemProvider, folder.FsConfig.Provider)
3200	assert.Len(t, folder.Users, 1)
3201	assert.Contains(t, folder.Users, user.Username)
3202	// update a field on the folder
3203	description := "updatedDesc"
3204	folder.MappedPath = mappedPath + "_update"
3205	folder.Description = description
3206	folder, _, err = httpdtest.UpdateFolder(folder, http.StatusOK)
3207	assert.NoError(t, err)
3208	assert.Equal(t, mappedPath+"_update", folder.MappedPath)
3209	assert.Equal(t, 0, folder.UsedQuotaFiles)
3210	assert.Equal(t, int64(0), folder.UsedQuotaSize)
3211	assert.Equal(t, int64(0), folder.LastQuotaUpdate)
3212	assert.Equal(t, description, folder.Description)
3213	assert.Equal(t, sdk.LocalFilesystemProvider, folder.FsConfig.Provider)
3214	// check that the user gets the changes
3215	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
3216	assert.NoError(t, err)
3217	userFolder := user.VirtualFolders[0].BaseVirtualFolder
3218	assert.Equal(t, mappedPath+"_update", folder.MappedPath)
3219	assert.Equal(t, 0, userFolder.UsedQuotaFiles)
3220	assert.Equal(t, int64(0), userFolder.UsedQuotaSize)
3221	assert.Equal(t, int64(0), userFolder.LastQuotaUpdate)
3222	assert.Equal(t, description, userFolder.Description)
3223	assert.Equal(t, sdk.LocalFilesystemProvider, userFolder.FsConfig.Provider)
3224	// now update the folder embedding it inside the user
3225	user.VirtualFolders = []vfs.VirtualFolder{
3226		{
3227			BaseVirtualFolder: vfs.BaseVirtualFolder{
3228				Name:            name,
3229				MappedPath:      "",
3230				UsedQuotaFiles:  1000,
3231				UsedQuotaSize:   8192,
3232				LastQuotaUpdate: 123,
3233				FsConfig: vfs.Filesystem{
3234					Provider: sdk.S3FilesystemProvider,
3235					S3Config: vfs.S3FsConfig{
3236						S3FsConfig: sdk.S3FsConfig{
3237							Bucket:       "test",
3238							Region:       "us-east-1",
3239							AccessKey:    "akey",
3240							AccessSecret: kms.NewPlainSecret("asecret"),
3241							Endpoint:     "http://127.0.1.1:9090",
3242						},
3243					},
3244				},
3245			},
3246			VirtualPath: "/vdir1",
3247			QuotaSize:   4096,
3248			QuotaFiles:  1,
3249		},
3250	}
3251	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
3252	assert.NoError(t, err)
3253	userFolder = user.VirtualFolders[0].BaseVirtualFolder
3254	assert.Equal(t, 0, userFolder.UsedQuotaFiles)
3255	assert.Equal(t, int64(0), userFolder.UsedQuotaSize)
3256	assert.Equal(t, int64(0), userFolder.LastQuotaUpdate)
3257	assert.Empty(t, userFolder.Description)
3258	assert.Equal(t, sdk.S3FilesystemProvider, userFolder.FsConfig.Provider)
3259	assert.Equal(t, "test", userFolder.FsConfig.S3Config.Bucket)
3260	assert.Equal(t, "us-east-1", userFolder.FsConfig.S3Config.Region)
3261	assert.Equal(t, "http://127.0.1.1:9090", userFolder.FsConfig.S3Config.Endpoint)
3262	assert.Equal(t, kms.SecretStatusSecretBox, userFolder.FsConfig.S3Config.AccessSecret.GetStatus())
3263	assert.NotEmpty(t, userFolder.FsConfig.S3Config.AccessSecret.GetPayload())
3264	assert.Empty(t, userFolder.FsConfig.S3Config.AccessSecret.GetKey())
3265	assert.Empty(t, userFolder.FsConfig.S3Config.AccessSecret.GetAdditionalData())
3266	// confirm the changes
3267	folder, _, err = httpdtest.GetFolderByName(name, http.StatusOK)
3268	assert.NoError(t, err)
3269	assert.Equal(t, 0, folder.UsedQuotaFiles)
3270	assert.Equal(t, int64(0), folder.UsedQuotaSize)
3271	assert.Equal(t, int64(0), folder.LastQuotaUpdate)
3272	assert.Empty(t, folder.Description)
3273	assert.Equal(t, sdk.S3FilesystemProvider, folder.FsConfig.Provider)
3274	assert.Equal(t, "test", folder.FsConfig.S3Config.Bucket)
3275	assert.Equal(t, "us-east-1", folder.FsConfig.S3Config.Region)
3276	assert.Equal(t, "http://127.0.1.1:9090", folder.FsConfig.S3Config.Endpoint)
3277	assert.Equal(t, kms.SecretStatusSecretBox, folder.FsConfig.S3Config.AccessSecret.GetStatus())
3278	assert.NotEmpty(t, folder.FsConfig.S3Config.AccessSecret.GetPayload())
3279	assert.Empty(t, folder.FsConfig.S3Config.AccessSecret.GetKey())
3280	assert.Empty(t, folder.FsConfig.S3Config.AccessSecret.GetAdditionalData())
3281	// now update folder usage limits and check that a folder update will not change them
3282	folder.UsedQuotaFiles = 100
3283	folder.UsedQuotaSize = 32768
3284	_, err = httpdtest.UpdateFolderQuotaUsage(folder, "reset", http.StatusOK)
3285	assert.NoError(t, err)
3286	folder, _, err = httpdtest.GetFolderByName(name, http.StatusOK)
3287	assert.NoError(t, err)
3288	assert.Equal(t, 100, folder.UsedQuotaFiles)
3289	assert.Equal(t, int64(32768), folder.UsedQuotaSize)
3290	assert.Greater(t, folder.LastQuotaUpdate, int64(0))
3291	assert.Equal(t, sdk.S3FilesystemProvider, folder.FsConfig.Provider)
3292	assert.Equal(t, "test", folder.FsConfig.S3Config.Bucket)
3293	assert.Equal(t, "us-east-1", folder.FsConfig.S3Config.Region)
3294	assert.Equal(t, "http://127.0.1.1:9090", folder.FsConfig.S3Config.Endpoint)
3295	assert.Equal(t, kms.SecretStatusSecretBox, folder.FsConfig.S3Config.AccessSecret.GetStatus())
3296	assert.NotEmpty(t, folder.FsConfig.S3Config.AccessSecret.GetPayload())
3297	assert.Empty(t, folder.FsConfig.S3Config.AccessSecret.GetKey())
3298	assert.Empty(t, folder.FsConfig.S3Config.AccessSecret.GetAdditionalData())
3299
3300	user.VirtualFolders[0].FsConfig.S3Config.AccessSecret = kms.NewPlainSecret("updated secret")
3301	user, resp, err := httpdtest.UpdateUser(user, http.StatusOK, "")
3302	assert.NoError(t, err, string(resp))
3303	userFolder = user.VirtualFolders[0].BaseVirtualFolder
3304	assert.Equal(t, 100, userFolder.UsedQuotaFiles)
3305	assert.Equal(t, int64(32768), userFolder.UsedQuotaSize)
3306	assert.Greater(t, userFolder.LastQuotaUpdate, int64(0))
3307	assert.Empty(t, userFolder.Description)
3308	assert.Equal(t, sdk.S3FilesystemProvider, userFolder.FsConfig.Provider)
3309	assert.Equal(t, "test", userFolder.FsConfig.S3Config.Bucket)
3310	assert.Equal(t, "us-east-1", userFolder.FsConfig.S3Config.Region)
3311	assert.Equal(t, "http://127.0.1.1:9090", userFolder.FsConfig.S3Config.Endpoint)
3312	assert.Equal(t, kms.SecretStatusSecretBox, userFolder.FsConfig.S3Config.AccessSecret.GetStatus())
3313	assert.NotEmpty(t, userFolder.FsConfig.S3Config.AccessSecret.GetPayload())
3314	assert.Empty(t, userFolder.FsConfig.S3Config.AccessSecret.GetKey())
3315	assert.Empty(t, userFolder.FsConfig.S3Config.AccessSecret.GetAdditionalData())
3316
3317	_, err = httpdtest.RemoveUser(user, http.StatusOK)
3318	assert.NoError(t, err)
3319	_, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: name}, http.StatusOK)
3320	assert.NoError(t, err)
3321}
3322
3323func TestUpdateFolderQuotaUsage(t *testing.T) {
3324	f := vfs.BaseVirtualFolder{
3325		Name:       "vdir",
3326		MappedPath: filepath.Join(os.TempDir(), "folder"),
3327	}
3328	usedQuotaFiles := 1
3329	usedQuotaSize := int64(65535)
3330	f.UsedQuotaFiles = usedQuotaFiles
3331	f.UsedQuotaSize = usedQuotaSize
3332	folder, _, err := httpdtest.AddFolder(f, http.StatusCreated)
3333	if assert.NoError(t, err) {
3334		assert.Equal(t, usedQuotaFiles, folder.UsedQuotaFiles)
3335		assert.Equal(t, usedQuotaSize, folder.UsedQuotaSize)
3336	}
3337	_, err = httpdtest.UpdateFolderQuotaUsage(folder, "invalid mode", http.StatusBadRequest)
3338	assert.NoError(t, err)
3339	_, err = httpdtest.UpdateFolderQuotaUsage(f, "reset", http.StatusOK)
3340	assert.NoError(t, err)
3341	folder, _, err = httpdtest.GetFolderByName(f.Name, http.StatusOK)
3342	assert.NoError(t, err)
3343	assert.Equal(t, usedQuotaFiles, folder.UsedQuotaFiles)
3344	assert.Equal(t, usedQuotaSize, folder.UsedQuotaSize)
3345	_, err = httpdtest.UpdateFolderQuotaUsage(f, "add", http.StatusOK)
3346	assert.NoError(t, err)
3347	folder, _, err = httpdtest.GetFolderByName(f.Name, http.StatusOK)
3348	assert.NoError(t, err)
3349	assert.Equal(t, 2*usedQuotaFiles, folder.UsedQuotaFiles)
3350	assert.Equal(t, 2*usedQuotaSize, folder.UsedQuotaSize)
3351	f.UsedQuotaSize = -1
3352	_, err = httpdtest.UpdateFolderQuotaUsage(f, "", http.StatusBadRequest)
3353	assert.NoError(t, err)
3354	f.UsedQuotaSize = usedQuotaSize
3355	f.Name = f.Name + "1"
3356	_, err = httpdtest.UpdateFolderQuotaUsage(f, "", http.StatusNotFound)
3357	assert.NoError(t, err)
3358	_, err = httpdtest.RemoveFolder(folder, http.StatusOK)
3359	assert.NoError(t, err)
3360}
3361
3362func TestGetVersion(t *testing.T) {
3363	_, _, err := httpdtest.GetVersion(http.StatusOK)
3364	assert.NoError(t, err)
3365	_, _, err = httpdtest.GetVersion(http.StatusInternalServerError)
3366	assert.Error(t, err, "get version request must succeed, we requested to check a wrong status code")
3367}
3368
3369func TestGetStatus(t *testing.T) {
3370	_, _, err := httpdtest.GetStatus(http.StatusOK)
3371	assert.NoError(t, err)
3372	_, _, err = httpdtest.GetStatus(http.StatusBadRequest)
3373	assert.Error(t, err, "get provider status request must succeed, we requested to check a wrong status code")
3374}
3375
3376func TestGetConnections(t *testing.T) {
3377	_, _, err := httpdtest.GetConnections(http.StatusOK)
3378	assert.NoError(t, err)
3379	_, _, err = httpdtest.GetConnections(http.StatusInternalServerError)
3380	assert.Error(t, err, "get sftp connections request must succeed, we requested to check a wrong status code")
3381}
3382
3383func TestCloseActiveConnection(t *testing.T) {
3384	_, err := httpdtest.CloseConnection("non_existent_id", http.StatusNotFound)
3385	assert.NoError(t, err)
3386	user := getTestUser()
3387	c := common.NewBaseConnection("connID", common.ProtocolSFTP, "", "", user)
3388	fakeConn := &fakeConnection{
3389		BaseConnection: c,
3390	}
3391	common.Connections.Add(fakeConn)
3392	_, err = httpdtest.CloseConnection(c.GetID(), http.StatusOK)
3393	assert.NoError(t, err)
3394	assert.Len(t, common.Connections.GetStats(), 0)
3395}
3396
3397func TestCloseConnectionAfterUserUpdateDelete(t *testing.T) {
3398	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
3399	assert.NoError(t, err)
3400	c := common.NewBaseConnection("connID", common.ProtocolFTP, "", "", user)
3401	fakeConn := &fakeConnection{
3402		BaseConnection: c,
3403	}
3404	common.Connections.Add(fakeConn)
3405	c1 := common.NewBaseConnection("connID1", common.ProtocolSFTP, "", "", user)
3406	fakeConn1 := &fakeConnection{
3407		BaseConnection: c1,
3408	}
3409	common.Connections.Add(fakeConn1)
3410	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "0")
3411	assert.NoError(t, err)
3412	assert.Len(t, common.Connections.GetStats(), 2)
3413	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "1")
3414	assert.NoError(t, err)
3415	assert.Len(t, common.Connections.GetStats(), 0)
3416
3417	common.Connections.Add(fakeConn)
3418	common.Connections.Add(fakeConn1)
3419	assert.Len(t, common.Connections.GetStats(), 2)
3420	_, err = httpdtest.RemoveUser(user, http.StatusOK)
3421	assert.NoError(t, err)
3422	assert.Len(t, common.Connections.GetStats(), 0)
3423}
3424
3425func TestSkipNaturalKeysValidation(t *testing.T) {
3426	smtpCfg := smtp.Config{
3427		Host:          "127.0.0.1",
3428		Port:          3525,
3429		TemplatesPath: "templates",
3430	}
3431	err := smtpCfg.Initialize("..")
3432	require.NoError(t, err)
3433	err = dataprovider.Close()
3434	assert.NoError(t, err)
3435	err = config.LoadConfig(configDir, "")
3436	assert.NoError(t, err)
3437	providerConf := config.GetProviderConf()
3438	providerConf.SkipNaturalKeysValidation = true
3439	err = dataprovider.Initialize(providerConf, configDir, true)
3440	assert.NoError(t, err)
3441
3442	u := getTestUser()
3443	u.Username = "user@user.me"
3444	u.Email = u.Username
3445	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
3446	assert.NoError(t, err)
3447	user.AdditionalInfo = "info"
3448	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
3449	assert.NoError(t, err)
3450	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
3451	assert.NoError(t, err)
3452
3453	a := getTestAdmin()
3454	a.Username = "admin@example.com"
3455	admin, _, err := httpdtest.AddAdmin(a, http.StatusCreated)
3456	assert.NoError(t, err)
3457	admin.Email = admin.Username
3458	admin, _, err = httpdtest.UpdateAdmin(admin, http.StatusOK)
3459	assert.NoError(t, err)
3460	admin, _, err = httpdtest.GetAdminByUsername(admin.Username, http.StatusOK)
3461	assert.NoError(t, err)
3462
3463	f := vfs.BaseVirtualFolder{
3464		Name:       "文件夹",
3465		MappedPath: filepath.Clean(os.TempDir()),
3466	}
3467	folder, resp, err := httpdtest.AddFolder(f, http.StatusCreated)
3468	assert.NoError(t, err, string(resp))
3469	folder.Description = folder.Name
3470	folder, resp, err = httpdtest.UpdateFolder(folder, http.StatusOK)
3471	assert.NoError(t, err, string(resp))
3472	folder, resp, err = httpdtest.GetFolderByName(folder.Name, http.StatusOK)
3473	assert.NoError(t, err, string(resp))
3474	_, err = httpdtest.RemoveFolder(folder, http.StatusOK)
3475	assert.NoError(t, err)
3476
3477	err = dataprovider.Close()
3478	assert.NoError(t, err)
3479	err = config.LoadConfig(configDir, "")
3480	assert.NoError(t, err)
3481	providerConf = config.GetProviderConf()
3482	providerConf.CredentialsPath = credentialsPath
3483	err = os.RemoveAll(credentialsPath)
3484	assert.NoError(t, err)
3485	err = dataprovider.Initialize(providerConf, configDir, true)
3486	assert.NoError(t, err)
3487	if config.GetProviderConf().Driver == dataprovider.MemoryDataProviderName {
3488		return
3489	}
3490
3491	csrfToken, err := getCSRFToken(httpBaseURL + webClientLoginPath)
3492	assert.NoError(t, err)
3493	token, err := getJWTWebClientTokenFromTestServer(user.Username, defaultPassword)
3494	assert.NoError(t, err)
3495	form := make(url.Values)
3496	form.Set(csrfFormToken, csrfToken)
3497	req, err := http.NewRequest(http.MethodPost, webClientProfilePath, bytes.NewBuffer([]byte(form.Encode())))
3498	assert.NoError(t, err)
3499	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
3500	setJWTCookieForReq(req, token)
3501	rr := executeRequest(req)
3502	checkResponseCode(t, http.StatusOK, rr)
3503	assert.Contains(t, rr.Body.String(), "the following characters are allowed")
3504	// test user reset password
3505	form = make(url.Values)
3506	form.Set("username", user.Username)
3507	form.Set(csrfFormToken, csrfToken)
3508	lastResetCode = ""
3509	req, err = http.NewRequest(http.MethodPost, webClientForgotPwdPath, bytes.NewBuffer([]byte(form.Encode())))
3510	assert.NoError(t, err)
3511	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
3512	rr = executeRequest(req)
3513	assert.Equal(t, http.StatusFound, rr.Code)
3514	assert.GreaterOrEqual(t, len(lastResetCode), 20)
3515	form = make(url.Values)
3516	form.Set(csrfFormToken, csrfToken)
3517	form.Set("code", lastResetCode)
3518	form.Set("password", defaultPassword)
3519	req, err = http.NewRequest(http.MethodPost, webClientResetPwdPath, bytes.NewBuffer([]byte(form.Encode())))
3520	assert.NoError(t, err)
3521	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
3522	rr = executeRequest(req)
3523	assert.Equal(t, http.StatusOK, rr.Code)
3524	assert.Contains(t, rr.Body.String(), "Unable to set the new password")
3525
3526	adminAPIToken, err := getJWTAPITokenFromTestServer(admin.Username, defaultTokenAuthPass)
3527	assert.NoError(t, err)
3528	userAPIToken, err := getJWTAPIUserTokenFromTestServer(user.Username, defaultPassword)
3529	assert.NoError(t, err)
3530	req, err = http.NewRequest(http.MethodPut, userPath+"/"+user.Username+"/2fa/disable", nil)
3531	assert.NoError(t, err)
3532	setBearerForReq(req, adminAPIToken)
3533	rr = executeRequest(req)
3534	checkResponseCode(t, http.StatusBadRequest, rr)
3535	assert.Contains(t, rr.Body.String(), "the following characters are allowed")
3536
3537	req, err = http.NewRequest(http.MethodPost, user2FARecoveryCodesPath, nil)
3538	assert.NoError(t, err)
3539	setBearerForReq(req, userAPIToken)
3540	rr = executeRequest(req)
3541	checkResponseCode(t, http.StatusBadRequest, rr)
3542	assert.Contains(t, rr.Body.String(), "the following characters are allowed")
3543
3544	apiKeyAuthReq := make(map[string]bool)
3545	apiKeyAuthReq["allow_api_key_auth"] = true
3546	asJSON, err := json.Marshal(apiKeyAuthReq)
3547	assert.NoError(t, err)
3548	req, err = http.NewRequest(http.MethodPut, userProfilePath, bytes.NewBuffer(asJSON))
3549	assert.NoError(t, err)
3550	setBearerForReq(req, userAPIToken)
3551	rr = executeRequest(req)
3552	checkResponseCode(t, http.StatusBadRequest, rr)
3553	assert.Contains(t, rr.Body.String(), "the following characters are allowed")
3554
3555	_, err = httpdtest.RemoveUser(user, http.StatusOK)
3556	assert.NoError(t, err)
3557	err = os.RemoveAll(user.GetHomeDir())
3558	assert.NoError(t, err)
3559
3560	token, err = getJWTWebTokenFromTestServer(admin.Username, defaultTokenAuthPass)
3561	assert.NoError(t, err)
3562	csrfToken, err = getCSRFToken(httpBaseURL + webLoginPath)
3563	assert.NoError(t, err)
3564	form = make(url.Values)
3565	form.Set(csrfFormToken, csrfToken)
3566	req, _ = http.NewRequest(http.MethodPost, webAdminProfilePath, bytes.NewBuffer([]byte(form.Encode())))
3567	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
3568	setJWTCookieForReq(req, token)
3569	rr = executeRequest(req)
3570	checkResponseCode(t, http.StatusOK, rr)
3571	assert.Contains(t, rr.Body.String(), "the following characters are allowed")
3572
3573	apiKeyAuthReq = make(map[string]bool)
3574	apiKeyAuthReq["allow_api_key_auth"] = true
3575	asJSON, err = json.Marshal(apiKeyAuthReq)
3576	assert.NoError(t, err)
3577	req, err = http.NewRequest(http.MethodPut, adminProfilePath, bytes.NewBuffer(asJSON))
3578	assert.NoError(t, err)
3579	setBearerForReq(req, adminAPIToken)
3580	rr = executeRequest(req)
3581	checkResponseCode(t, http.StatusBadRequest, rr)
3582	assert.Contains(t, rr.Body.String(), "the following characters are allowed")
3583
3584	req, err = http.NewRequest(http.MethodPut, adminPath+"/"+admin.Username+"/2fa/disable", nil)
3585	assert.NoError(t, err)
3586	setBearerForReq(req, adminAPIToken)
3587	rr = executeRequest(req)
3588	checkResponseCode(t, http.StatusBadRequest, rr)
3589	assert.Contains(t, rr.Body.String(), "the following characters are allowed")
3590
3591	req, err = http.NewRequest(http.MethodPost, admin2FARecoveryCodesPath, nil)
3592	assert.NoError(t, err)
3593	setBearerForReq(req, adminAPIToken)
3594	rr = executeRequest(req)
3595	checkResponseCode(t, http.StatusBadRequest, rr)
3596	assert.Contains(t, rr.Body.String(), "the following characters are allowed")
3597	// test admin reset password
3598	form = make(url.Values)
3599	form.Set("username", admin.Username)
3600	form.Set(csrfFormToken, csrfToken)
3601	lastResetCode = ""
3602	req, err = http.NewRequest(http.MethodPost, webAdminForgotPwdPath, bytes.NewBuffer([]byte(form.Encode())))
3603	assert.NoError(t, err)
3604	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
3605	rr = executeRequest(req)
3606	assert.Equal(t, http.StatusFound, rr.Code)
3607	assert.GreaterOrEqual(t, len(lastResetCode), 20)
3608	form = make(url.Values)
3609	form.Set(csrfFormToken, csrfToken)
3610	form.Set("code", lastResetCode)
3611	form.Set("password", defaultPassword)
3612	req, err = http.NewRequest(http.MethodPost, webAdminResetPwdPath, bytes.NewBuffer([]byte(form.Encode())))
3613	assert.NoError(t, err)
3614	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
3615	rr = executeRequest(req)
3616	assert.Equal(t, http.StatusOK, rr.Code)
3617	assert.Contains(t, rr.Body.String(), "Unable to set the new password")
3618
3619	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
3620	assert.NoError(t, err)
3621
3622	smtpCfg = smtp.Config{}
3623	err = smtpCfg.Initialize("..")
3624	require.NoError(t, err)
3625}
3626
3627func TestSaveErrors(t *testing.T) {
3628	err := dataprovider.Close()
3629	assert.NoError(t, err)
3630	err = config.LoadConfig(configDir, "")
3631	assert.NoError(t, err)
3632	providerConf := config.GetProviderConf()
3633	providerConf.SkipNaturalKeysValidation = true
3634	err = dataprovider.Initialize(providerConf, configDir, true)
3635	assert.NoError(t, err)
3636
3637	recCode := "recovery code"
3638	recoveryCodes := []sdk.RecoveryCode{
3639		{
3640			Secret: kms.NewPlainSecret(recCode),
3641			Used:   false,
3642		},
3643	}
3644
3645	u := getTestUser()
3646	u.Username = "user@example.com"
3647	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
3648	assert.NoError(t, err)
3649	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
3650	assert.NoError(t, err)
3651	configName, _, secret, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], user.Username)
3652	assert.NoError(t, err)
3653	user.Password = u.Password
3654	user.Filters.TOTPConfig = sdk.TOTPConfig{
3655		Enabled:    true,
3656		ConfigName: configName,
3657		Secret:     kms.NewPlainSecret(secret),
3658		Protocols:  []string{common.ProtocolSSH, common.ProtocolHTTP},
3659	}
3660	user.Filters.RecoveryCodes = recoveryCodes
3661	err = dataprovider.UpdateUser(&user, "", "")
3662	assert.NoError(t, err)
3663	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
3664	assert.NoError(t, err)
3665	assert.True(t, user.Filters.TOTPConfig.Enabled)
3666	assert.Len(t, user.Filters.RecoveryCodes, 1)
3667
3668	a := getTestAdmin()
3669	a.Username = "admin@example.com"
3670	admin, _, err := httpdtest.AddAdmin(a, http.StatusCreated)
3671	assert.NoError(t, err)
3672	admin.Email = admin.Username
3673	admin, _, err = httpdtest.UpdateAdmin(admin, http.StatusOK)
3674	assert.NoError(t, err)
3675	admin.Password = a.Password
3676	admin.Filters.TOTPConfig = dataprovider.TOTPConfig{
3677		Enabled:    true,
3678		ConfigName: configName,
3679		Secret:     kms.NewPlainSecret(secret),
3680	}
3681	admin.Filters.RecoveryCodes = recoveryCodes
3682	err = dataprovider.UpdateAdmin(&admin, "", "")
3683	assert.NoError(t, err)
3684	admin, _, err = httpdtest.GetAdminByUsername(admin.Username, http.StatusOK)
3685	assert.NoError(t, err)
3686	assert.True(t, admin.Filters.TOTPConfig.Enabled)
3687	assert.Len(t, admin.Filters.RecoveryCodes, 1)
3688
3689	err = dataprovider.Close()
3690	assert.NoError(t, err)
3691	err = config.LoadConfig(configDir, "")
3692	assert.NoError(t, err)
3693	providerConf = config.GetProviderConf()
3694	providerConf.CredentialsPath = credentialsPath
3695	err = os.RemoveAll(credentialsPath)
3696	assert.NoError(t, err)
3697	err = dataprovider.Initialize(providerConf, configDir, true)
3698	assert.NoError(t, err)
3699	if config.GetProviderConf().Driver == dataprovider.MemoryDataProviderName {
3700		return
3701	}
3702
3703	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
3704	assert.NoError(t, err)
3705	form := getLoginForm(a.Username, a.Password, csrfToken)
3706	req, err := http.NewRequest(http.MethodPost, webLoginPath, bytes.NewBuffer([]byte(form.Encode())))
3707	assert.NoError(t, err)
3708	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
3709	rr := executeRequest(req)
3710	assert.Equal(t, http.StatusFound, rr.Code)
3711	assert.Equal(t, webAdminTwoFactorPath, rr.Header().Get("Location"))
3712	cookie, err := getCookieFromResponse(rr)
3713	assert.NoError(t, err)
3714
3715	form = make(url.Values)
3716	form.Set("recovery_code", recCode)
3717	form.Set(csrfFormToken, csrfToken)
3718	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
3719	assert.NoError(t, err)
3720	setJWTCookieForReq(req, cookie)
3721	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
3722	rr = executeRequest(req)
3723	assert.Equal(t, http.StatusInternalServerError, rr.Code)
3724	assert.Contains(t, rr.Body.String(), "unable to set the recovery code as used")
3725
3726	csrfToken, err = getCSRFToken(httpBaseURL + webClientLoginPath)
3727	assert.NoError(t, err)
3728	form = getLoginForm(u.Username, u.Password, csrfToken)
3729	req, err = http.NewRequest(http.MethodPost, webClientLoginPath, bytes.NewBuffer([]byte(form.Encode())))
3730	assert.NoError(t, err)
3731	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
3732	rr = executeRequest(req)
3733	assert.Equal(t, http.StatusFound, rr.Code)
3734	assert.Equal(t, webClientTwoFactorPath, rr.Header().Get("Location"))
3735	cookie, err = getCookieFromResponse(rr)
3736	assert.NoError(t, err)
3737
3738	form = make(url.Values)
3739	form.Set("recovery_code", recCode)
3740	form.Set(csrfFormToken, csrfToken)
3741	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
3742	assert.NoError(t, err)
3743	setJWTCookieForReq(req, cookie)
3744	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
3745	rr = executeRequest(req)
3746	assert.Equal(t, http.StatusInternalServerError, rr.Code)
3747	assert.Contains(t, rr.Body.String(), "unable to set the recovery code as used")
3748
3749	_, err = httpdtest.RemoveUser(user, http.StatusOK)
3750	assert.NoError(t, err)
3751	err = os.RemoveAll(user.GetHomeDir())
3752	assert.NoError(t, err)
3753
3754	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
3755	assert.NoError(t, err)
3756}
3757
3758func TestUserBaseDir(t *testing.T) {
3759	err := dataprovider.Close()
3760	assert.NoError(t, err)
3761	err = config.LoadConfig(configDir, "")
3762	assert.NoError(t, err)
3763	providerConf := config.GetProviderConf()
3764	providerConf.UsersBaseDir = homeBasePath
3765	err = dataprovider.Initialize(providerConf, configDir, true)
3766	assert.NoError(t, err)
3767	u := getTestUser()
3768	u.HomeDir = ""
3769	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
3770	if assert.Error(t, err) {
3771		assert.EqualError(t, err, "home dir mismatch")
3772	}
3773	assert.Equal(t, filepath.Join(providerConf.UsersBaseDir, u.Username), user.HomeDir)
3774	_, err = httpdtest.RemoveUser(user, http.StatusOK)
3775	assert.NoError(t, err)
3776	err = dataprovider.Close()
3777	assert.NoError(t, err)
3778	err = config.LoadConfig(configDir, "")
3779	assert.NoError(t, err)
3780	providerConf = config.GetProviderConf()
3781	providerConf.CredentialsPath = credentialsPath
3782	err = os.RemoveAll(credentialsPath)
3783	assert.NoError(t, err)
3784	err = dataprovider.Initialize(providerConf, configDir, true)
3785	assert.NoError(t, err)
3786}
3787
3788func TestQuotaTrackingDisabled(t *testing.T) {
3789	err := dataprovider.Close()
3790	assert.NoError(t, err)
3791	err = config.LoadConfig(configDir, "")
3792	assert.NoError(t, err)
3793	providerConf := config.GetProviderConf()
3794	providerConf.TrackQuota = 0
3795	err = dataprovider.Initialize(providerConf, configDir, true)
3796	assert.NoError(t, err)
3797	// user quota scan must fail
3798	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
3799	assert.NoError(t, err)
3800	_, err = httpdtest.StartQuotaScan(user, http.StatusForbidden)
3801	assert.NoError(t, err)
3802	_, err = httpdtest.UpdateQuotaUsage(user, "", http.StatusForbidden)
3803	assert.NoError(t, err)
3804	_, err = httpdtest.RemoveUser(user, http.StatusOK)
3805	assert.NoError(t, err)
3806	// folder quota scan must fail
3807	folder := vfs.BaseVirtualFolder{
3808		Name:       "folder_quota_test",
3809		MappedPath: filepath.Clean(os.TempDir()),
3810	}
3811	folder, resp, err := httpdtest.AddFolder(folder, http.StatusCreated)
3812	assert.NoError(t, err, string(resp))
3813	_, err = httpdtest.StartFolderQuotaScan(folder, http.StatusForbidden)
3814	assert.NoError(t, err)
3815	_, err = httpdtest.UpdateFolderQuotaUsage(folder, "", http.StatusForbidden)
3816	assert.NoError(t, err)
3817	_, err = httpdtest.RemoveFolder(folder, http.StatusOK)
3818	assert.NoError(t, err)
3819
3820	err = dataprovider.Close()
3821	assert.NoError(t, err)
3822	err = config.LoadConfig(configDir, "")
3823	assert.NoError(t, err)
3824	providerConf = config.GetProviderConf()
3825	providerConf.CredentialsPath = credentialsPath
3826	err = os.RemoveAll(credentialsPath)
3827	assert.NoError(t, err)
3828	err = dataprovider.Initialize(providerConf, configDir, true)
3829	assert.NoError(t, err)
3830}
3831
3832func TestProviderErrors(t *testing.T) {
3833	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
3834	assert.NoError(t, err)
3835	userAPIToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
3836	assert.NoError(t, err)
3837	userWebToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
3838	assert.NoError(t, err)
3839	_, err = httpdtest.RemoveUser(user, http.StatusOK)
3840	assert.NoError(t, err)
3841	token, _, err := httpdtest.GetToken(defaultTokenAuthUser, defaultTokenAuthPass)
3842	assert.NoError(t, err)
3843	testServerToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
3844	assert.NoError(t, err)
3845	httpdtest.SetJWTToken(token)
3846	err = dataprovider.Close()
3847	assert.NoError(t, err)
3848	_, _, err = httpdtest.GetUserByUsername("na", http.StatusInternalServerError)
3849	assert.NoError(t, err)
3850	_, _, err = httpdtest.GetUsers(1, 0, http.StatusInternalServerError)
3851	assert.NoError(t, err)
3852	_, _, err = httpdtest.GetAdmins(1, 0, http.StatusInternalServerError)
3853	assert.NoError(t, err)
3854	_, _, err = httpdtest.GetAPIKeys(1, 0, http.StatusInternalServerError)
3855	assert.NoError(t, err)
3856	req, err := http.NewRequest(http.MethodGet, userSharesPath, nil)
3857	assert.NoError(t, err)
3858	setBearerForReq(req, userAPIToken)
3859	rr := executeRequest(req)
3860	checkResponseCode(t, http.StatusInternalServerError, rr)
3861
3862	// password reset errors
3863	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
3864	assert.NoError(t, err)
3865	form := make(url.Values)
3866	form.Set("username", "username")
3867	form.Set(csrfFormToken, csrfToken)
3868	req, err = http.NewRequest(http.MethodPost, webClientForgotPwdPath, bytes.NewBuffer([]byte(form.Encode())))
3869	assert.NoError(t, err)
3870	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
3871	rr = executeRequest(req)
3872	assert.Equal(t, http.StatusOK, rr.Code)
3873	assert.Contains(t, rr.Body.String(), "Error retrieving your account, please try again later")
3874
3875	req, err = http.NewRequest(http.MethodGet, webClientSharesPath, nil)
3876	assert.NoError(t, err)
3877	setJWTCookieForReq(req, userWebToken)
3878	rr = executeRequest(req)
3879	checkResponseCode(t, http.StatusInternalServerError, rr)
3880
3881	req, err = http.NewRequest(http.MethodGet, webClientSharePath+"/shareID", nil)
3882	assert.NoError(t, err)
3883	setJWTCookieForReq(req, userWebToken)
3884	rr = executeRequest(req)
3885	checkResponseCode(t, http.StatusInternalServerError, rr)
3886
3887	req, err = http.NewRequest(http.MethodPost, webClientSharePath+"/shareID", nil)
3888	assert.NoError(t, err)
3889	setJWTCookieForReq(req, userWebToken)
3890	rr = executeRequest(req)
3891	checkResponseCode(t, http.StatusInternalServerError, rr)
3892
3893	_, _, err = httpdtest.UpdateUser(dataprovider.User{BaseUser: sdk.BaseUser{Username: "auser"}}, http.StatusInternalServerError, "")
3894	assert.NoError(t, err)
3895	_, err = httpdtest.RemoveUser(dataprovider.User{BaseUser: sdk.BaseUser{Username: "auser"}}, http.StatusInternalServerError)
3896	assert.NoError(t, err)
3897	_, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: "aname"}, http.StatusInternalServerError)
3898	assert.NoError(t, err)
3899	status, _, err := httpdtest.GetStatus(http.StatusOK)
3900	if assert.NoError(t, err) {
3901		assert.False(t, status.DataProvider.IsActive)
3902	}
3903	_, _, err = httpdtest.Dumpdata("backup.json", "", "", http.StatusInternalServerError)
3904	assert.NoError(t, err)
3905	_, _, err = httpdtest.GetFolders(0, 0, http.StatusInternalServerError)
3906	assert.NoError(t, err)
3907	user = getTestUser()
3908	user.ID = 1
3909	backupData := dataprovider.BackupData{}
3910	backupData.Users = append(backupData.Users, user)
3911	backupContent, err := json.Marshal(backupData)
3912	assert.NoError(t, err)
3913	backupFilePath := filepath.Join(backupsPath, "backup.json")
3914	err = os.WriteFile(backupFilePath, backupContent, os.ModePerm)
3915	assert.NoError(t, err)
3916	_, _, err = httpdtest.Loaddata(backupFilePath, "", "", http.StatusInternalServerError)
3917	assert.NoError(t, err)
3918	backupData.Folders = append(backupData.Folders, vfs.BaseVirtualFolder{Name: "testFolder", MappedPath: filepath.Clean(os.TempDir())})
3919	backupContent, err = json.Marshal(backupData)
3920	assert.NoError(t, err)
3921	err = os.WriteFile(backupFilePath, backupContent, os.ModePerm)
3922	assert.NoError(t, err)
3923	_, _, err = httpdtest.Loaddata(backupFilePath, "", "", http.StatusInternalServerError)
3924	assert.NoError(t, err)
3925	backupData.Users = nil
3926	backupData.Folders = nil
3927	backupData.Admins = append(backupData.Admins, getTestAdmin())
3928	backupContent, err = json.Marshal(backupData)
3929	assert.NoError(t, err)
3930	err = os.WriteFile(backupFilePath, backupContent, os.ModePerm)
3931	assert.NoError(t, err)
3932	_, _, err = httpdtest.Loaddata(backupFilePath, "", "", http.StatusInternalServerError)
3933	assert.NoError(t, err)
3934	backupData.Users = nil
3935	backupData.Folders = nil
3936	backupData.Admins = nil
3937	backupData.APIKeys = append(backupData.APIKeys, dataprovider.APIKey{
3938		Name:  "name",
3939		KeyID: util.GenerateUniqueID(),
3940		Key:   fmt.Sprintf("%v.%v", util.GenerateUniqueID(), util.GenerateUniqueID()),
3941		Scope: dataprovider.APIKeyScopeUser,
3942	})
3943	backupContent, err = json.Marshal(backupData)
3944	assert.NoError(t, err)
3945	err = os.WriteFile(backupFilePath, backupContent, os.ModePerm)
3946	assert.NoError(t, err)
3947	_, _, err = httpdtest.Loaddata(backupFilePath, "", "", http.StatusInternalServerError)
3948	assert.NoError(t, err)
3949	backupData.APIKeys = nil
3950	backupData.Shares = append(backupData.Shares, dataprovider.Share{
3951		Name:     util.GenerateUniqueID(),
3952		ShareID:  util.GenerateUniqueID(),
3953		Scope:    dataprovider.ShareScopeRead,
3954		Paths:    []string{"/"},
3955		Username: defaultUsername,
3956	})
3957	backupContent, err = json.Marshal(backupData)
3958	assert.NoError(t, err)
3959	err = os.WriteFile(backupFilePath, backupContent, os.ModePerm)
3960	assert.NoError(t, err)
3961	_, _, err = httpdtest.Loaddata(backupFilePath, "", "", http.StatusInternalServerError)
3962	assert.NoError(t, err)
3963	err = os.Remove(backupFilePath)
3964	assert.NoError(t, err)
3965	req, err = http.NewRequest(http.MethodGet, webUserPath, nil)
3966	assert.NoError(t, err)
3967	setJWTCookieForReq(req, testServerToken)
3968	rr = executeRequest(req)
3969	checkResponseCode(t, http.StatusInternalServerError, rr)
3970	req, err = http.NewRequest(http.MethodGet, webUserPath+"?clone-from=user", nil)
3971	assert.NoError(t, err)
3972	setJWTCookieForReq(req, testServerToken)
3973	rr = executeRequest(req)
3974	checkResponseCode(t, http.StatusInternalServerError, rr)
3975	req, err = http.NewRequest(http.MethodGet, webTemplateUser+"?from=auser", nil)
3976	assert.NoError(t, err)
3977	setJWTCookieForReq(req, testServerToken)
3978	rr = executeRequest(req)
3979	checkResponseCode(t, http.StatusInternalServerError, rr)
3980	req, err = http.NewRequest(http.MethodGet, webTemplateFolder+"?from=afolder", nil)
3981	assert.NoError(t, err)
3982	setJWTCookieForReq(req, testServerToken)
3983	rr = executeRequest(req)
3984	checkResponseCode(t, http.StatusInternalServerError, rr)
3985	err = config.LoadConfig(configDir, "")
3986	assert.NoError(t, err)
3987	providerConf := config.GetProviderConf()
3988	providerConf.CredentialsPath = credentialsPath
3989	err = os.RemoveAll(credentialsPath)
3990	assert.NoError(t, err)
3991	err = dataprovider.Initialize(providerConf, configDir, true)
3992	assert.NoError(t, err)
3993	httpdtest.SetJWTToken("")
3994}
3995
3996func TestFolders(t *testing.T) {
3997	folder := vfs.BaseVirtualFolder{
3998		Name:       "name",
3999		MappedPath: "relative path",
4000		Users:      []string{"1", "2", "3"},
4001		FsConfig: vfs.Filesystem{
4002			Provider: sdk.CryptedFilesystemProvider,
4003			CryptConfig: vfs.CryptFsConfig{
4004				CryptFsConfig: sdk.CryptFsConfig{
4005					Passphrase: kms.NewPlainSecret("asecret"),
4006				},
4007			},
4008		},
4009	}
4010	_, _, err := httpdtest.AddFolder(folder, http.StatusBadRequest)
4011	assert.NoError(t, err)
4012	folder.MappedPath = filepath.Clean(os.TempDir())
4013	folder1, resp, err := httpdtest.AddFolder(folder, http.StatusCreated)
4014	assert.NoError(t, err, string(resp))
4015	assert.Equal(t, folder.Name, folder1.Name)
4016	assert.Equal(t, folder.MappedPath, folder1.MappedPath)
4017	assert.Equal(t, 0, folder1.UsedQuotaFiles)
4018	assert.Equal(t, int64(0), folder1.UsedQuotaSize)
4019	assert.Equal(t, int64(0), folder1.LastQuotaUpdate)
4020	assert.Equal(t, kms.SecretStatusSecretBox, folder1.FsConfig.CryptConfig.Passphrase.GetStatus())
4021	assert.NotEmpty(t, folder1.FsConfig.CryptConfig.Passphrase.GetPayload())
4022	assert.Empty(t, folder1.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
4023	assert.Empty(t, folder1.FsConfig.CryptConfig.Passphrase.GetKey())
4024	assert.Len(t, folder1.Users, 0)
4025	// adding a duplicate folder must fail
4026	_, _, err = httpdtest.AddFolder(folder, http.StatusCreated)
4027	assert.Error(t, err)
4028	folder.MappedPath = filepath.Join(os.TempDir(), "vfolder")
4029	folder.Name = filepath.Base(folder.MappedPath)
4030	folder.UsedQuotaFiles = 1
4031	folder.UsedQuotaSize = 345
4032	folder.LastQuotaUpdate = 10
4033	folder2, _, err := httpdtest.AddFolder(folder, http.StatusCreated)
4034	assert.NoError(t, err, string(resp))
4035	assert.Equal(t, 1, folder2.UsedQuotaFiles)
4036	assert.Equal(t, int64(345), folder2.UsedQuotaSize)
4037	assert.Equal(t, int64(10), folder2.LastQuotaUpdate)
4038	assert.Len(t, folder2.Users, 0)
4039	folders, _, err := httpdtest.GetFolders(0, 0, http.StatusOK)
4040	assert.NoError(t, err)
4041	numResults := len(folders)
4042	assert.GreaterOrEqual(t, numResults, 2)
4043	found := false
4044	for _, f := range folders {
4045		if f.Name == folder1.Name {
4046			found = true
4047			assert.Equal(t, folder1.MappedPath, f.MappedPath)
4048			assert.Equal(t, kms.SecretStatusSecretBox, f.FsConfig.CryptConfig.Passphrase.GetStatus())
4049			assert.NotEmpty(t, f.FsConfig.CryptConfig.Passphrase.GetPayload())
4050			assert.Empty(t, f.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
4051			assert.Empty(t, f.FsConfig.CryptConfig.Passphrase.GetKey())
4052			assert.Len(t, f.Users, 0)
4053		}
4054	}
4055	assert.True(t, found)
4056	folders, _, err = httpdtest.GetFolders(0, 1, http.StatusOK)
4057	assert.NoError(t, err)
4058	assert.Len(t, folders, numResults-1)
4059	folders, _, err = httpdtest.GetFolders(1, 0, http.StatusOK)
4060	assert.NoError(t, err)
4061	assert.Len(t, folders, 1)
4062	f, _, err := httpdtest.GetFolderByName(folder1.Name, http.StatusOK)
4063	assert.NoError(t, err)
4064	assert.Equal(t, folder1.Name, f.Name)
4065	assert.Equal(t, folder1.MappedPath, f.MappedPath)
4066	assert.Equal(t, kms.SecretStatusSecretBox, f.FsConfig.CryptConfig.Passphrase.GetStatus())
4067	assert.NotEmpty(t, f.FsConfig.CryptConfig.Passphrase.GetPayload())
4068	assert.Empty(t, f.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
4069	assert.Empty(t, f.FsConfig.CryptConfig.Passphrase.GetKey())
4070	assert.Len(t, f.Users, 0)
4071	f, _, err = httpdtest.GetFolderByName(folder2.Name, http.StatusOK)
4072	assert.NoError(t, err)
4073	assert.Equal(t, folder2.Name, f.Name)
4074	assert.Equal(t, folder2.MappedPath, f.MappedPath)
4075	_, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{
4076		Name: "invalid",
4077	}, http.StatusNotFound)
4078	assert.NoError(t, err)
4079	_, _, err = httpdtest.UpdateFolder(vfs.BaseVirtualFolder{Name: "notfound"}, http.StatusNotFound)
4080	assert.NoError(t, err)
4081	folder1.MappedPath = "a/relative/path"
4082	_, _, err = httpdtest.UpdateFolder(folder1, http.StatusBadRequest)
4083	assert.NoError(t, err)
4084	folder1.MappedPath = filepath.Join(os.TempDir(), "updated")
4085	folder1.Description = "updated folder description"
4086	f, resp, err = httpdtest.UpdateFolder(folder1, http.StatusOK)
4087	assert.NoError(t, err, string(resp))
4088	assert.Equal(t, folder1.MappedPath, f.MappedPath)
4089	assert.Equal(t, folder1.Description, f.Description)
4090
4091	_, err = httpdtest.RemoveFolder(folder1, http.StatusOK)
4092	assert.NoError(t, err)
4093	_, err = httpdtest.RemoveFolder(folder2, http.StatusOK)
4094	assert.NoError(t, err)
4095}
4096
4097func TestDumpdata(t *testing.T) {
4098	err := dataprovider.Close()
4099	assert.NoError(t, err)
4100	err = config.LoadConfig(configDir, "")
4101	assert.NoError(t, err)
4102	providerConf := config.GetProviderConf()
4103	err = dataprovider.Initialize(providerConf, configDir, true)
4104	assert.NoError(t, err)
4105	_, rawResp, err := httpdtest.Dumpdata("", "", "", http.StatusBadRequest)
4106	assert.NoError(t, err, string(rawResp))
4107	_, _, err = httpdtest.Dumpdata(filepath.Join(backupsPath, "backup.json"), "", "", http.StatusBadRequest)
4108	assert.NoError(t, err)
4109	_, rawResp, err = httpdtest.Dumpdata("../backup.json", "", "", http.StatusBadRequest)
4110	assert.NoError(t, err, string(rawResp))
4111	_, rawResp, err = httpdtest.Dumpdata("backup.json", "", "0", http.StatusOK)
4112	assert.NoError(t, err, string(rawResp))
4113	response, _, err := httpdtest.Dumpdata("", "1", "0", http.StatusOK)
4114	assert.NoError(t, err)
4115	_, ok := response["admins"]
4116	assert.True(t, ok)
4117	_, ok = response["users"]
4118	assert.True(t, ok)
4119	_, ok = response["folders"]
4120	assert.True(t, ok)
4121	_, ok = response["api_keys"]
4122	assert.True(t, ok)
4123	_, ok = response["shares"]
4124	assert.True(t, ok)
4125	_, ok = response["version"]
4126	assert.True(t, ok)
4127	_, rawResp, err = httpdtest.Dumpdata("backup.json", "", "1", http.StatusOK)
4128	assert.NoError(t, err, string(rawResp))
4129	err = os.Remove(filepath.Join(backupsPath, "backup.json"))
4130	assert.NoError(t, err)
4131	if runtime.GOOS != osWindows {
4132		err = os.Chmod(backupsPath, 0001)
4133		assert.NoError(t, err)
4134		_, _, err = httpdtest.Dumpdata("bck.json", "", "", http.StatusForbidden)
4135		assert.NoError(t, err)
4136		// subdir cannot be created
4137		_, _, err = httpdtest.Dumpdata(filepath.Join("subdir", "bck.json"), "", "", http.StatusForbidden)
4138		assert.NoError(t, err)
4139		err = os.Chmod(backupsPath, 0755)
4140		assert.NoError(t, err)
4141	}
4142	err = dataprovider.Close()
4143	assert.NoError(t, err)
4144	err = config.LoadConfig(configDir, "")
4145	assert.NoError(t, err)
4146	providerConf = config.GetProviderConf()
4147	providerConf.CredentialsPath = credentialsPath
4148	err = os.RemoveAll(credentialsPath)
4149	assert.NoError(t, err)
4150	err = dataprovider.Initialize(providerConf, configDir, true)
4151	assert.NoError(t, err)
4152}
4153
4154func TestDefenderAPI(t *testing.T) {
4155	oldConfig := config.GetCommonConfig()
4156
4157	cfg := config.GetCommonConfig()
4158	cfg.DefenderConfig.Enabled = true
4159	cfg.DefenderConfig.Threshold = 3
4160	cfg.DefenderConfig.ScoreLimitExceeded = 2
4161
4162	err := common.Initialize(cfg)
4163	require.NoError(t, err)
4164
4165	ip := "::1"
4166
4167	response, _, err := httpdtest.GetBanTime(ip, http.StatusOK)
4168	require.NoError(t, err)
4169	banTime, ok := response["date_time"]
4170	require.True(t, ok)
4171	assert.Nil(t, banTime)
4172
4173	hosts, _, err := httpdtest.GetDefenderHosts(http.StatusOK)
4174	require.NoError(t, err)
4175	assert.Len(t, hosts, 0)
4176
4177	response, _, err = httpdtest.GetScore(ip, http.StatusOK)
4178	require.NoError(t, err)
4179	score, ok := response["score"]
4180	require.True(t, ok)
4181	assert.Equal(t, float64(0), score)
4182
4183	err = httpdtest.UnbanIP(ip, http.StatusNotFound)
4184	require.NoError(t, err)
4185
4186	_, err = httpdtest.RemoveDefenderHostByIP(ip, http.StatusNotFound)
4187	require.NoError(t, err)
4188
4189	common.AddDefenderEvent(ip, common.HostEventNoLoginTried)
4190	response, _, err = httpdtest.GetScore(ip, http.StatusOK)
4191	require.NoError(t, err)
4192	score, ok = response["score"]
4193	require.True(t, ok)
4194	assert.Equal(t, float64(2), score)
4195
4196	hosts, _, err = httpdtest.GetDefenderHosts(http.StatusOK)
4197	require.NoError(t, err)
4198	if assert.Len(t, hosts, 1) {
4199		host := hosts[0]
4200		assert.Empty(t, host.GetBanTime())
4201		assert.Equal(t, 2, host.Score)
4202		assert.Equal(t, ip, host.IP)
4203	}
4204	host, _, err := httpdtest.GetDefenderHostByIP(ip, http.StatusOK)
4205	assert.NoError(t, err)
4206	assert.Empty(t, host.GetBanTime())
4207	assert.Equal(t, 2, host.Score)
4208
4209	common.AddDefenderEvent(ip, common.HostEventNoLoginTried)
4210	response, _, err = httpdtest.GetBanTime(ip, http.StatusOK)
4211	require.NoError(t, err)
4212	banTime, ok = response["date_time"]
4213	require.True(t, ok)
4214	assert.NotNil(t, banTime)
4215	hosts, _, err = httpdtest.GetDefenderHosts(http.StatusOK)
4216	require.NoError(t, err)
4217	if assert.Len(t, hosts, 1) {
4218		host := hosts[0]
4219		assert.NotEmpty(t, host.GetBanTime())
4220		assert.Equal(t, 0, host.Score)
4221		assert.Equal(t, ip, host.IP)
4222	}
4223	host, _, err = httpdtest.GetDefenderHostByIP(ip, http.StatusOK)
4224	assert.NoError(t, err)
4225	assert.NotEmpty(t, host.GetBanTime())
4226	assert.Equal(t, 0, host.Score)
4227
4228	err = httpdtest.UnbanIP(ip, http.StatusOK)
4229	require.NoError(t, err)
4230
4231	err = httpdtest.UnbanIP(ip, http.StatusNotFound)
4232	require.NoError(t, err)
4233
4234	host, _, err = httpdtest.GetDefenderHostByIP(ip, http.StatusNotFound)
4235	assert.NoError(t, err)
4236
4237	common.AddDefenderEvent(ip, common.HostEventNoLoginTried)
4238	common.AddDefenderEvent(ip, common.HostEventNoLoginTried)
4239	hosts, _, err = httpdtest.GetDefenderHosts(http.StatusOK)
4240	require.NoError(t, err)
4241	assert.Len(t, hosts, 1)
4242
4243	_, err = httpdtest.RemoveDefenderHostByIP(ip, http.StatusOK)
4244	assert.NoError(t, err)
4245
4246	host, _, err = httpdtest.GetDefenderHostByIP(ip, http.StatusNotFound)
4247	assert.NoError(t, err)
4248	_, err = httpdtest.RemoveDefenderHostByIP(ip, http.StatusNotFound)
4249	assert.NoError(t, err)
4250
4251	host, _, err = httpdtest.GetDefenderHostByIP("invalid_ip", http.StatusBadRequest)
4252	assert.NoError(t, err)
4253	_, err = httpdtest.RemoveDefenderHostByIP("invalid_ip", http.StatusBadRequest)
4254	assert.NoError(t, err)
4255
4256	err = common.Initialize(oldConfig)
4257	require.NoError(t, err)
4258}
4259
4260func TestDefenderAPIErrors(t *testing.T) {
4261	_, _, err := httpdtest.GetBanTime("", http.StatusBadRequest)
4262	require.NoError(t, err)
4263
4264	_, _, err = httpdtest.GetBanTime("invalid", http.StatusBadRequest)
4265	require.NoError(t, err)
4266
4267	_, _, err = httpdtest.GetScore("", http.StatusBadRequest)
4268	require.NoError(t, err)
4269
4270	err = httpdtest.UnbanIP("", http.StatusBadRequest)
4271	require.NoError(t, err)
4272}
4273
4274func TestRestoreShares(t *testing.T) {
4275	// shares should be restored preserving the UsedTokens, CreatedAt, LastUseAt, UpdatedAt,
4276	// and ExpiresAt, so an expired share can be restored while we cannot create an already
4277	// expired share
4278	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
4279	assert.NoError(t, err)
4280	share := dataprovider.Share{
4281		ShareID:     shortuuid.New(),
4282		Name:        "share name",
4283		Description: "share description",
4284		Scope:       dataprovider.ShareScopeRead,
4285		Paths:       []string{"/"},
4286		Username:    user.Username,
4287		CreatedAt:   util.GetTimeAsMsSinceEpoch(time.Now().Add(-144 * time.Hour)),
4288		UpdatedAt:   util.GetTimeAsMsSinceEpoch(time.Now().Add(-96 * time.Hour)),
4289		LastUseAt:   util.GetTimeAsMsSinceEpoch(time.Now().Add(-64 * time.Hour)),
4290		ExpiresAt:   util.GetTimeAsMsSinceEpoch(time.Now().Add(-48 * time.Hour)),
4291		MaxTokens:   10,
4292		UsedTokens:  8,
4293		AllowFrom:   []string{"127.0.0.0/8"},
4294	}
4295	backupData := dataprovider.BackupData{}
4296	backupData.Shares = append(backupData.Shares, share)
4297	backupContent, err := json.Marshal(backupData)
4298	assert.NoError(t, err)
4299	_, _, err = httpdtest.LoaddataFromPostBody(backupContent, "0", "0", http.StatusOK)
4300	assert.NoError(t, err)
4301	shareGet, err := dataprovider.ShareExists(share.ShareID, user.Username)
4302	assert.NoError(t, err)
4303	assert.Equal(t, share, shareGet)
4304
4305	share.CreatedAt = util.GetTimeAsMsSinceEpoch(time.Now().Add(-142 * time.Hour))
4306	share.UpdatedAt = util.GetTimeAsMsSinceEpoch(time.Now().Add(-92 * time.Hour))
4307	share.LastUseAt = util.GetTimeAsMsSinceEpoch(time.Now().Add(-62 * time.Hour))
4308	share.UsedTokens = 6
4309	backupData.Shares = []dataprovider.Share{share}
4310	backupContent, err = json.Marshal(backupData)
4311	assert.NoError(t, err)
4312	_, _, err = httpdtest.LoaddataFromPostBody(backupContent, "0", "0", http.StatusOK)
4313	assert.NoError(t, err)
4314	shareGet, err = dataprovider.ShareExists(share.ShareID, user.Username)
4315	assert.NoError(t, err)
4316	assert.Equal(t, share, shareGet)
4317
4318	_, err = httpdtest.RemoveUser(user, http.StatusOK)
4319	assert.NoError(t, err)
4320}
4321
4322func TestLoaddataFromPostBody(t *testing.T) {
4323	mappedPath := filepath.Join(os.TempDir(), "restored_folder")
4324	folderName := filepath.Base(mappedPath)
4325	user := getTestUser()
4326	user.ID = 1
4327	user.Username = "test_user_restored"
4328	admin := getTestAdmin()
4329	admin.ID = 1
4330	admin.Username = "test_admin_restored"
4331	backupData := dataprovider.BackupData{}
4332	backupData.Users = append(backupData.Users, user)
4333	backupData.Admins = append(backupData.Admins, admin)
4334	backupData.Folders = []vfs.BaseVirtualFolder{
4335		{
4336			Name:            folderName,
4337			MappedPath:      mappedPath,
4338			UsedQuotaSize:   123,
4339			UsedQuotaFiles:  456,
4340			LastQuotaUpdate: 789,
4341			Users:           []string{"user"},
4342		},
4343		{
4344			Name:       folderName,
4345			MappedPath: mappedPath + "1",
4346		},
4347	}
4348	backupData.APIKeys = append(backupData.APIKeys, dataprovider.APIKey{})
4349	backupData.Shares = append(backupData.Shares, dataprovider.Share{})
4350	backupContent, err := json.Marshal(backupData)
4351	assert.NoError(t, err)
4352	_, _, err = httpdtest.LoaddataFromPostBody(nil, "0", "0", http.StatusBadRequest)
4353	assert.NoError(t, err)
4354	_, _, err = httpdtest.LoaddataFromPostBody(backupContent, "a", "0", http.StatusBadRequest)
4355	assert.NoError(t, err)
4356	_, _, err = httpdtest.LoaddataFromPostBody([]byte("invalid content"), "0", "0", http.StatusBadRequest)
4357	assert.NoError(t, err)
4358	_, _, err = httpdtest.LoaddataFromPostBody(backupContent, "0", "0", http.StatusInternalServerError)
4359	assert.NoError(t, err)
4360
4361	keyID := util.GenerateUniqueID()
4362	backupData.APIKeys = []dataprovider.APIKey{
4363		{
4364			Name:  "test key",
4365			Scope: dataprovider.APIKeyScopeAdmin,
4366			KeyID: keyID,
4367			Key:   fmt.Sprintf("%v.%v", util.GenerateUniqueID(), util.GenerateUniqueID()),
4368		},
4369	}
4370	backupData.Shares = []dataprovider.Share{
4371		{
4372			ShareID:  keyID,
4373			Name:     keyID,
4374			Scope:    dataprovider.ShareScopeWrite,
4375			Paths:    []string{"/"},
4376			Username: user.Username,
4377		},
4378	}
4379	backupContent, err = json.Marshal(backupData)
4380	assert.NoError(t, err)
4381	_, _, err = httpdtest.LoaddataFromPostBody(backupContent, "0", "0", http.StatusOK)
4382	assert.NoError(t, err)
4383	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
4384	assert.NoError(t, err)
4385	_, err = dataprovider.ShareExists(keyID, user.Username)
4386	assert.NoError(t, err)
4387	_, err = httpdtest.RemoveUser(user, http.StatusOK)
4388	assert.NoError(t, err)
4389
4390	admin, _, err = httpdtest.GetAdminByUsername(admin.Username, http.StatusOK)
4391	assert.NoError(t, err)
4392	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
4393	assert.NoError(t, err)
4394	apiKey, _, err := httpdtest.GetAPIKeyByID(keyID, http.StatusOK)
4395	assert.NoError(t, err)
4396	_, err = httpdtest.RemoveAPIKey(apiKey, http.StatusOK)
4397	assert.NoError(t, err)
4398
4399	folder, _, err := httpdtest.GetFolderByName(folderName, http.StatusOK)
4400	assert.NoError(t, err)
4401	assert.Equal(t, mappedPath+"1", folder.MappedPath)
4402	assert.Equal(t, int64(123), folder.UsedQuotaSize)
4403	assert.Equal(t, 456, folder.UsedQuotaFiles)
4404	assert.Equal(t, int64(789), folder.LastQuotaUpdate)
4405	assert.Len(t, folder.Users, 0)
4406	_, err = httpdtest.RemoveFolder(folder, http.StatusOK)
4407	assert.NoError(t, err)
4408}
4409
4410func TestLoaddata(t *testing.T) {
4411	mappedPath := filepath.Join(os.TempDir(), "restored_folder")
4412	folderName := filepath.Base(mappedPath)
4413	foldeDesc := "restored folder desc"
4414	user := getTestUser()
4415	user.ID = 1
4416	user.Username = "test_user_restore"
4417	admin := getTestAdmin()
4418	admin.ID = 1
4419	admin.Username = "test_admin_restore"
4420	apiKey := dataprovider.APIKey{
4421		Name:  util.GenerateUniqueID(),
4422		Scope: dataprovider.APIKeyScopeAdmin,
4423		KeyID: util.GenerateUniqueID(),
4424		Key:   fmt.Sprintf("%v.%v", util.GenerateUniqueID(), util.GenerateUniqueID()),
4425	}
4426	share := dataprovider.Share{
4427		ShareID:  util.GenerateUniqueID(),
4428		Name:     util.GenerateUniqueID(),
4429		Scope:    dataprovider.ShareScopeRead,
4430		Paths:    []string{"/"},
4431		Username: user.Username,
4432	}
4433	backupData := dataprovider.BackupData{}
4434	backupData.Users = append(backupData.Users, user)
4435	backupData.Admins = append(backupData.Admins, admin)
4436	backupData.Folders = []vfs.BaseVirtualFolder{
4437		{
4438			Name:            folderName,
4439			MappedPath:      mappedPath + "1",
4440			UsedQuotaSize:   123,
4441			UsedQuotaFiles:  456,
4442			LastQuotaUpdate: 789,
4443			Users:           []string{"user"},
4444		},
4445		{
4446			MappedPath:  mappedPath,
4447			Name:        folderName,
4448			Description: foldeDesc,
4449		},
4450	}
4451	backupData.APIKeys = append(backupData.APIKeys, apiKey)
4452	backupData.Shares = append(backupData.Shares, share)
4453	backupContent, err := json.Marshal(backupData)
4454	assert.NoError(t, err)
4455	backupFilePath := filepath.Join(backupsPath, "backup.json")
4456	err = os.WriteFile(backupFilePath, backupContent, os.ModePerm)
4457	assert.NoError(t, err)
4458	_, _, err = httpdtest.Loaddata(backupFilePath, "a", "", http.StatusBadRequest)
4459	assert.NoError(t, err)
4460	_, _, err = httpdtest.Loaddata(backupFilePath, "", "a", http.StatusBadRequest)
4461	assert.NoError(t, err)
4462	_, _, err = httpdtest.Loaddata("backup.json", "1", "", http.StatusBadRequest)
4463	assert.NoError(t, err)
4464	_, _, err = httpdtest.Loaddata(backupFilePath+"a", "1", "", http.StatusBadRequest)
4465	assert.NoError(t, err)
4466	if runtime.GOOS != osWindows {
4467		err = os.Chmod(backupFilePath, 0111)
4468		assert.NoError(t, err)
4469		_, _, err = httpdtest.Loaddata(backupFilePath, "1", "", http.StatusForbidden)
4470		assert.NoError(t, err)
4471		err = os.Chmod(backupFilePath, 0644)
4472		assert.NoError(t, err)
4473	}
4474	// add user, folder, admin, API key, share from backup
4475	_, _, err = httpdtest.Loaddata(backupFilePath, "1", "", http.StatusOK)
4476	assert.NoError(t, err)
4477	// update from backup
4478	_, _, err = httpdtest.Loaddata(backupFilePath, "2", "", http.StatusOK)
4479	assert.NoError(t, err)
4480	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
4481	assert.NoError(t, err)
4482	_, err = dataprovider.ShareExists(share.ShareID, user.Username)
4483	assert.NoError(t, err)
4484	_, err = httpdtest.RemoveUser(user, http.StatusOK)
4485	assert.NoError(t, err)
4486
4487	admin, _, err = httpdtest.GetAdminByUsername(admin.Username, http.StatusOK)
4488	assert.NoError(t, err)
4489	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
4490	assert.NoError(t, err)
4491
4492	apiKey, _, err = httpdtest.GetAPIKeyByID(apiKey.KeyID, http.StatusOK)
4493	assert.NoError(t, err)
4494	_, err = httpdtest.RemoveAPIKey(apiKey, http.StatusOK)
4495	assert.NoError(t, err)
4496
4497	folder, _, err := httpdtest.GetFolderByName(folderName, http.StatusOK)
4498	assert.NoError(t, err)
4499	assert.Equal(t, mappedPath, folder.MappedPath)
4500	assert.Equal(t, int64(123), folder.UsedQuotaSize)
4501	assert.Equal(t, 456, folder.UsedQuotaFiles)
4502	assert.Equal(t, int64(789), folder.LastQuotaUpdate)
4503	assert.Equal(t, foldeDesc, folder.Description)
4504	assert.Len(t, folder.Users, 0)
4505	_, err = httpdtest.RemoveFolder(folder, http.StatusOK)
4506	assert.NoError(t, err)
4507	err = os.Remove(backupFilePath)
4508	assert.NoError(t, err)
4509	err = createTestFile(backupFilePath, 10485761)
4510	assert.NoError(t, err)
4511	_, _, err = httpdtest.Loaddata(backupFilePath, "1", "0", http.StatusBadRequest)
4512	assert.NoError(t, err)
4513	err = os.Remove(backupFilePath)
4514	assert.NoError(t, err)
4515	err = createTestFile(backupFilePath, 65535)
4516	assert.NoError(t, err)
4517	_, _, err = httpdtest.Loaddata(backupFilePath, "1", "0", http.StatusBadRequest)
4518	assert.NoError(t, err)
4519	err = os.Remove(backupFilePath)
4520	assert.NoError(t, err)
4521}
4522
4523func TestLoaddataMode(t *testing.T) {
4524	mappedPath := filepath.Join(os.TempDir(), "restored_fold")
4525	folderName := filepath.Base(mappedPath)
4526	user := getTestUser()
4527	user.ID = 1
4528	user.Username = "test_user_restore"
4529	admin := getTestAdmin()
4530	admin.ID = 1
4531	admin.Username = "test_admin_restore"
4532	apiKey := dataprovider.APIKey{
4533		Name:        util.GenerateUniqueID(),
4534		Scope:       dataprovider.APIKeyScopeAdmin,
4535		KeyID:       util.GenerateUniqueID(),
4536		Key:         fmt.Sprintf("%v.%v", util.GenerateUniqueID(), util.GenerateUniqueID()),
4537		Description: "desc",
4538	}
4539	share := dataprovider.Share{
4540		ShareID:  util.GenerateUniqueID(),
4541		Name:     util.GenerateUniqueID(),
4542		Scope:    dataprovider.ShareScopeRead,
4543		Paths:    []string{"/"},
4544		Username: user.Username,
4545	}
4546	backupData := dataprovider.BackupData{}
4547	backupData.Users = append(backupData.Users, user)
4548	backupData.Admins = append(backupData.Admins, admin)
4549	backupData.Folders = []vfs.BaseVirtualFolder{
4550		{
4551			Name:            folderName,
4552			MappedPath:      mappedPath,
4553			UsedQuotaSize:   123,
4554			UsedQuotaFiles:  456,
4555			LastQuotaUpdate: 789,
4556			Users:           []string{"user"},
4557		},
4558		{
4559			MappedPath: mappedPath + "1",
4560			Name:       folderName,
4561		},
4562	}
4563	backupData.APIKeys = append(backupData.APIKeys, apiKey)
4564	backupData.Shares = append(backupData.Shares, share)
4565	backupContent, _ := json.Marshal(backupData)
4566	backupFilePath := filepath.Join(backupsPath, "backup.json")
4567	err := os.WriteFile(backupFilePath, backupContent, os.ModePerm)
4568	assert.NoError(t, err)
4569	_, _, err = httpdtest.Loaddata(backupFilePath, "0", "0", http.StatusOK)
4570	assert.NoError(t, err)
4571	folder, _, err := httpdtest.GetFolderByName(folderName, http.StatusOK)
4572	assert.NoError(t, err)
4573	assert.Equal(t, mappedPath+"1", folder.MappedPath)
4574	assert.Equal(t, int64(123), folder.UsedQuotaSize)
4575	assert.Equal(t, 456, folder.UsedQuotaFiles)
4576	assert.Equal(t, int64(789), folder.LastQuotaUpdate)
4577	assert.Len(t, folder.Users, 0)
4578	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
4579	assert.NoError(t, err)
4580	oldUploadBandwidth := user.UploadBandwidth
4581	user.UploadBandwidth = oldUploadBandwidth + 128
4582	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
4583	assert.NoError(t, err)
4584	admin, _, err = httpdtest.GetAdminByUsername(admin.Username, http.StatusOK)
4585	assert.NoError(t, err)
4586	oldInfo := admin.AdditionalInfo
4587	oldDesc := admin.Description
4588	admin.AdditionalInfo = "newInfo"
4589	admin.Description = "newDesc"
4590	admin, _, err = httpdtest.UpdateAdmin(admin, http.StatusOK)
4591	assert.NoError(t, err)
4592	apiKey, _, err = httpdtest.GetAPIKeyByID(apiKey.KeyID, http.StatusOK)
4593	assert.NoError(t, err)
4594	oldAPIKeyDesc := apiKey.Description
4595	apiKey.ExpiresAt = util.GetTimeAsMsSinceEpoch(time.Now())
4596	apiKey.Description = "new desc"
4597	apiKey, _, err = httpdtest.UpdateAPIKey(apiKey, http.StatusOK)
4598	assert.NoError(t, err)
4599	share.Description = "test desc"
4600	err = dataprovider.UpdateShare(&share, "", "")
4601	assert.NoError(t, err)
4602
4603	backupData.Folders = []vfs.BaseVirtualFolder{
4604		{
4605			MappedPath: mappedPath,
4606			Name:       folderName,
4607		},
4608	}
4609	_, _, err = httpdtest.Loaddata(backupFilePath, "0", "1", http.StatusOK)
4610	assert.NoError(t, err)
4611	folder, _, err = httpdtest.GetFolderByName(folderName, http.StatusOK)
4612	assert.NoError(t, err)
4613	assert.Equal(t, mappedPath+"1", folder.MappedPath)
4614	assert.Equal(t, int64(123), folder.UsedQuotaSize)
4615	assert.Equal(t, 456, folder.UsedQuotaFiles)
4616	assert.Equal(t, int64(789), folder.LastQuotaUpdate)
4617	assert.Len(t, folder.Users, 0)
4618
4619	c := common.NewBaseConnection("connID", common.ProtocolFTP, "", "", user)
4620	fakeConn := &fakeConnection{
4621		BaseConnection: c,
4622	}
4623	common.Connections.Add(fakeConn)
4624	assert.Len(t, common.Connections.GetStats(), 1)
4625	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
4626	assert.NoError(t, err)
4627	assert.NotEqual(t, oldUploadBandwidth, user.UploadBandwidth)
4628	admin, _, err = httpdtest.GetAdminByUsername(admin.Username, http.StatusOK)
4629	assert.NoError(t, err)
4630	assert.NotEqual(t, oldInfo, admin.AdditionalInfo)
4631	assert.NotEqual(t, oldDesc, admin.Description)
4632
4633	apiKey, _, err = httpdtest.GetAPIKeyByID(apiKey.KeyID, http.StatusOK)
4634	assert.NoError(t, err)
4635	assert.NotEqual(t, int64(0), apiKey.ExpiresAt)
4636	assert.NotEqual(t, oldAPIKeyDesc, apiKey.Description)
4637
4638	share, err = dataprovider.ShareExists(share.ShareID, user.Username)
4639	assert.NoError(t, err)
4640	assert.NotEmpty(t, share.Description)
4641
4642	_, _, err = httpdtest.Loaddata(backupFilePath, "0", "2", http.StatusOK)
4643	assert.NoError(t, err)
4644	// mode 2 will update the user and close the previous connection
4645	assert.Len(t, common.Connections.GetStats(), 0)
4646	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
4647	assert.NoError(t, err)
4648	assert.Equal(t, oldUploadBandwidth, user.UploadBandwidth)
4649	_, err = httpdtest.RemoveUser(user, http.StatusOK)
4650	assert.NoError(t, err)
4651	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
4652	assert.NoError(t, err)
4653	_, err = httpdtest.RemoveFolder(folder, http.StatusOK)
4654	assert.NoError(t, err)
4655	err = os.Remove(backupFilePath)
4656	assert.NoError(t, err)
4657}
4658
4659func TestRateLimiter(t *testing.T) {
4660	oldConfig := config.GetCommonConfig()
4661
4662	cfg := config.GetCommonConfig()
4663	cfg.RateLimitersConfig = []common.RateLimiterConfig{
4664		{
4665			Average:   1,
4666			Period:    1000,
4667			Burst:     1,
4668			Type:      1,
4669			Protocols: []string{common.ProtocolHTTP},
4670		},
4671	}
4672
4673	err := common.Initialize(cfg)
4674	assert.NoError(t, err)
4675
4676	client := &http.Client{
4677		Timeout: 5 * time.Second,
4678	}
4679	resp, err := client.Get(httpBaseURL + healthzPath)
4680	assert.NoError(t, err)
4681	assert.Equal(t, http.StatusOK, resp.StatusCode)
4682	err = resp.Body.Close()
4683	assert.NoError(t, err)
4684
4685	resp, err = client.Get(httpBaseURL + healthzPath)
4686	assert.NoError(t, err)
4687	assert.Equal(t, http.StatusTooManyRequests, resp.StatusCode)
4688	assert.Equal(t, "1", resp.Header.Get("Retry-After"))
4689	assert.NotEmpty(t, resp.Header.Get("X-Retry-In"))
4690	err = resp.Body.Close()
4691	assert.NoError(t, err)
4692
4693	resp, err = client.Get(httpBaseURL + webLoginPath)
4694	assert.NoError(t, err)
4695	assert.Equal(t, http.StatusTooManyRequests, resp.StatusCode)
4696	assert.Equal(t, "1", resp.Header.Get("Retry-After"))
4697	assert.NotEmpty(t, resp.Header.Get("X-Retry-In"))
4698	err = resp.Body.Close()
4699	assert.NoError(t, err)
4700
4701	resp, err = client.Get(httpBaseURL + webClientLoginPath)
4702	assert.NoError(t, err)
4703	assert.Equal(t, http.StatusTooManyRequests, resp.StatusCode)
4704	assert.Equal(t, "1", resp.Header.Get("Retry-After"))
4705	assert.NotEmpty(t, resp.Header.Get("X-Retry-In"))
4706	err = resp.Body.Close()
4707	assert.NoError(t, err)
4708
4709	err = common.Initialize(oldConfig)
4710	assert.NoError(t, err)
4711}
4712
4713func TestHTTPSConnection(t *testing.T) {
4714	client := &http.Client{
4715		Timeout: 5 * time.Second,
4716	}
4717	resp, err := client.Get("https://localhost:8443" + healthzPath)
4718	if assert.Error(t, err) {
4719		if !strings.Contains(err.Error(), "certificate is not valid") &&
4720			!strings.Contains(err.Error(), "certificate signed by unknown authority") {
4721			assert.Fail(t, err.Error())
4722		}
4723	} else {
4724		resp.Body.Close()
4725	}
4726}
4727
4728// test using mock http server
4729
4730func TestBasicUserHandlingMock(t *testing.T) {
4731	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
4732	assert.NoError(t, err)
4733	user := getTestUser()
4734	userAsJSON := getUserAsJSON(t, user)
4735	req, err := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
4736	assert.NoError(t, err)
4737	setBearerForReq(req, token)
4738	rr := executeRequest(req)
4739	checkResponseCode(t, http.StatusCreated, rr)
4740	err = render.DecodeJSON(rr.Body, &user)
4741	assert.NoError(t, err)
4742	req, err = http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
4743	assert.NoError(t, err)
4744	setBearerForReq(req, token)
4745	rr = executeRequest(req)
4746	checkResponseCode(t, http.StatusInternalServerError, rr)
4747	user.MaxSessions = 10
4748	user.UploadBandwidth = 128
4749	user.Permissions["/"] = []string{dataprovider.PermAny, dataprovider.PermDelete, dataprovider.PermDownload}
4750	userAsJSON = getUserAsJSON(t, user)
4751	req, _ = http.NewRequest(http.MethodPut, userPath+"/"+user.Username, bytes.NewBuffer(userAsJSON))
4752	setBearerForReq(req, token)
4753	rr = executeRequest(req)
4754	checkResponseCode(t, http.StatusOK, rr)
4755
4756	req, _ = http.NewRequest(http.MethodGet, userPath+"/"+user.Username, nil)
4757	setBearerForReq(req, token)
4758	rr = executeRequest(req)
4759	checkResponseCode(t, http.StatusOK, rr)
4760
4761	var updatedUser dataprovider.User
4762	err = render.DecodeJSON(rr.Body, &updatedUser)
4763	assert.NoError(t, err)
4764	assert.Equal(t, user.MaxSessions, updatedUser.MaxSessions)
4765	assert.Equal(t, user.UploadBandwidth, updatedUser.UploadBandwidth)
4766	assert.Equal(t, 1, len(updatedUser.Permissions["/"]))
4767	assert.True(t, util.IsStringInSlice(dataprovider.PermAny, updatedUser.Permissions["/"]))
4768	req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+user.Username, nil)
4769	setBearerForReq(req, token)
4770	rr = executeRequest(req)
4771	checkResponseCode(t, http.StatusOK, rr)
4772}
4773
4774func TestAddUserNoUsernameMock(t *testing.T) {
4775	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
4776	assert.NoError(t, err)
4777	user := getTestUser()
4778	user.Username = ""
4779	userAsJSON := getUserAsJSON(t, user)
4780	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
4781	setBearerForReq(req, token)
4782	rr := executeRequest(req)
4783	checkResponseCode(t, http.StatusBadRequest, rr)
4784}
4785
4786func TestAddUserInvalidHomeDirMock(t *testing.T) {
4787	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
4788	assert.NoError(t, err)
4789	user := getTestUser()
4790	user.HomeDir = "relative_path"
4791	userAsJSON := getUserAsJSON(t, user)
4792	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
4793	setBearerForReq(req, token)
4794	rr := executeRequest(req)
4795	checkResponseCode(t, http.StatusBadRequest, rr)
4796}
4797
4798func TestAddUserInvalidPermsMock(t *testing.T) {
4799	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
4800	assert.NoError(t, err)
4801	user := getTestUser()
4802	user.Permissions["/"] = []string{}
4803	userAsJSON := getUserAsJSON(t, user)
4804	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
4805	setBearerForReq(req, token)
4806	rr := executeRequest(req)
4807	checkResponseCode(t, http.StatusBadRequest, rr)
4808}
4809
4810func TestAddFolderInvalidJsonMock(t *testing.T) {
4811	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
4812	assert.NoError(t, err)
4813	req, _ := http.NewRequest(http.MethodPost, folderPath, bytes.NewBuffer([]byte("invalid json")))
4814	setBearerForReq(req, token)
4815	rr := executeRequest(req)
4816	checkResponseCode(t, http.StatusBadRequest, rr)
4817}
4818
4819func TestUpdateFolderInvalidJsonMock(t *testing.T) {
4820	folder := vfs.BaseVirtualFolder{
4821		Name:       "name",
4822		MappedPath: filepath.Clean(os.TempDir()),
4823	}
4824	folder, resp, err := httpdtest.AddFolder(folder, http.StatusCreated)
4825	assert.NoError(t, err, string(resp))
4826
4827	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
4828	assert.NoError(t, err)
4829	req, _ := http.NewRequest(http.MethodPut, path.Join(folderPath, folder.Name), bytes.NewBuffer([]byte("not a json")))
4830	setBearerForReq(req, token)
4831	rr := executeRequest(req)
4832	checkResponseCode(t, http.StatusBadRequest, rr)
4833
4834	_, err = httpdtest.RemoveFolder(folder, http.StatusOK)
4835	assert.NoError(t, err)
4836}
4837
4838func TestUnbanInvalidJsonMock(t *testing.T) {
4839	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
4840	assert.NoError(t, err)
4841	req, _ := http.NewRequest(http.MethodPost, defenderUnban, bytes.NewBuffer([]byte("invalid json")))
4842	setBearerForReq(req, token)
4843	rr := executeRequest(req)
4844	checkResponseCode(t, http.StatusBadRequest, rr)
4845}
4846
4847func TestAddUserInvalidJsonMock(t *testing.T) {
4848	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
4849	assert.NoError(t, err)
4850	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer([]byte("invalid json")))
4851	setBearerForReq(req, token)
4852	rr := executeRequest(req)
4853	checkResponseCode(t, http.StatusBadRequest, rr)
4854}
4855
4856func TestAddAdminInvalidJsonMock(t *testing.T) {
4857	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
4858	assert.NoError(t, err)
4859	req, _ := http.NewRequest(http.MethodPost, adminPath, bytes.NewBuffer([]byte("...")))
4860	setBearerForReq(req, token)
4861	rr := executeRequest(req)
4862	checkResponseCode(t, http.StatusBadRequest, rr)
4863}
4864
4865func TestAddAdminNoPasswordMock(t *testing.T) {
4866	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
4867	assert.NoError(t, err)
4868	admin := getTestAdmin()
4869	admin.Password = ""
4870	asJSON, err := json.Marshal(admin)
4871	assert.NoError(t, err)
4872	req, _ := http.NewRequest(http.MethodPost, adminPath, bytes.NewBuffer(asJSON))
4873	setBearerForReq(req, token)
4874	rr := executeRequest(req)
4875	checkResponseCode(t, http.StatusBadRequest, rr)
4876	assert.Contains(t, rr.Body.String(), "please set a password")
4877}
4878
4879func TestAdminTwoFactorLogin(t *testing.T) {
4880	admin := getTestAdmin()
4881	admin.Username = altAdminUsername
4882	admin.Password = altAdminPassword
4883	admin, _, err := httpdtest.AddAdmin(admin, http.StatusCreated)
4884	assert.NoError(t, err)
4885	// enable two factor authentication
4886	configName, _, secret, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], admin.Username)
4887	assert.NoError(t, err)
4888	altToken, err := getJWTAPITokenFromTestServer(altAdminUsername, altAdminPassword)
4889	assert.NoError(t, err)
4890	adminTOTPConfig := dataprovider.TOTPConfig{
4891		Enabled:    true,
4892		ConfigName: configName,
4893		Secret:     kms.NewPlainSecret(secret),
4894	}
4895	asJSON, err := json.Marshal(adminTOTPConfig)
4896	assert.NoError(t, err)
4897	req, err := http.NewRequest(http.MethodPost, adminTOTPSavePath, bytes.NewBuffer(asJSON))
4898	assert.NoError(t, err)
4899	setBearerForReq(req, altToken)
4900	rr := executeRequest(req)
4901	checkResponseCode(t, http.StatusOK, rr)
4902	admin, _, err = httpdtest.GetAdminByUsername(admin.Username, http.StatusOK)
4903	assert.NoError(t, err)
4904	assert.True(t, admin.Filters.TOTPConfig.Enabled)
4905
4906	req, err = http.NewRequest(http.MethodGet, admin2FARecoveryCodesPath, nil)
4907	assert.NoError(t, err)
4908	setBearerForReq(req, altToken)
4909	rr = executeRequest(req)
4910	checkResponseCode(t, http.StatusOK, rr)
4911	var recCodes []recoveryCode
4912	err = json.Unmarshal(rr.Body.Bytes(), &recCodes)
4913	assert.NoError(t, err)
4914	assert.Len(t, recCodes, 12)
4915
4916	admin, _, err = httpdtest.GetAdminByUsername(altAdminUsername, http.StatusOK)
4917	assert.NoError(t, err)
4918	assert.Len(t, admin.Filters.RecoveryCodes, 12)
4919	for _, c := range admin.Filters.RecoveryCodes {
4920		assert.Empty(t, c.Secret.GetAdditionalData())
4921		assert.Empty(t, c.Secret.GetKey())
4922		assert.Equal(t, kms.SecretStatusSecretBox, c.Secret.GetStatus())
4923		assert.NotEmpty(t, c.Secret.GetPayload())
4924	}
4925
4926	webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
4927	assert.NoError(t, err)
4928	req, err = http.NewRequest(http.MethodGet, webAdminTwoFactorPath, nil)
4929	assert.NoError(t, err)
4930	setJWTCookieForReq(req, webToken)
4931	rr = executeRequest(req)
4932	checkResponseCode(t, http.StatusNotFound, rr)
4933
4934	req, err = http.NewRequest(http.MethodGet, webAdminTwoFactorRecoveryPath, nil)
4935	assert.NoError(t, err)
4936	setJWTCookieForReq(req, webToken)
4937	rr = executeRequest(req)
4938	checkResponseCode(t, http.StatusNotFound, rr)
4939
4940	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorPath, nil)
4941	assert.NoError(t, err)
4942	setJWTCookieForReq(req, webToken)
4943	rr = executeRequest(req)
4944	checkResponseCode(t, http.StatusNotFound, rr)
4945
4946	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorRecoveryPath, nil)
4947	assert.NoError(t, err)
4948	setJWTCookieForReq(req, webToken)
4949	rr = executeRequest(req)
4950	checkResponseCode(t, http.StatusNotFound, rr)
4951
4952	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
4953	assert.NoError(t, err)
4954	form := getLoginForm(altAdminUsername, altAdminPassword, csrfToken)
4955	req, err = http.NewRequest(http.MethodPost, webLoginPath, bytes.NewBuffer([]byte(form.Encode())))
4956	assert.NoError(t, err)
4957	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
4958	rr = executeRequest(req)
4959	assert.Equal(t, http.StatusFound, rr.Code)
4960	assert.Equal(t, webAdminTwoFactorPath, rr.Header().Get("Location"))
4961	cookie, err := getCookieFromResponse(rr)
4962	assert.NoError(t, err)
4963
4964	// without a cookie
4965	req, err = http.NewRequest(http.MethodGet, webAdminTwoFactorRecoveryPath, nil)
4966	assert.NoError(t, err)
4967	rr = executeRequest(req)
4968	checkResponseCode(t, http.StatusNotFound, rr)
4969
4970	req, err = http.NewRequest(http.MethodGet, webAdminTwoFactorPath, nil)
4971	assert.NoError(t, err)
4972	setJWTCookieForReq(req, cookie)
4973	rr = executeRequest(req)
4974	checkResponseCode(t, http.StatusOK, rr)
4975
4976	req, err = http.NewRequest(http.MethodGet, webAdminTwoFactorRecoveryPath, nil)
4977	assert.NoError(t, err)
4978	setJWTCookieForReq(req, cookie)
4979	rr = executeRequest(req)
4980	checkResponseCode(t, http.StatusOK, rr)
4981
4982	// any other page will be redirected to the two factor auth page
4983	req, err = http.NewRequest(http.MethodGet, webUsersPath, nil)
4984	assert.NoError(t, err)
4985	setJWTCookieForReq(req, cookie)
4986	rr = executeRequest(req)
4987	checkResponseCode(t, http.StatusFound, rr)
4988	assert.Equal(t, webAdminTwoFactorPath, rr.Header().Get("Location"))
4989	// a partial token cannot be used for user pages
4990	req, err = http.NewRequest(http.MethodGet, webClientFilesPath, nil)
4991	assert.NoError(t, err)
4992	setJWTCookieForReq(req, cookie)
4993	rr = executeRequest(req)
4994	checkResponseCode(t, http.StatusFound, rr)
4995	assert.Equal(t, webClientLoginPath, rr.Header().Get("Location"))
4996
4997	passcode, err := generateTOTPPasscode(secret)
4998	assert.NoError(t, err)
4999	form = make(url.Values)
5000	form.Set("passcode", passcode)
5001	// no csrf
5002	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorPath, bytes.NewBuffer([]byte(form.Encode())))
5003	assert.NoError(t, err)
5004	setJWTCookieForReq(req, cookie)
5005	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5006	rr = executeRequest(req)
5007	assert.Equal(t, http.StatusOK, rr.Code)
5008	assert.Contains(t, rr.Body.String(), "unable to verify form token")
5009
5010	form.Set(csrfFormToken, csrfToken)
5011	form.Set("passcode", "invalid_passcode")
5012	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorPath, bytes.NewBuffer([]byte(form.Encode())))
5013	assert.NoError(t, err)
5014	setJWTCookieForReq(req, cookie)
5015	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5016	rr = executeRequest(req)
5017	assert.Equal(t, http.StatusOK, rr.Code)
5018	assert.Contains(t, rr.Body.String(), "Invalid authentication code")
5019
5020	form.Set("passcode", "")
5021	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorPath, bytes.NewBuffer([]byte(form.Encode())))
5022	assert.NoError(t, err)
5023	setJWTCookieForReq(req, cookie)
5024	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5025	rr = executeRequest(req)
5026	assert.Equal(t, http.StatusOK, rr.Code)
5027	assert.Contains(t, rr.Body.String(), "Invalid credentials")
5028
5029	form.Set("passcode", passcode)
5030	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorPath, bytes.NewBuffer([]byte(form.Encode())))
5031	assert.NoError(t, err)
5032	setJWTCookieForReq(req, cookie)
5033	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5034	rr = executeRequest(req)
5035	assert.Equal(t, http.StatusFound, rr.Code)
5036	assert.Equal(t, webUsersPath, rr.Header().Get("Location"))
5037	// the same cookie cannot be reused
5038	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorPath, bytes.NewBuffer([]byte(form.Encode())))
5039	assert.NoError(t, err)
5040	setJWTCookieForReq(req, cookie)
5041	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5042	rr = executeRequest(req)
5043	assert.Equal(t, http.StatusNotFound, rr.Code)
5044	// get a new cookie and login using a recovery code
5045	form = getLoginForm(altAdminUsername, altAdminPassword, csrfToken)
5046	req, err = http.NewRequest(http.MethodPost, webLoginPath, bytes.NewBuffer([]byte(form.Encode())))
5047	assert.NoError(t, err)
5048	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5049	rr = executeRequest(req)
5050	assert.Equal(t, http.StatusFound, rr.Code)
5051	assert.Equal(t, webAdminTwoFactorPath, rr.Header().Get("Location"))
5052	cookie, err = getCookieFromResponse(rr)
5053	assert.NoError(t, err)
5054
5055	form = make(url.Values)
5056	recoveryCode := recCodes[0].Code
5057	form.Set("recovery_code", recoveryCode)
5058	// no csrf
5059	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
5060	assert.NoError(t, err)
5061	setJWTCookieForReq(req, cookie)
5062	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5063	rr = executeRequest(req)
5064	assert.Equal(t, http.StatusOK, rr.Code)
5065	assert.Contains(t, rr.Body.String(), "unable to verify form token")
5066
5067	form.Set(csrfFormToken, csrfToken)
5068	form.Set("recovery_code", "")
5069	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
5070	assert.NoError(t, err)
5071	setJWTCookieForReq(req, cookie)
5072	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5073	rr = executeRequest(req)
5074	assert.Equal(t, http.StatusOK, rr.Code)
5075	assert.Contains(t, rr.Body.String(), "Invalid credentials")
5076
5077	form.Set("recovery_code", recoveryCode)
5078	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
5079	assert.NoError(t, err)
5080	setJWTCookieForReq(req, cookie)
5081	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5082	rr = executeRequest(req)
5083	assert.Equal(t, http.StatusFound, rr.Code)
5084	assert.Equal(t, webUsersPath, rr.Header().Get("Location"))
5085	authenticatedCookie, err := getCookieFromResponse(rr)
5086	assert.NoError(t, err)
5087	//render MFA page
5088	req, err = http.NewRequest(http.MethodGet, webAdminMFAPath, nil)
5089	assert.NoError(t, err)
5090	setJWTCookieForReq(req, authenticatedCookie)
5091	rr = executeRequest(req)
5092	checkResponseCode(t, http.StatusOK, rr)
5093	// check that the recovery code was marked as used
5094	req, err = http.NewRequest(http.MethodGet, admin2FARecoveryCodesPath, nil)
5095	assert.NoError(t, err)
5096	setBearerForReq(req, altToken)
5097	rr = executeRequest(req)
5098	checkResponseCode(t, http.StatusOK, rr)
5099	recCodes = nil
5100	err = json.Unmarshal(rr.Body.Bytes(), &recCodes)
5101	assert.NoError(t, err)
5102	assert.Len(t, recCodes, 12)
5103	found := false
5104	for _, rc := range recCodes {
5105		if rc.Code == recoveryCode {
5106			found = true
5107			assert.True(t, rc.Used)
5108		} else {
5109			assert.False(t, rc.Used)
5110		}
5111	}
5112	assert.True(t, found)
5113	// the same recovery code cannot be reused
5114	form = getLoginForm(altAdminUsername, altAdminPassword, csrfToken)
5115	req, err = http.NewRequest(http.MethodPost, webLoginPath, bytes.NewBuffer([]byte(form.Encode())))
5116	assert.NoError(t, err)
5117	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5118	rr = executeRequest(req)
5119	assert.Equal(t, http.StatusFound, rr.Code)
5120	assert.Equal(t, webAdminTwoFactorPath, rr.Header().Get("Location"))
5121	cookie, err = getCookieFromResponse(rr)
5122	assert.NoError(t, err)
5123	form = make(url.Values)
5124	form.Set("recovery_code", recoveryCode)
5125	form.Set(csrfFormToken, csrfToken)
5126	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
5127	assert.NoError(t, err)
5128	setJWTCookieForReq(req, cookie)
5129	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5130	rr = executeRequest(req)
5131	assert.Equal(t, http.StatusOK, rr.Code)
5132	assert.Contains(t, rr.Body.String(), "This recovery code was already used")
5133
5134	form.Set("recovery_code", "invalid_recovery_code")
5135	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
5136	assert.NoError(t, err)
5137	setJWTCookieForReq(req, cookie)
5138	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5139	rr = executeRequest(req)
5140	assert.Equal(t, http.StatusOK, rr.Code)
5141	assert.Contains(t, rr.Body.String(), "Invalid recovery code")
5142
5143	form = getLoginForm(altAdminUsername, altAdminPassword, csrfToken)
5144	req, err = http.NewRequest(http.MethodPost, webLoginPath, bytes.NewBuffer([]byte(form.Encode())))
5145	assert.NoError(t, err)
5146	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5147	rr = executeRequest(req)
5148	assert.Equal(t, http.StatusFound, rr.Code)
5149	assert.Equal(t, webAdminTwoFactorPath, rr.Header().Get("Location"))
5150	cookie, err = getCookieFromResponse(rr)
5151	assert.NoError(t, err)
5152
5153	// disable TOTP
5154	req, err = http.NewRequest(http.MethodPut, adminPath+"/"+altAdminUsername+"/2fa/disable", nil)
5155	assert.NoError(t, err)
5156	setBearerForReq(req, altToken)
5157	rr = executeRequest(req)
5158	checkResponseCode(t, http.StatusOK, rr)
5159
5160	form = make(url.Values)
5161	form.Set("recovery_code", recoveryCode)
5162	form.Set(csrfFormToken, csrfToken)
5163	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
5164	assert.NoError(t, err)
5165	setJWTCookieForReq(req, cookie)
5166	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5167	rr = executeRequest(req)
5168	assert.Equal(t, http.StatusOK, rr.Code)
5169	assert.Contains(t, rr.Body.String(), "Two factory authentication is not enabled")
5170
5171	form.Set("passcode", passcode)
5172	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorPath, bytes.NewBuffer([]byte(form.Encode())))
5173	assert.NoError(t, err)
5174	setJWTCookieForReq(req, cookie)
5175	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5176	rr = executeRequest(req)
5177	assert.Equal(t, http.StatusOK, rr.Code)
5178	assert.Contains(t, rr.Body.String(), "Two factory authentication is not enabled")
5179
5180	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
5181	assert.NoError(t, err)
5182
5183	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
5184	assert.NoError(t, err)
5185	setJWTCookieForReq(req, cookie)
5186	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5187	rr = executeRequest(req)
5188	assert.Equal(t, http.StatusOK, rr.Code)
5189	assert.Contains(t, rr.Body.String(), "Invalid credentials")
5190
5191	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorPath, bytes.NewBuffer([]byte(form.Encode())))
5192	assert.NoError(t, err)
5193	setJWTCookieForReq(req, cookie)
5194	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5195	rr = executeRequest(req)
5196	assert.Equal(t, http.StatusOK, rr.Code)
5197	assert.Contains(t, rr.Body.String(), "Invalid credentials")
5198
5199	req, err = http.NewRequest(http.MethodGet, webAdminMFAPath, nil)
5200	assert.NoError(t, err)
5201	setJWTCookieForReq(req, authenticatedCookie)
5202	rr = executeRequest(req)
5203	checkResponseCode(t, http.StatusInternalServerError, rr)
5204}
5205
5206func TestAdminTOTP(t *testing.T) {
5207	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
5208	assert.NoError(t, err)
5209	admin := getTestAdmin()
5210	admin.Username = altAdminUsername
5211	admin.Password = altAdminPassword
5212	// TOTPConfig will be ignored on add
5213	admin.Filters.TOTPConfig = dataprovider.TOTPConfig{
5214		Enabled:    true,
5215		ConfigName: "config",
5216		Secret:     kms.NewEmptySecret(),
5217	}
5218	asJSON, err := json.Marshal(admin)
5219	assert.NoError(t, err)
5220	req, err := http.NewRequest(http.MethodPost, adminPath, bytes.NewBuffer(asJSON))
5221	assert.NoError(t, err)
5222	setBearerForReq(req, token)
5223	rr := executeRequest(req)
5224	checkResponseCode(t, http.StatusCreated, rr)
5225
5226	admin, _, err = httpdtest.GetAdminByUsername(altAdminUsername, http.StatusOK)
5227	assert.NoError(t, err)
5228	assert.False(t, admin.Filters.TOTPConfig.Enabled)
5229	assert.Len(t, admin.Filters.RecoveryCodes, 0)
5230
5231	altToken, err := getJWTAPITokenFromTestServer(altAdminUsername, altAdminPassword)
5232	assert.NoError(t, err)
5233
5234	req, err = http.NewRequest(http.MethodGet, adminTOTPConfigsPath, nil)
5235	assert.NoError(t, err)
5236	setBearerForReq(req, altToken)
5237	rr = executeRequest(req)
5238	checkResponseCode(t, http.StatusOK, rr)
5239	var configs []mfa.TOTPConfig
5240	err = json.Unmarshal(rr.Body.Bytes(), &configs)
5241	assert.NoError(t, err, rr.Body.String())
5242	assert.Len(t, configs, len(mfa.GetAvailableTOTPConfigs()))
5243	totpConfig := configs[0]
5244	totpReq := generateTOTPRequest{
5245		ConfigName: totpConfig.Name,
5246	}
5247	asJSON, err = json.Marshal(totpReq)
5248	assert.NoError(t, err)
5249	req, err = http.NewRequest(http.MethodPost, adminTOTPGeneratePath, bytes.NewBuffer(asJSON))
5250	assert.NoError(t, err)
5251	setBearerForReq(req, altToken)
5252	rr = executeRequest(req)
5253	checkResponseCode(t, http.StatusOK, rr)
5254	var totpGenResp generateTOTPResponse
5255	err = json.Unmarshal(rr.Body.Bytes(), &totpGenResp)
5256	assert.NoError(t, err)
5257	assert.NotEmpty(t, totpGenResp.Secret)
5258	assert.NotEmpty(t, totpGenResp.QRCode)
5259
5260	passcode, err := generateTOTPPasscode(totpGenResp.Secret)
5261	assert.NoError(t, err)
5262	validateReq := validateTOTPRequest{
5263		ConfigName: totpGenResp.ConfigName,
5264		Passcode:   passcode,
5265		Secret:     totpGenResp.Secret,
5266	}
5267	asJSON, err = json.Marshal(validateReq)
5268	assert.NoError(t, err)
5269	req, err = http.NewRequest(http.MethodPost, adminTOTPValidatePath, bytes.NewBuffer(asJSON))
5270	assert.NoError(t, err)
5271	setBearerForReq(req, altToken)
5272	rr = executeRequest(req)
5273	checkResponseCode(t, http.StatusOK, rr)
5274	// the same passcode cannot be reused
5275	req, err = http.NewRequest(http.MethodPost, adminTOTPValidatePath, bytes.NewBuffer(asJSON))
5276	assert.NoError(t, err)
5277	setBearerForReq(req, altToken)
5278	rr = executeRequest(req)
5279	checkResponseCode(t, http.StatusBadRequest, rr)
5280	assert.Contains(t, rr.Body.String(), "this passcode was already used")
5281
5282	adminTOTPConfig := dataprovider.TOTPConfig{
5283		Enabled:    true,
5284		ConfigName: totpGenResp.ConfigName,
5285		Secret:     kms.NewPlainSecret(totpGenResp.Secret),
5286	}
5287	asJSON, err = json.Marshal(adminTOTPConfig)
5288	assert.NoError(t, err)
5289	req, err = http.NewRequest(http.MethodPost, adminTOTPSavePath, bytes.NewBuffer(asJSON))
5290	assert.NoError(t, err)
5291	setBearerForReq(req, altToken)
5292	rr = executeRequest(req)
5293	checkResponseCode(t, http.StatusOK, rr)
5294	admin, _, err = httpdtest.GetAdminByUsername(altAdminUsername, http.StatusOK)
5295	assert.NoError(t, err)
5296	assert.True(t, admin.Filters.TOTPConfig.Enabled)
5297	assert.Equal(t, totpGenResp.ConfigName, admin.Filters.TOTPConfig.ConfigName)
5298	assert.Empty(t, admin.Filters.TOTPConfig.Secret.GetKey())
5299	assert.Empty(t, admin.Filters.TOTPConfig.Secret.GetAdditionalData())
5300	assert.NotEmpty(t, admin.Filters.TOTPConfig.Secret.GetPayload())
5301	assert.Equal(t, kms.SecretStatusSecretBox, admin.Filters.TOTPConfig.Secret.GetStatus())
5302	admin.Filters.TOTPConfig = dataprovider.TOTPConfig{
5303		Enabled:    false,
5304		ConfigName: util.GenerateUniqueID(),
5305		Secret:     kms.NewEmptySecret(),
5306	}
5307	admin.Filters.RecoveryCodes = []sdk.RecoveryCode{
5308		{
5309			Secret: kms.NewEmptySecret(),
5310		},
5311	}
5312	admin, resp, err := httpdtest.UpdateAdmin(admin, http.StatusOK)
5313	assert.NoError(t, err, string(resp))
5314	assert.True(t, admin.Filters.TOTPConfig.Enabled)
5315	assert.Len(t, admin.Filters.RecoveryCodes, 12)
5316	// if we use token we should get no recovery codes
5317	req, err = http.NewRequest(http.MethodGet, admin2FARecoveryCodesPath, nil)
5318	assert.NoError(t, err)
5319	setBearerForReq(req, token)
5320	rr = executeRequest(req)
5321	checkResponseCode(t, http.StatusOK, rr)
5322	var recCodes []recoveryCode
5323	err = json.Unmarshal(rr.Body.Bytes(), &recCodes)
5324	assert.NoError(t, err)
5325	assert.Len(t, recCodes, 0)
5326	// now the same but with altToken
5327	req, err = http.NewRequest(http.MethodGet, admin2FARecoveryCodesPath, nil)
5328	assert.NoError(t, err)
5329	setBearerForReq(req, altToken)
5330	rr = executeRequest(req)
5331	checkResponseCode(t, http.StatusOK, rr)
5332	recCodes = nil
5333	err = json.Unmarshal(rr.Body.Bytes(), &recCodes)
5334	assert.NoError(t, err)
5335	assert.Len(t, recCodes, 12)
5336	// regenerate recovery codes
5337	req, err = http.NewRequest(http.MethodPost, admin2FARecoveryCodesPath, nil)
5338	assert.NoError(t, err)
5339	setBearerForReq(req, altToken)
5340	rr = executeRequest(req)
5341	checkResponseCode(t, http.StatusOK, rr)
5342	// check that recovery codes are different
5343	req, err = http.NewRequest(http.MethodGet, admin2FARecoveryCodesPath, nil)
5344	assert.NoError(t, err)
5345	setBearerForReq(req, altToken)
5346	rr = executeRequest(req)
5347	checkResponseCode(t, http.StatusOK, rr)
5348	var newRecCodes []recoveryCode
5349	err = json.Unmarshal(rr.Body.Bytes(), &newRecCodes)
5350	assert.NoError(t, err)
5351	assert.Len(t, newRecCodes, 12)
5352	assert.NotEqual(t, recCodes, newRecCodes)
5353	// disable 2FA, the update admin API should not work
5354	admin.Filters.TOTPConfig.Enabled = false
5355	admin.Filters.RecoveryCodes = nil
5356	admin, _, err = httpdtest.UpdateAdmin(admin, http.StatusOK)
5357	assert.NoError(t, err)
5358	assert.Equal(t, altAdminUsername, admin.Username)
5359	assert.True(t, admin.Filters.TOTPConfig.Enabled)
5360	assert.Len(t, admin.Filters.RecoveryCodes, 12)
5361	// use the dedicated API
5362	req, err = http.NewRequest(http.MethodPut, adminPath+"/"+altAdminUsername+"/2fa/disable", nil)
5363	assert.NoError(t, err)
5364	setBearerForReq(req, token)
5365	rr = executeRequest(req)
5366	checkResponseCode(t, http.StatusOK, rr)
5367	admin, _, err = httpdtest.GetAdminByUsername(altAdminUsername, http.StatusOK)
5368	assert.NoError(t, err)
5369	assert.False(t, admin.Filters.TOTPConfig.Enabled)
5370	assert.Len(t, admin.Filters.RecoveryCodes, 0)
5371
5372	req, _ = http.NewRequest(http.MethodDelete, path.Join(adminPath, altAdminUsername), nil)
5373	setBearerForReq(req, token)
5374	rr = executeRequest(req)
5375	checkResponseCode(t, http.StatusOK, rr)
5376
5377	req, err = http.NewRequest(http.MethodPut, adminPath+"/"+altAdminUsername+"/2fa/disable", nil)
5378	assert.NoError(t, err)
5379	setBearerForReq(req, token)
5380	rr = executeRequest(req)
5381	checkResponseCode(t, http.StatusNotFound, rr)
5382
5383	req, err = http.NewRequest(http.MethodGet, admin2FARecoveryCodesPath, nil)
5384	assert.NoError(t, err)
5385	setBearerForReq(req, altToken)
5386	rr = executeRequest(req)
5387	checkResponseCode(t, http.StatusNotFound, rr)
5388
5389	req, err = http.NewRequest(http.MethodPost, admin2FARecoveryCodesPath, nil)
5390	assert.NoError(t, err)
5391	setBearerForReq(req, altToken)
5392	rr = executeRequest(req)
5393	checkResponseCode(t, http.StatusNotFound, rr)
5394
5395	req, err = http.NewRequest(http.MethodPost, adminTOTPSavePath, bytes.NewBuffer(asJSON))
5396	assert.NoError(t, err)
5397	setBearerForReq(req, altToken)
5398	rr = executeRequest(req)
5399	checkResponseCode(t, http.StatusNotFound, rr)
5400}
5401
5402func TestChangeAdminPwdInvalidJsonMock(t *testing.T) {
5403	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
5404	assert.NoError(t, err)
5405	req, _ := http.NewRequest(http.MethodPut, adminPwdPath, bytes.NewBuffer([]byte("{")))
5406	setBearerForReq(req, token)
5407	rr := executeRequest(req)
5408	checkResponseCode(t, http.StatusBadRequest, rr)
5409}
5410
5411func TestMFAPermission(t *testing.T) {
5412	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
5413	assert.NoError(t, err)
5414
5415	webToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
5416	assert.NoError(t, err)
5417
5418	req, err := http.NewRequest(http.MethodGet, webClientMFAPath, nil)
5419	assert.NoError(t, err)
5420	req.RequestURI = webClientMFAPath
5421	setJWTCookieForReq(req, webToken)
5422	rr := executeRequest(req)
5423	checkResponseCode(t, http.StatusOK, rr)
5424
5425	user.Filters.WebClient = []string{sdk.WebClientMFADisabled}
5426	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
5427	assert.NoError(t, err)
5428
5429	webToken, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
5430	assert.NoError(t, err)
5431
5432	req, err = http.NewRequest(http.MethodGet, webClientMFAPath, nil)
5433	assert.NoError(t, err)
5434	req.RequestURI = webClientMFAPath
5435	setJWTCookieForReq(req, webToken)
5436	rr = executeRequest(req)
5437	checkResponseCode(t, http.StatusForbidden, rr)
5438
5439	_, err = httpdtest.RemoveUser(user, http.StatusOK)
5440	assert.NoError(t, err)
5441	err = os.RemoveAll(user.GetHomeDir())
5442	assert.NoError(t, err)
5443}
5444
5445func TestWebUserTwoFactorLogin(t *testing.T) {
5446	u := getTestUser()
5447	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
5448	assert.NoError(t, err)
5449	// enable two factor authentication
5450	configName, _, secret, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], user.Username)
5451	assert.NoError(t, err)
5452	token, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
5453	assert.NoError(t, err)
5454	adminToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
5455	assert.NoError(t, err)
5456	webToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
5457	assert.NoError(t, err)
5458
5459	userTOTPConfig := sdk.TOTPConfig{
5460		Enabled:    true,
5461		ConfigName: configName,
5462		Secret:     kms.NewPlainSecret(secret),
5463		Protocols:  []string{common.ProtocolHTTP},
5464	}
5465	asJSON, err := json.Marshal(userTOTPConfig)
5466	assert.NoError(t, err)
5467	req, err := http.NewRequest(http.MethodPost, userTOTPSavePath, bytes.NewBuffer(asJSON))
5468	assert.NoError(t, err)
5469	setBearerForReq(req, token)
5470	rr := executeRequest(req)
5471	checkResponseCode(t, http.StatusOK, rr)
5472
5473	req, err = http.NewRequest(http.MethodGet, user2FARecoveryCodesPath, nil)
5474	assert.NoError(t, err)
5475	setBearerForReq(req, token)
5476	rr = executeRequest(req)
5477	checkResponseCode(t, http.StatusOK, rr)
5478	var recCodes []recoveryCode
5479	err = json.Unmarshal(rr.Body.Bytes(), &recCodes)
5480	assert.NoError(t, err)
5481	assert.Len(t, recCodes, 12)
5482
5483	user, _, err = httpdtest.GetUserByUsername(defaultUsername, http.StatusOK)
5484	assert.NoError(t, err)
5485	assert.Len(t, user.Filters.RecoveryCodes, 12)
5486	for _, c := range user.Filters.RecoveryCodes {
5487		assert.Empty(t, c.Secret.GetAdditionalData())
5488		assert.Empty(t, c.Secret.GetKey())
5489		assert.Equal(t, kms.SecretStatusSecretBox, c.Secret.GetStatus())
5490		assert.NotEmpty(t, c.Secret.GetPayload())
5491	}
5492
5493	req, err = http.NewRequest(http.MethodGet, webClientTwoFactorPath, nil)
5494	assert.NoError(t, err)
5495	setJWTCookieForReq(req, webToken)
5496	rr = executeRequest(req)
5497	checkResponseCode(t, http.StatusNotFound, rr)
5498
5499	req, err = http.NewRequest(http.MethodGet, webClientTwoFactorRecoveryPath, nil)
5500	assert.NoError(t, err)
5501	setJWTCookieForReq(req, webToken)
5502	rr = executeRequest(req)
5503	checkResponseCode(t, http.StatusNotFound, rr)
5504
5505	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorPath, nil)
5506	assert.NoError(t, err)
5507	setJWTCookieForReq(req, webToken)
5508	rr = executeRequest(req)
5509	checkResponseCode(t, http.StatusNotFound, rr)
5510
5511	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorRecoveryPath, nil)
5512	assert.NoError(t, err)
5513	setJWTCookieForReq(req, webToken)
5514	rr = executeRequest(req)
5515	checkResponseCode(t, http.StatusNotFound, rr)
5516
5517	csrfToken, err := getCSRFToken(httpBaseURL + webClientLoginPath)
5518	assert.NoError(t, err)
5519	form := getLoginForm(defaultUsername, defaultPassword, csrfToken)
5520	req, err = http.NewRequest(http.MethodPost, webClientLoginPath, bytes.NewBuffer([]byte(form.Encode())))
5521	assert.NoError(t, err)
5522	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5523	rr = executeRequest(req)
5524	assert.Equal(t, http.StatusFound, rr.Code)
5525	assert.Equal(t, webClientTwoFactorPath, rr.Header().Get("Location"))
5526	cookie, err := getCookieFromResponse(rr)
5527	assert.NoError(t, err)
5528
5529	req, err = http.NewRequest(http.MethodGet, webClientTwoFactorPath, nil)
5530	assert.NoError(t, err)
5531	setJWTCookieForReq(req, cookie)
5532	rr = executeRequest(req)
5533	checkResponseCode(t, http.StatusOK, rr)
5534
5535	// without a cookie
5536	req, err = http.NewRequest(http.MethodGet, webClientTwoFactorPath, nil)
5537	assert.NoError(t, err)
5538	rr = executeRequest(req)
5539	checkResponseCode(t, http.StatusNotFound, rr)
5540
5541	req, err = http.NewRequest(http.MethodGet, webClientTwoFactorRecoveryPath, nil)
5542	assert.NoError(t, err)
5543	setJWTCookieForReq(req, cookie)
5544	rr = executeRequest(req)
5545	checkResponseCode(t, http.StatusOK, rr)
5546	// any other page will be redirected to the two factor auth page
5547	req, err = http.NewRequest(http.MethodGet, webClientFilesPath, nil)
5548	assert.NoError(t, err)
5549	setJWTCookieForReq(req, cookie)
5550	rr = executeRequest(req)
5551	checkResponseCode(t, http.StatusFound, rr)
5552	assert.Equal(t, webClientTwoFactorPath, rr.Header().Get("Location"))
5553	// a partial token cannot be used for admin pages
5554	req, err = http.NewRequest(http.MethodGet, webUsersPath, nil)
5555	assert.NoError(t, err)
5556	setJWTCookieForReq(req, cookie)
5557	rr = executeRequest(req)
5558	checkResponseCode(t, http.StatusFound, rr)
5559	assert.Equal(t, webLoginPath, rr.Header().Get("Location"))
5560
5561	passcode, err := generateTOTPPasscode(secret)
5562	assert.NoError(t, err)
5563	form = make(url.Values)
5564	form.Set("passcode", passcode)
5565
5566	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorPath, bytes.NewBuffer([]byte(form.Encode())))
5567	assert.NoError(t, err)
5568	setJWTCookieForReq(req, cookie)
5569	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5570	rr = executeRequest(req)
5571	assert.Equal(t, http.StatusOK, rr.Code)
5572	assert.Contains(t, rr.Body.String(), "unable to verify form token")
5573
5574	form.Set(csrfFormToken, csrfToken)
5575	form.Set("passcode", "invalid_user_passcode")
5576	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorPath, bytes.NewBuffer([]byte(form.Encode())))
5577	assert.NoError(t, err)
5578	setJWTCookieForReq(req, cookie)
5579	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5580	rr = executeRequest(req)
5581	assert.Equal(t, http.StatusOK, rr.Code)
5582	assert.Contains(t, rr.Body.String(), "Invalid authentication code")
5583
5584	form.Set("passcode", "")
5585	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorPath, bytes.NewBuffer([]byte(form.Encode())))
5586	assert.NoError(t, err)
5587	setJWTCookieForReq(req, cookie)
5588	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5589	rr = executeRequest(req)
5590	assert.Equal(t, http.StatusOK, rr.Code)
5591	assert.Contains(t, rr.Body.String(), "Invalid credentials")
5592
5593	form.Set("passcode", passcode)
5594	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorPath, bytes.NewBuffer([]byte(form.Encode())))
5595	assert.NoError(t, err)
5596	setJWTCookieForReq(req, cookie)
5597	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5598	rr = executeRequest(req)
5599	assert.Equal(t, http.StatusFound, rr.Code)
5600	assert.Equal(t, webClientFilesPath, rr.Header().Get("Location"))
5601	// the same cookie cannot be reused
5602	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorPath, bytes.NewBuffer([]byte(form.Encode())))
5603	assert.NoError(t, err)
5604	setJWTCookieForReq(req, cookie)
5605	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5606	rr = executeRequest(req)
5607	assert.Equal(t, http.StatusNotFound, rr.Code)
5608	// get a new cookie and login using a recovery code
5609	form = getLoginForm(defaultUsername, defaultPassword, csrfToken)
5610	req, err = http.NewRequest(http.MethodPost, webClientLoginPath, bytes.NewBuffer([]byte(form.Encode())))
5611	assert.NoError(t, err)
5612	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5613	rr = executeRequest(req)
5614	assert.Equal(t, http.StatusFound, rr.Code)
5615	assert.Equal(t, webClientTwoFactorPath, rr.Header().Get("Location"))
5616	cookie, err = getCookieFromResponse(rr)
5617	assert.NoError(t, err)
5618
5619	form = make(url.Values)
5620	recoveryCode := recCodes[0].Code
5621	form.Set("recovery_code", recoveryCode)
5622	// no csrf
5623	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
5624	assert.NoError(t, err)
5625	setJWTCookieForReq(req, cookie)
5626	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5627	rr = executeRequest(req)
5628	assert.Equal(t, http.StatusOK, rr.Code)
5629	assert.Contains(t, rr.Body.String(), "unable to verify form token")
5630
5631	form.Set(csrfFormToken, csrfToken)
5632	form.Set("recovery_code", "")
5633	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
5634	assert.NoError(t, err)
5635	setJWTCookieForReq(req, cookie)
5636	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5637	rr = executeRequest(req)
5638	assert.Equal(t, http.StatusOK, rr.Code)
5639	assert.Contains(t, rr.Body.String(), "Invalid credentials")
5640
5641	form.Set("recovery_code", recoveryCode)
5642	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
5643	assert.NoError(t, err)
5644	setJWTCookieForReq(req, cookie)
5645	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5646	rr = executeRequest(req)
5647	assert.Equal(t, http.StatusFound, rr.Code)
5648	assert.Equal(t, webClientFilesPath, rr.Header().Get("Location"))
5649	authenticatedCookie, err := getCookieFromResponse(rr)
5650	assert.NoError(t, err)
5651	//render MFA page
5652	req, err = http.NewRequest(http.MethodGet, webClientMFAPath, nil)
5653	assert.NoError(t, err)
5654	setJWTCookieForReq(req, authenticatedCookie)
5655	rr = executeRequest(req)
5656	checkResponseCode(t, http.StatusOK, rr)
5657
5658	// check that the recovery code was marked as used
5659	req, err = http.NewRequest(http.MethodGet, user2FARecoveryCodesPath, nil)
5660	assert.NoError(t, err)
5661	setBearerForReq(req, token)
5662	rr = executeRequest(req)
5663	checkResponseCode(t, http.StatusOK, rr)
5664	recCodes = nil
5665	err = json.Unmarshal(rr.Body.Bytes(), &recCodes)
5666	assert.NoError(t, err)
5667	assert.Len(t, recCodes, 12)
5668	found := false
5669	for _, rc := range recCodes {
5670		if rc.Code == recoveryCode {
5671			found = true
5672			assert.True(t, rc.Used)
5673		} else {
5674			assert.False(t, rc.Used)
5675		}
5676	}
5677	assert.True(t, found)
5678	// the same recovery code cannot be reused
5679	form = getLoginForm(defaultUsername, defaultPassword, csrfToken)
5680	req, err = http.NewRequest(http.MethodPost, webClientLoginPath, bytes.NewBuffer([]byte(form.Encode())))
5681	assert.NoError(t, err)
5682	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5683	rr = executeRequest(req)
5684	assert.Equal(t, http.StatusFound, rr.Code)
5685	assert.Equal(t, webClientTwoFactorPath, rr.Header().Get("Location"))
5686	cookie, err = getCookieFromResponse(rr)
5687	assert.NoError(t, err)
5688	form = make(url.Values)
5689	form.Set("recovery_code", recoveryCode)
5690	form.Set(csrfFormToken, csrfToken)
5691	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
5692	assert.NoError(t, err)
5693	setJWTCookieForReq(req, cookie)
5694	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5695	rr = executeRequest(req)
5696	assert.Equal(t, http.StatusOK, rr.Code)
5697	assert.Contains(t, rr.Body.String(), "This recovery code was already used")
5698
5699	form.Set("recovery_code", "invalid_user_recovery_code")
5700	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
5701	assert.NoError(t, err)
5702	setJWTCookieForReq(req, cookie)
5703	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5704	rr = executeRequest(req)
5705	assert.Equal(t, http.StatusOK, rr.Code)
5706	assert.Contains(t, rr.Body.String(), "Invalid recovery code")
5707
5708	form = getLoginForm(defaultUsername, defaultPassword, csrfToken)
5709	req, err = http.NewRequest(http.MethodPost, webClientLoginPath, bytes.NewBuffer([]byte(form.Encode())))
5710	assert.NoError(t, err)
5711	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5712	rr = executeRequest(req)
5713	assert.Equal(t, http.StatusFound, rr.Code)
5714	assert.Equal(t, webClientTwoFactorPath, rr.Header().Get("Location"))
5715	cookie, err = getCookieFromResponse(rr)
5716	assert.NoError(t, err)
5717
5718	// disable TOTP
5719	req, err = http.NewRequest(http.MethodPut, userPath+"/"+user.Username+"/2fa/disable", nil)
5720	assert.NoError(t, err)
5721	setBearerForReq(req, adminToken)
5722	rr = executeRequest(req)
5723	checkResponseCode(t, http.StatusOK, rr)
5724
5725	form = make(url.Values)
5726	form.Set("recovery_code", recoveryCode)
5727	form.Set("passcode", passcode)
5728	form.Set(csrfFormToken, csrfToken)
5729
5730	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
5731	assert.NoError(t, err)
5732	setJWTCookieForReq(req, cookie)
5733	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5734	rr = executeRequest(req)
5735	assert.Equal(t, http.StatusOK, rr.Code)
5736	assert.Contains(t, rr.Body.String(), "Two factory authentication is not enabled")
5737
5738	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorPath, bytes.NewBuffer([]byte(form.Encode())))
5739	assert.NoError(t, err)
5740	setJWTCookieForReq(req, cookie)
5741	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5742	rr = executeRequest(req)
5743	assert.Equal(t, http.StatusOK, rr.Code)
5744	assert.Contains(t, rr.Body.String(), "Two factory authentication is not enabled")
5745
5746	_, err = httpdtest.RemoveUser(user, http.StatusOK)
5747	assert.NoError(t, err)
5748	err = os.RemoveAll(user.GetHomeDir())
5749	assert.NoError(t, err)
5750
5751	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
5752	assert.NoError(t, err)
5753	setJWTCookieForReq(req, cookie)
5754	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5755	rr = executeRequest(req)
5756	assert.Equal(t, http.StatusOK, rr.Code)
5757	assert.Contains(t, rr.Body.String(), "Invalid credentials")
5758
5759	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorPath, bytes.NewBuffer([]byte(form.Encode())))
5760	assert.NoError(t, err)
5761	setJWTCookieForReq(req, cookie)
5762	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
5763	rr = executeRequest(req)
5764	assert.Equal(t, http.StatusOK, rr.Code)
5765	assert.Contains(t, rr.Body.String(), "Invalid credentials")
5766
5767	req, err = http.NewRequest(http.MethodGet, webClientMFAPath, nil)
5768	assert.NoError(t, err)
5769	setJWTCookieForReq(req, authenticatedCookie)
5770	rr = executeRequest(req)
5771	checkResponseCode(t, http.StatusInternalServerError, rr)
5772}
5773
5774func TestSearchEvents(t *testing.T) {
5775	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
5776	assert.NoError(t, err)
5777
5778	req, err := http.NewRequest(http.MethodGet, fsEventsPath+"?limit=10&order=ASC", nil)
5779	assert.NoError(t, err)
5780	setBearerForReq(req, token)
5781	rr := executeRequest(req)
5782	checkResponseCode(t, http.StatusOK, rr)
5783
5784	// the test eventsearcher plugin returns error if start_timestamp < 0
5785	req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?start_timestamp=-1&end_timestamp=123456&statuses=1,2", nil)
5786	assert.NoError(t, err)
5787	setBearerForReq(req, token)
5788	rr = executeRequest(req)
5789	checkResponseCode(t, http.StatusInternalServerError, rr)
5790
5791	req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?limit=a", nil)
5792	assert.NoError(t, err)
5793	setBearerForReq(req, token)
5794	rr = executeRequest(req)
5795	checkResponseCode(t, http.StatusBadRequest, rr)
5796
5797	req, err = http.NewRequest(http.MethodGet, providerEventsPath, nil)
5798	assert.NoError(t, err)
5799	setBearerForReq(req, token)
5800	rr = executeRequest(req)
5801	checkResponseCode(t, http.StatusOK, rr)
5802
5803	// the test eventsearcher plugin returns error if start_timestamp < 0
5804	req, err = http.NewRequest(http.MethodGet, providerEventsPath+"?start_timestamp=-1", nil)
5805	assert.NoError(t, err)
5806	setBearerForReq(req, token)
5807	rr = executeRequest(req)
5808	checkResponseCode(t, http.StatusInternalServerError, rr)
5809
5810	req, err = http.NewRequest(http.MethodGet, providerEventsPath+"?limit=2000", nil)
5811	assert.NoError(t, err)
5812	setBearerForReq(req, token)
5813	rr = executeRequest(req)
5814	checkResponseCode(t, http.StatusBadRequest, rr)
5815
5816	req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?start_timestamp=a", nil)
5817	assert.NoError(t, err)
5818	setBearerForReq(req, token)
5819	rr = executeRequest(req)
5820	checkResponseCode(t, http.StatusBadRequest, rr)
5821
5822	req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?end_timestamp=a", nil)
5823	assert.NoError(t, err)
5824	setBearerForReq(req, token)
5825	rr = executeRequest(req)
5826	checkResponseCode(t, http.StatusBadRequest, rr)
5827
5828	req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?order=ASSC", nil)
5829	assert.NoError(t, err)
5830	setBearerForReq(req, token)
5831	rr = executeRequest(req)
5832	checkResponseCode(t, http.StatusBadRequest, rr)
5833
5834	req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?statuses=a,b", nil)
5835	assert.NoError(t, err)
5836	setBearerForReq(req, token)
5837	rr = executeRequest(req)
5838	checkResponseCode(t, http.StatusBadRequest, rr)
5839}
5840
5841func TestMFAErrors(t *testing.T) {
5842	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
5843	assert.NoError(t, err)
5844	assert.False(t, user.Filters.TOTPConfig.Enabled)
5845	userToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
5846	assert.NoError(t, err)
5847	adminToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
5848	assert.NoError(t, err)
5849
5850	// invalid config name
5851	totpReq := generateTOTPRequest{
5852		ConfigName: "invalid config name",
5853	}
5854	asJSON, err := json.Marshal(totpReq)
5855	assert.NoError(t, err)
5856	req, err := http.NewRequest(http.MethodPost, userTOTPGeneratePath, bytes.NewBuffer(asJSON))
5857	assert.NoError(t, err)
5858	setBearerForReq(req, userToken)
5859	rr := executeRequest(req)
5860	checkResponseCode(t, http.StatusBadRequest, rr)
5861	// invalid JSON
5862	invalidJSON := []byte("not a JSON")
5863	req, err = http.NewRequest(http.MethodPost, userTOTPGeneratePath, bytes.NewBuffer(invalidJSON))
5864	assert.NoError(t, err)
5865	setBearerForReq(req, userToken)
5866	rr = executeRequest(req)
5867	checkResponseCode(t, http.StatusBadRequest, rr)
5868
5869	req, err = http.NewRequest(http.MethodPost, userTOTPSavePath, bytes.NewBuffer(invalidJSON))
5870	assert.NoError(t, err)
5871	setBearerForReq(req, userToken)
5872	rr = executeRequest(req)
5873	checkResponseCode(t, http.StatusBadRequest, rr)
5874
5875	req, err = http.NewRequest(http.MethodPost, adminTOTPSavePath, bytes.NewBuffer(invalidJSON))
5876	assert.NoError(t, err)
5877	setBearerForReq(req, adminToken)
5878	rr = executeRequest(req)
5879	checkResponseCode(t, http.StatusBadRequest, rr)
5880
5881	req, err = http.NewRequest(http.MethodPost, adminTOTPValidatePath, bytes.NewBuffer(invalidJSON))
5882	assert.NoError(t, err)
5883	setBearerForReq(req, adminToken)
5884	rr = executeRequest(req)
5885	checkResponseCode(t, http.StatusBadRequest, rr)
5886	// invalid TOTP config name
5887	userTOTPConfig := sdk.TOTPConfig{
5888		Enabled:    true,
5889		ConfigName: "missing name",
5890		Secret:     kms.NewPlainSecret(xid.New().String()),
5891		Protocols:  []string{common.ProtocolSSH},
5892	}
5893	asJSON, err = json.Marshal(userTOTPConfig)
5894	assert.NoError(t, err)
5895	req, err = http.NewRequest(http.MethodPost, userTOTPSavePath, bytes.NewBuffer(asJSON))
5896	assert.NoError(t, err)
5897	setBearerForReq(req, userToken)
5898	rr = executeRequest(req)
5899	checkResponseCode(t, http.StatusBadRequest, rr)
5900	assert.Contains(t, rr.Body.String(), "totp: config name")
5901	// invalid TOTP secret
5902	userTOTPConfig = sdk.TOTPConfig{
5903		Enabled:    true,
5904		ConfigName: mfa.GetAvailableTOTPConfigNames()[0],
5905		Secret:     nil,
5906		Protocols:  []string{common.ProtocolSSH},
5907	}
5908	asJSON, err = json.Marshal(userTOTPConfig)
5909	assert.NoError(t, err)
5910	req, err = http.NewRequest(http.MethodPost, userTOTPSavePath, bytes.NewBuffer(asJSON))
5911	assert.NoError(t, err)
5912	setBearerForReq(req, userToken)
5913	rr = executeRequest(req)
5914	checkResponseCode(t, http.StatusBadRequest, rr)
5915	assert.Contains(t, rr.Body.String(), "totp: secret is mandatory")
5916	// no protocol
5917	userTOTPConfig = sdk.TOTPConfig{
5918		Enabled:    true,
5919		ConfigName: mfa.GetAvailableTOTPConfigNames()[0],
5920		Secret:     kms.NewPlainSecret(xid.New().String()),
5921		Protocols:  nil,
5922	}
5923	asJSON, err = json.Marshal(userTOTPConfig)
5924	assert.NoError(t, err)
5925	req, err = http.NewRequest(http.MethodPost, userTOTPSavePath, bytes.NewBuffer(asJSON))
5926	assert.NoError(t, err)
5927	setBearerForReq(req, userToken)
5928	rr = executeRequest(req)
5929	checkResponseCode(t, http.StatusBadRequest, rr)
5930	assert.Contains(t, rr.Body.String(), "totp: specify at least one protocol")
5931	// invalid protocol
5932	userTOTPConfig = sdk.TOTPConfig{
5933		Enabled:    true,
5934		ConfigName: mfa.GetAvailableTOTPConfigNames()[0],
5935		Secret:     kms.NewPlainSecret(xid.New().String()),
5936		Protocols:  []string{common.ProtocolWebDAV},
5937	}
5938	asJSON, err = json.Marshal(userTOTPConfig)
5939	assert.NoError(t, err)
5940	req, err = http.NewRequest(http.MethodPost, userTOTPSavePath, bytes.NewBuffer(asJSON))
5941	assert.NoError(t, err)
5942	setBearerForReq(req, userToken)
5943	rr = executeRequest(req)
5944	checkResponseCode(t, http.StatusBadRequest, rr)
5945	assert.Contains(t, rr.Body.String(), "totp: invalid protocol")
5946
5947	adminTOTPConfig := dataprovider.TOTPConfig{
5948		Enabled:    true,
5949		ConfigName: "",
5950		Secret:     kms.NewPlainSecret("secret"),
5951	}
5952	asJSON, err = json.Marshal(adminTOTPConfig)
5953	assert.NoError(t, err)
5954	req, err = http.NewRequest(http.MethodPost, adminTOTPSavePath, bytes.NewBuffer(asJSON))
5955	assert.NoError(t, err)
5956	setBearerForReq(req, adminToken)
5957	rr = executeRequest(req)
5958	checkResponseCode(t, http.StatusBadRequest, rr)
5959	assert.Contains(t, rr.Body.String(), "totp: config name is mandatory")
5960
5961	adminTOTPConfig = dataprovider.TOTPConfig{
5962		Enabled:    true,
5963		ConfigName: mfa.GetAvailableTOTPConfigNames()[0],
5964		Secret:     nil,
5965	}
5966	asJSON, err = json.Marshal(adminTOTPConfig)
5967	assert.NoError(t, err)
5968	req, err = http.NewRequest(http.MethodPost, adminTOTPSavePath, bytes.NewBuffer(asJSON))
5969	assert.NoError(t, err)
5970	setBearerForReq(req, adminToken)
5971	rr = executeRequest(req)
5972	checkResponseCode(t, http.StatusBadRequest, rr)
5973	assert.Contains(t, rr.Body.String(), "totp: secret is mandatory")
5974
5975	// invalid TOTP secret status
5976	userTOTPConfig = sdk.TOTPConfig{
5977		Enabled:    true,
5978		ConfigName: mfa.GetAvailableTOTPConfigNames()[0],
5979		Secret:     kms.NewSecret(kms.SecretStatusRedacted, "", "", ""),
5980		Protocols:  []string{common.ProtocolSSH},
5981	}
5982	asJSON, err = json.Marshal(userTOTPConfig)
5983	assert.NoError(t, err)
5984	req, err = http.NewRequest(http.MethodPost, userTOTPSavePath, bytes.NewBuffer(asJSON))
5985	assert.NoError(t, err)
5986	setBearerForReq(req, userToken)
5987	rr = executeRequest(req)
5988	checkResponseCode(t, http.StatusBadRequest, rr)
5989	// previous secret will be preserved and we have no secret saved
5990	assert.Contains(t, rr.Body.String(), "totp: secret is mandatory")
5991
5992	req, err = http.NewRequest(http.MethodPost, adminTOTPSavePath, bytes.NewBuffer(asJSON))
5993	assert.NoError(t, err)
5994	setBearerForReq(req, adminToken)
5995	rr = executeRequest(req)
5996	checkResponseCode(t, http.StatusBadRequest, rr)
5997	assert.Contains(t, rr.Body.String(), "totp: secret is mandatory")
5998
5999	_, err = httpdtest.RemoveUser(user, http.StatusOK)
6000	assert.NoError(t, err)
6001	err = os.RemoveAll(user.GetHomeDir())
6002	assert.NoError(t, err)
6003}
6004
6005func TestMFAInvalidSecret(t *testing.T) {
6006	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
6007	assert.NoError(t, err)
6008
6009	userToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
6010	assert.NoError(t, err)
6011
6012	user.Password = defaultPassword
6013	user.Filters.TOTPConfig = sdk.TOTPConfig{
6014		Enabled:    true,
6015		ConfigName: mfa.GetAvailableTOTPConfigNames()[0],
6016		Secret:     kms.NewSecret(kms.SecretStatusSecretBox, "payload", "key", user.Username),
6017		Protocols:  []string{common.ProtocolSSH, common.ProtocolHTTP},
6018	}
6019	user.Filters.RecoveryCodes = append(user.Filters.RecoveryCodes, sdk.RecoveryCode{
6020		Used:   false,
6021		Secret: kms.NewSecret(kms.SecretStatusSecretBox, "payload", "key", user.Username),
6022	})
6023	err = dataprovider.UpdateUser(&user, "", "")
6024	assert.NoError(t, err)
6025
6026	req, err := http.NewRequest(http.MethodGet, user2FARecoveryCodesPath, nil)
6027	assert.NoError(t, err)
6028	setBearerForReq(req, userToken)
6029	rr := executeRequest(req)
6030	checkResponseCode(t, http.StatusInternalServerError, rr)
6031	assert.Contains(t, rr.Body.String(), "Unable to decrypt recovery codes")
6032
6033	csrfToken, err := getCSRFToken(httpBaseURL + webClientLoginPath)
6034	assert.NoError(t, err)
6035	form := getLoginForm(defaultUsername, defaultPassword, csrfToken)
6036	req, err = http.NewRequest(http.MethodPost, webClientLoginPath, bytes.NewBuffer([]byte(form.Encode())))
6037	assert.NoError(t, err)
6038	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
6039	rr = executeRequest(req)
6040	assert.Equal(t, http.StatusFound, rr.Code)
6041	assert.Equal(t, webClientTwoFactorPath, rr.Header().Get("Location"))
6042	cookie, err := getCookieFromResponse(rr)
6043	assert.NoError(t, err)
6044	form = make(url.Values)
6045	form.Set(csrfFormToken, csrfToken)
6046	form.Set("passcode", "123456")
6047	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorPath, bytes.NewBuffer([]byte(form.Encode())))
6048	assert.NoError(t, err)
6049	setJWTCookieForReq(req, cookie)
6050	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
6051	rr = executeRequest(req)
6052	assert.Equal(t, http.StatusInternalServerError, rr.Code)
6053
6054	form = make(url.Values)
6055	form.Set(csrfFormToken, csrfToken)
6056	form.Set("recovery_code", "RC-123456")
6057	req, err = http.NewRequest(http.MethodPost, webClientTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
6058	assert.NoError(t, err)
6059	setJWTCookieForReq(req, cookie)
6060	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
6061	rr = executeRequest(req)
6062	assert.Equal(t, http.StatusInternalServerError, rr.Code)
6063
6064	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userTokenPath), nil)
6065	assert.NoError(t, err)
6066	req.Header.Set("X-SFTPGO-OTP", "authcode")
6067	req.SetBasicAuth(defaultUsername, defaultPassword)
6068	resp, err := httpclient.GetHTTPClient().Do(req)
6069	assert.NoError(t, err)
6070	assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
6071	err = resp.Body.Close()
6072	assert.NoError(t, err)
6073
6074	_, err = httpdtest.RemoveUser(user, http.StatusOK)
6075	assert.NoError(t, err)
6076	err = os.RemoveAll(user.GetHomeDir())
6077	assert.NoError(t, err)
6078
6079	admin := getTestAdmin()
6080	admin.Username = altAdminUsername
6081	admin.Password = altAdminPassword
6082	admin, _, err = httpdtest.AddAdmin(admin, http.StatusCreated)
6083	assert.NoError(t, err)
6084
6085	admin.Password = altAdminPassword
6086	admin.Filters.TOTPConfig = dataprovider.TOTPConfig{
6087		Enabled:    true,
6088		ConfigName: mfa.GetAvailableTOTPConfigNames()[0],
6089		Secret:     kms.NewSecret(kms.SecretStatusSecretBox, "payload", "key", user.Username),
6090	}
6091	admin.Filters.RecoveryCodes = append(user.Filters.RecoveryCodes, sdk.RecoveryCode{
6092		Used:   false,
6093		Secret: kms.NewSecret(kms.SecretStatusSecretBox, "payload", "key", user.Username),
6094	})
6095	err = dataprovider.UpdateAdmin(&admin, "", "")
6096	assert.NoError(t, err)
6097
6098	csrfToken, err = getCSRFToken(httpBaseURL + webLoginPath)
6099	assert.NoError(t, err)
6100	form = getLoginForm(altAdminUsername, altAdminPassword, csrfToken)
6101	req, err = http.NewRequest(http.MethodPost, webLoginPath, bytes.NewBuffer([]byte(form.Encode())))
6102	assert.NoError(t, err)
6103	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
6104	rr = executeRequest(req)
6105	assert.Equal(t, http.StatusFound, rr.Code)
6106	assert.Equal(t, webAdminTwoFactorPath, rr.Header().Get("Location"))
6107	cookie, err = getCookieFromResponse(rr)
6108	assert.NoError(t, err)
6109	form = make(url.Values)
6110	form.Set(csrfFormToken, csrfToken)
6111	form.Set("passcode", "123456")
6112	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorPath, bytes.NewBuffer([]byte(form.Encode())))
6113	assert.NoError(t, err)
6114	setJWTCookieForReq(req, cookie)
6115	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
6116	rr = executeRequest(req)
6117	assert.Equal(t, http.StatusInternalServerError, rr.Code)
6118
6119	form = make(url.Values)
6120	form.Set(csrfFormToken, csrfToken)
6121	form.Set("recovery_code", "RC-123456")
6122	req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
6123	assert.NoError(t, err)
6124	setJWTCookieForReq(req, cookie)
6125	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
6126	rr = executeRequest(req)
6127	assert.Equal(t, http.StatusInternalServerError, rr.Code)
6128
6129	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, tokenPath), nil)
6130	assert.NoError(t, err)
6131	req.Header.Set("X-SFTPGO-OTP", "auth-code")
6132	req.SetBasicAuth(altAdminUsername, altAdminPassword)
6133	resp, err = httpclient.GetHTTPClient().Do(req)
6134	assert.NoError(t, err)
6135	assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
6136	err = resp.Body.Close()
6137	assert.NoError(t, err)
6138
6139	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
6140	assert.NoError(t, err)
6141}
6142
6143func TestWebUserTOTP(t *testing.T) {
6144	u := getTestUser()
6145	// TOTPConfig will be ignored on add
6146	u.Filters.TOTPConfig = sdk.TOTPConfig{
6147		Enabled:    true,
6148		ConfigName: "",
6149		Secret:     kms.NewEmptySecret(),
6150		Protocols:  []string{common.ProtocolSSH},
6151	}
6152	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
6153	assert.NoError(t, err)
6154	assert.False(t, user.Filters.TOTPConfig.Enabled)
6155	token, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
6156	assert.NoError(t, err)
6157
6158	req, err := http.NewRequest(http.MethodGet, userTOTPConfigsPath, nil)
6159	assert.NoError(t, err)
6160	setBearerForReq(req, token)
6161	rr := executeRequest(req)
6162	checkResponseCode(t, http.StatusOK, rr)
6163	var configs []mfa.TOTPConfig
6164	err = json.Unmarshal(rr.Body.Bytes(), &configs)
6165	assert.NoError(t, err, rr.Body.String())
6166	assert.Len(t, configs, len(mfa.GetAvailableTOTPConfigs()))
6167	totpConfig := configs[0]
6168	totpReq := generateTOTPRequest{
6169		ConfigName: totpConfig.Name,
6170	}
6171	asJSON, err := json.Marshal(totpReq)
6172	assert.NoError(t, err)
6173	req, err = http.NewRequest(http.MethodPost, userTOTPGeneratePath, bytes.NewBuffer(asJSON))
6174	assert.NoError(t, err)
6175	setBearerForReq(req, token)
6176	rr = executeRequest(req)
6177	checkResponseCode(t, http.StatusOK, rr)
6178	var totpGenResp generateTOTPResponse
6179	err = json.Unmarshal(rr.Body.Bytes(), &totpGenResp)
6180	assert.NoError(t, err)
6181	assert.NotEmpty(t, totpGenResp.Secret)
6182	assert.NotEmpty(t, totpGenResp.QRCode)
6183
6184	passcode, err := generateTOTPPasscode(totpGenResp.Secret)
6185	assert.NoError(t, err)
6186	validateReq := validateTOTPRequest{
6187		ConfigName: totpGenResp.ConfigName,
6188		Passcode:   passcode,
6189		Secret:     totpGenResp.Secret,
6190	}
6191	asJSON, err = json.Marshal(validateReq)
6192	assert.NoError(t, err)
6193	req, err = http.NewRequest(http.MethodPost, userTOTPValidatePath, bytes.NewBuffer(asJSON))
6194	assert.NoError(t, err)
6195	setBearerForReq(req, token)
6196	rr = executeRequest(req)
6197	checkResponseCode(t, http.StatusOK, rr)
6198	// the same passcode cannot be reused
6199	req, err = http.NewRequest(http.MethodPost, userTOTPValidatePath, bytes.NewBuffer(asJSON))
6200	assert.NoError(t, err)
6201	setBearerForReq(req, token)
6202	rr = executeRequest(req)
6203	checkResponseCode(t, http.StatusBadRequest, rr)
6204	assert.Contains(t, rr.Body.String(), "this passcode was already used")
6205
6206	userTOTPConfig := sdk.TOTPConfig{
6207		Enabled:    true,
6208		ConfigName: totpGenResp.ConfigName,
6209		Secret:     kms.NewPlainSecret(totpGenResp.Secret),
6210		Protocols:  []string{common.ProtocolSSH},
6211	}
6212	asJSON, err = json.Marshal(userTOTPConfig)
6213	assert.NoError(t, err)
6214	req, err = http.NewRequest(http.MethodPost, userTOTPSavePath, bytes.NewBuffer(asJSON))
6215	assert.NoError(t, err)
6216	setBearerForReq(req, token)
6217	rr = executeRequest(req)
6218	checkResponseCode(t, http.StatusOK, rr)
6219
6220	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
6221	assert.NoError(t, err)
6222	totpCfg := user.Filters.TOTPConfig
6223	assert.True(t, totpCfg.Enabled)
6224	secretPayload := totpCfg.Secret.GetPayload()
6225	assert.Equal(t, totpGenResp.ConfigName, totpCfg.ConfigName)
6226	assert.Empty(t, totpCfg.Secret.GetKey())
6227	assert.Empty(t, totpCfg.Secret.GetAdditionalData())
6228	assert.NotEmpty(t, secretPayload)
6229	assert.Equal(t, kms.SecretStatusSecretBox, totpCfg.Secret.GetStatus())
6230	assert.Len(t, totpCfg.Protocols, 1)
6231	assert.Contains(t, totpCfg.Protocols, common.ProtocolSSH)
6232	// update protocols only
6233	userTOTPConfig = sdk.TOTPConfig{
6234		Protocols: []string{common.ProtocolSSH, common.ProtocolFTP},
6235		Secret:    kms.NewEmptySecret(),
6236	}
6237	asJSON, err = json.Marshal(userTOTPConfig)
6238	assert.NoError(t, err)
6239	req, err = http.NewRequest(http.MethodPost, userTOTPSavePath, bytes.NewBuffer(asJSON))
6240	assert.NoError(t, err)
6241	setBearerForReq(req, token)
6242	rr = executeRequest(req)
6243	checkResponseCode(t, http.StatusOK, rr)
6244
6245	// update the user, TOTP should not be affected
6246	user.Filters.TOTPConfig = sdk.TOTPConfig{
6247		Enabled: false,
6248		Secret:  kms.NewEmptySecret(),
6249	}
6250	_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
6251	assert.NoError(t, err)
6252
6253	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
6254	assert.NoError(t, err)
6255	assert.True(t, user.Filters.TOTPConfig.Enabled)
6256	assert.Equal(t, totpCfg.ConfigName, user.Filters.TOTPConfig.ConfigName)
6257	assert.Empty(t, user.Filters.TOTPConfig.Secret.GetKey())
6258	assert.Empty(t, user.Filters.TOTPConfig.Secret.GetAdditionalData())
6259	assert.Equal(t, secretPayload, user.Filters.TOTPConfig.Secret.GetPayload())
6260	assert.Equal(t, kms.SecretStatusSecretBox, user.Filters.TOTPConfig.Secret.GetStatus())
6261	assert.Len(t, user.Filters.TOTPConfig.Protocols, 2)
6262	assert.Contains(t, user.Filters.TOTPConfig.Protocols, common.ProtocolSSH)
6263	assert.Contains(t, user.Filters.TOTPConfig.Protocols, common.ProtocolFTP)
6264
6265	req, err = http.NewRequest(http.MethodGet, user2FARecoveryCodesPath, nil)
6266	assert.NoError(t, err)
6267	setBearerForReq(req, token)
6268	rr = executeRequest(req)
6269	checkResponseCode(t, http.StatusOK, rr)
6270	var recCodes []recoveryCode
6271	err = json.Unmarshal(rr.Body.Bytes(), &recCodes)
6272	assert.NoError(t, err)
6273	assert.Len(t, recCodes, 12)
6274	// regenerate recovery codes
6275	req, err = http.NewRequest(http.MethodPost, user2FARecoveryCodesPath, nil)
6276	assert.NoError(t, err)
6277	setBearerForReq(req, token)
6278	rr = executeRequest(req)
6279	checkResponseCode(t, http.StatusOK, rr)
6280	// check that recovery codes are different
6281	req, err = http.NewRequest(http.MethodGet, user2FARecoveryCodesPath, nil)
6282	assert.NoError(t, err)
6283	setBearerForReq(req, token)
6284	rr = executeRequest(req)
6285	checkResponseCode(t, http.StatusOK, rr)
6286	var newRecCodes []recoveryCode
6287	err = json.Unmarshal(rr.Body.Bytes(), &newRecCodes)
6288	assert.NoError(t, err)
6289	assert.Len(t, newRecCodes, 12)
6290	assert.NotEqual(t, recCodes, newRecCodes)
6291	// disable 2FA, the update user API should not work
6292	adminToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
6293	assert.NoError(t, err)
6294	user.Filters.TOTPConfig.Enabled = false
6295	user.Filters.RecoveryCodes = nil
6296	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
6297	assert.NoError(t, err)
6298	assert.Equal(t, defaultUsername, user.Username)
6299	assert.True(t, user.Filters.TOTPConfig.Enabled)
6300	assert.Len(t, user.Filters.RecoveryCodes, 12)
6301	// use the dedicated API
6302	req, err = http.NewRequest(http.MethodPut, userPath+"/"+defaultUsername+"/2fa/disable", nil)
6303	assert.NoError(t, err)
6304	setBearerForReq(req, adminToken)
6305	rr = executeRequest(req)
6306	checkResponseCode(t, http.StatusOK, rr)
6307	user, _, err = httpdtest.GetUserByUsername(defaultUsername, http.StatusOK)
6308	assert.NoError(t, err)
6309	assert.False(t, user.Filters.TOTPConfig.Enabled)
6310	assert.Len(t, user.Filters.RecoveryCodes, 0)
6311
6312	_, err = httpdtest.RemoveUser(user, http.StatusOK)
6313	assert.NoError(t, err)
6314	err = os.RemoveAll(user.GetHomeDir())
6315	assert.NoError(t, err)
6316
6317	req, err = http.NewRequest(http.MethodPut, userPath+"/"+defaultUsername+"/2fa/disable", nil)
6318	assert.NoError(t, err)
6319	setBearerForReq(req, adminToken)
6320	rr = executeRequest(req)
6321	checkResponseCode(t, http.StatusNotFound, rr)
6322
6323	req, err = http.NewRequest(http.MethodGet, user2FARecoveryCodesPath, nil)
6324	assert.NoError(t, err)
6325	setBearerForReq(req, token)
6326	rr = executeRequest(req)
6327	checkResponseCode(t, http.StatusNotFound, rr)
6328
6329	req, err = http.NewRequest(http.MethodPost, user2FARecoveryCodesPath, nil)
6330	assert.NoError(t, err)
6331	setBearerForReq(req, token)
6332	rr = executeRequest(req)
6333	checkResponseCode(t, http.StatusNotFound, rr)
6334
6335	req, err = http.NewRequest(http.MethodPost, userTOTPSavePath, bytes.NewBuffer(asJSON))
6336	assert.NoError(t, err)
6337	setBearerForReq(req, token)
6338	rr = executeRequest(req)
6339	checkResponseCode(t, http.StatusNotFound, rr)
6340}
6341
6342func TestWebAPIChangeUserProfileMock(t *testing.T) {
6343	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
6344	assert.NoError(t, err)
6345	assert.False(t, user.Filters.AllowAPIKeyAuth)
6346	token, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
6347	assert.NoError(t, err)
6348	// invalid json
6349	req, err := http.NewRequest(http.MethodPut, userProfilePath, bytes.NewBuffer([]byte("{")))
6350	assert.NoError(t, err)
6351	setBearerForReq(req, token)
6352	rr := executeRequest(req)
6353	checkResponseCode(t, http.StatusBadRequest, rr)
6354
6355	email := "userapi@example.com"
6356	description := "user API description"
6357	profileReq := make(map[string]interface{})
6358	profileReq["allow_api_key_auth"] = true
6359	profileReq["email"] = email
6360	profileReq["description"] = description
6361	profileReq["public_keys"] = []string{testPubKey, testPubKey1}
6362	asJSON, err := json.Marshal(profileReq)
6363	assert.NoError(t, err)
6364	req, err = http.NewRequest(http.MethodPut, userProfilePath, bytes.NewBuffer(asJSON))
6365	assert.NoError(t, err)
6366	setBearerForReq(req, token)
6367	rr = executeRequest(req)
6368	checkResponseCode(t, http.StatusOK, rr)
6369
6370	profileReq = make(map[string]interface{})
6371	req, err = http.NewRequest(http.MethodGet, userProfilePath, nil)
6372	assert.NoError(t, err)
6373	setBearerForReq(req, token)
6374	rr = executeRequest(req)
6375	checkResponseCode(t, http.StatusOK, rr)
6376	err = json.Unmarshal(rr.Body.Bytes(), &profileReq)
6377	assert.NoError(t, err)
6378	assert.Equal(t, email, profileReq["email"].(string))
6379	assert.Equal(t, description, profileReq["description"].(string))
6380	assert.True(t, profileReq["allow_api_key_auth"].(bool))
6381	assert.Len(t, profileReq["public_keys"].([]interface{}), 2)
6382	// set an invalid email
6383	profileReq = make(map[string]interface{})
6384	profileReq["email"] = "notavalidemail"
6385	asJSON, err = json.Marshal(profileReq)
6386	assert.NoError(t, err)
6387	req, err = http.NewRequest(http.MethodPut, userProfilePath, bytes.NewBuffer(asJSON))
6388	assert.NoError(t, err)
6389	setBearerForReq(req, token)
6390	rr = executeRequest(req)
6391	checkResponseCode(t, http.StatusBadRequest, rr)
6392	assert.Contains(t, rr.Body.String(), "Validation error: email")
6393	// set an invalid public key
6394	profileReq = make(map[string]interface{})
6395	profileReq["public_keys"] = []string{"not a public key"}
6396	asJSON, err = json.Marshal(profileReq)
6397	assert.NoError(t, err)
6398	req, err = http.NewRequest(http.MethodPut, userProfilePath, bytes.NewBuffer(asJSON))
6399	assert.NoError(t, err)
6400	setBearerForReq(req, token)
6401	rr = executeRequest(req)
6402	checkResponseCode(t, http.StatusBadRequest, rr)
6403	assert.Contains(t, rr.Body.String(), "Validation error: could not parse key")
6404
6405	user.Filters.WebClient = []string{sdk.WebClientAPIKeyAuthChangeDisabled, sdk.WebClientPubKeyChangeDisabled}
6406	user.Email = email
6407	user.Description = description
6408	user.Filters.AllowAPIKeyAuth = true
6409	_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
6410	assert.NoError(t, err)
6411	token, err = getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
6412	assert.NoError(t, err)
6413
6414	profileReq = make(map[string]interface{})
6415	profileReq["allow_api_key_auth"] = false
6416	profileReq["email"] = email
6417	profileReq["description"] = description + "_mod"
6418	profileReq["public_keys"] = []string{testPubKey}
6419	asJSON, err = json.Marshal(profileReq)
6420	assert.NoError(t, err)
6421	req, err = http.NewRequest(http.MethodPut, userProfilePath, bytes.NewBuffer(asJSON))
6422	assert.NoError(t, err)
6423	setBearerForReq(req, token)
6424	rr = executeRequest(req)
6425	checkResponseCode(t, http.StatusOK, rr)
6426	assert.Contains(t, rr.Body.String(), "Profile updated")
6427	// check that api key auth and public keys were not changed
6428	profileReq = make(map[string]interface{})
6429	req, err = http.NewRequest(http.MethodGet, userProfilePath, nil)
6430	assert.NoError(t, err)
6431	setBearerForReq(req, token)
6432	rr = executeRequest(req)
6433	checkResponseCode(t, http.StatusOK, rr)
6434	err = json.Unmarshal(rr.Body.Bytes(), &profileReq)
6435	assert.NoError(t, err)
6436	assert.Equal(t, email, profileReq["email"].(string))
6437	assert.Equal(t, description+"_mod", profileReq["description"].(string))
6438	assert.True(t, profileReq["allow_api_key_auth"].(bool))
6439	assert.Len(t, profileReq["public_keys"].([]interface{}), 2)
6440
6441	user.Filters.WebClient = []string{sdk.WebClientAPIKeyAuthChangeDisabled, sdk.WebClientInfoChangeDisabled}
6442	user.Description = description + "_mod"
6443	_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
6444	assert.NoError(t, err)
6445	token, err = getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
6446	assert.NoError(t, err)
6447
6448	profileReq = make(map[string]interface{})
6449	profileReq["allow_api_key_auth"] = false
6450	profileReq["email"] = "newemail@apiuser.com"
6451	profileReq["description"] = description
6452	profileReq["public_keys"] = []string{testPubKey}
6453	asJSON, err = json.Marshal(profileReq)
6454	assert.NoError(t, err)
6455	req, err = http.NewRequest(http.MethodPut, userProfilePath, bytes.NewBuffer(asJSON))
6456	assert.NoError(t, err)
6457	setBearerForReq(req, token)
6458	rr = executeRequest(req)
6459	checkResponseCode(t, http.StatusOK, rr)
6460	profileReq = make(map[string]interface{})
6461	req, err = http.NewRequest(http.MethodGet, userProfilePath, nil)
6462	assert.NoError(t, err)
6463	setBearerForReq(req, token)
6464	rr = executeRequest(req)
6465	checkResponseCode(t, http.StatusOK, rr)
6466	err = json.Unmarshal(rr.Body.Bytes(), &profileReq)
6467	assert.NoError(t, err)
6468	assert.Equal(t, email, profileReq["email"].(string))
6469	assert.Equal(t, description+"_mod", profileReq["description"].(string))
6470	assert.True(t, profileReq["allow_api_key_auth"].(bool))
6471	assert.Len(t, profileReq["public_keys"].([]interface{}), 1)
6472	// finally disable all profile permissions
6473	user.Filters.WebClient = []string{sdk.WebClientAPIKeyAuthChangeDisabled, sdk.WebClientInfoChangeDisabled,
6474		sdk.WebClientPubKeyChangeDisabled}
6475	_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
6476	assert.NoError(t, err)
6477	req, err = http.NewRequest(http.MethodPut, userProfilePath, bytes.NewBuffer(asJSON))
6478	assert.NoError(t, err)
6479	setBearerForReq(req, token)
6480	rr = executeRequest(req)
6481	checkResponseCode(t, http.StatusForbidden, rr)
6482	assert.Contains(t, rr.Body.String(), "You are not allowed to change anything")
6483
6484	_, err = httpdtest.RemoveUser(user, http.StatusOK)
6485	assert.NoError(t, err)
6486	err = os.RemoveAll(user.GetHomeDir())
6487	assert.NoError(t, err)
6488
6489	req, err = http.NewRequest(http.MethodGet, userProfilePath, nil)
6490	assert.NoError(t, err)
6491	setBearerForReq(req, token)
6492	rr = executeRequest(req)
6493	checkResponseCode(t, http.StatusNotFound, rr)
6494
6495	req, err = http.NewRequest(http.MethodPut, userProfilePath, bytes.NewBuffer(asJSON))
6496	assert.NoError(t, err)
6497	setBearerForReq(req, token)
6498	rr = executeRequest(req)
6499	checkResponseCode(t, http.StatusNotFound, rr)
6500}
6501
6502func TestWebAPIChangeUserPwdMock(t *testing.T) {
6503	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
6504	assert.NoError(t, err)
6505	token, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
6506	assert.NoError(t, err)
6507	// invalid json
6508	req, err := http.NewRequest(http.MethodPut, userPwdPath, bytes.NewBuffer([]byte("{")))
6509	assert.NoError(t, err)
6510	setBearerForReq(req, token)
6511	rr := executeRequest(req)
6512	checkResponseCode(t, http.StatusBadRequest, rr)
6513
6514	pwd := make(map[string]string)
6515	pwd["current_password"] = defaultPassword
6516	pwd["new_password"] = defaultPassword
6517	asJSON, err := json.Marshal(pwd)
6518	assert.NoError(t, err)
6519	req, err = http.NewRequest(http.MethodPut, userPwdPath, bytes.NewBuffer(asJSON))
6520	assert.NoError(t, err)
6521	setBearerForReq(req, token)
6522	rr = executeRequest(req)
6523	checkResponseCode(t, http.StatusBadRequest, rr)
6524	assert.Contains(t, rr.Body.String(), "the new password must be different from the current one")
6525
6526	pwd["new_password"] = altAdminPassword
6527	asJSON, err = json.Marshal(pwd)
6528	assert.NoError(t, err)
6529	req, err = http.NewRequest(http.MethodPut, userPwdPath, bytes.NewBuffer(asJSON))
6530	assert.NoError(t, err)
6531	setBearerForReq(req, token)
6532	rr = executeRequest(req)
6533	checkResponseCode(t, http.StatusOK, rr)
6534	_, err = getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
6535	assert.Error(t, err)
6536	token, err = getJWTAPIUserTokenFromTestServer(defaultUsername, altAdminPassword)
6537	assert.NoError(t, err)
6538	assert.NotEmpty(t, token)
6539
6540	// remove the change password permission
6541	user.Filters.WebClient = []string{sdk.WebClientPasswordChangeDisabled}
6542	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
6543	assert.NoError(t, err)
6544	assert.Len(t, user.Filters.WebClient, 1)
6545	assert.Contains(t, user.Filters.WebClient, sdk.WebClientPasswordChangeDisabled)
6546
6547	token, err = getJWTAPIUserTokenFromTestServer(defaultUsername, altAdminPassword)
6548	assert.NoError(t, err)
6549	assert.NotEmpty(t, token)
6550
6551	pwd["current_password"] = altAdminPassword
6552	pwd["new_password"] = defaultPassword
6553	asJSON, err = json.Marshal(pwd)
6554	assert.NoError(t, err)
6555	req, err = http.NewRequest(http.MethodPut, userPwdPath, bytes.NewBuffer(asJSON))
6556	assert.NoError(t, err)
6557	setBearerForReq(req, token)
6558	rr = executeRequest(req)
6559	checkResponseCode(t, http.StatusForbidden, rr)
6560
6561	_, err = httpdtest.RemoveUser(user, http.StatusOK)
6562	assert.NoError(t, err)
6563	err = os.RemoveAll(user.GetHomeDir())
6564	assert.NoError(t, err)
6565}
6566
6567func TestLoginInvalidPasswordMock(t *testing.T) {
6568	_, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass+"1")
6569	assert.Error(t, err)
6570	// now a login with no credentials
6571	req, _ := http.NewRequest(http.MethodGet, "/api/v2/token", nil)
6572	rr := executeRequest(req)
6573	assert.Equal(t, http.StatusUnauthorized, rr.Code)
6574}
6575
6576func TestWebAPIChangeAdminProfileMock(t *testing.T) {
6577	admin := getTestAdmin()
6578	admin.Username = altAdminUsername
6579	admin.Password = altAdminPassword
6580	admin, _, err := httpdtest.AddAdmin(admin, http.StatusCreated)
6581	assert.NoError(t, err)
6582	assert.False(t, admin.Filters.AllowAPIKeyAuth)
6583
6584	token, err := getJWTAPITokenFromTestServer(altAdminUsername, altAdminPassword)
6585	assert.NoError(t, err)
6586	// invalid json
6587	req, err := http.NewRequest(http.MethodPut, adminProfilePath, bytes.NewBuffer([]byte("{")))
6588	assert.NoError(t, err)
6589	setBearerForReq(req, token)
6590	rr := executeRequest(req)
6591	checkResponseCode(t, http.StatusBadRequest, rr)
6592
6593	email := "adminapi@example.com"
6594	description := "admin API description"
6595	profileReq := make(map[string]interface{})
6596	profileReq["allow_api_key_auth"] = true
6597	profileReq["email"] = email
6598	profileReq["description"] = description
6599	asJSON, err := json.Marshal(profileReq)
6600	assert.NoError(t, err)
6601	req, err = http.NewRequest(http.MethodPut, adminProfilePath, bytes.NewBuffer(asJSON))
6602	assert.NoError(t, err)
6603	setBearerForReq(req, token)
6604	rr = executeRequest(req)
6605	checkResponseCode(t, http.StatusOK, rr)
6606	assert.Contains(t, rr.Body.String(), "Profile updated")
6607
6608	profileReq = make(map[string]interface{})
6609	req, err = http.NewRequest(http.MethodGet, adminProfilePath, nil)
6610	assert.NoError(t, err)
6611	setBearerForReq(req, token)
6612	rr = executeRequest(req)
6613	checkResponseCode(t, http.StatusOK, rr)
6614	err = json.Unmarshal(rr.Body.Bytes(), &profileReq)
6615	assert.NoError(t, err)
6616	assert.Equal(t, email, profileReq["email"].(string))
6617	assert.Equal(t, description, profileReq["description"].(string))
6618	assert.True(t, profileReq["allow_api_key_auth"].(bool))
6619	// set an invalid email
6620	profileReq["email"] = "admin_invalid_email"
6621	asJSON, err = json.Marshal(profileReq)
6622	assert.NoError(t, err)
6623	req, err = http.NewRequest(http.MethodPut, adminProfilePath, bytes.NewBuffer(asJSON))
6624	assert.NoError(t, err)
6625	setBearerForReq(req, token)
6626	rr = executeRequest(req)
6627	checkResponseCode(t, http.StatusBadRequest, rr)
6628	assert.Contains(t, rr.Body.String(), "Validation error: email")
6629
6630	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
6631	assert.NoError(t, err)
6632
6633	req, err = http.NewRequest(http.MethodGet, adminProfilePath, nil)
6634	assert.NoError(t, err)
6635	setBearerForReq(req, token)
6636	rr = executeRequest(req)
6637	checkResponseCode(t, http.StatusNotFound, rr)
6638
6639	req, err = http.NewRequest(http.MethodPut, adminProfilePath, bytes.NewBuffer(asJSON))
6640	assert.NoError(t, err)
6641	setBearerForReq(req, token)
6642	rr = executeRequest(req)
6643	checkResponseCode(t, http.StatusNotFound, rr)
6644}
6645
6646func TestChangeAdminPwdMock(t *testing.T) {
6647	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
6648	assert.NoError(t, err)
6649	admin := getTestAdmin()
6650	admin.Username = altAdminUsername
6651	admin.Password = altAdminPassword
6652	admin.Permissions = []string{dataprovider.PermAdminAddUsers, dataprovider.PermAdminDeleteUsers}
6653	asJSON, err := json.Marshal(admin)
6654	assert.NoError(t, err)
6655	req, _ := http.NewRequest(http.MethodPost, adminPath, bytes.NewBuffer(asJSON))
6656	setBearerForReq(req, token)
6657	rr := executeRequest(req)
6658	checkResponseCode(t, http.StatusCreated, rr)
6659
6660	altToken, err := getJWTAPITokenFromTestServer(altAdminUsername, altAdminPassword)
6661	assert.NoError(t, err)
6662	user := getTestUser()
6663	userAsJSON := getUserAsJSON(t, user)
6664	req, _ = http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
6665	setBearerForReq(req, altToken)
6666	rr = executeRequest(req)
6667	checkResponseCode(t, http.StatusCreated, rr)
6668
6669	req, _ = http.NewRequest(http.MethodPut, path.Join(userPath, user.Username), bytes.NewBuffer(userAsJSON))
6670	setBearerForReq(req, altToken)
6671	rr = executeRequest(req)
6672	checkResponseCode(t, http.StatusForbidden, rr)
6673
6674	pwd := make(map[string]string)
6675	pwd["current_password"] = altAdminPassword
6676	pwd["new_password"] = defaultTokenAuthPass
6677	asJSON, err = json.Marshal(pwd)
6678	assert.NoError(t, err)
6679	req, _ = http.NewRequest(http.MethodPut, adminPwdPath, bytes.NewBuffer(asJSON))
6680	setBearerForReq(req, altToken)
6681	rr = executeRequest(req)
6682	checkResponseCode(t, http.StatusOK, rr)
6683
6684	_, err = getJWTAPITokenFromTestServer(altAdminUsername, altAdminPassword)
6685	assert.Error(t, err)
6686
6687	altToken, err = getJWTAPITokenFromTestServer(altAdminUsername, defaultTokenAuthPass)
6688	assert.NoError(t, err)
6689	req, _ = http.NewRequest(http.MethodPut, adminPwdPath, bytes.NewBuffer(asJSON))
6690	setBearerForReq(req, altToken)
6691	rr = executeRequest(req)
6692	checkResponseCode(t, http.StatusBadRequest, rr) // current password does not match
6693
6694	req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
6695	setBearerForReq(req, altToken)
6696	rr = executeRequest(req)
6697	checkResponseCode(t, http.StatusOK, rr)
6698
6699	req, _ = http.NewRequest(http.MethodDelete, path.Join(adminPath, altAdminUsername), nil)
6700	setBearerForReq(req, token)
6701	rr = executeRequest(req)
6702	checkResponseCode(t, http.StatusOK, rr)
6703}
6704
6705func TestUpdateAdminMock(t *testing.T) {
6706	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
6707	assert.NoError(t, err)
6708	_, err = getJWTAPITokenFromTestServer(altAdminUsername, defaultTokenAuthPass)
6709	assert.Error(t, err)
6710	admin := getTestAdmin()
6711	admin.Username = altAdminUsername
6712	admin.Permissions = []string{dataprovider.PermAdminManageAdmins}
6713	asJSON, err := json.Marshal(admin)
6714	assert.NoError(t, err)
6715	req, _ := http.NewRequest(http.MethodPost, adminPath, bytes.NewBuffer(asJSON))
6716	setBearerForReq(req, token)
6717	rr := executeRequest(req)
6718	checkResponseCode(t, http.StatusCreated, rr)
6719	_, err = getJWTAPITokenFromTestServer(altAdminUsername, defaultTokenAuthPass)
6720	assert.NoError(t, err)
6721
6722	req, _ = http.NewRequest(http.MethodPut, path.Join(adminPath, "abc"), bytes.NewBuffer(asJSON))
6723	setBearerForReq(req, token)
6724	rr = executeRequest(req)
6725	checkResponseCode(t, http.StatusNotFound, rr)
6726	req, _ = http.NewRequest(http.MethodPut, path.Join(adminPath, altAdminUsername), bytes.NewBuffer([]byte("no json")))
6727	setBearerForReq(req, token)
6728	rr = executeRequest(req)
6729	checkResponseCode(t, http.StatusBadRequest, rr)
6730	admin.Permissions = nil
6731	asJSON, err = json.Marshal(admin)
6732	assert.NoError(t, err)
6733	req, _ = http.NewRequest(http.MethodPut, path.Join(adminPath, altAdminUsername), bytes.NewBuffer(asJSON))
6734	setBearerForReq(req, token)
6735	rr = executeRequest(req)
6736	checkResponseCode(t, http.StatusBadRequest, rr)
6737	admin = getTestAdmin()
6738	admin.Status = 0
6739	asJSON, err = json.Marshal(admin)
6740	assert.NoError(t, err)
6741	req, _ = http.NewRequest(http.MethodPut, path.Join(adminPath, defaultTokenAuthUser), bytes.NewBuffer(asJSON))
6742	setBearerForReq(req, token)
6743	rr = executeRequest(req)
6744	checkResponseCode(t, http.StatusBadRequest, rr)
6745	admin.Status = 1
6746	admin.Permissions = []string{dataprovider.PermAdminAddUsers}
6747	asJSON, err = json.Marshal(admin)
6748	assert.NoError(t, err)
6749	req, _ = http.NewRequest(http.MethodPut, path.Join(adminPath, defaultTokenAuthUser), bytes.NewBuffer(asJSON))
6750	setBearerForReq(req, token)
6751	rr = executeRequest(req)
6752	checkResponseCode(t, http.StatusBadRequest, rr)
6753
6754	altToken, err := getJWTAPITokenFromTestServer(altAdminUsername, defaultTokenAuthPass)
6755	assert.NoError(t, err)
6756	admin.Password = "" // it must remain unchanged
6757	admin.Permissions = []string{dataprovider.PermAdminManageAdmins, dataprovider.PermAdminCloseConnections}
6758	asJSON, err = json.Marshal(admin)
6759	assert.NoError(t, err)
6760	req, _ = http.NewRequest(http.MethodPut, path.Join(adminPath, altAdminUsername), bytes.NewBuffer(asJSON))
6761	setBearerForReq(req, altToken)
6762	rr = executeRequest(req)
6763	checkResponseCode(t, http.StatusOK, rr)
6764
6765	_, err = getJWTAPITokenFromTestServer(altAdminUsername, defaultTokenAuthPass)
6766	assert.NoError(t, err)
6767
6768	req, _ = http.NewRequest(http.MethodDelete, path.Join(adminPath, altAdminUsername), nil)
6769	setBearerForReq(req, token)
6770	rr = executeRequest(req)
6771	checkResponseCode(t, http.StatusOK, rr)
6772}
6773
6774func TestAdminLastLoginWithAPIKey(t *testing.T) {
6775	admin := getTestAdmin()
6776	admin.Username = altAdminUsername
6777	admin.Filters.AllowAPIKeyAuth = true
6778	admin, resp, err := httpdtest.AddAdmin(admin, http.StatusCreated)
6779	assert.NoError(t, err, string(resp))
6780	assert.Equal(t, int64(0), admin.LastLogin)
6781
6782	apiKey := dataprovider.APIKey{
6783		Name:      "admin API key",
6784		Scope:     dataprovider.APIKeyScopeAdmin,
6785		Admin:     altAdminUsername,
6786		LastUseAt: 123,
6787	}
6788
6789	apiKey, resp, err = httpdtest.AddAPIKey(apiKey, http.StatusCreated)
6790	assert.NoError(t, err, string(resp))
6791	assert.Equal(t, int64(0), apiKey.LastUseAt)
6792
6793	req, err := http.NewRequest(http.MethodGet, versionPath, nil)
6794	assert.NoError(t, err)
6795	setAPIKeyForReq(req, apiKey.Key, admin.Username)
6796	rr := executeRequest(req)
6797	checkResponseCode(t, http.StatusOK, rr)
6798
6799	admin, _, err = httpdtest.GetAdminByUsername(altAdminUsername, http.StatusOK)
6800	assert.NoError(t, err)
6801	assert.Greater(t, admin.LastLogin, int64(0))
6802
6803	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
6804	assert.NoError(t, err)
6805}
6806
6807func TestUserLastLoginWithAPIKey(t *testing.T) {
6808	user := getTestUser()
6809	user.Filters.AllowAPIKeyAuth = true
6810	user, resp, err := httpdtest.AddUser(user, http.StatusCreated)
6811	assert.NoError(t, err, string(resp))
6812	assert.Equal(t, int64(0), user.LastLogin)
6813
6814	apiKey := dataprovider.APIKey{
6815		Name:  "user API key",
6816		Scope: dataprovider.APIKeyScopeUser,
6817		User:  user.Username,
6818	}
6819
6820	apiKey, resp, err = httpdtest.AddAPIKey(apiKey, http.StatusCreated)
6821	assert.NoError(t, err, string(resp))
6822
6823	req, err := http.NewRequest(http.MethodGet, userDirsPath, nil)
6824	assert.NoError(t, err)
6825	setAPIKeyForReq(req, apiKey.Key, "")
6826	rr := executeRequest(req)
6827	checkResponseCode(t, http.StatusOK, rr)
6828
6829	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
6830	assert.NoError(t, err)
6831	assert.Greater(t, user.LastLogin, int64(0))
6832
6833	_, err = httpdtest.RemoveUser(user, http.StatusOK)
6834	assert.NoError(t, err)
6835	err = os.RemoveAll(user.GetHomeDir())
6836	assert.NoError(t, err)
6837}
6838
6839func TestAdminHandlingWithAPIKeys(t *testing.T) {
6840	sysAdmin, _, err := httpdtest.GetAdminByUsername(defaultTokenAuthUser, http.StatusOK)
6841	assert.NoError(t, err)
6842	sysAdmin.Filters.AllowAPIKeyAuth = true
6843	sysAdmin, _, err = httpdtest.UpdateAdmin(sysAdmin, http.StatusOK)
6844	assert.NoError(t, err)
6845
6846	apiKey := dataprovider.APIKey{
6847		Name:  "test admin API key",
6848		Scope: dataprovider.APIKeyScopeAdmin,
6849		Admin: defaultTokenAuthUser,
6850	}
6851
6852	apiKey, resp, err := httpdtest.AddAPIKey(apiKey, http.StatusCreated)
6853	assert.NoError(t, err, string(resp))
6854
6855	admin := getTestAdmin()
6856	admin.Username = altAdminUsername
6857	asJSON, err := json.Marshal(admin)
6858	assert.NoError(t, err)
6859	req, err := http.NewRequest(http.MethodPost, adminPath, bytes.NewBuffer(asJSON))
6860	assert.NoError(t, err)
6861	setAPIKeyForReq(req, apiKey.Key, "")
6862	rr := executeRequest(req)
6863	checkResponseCode(t, http.StatusCreated, rr)
6864	_, err = getJWTAPITokenFromTestServer(altAdminUsername, defaultTokenAuthPass)
6865	assert.NoError(t, err)
6866
6867	admin.Filters.AllowAPIKeyAuth = true
6868	asJSON, err = json.Marshal(admin)
6869	assert.NoError(t, err)
6870	req, err = http.NewRequest(http.MethodPut, path.Join(adminPath, altAdminUsername), bytes.NewBuffer(asJSON))
6871	assert.NoError(t, err)
6872	setAPIKeyForReq(req, apiKey.Key, "")
6873	rr = executeRequest(req)
6874	checkResponseCode(t, http.StatusOK, rr)
6875
6876	req, err = http.NewRequest(http.MethodGet, path.Join(adminPath, altAdminUsername), nil)
6877	assert.NoError(t, err)
6878	setAPIKeyForReq(req, apiKey.Key, "")
6879	rr = executeRequest(req)
6880	checkResponseCode(t, http.StatusOK, rr)
6881	var adminGet dataprovider.Admin
6882	err = json.Unmarshal(rr.Body.Bytes(), &adminGet)
6883	assert.NoError(t, err)
6884	assert.True(t, adminGet.Filters.AllowAPIKeyAuth)
6885
6886	req, err = http.NewRequest(http.MethodPut, path.Join(adminPath, defaultTokenAuthUser), bytes.NewBuffer(asJSON))
6887	assert.NoError(t, err)
6888	setAPIKeyForReq(req, apiKey.Key, "")
6889	rr = executeRequest(req)
6890	checkResponseCode(t, http.StatusBadRequest, rr)
6891	assert.Contains(t, rr.Body.String(), "updating the admin impersonated with an API key is not allowed")
6892	// changing the password for the impersonated admin is not allowed
6893	pwd := make(map[string]string)
6894	pwd["current_password"] = defaultTokenAuthPass
6895	pwd["new_password"] = altAdminPassword
6896	asJSON, err = json.Marshal(pwd)
6897	assert.NoError(t, err)
6898	req, _ = http.NewRequest(http.MethodPut, adminPwdPath, bytes.NewBuffer(asJSON))
6899	setAPIKeyForReq(req, apiKey.Key, "")
6900	rr = executeRequest(req)
6901	checkResponseCode(t, http.StatusForbidden, rr)
6902	assert.Contains(t, rr.Body.String(), "API key authentication is not allowed")
6903
6904	req, err = http.NewRequest(http.MethodDelete, path.Join(adminPath, defaultTokenAuthUser), nil)
6905	assert.NoError(t, err)
6906	setAPIKeyForReq(req, apiKey.Key, "")
6907	rr = executeRequest(req)
6908	checkResponseCode(t, http.StatusBadRequest, rr)
6909	assert.Contains(t, rr.Body.String(), "you cannot delete yourself")
6910
6911	req, err = http.NewRequest(http.MethodDelete, path.Join(adminPath, altAdminUsername), nil)
6912	assert.NoError(t, err)
6913	setAPIKeyForReq(req, apiKey.Key, "")
6914	rr = executeRequest(req)
6915	checkResponseCode(t, http.StatusOK, rr)
6916
6917	_, err = httpdtest.RemoveAPIKey(apiKey, http.StatusOK)
6918	assert.NoError(t, err)
6919
6920	dbAdmin, err := dataprovider.AdminExists(defaultTokenAuthUser)
6921	assert.NoError(t, err)
6922	dbAdmin.Filters.AllowAPIKeyAuth = false
6923	err = dataprovider.UpdateAdmin(&dbAdmin, "", "")
6924	assert.NoError(t, err)
6925	sysAdmin, _, err = httpdtest.GetAdminByUsername(defaultTokenAuthUser, http.StatusOK)
6926	assert.NoError(t, err)
6927	assert.False(t, sysAdmin.Filters.AllowAPIKeyAuth)
6928}
6929
6930func TestUserHandlingWithAPIKey(t *testing.T) {
6931	admin := getTestAdmin()
6932	admin.Username = altAdminUsername
6933	admin.Filters.AllowAPIKeyAuth = true
6934	admin, _, err := httpdtest.AddAdmin(admin, http.StatusCreated)
6935	assert.NoError(t, err)
6936
6937	apiKey := dataprovider.APIKey{
6938		Name:  "test admin API key",
6939		Scope: dataprovider.APIKeyScopeAdmin,
6940		Admin: admin.Username,
6941	}
6942
6943	apiKey, _, err = httpdtest.AddAPIKey(apiKey, http.StatusCreated)
6944	assert.NoError(t, err)
6945
6946	user := getTestUser()
6947	userAsJSON := getUserAsJSON(t, user)
6948	req, err := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
6949	assert.NoError(t, err)
6950	setAPIKeyForReq(req, apiKey.Key, "")
6951	rr := executeRequest(req)
6952	checkResponseCode(t, http.StatusCreated, rr)
6953
6954	user.Filters.DisableFsChecks = true
6955	user.Description = "desc"
6956	userAsJSON = getUserAsJSON(t, user)
6957	req, err = http.NewRequest(http.MethodPut, path.Join(userPath, user.Username), bytes.NewBuffer(userAsJSON))
6958	assert.NoError(t, err)
6959	setAPIKeyForReq(req, apiKey.Key, "")
6960	rr = executeRequest(req)
6961	checkResponseCode(t, http.StatusOK, rr)
6962
6963	req, err = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
6964	assert.NoError(t, err)
6965	setAPIKeyForReq(req, apiKey.Key, "")
6966	rr = executeRequest(req)
6967	checkResponseCode(t, http.StatusOK, rr)
6968	var updatedUser dataprovider.User
6969	err = json.Unmarshal(rr.Body.Bytes(), &updatedUser)
6970	assert.NoError(t, err)
6971	assert.True(t, updatedUser.Filters.DisableFsChecks)
6972	assert.Equal(t, user.Description, updatedUser.Description)
6973
6974	req, err = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
6975	assert.NoError(t, err)
6976	setAPIKeyForReq(req, apiKey.Key, "")
6977	rr = executeRequest(req)
6978	checkResponseCode(t, http.StatusOK, rr)
6979	err = os.RemoveAll(user.GetHomeDir())
6980	assert.NoError(t, err)
6981
6982	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
6983	assert.NoError(t, err)
6984	_, _, err = httpdtest.GetAPIKeyByID(apiKey.KeyID, http.StatusNotFound)
6985	assert.NoError(t, err)
6986}
6987
6988func TestUpdateUserMock(t *testing.T) {
6989	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
6990	assert.NoError(t, err)
6991	user := getTestUser()
6992	userAsJSON := getUserAsJSON(t, user)
6993	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
6994	setBearerForReq(req, token)
6995	rr := executeRequest(req)
6996	checkResponseCode(t, http.StatusCreated, rr)
6997	err = render.DecodeJSON(rr.Body, &user)
6998	assert.NoError(t, err)
6999	// permissions should not change if empty or nil
7000	permissions := user.Permissions
7001	user.Permissions = make(map[string][]string)
7002	userAsJSON = getUserAsJSON(t, user)
7003	req, _ = http.NewRequest(http.MethodPut, userPath+"/"+user.Username, bytes.NewBuffer(userAsJSON))
7004	setBearerForReq(req, token)
7005	rr = executeRequest(req)
7006	checkResponseCode(t, http.StatusOK, rr)
7007	req, _ = http.NewRequest(http.MethodGet, userPath+"/"+user.Username, nil)
7008	setBearerForReq(req, token)
7009	rr = executeRequest(req)
7010	checkResponseCode(t, http.StatusOK, rr)
7011	var updatedUser dataprovider.User
7012	err = render.DecodeJSON(rr.Body, &updatedUser)
7013	assert.NoError(t, err)
7014	for dir, perms := range permissions {
7015		if actualPerms, ok := updatedUser.Permissions[dir]; ok {
7016			for _, v := range actualPerms {
7017				assert.True(t, util.IsStringInSlice(v, perms))
7018			}
7019		} else {
7020			assert.Fail(t, "Permissions directories mismatch")
7021		}
7022	}
7023	req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
7024	setBearerForReq(req, token)
7025	rr = executeRequest(req)
7026	checkResponseCode(t, http.StatusOK, rr)
7027}
7028
7029func TestUpdateUserQuotaUsageMock(t *testing.T) {
7030	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7031	assert.NoError(t, err)
7032	var user dataprovider.User
7033	u := getTestUser()
7034	usedQuotaFiles := 1
7035	usedQuotaSize := int64(65535)
7036	u.UsedQuotaFiles = usedQuotaFiles
7037	u.UsedQuotaSize = usedQuotaSize
7038	u.QuotaFiles = 100
7039	userAsJSON := getUserAsJSON(t, u)
7040	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
7041	setBearerForReq(req, token)
7042	rr := executeRequest(req)
7043	checkResponseCode(t, http.StatusCreated, rr)
7044	err = render.DecodeJSON(rr.Body, &user)
7045	assert.NoError(t, err)
7046	req, _ = http.NewRequest(http.MethodPut, path.Join(quotasBasePath, "users", u.Username, "usage"), bytes.NewBuffer(userAsJSON))
7047	setBearerForReq(req, token)
7048	rr = executeRequest(req)
7049	checkResponseCode(t, http.StatusOK, rr)
7050	req, _ = http.NewRequest(http.MethodPut, updateUsedQuotaCompatPath, bytes.NewBuffer(userAsJSON))
7051	setBearerForReq(req, token)
7052	rr = executeRequest(req)
7053	checkResponseCode(t, http.StatusOK, rr)
7054	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
7055	setBearerForReq(req, token)
7056	rr = executeRequest(req)
7057	checkResponseCode(t, http.StatusOK, rr)
7058	err = render.DecodeJSON(rr.Body, &user)
7059	assert.NoError(t, err)
7060	assert.Equal(t, usedQuotaFiles, user.UsedQuotaFiles)
7061	assert.Equal(t, usedQuotaSize, user.UsedQuotaSize)
7062	// now update only quota size
7063	u.UsedQuotaFiles = 0
7064	userAsJSON = getUserAsJSON(t, u)
7065	req, _ = http.NewRequest(http.MethodPut, path.Join(quotasBasePath, "users", u.Username, "usage")+"?mode=add", bytes.NewBuffer(userAsJSON))
7066	setBearerForReq(req, token)
7067	rr = executeRequest(req)
7068	checkResponseCode(t, http.StatusOK, rr)
7069	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
7070	setBearerForReq(req, token)
7071	rr = executeRequest(req)
7072	checkResponseCode(t, http.StatusOK, rr)
7073	err = render.DecodeJSON(rr.Body, &user)
7074	assert.NoError(t, err)
7075	assert.Equal(t, usedQuotaFiles, user.UsedQuotaFiles)
7076	assert.Equal(t, usedQuotaSize*2, user.UsedQuotaSize)
7077	// only quota files
7078	u.UsedQuotaFiles = usedQuotaFiles
7079	u.UsedQuotaSize = 0
7080	userAsJSON = getUserAsJSON(t, u)
7081	req, _ = http.NewRequest(http.MethodPut, path.Join(quotasBasePath, "users", u.Username, "usage")+"?mode=add", bytes.NewBuffer(userAsJSON))
7082	setBearerForReq(req, token)
7083	rr = executeRequest(req)
7084	checkResponseCode(t, http.StatusOK, rr)
7085	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
7086	setBearerForReq(req, token)
7087	rr = executeRequest(req)
7088	checkResponseCode(t, http.StatusOK, rr)
7089	err = render.DecodeJSON(rr.Body, &user)
7090	assert.NoError(t, err)
7091	assert.Equal(t, usedQuotaFiles*2, user.UsedQuotaFiles)
7092	assert.Equal(t, usedQuotaSize*2, user.UsedQuotaSize)
7093	req, _ = http.NewRequest(http.MethodPut, updateUsedQuotaCompatPath, bytes.NewBuffer([]byte("string")))
7094	setBearerForReq(req, token)
7095	rr = executeRequest(req)
7096	checkResponseCode(t, http.StatusBadRequest, rr)
7097	req, _ = http.NewRequest(http.MethodPut, path.Join(quotasBasePath, "users", u.Username, "usage"), bytes.NewBuffer([]byte("string")))
7098	setBearerForReq(req, token)
7099	rr = executeRequest(req)
7100	checkResponseCode(t, http.StatusBadRequest, rr)
7101	assert.True(t, common.QuotaScans.AddUserQuotaScan(user.Username))
7102	req, _ = http.NewRequest(http.MethodPut, path.Join(quotasBasePath, "users", u.Username, "usage"), bytes.NewBuffer(userAsJSON))
7103	setBearerForReq(req, token)
7104	rr = executeRequest(req)
7105	checkResponseCode(t, http.StatusConflict, rr)
7106	assert.True(t, common.QuotaScans.RemoveUserQuotaScan(user.Username))
7107	req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
7108	setBearerForReq(req, token)
7109	rr = executeRequest(req)
7110	checkResponseCode(t, http.StatusOK, rr)
7111}
7112
7113func TestUserPermissionsMock(t *testing.T) {
7114	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7115	assert.NoError(t, err)
7116	user := getTestUser()
7117	user.Permissions = make(map[string][]string)
7118	user.Permissions["/somedir"] = []string{dataprovider.PermAny}
7119	userAsJSON := getUserAsJSON(t, user)
7120	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
7121	setBearerForReq(req, token)
7122	rr := executeRequest(req)
7123	checkResponseCode(t, http.StatusBadRequest, rr)
7124	user.Permissions = make(map[string][]string)
7125	user.Permissions["/"] = []string{dataprovider.PermAny}
7126	user.Permissions[".."] = []string{dataprovider.PermAny}
7127	userAsJSON = getUserAsJSON(t, user)
7128	req, _ = http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
7129	setBearerForReq(req, token)
7130	rr = executeRequest(req)
7131	checkResponseCode(t, http.StatusBadRequest, rr)
7132	user.Permissions = make(map[string][]string)
7133	user.Permissions["/"] = []string{dataprovider.PermAny}
7134	userAsJSON = getUserAsJSON(t, user)
7135	req, _ = http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
7136	setBearerForReq(req, token)
7137	rr = executeRequest(req)
7138	checkResponseCode(t, http.StatusCreated, rr)
7139	err = render.DecodeJSON(rr.Body, &user)
7140	assert.NoError(t, err)
7141	user.Permissions["/somedir"] = []string{"invalid"}
7142	userAsJSON = getUserAsJSON(t, user)
7143	req, _ = http.NewRequest(http.MethodPut, path.Join(userPath, user.Username), bytes.NewBuffer(userAsJSON))
7144	setBearerForReq(req, token)
7145	rr = executeRequest(req)
7146	checkResponseCode(t, http.StatusBadRequest, rr)
7147	delete(user.Permissions, "/somedir")
7148	user.Permissions["/somedir/.."] = []string{dataprovider.PermAny}
7149	userAsJSON = getUserAsJSON(t, user)
7150	req, _ = http.NewRequest(http.MethodPut, path.Join(userPath, user.Username), bytes.NewBuffer(userAsJSON))
7151	setBearerForReq(req, token)
7152	rr = executeRequest(req)
7153	checkResponseCode(t, http.StatusBadRequest, rr)
7154	delete(user.Permissions, "/somedir/..")
7155	user.Permissions["not_abs_path"] = []string{dataprovider.PermAny}
7156	userAsJSON = getUserAsJSON(t, user)
7157	req, _ = http.NewRequest(http.MethodPut, path.Join(userPath, user.Username), bytes.NewBuffer(userAsJSON))
7158	setBearerForReq(req, token)
7159	rr = executeRequest(req)
7160	checkResponseCode(t, http.StatusBadRequest, rr)
7161	delete(user.Permissions, "not_abs_path")
7162	user.Permissions["/somedir/../otherdir/"] = []string{dataprovider.PermListItems}
7163	userAsJSON = getUserAsJSON(t, user)
7164	req, _ = http.NewRequest(http.MethodPut, path.Join(userPath, user.Username), bytes.NewBuffer(userAsJSON))
7165	setBearerForReq(req, token)
7166	rr = executeRequest(req)
7167	checkResponseCode(t, http.StatusOK, rr)
7168	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
7169	setBearerForReq(req, token)
7170	rr = executeRequest(req)
7171	checkResponseCode(t, http.StatusOK, rr)
7172	var updatedUser dataprovider.User
7173	err = render.DecodeJSON(rr.Body, &updatedUser)
7174	assert.NoError(t, err)
7175	if val, ok := updatedUser.Permissions["/otherdir"]; ok {
7176		assert.True(t, util.IsStringInSlice(dataprovider.PermListItems, val))
7177		assert.Equal(t, 1, len(val))
7178	} else {
7179		assert.Fail(t, "expected dir not found in permissions")
7180	}
7181	req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
7182	setBearerForReq(req, token)
7183	rr = executeRequest(req)
7184	checkResponseCode(t, http.StatusOK, rr)
7185}
7186
7187func TestUpdateUserInvalidJsonMock(t *testing.T) {
7188	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7189	assert.NoError(t, err)
7190	user := getTestUser()
7191	userAsJSON := getUserAsJSON(t, user)
7192	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
7193	setBearerForReq(req, token)
7194	rr := executeRequest(req)
7195	checkResponseCode(t, http.StatusCreated, rr)
7196	err = render.DecodeJSON(rr.Body, &user)
7197	assert.NoError(t, err)
7198	req, _ = http.NewRequest(http.MethodPut, path.Join(userPath, user.Username), bytes.NewBuffer([]byte("Invalid json")))
7199	setBearerForReq(req, token)
7200	rr = executeRequest(req)
7201	checkResponseCode(t, http.StatusBadRequest, rr)
7202	req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
7203	setBearerForReq(req, token)
7204	rr = executeRequest(req)
7205	checkResponseCode(t, http.StatusOK, rr)
7206}
7207
7208func TestUpdateUserInvalidParamsMock(t *testing.T) {
7209	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7210	assert.NoError(t, err)
7211	user := getTestUser()
7212	userAsJSON := getUserAsJSON(t, user)
7213	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
7214	setBearerForReq(req, token)
7215	rr := executeRequest(req)
7216	checkResponseCode(t, http.StatusCreated, rr)
7217	err = render.DecodeJSON(rr.Body, &user)
7218	assert.NoError(t, err)
7219	user.HomeDir = ""
7220	userAsJSON = getUserAsJSON(t, user)
7221	req, _ = http.NewRequest(http.MethodPut, path.Join(userPath, user.Username), bytes.NewBuffer(userAsJSON))
7222	setBearerForReq(req, token)
7223	rr = executeRequest(req)
7224	checkResponseCode(t, http.StatusBadRequest, rr)
7225	userID := user.ID
7226	user.ID = 0
7227	user.CreatedAt = 0
7228	userAsJSON = getUserAsJSON(t, user)
7229	req, _ = http.NewRequest(http.MethodPut, path.Join(userPath, user.Username), bytes.NewBuffer(userAsJSON))
7230	setBearerForReq(req, token)
7231	rr = executeRequest(req)
7232	checkResponseCode(t, http.StatusBadRequest, rr)
7233	user.ID = userID
7234	req, _ = http.NewRequest(http.MethodPut, userPath+"/0", bytes.NewBuffer(userAsJSON))
7235	setBearerForReq(req, token)
7236	rr = executeRequest(req)
7237	checkResponseCode(t, http.StatusNotFound, rr)
7238	req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
7239	setBearerForReq(req, token)
7240	rr = executeRequest(req)
7241	checkResponseCode(t, http.StatusOK, rr)
7242}
7243
7244func TestGetAdminsMock(t *testing.T) {
7245	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7246	assert.NoError(t, err)
7247	admin := getTestAdmin()
7248	admin.Username = altAdminUsername
7249	asJSON, err := json.Marshal(admin)
7250	assert.NoError(t, err)
7251	req, _ := http.NewRequest(http.MethodPost, adminPath, bytes.NewBuffer(asJSON))
7252	setBearerForReq(req, token)
7253	rr := executeRequest(req)
7254	checkResponseCode(t, http.StatusCreated, rr)
7255
7256	req, _ = http.NewRequest(http.MethodGet, adminPath+"?limit=510&offset=0&order=ASC", nil)
7257	setBearerForReq(req, token)
7258	rr = executeRequest(req)
7259	checkResponseCode(t, http.StatusOK, rr)
7260	var admins []dataprovider.Admin
7261	err = render.DecodeJSON(rr.Body, &admins)
7262	assert.NoError(t, err)
7263	assert.GreaterOrEqual(t, len(admins), 1)
7264	firtAdmin := admins[0].Username
7265	req, _ = http.NewRequest(http.MethodGet, adminPath+"?limit=510&offset=0&order=DESC", nil)
7266	setBearerForReq(req, token)
7267	rr = executeRequest(req)
7268	checkResponseCode(t, http.StatusOK, rr)
7269	admins = nil
7270	err = render.DecodeJSON(rr.Body, &admins)
7271	assert.NoError(t, err)
7272	assert.GreaterOrEqual(t, len(admins), 1)
7273	assert.NotEqual(t, firtAdmin, admins[0].Username)
7274
7275	req, _ = http.NewRequest(http.MethodGet, adminPath+"?limit=510&offset=1&order=ASC", nil)
7276	setBearerForReq(req, token)
7277	rr = executeRequest(req)
7278	checkResponseCode(t, http.StatusOK, rr)
7279	admins = nil
7280	err = render.DecodeJSON(rr.Body, &admins)
7281	assert.NoError(t, err)
7282	assert.GreaterOrEqual(t, len(admins), 1)
7283	assert.NotEqual(t, firtAdmin, admins[0].Username)
7284
7285	req, _ = http.NewRequest(http.MethodGet, adminPath+"?limit=a&offset=0&order=ASC", nil)
7286	setBearerForReq(req, token)
7287	rr = executeRequest(req)
7288	checkResponseCode(t, http.StatusBadRequest, rr)
7289	req, _ = http.NewRequest(http.MethodGet, adminPath+"?limit=1&offset=a&order=ASC", nil)
7290	setBearerForReq(req, token)
7291	rr = executeRequest(req)
7292	checkResponseCode(t, http.StatusBadRequest, rr)
7293	req, _ = http.NewRequest(http.MethodGet, adminPath+"?limit=1&offset=0&order=ASCa", nil)
7294	setBearerForReq(req, token)
7295	rr = executeRequest(req)
7296	checkResponseCode(t, http.StatusBadRequest, rr)
7297
7298	req, _ = http.NewRequest(http.MethodDelete, path.Join(adminPath, admin.Username), nil)
7299	setBearerForReq(req, token)
7300	rr = executeRequest(req)
7301	checkResponseCode(t, http.StatusOK, rr)
7302}
7303
7304func TestGetUsersMock(t *testing.T) {
7305	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7306	assert.NoError(t, err)
7307	user := getTestUser()
7308	userAsJSON := getUserAsJSON(t, user)
7309	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
7310	setBearerForReq(req, token)
7311	rr := executeRequest(req)
7312	checkResponseCode(t, http.StatusCreated, rr)
7313	err = render.DecodeJSON(rr.Body, &user)
7314	assert.NoError(t, err)
7315	req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=510&offset=0&order=ASC", nil)
7316	setBearerForReq(req, token)
7317	rr = executeRequest(req)
7318	checkResponseCode(t, http.StatusOK, rr)
7319	var users []dataprovider.User
7320	err = render.DecodeJSON(rr.Body, &users)
7321	assert.NoError(t, err)
7322	assert.GreaterOrEqual(t, len(users), 1)
7323	req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=a&offset=0&order=ASC", nil)
7324	setBearerForReq(req, token)
7325	rr = executeRequest(req)
7326	checkResponseCode(t, http.StatusBadRequest, rr)
7327	req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=1&offset=a&order=ASC", nil)
7328	setBearerForReq(req, token)
7329	rr = executeRequest(req)
7330	checkResponseCode(t, http.StatusBadRequest, rr)
7331	req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=1&offset=0&order=ASCa", nil)
7332	setBearerForReq(req, token)
7333	rr = executeRequest(req)
7334	checkResponseCode(t, http.StatusBadRequest, rr)
7335
7336	req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
7337	setBearerForReq(req, token)
7338	rr = executeRequest(req)
7339	checkResponseCode(t, http.StatusOK, rr)
7340}
7341
7342func TestDeleteUserInvalidParamsMock(t *testing.T) {
7343	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7344	assert.NoError(t, err)
7345	req, _ := http.NewRequest(http.MethodDelete, userPath+"/0", nil)
7346	setBearerForReq(req, token)
7347	rr := executeRequest(req)
7348	checkResponseCode(t, http.StatusNotFound, rr)
7349}
7350
7351func TestGetQuotaScansMock(t *testing.T) {
7352	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7353	assert.NoError(t, err)
7354	req, err := http.NewRequest(http.MethodGet, quotaScanPath, nil)
7355	assert.NoError(t, err)
7356	setBearerForReq(req, token)
7357	rr := executeRequest(req)
7358	checkResponseCode(t, http.StatusOK, rr)
7359}
7360
7361func TestStartQuotaScanMock(t *testing.T) {
7362	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7363	assert.NoError(t, err)
7364	user := getTestUser()
7365	userAsJSON := getUserAsJSON(t, user)
7366	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
7367	setBearerForReq(req, token)
7368	rr := executeRequest(req)
7369	checkResponseCode(t, http.StatusCreated, rr)
7370	err = render.DecodeJSON(rr.Body, &user)
7371	assert.NoError(t, err)
7372	_, err = os.Stat(user.HomeDir)
7373	if err == nil {
7374		err = os.Remove(user.HomeDir)
7375		assert.NoError(t, err)
7376	}
7377	// simulate a duplicate quota scan
7378	common.QuotaScans.AddUserQuotaScan(user.Username)
7379	req, _ = http.NewRequest(http.MethodPost, path.Join(quotasBasePath, "users", user.Username, "scan"), nil)
7380	setBearerForReq(req, token)
7381	rr = executeRequest(req)
7382	checkResponseCode(t, http.StatusConflict, rr)
7383	assert.True(t, common.QuotaScans.RemoveUserQuotaScan(user.Username))
7384
7385	req, _ = http.NewRequest(http.MethodPost, path.Join(quotasBasePath, "users", user.Username, "scan"), nil)
7386	setBearerForReq(req, token)
7387	rr = executeRequest(req)
7388	checkResponseCode(t, http.StatusAccepted, rr)
7389
7390	waitForUsersQuotaScan(t, token)
7391
7392	_, err = os.Stat(user.HomeDir)
7393	if err != nil && os.IsNotExist(err) {
7394		err = os.MkdirAll(user.HomeDir, os.ModePerm)
7395		assert.NoError(t, err)
7396	}
7397	req, _ = http.NewRequest(http.MethodPost, path.Join(quotasBasePath, "users", user.Username, "scan"), nil)
7398	setBearerForReq(req, token)
7399	rr = executeRequest(req)
7400	checkResponseCode(t, http.StatusAccepted, rr)
7401
7402	waitForUsersQuotaScan(t, token)
7403
7404	req, _ = http.NewRequest(http.MethodPost, path.Join(quotasBasePath, "users", user.Username, "scan"), nil)
7405	setBearerForReq(req, token)
7406	rr = executeRequest(req)
7407	checkResponseCode(t, http.StatusAccepted, rr)
7408
7409	waitForUsersQuotaScan(t, token)
7410
7411	asJSON, err := json.Marshal(user)
7412	assert.NoError(t, err)
7413	req, _ = http.NewRequest(http.MethodPost, quotaScanCompatPath, bytes.NewBuffer(asJSON))
7414	setBearerForReq(req, token)
7415	rr = executeRequest(req)
7416	checkResponseCode(t, http.StatusAccepted, rr)
7417
7418	waitForUsersQuotaScan(t, token)
7419
7420	req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
7421	setBearerForReq(req, token)
7422	rr = executeRequest(req)
7423	checkResponseCode(t, http.StatusOK, rr)
7424	err = os.RemoveAll(user.GetHomeDir())
7425	assert.NoError(t, err)
7426}
7427
7428func TestUpdateFolderQuotaUsageMock(t *testing.T) {
7429	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7430	assert.NoError(t, err)
7431	mappedPath := filepath.Join(os.TempDir(), "vfolder")
7432	folderName := filepath.Base(mappedPath)
7433	f := vfs.BaseVirtualFolder{
7434		MappedPath: mappedPath,
7435		Name:       folderName,
7436	}
7437	usedQuotaFiles := 1
7438	usedQuotaSize := int64(65535)
7439	f.UsedQuotaFiles = usedQuotaFiles
7440	f.UsedQuotaSize = usedQuotaSize
7441	var folder vfs.BaseVirtualFolder
7442	folderAsJSON, err := json.Marshal(f)
7443	assert.NoError(t, err)
7444	req, _ := http.NewRequest(http.MethodPost, folderPath, bytes.NewBuffer(folderAsJSON))
7445	setBearerForReq(req, token)
7446	rr := executeRequest(req)
7447	checkResponseCode(t, http.StatusCreated, rr)
7448	err = render.DecodeJSON(rr.Body, &folder)
7449	assert.NoError(t, err)
7450	req, _ = http.NewRequest(http.MethodPut, path.Join(quotasBasePath, "folders", folder.Name, "usage"), bytes.NewBuffer(folderAsJSON))
7451	setBearerForReq(req, token)
7452	rr = executeRequest(req)
7453	checkResponseCode(t, http.StatusOK, rr)
7454	req, _ = http.NewRequest(http.MethodPut, updateFolderUsedQuotaCompatPath, bytes.NewBuffer(folderAsJSON))
7455	setBearerForReq(req, token)
7456	rr = executeRequest(req)
7457	checkResponseCode(t, http.StatusOK, rr)
7458	var folderGet vfs.BaseVirtualFolder
7459	req, _ = http.NewRequest(http.MethodGet, path.Join(folderPath, folderName), nil)
7460	setBearerForReq(req, token)
7461	rr = executeRequest(req)
7462	checkResponseCode(t, http.StatusOK, rr)
7463	err = render.DecodeJSON(rr.Body, &folderGet)
7464	assert.NoError(t, err)
7465	assert.Equal(t, usedQuotaFiles, folderGet.UsedQuotaFiles)
7466	assert.Equal(t, usedQuotaSize, folderGet.UsedQuotaSize)
7467	// now update only quota size
7468	f.UsedQuotaFiles = 0
7469	folderAsJSON, err = json.Marshal(f)
7470	assert.NoError(t, err)
7471	req, _ = http.NewRequest(http.MethodPut, path.Join(quotasBasePath, "folders", folder.Name, "usage")+"?mode=add",
7472		bytes.NewBuffer(folderAsJSON))
7473	setBearerForReq(req, token)
7474	rr = executeRequest(req)
7475	checkResponseCode(t, http.StatusOK, rr)
7476	folderGet = vfs.BaseVirtualFolder{}
7477	req, _ = http.NewRequest(http.MethodGet, path.Join(folderPath, folderName), nil)
7478	setBearerForReq(req, token)
7479	rr = executeRequest(req)
7480	checkResponseCode(t, http.StatusOK, rr)
7481	err = render.DecodeJSON(rr.Body, &folderGet)
7482	assert.NoError(t, err)
7483	assert.Equal(t, usedQuotaFiles, folderGet.UsedQuotaFiles)
7484	assert.Equal(t, usedQuotaSize*2, folderGet.UsedQuotaSize)
7485	// now update only quota files
7486	f.UsedQuotaSize = 0
7487	f.UsedQuotaFiles = 1
7488	folderAsJSON, err = json.Marshal(f)
7489	assert.NoError(t, err)
7490	req, _ = http.NewRequest(http.MethodPut, path.Join(quotasBasePath, "folders", folder.Name, "usage")+"?mode=add",
7491		bytes.NewBuffer(folderAsJSON))
7492	setBearerForReq(req, token)
7493	rr = executeRequest(req)
7494	checkResponseCode(t, http.StatusOK, rr)
7495	folderGet = vfs.BaseVirtualFolder{}
7496	req, _ = http.NewRequest(http.MethodGet, path.Join(folderPath, folderName), nil)
7497	setBearerForReq(req, token)
7498	rr = executeRequest(req)
7499	checkResponseCode(t, http.StatusOK, rr)
7500	err = render.DecodeJSON(rr.Body, &folderGet)
7501	assert.NoError(t, err)
7502	assert.Equal(t, usedQuotaFiles*2, folderGet.UsedQuotaFiles)
7503	assert.Equal(t, usedQuotaSize*2, folderGet.UsedQuotaSize)
7504	req, _ = http.NewRequest(http.MethodPut, updateFolderUsedQuotaCompatPath, bytes.NewBuffer([]byte("string")))
7505	setBearerForReq(req, token)
7506	rr = executeRequest(req)
7507	checkResponseCode(t, http.StatusBadRequest, rr)
7508	req, _ = http.NewRequest(http.MethodPut, path.Join(quotasBasePath, "folders", folder.Name, "usage"),
7509		bytes.NewBuffer([]byte("not a json")))
7510	setBearerForReq(req, token)
7511	rr = executeRequest(req)
7512	checkResponseCode(t, http.StatusBadRequest, rr)
7513
7514	assert.True(t, common.QuotaScans.AddVFolderQuotaScan(folderName))
7515	req, _ = http.NewRequest(http.MethodPut, path.Join(quotasBasePath, "folders", folder.Name, "usage"),
7516		bytes.NewBuffer(folderAsJSON))
7517	setBearerForReq(req, token)
7518	rr = executeRequest(req)
7519	checkResponseCode(t, http.StatusConflict, rr)
7520	assert.True(t, common.QuotaScans.RemoveVFolderQuotaScan(folderName))
7521
7522	req, _ = http.NewRequest(http.MethodDelete, path.Join(folderPath, folderName), nil)
7523	setBearerForReq(req, token)
7524	rr = executeRequest(req)
7525	checkResponseCode(t, http.StatusOK, rr)
7526}
7527
7528func TestStartFolderQuotaScanMock(t *testing.T) {
7529	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7530	assert.NoError(t, err)
7531	mappedPath := filepath.Join(os.TempDir(), "vfolder")
7532	folderName := filepath.Base(mappedPath)
7533	folder := vfs.BaseVirtualFolder{
7534		Name:       folderName,
7535		MappedPath: mappedPath,
7536	}
7537	folderAsJSON, err := json.Marshal(folder)
7538	assert.NoError(t, err)
7539	req, _ := http.NewRequest(http.MethodPost, folderPath, bytes.NewBuffer(folderAsJSON))
7540	setBearerForReq(req, token)
7541	rr := executeRequest(req)
7542	checkResponseCode(t, http.StatusCreated, rr)
7543	_, err = os.Stat(mappedPath)
7544	if err == nil {
7545		err = os.Remove(mappedPath)
7546		assert.NoError(t, err)
7547	}
7548	// simulate a duplicate quota scan
7549	common.QuotaScans.AddVFolderQuotaScan(folderName)
7550	req, _ = http.NewRequest(http.MethodPost, path.Join(quotasBasePath, "folders", folder.Name, "scan"), nil)
7551	setBearerForReq(req, token)
7552	rr = executeRequest(req)
7553	checkResponseCode(t, http.StatusConflict, rr)
7554	assert.True(t, common.QuotaScans.RemoveVFolderQuotaScan(folderName))
7555	// and now a real quota scan
7556	_, err = os.Stat(mappedPath)
7557	if err != nil && os.IsNotExist(err) {
7558		err = os.MkdirAll(mappedPath, os.ModePerm)
7559		assert.NoError(t, err)
7560	}
7561	req, _ = http.NewRequest(http.MethodPost, path.Join(quotasBasePath, "folders", folder.Name, "scan"), nil)
7562	setBearerForReq(req, token)
7563	rr = executeRequest(req)
7564	checkResponseCode(t, http.StatusAccepted, rr)
7565	waitForFoldersQuotaScanPath(t, token)
7566
7567	asJSON, err := json.Marshal(folder)
7568	assert.NoError(t, err)
7569	req, _ = http.NewRequest(http.MethodPost, quotaScanVFolderCompatPath, bytes.NewBuffer(asJSON))
7570	setBearerForReq(req, token)
7571	rr = executeRequest(req)
7572	checkResponseCode(t, http.StatusAccepted, rr)
7573	waitForFoldersQuotaScanPath(t, token)
7574
7575	// cleanup
7576
7577	req, _ = http.NewRequest(http.MethodDelete, path.Join(folderPath, folderName), nil)
7578	setBearerForReq(req, token)
7579	rr = executeRequest(req)
7580	checkResponseCode(t, http.StatusOK, rr)
7581	err = os.RemoveAll(folderPath)
7582	assert.NoError(t, err)
7583	err = os.RemoveAll(mappedPath)
7584	assert.NoError(t, err)
7585}
7586
7587func TestStartQuotaScanNonExistentUserMock(t *testing.T) {
7588	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7589	assert.NoError(t, err)
7590	user := getTestUser()
7591
7592	req, _ := http.NewRequest(http.MethodPost, path.Join(quotasBasePath, "users", user.Username, "scan"), nil)
7593	setBearerForReq(req, token)
7594	rr := executeRequest(req)
7595	checkResponseCode(t, http.StatusNotFound, rr)
7596}
7597
7598func TestStartQuotaScanBadUserMock(t *testing.T) {
7599	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7600	assert.NoError(t, err)
7601	req, _ := http.NewRequest(http.MethodPost, quotaScanCompatPath, bytes.NewBuffer([]byte("invalid json")))
7602	setBearerForReq(req, token)
7603	rr := executeRequest(req)
7604	checkResponseCode(t, http.StatusBadRequest, rr)
7605}
7606
7607func TestStartQuotaScanBadFolderMock(t *testing.T) {
7608	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7609	assert.NoError(t, err)
7610	req, _ := http.NewRequest(http.MethodPost, quotaScanVFolderCompatPath, bytes.NewBuffer([]byte("invalid json")))
7611	setBearerForReq(req, token)
7612	rr := executeRequest(req)
7613	checkResponseCode(t, http.StatusBadRequest, rr)
7614}
7615
7616func TestStartQuotaScanNonExistentFolderMock(t *testing.T) {
7617	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7618	assert.NoError(t, err)
7619	folder := vfs.BaseVirtualFolder{
7620		MappedPath: os.TempDir(),
7621		Name:       "afolder",
7622	}
7623	req, _ := http.NewRequest(http.MethodPost, path.Join(quotasBasePath, "folders", folder.Name, "scan"), nil)
7624	setBearerForReq(req, token)
7625	rr := executeRequest(req)
7626	checkResponseCode(t, http.StatusNotFound, rr)
7627}
7628
7629func TestGetFoldersMock(t *testing.T) {
7630	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7631	assert.NoError(t, err)
7632	mappedPath := filepath.Join(os.TempDir(), "vfolder")
7633	folderName := filepath.Base(mappedPath)
7634	folder := vfs.BaseVirtualFolder{
7635		Name:       folderName,
7636		MappedPath: mappedPath,
7637	}
7638	folderAsJSON, err := json.Marshal(folder)
7639	assert.NoError(t, err)
7640	req, _ := http.NewRequest(http.MethodPost, folderPath, bytes.NewBuffer(folderAsJSON))
7641	setBearerForReq(req, token)
7642	rr := executeRequest(req)
7643	checkResponseCode(t, http.StatusCreated, rr)
7644	err = render.DecodeJSON(rr.Body, &folder)
7645	assert.NoError(t, err)
7646
7647	var folders []vfs.BaseVirtualFolder
7648	url, err := url.Parse(folderPath + "?limit=510&offset=0&order=DESC")
7649	assert.NoError(t, err)
7650	req, _ = http.NewRequest(http.MethodGet, url.String(), nil)
7651	setBearerForReq(req, token)
7652	rr = executeRequest(req)
7653	checkResponseCode(t, http.StatusOK, rr)
7654	err = render.DecodeJSON(rr.Body, &folders)
7655	assert.NoError(t, err)
7656	assert.GreaterOrEqual(t, len(folders), 1)
7657	req, _ = http.NewRequest(http.MethodGet, folderPath+"?limit=a&offset=0&order=ASC", nil)
7658	setBearerForReq(req, token)
7659	rr = executeRequest(req)
7660	checkResponseCode(t, http.StatusBadRequest, rr)
7661	req, _ = http.NewRequest(http.MethodGet, folderPath+"?limit=1&offset=a&order=ASC", nil)
7662	setBearerForReq(req, token)
7663	rr = executeRequest(req)
7664	checkResponseCode(t, http.StatusBadRequest, rr)
7665	req, _ = http.NewRequest(http.MethodGet, folderPath+"?limit=1&offset=0&order=ASCa", nil)
7666	setBearerForReq(req, token)
7667	rr = executeRequest(req)
7668	checkResponseCode(t, http.StatusBadRequest, rr)
7669
7670	req, _ = http.NewRequest(http.MethodDelete, path.Join(folderPath, folderName), nil)
7671	setBearerForReq(req, token)
7672	rr = executeRequest(req)
7673	checkResponseCode(t, http.StatusOK, rr)
7674}
7675
7676func TestGetVersionMock(t *testing.T) {
7677	req, _ := http.NewRequest(http.MethodGet, versionPath, nil)
7678	rr := executeRequest(req)
7679	checkResponseCode(t, http.StatusUnauthorized, rr)
7680	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7681	assert.NoError(t, err)
7682	req, _ = http.NewRequest(http.MethodGet, versionPath, nil)
7683	setBearerForReq(req, token)
7684	rr = executeRequest(req)
7685	checkResponseCode(t, http.StatusOK, rr)
7686	req, _ = http.NewRequest(http.MethodGet, versionPath, nil)
7687	setBearerForReq(req, "abcde")
7688	rr = executeRequest(req)
7689	checkResponseCode(t, http.StatusUnauthorized, rr)
7690}
7691
7692func TestGetConnectionsMock(t *testing.T) {
7693	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7694	assert.NoError(t, err)
7695	req, _ := http.NewRequest(http.MethodGet, activeConnectionsPath, nil)
7696	setBearerForReq(req, token)
7697	rr := executeRequest(req)
7698	checkResponseCode(t, http.StatusOK, rr)
7699}
7700
7701func TestGetStatusMock(t *testing.T) {
7702	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7703	assert.NoError(t, err)
7704	req, _ := http.NewRequest(http.MethodGet, serverStatusPath, nil)
7705	setBearerForReq(req, token)
7706	rr := executeRequest(req)
7707	checkResponseCode(t, http.StatusOK, rr)
7708}
7709
7710func TestDeleteActiveConnectionMock(t *testing.T) {
7711	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7712	assert.NoError(t, err)
7713	req, _ := http.NewRequest(http.MethodDelete, activeConnectionsPath+"/connectionID", nil)
7714	setBearerForReq(req, token)
7715	rr := executeRequest(req)
7716	checkResponseCode(t, http.StatusNotFound, rr)
7717}
7718
7719func TestNotFoundMock(t *testing.T) {
7720	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7721	assert.NoError(t, err)
7722	req, _ := http.NewRequest(http.MethodGet, "/non/existing/path", nil)
7723	setBearerForReq(req, token)
7724	rr := executeRequest(req)
7725	checkResponseCode(t, http.StatusNotFound, rr)
7726}
7727
7728func TestMethodNotAllowedMock(t *testing.T) {
7729	req, _ := http.NewRequest(http.MethodPost, activeConnectionsPath, nil)
7730	rr := executeRequest(req)
7731	checkResponseCode(t, http.StatusMethodNotAllowed, rr)
7732}
7733
7734func TestHealthCheck(t *testing.T) {
7735	req, _ := http.NewRequest(http.MethodGet, "/healthz", nil)
7736	rr := executeRequest(req)
7737	checkResponseCode(t, http.StatusOK, rr)
7738	assert.Equal(t, "ok", rr.Body.String())
7739}
7740
7741func TestGetWebRootMock(t *testing.T) {
7742	req, _ := http.NewRequest(http.MethodGet, "/", nil)
7743	rr := executeRequest(req)
7744	checkResponseCode(t, http.StatusMovedPermanently, rr)
7745	assert.Equal(t, webClientLoginPath, rr.Header().Get("Location"))
7746	req, _ = http.NewRequest(http.MethodGet, webBasePath, nil)
7747	rr = executeRequest(req)
7748	checkResponseCode(t, http.StatusMovedPermanently, rr)
7749	assert.Equal(t, webClientLoginPath, rr.Header().Get("Location"))
7750	req, _ = http.NewRequest(http.MethodGet, webBasePathAdmin, nil)
7751	rr = executeRequest(req)
7752	checkResponseCode(t, http.StatusMovedPermanently, rr)
7753	assert.Equal(t, webLoginPath, rr.Header().Get("Location"))
7754	req, _ = http.NewRequest(http.MethodGet, webBasePathClient, nil)
7755	rr = executeRequest(req)
7756	checkResponseCode(t, http.StatusMovedPermanently, rr)
7757	assert.Equal(t, webClientLoginPath, rr.Header().Get("Location"))
7758}
7759
7760func TestWebNotFoundURI(t *testing.T) {
7761	urlString := httpBaseURL + webBasePath + "/a"
7762	req, err := http.NewRequest(http.MethodGet, urlString, nil)
7763	assert.NoError(t, err)
7764	resp, err := httpclient.GetHTTPClient().Do(req)
7765	require.NoError(t, err)
7766	defer resp.Body.Close()
7767	assert.Equal(t, http.StatusNotFound, resp.StatusCode)
7768
7769	req, err = http.NewRequest(http.MethodGet, urlString, nil)
7770	assert.NoError(t, err)
7771	setJWTCookieForReq(req, "invalid token")
7772	resp, err = httpclient.GetHTTPClient().Do(req)
7773	require.NoError(t, err)
7774	defer resp.Body.Close()
7775	assert.Equal(t, http.StatusNotFound, resp.StatusCode)
7776
7777	urlString = httpBaseURL + webBasePathClient + "/a"
7778	req, err = http.NewRequest(http.MethodGet, urlString, nil)
7779	assert.NoError(t, err)
7780	resp, err = httpclient.GetHTTPClient().Do(req)
7781	require.NoError(t, err)
7782	defer resp.Body.Close()
7783	assert.Equal(t, http.StatusNotFound, resp.StatusCode)
7784
7785	req, err = http.NewRequest(http.MethodGet, urlString, nil)
7786	assert.NoError(t, err)
7787	setJWTCookieForReq(req, "invalid client token")
7788	resp, err = httpclient.GetHTTPClient().Do(req)
7789	require.NoError(t, err)
7790	defer resp.Body.Close()
7791	assert.Equal(t, http.StatusNotFound, resp.StatusCode)
7792}
7793
7794func TestLogout(t *testing.T) {
7795	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7796	assert.NoError(t, err)
7797	req, _ := http.NewRequest(http.MethodGet, serverStatusPath, nil)
7798	setBearerForReq(req, token)
7799	rr := executeRequest(req)
7800	checkResponseCode(t, http.StatusOK, rr)
7801
7802	req, _ = http.NewRequest(http.MethodGet, logoutPath, nil)
7803	setBearerForReq(req, token)
7804	rr = executeRequest(req)
7805	checkResponseCode(t, http.StatusOK, rr)
7806
7807	req, _ = http.NewRequest(http.MethodGet, serverStatusPath, nil)
7808	setBearerForReq(req, token)
7809	rr = executeRequest(req)
7810	checkResponseCode(t, http.StatusUnauthorized, rr)
7811	assert.Contains(t, rr.Body.String(), "Your token is no longer valid")
7812}
7813
7814func TestDefenderAPIInvalidIDMock(t *testing.T) {
7815	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7816	assert.NoError(t, err)
7817	req, _ := http.NewRequest(http.MethodGet, path.Join(defenderHosts, "abc"), nil) // not hex id
7818	setBearerForReq(req, token)
7819	rr := executeRequest(req)
7820	checkResponseCode(t, http.StatusBadRequest, rr)
7821	assert.Contains(t, rr.Body.String(), "invalid host id")
7822}
7823
7824func TestTokenHeaderCookie(t *testing.T) {
7825	apiToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7826	assert.NoError(t, err)
7827	webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7828	assert.NoError(t, err)
7829
7830	req, _ := http.NewRequest(http.MethodGet, serverStatusPath, nil)
7831	setJWTCookieForReq(req, apiToken)
7832	rr := executeRequest(req)
7833	checkResponseCode(t, http.StatusUnauthorized, rr)
7834	assert.Contains(t, rr.Body.String(), "no token found")
7835
7836	req, _ = http.NewRequest(http.MethodGet, serverStatusPath, nil)
7837	setBearerForReq(req, apiToken)
7838	rr = executeRequest(req)
7839	checkResponseCode(t, http.StatusOK, rr)
7840
7841	req, _ = http.NewRequest(http.MethodGet, webStatusPath, nil)
7842	setBearerForReq(req, webToken)
7843	rr = executeRequest(req)
7844	checkResponseCode(t, http.StatusFound, rr)
7845	assert.Equal(t, webLoginPath, rr.Header().Get("Location"))
7846
7847	req, _ = http.NewRequest(http.MethodGet, webStatusPath, nil)
7848	setJWTCookieForReq(req, webToken)
7849	rr = executeRequest(req)
7850	checkResponseCode(t, http.StatusOK, rr)
7851}
7852
7853func TestTokenAudience(t *testing.T) {
7854	webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7855	assert.NoError(t, err)
7856	apiToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
7857	assert.NoError(t, err)
7858
7859	req, _ := http.NewRequest(http.MethodGet, serverStatusPath, nil)
7860	setBearerForReq(req, apiToken)
7861	rr := executeRequest(req)
7862	checkResponseCode(t, http.StatusOK, rr)
7863
7864	req, _ = http.NewRequest(http.MethodGet, serverStatusPath, nil)
7865	setBearerForReq(req, webToken)
7866	rr = executeRequest(req)
7867	checkResponseCode(t, http.StatusUnauthorized, rr)
7868	assert.Contains(t, rr.Body.String(), "Your token audience is not valid")
7869
7870	req, _ = http.NewRequest(http.MethodGet, webStatusPath, nil)
7871	setJWTCookieForReq(req, webToken)
7872	rr = executeRequest(req)
7873	checkResponseCode(t, http.StatusOK, rr)
7874
7875	req, _ = http.NewRequest(http.MethodGet, webStatusPath, nil)
7876	setJWTCookieForReq(req, apiToken)
7877	rr = executeRequest(req)
7878	checkResponseCode(t, http.StatusFound, rr)
7879	assert.Equal(t, webLoginPath, rr.Header().Get("Location"))
7880}
7881
7882func TestWebAPILoginMock(t *testing.T) {
7883	_, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
7884	assert.Error(t, err)
7885	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
7886	assert.NoError(t, err)
7887	_, err = getJWTAPIUserTokenFromTestServer(defaultUsername+"1", defaultPassword)
7888	assert.Error(t, err)
7889	_, err = getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword+"1")
7890	assert.Error(t, err)
7891	apiToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
7892	assert.NoError(t, err)
7893	webToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
7894	assert.NoError(t, err)
7895	// a web token is not valid for API usage
7896	req, err := http.NewRequest(http.MethodGet, userDirsPath, nil)
7897	assert.NoError(t, err)
7898	setBearerForReq(req, webToken)
7899	rr := executeRequest(req)
7900	checkResponseCode(t, http.StatusUnauthorized, rr)
7901	assert.Contains(t, rr.Body.String(), "Your token audience is not valid")
7902
7903	req, err = http.NewRequest(http.MethodGet, userDirsPath+"/?path=%2F", nil)
7904	assert.NoError(t, err)
7905	setBearerForReq(req, apiToken)
7906	rr = executeRequest(req)
7907	checkResponseCode(t, http.StatusOK, rr)
7908	// API token is not valid for web usage
7909	req, _ = http.NewRequest(http.MethodGet, webClientProfilePath, nil)
7910	setJWTCookieForReq(req, apiToken)
7911	rr = executeRequest(req)
7912	checkResponseCode(t, http.StatusFound, rr)
7913	assert.Equal(t, webClientLoginPath, rr.Header().Get("Location"))
7914
7915	req, _ = http.NewRequest(http.MethodGet, webClientProfilePath, nil)
7916	setJWTCookieForReq(req, webToken)
7917	rr = executeRequest(req)
7918	checkResponseCode(t, http.StatusOK, rr)
7919
7920	_, err = httpdtest.RemoveUser(user, http.StatusOK)
7921	assert.NoError(t, err)
7922	err = os.RemoveAll(user.GetHomeDir())
7923	assert.NoError(t, err)
7924}
7925
7926func TestWebClientLoginMock(t *testing.T) {
7927	_, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
7928	assert.Error(t, err)
7929	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
7930	assert.NoError(t, err)
7931	webToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
7932	assert.NoError(t, err)
7933	// a web token is not valid for API or WebAdmin usage
7934	req, _ := http.NewRequest(http.MethodGet, serverStatusPath, nil)
7935	setBearerForReq(req, webToken)
7936	rr := executeRequest(req)
7937	checkResponseCode(t, http.StatusUnauthorized, rr)
7938	assert.Contains(t, rr.Body.String(), "Your token audience is not valid")
7939	req, _ = http.NewRequest(http.MethodGet, webStatusPath, nil)
7940	setJWTCookieForReq(req, webToken)
7941	rr = executeRequest(req)
7942	checkResponseCode(t, http.StatusFound, rr)
7943	assert.Equal(t, webLoginPath, rr.Header().Get("Location"))
7944	// bearer should not work
7945	req, _ = http.NewRequest(http.MethodGet, webClientProfilePath, nil)
7946	setBearerForReq(req, webToken)
7947	rr = executeRequest(req)
7948	checkResponseCode(t, http.StatusFound, rr)
7949	assert.Equal(t, webClientLoginPath, rr.Header().Get("Location"))
7950	// now try to render client pages
7951	req, _ = http.NewRequest(http.MethodGet, webClientProfilePath, nil)
7952	setJWTCookieForReq(req, webToken)
7953	rr = executeRequest(req)
7954	checkResponseCode(t, http.StatusOK, rr)
7955	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath, nil)
7956	setJWTCookieForReq(req, webToken)
7957	rr = executeRequest(req)
7958	checkResponseCode(t, http.StatusOK, rr)
7959	// now logout
7960	req, _ = http.NewRequest(http.MethodGet, webClientLogoutPath, nil)
7961	setJWTCookieForReq(req, webToken)
7962	rr = executeRequest(req)
7963	checkResponseCode(t, http.StatusFound, rr)
7964	assert.Equal(t, webClientLoginPath, rr.Header().Get("Location"))
7965	req, _ = http.NewRequest(http.MethodGet, webClientProfilePath, nil)
7966	setJWTCookieForReq(req, webToken)
7967	rr = executeRequest(req)
7968	checkResponseCode(t, http.StatusFound, rr)
7969	assert.Equal(t, webClientLoginPath, rr.Header().Get("Location"))
7970
7971	// get a new token and use it after removing the user
7972	webToken, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
7973	assert.NoError(t, err)
7974	apiUserToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
7975	assert.NoError(t, err)
7976	_, err = httpdtest.RemoveUser(user, http.StatusOK)
7977	assert.NoError(t, err)
7978	err = os.RemoveAll(user.GetHomeDir())
7979	assert.NoError(t, err)
7980
7981	req, _ = http.NewRequest(http.MethodGet, webClientProfilePath, nil)
7982	setJWTCookieForReq(req, webToken)
7983	rr = executeRequest(req)
7984	checkResponseCode(t, http.StatusInternalServerError, rr)
7985	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath, nil)
7986	setJWTCookieForReq(req, webToken)
7987	rr = executeRequest(req)
7988	checkResponseCode(t, http.StatusNotFound, rr)
7989	assert.Contains(t, rr.Body.String(), "Unable to retrieve your user")
7990
7991	req, _ = http.NewRequest(http.MethodGet, webClientDirsPath, nil)
7992	setJWTCookieForReq(req, webToken)
7993	rr = executeRequest(req)
7994	checkResponseCode(t, http.StatusNotFound, rr)
7995	assert.Contains(t, rr.Body.String(), "Unable to retrieve your user")
7996
7997	req, _ = http.NewRequest(http.MethodGet, webClientDownloadZipPath, nil)
7998	setJWTCookieForReq(req, webToken)
7999	rr = executeRequest(req)
8000	checkResponseCode(t, http.StatusNotFound, rr)
8001	assert.Contains(t, rr.Body.String(), "Unable to retrieve your user")
8002
8003	req, _ = http.NewRequest(http.MethodGet, userDirsPath, nil)
8004	setBearerForReq(req, apiUserToken)
8005	rr = executeRequest(req)
8006	checkResponseCode(t, http.StatusNotFound, rr)
8007	assert.Contains(t, rr.Body.String(), "Unable to retrieve your user")
8008
8009	req, _ = http.NewRequest(http.MethodGet, userFilesPath, nil)
8010	setBearerForReq(req, apiUserToken)
8011	rr = executeRequest(req)
8012	checkResponseCode(t, http.StatusNotFound, rr)
8013	assert.Contains(t, rr.Body.String(), "Unable to retrieve your user")
8014
8015	req, _ = http.NewRequest(http.MethodPost, userStreamZipPath, bytes.NewBuffer([]byte(`{}`)))
8016	setBearerForReq(req, apiUserToken)
8017	rr = executeRequest(req)
8018	checkResponseCode(t, http.StatusNotFound, rr)
8019	assert.Contains(t, rr.Body.String(), "Unable to retrieve your user")
8020
8021	req, _ = http.NewRequest(http.MethodGet, userPublicKeysPath, nil)
8022	setBearerForReq(req, apiUserToken)
8023	rr = executeRequest(req)
8024	checkResponseCode(t, http.StatusNotFound, rr)
8025	assert.Contains(t, rr.Body.String(), "Unable to retrieve your user")
8026
8027	req, _ = http.NewRequest(http.MethodPut, userPublicKeysPath, bytes.NewBuffer([]byte(`{}`)))
8028	setBearerForReq(req, apiUserToken)
8029	rr = executeRequest(req)
8030	checkResponseCode(t, http.StatusNotFound, rr)
8031	assert.Contains(t, rr.Body.String(), "Unable to retrieve your user")
8032
8033	csrfToken, err := getCSRFToken(httpBaseURL + webClientLoginPath)
8034	assert.NoError(t, err)
8035	form := make(url.Values)
8036	form.Set("public_keys", testPubKey)
8037	form.Set(csrfFormToken, csrfToken)
8038	req, _ = http.NewRequest(http.MethodPost, webClientProfilePath, bytes.NewBuffer([]byte(form.Encode())))
8039	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
8040	setJWTCookieForReq(req, webToken)
8041	rr = executeRequest(req)
8042	checkResponseCode(t, http.StatusInternalServerError, rr)
8043}
8044
8045func TestWebClientLoginErrorsMock(t *testing.T) {
8046	form := getLoginForm("", "", "")
8047	req, _ := http.NewRequest(http.MethodPost, webClientLoginPath, bytes.NewBuffer([]byte(form.Encode())))
8048	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
8049	rr := executeRequest(req)
8050	assert.Equal(t, http.StatusOK, rr.Code)
8051	assert.Contains(t, rr.Body.String(), "Invalid credentials")
8052
8053	form = getLoginForm(defaultUsername, defaultPassword, "")
8054	req, _ = http.NewRequest(http.MethodPost, webClientLoginPath, bytes.NewBuffer([]byte(form.Encode())))
8055	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
8056	rr = executeRequest(req)
8057	assert.Equal(t, http.StatusOK, rr.Code)
8058	assert.Contains(t, rr.Body.String(), "unable to verify form token")
8059}
8060
8061func TestWebClientMaxConnections(t *testing.T) {
8062	oldValue := common.Config.MaxTotalConnections
8063	common.Config.MaxTotalConnections = 1
8064
8065	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
8066	assert.NoError(t, err)
8067
8068	webToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
8069	assert.NoError(t, err)
8070	req, _ := http.NewRequest(http.MethodGet, webClientFilesPath, nil)
8071	setJWTCookieForReq(req, webToken)
8072	rr := executeRequest(req)
8073	checkResponseCode(t, http.StatusOK, rr)
8074
8075	// now add a fake connection
8076	fs := vfs.NewOsFs("id", os.TempDir(), "")
8077	connection := &httpd.Connection{
8078		BaseConnection: common.NewBaseConnection(fs.ConnectionID(), common.ProtocolHTTP, "", "", user),
8079	}
8080	common.Connections.Add(connection)
8081
8082	_, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
8083	assert.Error(t, err)
8084
8085	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath, nil)
8086	setJWTCookieForReq(req, webToken)
8087	rr = executeRequest(req)
8088	checkResponseCode(t, http.StatusForbidden, rr)
8089	assert.Contains(t, rr.Body.String(), "configured connections limit reached")
8090
8091	common.Connections.Remove(connection.GetID())
8092	_, err = httpdtest.RemoveUser(user, http.StatusOK)
8093	assert.NoError(t, err)
8094	err = os.RemoveAll(user.GetHomeDir())
8095	assert.NoError(t, err)
8096	assert.Len(t, common.Connections.GetStats(), 0)
8097
8098	common.Config.MaxTotalConnections = oldValue
8099}
8100
8101func TestDefender(t *testing.T) {
8102	oldConfig := config.GetCommonConfig()
8103
8104	cfg := config.GetCommonConfig()
8105	cfg.DefenderConfig.Enabled = true
8106	cfg.DefenderConfig.Threshold = 3
8107	cfg.DefenderConfig.ScoreLimitExceeded = 2
8108
8109	err := common.Initialize(cfg)
8110	assert.NoError(t, err)
8111
8112	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
8113	assert.NoError(t, err)
8114
8115	remoteAddr := "172.16.5.6:9876"
8116
8117	webAdminToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
8118	assert.NoError(t, err)
8119	webToken, err := getJWTWebClientTokenFromTestServerWithAddr(defaultUsername, defaultPassword, remoteAddr)
8120	assert.NoError(t, err)
8121
8122	req, _ := http.NewRequest(http.MethodGet, webClientFilesPath, nil)
8123	req.RemoteAddr = remoteAddr
8124	req.RequestURI = webClientFilesPath
8125	setJWTCookieForReq(req, webToken)
8126	rr := executeRequest(req)
8127	checkResponseCode(t, http.StatusOK, rr)
8128
8129	for i := 0; i < 3; i++ {
8130		_, err = getJWTWebClientTokenFromTestServerWithAddr(defaultUsername, "wrong pwd", remoteAddr)
8131		assert.Error(t, err)
8132	}
8133
8134	_, err = getJWTWebClientTokenFromTestServerWithAddr(defaultUsername, defaultPassword, remoteAddr)
8135	assert.Error(t, err)
8136	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath, nil)
8137	req.RemoteAddr = remoteAddr
8138	req.RequestURI = webClientFilesPath
8139	setJWTCookieForReq(req, webToken)
8140	rr = executeRequest(req)
8141	checkResponseCode(t, http.StatusForbidden, rr)
8142	assert.Contains(t, rr.Body.String(), "your IP address is banned")
8143
8144	req, _ = http.NewRequest(http.MethodGet, webUsersPath, nil)
8145	req.RemoteAddr = remoteAddr
8146	req.RequestURI = webUsersPath
8147	setJWTCookieForReq(req, webAdminToken)
8148	rr = executeRequest(req)
8149	checkResponseCode(t, http.StatusForbidden, rr)
8150	assert.Contains(t, rr.Body.String(), "your IP address is banned")
8151
8152	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath, nil)
8153	req.RemoteAddr = remoteAddr
8154	req.Header.Set("X-Real-IP", "127.0.0.1:2345")
8155	setJWTCookieForReq(req, webToken)
8156	rr = executeRequest(req)
8157	checkResponseCode(t, http.StatusForbidden, rr)
8158	assert.Contains(t, rr.Body.String(), "your IP address is banned")
8159
8160	_, err = httpdtest.RemoveUser(user, http.StatusOK)
8161	assert.NoError(t, err)
8162	err = os.RemoveAll(user.GetHomeDir())
8163	assert.NoError(t, err)
8164
8165	err = common.Initialize(oldConfig)
8166	assert.NoError(t, err)
8167}
8168
8169func TestPostConnectHook(t *testing.T) {
8170	if runtime.GOOS == osWindows {
8171		t.Skip("this test is not available on Windows")
8172	}
8173	common.Config.PostConnectHook = postConnectPath
8174
8175	u := getTestUser()
8176	u.Filters.AllowAPIKeyAuth = true
8177	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
8178	assert.NoError(t, err)
8179	apiKey, _, err := httpdtest.AddAPIKey(dataprovider.APIKey{
8180		Name:  "name",
8181		Scope: dataprovider.APIKeyScopeUser,
8182		User:  user.Username,
8183	}, http.StatusCreated)
8184	assert.NoError(t, err)
8185	err = os.WriteFile(postConnectPath, getExitCodeScriptContent(0), os.ModePerm)
8186	assert.NoError(t, err)
8187
8188	_, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
8189	assert.NoError(t, err)
8190
8191	_, err = getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
8192	assert.NoError(t, err)
8193
8194	req, err := http.NewRequest(http.MethodGet, userDirsPath, nil)
8195	assert.NoError(t, err)
8196	setAPIKeyForReq(req, apiKey.Key, "")
8197	rr := executeRequest(req)
8198	checkResponseCode(t, http.StatusOK, rr)
8199
8200	err = os.WriteFile(postConnectPath, getExitCodeScriptContent(1), os.ModePerm)
8201	assert.NoError(t, err)
8202
8203	_, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
8204	assert.Error(t, err)
8205
8206	_, err = getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
8207	assert.Error(t, err)
8208
8209	req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
8210	assert.NoError(t, err)
8211	setAPIKeyForReq(req, apiKey.Key, "")
8212	rr = executeRequest(req)
8213	checkResponseCode(t, http.StatusUnauthorized, rr)
8214
8215	_, err = httpdtest.RemoveUser(user, http.StatusOK)
8216	assert.NoError(t, err)
8217	err = os.RemoveAll(user.GetHomeDir())
8218	assert.NoError(t, err)
8219
8220	common.Config.PostConnectHook = ""
8221}
8222
8223func TestMaxSessions(t *testing.T) {
8224	u := getTestUser()
8225	u.MaxSessions = 1
8226	u.Email = "user@session.com"
8227	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
8228	assert.NoError(t, err)
8229	_, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
8230	assert.NoError(t, err)
8231	_, err = getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
8232	assert.NoError(t, err)
8233	// now add a fake connection
8234	fs := vfs.NewOsFs("id", os.TempDir(), "")
8235	connection := &httpd.Connection{
8236		BaseConnection: common.NewBaseConnection(fs.ConnectionID(), common.ProtocolHTTP, "", "", user),
8237	}
8238	common.Connections.Add(connection)
8239	_, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
8240	assert.Error(t, err)
8241	_, err = getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
8242	assert.Error(t, err)
8243	// test reset password
8244	smtpCfg := smtp.Config{
8245		Host:          "127.0.0.1",
8246		Port:          3525,
8247		TemplatesPath: "templates",
8248	}
8249	err = smtpCfg.Initialize("..")
8250	assert.NoError(t, err)
8251
8252	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
8253	assert.NoError(t, err)
8254	form := make(url.Values)
8255	form.Set(csrfFormToken, csrfToken)
8256	form.Set("username", user.Username)
8257	lastResetCode = ""
8258	req, err := http.NewRequest(http.MethodPost, webClientForgotPwdPath, bytes.NewBuffer([]byte(form.Encode())))
8259	assert.NoError(t, err)
8260	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
8261	rr := executeRequest(req)
8262	assert.Equal(t, http.StatusFound, rr.Code)
8263	assert.GreaterOrEqual(t, len(lastResetCode), 20)
8264	form = make(url.Values)
8265	form.Set(csrfFormToken, csrfToken)
8266	form.Set("password", defaultPassword)
8267	form.Set("code", lastResetCode)
8268	req, err = http.NewRequest(http.MethodPost, webClientResetPwdPath, bytes.NewBuffer([]byte(form.Encode())))
8269	assert.NoError(t, err)
8270	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
8271	rr = executeRequest(req)
8272	assert.Equal(t, http.StatusOK, rr.Code)
8273	assert.Contains(t, rr.Body.String(), "Password reset successfully but unable to login")
8274
8275	smtpCfg = smtp.Config{}
8276	err = smtpCfg.Initialize("..")
8277	require.NoError(t, err)
8278
8279	common.Connections.Remove(connection.GetID())
8280	_, err = httpdtest.RemoveUser(user, http.StatusOK)
8281	assert.NoError(t, err)
8282	err = os.RemoveAll(user.GetHomeDir())
8283	assert.NoError(t, err)
8284	assert.Len(t, common.Connections.GetStats(), 0)
8285}
8286
8287func TestSFTPLoopError(t *testing.T) {
8288	user1 := getTestUser()
8289	user2 := getTestUser()
8290	user1.Username += "1"
8291	user1.Email = "user1@test.com"
8292	user2.Username += "2"
8293	user1.FsConfig = vfs.Filesystem{
8294		Provider: sdk.SFTPFilesystemProvider,
8295		SFTPConfig: vfs.SFTPFsConfig{
8296			SFTPFsConfig: sdk.SFTPFsConfig{
8297				Endpoint: sftpServerAddr,
8298				Username: user2.Username,
8299				Password: kms.NewPlainSecret(defaultPassword),
8300			},
8301		},
8302	}
8303
8304	user2.FsConfig.Provider = sdk.SFTPFilesystemProvider
8305	user2.FsConfig.SFTPConfig = vfs.SFTPFsConfig{
8306		SFTPFsConfig: sdk.SFTPFsConfig{
8307			Endpoint: sftpServerAddr,
8308			Username: user1.Username,
8309			Password: kms.NewPlainSecret(defaultPassword),
8310		},
8311	}
8312
8313	user1, resp, err := httpdtest.AddUser(user1, http.StatusCreated)
8314	assert.NoError(t, err, string(resp))
8315	user2, resp, err = httpdtest.AddUser(user2, http.StatusCreated)
8316	assert.NoError(t, err, string(resp))
8317
8318	// test reset password
8319	smtpCfg := smtp.Config{
8320		Host:          "127.0.0.1",
8321		Port:          3525,
8322		TemplatesPath: "templates",
8323	}
8324	err = smtpCfg.Initialize("..")
8325	assert.NoError(t, err)
8326
8327	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
8328	assert.NoError(t, err)
8329	form := make(url.Values)
8330	form.Set(csrfFormToken, csrfToken)
8331	form.Set("username", user1.Username)
8332	lastResetCode = ""
8333	req, err := http.NewRequest(http.MethodPost, webClientForgotPwdPath, bytes.NewBuffer([]byte(form.Encode())))
8334	assert.NoError(t, err)
8335	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
8336	rr := executeRequest(req)
8337	assert.Equal(t, http.StatusFound, rr.Code)
8338	assert.GreaterOrEqual(t, len(lastResetCode), 20)
8339	form = make(url.Values)
8340	form.Set(csrfFormToken, csrfToken)
8341	form.Set("password", defaultPassword)
8342	form.Set("code", lastResetCode)
8343	req, err = http.NewRequest(http.MethodPost, webClientResetPwdPath, bytes.NewBuffer([]byte(form.Encode())))
8344	assert.NoError(t, err)
8345	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
8346	rr = executeRequest(req)
8347	assert.Equal(t, http.StatusOK, rr.Code)
8348	assert.Contains(t, rr.Body.String(), "Password reset successfully but unable to login")
8349
8350	smtpCfg = smtp.Config{}
8351	err = smtpCfg.Initialize("..")
8352	require.NoError(t, err)
8353
8354	_, err = httpdtest.RemoveUser(user1, http.StatusOK)
8355	assert.NoError(t, err)
8356	err = os.RemoveAll(user1.GetHomeDir())
8357	assert.NoError(t, err)
8358	_, err = httpdtest.RemoveUser(user2, http.StatusOK)
8359	assert.NoError(t, err)
8360	err = os.RemoveAll(user2.GetHomeDir())
8361	assert.NoError(t, err)
8362}
8363
8364func TestLoginInvalidFs(t *testing.T) {
8365	u := getTestUser()
8366	u.Filters.AllowAPIKeyAuth = true
8367	u.FsConfig.Provider = sdk.GCSFilesystemProvider
8368	u.FsConfig.GCSConfig.Bucket = "test"
8369	u.FsConfig.GCSConfig.Credentials = kms.NewPlainSecret("invalid JSON for credentials")
8370	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
8371	assert.NoError(t, err)
8372	apiKey, _, err := httpdtest.AddAPIKey(dataprovider.APIKey{
8373		Name:  "testk",
8374		Scope: dataprovider.APIKeyScopeUser,
8375		User:  u.Username,
8376	}, http.StatusCreated)
8377	assert.NoError(t, err)
8378
8379	credentialsFile := filepath.Join(credentialsPath, fmt.Sprintf("%v_gcs_credentials.json", u.Username))
8380	if !filepath.IsAbs(credentialsFile) {
8381		credentialsFile = filepath.Join(configDir, credentialsFile)
8382	}
8383
8384	// now remove the credentials file so the filesystem creation will fail
8385	err = os.Remove(credentialsFile)
8386	assert.NoError(t, err)
8387
8388	_, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
8389	assert.Error(t, err)
8390
8391	_, err = getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
8392	assert.Error(t, err)
8393
8394	req, err := http.NewRequest(http.MethodGet, userDirsPath, nil)
8395	assert.NoError(t, err)
8396	setAPIKeyForReq(req, apiKey.Key, "")
8397	rr := executeRequest(req)
8398	checkResponseCode(t, http.StatusInternalServerError, rr)
8399
8400	_, err = httpdtest.RemoveUser(user, http.StatusOK)
8401	assert.NoError(t, err)
8402	err = os.RemoveAll(user.GetHomeDir())
8403	assert.NoError(t, err)
8404}
8405
8406func TestWebClientChangePwd(t *testing.T) {
8407	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
8408	assert.NoError(t, err)
8409	webToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
8410	assert.NoError(t, err)
8411	csrfToken, err := getCSRFToken(httpBaseURL + webClientLoginPath)
8412	assert.NoError(t, err)
8413
8414	req, err := http.NewRequest(http.MethodGet, webChangeClientPwdPath, nil)
8415	assert.NoError(t, err)
8416	setJWTCookieForReq(req, webToken)
8417	rr := executeRequest(req)
8418	checkResponseCode(t, http.StatusOK, rr)
8419
8420	form := make(url.Values)
8421	form.Set("current_password", defaultPassword)
8422	form.Set("new_password1", defaultPassword)
8423	form.Set("new_password2", defaultPassword)
8424	// no csrf token
8425	req, err = http.NewRequest(http.MethodPost, webChangeClientPwdPath, bytes.NewBuffer([]byte(form.Encode())))
8426	assert.NoError(t, err)
8427	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
8428	setJWTCookieForReq(req, webToken)
8429	rr = executeRequest(req)
8430	checkResponseCode(t, http.StatusForbidden, rr)
8431	assert.Contains(t, rr.Body.String(), "unable to verify form token")
8432
8433	form.Set(csrfFormToken, csrfToken)
8434	req, _ = http.NewRequest(http.MethodPost, webChangeClientPwdPath, bytes.NewBuffer([]byte(form.Encode())))
8435	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
8436	setJWTCookieForReq(req, webToken)
8437	rr = executeRequest(req)
8438	checkResponseCode(t, http.StatusOK, rr)
8439	assert.Contains(t, rr.Body.String(), "the new password must be different from the current one")
8440
8441	form.Set("current_password", defaultPassword+"2")
8442	form.Set("new_password1", defaultPassword+"1")
8443	form.Set("new_password2", defaultPassword+"1")
8444	req, _ = http.NewRequest(http.MethodPost, webChangeClientPwdPath, bytes.NewBuffer([]byte(form.Encode())))
8445	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
8446	setJWTCookieForReq(req, webToken)
8447	rr = executeRequest(req)
8448	checkResponseCode(t, http.StatusOK, rr)
8449	assert.Contains(t, rr.Body.String(), "current password does not match")
8450
8451	form.Set("current_password", defaultPassword)
8452	form.Set("new_password1", defaultPassword+"1")
8453	form.Set("new_password2", defaultPassword+"1")
8454	req, _ = http.NewRequest(http.MethodPost, webChangeClientPwdPath, bytes.NewBuffer([]byte(form.Encode())))
8455	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
8456	setJWTCookieForReq(req, webToken)
8457	rr = executeRequest(req)
8458	checkResponseCode(t, http.StatusFound, rr)
8459	assert.Equal(t, webClientLoginPath, rr.Header().Get("Location"))
8460
8461	_, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
8462	assert.Error(t, err)
8463	_, err = getJWTWebClientTokenFromTestServer(defaultUsername+"1", defaultPassword+"1")
8464	assert.Error(t, err)
8465	_, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword+"1")
8466	assert.NoError(t, err)
8467
8468	// remove the change password permission
8469	user.Filters.WebClient = []string{sdk.WebClientPasswordChangeDisabled}
8470	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
8471	assert.NoError(t, err)
8472	assert.Len(t, user.Filters.WebClient, 1)
8473	assert.Contains(t, user.Filters.WebClient, sdk.WebClientPasswordChangeDisabled)
8474
8475	webToken, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword+"1")
8476	assert.NoError(t, err)
8477	form.Set("current_password", defaultPassword+"1")
8478	form.Set("new_password1", defaultPassword)
8479	form.Set("new_password2", defaultPassword)
8480	req, _ = http.NewRequest(http.MethodPost, webChangeClientPwdPath, bytes.NewBuffer([]byte(form.Encode())))
8481	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
8482	setJWTCookieForReq(req, webToken)
8483	rr = executeRequest(req)
8484	checkResponseCode(t, http.StatusForbidden, rr)
8485
8486	_, err = httpdtest.RemoveUser(user, http.StatusOK)
8487	assert.NoError(t, err)
8488	err = os.RemoveAll(user.GetHomeDir())
8489	assert.NoError(t, err)
8490}
8491
8492func TestWebAPIPublicKeys(t *testing.T) {
8493	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
8494	assert.NoError(t, err)
8495	apiToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
8496	assert.NoError(t, err)
8497
8498	req, err := http.NewRequest(http.MethodGet, userPublicKeysPath, nil)
8499	assert.NoError(t, err)
8500	setBearerForReq(req, apiToken)
8501	rr := executeRequest(req)
8502	checkResponseCode(t, http.StatusOK, rr)
8503	var keys []string
8504	err = json.Unmarshal(rr.Body.Bytes(), &keys)
8505	assert.NoError(t, err)
8506	assert.Len(t, keys, 0)
8507
8508	keys = []string{testPubKey, testPubKey1}
8509	asJSON, err := json.Marshal(keys)
8510	assert.NoError(t, err)
8511	req, err = http.NewRequest(http.MethodPut, userPublicKeysPath, bytes.NewBuffer(asJSON))
8512	assert.NoError(t, err)
8513	setBearerForReq(req, apiToken)
8514	rr = executeRequest(req)
8515	checkResponseCode(t, http.StatusOK, rr)
8516
8517	req, err = http.NewRequest(http.MethodGet, userPublicKeysPath, nil)
8518	assert.NoError(t, err)
8519	setBearerForReq(req, apiToken)
8520	rr = executeRequest(req)
8521	checkResponseCode(t, http.StatusOK, rr)
8522	keys = nil
8523	err = json.Unmarshal(rr.Body.Bytes(), &keys)
8524	assert.NoError(t, err)
8525	assert.Len(t, keys, 2)
8526
8527	req, err = http.NewRequest(http.MethodPut, userPublicKeysPath, bytes.NewBuffer([]byte(`invalid json`)))
8528	assert.NoError(t, err)
8529	setBearerForReq(req, apiToken)
8530	rr = executeRequest(req)
8531	checkResponseCode(t, http.StatusBadRequest, rr)
8532
8533	keys = []string{`not a public key`}
8534	asJSON, err = json.Marshal(keys)
8535	assert.NoError(t, err)
8536	req, err = http.NewRequest(http.MethodPut, userPublicKeysPath, bytes.NewBuffer(asJSON))
8537	assert.NoError(t, err)
8538	setBearerForReq(req, apiToken)
8539	rr = executeRequest(req)
8540	checkResponseCode(t, http.StatusBadRequest, rr)
8541	assert.Contains(t, rr.Body.String(), "could not parse key")
8542
8543	user.Filters.WebClient = append(user.Filters.WebClient, sdk.WebClientPubKeyChangeDisabled)
8544	_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
8545	assert.NoError(t, err)
8546
8547	apiToken, err = getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
8548	assert.NoError(t, err)
8549	req, err = http.NewRequest(http.MethodGet, userPublicKeysPath, nil)
8550	assert.NoError(t, err)
8551	setBearerForReq(req, apiToken)
8552	rr = executeRequest(req)
8553	checkResponseCode(t, http.StatusForbidden, rr)
8554
8555	_, err = httpdtest.RemoveUser(user, http.StatusOK)
8556	assert.NoError(t, err)
8557	err = os.RemoveAll(user.GetHomeDir())
8558	assert.NoError(t, err)
8559}
8560
8561func TestPreDownloadHook(t *testing.T) {
8562	if runtime.GOOS == osWindows {
8563		t.Skip("this test is not available on Windows")
8564	}
8565	oldExecuteOn := common.Config.Actions.ExecuteOn
8566	oldHook := common.Config.Actions.Hook
8567
8568	common.Config.Actions.ExecuteOn = []string{common.OperationPreDownload}
8569	common.Config.Actions.Hook = preActionPath
8570
8571	u := getTestUser()
8572	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
8573	assert.NoError(t, err)
8574	err = os.WriteFile(preActionPath, getExitCodeScriptContent(0), os.ModePerm)
8575	assert.NoError(t, err)
8576
8577	testFileName := "testfile"
8578	testFileContents := []byte("file contents")
8579	err = os.MkdirAll(filepath.Join(user.GetHomeDir()), os.ModePerm)
8580	assert.NoError(t, err)
8581	err = os.WriteFile(filepath.Join(user.GetHomeDir(), testFileName), testFileContents, os.ModePerm)
8582	assert.NoError(t, err)
8583
8584	webToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
8585	assert.NoError(t, err)
8586	webAPIToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
8587	assert.NoError(t, err)
8588	req, err := http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testFileName, nil)
8589	assert.NoError(t, err)
8590	setJWTCookieForReq(req, webToken)
8591	rr := executeRequest(req)
8592	checkResponseCode(t, http.StatusOK, rr)
8593	assert.Equal(t, testFileContents, rr.Body.Bytes())
8594
8595	req, err = http.NewRequest(http.MethodGet, userFilesPath+"?path="+testFileName, nil)
8596	assert.NoError(t, err)
8597	setBearerForReq(req, webAPIToken)
8598	rr = executeRequest(req)
8599	checkResponseCode(t, http.StatusOK, rr)
8600	assert.Equal(t, testFileContents, rr.Body.Bytes())
8601
8602	err = os.WriteFile(preActionPath, getExitCodeScriptContent(1), os.ModePerm)
8603	assert.NoError(t, err)
8604	req, err = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testFileName, nil)
8605	assert.NoError(t, err)
8606	setJWTCookieForReq(req, webToken)
8607	rr = executeRequest(req)
8608	checkResponseCode(t, http.StatusOK, rr)
8609	assert.Contains(t, rr.Body.String(), "permission denied")
8610
8611	req, err = http.NewRequest(http.MethodGet, userFilesPath+"?path="+testFileName, nil)
8612	assert.NoError(t, err)
8613	setBearerForReq(req, webAPIToken)
8614	rr = executeRequest(req)
8615	checkResponseCode(t, http.StatusForbidden, rr)
8616	assert.Contains(t, rr.Body.String(), "permission denied")
8617
8618	_, err = httpdtest.RemoveUser(user, http.StatusOK)
8619	assert.NoError(t, err)
8620	err = os.RemoveAll(user.GetHomeDir())
8621	assert.NoError(t, err)
8622
8623	common.Config.Actions.ExecuteOn = oldExecuteOn
8624	common.Config.Actions.Hook = oldHook
8625}
8626
8627func TestPreUploadHook(t *testing.T) {
8628	if runtime.GOOS == osWindows {
8629		t.Skip("this test is not available on Windows")
8630	}
8631	oldExecuteOn := common.Config.Actions.ExecuteOn
8632	oldHook := common.Config.Actions.Hook
8633
8634	common.Config.Actions.ExecuteOn = []string{common.OperationPreUpload}
8635	common.Config.Actions.Hook = preActionPath
8636
8637	u := getTestUser()
8638	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
8639	assert.NoError(t, err)
8640	err = os.WriteFile(preActionPath, getExitCodeScriptContent(0), os.ModePerm)
8641	assert.NoError(t, err)
8642
8643	webAPIToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
8644	assert.NoError(t, err)
8645
8646	body := new(bytes.Buffer)
8647	writer := multipart.NewWriter(body)
8648	part, err := writer.CreateFormFile("filenames", "filepre")
8649	assert.NoError(t, err)
8650	_, err = part.Write([]byte("file content"))
8651	assert.NoError(t, err)
8652	err = writer.Close()
8653	assert.NoError(t, err)
8654	reader := bytes.NewReader(body.Bytes())
8655	_, err = reader.Seek(0, io.SeekStart)
8656	assert.NoError(t, err)
8657	req, err := http.NewRequest(http.MethodPost, userFilesPath, reader)
8658	assert.NoError(t, err)
8659	req.Header.Add("Content-Type", writer.FormDataContentType())
8660	setBearerForReq(req, webAPIToken)
8661	rr := executeRequest(req)
8662	checkResponseCode(t, http.StatusCreated, rr)
8663
8664	err = os.WriteFile(preActionPath, getExitCodeScriptContent(1), os.ModePerm)
8665	assert.NoError(t, err)
8666	_, err = reader.Seek(0, io.SeekStart)
8667	assert.NoError(t, err)
8668	req, err = http.NewRequest(http.MethodPost, userFilesPath, reader)
8669	assert.NoError(t, err)
8670	req.Header.Add("Content-Type", writer.FormDataContentType())
8671	setBearerForReq(req, webAPIToken)
8672	rr = executeRequest(req)
8673	checkResponseCode(t, http.StatusForbidden, rr)
8674
8675	_, err = httpdtest.RemoveUser(user, http.StatusOK)
8676	assert.NoError(t, err)
8677	err = os.RemoveAll(user.GetHomeDir())
8678	assert.NoError(t, err)
8679
8680	common.Config.Actions.ExecuteOn = oldExecuteOn
8681	common.Config.Actions.Hook = oldHook
8682}
8683
8684func TestShareUsage(t *testing.T) {
8685	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
8686	assert.NoError(t, err)
8687
8688	testFileName := "testfile.dat"
8689	testFileSize := int64(65536)
8690	testFilePath := filepath.Join(user.GetHomeDir(), testFileName)
8691	err = createTestFile(testFilePath, testFileSize)
8692	assert.NoError(t, err)
8693
8694	token, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
8695	assert.NoError(t, err)
8696
8697	share := dataprovider.Share{
8698		Name:      "test share",
8699		Scope:     dataprovider.ShareScopeRead,
8700		Paths:     []string{"/"},
8701		Password:  defaultPassword,
8702		MaxTokens: 2,
8703		ExpiresAt: util.GetTimeAsMsSinceEpoch(time.Now().Add(1 * time.Second)),
8704	}
8705	asJSON, err := json.Marshal(share)
8706	assert.NoError(t, err)
8707	req, err := http.NewRequest(http.MethodPost, userSharesPath, bytes.NewBuffer(asJSON))
8708	assert.NoError(t, err)
8709	setBearerForReq(req, token)
8710	rr := executeRequest(req)
8711	checkResponseCode(t, http.StatusCreated, rr)
8712	objectID := rr.Header().Get("X-Object-ID")
8713	assert.NotEmpty(t, objectID)
8714
8715	req, err = http.NewRequest(http.MethodGet, sharesPath+"/unknownid", nil)
8716	assert.NoError(t, err)
8717	rr = executeRequest(req)
8718	checkResponseCode(t, http.StatusNotFound, rr)
8719
8720	req, err = http.NewRequest(http.MethodGet, sharesPath+"/"+objectID, nil)
8721	assert.NoError(t, err)
8722	rr = executeRequest(req)
8723	checkResponseCode(t, http.StatusUnauthorized, rr)
8724
8725	req.SetBasicAuth(defaultUsername, "wrong password")
8726	rr = executeRequest(req)
8727	checkResponseCode(t, http.StatusUnauthorized, rr)
8728
8729	req.SetBasicAuth(defaultUsername, defaultPassword)
8730	rr = executeRequest(req)
8731	checkResponseCode(t, http.StatusOK, rr)
8732
8733	time.Sleep(2 * time.Second)
8734
8735	req, err = http.NewRequest(http.MethodGet, webClientPubSharesPath+"/"+objectID, nil)
8736	assert.NoError(t, err)
8737	req.SetBasicAuth(defaultUsername, defaultPassword)
8738	rr = executeRequest(req)
8739	checkResponseCode(t, http.StatusNotFound, rr)
8740
8741	share.ExpiresAt = 0
8742	jsonReq := make(map[string]interface{})
8743	jsonReq["name"] = share.Name
8744	jsonReq["scope"] = share.Scope
8745	jsonReq["paths"] = share.Paths
8746	jsonReq["password"] = share.Password
8747	jsonReq["max_tokens"] = share.MaxTokens
8748	jsonReq["expires_at"] = share.ExpiresAt
8749	asJSON, err = json.Marshal(jsonReq)
8750	assert.NoError(t, err)
8751	req, err = http.NewRequest(http.MethodPut, userSharesPath+"/"+objectID, bytes.NewBuffer(asJSON))
8752	assert.NoError(t, err)
8753	setBearerForReq(req, token)
8754	rr = executeRequest(req)
8755	checkResponseCode(t, http.StatusOK, rr)
8756
8757	req, err = http.NewRequest(http.MethodGet, webClientPubSharesPath+"/"+objectID, nil)
8758	assert.NoError(t, err)
8759	req.SetBasicAuth(defaultUsername, defaultPassword)
8760	rr = executeRequest(req)
8761	checkResponseCode(t, http.StatusOK, rr)
8762
8763	rr = executeRequest(req)
8764	checkResponseCode(t, http.StatusNotFound, rr)
8765
8766	req, err = http.NewRequest(http.MethodPost, sharesPath+"/"+objectID, nil)
8767	assert.NoError(t, err)
8768	rr = executeRequest(req)
8769	checkResponseCode(t, http.StatusForbidden, rr)
8770	assert.Contains(t, rr.Body.String(), "Invalid share scope")
8771
8772	share.MaxTokens = 3
8773	share.Scope = dataprovider.ShareScopeWrite
8774	asJSON, err = json.Marshal(share)
8775	assert.NoError(t, err)
8776	req, err = http.NewRequest(http.MethodPut, userSharesPath+"/"+objectID, bytes.NewBuffer(asJSON))
8777	assert.NoError(t, err)
8778	setBearerForReq(req, token)
8779	rr = executeRequest(req)
8780	checkResponseCode(t, http.StatusOK, rr)
8781
8782	body := new(bytes.Buffer)
8783	writer := multipart.NewWriter(body)
8784	part1, err := writer.CreateFormFile("filenames", "file1.txt")
8785	assert.NoError(t, err)
8786	_, err = part1.Write([]byte("file1 content"))
8787	assert.NoError(t, err)
8788	part2, err := writer.CreateFormFile("filenames", "file2.txt")
8789	assert.NoError(t, err)
8790	_, err = part2.Write([]byte("file2 content"))
8791	assert.NoError(t, err)
8792	err = writer.Close()
8793	assert.NoError(t, err)
8794	reader := bytes.NewReader(body.Bytes())
8795
8796	req, err = http.NewRequest(http.MethodPost, sharesPath+"/"+objectID, reader)
8797	assert.NoError(t, err)
8798	req.SetBasicAuth(defaultUsername, defaultPassword)
8799	rr = executeRequest(req)
8800	checkResponseCode(t, http.StatusBadRequest, rr)
8801	assert.Contains(t, rr.Body.String(), "Unable to parse multipart form")
8802
8803	_, err = reader.Seek(0, io.SeekStart)
8804	assert.NoError(t, err)
8805	// set the proper content type
8806	req, err = http.NewRequest(http.MethodPost, sharesPath+"/"+objectID, reader)
8807	assert.NoError(t, err)
8808	req.Header.Add("Content-Type", writer.FormDataContentType())
8809	req.SetBasicAuth(defaultUsername, defaultPassword)
8810	rr = executeRequest(req)
8811	checkResponseCode(t, http.StatusBadRequest, rr)
8812	assert.Contains(t, rr.Body.String(), "Allowed usage exceeded")
8813
8814	share.MaxTokens = 6
8815	share.Scope = dataprovider.ShareScopeWrite
8816	asJSON, err = json.Marshal(share)
8817	assert.NoError(t, err)
8818	req, err = http.NewRequest(http.MethodPut, userSharesPath+"/"+objectID, bytes.NewBuffer(asJSON))
8819	assert.NoError(t, err)
8820	setBearerForReq(req, token)
8821	rr = executeRequest(req)
8822	checkResponseCode(t, http.StatusOK, rr)
8823
8824	_, err = reader.Seek(0, io.SeekStart)
8825	assert.NoError(t, err)
8826	req, err = http.NewRequest(http.MethodPost, sharesPath+"/"+objectID, reader)
8827	assert.NoError(t, err)
8828	req.Header.Add("Content-Type", writer.FormDataContentType())
8829	req.SetBasicAuth(defaultUsername, defaultPassword)
8830	rr = executeRequest(req)
8831	checkResponseCode(t, http.StatusCreated, rr)
8832
8833	_, err = reader.Seek(0, io.SeekStart)
8834	assert.NoError(t, err)
8835	req, err = http.NewRequest(http.MethodPost, webClientPubSharesPath+"/"+objectID, reader)
8836	assert.NoError(t, err)
8837	req.Header.Add("Content-Type", writer.FormDataContentType())
8838	req.SetBasicAuth(defaultUsername, defaultPassword)
8839	rr = executeRequest(req)
8840	checkResponseCode(t, http.StatusCreated, rr)
8841
8842	share, err = dataprovider.ShareExists(objectID, user.Username)
8843	assert.NoError(t, err)
8844	assert.Equal(t, 6, share.UsedTokens)
8845
8846	_, err = reader.Seek(0, io.SeekStart)
8847	assert.NoError(t, err)
8848	req, err = http.NewRequest(http.MethodPost, sharesPath+"/"+objectID, reader)
8849	assert.NoError(t, err)
8850	req.Header.Add("Content-Type", writer.FormDataContentType())
8851	req.SetBasicAuth(defaultUsername, defaultPassword)
8852	rr = executeRequest(req)
8853	checkResponseCode(t, http.StatusNotFound, rr)
8854
8855	share.MaxTokens = 0
8856	err = dataprovider.UpdateShare(&share, user.Username, "")
8857	assert.NoError(t, err)
8858
8859	user.Permissions["/"] = []string{dataprovider.PermListItems, dataprovider.PermDownload}
8860	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
8861	assert.NoError(t, err)
8862
8863	_, err = reader.Seek(0, io.SeekStart)
8864	assert.NoError(t, err)
8865	req, err = http.NewRequest(http.MethodPost, sharesPath+"/"+objectID, reader)
8866	assert.NoError(t, err)
8867	req.Header.Add("Content-Type", writer.FormDataContentType())
8868	req.SetBasicAuth(defaultUsername, defaultPassword)
8869	rr = executeRequest(req)
8870	checkResponseCode(t, http.StatusInternalServerError, rr)
8871	assert.Contains(t, rr.Body.String(), "permission denied")
8872
8873	body = new(bytes.Buffer)
8874	writer = multipart.NewWriter(body)
8875	part, err := writer.CreateFormFile("filename", "file1.txt")
8876	assert.NoError(t, err)
8877	_, err = part.Write([]byte("file content"))
8878	assert.NoError(t, err)
8879	err = writer.Close()
8880	assert.NoError(t, err)
8881	reader = bytes.NewReader(body.Bytes())
8882
8883	req, err = http.NewRequest(http.MethodPost, sharesPath+"/"+objectID, reader)
8884	assert.NoError(t, err)
8885	req.Header.Add("Content-Type", writer.FormDataContentType())
8886	req.SetBasicAuth(defaultUsername, defaultPassword)
8887	rr = executeRequest(req)
8888	checkResponseCode(t, http.StatusBadRequest, rr)
8889	assert.Contains(t, rr.Body.String(), "No files uploaded!")
8890
8891	share.Scope = dataprovider.ShareScopeRead
8892	share.Paths = []string{"/missing"}
8893	err = dataprovider.UpdateShare(&share, user.Username, "")
8894	assert.NoError(t, err)
8895
8896	defer func() {
8897		rcv := recover()
8898		assert.Equal(t, http.ErrAbortHandler, rcv)
8899
8900		share, err = dataprovider.ShareExists(objectID, user.Username)
8901		assert.NoError(t, err)
8902		assert.Equal(t, 6, share.UsedTokens)
8903
8904		_, err = httpdtest.RemoveUser(user, http.StatusOK)
8905		assert.NoError(t, err)
8906		err = os.RemoveAll(user.GetHomeDir())
8907		assert.NoError(t, err)
8908	}()
8909
8910	req, err = http.NewRequest(http.MethodGet, sharesPath+"/"+objectID, nil)
8911	assert.NoError(t, err)
8912	req.SetBasicAuth(defaultUsername, defaultPassword)
8913	executeRequest(req)
8914}
8915
8916func TestUserAPIShareErrors(t *testing.T) {
8917	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
8918	assert.NoError(t, err)
8919	token, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
8920	assert.NoError(t, err)
8921
8922	share := dataprovider.Share{
8923		Scope: 1000,
8924	}
8925	asJSON, err := json.Marshal(share)
8926	assert.NoError(t, err)
8927
8928	req, err := http.NewRequest(http.MethodPost, userSharesPath, bytes.NewBuffer(asJSON))
8929	assert.NoError(t, err)
8930	setBearerForReq(req, token)
8931	rr := executeRequest(req)
8932	checkResponseCode(t, http.StatusBadRequest, rr)
8933	assert.Contains(t, rr.Body.String(), "invalid scope")
8934	// invalid json
8935	req, err = http.NewRequest(http.MethodPost, userSharesPath, bytes.NewBuffer([]byte("{")))
8936	assert.NoError(t, err)
8937	setBearerForReq(req, token)
8938	rr = executeRequest(req)
8939	checkResponseCode(t, http.StatusBadRequest, rr)
8940
8941	share.Scope = dataprovider.ShareScopeWrite
8942	asJSON, err = json.Marshal(share)
8943	assert.NoError(t, err)
8944	req, err = http.NewRequest(http.MethodPost, userSharesPath, bytes.NewBuffer(asJSON))
8945	assert.NoError(t, err)
8946	setBearerForReq(req, token)
8947	rr = executeRequest(req)
8948	checkResponseCode(t, http.StatusBadRequest, rr)
8949	assert.Contains(t, rr.Body.String(), "at least a shared path is required")
8950
8951	share.Paths = []string{"path1", "../path1", "/path2"}
8952	asJSON, err = json.Marshal(share)
8953	assert.NoError(t, err)
8954	req, err = http.NewRequest(http.MethodPost, userSharesPath, bytes.NewBuffer(asJSON))
8955	assert.NoError(t, err)
8956	setBearerForReq(req, token)
8957	rr = executeRequest(req)
8958	checkResponseCode(t, http.StatusBadRequest, rr)
8959	assert.Contains(t, rr.Body.String(), "the write share scope requires exactly one path")
8960
8961	share.Paths = []string{"", ""}
8962	asJSON, err = json.Marshal(share)
8963	assert.NoError(t, err)
8964	req, err = http.NewRequest(http.MethodPost, userSharesPath, bytes.NewBuffer(asJSON))
8965	assert.NoError(t, err)
8966	setBearerForReq(req, token)
8967	rr = executeRequest(req)
8968	checkResponseCode(t, http.StatusBadRequest, rr)
8969	assert.Contains(t, rr.Body.String(), "at least a shared path is required")
8970
8971	share.Paths = []string{"path1", "../path1", "/path1"}
8972	share.Password = redactedSecret
8973	asJSON, err = json.Marshal(share)
8974	assert.NoError(t, err)
8975	req, err = http.NewRequest(http.MethodPost, userSharesPath, bytes.NewBuffer(asJSON))
8976	assert.NoError(t, err)
8977	setBearerForReq(req, token)
8978	rr = executeRequest(req)
8979	checkResponseCode(t, http.StatusBadRequest, rr)
8980	assert.Contains(t, rr.Body.String(), "cannot save a share with a redacted password")
8981
8982	share.Password = "newpass"
8983	share.AllowFrom = []string{"not valid"}
8984	asJSON, err = json.Marshal(share)
8985	assert.NoError(t, err)
8986	req, err = http.NewRequest(http.MethodPost, userSharesPath, bytes.NewBuffer(asJSON))
8987	assert.NoError(t, err)
8988	setBearerForReq(req, token)
8989	rr = executeRequest(req)
8990	checkResponseCode(t, http.StatusBadRequest, rr)
8991	assert.Contains(t, rr.Body.String(), "could not parse allow from entry")
8992
8993	share.AllowFrom = []string{"127.0.0.1/8"}
8994	share.ExpiresAt = util.GetTimeAsMsSinceEpoch(time.Now().Add(-12 * time.Hour))
8995	asJSON, err = json.Marshal(share)
8996	assert.NoError(t, err)
8997	req, err = http.NewRequest(http.MethodPost, userSharesPath, bytes.NewBuffer(asJSON))
8998	assert.NoError(t, err)
8999	setBearerForReq(req, token)
9000	rr = executeRequest(req)
9001	checkResponseCode(t, http.StatusBadRequest, rr)
9002	assert.Contains(t, rr.Body.String(), "expiration must be in the future")
9003
9004	share.ExpiresAt = util.GetTimeAsMsSinceEpoch(time.Now().Add(12 * time.Hour))
9005	asJSON, err = json.Marshal(share)
9006	assert.NoError(t, err)
9007	req, err = http.NewRequest(http.MethodPost, userSharesPath, bytes.NewBuffer(asJSON))
9008	assert.NoError(t, err)
9009	setBearerForReq(req, token)
9010	rr = executeRequest(req)
9011	checkResponseCode(t, http.StatusCreated, rr)
9012	location := rr.Header().Get("Location")
9013
9014	asJSON, err = json.Marshal(share)
9015	assert.NoError(t, err)
9016	req, err = http.NewRequest(http.MethodPut, location, bytes.NewBuffer(asJSON))
9017	assert.NoError(t, err)
9018	setBearerForReq(req, token)
9019	rr = executeRequest(req)
9020	checkResponseCode(t, http.StatusBadRequest, rr)
9021	assert.Contains(t, rr.Body.String(), "name is mandatory")
9022	// invalid json
9023	req, err = http.NewRequest(http.MethodPut, location, bytes.NewBuffer([]byte("}")))
9024	assert.NoError(t, err)
9025	setBearerForReq(req, token)
9026	rr = executeRequest(req)
9027	checkResponseCode(t, http.StatusBadRequest, rr)
9028
9029	req, err = http.NewRequest(http.MethodGet, userSharesPath+"?limit=a", nil)
9030	assert.NoError(t, err)
9031	setBearerForReq(req, token)
9032	rr = executeRequest(req)
9033	checkResponseCode(t, http.StatusBadRequest, rr)
9034
9035	_, err = httpdtest.RemoveUser(user, http.StatusOK)
9036	assert.NoError(t, err)
9037	err = os.RemoveAll(user.GetHomeDir())
9038	assert.NoError(t, err)
9039}
9040
9041func TestUserAPIShares(t *testing.T) {
9042	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
9043	assert.NoError(t, err)
9044	token, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
9045	assert.NoError(t, err)
9046
9047	u := getTestUser()
9048	u.Username = altAdminUsername
9049	user1, _, err := httpdtest.AddUser(u, http.StatusCreated)
9050	assert.NoError(t, err)
9051	token1, err := getJWTAPIUserTokenFromTestServer(user1.Username, defaultPassword)
9052	assert.NoError(t, err)
9053
9054	// the share username will be set from
9055	share := dataprovider.Share{
9056		Name:        "share1",
9057		Description: "description1",
9058		Scope:       dataprovider.ShareScopeRead,
9059		Paths:       []string{"/"},
9060		CreatedAt:   1,
9061		UpdatedAt:   2,
9062		LastUseAt:   3,
9063		ExpiresAt:   util.GetTimeAsMsSinceEpoch(time.Now().Add(2 * time.Hour)),
9064		Password:    defaultPassword,
9065		MaxTokens:   10,
9066		UsedTokens:  2,
9067		AllowFrom:   []string{"192.168.1.0/24"},
9068	}
9069	asJSON, err := json.Marshal(share)
9070	assert.NoError(t, err)
9071
9072	req, err := http.NewRequest(http.MethodPost, userSharesPath, bytes.NewBuffer(asJSON))
9073	assert.NoError(t, err)
9074	setBearerForReq(req, token)
9075	rr := executeRequest(req)
9076	checkResponseCode(t, http.StatusCreated, rr)
9077	location := rr.Header().Get("Location")
9078	assert.NotEmpty(t, location)
9079	objectID := rr.Header().Get("X-Object-ID")
9080	assert.NotEmpty(t, objectID)
9081	assert.Equal(t, fmt.Sprintf("%v/%v", userSharesPath, objectID), location)
9082
9083	req, err = http.NewRequest(http.MethodGet, location, nil)
9084	assert.NoError(t, err)
9085	setBearerForReq(req, token)
9086	rr = executeRequest(req)
9087	checkResponseCode(t, http.StatusOK, rr)
9088	var shareGet dataprovider.Share
9089	err = json.Unmarshal(rr.Body.Bytes(), &shareGet)
9090	assert.NoError(t, err)
9091	assert.Equal(t, objectID, shareGet.ShareID)
9092	assert.Equal(t, share.Name, shareGet.Name)
9093	assert.Equal(t, share.Description, shareGet.Description)
9094	assert.Equal(t, share.Scope, shareGet.Scope)
9095	assert.Equal(t, share.Paths, shareGet.Paths)
9096	assert.Equal(t, int64(0), shareGet.LastUseAt)
9097	assert.Greater(t, shareGet.CreatedAt, share.CreatedAt)
9098	assert.Greater(t, shareGet.UpdatedAt, share.UpdatedAt)
9099	assert.Equal(t, share.ExpiresAt, shareGet.ExpiresAt)
9100	assert.Equal(t, share.MaxTokens, shareGet.MaxTokens)
9101	assert.Equal(t, 0, shareGet.UsedTokens)
9102	assert.Equal(t, share.Paths, shareGet.Paths)
9103	assert.Equal(t, redactedSecret, shareGet.Password)
9104
9105	req, err = http.NewRequest(http.MethodGet, location, nil)
9106	assert.NoError(t, err)
9107	setBearerForReq(req, token1)
9108	rr = executeRequest(req)
9109	checkResponseCode(t, http.StatusNotFound, rr)
9110
9111	s, err := dataprovider.ShareExists(objectID, defaultUsername)
9112	assert.NoError(t, err)
9113	match, err := s.CheckPassword(defaultPassword)
9114	assert.True(t, match)
9115	assert.NoError(t, err)
9116	match, err = s.CheckPassword(defaultPassword + "mod")
9117	assert.False(t, match)
9118	assert.Error(t, err)
9119
9120	shareGet.ExpiresAt = util.GetTimeAsMsSinceEpoch(time.Now().Add(3 * time.Hour))
9121	asJSON, err = json.Marshal(shareGet)
9122	assert.NoError(t, err)
9123	req, err = http.NewRequest(http.MethodPut, location, bytes.NewBuffer(asJSON))
9124	assert.NoError(t, err)
9125	setBearerForReq(req, token)
9126	rr = executeRequest(req)
9127	checkResponseCode(t, http.StatusOK, rr)
9128
9129	s, err = dataprovider.ShareExists(objectID, defaultUsername)
9130	assert.NoError(t, err)
9131	match, err = s.CheckPassword(defaultPassword)
9132	assert.True(t, match)
9133	assert.NoError(t, err)
9134	match, err = s.CheckPassword(defaultPassword + "mod")
9135	assert.False(t, match)
9136	assert.Error(t, err)
9137
9138	req, err = http.NewRequest(http.MethodGet, location, nil)
9139	assert.NoError(t, err)
9140	setBearerForReq(req, token)
9141	rr = executeRequest(req)
9142	checkResponseCode(t, http.StatusOK, rr)
9143	var shareGetNew dataprovider.Share
9144	err = json.Unmarshal(rr.Body.Bytes(), &shareGetNew)
9145	assert.NoError(t, err)
9146	assert.NotEqual(t, shareGet.UpdatedAt, shareGetNew.UpdatedAt)
9147	shareGet.UpdatedAt = shareGetNew.UpdatedAt
9148	assert.Equal(t, shareGet, shareGetNew)
9149
9150	req, err = http.NewRequest(http.MethodGet, userSharesPath, nil)
9151	assert.NoError(t, err)
9152	setBearerForReq(req, token)
9153	rr = executeRequest(req)
9154	checkResponseCode(t, http.StatusOK, rr)
9155	var shares []dataprovider.Share
9156	err = json.Unmarshal(rr.Body.Bytes(), &shares)
9157	assert.NoError(t, err)
9158	if assert.Len(t, shares, 1) {
9159		assert.Equal(t, shareGetNew, shares[0])
9160	}
9161
9162	err = dataprovider.UpdateShareLastUse(&shareGetNew, 2)
9163	assert.NoError(t, err)
9164	req, err = http.NewRequest(http.MethodGet, location, nil)
9165	assert.NoError(t, err)
9166	setBearerForReq(req, token)
9167	rr = executeRequest(req)
9168	checkResponseCode(t, http.StatusOK, rr)
9169	shareGetNew = dataprovider.Share{}
9170	err = json.Unmarshal(rr.Body.Bytes(), &shareGetNew)
9171	assert.NoError(t, err)
9172	assert.Equal(t, 2, shareGetNew.UsedTokens, "share: %v", shareGetNew)
9173	assert.Greater(t, shareGetNew.LastUseAt, int64(0), "share: %v", shareGetNew)
9174
9175	req, err = http.NewRequest(http.MethodGet, userSharesPath, nil)
9176	assert.NoError(t, err)
9177	setBearerForReq(req, token1)
9178	rr = executeRequest(req)
9179	checkResponseCode(t, http.StatusOK, rr)
9180	shares = nil
9181	err = json.Unmarshal(rr.Body.Bytes(), &shares)
9182	assert.NoError(t, err)
9183	assert.Len(t, shares, 0)
9184
9185	// set an empty password
9186	shareGet.Password = ""
9187	asJSON, err = json.Marshal(shareGet)
9188	assert.NoError(t, err)
9189	req, err = http.NewRequest(http.MethodPut, location, bytes.NewBuffer(asJSON))
9190	assert.NoError(t, err)
9191	setBearerForReq(req, token)
9192	rr = executeRequest(req)
9193	checkResponseCode(t, http.StatusOK, rr)
9194
9195	req, err = http.NewRequest(http.MethodGet, location, nil)
9196	assert.NoError(t, err)
9197	setBearerForReq(req, token)
9198	rr = executeRequest(req)
9199	checkResponseCode(t, http.StatusOK, rr)
9200	shareGetNew = dataprovider.Share{}
9201	err = json.Unmarshal(rr.Body.Bytes(), &shareGetNew)
9202	assert.NoError(t, err)
9203	assert.Empty(t, shareGetNew.Password)
9204
9205	req, err = http.NewRequest(http.MethodDelete, location, nil)
9206	assert.NoError(t, err)
9207	setBearerForReq(req, token)
9208	rr = executeRequest(req)
9209	checkResponseCode(t, http.StatusOK, rr)
9210
9211	share.Name = ""
9212	asJSON, err = json.Marshal(share)
9213	assert.NoError(t, err)
9214
9215	req, err = http.NewRequest(http.MethodPost, userSharesPath, bytes.NewBuffer(asJSON))
9216	assert.NoError(t, err)
9217	setBearerForReq(req, token)
9218	rr = executeRequest(req)
9219	checkResponseCode(t, http.StatusCreated, rr)
9220	location = rr.Header().Get("Location")
9221
9222	_, err = httpdtest.RemoveUser(user, http.StatusOK)
9223	assert.NoError(t, err)
9224	err = os.RemoveAll(user.GetHomeDir())
9225	assert.NoError(t, err)
9226	// the share should be deleted with the associated user
9227	req, err = http.NewRequest(http.MethodGet, location, nil)
9228	assert.NoError(t, err)
9229	setBearerForReq(req, token)
9230	rr = executeRequest(req)
9231	checkResponseCode(t, http.StatusNotFound, rr)
9232
9233	req, err = http.NewRequest(http.MethodPut, location, bytes.NewBuffer(asJSON))
9234	assert.NoError(t, err)
9235	setBearerForReq(req, token)
9236	rr = executeRequest(req)
9237	checkResponseCode(t, http.StatusNotFound, rr)
9238
9239	req, err = http.NewRequest(http.MethodDelete, location, nil)
9240	assert.NoError(t, err)
9241	setBearerForReq(req, token)
9242	rr = executeRequest(req)
9243	checkResponseCode(t, http.StatusNotFound, rr)
9244
9245	_, err = httpdtest.RemoveUser(user1, http.StatusOK)
9246	assert.NoError(t, err)
9247	err = os.RemoveAll(user1.GetHomeDir())
9248	assert.NoError(t, err)
9249}
9250
9251func TestUserAPIKey(t *testing.T) {
9252	u := getTestUser()
9253	u.Filters.AllowAPIKeyAuth = true
9254	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
9255	assert.NoError(t, err)
9256	apiKey := dataprovider.APIKey{
9257		Name:  "testkey",
9258		User:  user.Username + "1",
9259		Scope: dataprovider.APIKeyScopeUser,
9260	}
9261	_, _, err = httpdtest.AddAPIKey(apiKey, http.StatusBadRequest)
9262	assert.NoError(t, err)
9263	apiKey.User = user.Username
9264	apiKey, _, err = httpdtest.AddAPIKey(apiKey, http.StatusCreated)
9265	assert.NoError(t, err)
9266
9267	body := new(bytes.Buffer)
9268	writer := multipart.NewWriter(body)
9269	part, err := writer.CreateFormFile("filenames", "filenametest")
9270	assert.NoError(t, err)
9271	_, err = part.Write([]byte("test file content"))
9272	assert.NoError(t, err)
9273	err = writer.Close()
9274	assert.NoError(t, err)
9275	reader := bytes.NewReader(body.Bytes())
9276	_, err = reader.Seek(0, io.SeekStart)
9277	assert.NoError(t, err)
9278	req, err := http.NewRequest(http.MethodPost, userFilesPath, reader)
9279	assert.NoError(t, err)
9280	req.Header.Add("Content-Type", writer.FormDataContentType())
9281	setAPIKeyForReq(req, apiKey.Key, "")
9282	rr := executeRequest(req)
9283	checkResponseCode(t, http.StatusCreated, rr)
9284
9285	req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
9286	assert.NoError(t, err)
9287	setAPIKeyForReq(req, apiKey.Key, "")
9288	rr = executeRequest(req)
9289	checkResponseCode(t, http.StatusOK, rr)
9290	var dirEntries []map[string]interface{}
9291	err = json.Unmarshal(rr.Body.Bytes(), &dirEntries)
9292	assert.NoError(t, err)
9293	assert.Len(t, dirEntries, 1)
9294
9295	user.Status = 0
9296	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
9297	assert.NoError(t, err)
9298	req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
9299	assert.NoError(t, err)
9300	setAPIKeyForReq(req, apiKey.Key, "")
9301	rr = executeRequest(req)
9302	checkResponseCode(t, http.StatusUnauthorized, rr)
9303
9304	user.Status = 1
9305	user.Filters.DeniedProtocols = []string{common.ProtocolHTTP}
9306	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
9307	assert.NoError(t, err)
9308
9309	req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
9310	assert.NoError(t, err)
9311	setAPIKeyForReq(req, apiKey.Key, "")
9312	rr = executeRequest(req)
9313	checkResponseCode(t, http.StatusUnauthorized, rr)
9314
9315	user.Filters.DeniedProtocols = []string{common.ProtocolFTP}
9316	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
9317	assert.NoError(t, err)
9318
9319	req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
9320	assert.NoError(t, err)
9321	setAPIKeyForReq(req, apiKey.Key, "")
9322	rr = executeRequest(req)
9323	checkResponseCode(t, http.StatusOK, rr)
9324	apiKeyNew := dataprovider.APIKey{
9325		Name:  apiKey.Name,
9326		Scope: dataprovider.APIKeyScopeUser,
9327	}
9328
9329	apiKeyNew, _, err = httpdtest.AddAPIKey(apiKeyNew, http.StatusCreated)
9330	assert.NoError(t, err)
9331	req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
9332	assert.NoError(t, err)
9333	setAPIKeyForReq(req, apiKeyNew.Key, "")
9334	rr = executeRequest(req)
9335	checkResponseCode(t, http.StatusUnauthorized, rr)
9336	// now associate a user
9337	req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
9338	assert.NoError(t, err)
9339	setAPIKeyForReq(req, apiKeyNew.Key, user.Username)
9340	rr = executeRequest(req)
9341	checkResponseCode(t, http.StatusOK, rr)
9342	// now with a missing user
9343	req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
9344	assert.NoError(t, err)
9345	setAPIKeyForReq(req, apiKeyNew.Key, user.Username+"1")
9346	rr = executeRequest(req)
9347	checkResponseCode(t, http.StatusUnauthorized, rr)
9348	// empty user and key not associated to any user
9349	req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
9350	assert.NoError(t, err)
9351	setAPIKeyForReq(req, apiKeyNew.Key, "")
9352	rr = executeRequest(req)
9353	checkResponseCode(t, http.StatusUnauthorized, rr)
9354	apiKeyNew.ExpiresAt = util.GetTimeAsMsSinceEpoch(time.Now().Add(-24 * time.Hour))
9355	_, _, err = httpdtest.UpdateAPIKey(apiKeyNew, http.StatusOK)
9356	assert.NoError(t, err)
9357	// expired API key
9358	req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
9359	assert.NoError(t, err)
9360	setAPIKeyForReq(req, apiKeyNew.Key, user.Username)
9361	rr = executeRequest(req)
9362	checkResponseCode(t, http.StatusUnauthorized, rr)
9363
9364	_, err = httpdtest.RemoveUser(user, http.StatusOK)
9365	assert.NoError(t, err)
9366	err = os.RemoveAll(user.GetHomeDir())
9367	assert.NoError(t, err)
9368
9369	_, err = httpdtest.RemoveAPIKey(apiKeyNew, http.StatusOK)
9370	assert.NoError(t, err)
9371}
9372
9373func TestWebClientViewPDF(t *testing.T) {
9374	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
9375	assert.NoError(t, err)
9376
9377	webToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
9378	assert.NoError(t, err)
9379
9380	req, err := http.NewRequest(http.MethodGet, webClientViewPDFPath, nil)
9381	assert.NoError(t, err)
9382	setJWTCookieForReq(req, webToken)
9383	rr := executeRequest(req)
9384	checkResponseCode(t, http.StatusBadRequest, rr)
9385
9386	req, err = http.NewRequest(http.MethodGet, webClientViewPDFPath+"?path=test.pdf", nil)
9387	assert.NoError(t, err)
9388	setJWTCookieForReq(req, webToken)
9389	rr = executeRequest(req)
9390	checkResponseCode(t, http.StatusOK, rr)
9391
9392	_, err = httpdtest.RemoveUser(user, http.StatusOK)
9393	assert.NoError(t, err)
9394	err = os.RemoveAll(user.GetHomeDir())
9395	assert.NoError(t, err)
9396}
9397
9398func TestWebEditFile(t *testing.T) {
9399	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
9400	assert.NoError(t, err)
9401	testFile1 := "testfile1.txt"
9402	testFile2 := "testfile2"
9403	file1Size := int64(65536)
9404	file2Size := int64(1048576 * 2)
9405	err = createTestFile(filepath.Join(user.GetHomeDir(), testFile1), file1Size)
9406	assert.NoError(t, err)
9407	err = createTestFile(filepath.Join(user.GetHomeDir(), testFile2), file2Size)
9408	assert.NoError(t, err)
9409
9410	webToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
9411	assert.NoError(t, err)
9412
9413	req, err := http.NewRequest(http.MethodGet, webClientEditFilePath+"?path="+testFile1, nil)
9414	assert.NoError(t, err)
9415	setJWTCookieForReq(req, webToken)
9416	rr := executeRequest(req)
9417	checkResponseCode(t, http.StatusOK, rr)
9418
9419	req, err = http.NewRequest(http.MethodGet, webClientEditFilePath+"?path="+testFile2, nil)
9420	assert.NoError(t, err)
9421	setJWTCookieForReq(req, webToken)
9422	rr = executeRequest(req)
9423	checkResponseCode(t, http.StatusBadRequest, rr)
9424	assert.Contains(t, rr.Body.String(), "exceeds the maximum allowed size")
9425
9426	req, err = http.NewRequest(http.MethodGet, webClientEditFilePath+"?path=missing", nil)
9427	assert.NoError(t, err)
9428	setJWTCookieForReq(req, webToken)
9429	rr = executeRequest(req)
9430	checkResponseCode(t, http.StatusBadRequest, rr)
9431	assert.Contains(t, rr.Body.String(), "Unable to stat file")
9432
9433	req, err = http.NewRequest(http.MethodGet, webClientEditFilePath+"?path=%2F", nil)
9434	assert.NoError(t, err)
9435	setJWTCookieForReq(req, webToken)
9436	rr = executeRequest(req)
9437	checkResponseCode(t, http.StatusBadRequest, rr)
9438	assert.Contains(t, rr.Body.String(), "does not point to a file")
9439
9440	user.Filters.DeniedProtocols = []string{common.ProtocolHTTP}
9441	_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
9442	assert.NoError(t, err)
9443
9444	req, err = http.NewRequest(http.MethodGet, webClientEditFilePath+"?path="+testFile1, nil)
9445	assert.NoError(t, err)
9446	setJWTCookieForReq(req, webToken)
9447	rr = executeRequest(req)
9448	checkResponseCode(t, http.StatusForbidden, rr)
9449
9450	user.Filters.DeniedProtocols = []string{common.ProtocolFTP}
9451	user.Filters.FilePatterns = []sdk.PatternsFilter{
9452		{
9453			Path:           "/",
9454			DeniedPatterns: []string{"*.txt"},
9455		},
9456	}
9457	_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
9458	assert.NoError(t, err)
9459
9460	req, err = http.NewRequest(http.MethodGet, webClientEditFilePath+"?path="+testFile1, nil)
9461	assert.NoError(t, err)
9462	setJWTCookieForReq(req, webToken)
9463	rr = executeRequest(req)
9464	checkResponseCode(t, http.StatusForbidden, rr)
9465	assert.Contains(t, rr.Body.String(), "Unable to get a reader")
9466
9467	_, err = httpdtest.RemoveUser(user, http.StatusOK)
9468	assert.NoError(t, err)
9469	err = os.RemoveAll(user.GetHomeDir())
9470	assert.NoError(t, err)
9471
9472	req, err = http.NewRequest(http.MethodGet, webClientEditFilePath+"?path="+testFile1, nil)
9473	assert.NoError(t, err)
9474	setJWTCookieForReq(req, webToken)
9475	rr = executeRequest(req)
9476	checkResponseCode(t, http.StatusNotFound, rr)
9477}
9478
9479func TestWebGetFiles(t *testing.T) {
9480	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
9481	assert.NoError(t, err)
9482	testFileName := "testfile"
9483	testDir := "testdir"
9484	testFileContents := []byte("file contents")
9485	err = os.MkdirAll(filepath.Join(user.GetHomeDir(), testDir), os.ModePerm)
9486	assert.NoError(t, err)
9487	extensions := []string{"", ".doc", ".ppt", ".xls", ".pdf", ".mkv", ".png", ".go", ".zip", ".txt"}
9488	for _, ext := range extensions {
9489		err = os.WriteFile(filepath.Join(user.GetHomeDir(), testFileName+ext), testFileContents, os.ModePerm)
9490		assert.NoError(t, err)
9491	}
9492	err = os.Symlink(filepath.Join(user.GetHomeDir(), testFileName+".doc"), filepath.Join(user.GetHomeDir(), testDir, testFileName+".link"))
9493	assert.NoError(t, err)
9494	webToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
9495	assert.NoError(t, err)
9496	webAPIToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
9497	assert.NoError(t, err)
9498	req, _ := http.NewRequest(http.MethodGet, webClientFilesPath, nil)
9499	setJWTCookieForReq(req, webToken)
9500	rr := executeRequest(req)
9501	checkResponseCode(t, http.StatusOK, rr)
9502
9503	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testDir, nil)
9504	setJWTCookieForReq(req, webToken)
9505	rr = executeRequest(req)
9506	checkResponseCode(t, http.StatusOK, rr)
9507
9508	req, _ = http.NewRequest(http.MethodGet, webClientDirsPath+"?path="+testDir, nil)
9509	setJWTCookieForReq(req, webToken)
9510	rr = executeRequest(req)
9511	checkResponseCode(t, http.StatusOK, rr)
9512	var dirContents []map[string]string
9513	err = json.Unmarshal(rr.Body.Bytes(), &dirContents)
9514	assert.NoError(t, err)
9515	assert.Len(t, dirContents, 1)
9516
9517	req, _ = http.NewRequest(http.MethodGet, userDirsPath+"?path="+testDir, nil)
9518	setBearerForReq(req, webAPIToken)
9519	rr = executeRequest(req)
9520	checkResponseCode(t, http.StatusOK, rr)
9521	var dirEntries []map[string]interface{}
9522	err = json.Unmarshal(rr.Body.Bytes(), &dirEntries)
9523	assert.NoError(t, err)
9524	assert.Len(t, dirEntries, 1)
9525
9526	req, _ = http.NewRequest(http.MethodGet, webClientDownloadZipPath+"?path="+url.QueryEscape("/")+"&files="+
9527		url.QueryEscape(fmt.Sprintf(`["%v","%v","%v"]`, testFileName, testDir, testFileName+extensions[2])), nil)
9528	setJWTCookieForReq(req, webToken)
9529	rr = executeRequest(req)
9530	checkResponseCode(t, http.StatusOK, rr)
9531
9532	filesList := []string{testFileName, testDir, testFileName + extensions[2]}
9533	asJSON, err := json.Marshal(filesList)
9534	assert.NoError(t, err)
9535	req, _ = http.NewRequest(http.MethodPost, userStreamZipPath, bytes.NewBuffer(asJSON))
9536	setBearerForReq(req, webAPIToken)
9537	rr = executeRequest(req)
9538	checkResponseCode(t, http.StatusOK, rr)
9539
9540	assert.NoError(t, err)
9541	req, _ = http.NewRequest(http.MethodPost, userStreamZipPath, bytes.NewBuffer([]byte(`file`)))
9542	setBearerForReq(req, webAPIToken)
9543	rr = executeRequest(req)
9544	checkResponseCode(t, http.StatusBadRequest, rr)
9545
9546	req, _ = http.NewRequest(http.MethodGet, webClientDownloadZipPath+"?path="+url.QueryEscape("/")+"&files="+
9547		url.QueryEscape(fmt.Sprintf(`["%v"]`, testDir)), nil)
9548	setJWTCookieForReq(req, webToken)
9549	rr = executeRequest(req)
9550	checkResponseCode(t, http.StatusOK, rr)
9551
9552	req, _ = http.NewRequest(http.MethodGet, webClientDownloadZipPath+"?path="+url.QueryEscape("/")+"&files=notalist", nil)
9553	setJWTCookieForReq(req, webToken)
9554	rr = executeRequest(req)
9555	checkResponseCode(t, http.StatusInternalServerError, rr)
9556	assert.Contains(t, rr.Body.String(), "Unable to get files list")
9557
9558	req, _ = http.NewRequest(http.MethodGet, webClientDirsPath+"?path=/", nil)
9559	setJWTCookieForReq(req, webToken)
9560	rr = executeRequest(req)
9561	checkResponseCode(t, http.StatusOK, rr)
9562	dirContents = nil
9563	err = json.Unmarshal(rr.Body.Bytes(), &dirContents)
9564	assert.NoError(t, err)
9565	assert.Len(t, dirContents, len(extensions)+1)
9566
9567	req, _ = http.NewRequest(http.MethodGet, userDirsPath+"?path=/", nil)
9568	setBearerForReq(req, webAPIToken)
9569	rr = executeRequest(req)
9570	checkResponseCode(t, http.StatusOK, rr)
9571	dirEntries = nil
9572	err = json.Unmarshal(rr.Body.Bytes(), &dirEntries)
9573	assert.NoError(t, err)
9574	assert.Len(t, dirEntries, len(extensions)+1)
9575
9576	req, _ = http.NewRequest(http.MethodGet, webClientDirsPath+"?path=/missing", nil)
9577	setJWTCookieForReq(req, webToken)
9578	rr = executeRequest(req)
9579	checkResponseCode(t, http.StatusNotFound, rr)
9580	assert.Contains(t, rr.Body.String(), "Unable to get directory contents")
9581
9582	req, _ = http.NewRequest(http.MethodGet, userDirsPath+"?path=missing", nil)
9583	setBearerForReq(req, webAPIToken)
9584	rr = executeRequest(req)
9585	checkResponseCode(t, http.StatusNotFound, rr)
9586	assert.Contains(t, rr.Body.String(), "Unable to get directory contents")
9587
9588	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testFileName, nil)
9589	setJWTCookieForReq(req, webToken)
9590	rr = executeRequest(req)
9591	checkResponseCode(t, http.StatusOK, rr)
9592	assert.Equal(t, testFileContents, rr.Body.Bytes())
9593
9594	req, _ = http.NewRequest(http.MethodGet, userFilesPath+"?path="+testFileName, nil)
9595	setBearerForReq(req, webAPIToken)
9596	rr = executeRequest(req)
9597	checkResponseCode(t, http.StatusOK, rr)
9598	assert.Equal(t, testFileContents, rr.Body.Bytes())
9599
9600	req, _ = http.NewRequest(http.MethodGet, userFilesPath+"?path=", nil)
9601	setBearerForReq(req, webAPIToken)
9602	rr = executeRequest(req)
9603	checkResponseCode(t, http.StatusBadRequest, rr)
9604	assert.Contains(t, rr.Body.String(), "Please set the path to a valid file")
9605
9606	req, _ = http.NewRequest(http.MethodGet, userFilesPath+"?path="+testDir, nil)
9607	setBearerForReq(req, webAPIToken)
9608	rr = executeRequest(req)
9609	checkResponseCode(t, http.StatusBadRequest, rr)
9610	assert.Contains(t, rr.Body.String(), "is a directory")
9611
9612	req, _ = http.NewRequest(http.MethodGet, userFilesPath+"?path=notafile", nil)
9613	setBearerForReq(req, webAPIToken)
9614	rr = executeRequest(req)
9615	checkResponseCode(t, http.StatusNotFound, rr)
9616	assert.Contains(t, rr.Body.String(), "Unable to stat the requested file")
9617
9618	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testFileName, nil)
9619	req.Header.Set("Range", "bytes=2-")
9620	setJWTCookieForReq(req, webToken)
9621	rr = executeRequest(req)
9622	checkResponseCode(t, http.StatusPartialContent, rr)
9623	assert.Equal(t, testFileContents[2:], rr.Body.Bytes())
9624
9625	req, _ = http.NewRequest(http.MethodGet, userFilesPath+"?path="+testFileName, nil)
9626	req.Header.Set("Range", "bytes=2-")
9627	setBearerForReq(req, webAPIToken)
9628	rr = executeRequest(req)
9629	checkResponseCode(t, http.StatusPartialContent, rr)
9630	assert.Equal(t, testFileContents[2:], rr.Body.Bytes())
9631
9632	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testFileName, nil)
9633	req.Header.Set("Range", "bytes=-2")
9634	setJWTCookieForReq(req, webToken)
9635	rr = executeRequest(req)
9636	checkResponseCode(t, http.StatusPartialContent, rr)
9637	assert.Equal(t, testFileContents[11:], rr.Body.Bytes())
9638
9639	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testFileName, nil)
9640	req.Header.Set("Range", "bytes=-2,")
9641	setJWTCookieForReq(req, webToken)
9642	rr = executeRequest(req)
9643	checkResponseCode(t, http.StatusRequestedRangeNotSatisfiable, rr)
9644
9645	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testFileName, nil)
9646	req.Header.Set("Range", "bytes=1a-")
9647	setJWTCookieForReq(req, webToken)
9648	rr = executeRequest(req)
9649	checkResponseCode(t, http.StatusRequestedRangeNotSatisfiable, rr)
9650
9651	req, _ = http.NewRequest(http.MethodGet, userFilesPath+"?path="+testFileName, nil)
9652	req.Header.Set("Range", "bytes=2b-")
9653	setBearerForReq(req, webAPIToken)
9654	rr = executeRequest(req)
9655	checkResponseCode(t, http.StatusRequestedRangeNotSatisfiable, rr)
9656
9657	req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
9658	req.Header.Set("Range", "bytes=2-")
9659	req.Header.Set("If-Range", time.Now().UTC().Add(120*time.Second).Format(http.TimeFormat))
9660	setJWTCookieForReq(req, webToken)
9661	rr = executeRequest(req)
9662	checkResponseCode(t, http.StatusPartialContent, rr)
9663
9664	req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
9665	req.Header.Set("Range", "bytes=2-")
9666	req.Header.Set("If-Range", time.Now().UTC().Add(-120*time.Second).Format(http.TimeFormat))
9667	setJWTCookieForReq(req, webToken)
9668	rr = executeRequest(req)
9669	checkResponseCode(t, http.StatusOK, rr)
9670
9671	req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
9672	req.Header.Set("If-Modified-Since", time.Now().UTC().Add(-120*time.Second).Format(http.TimeFormat))
9673	setJWTCookieForReq(req, webToken)
9674	rr = executeRequest(req)
9675	checkResponseCode(t, http.StatusOK, rr)
9676
9677	req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
9678	req.Header.Set("If-Modified-Since", time.Now().UTC().Add(120*time.Second).Format(http.TimeFormat))
9679	setJWTCookieForReq(req, webToken)
9680	rr = executeRequest(req)
9681	checkResponseCode(t, http.StatusNotModified, rr)
9682
9683	req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
9684	req.Header.Set("If-Unmodified-Since", time.Now().UTC().Add(-120*time.Second).Format(http.TimeFormat))
9685	setJWTCookieForReq(req, webToken)
9686	rr = executeRequest(req)
9687	checkResponseCode(t, http.StatusPreconditionFailed, rr)
9688
9689	req, _ = http.NewRequest(http.MethodHead, userFilesPath+"?path="+testFileName, nil)
9690	req.Header.Set("If-Unmodified-Since", time.Now().UTC().Add(-120*time.Second).Format(http.TimeFormat))
9691	setBearerForReq(req, webAPIToken)
9692	rr = executeRequest(req)
9693	checkResponseCode(t, http.StatusPreconditionFailed, rr)
9694
9695	req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
9696	req.Header.Set("If-Unmodified-Since", time.Now().UTC().Add(120*time.Second).Format(http.TimeFormat))
9697	setJWTCookieForReq(req, webToken)
9698	rr = executeRequest(req)
9699	checkResponseCode(t, http.StatusOK, rr)
9700
9701	user.Filters.DeniedProtocols = []string{common.ProtocolHTTP}
9702	_, resp, err := httpdtest.UpdateUser(user, http.StatusOK, "")
9703	assert.NoError(t, err, string(resp))
9704
9705	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath, nil)
9706	setJWTCookieForReq(req, webToken)
9707	rr = executeRequest(req)
9708	checkResponseCode(t, http.StatusForbidden, rr)
9709
9710	req, _ = http.NewRequest(http.MethodGet, webClientDirsPath+"?path=/", nil)
9711	setJWTCookieForReq(req, webToken)
9712	rr = executeRequest(req)
9713	checkResponseCode(t, http.StatusForbidden, rr)
9714
9715	req, _ = http.NewRequest(http.MethodGet, userFilesPath+"?path="+testFileName, nil)
9716	setBearerForReq(req, webAPIToken)
9717	rr = executeRequest(req)
9718	checkResponseCode(t, http.StatusForbidden, rr)
9719
9720	req, _ = http.NewRequest(http.MethodGet, userDirsPath+"?path="+testDir, nil)
9721	setBearerForReq(req, webAPIToken)
9722	rr = executeRequest(req)
9723	checkResponseCode(t, http.StatusForbidden, rr)
9724
9725	filesList = []string{testDir}
9726	asJSON, err = json.Marshal(filesList)
9727	assert.NoError(t, err)
9728	req, _ = http.NewRequest(http.MethodPost, userStreamZipPath, bytes.NewBuffer(asJSON))
9729	setBearerForReq(req, webAPIToken)
9730	rr = executeRequest(req)
9731	checkResponseCode(t, http.StatusForbidden, rr)
9732
9733	user.Filters.DeniedProtocols = []string{common.ProtocolFTP}
9734	user.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodPassword}
9735	_, resp, err = httpdtest.UpdateUser(user, http.StatusOK, "")
9736	assert.NoError(t, err, string(resp))
9737
9738	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath, nil)
9739	setJWTCookieForReq(req, webToken)
9740	rr = executeRequest(req)
9741	checkResponseCode(t, http.StatusForbidden, rr)
9742
9743	req, _ = http.NewRequest(http.MethodGet, webClientDownloadZipPath, nil)
9744	setJWTCookieForReq(req, webToken)
9745	rr = executeRequest(req)
9746	checkResponseCode(t, http.StatusForbidden, rr)
9747
9748	req, _ = http.NewRequest(http.MethodGet, userDirsPath+"?path="+testDir, nil)
9749	setBearerForReq(req, webAPIToken)
9750	rr = executeRequest(req)
9751	checkResponseCode(t, http.StatusForbidden, rr)
9752
9753	_, err = httpdtest.RemoveUser(user, http.StatusOK)
9754	assert.NoError(t, err)
9755	err = os.RemoveAll(user.GetHomeDir())
9756	assert.NoError(t, err)
9757}
9758
9759func TestWebDirsAPI(t *testing.T) {
9760	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
9761	assert.NoError(t, err)
9762	webAPIToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
9763	assert.NoError(t, err)
9764	testDir := "testdir"
9765
9766	req, err := http.NewRequest(http.MethodGet, userDirsPath, nil)
9767	assert.NoError(t, err)
9768	setBearerForReq(req, webAPIToken)
9769	rr := executeRequest(req)
9770	checkResponseCode(t, http.StatusOK, rr)
9771	var contents []map[string]interface{}
9772	err = json.NewDecoder(rr.Body).Decode(&contents)
9773	assert.NoError(t, err)
9774	assert.Len(t, contents, 0)
9775
9776	// rename a missing folder
9777	req, err = http.NewRequest(http.MethodPatch, userDirsPath+"?path="+testDir+"&target="+testDir+"new", nil)
9778	assert.NoError(t, err)
9779	setBearerForReq(req, webAPIToken)
9780	rr = executeRequest(req)
9781	checkResponseCode(t, http.StatusNotFound, rr)
9782	// delete a missing folder
9783	req, err = http.NewRequest(http.MethodDelete, userDirsPath+"?path="+testDir, nil)
9784	assert.NoError(t, err)
9785	setBearerForReq(req, webAPIToken)
9786	rr = executeRequest(req)
9787	checkResponseCode(t, http.StatusNotFound, rr)
9788	// create a dir
9789	req, err = http.NewRequest(http.MethodPost, userDirsPath+"?path="+testDir, nil)
9790	assert.NoError(t, err)
9791	setBearerForReq(req, webAPIToken)
9792	rr = executeRequest(req)
9793	checkResponseCode(t, http.StatusCreated, rr)
9794	// check the dir was created
9795	req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
9796	assert.NoError(t, err)
9797	setBearerForReq(req, webAPIToken)
9798	rr = executeRequest(req)
9799	checkResponseCode(t, http.StatusOK, rr)
9800	contents = nil
9801	err = json.NewDecoder(rr.Body).Decode(&contents)
9802	assert.NoError(t, err)
9803	if assert.Len(t, contents, 1) {
9804		assert.Equal(t, testDir, contents[0]["name"])
9805	}
9806	// rename the dir
9807	req, err = http.NewRequest(http.MethodPatch, userDirsPath+"?path="+testDir+"&target="+testDir+"new", nil)
9808	assert.NoError(t, err)
9809	setBearerForReq(req, webAPIToken)
9810	rr = executeRequest(req)
9811	checkResponseCode(t, http.StatusOK, rr)
9812	// delete the dir
9813	req, err = http.NewRequest(http.MethodDelete, userDirsPath+"?path="+testDir+"new", nil)
9814	assert.NoError(t, err)
9815	setBearerForReq(req, webAPIToken)
9816	rr = executeRequest(req)
9817	checkResponseCode(t, http.StatusOK, rr)
9818	// the root dir cannot be created
9819	req, err = http.NewRequest(http.MethodPost, userDirsPath, nil)
9820	assert.NoError(t, err)
9821	setBearerForReq(req, webAPIToken)
9822	rr = executeRequest(req)
9823	checkResponseCode(t, http.StatusInternalServerError, rr)
9824
9825	user.Permissions["/"] = []string{dataprovider.PermListItems}
9826	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
9827	assert.NoError(t, err)
9828	// the user has no more the permission to create the directory
9829	req, err = http.NewRequest(http.MethodPost, userDirsPath+"?path="+testDir, nil)
9830	assert.NoError(t, err)
9831	setBearerForReq(req, webAPIToken)
9832	rr = executeRequest(req)
9833	checkResponseCode(t, http.StatusForbidden, rr)
9834
9835	_, err = httpdtest.RemoveUser(user, http.StatusOK)
9836	assert.NoError(t, err)
9837	err = os.RemoveAll(user.GetHomeDir())
9838	assert.NoError(t, err)
9839
9840	// the user is deleted, any API call should fail
9841	req, err = http.NewRequest(http.MethodPost, userDirsPath+"?path="+testDir, nil)
9842	assert.NoError(t, err)
9843	setBearerForReq(req, webAPIToken)
9844	rr = executeRequest(req)
9845	checkResponseCode(t, http.StatusNotFound, rr)
9846
9847	req, err = http.NewRequest(http.MethodPatch, userDirsPath+"?path="+testDir+"&target="+testDir+"new", nil)
9848	assert.NoError(t, err)
9849	setBearerForReq(req, webAPIToken)
9850	rr = executeRequest(req)
9851	checkResponseCode(t, http.StatusNotFound, rr)
9852
9853	req, err = http.NewRequest(http.MethodDelete, userDirsPath+"?path="+testDir+"new", nil)
9854	assert.NoError(t, err)
9855	setBearerForReq(req, webAPIToken)
9856	rr = executeRequest(req)
9857	checkResponseCode(t, http.StatusNotFound, rr)
9858}
9859
9860func TestWebFilesAPI(t *testing.T) {
9861	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
9862	assert.NoError(t, err)
9863	webAPIToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
9864	assert.NoError(t, err)
9865
9866	body := new(bytes.Buffer)
9867	writer := multipart.NewWriter(body)
9868	part1, err := writer.CreateFormFile("filenames", "file1.txt")
9869	assert.NoError(t, err)
9870	_, err = part1.Write([]byte("file1 content"))
9871	assert.NoError(t, err)
9872	part2, err := writer.CreateFormFile("filenames", "file2.txt")
9873	assert.NoError(t, err)
9874	_, err = part2.Write([]byte("file2 content"))
9875	assert.NoError(t, err)
9876	err = writer.Close()
9877	assert.NoError(t, err)
9878	reader := bytes.NewReader(body.Bytes())
9879
9880	req, err := http.NewRequest(http.MethodPost, userFilesPath, reader)
9881	assert.NoError(t, err)
9882	setBearerForReq(req, webAPIToken)
9883	rr := executeRequest(req)
9884	checkResponseCode(t, http.StatusBadRequest, rr)
9885	assert.Contains(t, rr.Body.String(), "Unable to parse multipart form")
9886	_, err = reader.Seek(0, io.SeekStart)
9887	assert.NoError(t, err)
9888	// set the proper content type
9889	req, err = http.NewRequest(http.MethodPost, userFilesPath, reader)
9890	assert.NoError(t, err)
9891	req.Header.Add("Content-Type", writer.FormDataContentType())
9892	setBearerForReq(req, webAPIToken)
9893	rr = executeRequest(req)
9894	checkResponseCode(t, http.StatusCreated, rr)
9895	// check we have 2 files
9896	req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
9897	assert.NoError(t, err)
9898	setBearerForReq(req, webAPIToken)
9899	rr = executeRequest(req)
9900	checkResponseCode(t, http.StatusOK, rr)
9901	var contents []map[string]interface{}
9902	err = json.NewDecoder(rr.Body).Decode(&contents)
9903	assert.NoError(t, err)
9904	assert.Len(t, contents, 2)
9905	// overwrite the existing files
9906	_, err = reader.Seek(0, io.SeekStart)
9907	assert.NoError(t, err)
9908	req, err = http.NewRequest(http.MethodPost, userFilesPath, reader)
9909	assert.NoError(t, err)
9910	req.Header.Add("Content-Type", writer.FormDataContentType())
9911	setBearerForReq(req, webAPIToken)
9912	rr = executeRequest(req)
9913	checkResponseCode(t, http.StatusCreated, rr)
9914	req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
9915	assert.NoError(t, err)
9916	setBearerForReq(req, webAPIToken)
9917	rr = executeRequest(req)
9918	checkResponseCode(t, http.StatusOK, rr)
9919	contents = nil
9920	err = json.NewDecoder(rr.Body).Decode(&contents)
9921	assert.NoError(t, err)
9922	assert.Len(t, contents, 2)
9923	// now create a dir and upload to that dir
9924	testDir := "tdir"
9925	req, err = http.NewRequest(http.MethodPost, userDirsPath+"?path="+testDir, nil)
9926	assert.NoError(t, err)
9927	setBearerForReq(req, webAPIToken)
9928	rr = executeRequest(req)
9929	checkResponseCode(t, http.StatusCreated, rr)
9930	_, err = reader.Seek(0, io.SeekStart)
9931	assert.NoError(t, err)
9932	req, err = http.NewRequest(http.MethodPost, userFilesPath+"?path="+testDir, reader)
9933	assert.NoError(t, err)
9934	req.Header.Add("Content-Type", writer.FormDataContentType())
9935	setBearerForReq(req, webAPIToken)
9936	rr = executeRequest(req)
9937	checkResponseCode(t, http.StatusCreated, rr)
9938	req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
9939	assert.NoError(t, err)
9940	setBearerForReq(req, webAPIToken)
9941	rr = executeRequest(req)
9942	checkResponseCode(t, http.StatusOK, rr)
9943	contents = nil
9944	err = json.NewDecoder(rr.Body).Decode(&contents)
9945	assert.NoError(t, err)
9946	assert.Len(t, contents, 3)
9947	req, err = http.NewRequest(http.MethodGet, userDirsPath+"?path="+testDir, nil)
9948	assert.NoError(t, err)
9949	setBearerForReq(req, webAPIToken)
9950	rr = executeRequest(req)
9951	checkResponseCode(t, http.StatusOK, rr)
9952	contents = nil
9953	err = json.NewDecoder(rr.Body).Decode(&contents)
9954	assert.NoError(t, err)
9955	assert.Len(t, contents, 2)
9956	// rename a file
9957	req, err = http.NewRequest(http.MethodPatch, userFilesPath+"?path=file1.txt&target=%2Ftdir%2Ffile3.txt", nil)
9958	assert.NoError(t, err)
9959	setBearerForReq(req, webAPIToken)
9960	rr = executeRequest(req)
9961	checkResponseCode(t, http.StatusOK, rr)
9962	// rename a missing file
9963	req, err = http.NewRequest(http.MethodPatch, userFilesPath+"?path=file1.txt&target=%2Ftdir%2Ffile3.txt", nil)
9964	assert.NoError(t, err)
9965	setBearerForReq(req, webAPIToken)
9966	rr = executeRequest(req)
9967	checkResponseCode(t, http.StatusNotFound, rr)
9968	// delete a file
9969	req, err = http.NewRequest(http.MethodDelete, userFilesPath+"?path=file2.txt", nil)
9970	assert.NoError(t, err)
9971	setBearerForReq(req, webAPIToken)
9972	rr = executeRequest(req)
9973	checkResponseCode(t, http.StatusOK, rr)
9974	// delete a missing file
9975	req, err = http.NewRequest(http.MethodDelete, userFilesPath+"?path=file2.txt", nil)
9976	assert.NoError(t, err)
9977	setBearerForReq(req, webAPIToken)
9978	rr = executeRequest(req)
9979	checkResponseCode(t, http.StatusNotFound, rr)
9980	// delete a directory
9981	req, err = http.NewRequest(http.MethodDelete, userFilesPath+"?path=tdir", nil)
9982	assert.NoError(t, err)
9983	setBearerForReq(req, webAPIToken)
9984	rr = executeRequest(req)
9985	checkResponseCode(t, http.StatusBadRequest, rr)
9986	// make a symlink outside the home dir and then try to delete it
9987	extPath := filepath.Join(os.TempDir(), "file")
9988	err = os.WriteFile(extPath, []byte("contents"), os.ModePerm)
9989	assert.NoError(t, err)
9990	err = os.Symlink(extPath, filepath.Join(user.GetHomeDir(), "file"))
9991	assert.NoError(t, err)
9992	req, err = http.NewRequest(http.MethodDelete, userFilesPath+"?path=file", nil)
9993	assert.NoError(t, err)
9994	setBearerForReq(req, webAPIToken)
9995	rr = executeRequest(req)
9996	checkResponseCode(t, http.StatusForbidden, rr)
9997	err = os.Remove(extPath)
9998	assert.NoError(t, err)
9999	// remove delete and overwrite permissions
10000	user.Permissions["/"] = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermUpload}
10001	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
10002	assert.NoError(t, err)
10003	_, err = reader.Seek(0, io.SeekStart)
10004	assert.NoError(t, err)
10005	req, err = http.NewRequest(http.MethodPost, userFilesPath+"?path=tdir", reader)
10006	assert.NoError(t, err)
10007	req.Header.Add("Content-Type", writer.FormDataContentType())
10008	setBearerForReq(req, webAPIToken)
10009	rr = executeRequest(req)
10010	checkResponseCode(t, http.StatusForbidden, rr)
10011
10012	req, err = http.NewRequest(http.MethodDelete, userFilesPath+"?path=%2Ftdir%2Ffile1.txt", nil)
10013	assert.NoError(t, err)
10014	setBearerForReq(req, webAPIToken)
10015	rr = executeRequest(req)
10016	checkResponseCode(t, http.StatusForbidden, rr)
10017
10018	_, err = httpdtest.RemoveUser(user, http.StatusOK)
10019	assert.NoError(t, err)
10020	err = os.RemoveAll(user.GetHomeDir())
10021	assert.NoError(t, err)
10022	// the user is deleted, any API call should fail
10023	_, err = reader.Seek(0, io.SeekStart)
10024	assert.NoError(t, err)
10025	req, err = http.NewRequest(http.MethodPost, userFilesPath, reader)
10026	assert.NoError(t, err)
10027	req.Header.Add("Content-Type", writer.FormDataContentType())
10028	setBearerForReq(req, webAPIToken)
10029	rr = executeRequest(req)
10030	checkResponseCode(t, http.StatusNotFound, rr)
10031
10032	req, err = http.NewRequest(http.MethodPatch, userFilesPath+"?path=file1.txt&target=%2Ftdir%2Ffile3.txt", nil)
10033	assert.NoError(t, err)
10034	setBearerForReq(req, webAPIToken)
10035	rr = executeRequest(req)
10036	checkResponseCode(t, http.StatusNotFound, rr)
10037
10038	req, err = http.NewRequest(http.MethodDelete, userFilesPath+"?path=file2.txt", nil)
10039	assert.NoError(t, err)
10040	setBearerForReq(req, webAPIToken)
10041	rr = executeRequest(req)
10042	checkResponseCode(t, http.StatusNotFound, rr)
10043}
10044
10045func TestWebUploadErrors(t *testing.T) {
10046	u := getTestUser()
10047	u.QuotaSize = 65535
10048	subDir1 := "sub1"
10049	subDir2 := "sub2"
10050	u.Permissions[path.Join("/", subDir1)] = []string{dataprovider.PermListItems}
10051	u.Permissions[path.Join("/", subDir2)] = []string{dataprovider.PermListItems, dataprovider.PermUpload,
10052		dataprovider.PermDelete}
10053	u.Filters.FilePatterns = []sdk.PatternsFilter{
10054		{
10055			Path:            "/sub2",
10056			AllowedPatterns: []string{},
10057			DeniedPatterns:  []string{"*.zip"},
10058		},
10059	}
10060	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
10061	assert.NoError(t, err)
10062	webAPIToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
10063	assert.NoError(t, err)
10064
10065	body := new(bytes.Buffer)
10066	writer := multipart.NewWriter(body)
10067	part, err := writer.CreateFormFile("filenames", "file.zip")
10068	assert.NoError(t, err)
10069	_, err = part.Write([]byte("file content"))
10070	assert.NoError(t, err)
10071	err = writer.Close()
10072	assert.NoError(t, err)
10073	reader := bytes.NewReader(body.Bytes())
10074	_, err = reader.Seek(0, io.SeekStart)
10075	assert.NoError(t, err)
10076	// zip file are not allowed within sub2
10077	req, err := http.NewRequest(http.MethodPost, userFilesPath+"?path=sub2", reader)
10078	assert.NoError(t, err)
10079	req.Header.Add("Content-Type", writer.FormDataContentType())
10080	setBearerForReq(req, webAPIToken)
10081	rr := executeRequest(req)
10082	checkResponseCode(t, http.StatusForbidden, rr)
10083
10084	_, err = reader.Seek(0, io.SeekStart)
10085	assert.NoError(t, err)
10086	// we have no upload permissions within sub1
10087	req, err = http.NewRequest(http.MethodPost, userFilesPath+"?path=sub1", reader)
10088	assert.NoError(t, err)
10089	req.Header.Add("Content-Type", writer.FormDataContentType())
10090	setBearerForReq(req, webAPIToken)
10091	rr = executeRequest(req)
10092	checkResponseCode(t, http.StatusForbidden, rr)
10093
10094	// create a dir and try to overwrite it with a file
10095	req, err = http.NewRequest(http.MethodPost, userDirsPath+"?path=file.zip", nil)
10096	assert.NoError(t, err)
10097	setBearerForReq(req, webAPIToken)
10098	rr = executeRequest(req)
10099	checkResponseCode(t, http.StatusCreated, rr)
10100
10101	_, err = reader.Seek(0, io.SeekStart)
10102	assert.NoError(t, err)
10103	req, err = http.NewRequest(http.MethodPost, userFilesPath, reader)
10104	assert.NoError(t, err)
10105	req.Header.Add("Content-Type", writer.FormDataContentType())
10106	setBearerForReq(req, webAPIToken)
10107	rr = executeRequest(req)
10108	checkResponseCode(t, http.StatusInternalServerError, rr)
10109	assert.Contains(t, rr.Body.String(), "operation unsupported")
10110	// try to upload to a missing parent directory
10111	_, err = reader.Seek(0, io.SeekStart)
10112	assert.NoError(t, err)
10113	req, err = http.NewRequest(http.MethodPost, userFilesPath+"?path=missingdir", reader)
10114	assert.NoError(t, err)
10115	req.Header.Add("Content-Type", writer.FormDataContentType())
10116	setBearerForReq(req, webAPIToken)
10117	rr = executeRequest(req)
10118	checkResponseCode(t, http.StatusNotFound, rr)
10119
10120	req, err = http.NewRequest(http.MethodDelete, userDirsPath+"?path=file.zip", nil)
10121	assert.NoError(t, err)
10122	setBearerForReq(req, webAPIToken)
10123	rr = executeRequest(req)
10124	checkResponseCode(t, http.StatusOK, rr)
10125	// upload will work now
10126	_, err = reader.Seek(0, io.SeekStart)
10127	assert.NoError(t, err)
10128	req, err = http.NewRequest(http.MethodPost, userFilesPath, reader)
10129	assert.NoError(t, err)
10130	req.Header.Add("Content-Type", writer.FormDataContentType())
10131	setBearerForReq(req, webAPIToken)
10132	rr = executeRequest(req)
10133	checkResponseCode(t, http.StatusCreated, rr)
10134	// overwrite the file
10135	_, err = reader.Seek(0, io.SeekStart)
10136	assert.NoError(t, err)
10137	req, err = http.NewRequest(http.MethodPost, userFilesPath, reader)
10138	assert.NoError(t, err)
10139	req.Header.Add("Content-Type", writer.FormDataContentType())
10140	setBearerForReq(req, webAPIToken)
10141	rr = executeRequest(req)
10142	checkResponseCode(t, http.StatusCreated, rr)
10143
10144	vfs.SetTempPath(filepath.Join(os.TempDir(), "missingpath"))
10145
10146	_, err = reader.Seek(0, io.SeekStart)
10147	assert.NoError(t, err)
10148	req, err = http.NewRequest(http.MethodPost, userFilesPath, reader)
10149	assert.NoError(t, err)
10150	req.Header.Add("Content-Type", writer.FormDataContentType())
10151	setBearerForReq(req, webAPIToken)
10152	rr = executeRequest(req)
10153	checkResponseCode(t, http.StatusNotFound, rr)
10154
10155	if runtime.GOOS != osWindows {
10156		req, err = http.NewRequest(http.MethodDelete, userFilesPath+"?path=file.zip", reader)
10157		assert.NoError(t, err)
10158		setBearerForReq(req, webAPIToken)
10159		rr = executeRequest(req)
10160		checkResponseCode(t, http.StatusOK, rr)
10161
10162		vfs.SetTempPath(filepath.Clean(os.TempDir()))
10163		err = os.Chmod(user.GetHomeDir(), 0555)
10164		assert.NoError(t, err)
10165
10166		_, err = reader.Seek(0, io.SeekStart)
10167		assert.NoError(t, err)
10168		req, err = http.NewRequest(http.MethodPost, userFilesPath, reader)
10169		assert.NoError(t, err)
10170		req.Header.Add("Content-Type", writer.FormDataContentType())
10171		setBearerForReq(req, webAPIToken)
10172		rr = executeRequest(req)
10173		checkResponseCode(t, http.StatusForbidden, rr)
10174		assert.Contains(t, rr.Body.String(), "Error closing file")
10175
10176		err = os.Chmod(user.GetHomeDir(), os.ModePerm)
10177		assert.NoError(t, err)
10178	}
10179
10180	vfs.SetTempPath("")
10181
10182	// upload a multipart form with no files
10183	body = new(bytes.Buffer)
10184	writer = multipart.NewWriter(body)
10185	err = writer.Close()
10186	assert.NoError(t, err)
10187	reader = bytes.NewReader(body.Bytes())
10188	_, err = reader.Seek(0, io.SeekStart)
10189	assert.NoError(t, err)
10190	req, err = http.NewRequest(http.MethodPost, userFilesPath+"?path=sub2", reader)
10191	assert.NoError(t, err)
10192	req.Header.Add("Content-Type", writer.FormDataContentType())
10193	setBearerForReq(req, webAPIToken)
10194	rr = executeRequest(req)
10195	checkResponseCode(t, http.StatusBadRequest, rr)
10196	assert.Contains(t, rr.Body.String(), "No files uploaded!")
10197
10198	_, err = httpdtest.RemoveUser(user, http.StatusOK)
10199	assert.NoError(t, err)
10200	err = os.RemoveAll(user.GetHomeDir())
10201	assert.NoError(t, err)
10202}
10203
10204func TestWebAPIVFolder(t *testing.T) {
10205	u := getTestUser()
10206	u.QuotaSize = 65535
10207	vdir := "/vdir"
10208	mappedPath := filepath.Join(os.TempDir(), "vdir")
10209	folderName := filepath.Base(mappedPath)
10210	u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
10211		BaseVirtualFolder: vfs.BaseVirtualFolder{
10212			Name:       folderName,
10213			MappedPath: mappedPath,
10214		},
10215		VirtualPath: vdir,
10216		QuotaSize:   -1,
10217		QuotaFiles:  -1,
10218	})
10219	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
10220	assert.NoError(t, err)
10221
10222	webAPIToken, err := getJWTAPIUserTokenFromTestServer(user.Username, defaultPassword)
10223	assert.NoError(t, err)
10224
10225	fileContents := []byte("test contents")
10226
10227	body := new(bytes.Buffer)
10228	writer := multipart.NewWriter(body)
10229	part, err := writer.CreateFormFile("filenames", "file.txt")
10230	assert.NoError(t, err)
10231	_, err = part.Write(fileContents)
10232	assert.NoError(t, err)
10233	err = writer.Close()
10234	assert.NoError(t, err)
10235	reader := bytes.NewReader(body.Bytes())
10236
10237	req, err := http.NewRequest(http.MethodPost, userFilesPath+"?path=vdir", reader)
10238	assert.NoError(t, err)
10239	req.Header.Add("Content-Type", writer.FormDataContentType())
10240	setBearerForReq(req, webAPIToken)
10241	rr := executeRequest(req)
10242	checkResponseCode(t, http.StatusCreated, rr)
10243
10244	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
10245	assert.NoError(t, err)
10246	assert.Equal(t, int64(len(fileContents)), user.UsedQuotaSize)
10247
10248	folder, _, err := httpdtest.GetFolderByName(folderName, http.StatusOK)
10249	assert.NoError(t, err)
10250	assert.Equal(t, int64(len(fileContents)), folder.UsedQuotaSize)
10251
10252	_, err = reader.Seek(0, io.SeekStart)
10253	assert.NoError(t, err)
10254	req, err = http.NewRequest(http.MethodPost, userFilesPath+"?path=vdir", reader)
10255	assert.NoError(t, err)
10256	req.Header.Add("Content-Type", writer.FormDataContentType())
10257	setBearerForReq(req, webAPIToken)
10258	rr = executeRequest(req)
10259	checkResponseCode(t, http.StatusCreated, rr)
10260
10261	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
10262	assert.NoError(t, err)
10263	assert.Equal(t, int64(len(fileContents)), user.UsedQuotaSize)
10264
10265	folder, _, err = httpdtest.GetFolderByName(folderName, http.StatusOK)
10266	assert.NoError(t, err)
10267	assert.Equal(t, int64(len(fileContents)), folder.UsedQuotaSize)
10268
10269	_, err = httpdtest.RemoveUser(user, http.StatusOK)
10270	assert.NoError(t, err)
10271	_, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName}, http.StatusOK)
10272	assert.NoError(t, err)
10273	err = os.RemoveAll(user.GetHomeDir())
10274	assert.NoError(t, err)
10275	err = os.RemoveAll(mappedPath)
10276	assert.NoError(t, err)
10277}
10278
10279func TestWebAPIWritePermission(t *testing.T) {
10280	u := getTestUser()
10281	u.Filters.WebClient = append(u.Filters.WebClient, sdk.WebClientWriteDisabled)
10282	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
10283	assert.NoError(t, err)
10284	webAPIToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
10285	assert.NoError(t, err)
10286
10287	body := new(bytes.Buffer)
10288	writer := multipart.NewWriter(body)
10289	part, err := writer.CreateFormFile("filenames", "file.txt")
10290	assert.NoError(t, err)
10291	_, err = part.Write([]byte(""))
10292	assert.NoError(t, err)
10293	err = writer.Close()
10294	assert.NoError(t, err)
10295	reader := bytes.NewReader(body.Bytes())
10296
10297	req, err := http.NewRequest(http.MethodPost, userFilesPath, reader)
10298	assert.NoError(t, err)
10299	req.Header.Add("Content-Type", writer.FormDataContentType())
10300	setBearerForReq(req, webAPIToken)
10301	rr := executeRequest(req)
10302	checkResponseCode(t, http.StatusForbidden, rr)
10303
10304	req, err = http.NewRequest(http.MethodPatch, userFilesPath+"?path=a&target=b", nil)
10305	assert.NoError(t, err)
10306	req.Header.Add("Content-Type", writer.FormDataContentType())
10307	setBearerForReq(req, webAPIToken)
10308	rr = executeRequest(req)
10309	checkResponseCode(t, http.StatusForbidden, rr)
10310
10311	req, err = http.NewRequest(http.MethodDelete, userFilesPath+"?path=a", nil)
10312	assert.NoError(t, err)
10313	req.Header.Add("Content-Type", writer.FormDataContentType())
10314	setBearerForReq(req, webAPIToken)
10315	rr = executeRequest(req)
10316	checkResponseCode(t, http.StatusForbidden, rr)
10317
10318	req, err = http.NewRequest(http.MethodGet, userFilesPath+"?path=a.txt", nil)
10319	assert.NoError(t, err)
10320	req.Header.Add("Content-Type", writer.FormDataContentType())
10321	setBearerForReq(req, webAPIToken)
10322	rr = executeRequest(req)
10323	checkResponseCode(t, http.StatusNotFound, rr)
10324
10325	req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
10326	assert.NoError(t, err)
10327	req.Header.Add("Content-Type", writer.FormDataContentType())
10328	setBearerForReq(req, webAPIToken)
10329	rr = executeRequest(req)
10330	checkResponseCode(t, http.StatusOK, rr)
10331
10332	req, err = http.NewRequest(http.MethodPost, userDirsPath+"?path=dir", nil)
10333	assert.NoError(t, err)
10334	req.Header.Add("Content-Type", writer.FormDataContentType())
10335	setBearerForReq(req, webAPIToken)
10336	rr = executeRequest(req)
10337	checkResponseCode(t, http.StatusForbidden, rr)
10338
10339	req, err = http.NewRequest(http.MethodPatch, userDirsPath+"?path=dir&target=dir1", nil)
10340	assert.NoError(t, err)
10341	req.Header.Add("Content-Type", writer.FormDataContentType())
10342	setBearerForReq(req, webAPIToken)
10343	rr = executeRequest(req)
10344	checkResponseCode(t, http.StatusForbidden, rr)
10345
10346	req, err = http.NewRequest(http.MethodDelete, userDirsPath+"?path=dir", nil)
10347	assert.NoError(t, err)
10348	req.Header.Add("Content-Type", writer.FormDataContentType())
10349	setBearerForReq(req, webAPIToken)
10350	rr = executeRequest(req)
10351	checkResponseCode(t, http.StatusForbidden, rr)
10352
10353	_, err = httpdtest.RemoveUser(user, http.StatusOK)
10354	assert.NoError(t, err)
10355	err = os.RemoveAll(user.GetHomeDir())
10356	assert.NoError(t, err)
10357}
10358
10359func TestWebAPICryptFs(t *testing.T) {
10360	u := getTestUser()
10361	u.QuotaSize = 65535
10362	u.FsConfig.Provider = sdk.CryptedFilesystemProvider
10363	u.FsConfig.CryptConfig.Passphrase = kms.NewPlainSecret(defaultPassword)
10364	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
10365	assert.NoError(t, err)
10366	webAPIToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
10367	assert.NoError(t, err)
10368
10369	body := new(bytes.Buffer)
10370	writer := multipart.NewWriter(body)
10371	part, err := writer.CreateFormFile("filenames", "file.txt")
10372	assert.NoError(t, err)
10373	_, err = part.Write([]byte("content"))
10374	assert.NoError(t, err)
10375	err = writer.Close()
10376	assert.NoError(t, err)
10377	reader := bytes.NewReader(body.Bytes())
10378
10379	req, err := http.NewRequest(http.MethodPost, userFilesPath, reader)
10380	assert.NoError(t, err)
10381	req.Header.Add("Content-Type", writer.FormDataContentType())
10382	setBearerForReq(req, webAPIToken)
10383	rr := executeRequest(req)
10384	checkResponseCode(t, http.StatusCreated, rr)
10385
10386	_, err = reader.Seek(0, io.SeekStart)
10387	assert.NoError(t, err)
10388	req, err = http.NewRequest(http.MethodPost, userFilesPath, reader)
10389	assert.NoError(t, err)
10390	req.Header.Add("Content-Type", writer.FormDataContentType())
10391	setBearerForReq(req, webAPIToken)
10392	rr = executeRequest(req)
10393	checkResponseCode(t, http.StatusCreated, rr)
10394
10395	_, err = httpdtest.RemoveUser(user, http.StatusOK)
10396	assert.NoError(t, err)
10397	err = os.RemoveAll(user.GetHomeDir())
10398	assert.NoError(t, err)
10399}
10400
10401func TestWebUploadSFTP(t *testing.T) {
10402	u := getTestUser()
10403	localUser, _, err := httpdtest.AddUser(u, http.StatusCreated)
10404	assert.NoError(t, err)
10405	u = getTestSFTPUser()
10406	u.QuotaFiles = 100
10407	u.FsConfig.SFTPConfig.BufferSize = 2
10408	u.HomeDir = filepath.Join(os.TempDir(), u.Username)
10409	sftpUser, _, err := httpdtest.AddUser(u, http.StatusCreated)
10410	assert.NoError(t, err)
10411
10412	webAPIToken, err := getJWTAPIUserTokenFromTestServer(sftpUser.Username, defaultPassword)
10413	assert.NoError(t, err)
10414
10415	body := new(bytes.Buffer)
10416	writer := multipart.NewWriter(body)
10417	part, err := writer.CreateFormFile("filenames", "file.txt")
10418	assert.NoError(t, err)
10419	_, err = part.Write([]byte("test file content"))
10420	assert.NoError(t, err)
10421	err = writer.Close()
10422	assert.NoError(t, err)
10423	reader := bytes.NewReader(body.Bytes())
10424
10425	req, err := http.NewRequest(http.MethodPost, userFilesPath, reader)
10426	assert.NoError(t, err)
10427	req.Header.Add("Content-Type", writer.FormDataContentType())
10428	setBearerForReq(req, webAPIToken)
10429	rr := executeRequest(req)
10430	checkResponseCode(t, http.StatusCreated, rr)
10431
10432	expectedQuotaSize := int64(17)
10433	expectedQuotaFiles := 1
10434	user, _, err := httpdtest.GetUserByUsername(sftpUser.Username, http.StatusOK)
10435	assert.NoError(t, err)
10436	assert.Equal(t, expectedQuotaFiles, user.UsedQuotaFiles)
10437	assert.Equal(t, expectedQuotaSize, user.UsedQuotaSize)
10438
10439	user.QuotaSize = 10
10440	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
10441	assert.NoError(t, err)
10442	// we are now overquota on overwrite
10443	_, err = reader.Seek(0, io.SeekStart)
10444	assert.NoError(t, err)
10445	req, err = http.NewRequest(http.MethodPost, userFilesPath, reader)
10446	assert.NoError(t, err)
10447	req.Header.Add("Content-Type", writer.FormDataContentType())
10448	setBearerForReq(req, webAPIToken)
10449	rr = executeRequest(req)
10450	checkResponseCode(t, http.StatusRequestEntityTooLarge, rr)
10451	assert.Contains(t, rr.Body.String(), "denying write due to space limit")
10452	// delete the file
10453	req, err = http.NewRequest(http.MethodDelete, userFilesPath+"?path=file.txt", nil)
10454	assert.NoError(t, err)
10455	setBearerForReq(req, webAPIToken)
10456	rr = executeRequest(req)
10457	checkResponseCode(t, http.StatusOK, rr)
10458
10459	_, err = reader.Seek(0, io.SeekStart)
10460	assert.NoError(t, err)
10461	req, err = http.NewRequest(http.MethodPost, userFilesPath, reader)
10462	assert.NoError(t, err)
10463	req.Header.Add("Content-Type", writer.FormDataContentType())
10464	setBearerForReq(req, webAPIToken)
10465	rr = executeRequest(req)
10466	checkResponseCode(t, http.StatusRequestEntityTooLarge, rr)
10467	assert.Contains(t, rr.Body.String(), "denying write due to space limit")
10468
10469	_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
10470	assert.NoError(t, err)
10471	_, err = httpdtest.RemoveUser(localUser, http.StatusOK)
10472	assert.NoError(t, err)
10473	err = os.RemoveAll(localUser.GetHomeDir())
10474	assert.NoError(t, err)
10475	err = os.RemoveAll(sftpUser.GetHomeDir())
10476	assert.NoError(t, err)
10477}
10478
10479func TestWebUploadMultipartFormReadError(t *testing.T) {
10480	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
10481	assert.NoError(t, err)
10482	webAPIToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
10483	assert.NoError(t, err)
10484
10485	req, err := http.NewRequest(http.MethodPost, userFilesPath, nil)
10486	assert.NoError(t, err)
10487
10488	mpartForm := &multipart.Form{
10489		File: make(map[string][]*multipart.FileHeader),
10490	}
10491	mpartForm.File["filenames"] = append(mpartForm.File["filenames"], &multipart.FileHeader{Filename: "missing"})
10492	req.MultipartForm = mpartForm
10493	req.Header.Add("Content-Type", "multipart/form-data")
10494	setBearerForReq(req, webAPIToken)
10495	rr := executeRequest(req)
10496	checkResponseCode(t, http.StatusInternalServerError, rr)
10497	assert.Contains(t, rr.Body.String(), "Unable to read uploaded file")
10498
10499	_, err = httpdtest.RemoveUser(user, http.StatusOK)
10500	assert.NoError(t, err)
10501	err = os.RemoveAll(user.GetHomeDir())
10502	assert.NoError(t, err)
10503}
10504
10505func TestCompressionErrorMock(t *testing.T) {
10506	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
10507	assert.NoError(t, err)
10508
10509	defer func() {
10510		rcv := recover()
10511		assert.Equal(t, http.ErrAbortHandler, rcv)
10512		_, err := httpdtest.RemoveUser(user, http.StatusOK)
10513		assert.NoError(t, err)
10514		err = os.RemoveAll(user.GetHomeDir())
10515		assert.NoError(t, err)
10516	}()
10517
10518	webToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
10519	assert.NoError(t, err)
10520
10521	req, _ := http.NewRequest(http.MethodGet, webClientDownloadZipPath+"?path="+url.QueryEscape("/")+"&files="+
10522		url.QueryEscape(`["missing"]`), nil)
10523	setJWTCookieForReq(req, webToken)
10524	executeRequest(req)
10525}
10526
10527func TestGetFilesSFTPBackend(t *testing.T) {
10528	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
10529	assert.NoError(t, err)
10530	u := getTestSFTPUser()
10531	u.FsConfig.SFTPConfig.BufferSize = 2
10532	u.Permissions["/adir"] = nil
10533	u.Permissions["/adir1"] = []string{dataprovider.PermListItems}
10534	u.Filters.FilePatterns = []sdk.PatternsFilter{
10535		{
10536			Path:           "/adir2",
10537			DeniedPatterns: []string{"*.txt"},
10538		},
10539	}
10540	sftpUser, _, err := httpdtest.AddUser(u, http.StatusCreated)
10541	assert.NoError(t, err)
10542
10543	testFileName := "testsftpfile"
10544	testDir := "testsftpdir"
10545	testFileContents := []byte("sftp file contents")
10546	err = os.MkdirAll(filepath.Join(user.GetHomeDir(), testDir, "sub"), os.ModePerm)
10547	assert.NoError(t, err)
10548	err = os.MkdirAll(filepath.Join(user.GetHomeDir(), "adir1"), os.ModePerm)
10549	assert.NoError(t, err)
10550	err = os.MkdirAll(filepath.Join(user.GetHomeDir(), "adir2"), os.ModePerm)
10551	assert.NoError(t, err)
10552	err = os.WriteFile(filepath.Join(user.GetHomeDir(), testFileName), testFileContents, os.ModePerm)
10553	assert.NoError(t, err)
10554	err = os.WriteFile(filepath.Join(user.GetHomeDir(), "adir1", "afile"), testFileContents, os.ModePerm)
10555	assert.NoError(t, err)
10556	err = os.WriteFile(filepath.Join(user.GetHomeDir(), "adir2", "afile.txt"), testFileContents, os.ModePerm)
10557	assert.NoError(t, err)
10558	webToken, err := getJWTWebClientTokenFromTestServer(sftpUser.Username, defaultPassword)
10559	assert.NoError(t, err)
10560	req, _ := http.NewRequest(http.MethodGet, webClientFilesPath, nil)
10561	setJWTCookieForReq(req, webToken)
10562	rr := executeRequest(req)
10563	checkResponseCode(t, http.StatusOK, rr)
10564
10565	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+path.Join(testDir, "sub"), nil)
10566	setJWTCookieForReq(req, webToken)
10567	rr = executeRequest(req)
10568	checkResponseCode(t, http.StatusOK, rr)
10569
10570	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+path.Join(testDir, "missing"), nil)
10571	setJWTCookieForReq(req, webToken)
10572	rr = executeRequest(req)
10573	checkResponseCode(t, http.StatusOK, rr)
10574	assert.Contains(t, rr.Body.String(), "card-body text-form-error")
10575	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path=adir/sub", nil)
10576	setJWTCookieForReq(req, webToken)
10577	rr = executeRequest(req)
10578	checkResponseCode(t, http.StatusOK, rr)
10579	assert.Contains(t, rr.Body.String(), "card-body text-form-error")
10580
10581	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path=adir1/afile", nil)
10582	setJWTCookieForReq(req, webToken)
10583	rr = executeRequest(req)
10584	checkResponseCode(t, http.StatusOK, rr)
10585	assert.Contains(t, rr.Body.String(), "card-body text-form-error")
10586
10587	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path=adir2/afile.txt", nil)
10588	setJWTCookieForReq(req, webToken)
10589	rr = executeRequest(req)
10590	checkResponseCode(t, http.StatusOK, rr)
10591	assert.Contains(t, rr.Body.String(), "card-body text-form-error")
10592
10593	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testFileName, nil)
10594	setJWTCookieForReq(req, webToken)
10595	rr = executeRequest(req)
10596	checkResponseCode(t, http.StatusOK, rr)
10597	assert.Equal(t, testFileContents, rr.Body.Bytes())
10598
10599	req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testFileName, nil)
10600	req.Header.Set("Range", "bytes=2-")
10601	setJWTCookieForReq(req, webToken)
10602	rr = executeRequest(req)
10603	checkResponseCode(t, http.StatusPartialContent, rr)
10604	assert.Equal(t, testFileContents[2:], rr.Body.Bytes())
10605
10606	_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
10607	assert.NoError(t, err)
10608	err = os.RemoveAll(sftpUser.GetHomeDir())
10609	assert.NoError(t, err)
10610	_, err = httpdtest.RemoveUser(user, http.StatusOK)
10611	assert.NoError(t, err)
10612	err = os.RemoveAll(user.GetHomeDir())
10613	assert.NoError(t, err)
10614}
10615
10616func TestClientUserClose(t *testing.T) {
10617	u := getTestUser()
10618	u.UploadBandwidth = 32
10619	u.DownloadBandwidth = 32
10620	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
10621	assert.NoError(t, err)
10622	testFileName := "file.dat"
10623	testFileSize := int64(524288)
10624	testFilePath := filepath.Join(user.GetHomeDir(), testFileName)
10625	err = createTestFile(testFilePath, testFileSize)
10626	assert.NoError(t, err)
10627	uploadContent := make([]byte, testFileSize)
10628	_, err = rand.Read(uploadContent)
10629	assert.NoError(t, err)
10630	webToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
10631	assert.NoError(t, err)
10632	webAPIToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
10633	assert.NoError(t, err)
10634
10635	var wg sync.WaitGroup
10636	wg.Add(1)
10637	go func() {
10638		defer wg.Done()
10639		req, _ := http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testFileName, nil)
10640		setJWTCookieForReq(req, webToken)
10641		rr := executeRequest(req)
10642		checkResponseCode(t, http.StatusOK, rr)
10643	}()
10644	wg.Add(1)
10645	go func() {
10646		defer wg.Done()
10647		req, _ := http.NewRequest(http.MethodGet, webClientEditFilePath+"?path="+testFileName, nil)
10648		setJWTCookieForReq(req, webToken)
10649		rr := executeRequest(req)
10650		checkResponseCode(t, http.StatusInternalServerError, rr)
10651		assert.Contains(t, rr.Body.String(), "Unable to read the file")
10652	}()
10653	wg.Add(1)
10654	go func() {
10655		defer wg.Done()
10656		body := new(bytes.Buffer)
10657		writer := multipart.NewWriter(body)
10658		part, err := writer.CreateFormFile("filenames", "upload.dat")
10659		assert.NoError(t, err)
10660		n, err := part.Write(uploadContent)
10661		assert.NoError(t, err)
10662		assert.Equal(t, testFileSize, int64(n))
10663		err = writer.Close()
10664		assert.NoError(t, err)
10665		reader := bytes.NewReader(body.Bytes())
10666		req, err := http.NewRequest(http.MethodPost, userFilesPath, reader)
10667		assert.NoError(t, err)
10668		req.Header.Add("Content-Type", writer.FormDataContentType())
10669		setBearerForReq(req, webAPIToken)
10670		rr := executeRequest(req)
10671		checkResponseCode(t, http.StatusInternalServerError, rr)
10672		assert.Contains(t, rr.Body.String(), "transfer aborted")
10673	}()
10674	// wait for the transfers
10675	assert.Eventually(t, func() bool {
10676		stats := common.Connections.GetStats()
10677		if len(stats) == 3 {
10678			if len(stats[0].Transfers) > 0 && len(stats[1].Transfers) > 0 {
10679				return true
10680			}
10681		}
10682		return false
10683	}, 1*time.Second, 50*time.Millisecond)
10684
10685	for _, stat := range common.Connections.GetStats() {
10686		// close all the active transfers
10687		common.Connections.Close(stat.ConnectionID)
10688	}
10689	wg.Wait()
10690	assert.Eventually(t, func() bool { return len(common.Connections.GetStats()) == 0 },
10691		1*time.Second, 100*time.Millisecond)
10692
10693	_, err = httpdtest.RemoveUser(user, http.StatusOK)
10694	assert.NoError(t, err)
10695	err = os.RemoveAll(user.GetHomeDir())
10696	assert.NoError(t, err)
10697}
10698
10699func TestWebAdminSetupMock(t *testing.T) {
10700	req, err := http.NewRequest(http.MethodGet, webAdminSetupPath, nil)
10701	assert.NoError(t, err)
10702	rr := executeRequest(req)
10703	checkResponseCode(t, http.StatusFound, rr)
10704	assert.Equal(t, webLoginPath, rr.Header().Get("Location"))
10705	// now delete all the admins
10706	admins, err := dataprovider.GetAdmins(100, 0, dataprovider.OrderASC)
10707	assert.NoError(t, err)
10708	for _, admin := range admins {
10709		err = dataprovider.DeleteAdmin(admin.Username, "", "")
10710		assert.NoError(t, err)
10711	}
10712	// close the provider and initializes it without creating the default admin
10713	os.Setenv("SFTPGO_DATA_PROVIDER__CREATE_DEFAULT_ADMIN", "0")
10714	err = dataprovider.Close()
10715	assert.NoError(t, err)
10716	err = config.LoadConfig(configDir, "")
10717	assert.NoError(t, err)
10718	providerConf := config.GetProviderConf()
10719	providerConf.CredentialsPath = credentialsPath
10720	err = os.RemoveAll(credentialsPath)
10721	assert.NoError(t, err)
10722	err = dataprovider.Initialize(providerConf, configDir, true)
10723	assert.NoError(t, err)
10724	// now the setup page must be rendered
10725	req, err = http.NewRequest(http.MethodGet, webAdminSetupPath, nil)
10726	assert.NoError(t, err)
10727	rr = executeRequest(req)
10728	checkResponseCode(t, http.StatusOK, rr)
10729	// check redirects to the setup page
10730	req, err = http.NewRequest(http.MethodGet, "/", nil)
10731	assert.NoError(t, err)
10732	rr = executeRequest(req)
10733	checkResponseCode(t, http.StatusFound, rr)
10734	assert.Equal(t, webAdminSetupPath, rr.Header().Get("Location"))
10735	req, err = http.NewRequest(http.MethodGet, webBasePath, nil)
10736	assert.NoError(t, err)
10737	rr = executeRequest(req)
10738	checkResponseCode(t, http.StatusFound, rr)
10739	assert.Equal(t, webAdminSetupPath, rr.Header().Get("Location"))
10740	req, err = http.NewRequest(http.MethodGet, webBasePathAdmin, nil)
10741	assert.NoError(t, err)
10742	rr = executeRequest(req)
10743	checkResponseCode(t, http.StatusFound, rr)
10744	assert.Equal(t, webAdminSetupPath, rr.Header().Get("Location"))
10745	req, err = http.NewRequest(http.MethodGet, webLoginPath, nil)
10746	assert.NoError(t, err)
10747	rr = executeRequest(req)
10748	checkResponseCode(t, http.StatusFound, rr)
10749	assert.Equal(t, webAdminSetupPath, rr.Header().Get("Location"))
10750	req, err = http.NewRequest(http.MethodGet, webClientLoginPath, nil)
10751	assert.NoError(t, err)
10752	rr = executeRequest(req)
10753	checkResponseCode(t, http.StatusFound, rr)
10754	assert.Equal(t, webAdminSetupPath, rr.Header().Get("Location"))
10755
10756	csrfToken, err := getCSRFToken(httpBaseURL + webAdminSetupPath)
10757	assert.NoError(t, err)
10758	form := make(url.Values)
10759	req, err = http.NewRequest(http.MethodPost, webAdminSetupPath, bytes.NewBuffer([]byte(form.Encode())))
10760	assert.NoError(t, err)
10761	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
10762	rr = executeRequest(req)
10763	checkResponseCode(t, http.StatusForbidden, rr)
10764	form.Set(csrfFormToken, csrfToken)
10765	req, err = http.NewRequest(http.MethodPost, webAdminSetupPath, bytes.NewBuffer([]byte(form.Encode())))
10766	assert.NoError(t, err)
10767	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
10768	rr = executeRequest(req)
10769	checkResponseCode(t, http.StatusOK, rr)
10770	assert.Contains(t, rr.Body.String(), "Please set a username")
10771	form.Set("username", defaultTokenAuthUser)
10772	req, err = http.NewRequest(http.MethodPost, webAdminSetupPath, bytes.NewBuffer([]byte(form.Encode())))
10773	assert.NoError(t, err)
10774	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
10775	rr = executeRequest(req)
10776	checkResponseCode(t, http.StatusOK, rr)
10777	assert.Contains(t, rr.Body.String(), "Please set a password")
10778	form.Set("password", defaultTokenAuthPass)
10779	req, err = http.NewRequest(http.MethodPost, webAdminSetupPath, bytes.NewBuffer([]byte(form.Encode())))
10780	assert.NoError(t, err)
10781	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
10782	rr = executeRequest(req)
10783	checkResponseCode(t, http.StatusOK, rr)
10784	assert.Contains(t, rr.Body.String(), "Passwords mismatch")
10785	form.Set("confirm_password", defaultTokenAuthPass)
10786	// test a parse form error
10787	req, err = http.NewRequest(http.MethodPost, webAdminSetupPath+"?param=p%C3%AO%GH", bytes.NewBuffer([]byte(form.Encode())))
10788	assert.NoError(t, err)
10789	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
10790	rr = executeRequest(req)
10791	checkResponseCode(t, http.StatusOK, rr)
10792	// test a dataprovider error
10793	err = dataprovider.Close()
10794	assert.NoError(t, err)
10795	req, err = http.NewRequest(http.MethodPost, webAdminSetupPath, bytes.NewBuffer([]byte(form.Encode())))
10796	assert.NoError(t, err)
10797	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
10798	rr = executeRequest(req)
10799	checkResponseCode(t, http.StatusOK, rr)
10800	// finally initialize the provider and create the default admin
10801	err = config.LoadConfig(configDir, "")
10802	assert.NoError(t, err)
10803	providerConf = config.GetProviderConf()
10804	providerConf.CredentialsPath = credentialsPath
10805	err = os.RemoveAll(credentialsPath)
10806	assert.NoError(t, err)
10807	err = dataprovider.Initialize(providerConf, configDir, true)
10808	assert.NoError(t, err)
10809	req, err = http.NewRequest(http.MethodPost, webAdminSetupPath, bytes.NewBuffer([]byte(form.Encode())))
10810	assert.NoError(t, err)
10811	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
10812	rr = executeRequest(req)
10813	checkResponseCode(t, http.StatusFound, rr)
10814	assert.Equal(t, webUsersPath, rr.Header().Get("Location"))
10815	// if we resubmit the form we get a bad request, an admin already exists
10816	req, err = http.NewRequest(http.MethodPost, webAdminSetupPath, bytes.NewBuffer([]byte(form.Encode())))
10817	assert.NoError(t, err)
10818	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
10819	rr = executeRequest(req)
10820	checkResponseCode(t, http.StatusBadRequest, rr)
10821	assert.Contains(t, rr.Body.String(), "an admin user already exists")
10822	os.Setenv("SFTPGO_DATA_PROVIDER__CREATE_DEFAULT_ADMIN", "1")
10823}
10824
10825func TestWebAdminLoginMock(t *testing.T) {
10826	webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
10827	assert.NoError(t, err)
10828	apiToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
10829	assert.NoError(t, err)
10830
10831	req, _ := http.NewRequest(http.MethodGet, serverStatusPath, nil)
10832	setBearerForReq(req, apiToken)
10833	rr := executeRequest(req)
10834	checkResponseCode(t, http.StatusOK, rr)
10835
10836	req, _ = http.NewRequest(http.MethodGet, webStatusPath+"notfound", nil)
10837	req.RequestURI = webStatusPath + "notfound"
10838	setJWTCookieForReq(req, webToken)
10839	rr = executeRequest(req)
10840	checkResponseCode(t, http.StatusNotFound, rr)
10841
10842	req, _ = http.NewRequest(http.MethodGet, webStatusPath, nil)
10843	setJWTCookieForReq(req, webToken)
10844	rr = executeRequest(req)
10845	checkResponseCode(t, http.StatusOK, rr)
10846
10847	req, _ = http.NewRequest(http.MethodGet, webLogoutPath, nil)
10848	setJWTCookieForReq(req, webToken)
10849	rr = executeRequest(req)
10850	checkResponseCode(t, http.StatusFound, rr)
10851	cookie := rr.Header().Get("Cookie")
10852	assert.Empty(t, cookie)
10853
10854	req, _ = http.NewRequest(http.MethodGet, logoutPath, nil)
10855	setBearerForReq(req, apiToken)
10856	rr = executeRequest(req)
10857	checkResponseCode(t, http.StatusOK, rr)
10858
10859	req, _ = http.NewRequest(http.MethodGet, serverStatusPath, nil)
10860	setBearerForReq(req, apiToken)
10861	rr = executeRequest(req)
10862	checkResponseCode(t, http.StatusUnauthorized, rr)
10863	assert.Contains(t, rr.Body.String(), "Your token is no longer valid")
10864
10865	req, _ = http.NewRequest(http.MethodGet, webStatusPath, nil)
10866	setJWTCookieForReq(req, webToken)
10867	rr = executeRequest(req)
10868	checkResponseCode(t, http.StatusFound, rr)
10869
10870	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
10871	assert.NoError(t, err)
10872	// now try using wrong credentials
10873	form := getLoginForm(defaultTokenAuthUser, "wrong pwd", csrfToken)
10874	req, _ = http.NewRequest(http.MethodPost, webLoginPath, bytes.NewBuffer([]byte(form.Encode())))
10875	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
10876	rr = executeRequest(req)
10877	checkResponseCode(t, http.StatusOK, rr)
10878	// try from an ip not allowed
10879	a := getTestAdmin()
10880	a.Username = altAdminUsername
10881	a.Password = altAdminPassword
10882	a.Filters.AllowList = []string{"10.0.0.0/8"}
10883
10884	_, _, err = httpdtest.AddAdmin(a, http.StatusCreated)
10885	assert.NoError(t, err)
10886
10887	form = getLoginForm(altAdminUsername, altAdminPassword, csrfToken)
10888	req, _ = http.NewRequest(http.MethodPost, webLoginPath, bytes.NewBuffer([]byte(form.Encode())))
10889	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
10890	req.RemoteAddr = "127.1.1.1:1234"
10891	rr = executeRequest(req)
10892	checkResponseCode(t, http.StatusOK, rr)
10893	assert.Contains(t, rr.Body.String(), "login from IP 127.1.1.1 not allowed")
10894
10895	req, _ = http.NewRequest(http.MethodPost, webLoginPath, bytes.NewBuffer([]byte(form.Encode())))
10896	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
10897	req.RemoteAddr = "10.9.9.9:1234"
10898	rr = executeRequest(req)
10899	checkResponseCode(t, http.StatusFound, rr)
10900
10901	req, _ = http.NewRequest(http.MethodPost, webLoginPath, bytes.NewBuffer([]byte(form.Encode())))
10902	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
10903	req.RemoteAddr = "127.0.1.1:4567"
10904	req.Header.Set("X-Forwarded-For", "10.9.9.9")
10905	rr = executeRequest(req)
10906	checkResponseCode(t, http.StatusOK, rr)
10907	assert.Contains(t, rr.Body.String(), "login from IP 127.0.1.1 not allowed")
10908
10909	// invalid csrf token
10910	form = getLoginForm(altAdminUsername, altAdminPassword, "invalid csrf")
10911	req, _ = http.NewRequest(http.MethodPost, webLoginPath, bytes.NewBuffer([]byte(form.Encode())))
10912	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
10913	req.RemoteAddr = "10.9.9.8:1234"
10914	rr = executeRequest(req)
10915	checkResponseCode(t, http.StatusOK, rr)
10916	assert.Contains(t, rr.Body.String(), "unable to verify form token")
10917
10918	req, _ = http.NewRequest(http.MethodGet, webLoginPath, nil)
10919	rr = executeRequest(req)
10920	checkResponseCode(t, http.StatusOK, rr)
10921
10922	_, err = httpdtest.RemoveAdmin(a, http.StatusOK)
10923	assert.NoError(t, err)
10924}
10925
10926func TestAdminNoToken(t *testing.T) {
10927	req, _ := http.NewRequest(http.MethodGet, webAdminProfilePath, nil)
10928	rr := executeRequest(req)
10929	checkResponseCode(t, http.StatusFound, rr)
10930	assert.Equal(t, webLoginPath, rr.Header().Get("Location"))
10931
10932	req, _ = http.NewRequest(http.MethodGet, webUserPath, nil)
10933	rr = executeRequest(req)
10934	checkResponseCode(t, http.StatusFound, rr)
10935	assert.Equal(t, webLoginPath, rr.Header().Get("Location"))
10936
10937	req, _ = http.NewRequest(http.MethodGet, userPath, nil)
10938	rr = executeRequest(req)
10939	checkResponseCode(t, http.StatusUnauthorized, rr)
10940
10941	req, _ = http.NewRequest(http.MethodGet, activeConnectionsPath, nil)
10942	rr = executeRequest(req)
10943	checkResponseCode(t, http.StatusUnauthorized, rr)
10944}
10945
10946func TestWebUserShare(t *testing.T) {
10947	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
10948	assert.NoError(t, err)
10949
10950	csrfToken, err := getCSRFToken(httpBaseURL + webClientLoginPath)
10951	assert.NoError(t, err)
10952	token, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
10953	assert.NoError(t, err)
10954	userAPItoken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
10955	assert.NoError(t, err)
10956
10957	share := dataprovider.Share{
10958		Name:        "test share",
10959		Description: "test share desc",
10960		Scope:       dataprovider.ShareScopeRead,
10961		Paths:       []string{"/"},
10962		ExpiresAt:   util.GetTimeAsMsSinceEpoch(time.Now().Add(24 * time.Hour)),
10963		MaxTokens:   100,
10964		AllowFrom:   []string{"127.0.0.0/8", "172.16.0.0/16"},
10965		Password:    defaultPassword,
10966	}
10967	form := make(url.Values)
10968	form.Set("name", share.Name)
10969	form.Set("scope", strconv.Itoa(int(share.Scope)))
10970	form.Set("paths", "/")
10971	form.Set("max_tokens", strconv.Itoa(share.MaxTokens))
10972	form.Set("allowed_ip", strings.Join(share.AllowFrom, ","))
10973	form.Set("description", share.Description)
10974	form.Set("password", share.Password)
10975	form.Set("expiration_date", "123")
10976	// invalid expiration date
10977	req, err := http.NewRequest(http.MethodPost, webClientSharePath, bytes.NewBuffer([]byte(form.Encode())))
10978	assert.NoError(t, err)
10979	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
10980	setJWTCookieForReq(req, token)
10981	rr := executeRequest(req)
10982	checkResponseCode(t, http.StatusOK, rr)
10983	assert.Contains(t, rr.Body.String(), "cannot parse")
10984	form.Set("expiration_date", util.GetTimeFromMsecSinceEpoch(share.ExpiresAt).UTC().Format("2006-01-02 15:04:05"))
10985	form.Set("scope", "")
10986	// invalid scope
10987	req, err = http.NewRequest(http.MethodPost, webClientSharePath, bytes.NewBuffer([]byte(form.Encode())))
10988	assert.NoError(t, err)
10989	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
10990	setJWTCookieForReq(req, token)
10991	rr = executeRequest(req)
10992	checkResponseCode(t, http.StatusOK, rr)
10993	assert.Contains(t, rr.Body.String(), "invalid syntax")
10994	form.Set("scope", strconv.Itoa(int(share.Scope)))
10995	// invalid max tokens
10996	form.Set("max_tokens", "t")
10997	req, err = http.NewRequest(http.MethodPost, webClientSharePath, bytes.NewBuffer([]byte(form.Encode())))
10998	assert.NoError(t, err)
10999	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11000	setJWTCookieForReq(req, token)
11001	rr = executeRequest(req)
11002	checkResponseCode(t, http.StatusOK, rr)
11003	assert.Contains(t, rr.Body.String(), "invalid syntax")
11004	form.Set("max_tokens", strconv.Itoa(share.MaxTokens))
11005	// no csrf token
11006	req, err = http.NewRequest(http.MethodPost, webClientSharePath, bytes.NewBuffer([]byte(form.Encode())))
11007	assert.NoError(t, err)
11008	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11009	setJWTCookieForReq(req, token)
11010	rr = executeRequest(req)
11011	checkResponseCode(t, http.StatusForbidden, rr)
11012	assert.Contains(t, rr.Body.String(), "unable to verify form token")
11013
11014	form.Set(csrfFormToken, csrfToken)
11015	form.Set("scope", "100")
11016	req, err = http.NewRequest(http.MethodPost, webClientSharePath, bytes.NewBuffer([]byte(form.Encode())))
11017	assert.NoError(t, err)
11018	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11019	setJWTCookieForReq(req, token)
11020	rr = executeRequest(req)
11021	checkResponseCode(t, http.StatusOK, rr)
11022	assert.Contains(t, rr.Body.String(), "Validation error: invalid scope")
11023
11024	form.Set("scope", strconv.Itoa(int(share.Scope)))
11025	req, err = http.NewRequest(http.MethodPost, webClientSharePath, bytes.NewBuffer([]byte(form.Encode())))
11026	assert.NoError(t, err)
11027	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11028	setJWTCookieForReq(req, token)
11029	rr = executeRequest(req)
11030	checkResponseCode(t, http.StatusSeeOther, rr)
11031
11032	req, err = http.NewRequest(http.MethodGet, userSharesPath, nil)
11033	assert.NoError(t, err)
11034	setBearerForReq(req, userAPItoken)
11035	rr = executeRequest(req)
11036	checkResponseCode(t, http.StatusOK, rr)
11037	var shares []dataprovider.Share
11038	err = json.Unmarshal(rr.Body.Bytes(), &shares)
11039	assert.NoError(t, err)
11040	if assert.Len(t, shares, 1) {
11041		s := shares[0]
11042		assert.Equal(t, share.Name, s.Name)
11043		assert.Equal(t, share.Description, s.Description)
11044		assert.Equal(t, share.Scope, s.Scope)
11045		assert.Equal(t, share.Paths, s.Paths)
11046		assert.InDelta(t, share.ExpiresAt, s.ExpiresAt, 999)
11047		assert.Equal(t, share.MaxTokens, s.MaxTokens)
11048		assert.Equal(t, share.AllowFrom, s.AllowFrom)
11049		assert.Equal(t, redactedSecret, s.Password)
11050		share.ShareID = s.ShareID
11051	}
11052	form.Set("password", redactedSecret)
11053	form.Set("expiration_date", "123")
11054	req, err = http.NewRequest(http.MethodPost, webClientSharePath+"/unknowid", bytes.NewBuffer([]byte(form.Encode())))
11055	assert.NoError(t, err)
11056	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11057	setJWTCookieForReq(req, token)
11058	rr = executeRequest(req)
11059	checkResponseCode(t, http.StatusNotFound, rr)
11060
11061	req, err = http.NewRequest(http.MethodPost, webClientSharePath+"/"+share.ShareID, bytes.NewBuffer([]byte(form.Encode())))
11062	assert.NoError(t, err)
11063	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11064	setJWTCookieForReq(req, token)
11065	rr = executeRequest(req)
11066	checkResponseCode(t, http.StatusOK, rr)
11067	assert.Contains(t, rr.Body.String(), "cannot parse")
11068
11069	form.Set("expiration_date", "")
11070	form.Set(csrfFormToken, "")
11071	req, err = http.NewRequest(http.MethodPost, webClientSharePath+"/"+share.ShareID, bytes.NewBuffer([]byte(form.Encode())))
11072	assert.NoError(t, err)
11073	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11074	setJWTCookieForReq(req, token)
11075	rr = executeRequest(req)
11076	checkResponseCode(t, http.StatusForbidden, rr)
11077	assert.Contains(t, rr.Body.String(), "unable to verify form token")
11078
11079	form.Set(csrfFormToken, csrfToken)
11080	form.Set("allowed_ip", "1.1.1")
11081	req, err = http.NewRequest(http.MethodPost, webClientSharePath+"/"+share.ShareID, bytes.NewBuffer([]byte(form.Encode())))
11082	assert.NoError(t, err)
11083	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11084	setJWTCookieForReq(req, token)
11085	rr = executeRequest(req)
11086	checkResponseCode(t, http.StatusOK, rr)
11087	assert.Contains(t, rr.Body.String(), "Validation error: could not parse allow from entry")
11088
11089	form.Set("allowed_ip", "")
11090	req, err = http.NewRequest(http.MethodPost, webClientSharePath+"/"+share.ShareID, bytes.NewBuffer([]byte(form.Encode())))
11091	assert.NoError(t, err)
11092	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11093	setJWTCookieForReq(req, token)
11094	rr = executeRequest(req)
11095	checkResponseCode(t, http.StatusSeeOther, rr)
11096
11097	req, err = http.NewRequest(http.MethodGet, userSharesPath, nil)
11098	assert.NoError(t, err)
11099	setBearerForReq(req, userAPItoken)
11100	rr = executeRequest(req)
11101	checkResponseCode(t, http.StatusOK, rr)
11102	shares = nil
11103	err = json.Unmarshal(rr.Body.Bytes(), &shares)
11104	assert.NoError(t, err)
11105	if assert.Len(t, shares, 1) {
11106		s := shares[0]
11107		assert.Equal(t, share.Name, s.Name)
11108		assert.Equal(t, share.Description, s.Description)
11109		assert.Equal(t, share.Scope, s.Scope)
11110		assert.Equal(t, share.Paths, s.Paths)
11111		assert.Equal(t, int64(0), s.ExpiresAt)
11112		assert.Equal(t, share.MaxTokens, s.MaxTokens)
11113		assert.Empty(t, s.AllowFrom)
11114	}
11115	// check the password
11116	s, err := dataprovider.ShareExists(share.ShareID, user.Username)
11117	assert.NoError(t, err)
11118	match, err := s.CheckPassword(defaultPassword)
11119	assert.NoError(t, err)
11120	assert.True(t, match)
11121
11122	req, err = http.NewRequest(http.MethodGet, webClientSharePath+"?path=%2F&files=a", nil)
11123	assert.NoError(t, err)
11124	setJWTCookieForReq(req, token)
11125	rr = executeRequest(req)
11126	checkResponseCode(t, http.StatusBadRequest, rr)
11127	assert.Contains(t, rr.Body.String(), "Invalid share list")
11128
11129	req, err = http.NewRequest(http.MethodGet, webClientSharePath+"?path=%2F&files=%5B\"adir\"%5D", nil)
11130	assert.NoError(t, err)
11131	setJWTCookieForReq(req, token)
11132	rr = executeRequest(req)
11133	checkResponseCode(t, http.StatusOK, rr)
11134
11135	req, err = http.NewRequest(http.MethodGet, webClientSharePath+"/unknown", nil)
11136	assert.NoError(t, err)
11137	setJWTCookieForReq(req, token)
11138	rr = executeRequest(req)
11139	checkResponseCode(t, http.StatusNotFound, rr)
11140
11141	req, err = http.NewRequest(http.MethodGet, webClientSharePath+"/"+share.ShareID, nil)
11142	assert.NoError(t, err)
11143	setJWTCookieForReq(req, token)
11144	rr = executeRequest(req)
11145	checkResponseCode(t, http.StatusOK, rr)
11146
11147	req, err = http.NewRequest(http.MethodGet, webClientSharesPath+"?qlimit=a", nil)
11148	assert.NoError(t, err)
11149	setJWTCookieForReq(req, token)
11150	rr = executeRequest(req)
11151	checkResponseCode(t, http.StatusOK, rr)
11152
11153	req, err = http.NewRequest(http.MethodGet, webClientSharesPath+"?qlimit=1", nil)
11154	assert.NoError(t, err)
11155	setJWTCookieForReq(req, token)
11156	rr = executeRequest(req)
11157	checkResponseCode(t, http.StatusOK, rr)
11158
11159	err = os.RemoveAll(user.GetHomeDir())
11160	assert.NoError(t, err)
11161	_, err = httpdtest.RemoveUser(user, http.StatusOK)
11162	assert.NoError(t, err)
11163}
11164
11165func TestWebUserProfile(t *testing.T) {
11166	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
11167	assert.NoError(t, err)
11168
11169	csrfToken, err := getCSRFToken(httpBaseURL + webClientLoginPath)
11170	assert.NoError(t, err)
11171	token, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
11172	assert.NoError(t, err)
11173
11174	email := "user@user.com"
11175	description := "User"
11176
11177	form := make(url.Values)
11178	form.Set("allow_api_key_auth", "1")
11179	form.Set("email", email)
11180	form.Set("description", description)
11181	form.Set("public_keys", testPubKey)
11182	form.Add("public_keys", testPubKey1)
11183	// no csrf token
11184	req, err := http.NewRequest(http.MethodPost, webClientProfilePath, bytes.NewBuffer([]byte(form.Encode())))
11185	assert.NoError(t, err)
11186	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11187	setJWTCookieForReq(req, token)
11188	rr := executeRequest(req)
11189	checkResponseCode(t, http.StatusForbidden, rr)
11190	assert.Contains(t, rr.Body.String(), "unable to verify form token")
11191
11192	form.Set(csrfFormToken, csrfToken)
11193	req, _ = http.NewRequest(http.MethodPost, webClientProfilePath, bytes.NewBuffer([]byte(form.Encode())))
11194	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11195	setJWTCookieForReq(req, token)
11196	rr = executeRequest(req)
11197	checkResponseCode(t, http.StatusOK, rr)
11198	assert.Contains(t, rr.Body.String(), "Your profile has been successfully updated")
11199
11200	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
11201	assert.NoError(t, err)
11202	assert.True(t, user.Filters.AllowAPIKeyAuth)
11203	assert.Len(t, user.PublicKeys, 2)
11204	assert.Equal(t, email, user.Email)
11205	assert.Equal(t, description, user.Description)
11206
11207	// set an invalid email
11208	form.Set("email", "not an email")
11209	req, _ = http.NewRequest(http.MethodPost, webClientProfilePath, bytes.NewBuffer([]byte(form.Encode())))
11210	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11211	setJWTCookieForReq(req, token)
11212	rr = executeRequest(req)
11213	checkResponseCode(t, http.StatusOK, rr)
11214	assert.Contains(t, rr.Body.String(), "Validation error: email")
11215	// invalid public key
11216	form.Set("email", email)
11217	form.Set("public_keys", "invalid")
11218	req, _ = http.NewRequest(http.MethodPost, webClientProfilePath, bytes.NewBuffer([]byte(form.Encode())))
11219	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11220	setJWTCookieForReq(req, token)
11221	rr = executeRequest(req)
11222	checkResponseCode(t, http.StatusOK, rr)
11223	assert.Contains(t, rr.Body.String(), "Validation error: could not parse key")
11224	// now remove permissions
11225	form.Set("public_keys", testPubKey)
11226	user.Filters.WebClient = []string{sdk.WebClientAPIKeyAuthChangeDisabled}
11227	_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
11228	assert.NoError(t, err)
11229	token, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
11230	assert.NoError(t, err)
11231
11232	form.Set("allow_api_key_auth", "0")
11233	form.Set(csrfFormToken, csrfToken)
11234	req, _ = http.NewRequest(http.MethodPost, webClientProfilePath, bytes.NewBuffer([]byte(form.Encode())))
11235	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11236	setJWTCookieForReq(req, token)
11237	rr = executeRequest(req)
11238	checkResponseCode(t, http.StatusOK, rr)
11239	assert.Contains(t, rr.Body.String(), "Your profile has been successfully updated")
11240	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
11241	assert.NoError(t, err)
11242	assert.True(t, user.Filters.AllowAPIKeyAuth)
11243	assert.Len(t, user.PublicKeys, 1)
11244	assert.Equal(t, email, user.Email)
11245	assert.Equal(t, description, user.Description)
11246
11247	user.Filters.WebClient = []string{sdk.WebClientAPIKeyAuthChangeDisabled, sdk.WebClientPubKeyChangeDisabled}
11248	_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
11249	assert.NoError(t, err)
11250	token, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
11251	assert.NoError(t, err)
11252	form.Set("public_keys", testPubKey)
11253	form.Add("public_keys", testPubKey1)
11254	req, _ = http.NewRequest(http.MethodPost, webClientProfilePath, bytes.NewBuffer([]byte(form.Encode())))
11255	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11256	setJWTCookieForReq(req, token)
11257	rr = executeRequest(req)
11258	checkResponseCode(t, http.StatusOK, rr)
11259	assert.Contains(t, rr.Body.String(), "Your profile has been successfully updated")
11260	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
11261	assert.NoError(t, err)
11262	assert.True(t, user.Filters.AllowAPIKeyAuth)
11263	assert.Len(t, user.PublicKeys, 1)
11264	assert.Equal(t, email, user.Email)
11265	assert.Equal(t, description, user.Description)
11266
11267	user.Filters.WebClient = []string{sdk.WebClientAPIKeyAuthChangeDisabled, sdk.WebClientInfoChangeDisabled}
11268	_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
11269	assert.NoError(t, err)
11270	token, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
11271	assert.NoError(t, err)
11272	form.Set("email", "newemail@user.com")
11273	form.Set("description", "new description")
11274	req, _ = http.NewRequest(http.MethodPost, webClientProfilePath, bytes.NewBuffer([]byte(form.Encode())))
11275	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11276	setJWTCookieForReq(req, token)
11277	rr = executeRequest(req)
11278	checkResponseCode(t, http.StatusOK, rr)
11279	assert.Contains(t, rr.Body.String(), "Your profile has been successfully updated")
11280	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
11281	assert.NoError(t, err)
11282	assert.True(t, user.Filters.AllowAPIKeyAuth)
11283	assert.Len(t, user.PublicKeys, 2)
11284	assert.Equal(t, email, user.Email)
11285	assert.Equal(t, description, user.Description)
11286	// finally disable all profile permissions
11287	user.Filters.WebClient = []string{sdk.WebClientAPIKeyAuthChangeDisabled, sdk.WebClientInfoChangeDisabled,
11288		sdk.WebClientPubKeyChangeDisabled}
11289	_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
11290	assert.NoError(t, err)
11291	token, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
11292	assert.NoError(t, err)
11293	req, _ = http.NewRequest(http.MethodPost, webClientProfilePath, bytes.NewBuffer([]byte(form.Encode())))
11294	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11295	setJWTCookieForReq(req, token)
11296	rr = executeRequest(req)
11297	checkResponseCode(t, http.StatusForbidden, rr)
11298
11299	err = os.RemoveAll(user.GetHomeDir())
11300	assert.NoError(t, err)
11301	_, err = httpdtest.RemoveUser(user, http.StatusOK)
11302	assert.NoError(t, err)
11303
11304	form = make(url.Values)
11305	form.Set(csrfFormToken, csrfToken)
11306	req, _ = http.NewRequest(http.MethodPost, webClientProfilePath, bytes.NewBuffer([]byte(form.Encode())))
11307	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11308	setJWTCookieForReq(req, token)
11309	rr = executeRequest(req)
11310	checkResponseCode(t, http.StatusInternalServerError, rr)
11311}
11312
11313func TestWebAdminProfile(t *testing.T) {
11314	admin := getTestAdmin()
11315	admin.Username = altAdminUsername
11316	admin.Password = altAdminPassword
11317	admin, _, err := httpdtest.AddAdmin(admin, http.StatusCreated)
11318	assert.NoError(t, err)
11319	token, err := getJWTWebTokenFromTestServer(admin.Username, altAdminPassword)
11320	assert.NoError(t, err)
11321	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
11322	assert.NoError(t, err)
11323	req, err := http.NewRequest(http.MethodGet, webAdminProfilePath, nil)
11324	assert.NoError(t, err)
11325	setJWTCookieForReq(req, token)
11326	rr := executeRequest(req)
11327	checkResponseCode(t, http.StatusOK, rr)
11328
11329	form := make(url.Values)
11330	form.Set("allow_api_key_auth", "1")
11331	form.Set("email", "admin@example.com")
11332	form.Set("description", "admin desc")
11333	// no csrf token
11334	req, err = http.NewRequest(http.MethodPost, webAdminProfilePath, bytes.NewBuffer([]byte(form.Encode())))
11335	assert.NoError(t, err)
11336	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11337	setJWTCookieForReq(req, token)
11338	rr = executeRequest(req)
11339	checkResponseCode(t, http.StatusForbidden, rr)
11340	assert.Contains(t, rr.Body.String(), "unable to verify form token")
11341
11342	form.Set(csrfFormToken, csrfToken)
11343	req, _ = http.NewRequest(http.MethodPost, webAdminProfilePath, bytes.NewBuffer([]byte(form.Encode())))
11344	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11345	setJWTCookieForReq(req, token)
11346	rr = executeRequest(req)
11347	checkResponseCode(t, http.StatusOK, rr)
11348	assert.Contains(t, rr.Body.String(), "Your profile has been successfully updated")
11349
11350	admin, _, err = httpdtest.GetAdminByUsername(admin.Username, http.StatusOK)
11351	assert.NoError(t, err)
11352	assert.True(t, admin.Filters.AllowAPIKeyAuth)
11353	assert.Equal(t, "admin@example.com", admin.Email)
11354	assert.Equal(t, "admin desc", admin.Description)
11355
11356	form = make(url.Values)
11357	form.Set(csrfFormToken, csrfToken)
11358	req, _ = http.NewRequest(http.MethodPost, webAdminProfilePath, bytes.NewBuffer([]byte(form.Encode())))
11359	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11360	setJWTCookieForReq(req, token)
11361	rr = executeRequest(req)
11362	checkResponseCode(t, http.StatusOK, rr)
11363	assert.Contains(t, rr.Body.String(), "Your profile has been successfully updated")
11364
11365	admin, _, err = httpdtest.GetAdminByUsername(admin.Username, http.StatusOK)
11366	assert.NoError(t, err)
11367	assert.False(t, admin.Filters.AllowAPIKeyAuth)
11368	assert.Empty(t, admin.Email)
11369	assert.Empty(t, admin.Description)
11370
11371	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
11372	assert.NoError(t, err)
11373
11374	form = make(url.Values)
11375	form.Set(csrfFormToken, csrfToken)
11376	req, _ = http.NewRequest(http.MethodPost, webAdminProfilePath, bytes.NewBuffer([]byte(form.Encode())))
11377	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11378	setJWTCookieForReq(req, token)
11379	rr = executeRequest(req)
11380	checkResponseCode(t, http.StatusInternalServerError, rr)
11381}
11382
11383func TestWebAdminPwdChange(t *testing.T) {
11384	admin := getTestAdmin()
11385	admin.Username = altAdminUsername
11386	admin.Password = altAdminPassword
11387	admin, _, err := httpdtest.AddAdmin(admin, http.StatusCreated)
11388	assert.NoError(t, err)
11389
11390	token, err := getJWTWebTokenFromTestServer(admin.Username, altAdminPassword)
11391	assert.NoError(t, err)
11392	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
11393	assert.NoError(t, err)
11394	req, err := http.NewRequest(http.MethodGet, webChangeAdminPwdPath, nil)
11395	assert.NoError(t, err)
11396	setJWTCookieForReq(req, token)
11397	rr := executeRequest(req)
11398	checkResponseCode(t, http.StatusOK, rr)
11399	form := make(url.Values)
11400	form.Set("current_password", altAdminPassword)
11401	form.Set("new_password1", altAdminPassword)
11402	form.Set("new_password2", altAdminPassword)
11403	// no csrf token
11404	req, _ = http.NewRequest(http.MethodPost, webChangeAdminPwdPath, bytes.NewBuffer([]byte(form.Encode())))
11405	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11406	setJWTCookieForReq(req, token)
11407	rr = executeRequest(req)
11408	checkResponseCode(t, http.StatusForbidden, rr)
11409	assert.Contains(t, rr.Body.String(), "unable to verify form token")
11410
11411	form.Set(csrfFormToken, csrfToken)
11412	req, _ = http.NewRequest(http.MethodPost, webChangeAdminPwdPath, bytes.NewBuffer([]byte(form.Encode())))
11413	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11414	setJWTCookieForReq(req, token)
11415	rr = executeRequest(req)
11416	checkResponseCode(t, http.StatusOK, rr)
11417	assert.Contains(t, rr.Body.String(), "the new password must be different from the current one")
11418
11419	form.Set("new_password1", altAdminPassword+"1")
11420	form.Set("new_password2", altAdminPassword+"1")
11421	req, _ = http.NewRequest(http.MethodPost, webChangeAdminPwdPath, bytes.NewBuffer([]byte(form.Encode())))
11422	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11423	setJWTCookieForReq(req, token)
11424	rr = executeRequest(req)
11425	checkResponseCode(t, http.StatusFound, rr)
11426	assert.Equal(t, webLoginPath, rr.Header().Get("Location"))
11427
11428	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
11429	assert.NoError(t, err)
11430}
11431
11432func TestAPIKeysManagement(t *testing.T) {
11433	admin := getTestAdmin()
11434	admin.Username = altAdminUsername
11435	admin.Password = altAdminPassword
11436	admin, _, err := httpdtest.AddAdmin(admin, http.StatusCreated)
11437	assert.NoError(t, err)
11438	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
11439	assert.NoError(t, err)
11440	apiKey := dataprovider.APIKey{
11441		Name:  "test key",
11442		Scope: dataprovider.APIKeyScopeAdmin,
11443	}
11444	asJSON, err := json.Marshal(apiKey)
11445	assert.NoError(t, err)
11446	req, err := http.NewRequest(http.MethodPost, apiKeysPath, bytes.NewBuffer(asJSON))
11447	assert.NoError(t, err)
11448	setBearerForReq(req, token)
11449	rr := executeRequest(req)
11450	checkResponseCode(t, http.StatusCreated, rr)
11451	location := rr.Header().Get("Location")
11452	assert.NotEmpty(t, location)
11453	objectID := rr.Header().Get("X-Object-ID")
11454	assert.NotEmpty(t, objectID)
11455	assert.Equal(t, fmt.Sprintf("%v/%v", apiKeysPath, objectID), location)
11456	apiKey.KeyID = objectID
11457	response := make(map[string]string)
11458	err = json.Unmarshal(rr.Body.Bytes(), &response)
11459	assert.NoError(t, err)
11460	key := response["key"]
11461	assert.NotEmpty(t, key)
11462	assert.True(t, strings.HasPrefix(key, apiKey.KeyID+"."))
11463
11464	req, err = http.NewRequest(http.MethodGet, location, nil)
11465	assert.NoError(t, err)
11466	setBearerForReq(req, token)
11467	rr = executeRequest(req)
11468	checkResponseCode(t, http.StatusOK, rr)
11469	var keyGet dataprovider.APIKey
11470	err = json.Unmarshal(rr.Body.Bytes(), &keyGet)
11471	assert.NoError(t, err)
11472	assert.Empty(t, keyGet.Key)
11473	assert.Equal(t, apiKey.KeyID, keyGet.KeyID)
11474	assert.Equal(t, apiKey.Scope, keyGet.Scope)
11475	assert.Equal(t, apiKey.Name, keyGet.Name)
11476	assert.Equal(t, int64(0), keyGet.ExpiresAt)
11477	assert.Equal(t, int64(0), keyGet.LastUseAt)
11478	assert.Greater(t, keyGet.CreatedAt, int64(0))
11479	assert.Greater(t, keyGet.UpdatedAt, int64(0))
11480	assert.Empty(t, keyGet.Description)
11481	assert.Empty(t, keyGet.User)
11482	assert.Empty(t, keyGet.Admin)
11483
11484	// API key is not enabled for the admin user so this request should fail
11485	req, err = http.NewRequest(http.MethodGet, versionPath, nil)
11486	assert.NoError(t, err)
11487	setAPIKeyForReq(req, key, admin.Username)
11488	rr = executeRequest(req)
11489	checkResponseCode(t, http.StatusUnauthorized, rr)
11490	assert.Contains(t, rr.Body.String(), "the admin associated with the provided api key cannot be authenticated")
11491
11492	admin.Filters.AllowAPIKeyAuth = true
11493	admin, _, err = httpdtest.UpdateAdmin(admin, http.StatusOK)
11494	assert.NoError(t, err)
11495	req, err = http.NewRequest(http.MethodGet, versionPath, nil)
11496	assert.NoError(t, err)
11497	setAPIKeyForReq(req, key, admin.Username)
11498	rr = executeRequest(req)
11499	checkResponseCode(t, http.StatusOK, rr)
11500
11501	req, err = http.NewRequest(http.MethodGet, versionPath, nil)
11502	assert.NoError(t, err)
11503	setAPIKeyForReq(req, key, admin.Username+"1")
11504	rr = executeRequest(req)
11505	checkResponseCode(t, http.StatusUnauthorized, rr)
11506
11507	req, err = http.NewRequest(http.MethodGet, versionPath, nil)
11508	assert.NoError(t, err)
11509	setAPIKeyForReq(req, key, "")
11510	rr = executeRequest(req)
11511	checkResponseCode(t, http.StatusUnauthorized, rr)
11512
11513	// now associate the key directly to the admin
11514	apiKey.Admin = admin.Username
11515	apiKey.Description = "test description"
11516	asJSON, err = json.Marshal(apiKey)
11517	assert.NoError(t, err)
11518	req, err = http.NewRequest(http.MethodPut, location, bytes.NewBuffer(asJSON))
11519	assert.NoError(t, err)
11520	setBearerForReq(req, token)
11521	rr = executeRequest(req)
11522	checkResponseCode(t, http.StatusOK, rr)
11523
11524	req, err = http.NewRequest(http.MethodGet, apiKeysPath, nil)
11525	assert.NoError(t, err)
11526	setBearerForReq(req, token)
11527	rr = executeRequest(req)
11528	checkResponseCode(t, http.StatusOK, rr)
11529	var keys []dataprovider.APIKey
11530	err = json.Unmarshal(rr.Body.Bytes(), &keys)
11531	assert.NoError(t, err)
11532	if assert.GreaterOrEqual(t, len(keys), 1) {
11533		found := false
11534		for _, k := range keys {
11535			if k.KeyID == apiKey.KeyID {
11536				found = true
11537				assert.Empty(t, k.Key)
11538				assert.Equal(t, apiKey.Scope, k.Scope)
11539				assert.Equal(t, apiKey.Name, k.Name)
11540				assert.Equal(t, int64(0), k.ExpiresAt)
11541				assert.Greater(t, k.LastUseAt, int64(0))
11542				assert.Equal(t, k.CreatedAt, keyGet.CreatedAt)
11543				assert.Greater(t, k.UpdatedAt, keyGet.UpdatedAt)
11544				assert.Equal(t, apiKey.Description, k.Description)
11545				assert.Empty(t, k.User)
11546				assert.Equal(t, admin.Username, k.Admin)
11547			}
11548		}
11549		assert.True(t, found)
11550	}
11551	req, err = http.NewRequest(http.MethodGet, versionPath, nil)
11552	assert.NoError(t, err)
11553	setAPIKeyForReq(req, key, "")
11554	rr = executeRequest(req)
11555	checkResponseCode(t, http.StatusOK, rr)
11556	// invalid API keys
11557	req, err = http.NewRequest(http.MethodGet, versionPath, nil)
11558	assert.NoError(t, err)
11559	setAPIKeyForReq(req, key+"invalid", "")
11560	rr = executeRequest(req)
11561	checkResponseCode(t, http.StatusUnauthorized, rr)
11562	assert.Contains(t, rr.Body.String(), "the provided api key cannot be authenticated")
11563	req, err = http.NewRequest(http.MethodGet, versionPath, nil)
11564	assert.NoError(t, err)
11565	setAPIKeyForReq(req, "invalid", "")
11566	rr = executeRequest(req)
11567	checkResponseCode(t, http.StatusBadRequest, rr)
11568	// using an API key we cannot modify/get API keys
11569	req, err = http.NewRequest(http.MethodPut, location, bytes.NewBuffer(asJSON))
11570	assert.NoError(t, err)
11571	setAPIKeyForReq(req, key, "")
11572	rr = executeRequest(req)
11573	checkResponseCode(t, http.StatusForbidden, rr)
11574
11575	req, err = http.NewRequest(http.MethodGet, location, nil)
11576	assert.NoError(t, err)
11577	setAPIKeyForReq(req, key, "")
11578	rr = executeRequest(req)
11579	checkResponseCode(t, http.StatusForbidden, rr)
11580
11581	admin.Filters.AllowList = []string{"172.16.18.0/24"}
11582	admin, _, err = httpdtest.UpdateAdmin(admin, http.StatusOK)
11583	assert.NoError(t, err)
11584	req, err = http.NewRequest(http.MethodGet, versionPath, nil)
11585	assert.NoError(t, err)
11586	setAPIKeyForReq(req, key, "")
11587	rr = executeRequest(req)
11588	checkResponseCode(t, http.StatusUnauthorized, rr)
11589
11590	req, err = http.NewRequest(http.MethodDelete, location, nil)
11591	assert.NoError(t, err)
11592	setBearerForReq(req, token)
11593	rr = executeRequest(req)
11594	checkResponseCode(t, http.StatusOK, rr)
11595
11596	req, err = http.NewRequest(http.MethodGet, versionPath, nil)
11597	assert.NoError(t, err)
11598	setAPIKeyForReq(req, key, "")
11599	rr = executeRequest(req)
11600	checkResponseCode(t, http.StatusBadRequest, rr)
11601	assert.Contains(t, rr.Body.String(), "the provided api key is not valid")
11602
11603	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
11604	assert.NoError(t, err)
11605}
11606
11607func TestAPIKeySearch(t *testing.T) {
11608	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
11609	assert.NoError(t, err)
11610	apiKey := dataprovider.APIKey{
11611		Scope: dataprovider.APIKeyScopeAdmin,
11612	}
11613	for i := 1; i < 5; i++ {
11614		apiKey.Name = fmt.Sprintf("testapikey%v", i)
11615		asJSON, err := json.Marshal(apiKey)
11616		assert.NoError(t, err)
11617		req, err := http.NewRequest(http.MethodPost, apiKeysPath, bytes.NewBuffer(asJSON))
11618		assert.NoError(t, err)
11619		setBearerForReq(req, token)
11620		rr := executeRequest(req)
11621		checkResponseCode(t, http.StatusCreated, rr)
11622	}
11623
11624	req, err := http.NewRequest(http.MethodGet, apiKeysPath+"?limit=1&order=ASC", nil)
11625	assert.NoError(t, err)
11626	setBearerForReq(req, token)
11627	rr := executeRequest(req)
11628	checkResponseCode(t, http.StatusOK, rr)
11629	var keys []dataprovider.APIKey
11630	err = json.Unmarshal(rr.Body.Bytes(), &keys)
11631	assert.NoError(t, err)
11632	assert.Len(t, keys, 1)
11633	firstKey := keys[0]
11634
11635	req, err = http.NewRequest(http.MethodGet, apiKeysPath+"?limit=1&order=DESC", nil)
11636	assert.NoError(t, err)
11637	setBearerForReq(req, token)
11638	rr = executeRequest(req)
11639	checkResponseCode(t, http.StatusOK, rr)
11640	keys = nil
11641	err = json.Unmarshal(rr.Body.Bytes(), &keys)
11642	assert.NoError(t, err)
11643	if assert.Len(t, keys, 1) {
11644		assert.NotEqual(t, firstKey.KeyID, keys[0].KeyID)
11645	}
11646
11647	req, err = http.NewRequest(http.MethodGet, apiKeysPath+"?limit=1&offset=100", nil)
11648	assert.NoError(t, err)
11649	setBearerForReq(req, token)
11650	rr = executeRequest(req)
11651	checkResponseCode(t, http.StatusOK, rr)
11652	keys = nil
11653	err = json.Unmarshal(rr.Body.Bytes(), &keys)
11654	assert.NoError(t, err)
11655	assert.Len(t, keys, 0)
11656
11657	req, err = http.NewRequest(http.MethodGet, apiKeysPath+"?limit=a", nil)
11658	assert.NoError(t, err)
11659	setBearerForReq(req, token)
11660	rr = executeRequest(req)
11661	checkResponseCode(t, http.StatusBadRequest, rr)
11662
11663	req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v/%v", apiKeysPath, "missingid"), nil)
11664	assert.NoError(t, err)
11665	setBearerForReq(req, token)
11666	rr = executeRequest(req)
11667	checkResponseCode(t, http.StatusNotFound, rr)
11668
11669	req, err = http.NewRequest(http.MethodGet, apiKeysPath, nil)
11670	assert.NoError(t, err)
11671	setBearerForReq(req, token)
11672	rr = executeRequest(req)
11673	checkResponseCode(t, http.StatusOK, rr)
11674	keys = nil
11675	err = json.Unmarshal(rr.Body.Bytes(), &keys)
11676	assert.NoError(t, err)
11677	counter := 0
11678	for _, k := range keys {
11679		if strings.HasPrefix(k.Name, "testapikey") {
11680			req, err = http.NewRequest(http.MethodDelete, fmt.Sprintf("%v/%v", apiKeysPath, k.KeyID), nil)
11681			assert.NoError(t, err)
11682			setBearerForReq(req, token)
11683			rr = executeRequest(req)
11684			checkResponseCode(t, http.StatusOK, rr)
11685			counter++
11686		}
11687	}
11688	assert.Equal(t, 4, counter)
11689}
11690
11691func TestAPIKeyErrors(t *testing.T) {
11692	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
11693	assert.NoError(t, err)
11694	apiKey := dataprovider.APIKey{
11695		Name:  "testkey",
11696		Scope: dataprovider.APIKeyScopeUser,
11697	}
11698	asJSON, err := json.Marshal(apiKey)
11699	assert.NoError(t, err)
11700	req, err := http.NewRequest(http.MethodPost, apiKeysPath, bytes.NewBuffer(asJSON))
11701	assert.NoError(t, err)
11702	setBearerForReq(req, token)
11703	rr := executeRequest(req)
11704	checkResponseCode(t, http.StatusCreated, rr)
11705	location := rr.Header().Get("Location")
11706	assert.NotEmpty(t, location)
11707
11708	// invalid API scope
11709	apiKey.Scope = 1000
11710	asJSON, err = json.Marshal(apiKey)
11711	assert.NoError(t, err)
11712	req, err = http.NewRequest(http.MethodPost, apiKeysPath, bytes.NewBuffer(asJSON))
11713	assert.NoError(t, err)
11714	setBearerForReq(req, token)
11715	rr = executeRequest(req)
11716	checkResponseCode(t, http.StatusBadRequest, rr)
11717
11718	req, err = http.NewRequest(http.MethodPut, location, bytes.NewBuffer(asJSON))
11719	assert.NoError(t, err)
11720	setBearerForReq(req, token)
11721	rr = executeRequest(req)
11722	checkResponseCode(t, http.StatusBadRequest, rr)
11723
11724	// invalid JSON
11725	req, err = http.NewRequest(http.MethodPost, apiKeysPath, bytes.NewBuffer([]byte(`invalid JSON`)))
11726	assert.NoError(t, err)
11727	setBearerForReq(req, token)
11728	rr = executeRequest(req)
11729	checkResponseCode(t, http.StatusBadRequest, rr)
11730
11731	req, err = http.NewRequest(http.MethodPut, location, bytes.NewBuffer([]byte(`invalid JSON`)))
11732	assert.NoError(t, err)
11733	setBearerForReq(req, token)
11734	rr = executeRequest(req)
11735	checkResponseCode(t, http.StatusBadRequest, rr)
11736
11737	req, err = http.NewRequest(http.MethodDelete, location, nil)
11738	assert.NoError(t, err)
11739	setBearerForReq(req, token)
11740	rr = executeRequest(req)
11741	checkResponseCode(t, http.StatusOK, rr)
11742
11743	req, err = http.NewRequest(http.MethodDelete, location, nil)
11744	assert.NoError(t, err)
11745	setBearerForReq(req, token)
11746	rr = executeRequest(req)
11747	checkResponseCode(t, http.StatusNotFound, rr)
11748
11749	req, err = http.NewRequest(http.MethodPut, location, bytes.NewBuffer(asJSON))
11750	assert.NoError(t, err)
11751	setBearerForReq(req, token)
11752	rr = executeRequest(req)
11753	checkResponseCode(t, http.StatusNotFound, rr)
11754}
11755
11756func TestAPIKeyOnDeleteCascade(t *testing.T) {
11757	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
11758	assert.NoError(t, err)
11759	admin := getTestAdmin()
11760	admin.Username = altAdminUsername
11761	admin.Password = altAdminPassword
11762	admin, _, err = httpdtest.AddAdmin(admin, http.StatusCreated)
11763	assert.NoError(t, err)
11764
11765	apiKey := dataprovider.APIKey{
11766		Name:  "user api key",
11767		Scope: dataprovider.APIKeyScopeUser,
11768		User:  user.Username,
11769	}
11770
11771	apiKey, _, err = httpdtest.AddAPIKey(apiKey, http.StatusCreated)
11772	assert.NoError(t, err)
11773
11774	req, err := http.NewRequest(http.MethodGet, userDirsPath, nil)
11775	assert.NoError(t, err)
11776	setAPIKeyForReq(req, apiKey.Key, "")
11777	rr := executeRequest(req)
11778	checkResponseCode(t, http.StatusUnauthorized, rr)
11779
11780	user.Filters.AllowAPIKeyAuth = true
11781	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
11782	assert.NoError(t, err)
11783	req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
11784	assert.NoError(t, err)
11785	setAPIKeyForReq(req, apiKey.Key, "")
11786	rr = executeRequest(req)
11787	checkResponseCode(t, http.StatusOK, rr)
11788	var contents []map[string]interface{}
11789	err = json.NewDecoder(rr.Body).Decode(&contents)
11790	assert.NoError(t, err)
11791	assert.Len(t, contents, 0)
11792
11793	_, err = httpdtest.RemoveUser(user, http.StatusOK)
11794	assert.NoError(t, err)
11795	err = os.RemoveAll(user.GetHomeDir())
11796	assert.NoError(t, err)
11797
11798	_, _, err = httpdtest.GetAPIKeyByID(apiKey.KeyID, http.StatusNotFound)
11799	assert.NoError(t, err)
11800
11801	apiKey.User = ""
11802	apiKey.Admin = admin.Username
11803	apiKey.Scope = dataprovider.APIKeyScopeAdmin
11804
11805	apiKey, _, err = httpdtest.AddAPIKey(apiKey, http.StatusCreated)
11806	assert.NoError(t, err)
11807
11808	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
11809	assert.NoError(t, err)
11810
11811	_, _, err = httpdtest.GetAPIKeyByID(apiKey.KeyID, http.StatusNotFound)
11812	assert.NoError(t, err)
11813}
11814
11815func TestBasicWebUsersMock(t *testing.T) {
11816	token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
11817	assert.NoError(t, err)
11818	user := getTestUser()
11819	userAsJSON := getUserAsJSON(t, user)
11820	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
11821	setBearerForReq(req, token)
11822	rr := executeRequest(req)
11823	checkResponseCode(t, http.StatusCreated, rr)
11824	err = render.DecodeJSON(rr.Body, &user)
11825	assert.NoError(t, err)
11826	user1 := getTestUser()
11827	user1.Username += "1"
11828	user1AsJSON := getUserAsJSON(t, user1)
11829	req, _ = http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(user1AsJSON))
11830	setBearerForReq(req, token)
11831	rr = executeRequest(req)
11832	checkResponseCode(t, http.StatusCreated, rr)
11833	err = render.DecodeJSON(rr.Body, &user1)
11834	assert.NoError(t, err)
11835	webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
11836	assert.NoError(t, err)
11837	req, _ = http.NewRequest(http.MethodGet, webUsersPath, nil)
11838	setJWTCookieForReq(req, webToken)
11839	rr = executeRequest(req)
11840	checkResponseCode(t, http.StatusOK, rr)
11841	req, _ = http.NewRequest(http.MethodGet, webUsersPath+"?qlimit=a", nil)
11842	setJWTCookieForReq(req, webToken)
11843	rr = executeRequest(req)
11844	checkResponseCode(t, http.StatusOK, rr)
11845	req, _ = http.NewRequest(http.MethodGet, webUsersPath+"?qlimit=1", nil)
11846	setJWTCookieForReq(req, webToken)
11847	rr = executeRequest(req)
11848	checkResponseCode(t, http.StatusOK, rr)
11849	req, _ = http.NewRequest(http.MethodGet, webUserPath, nil)
11850	setJWTCookieForReq(req, webToken)
11851	rr = executeRequest(req)
11852	checkResponseCode(t, http.StatusOK, rr)
11853	req, _ = http.NewRequest(http.MethodGet, path.Join(webUserPath, user.Username), nil)
11854	setJWTCookieForReq(req, webToken)
11855	rr = executeRequest(req)
11856	checkResponseCode(t, http.StatusOK, rr)
11857	req, _ = http.NewRequest(http.MethodGet, webUserPath+"/0", nil)
11858	setJWTCookieForReq(req, webToken)
11859	rr = executeRequest(req)
11860	checkResponseCode(t, http.StatusNotFound, rr)
11861	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
11862	assert.NoError(t, err)
11863	form := make(url.Values)
11864	form.Set("username", user.Username)
11865	form.Set(csrfFormToken, csrfToken)
11866	b, contentType, _ := getMultipartFormData(form, "", "")
11867	req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
11868	setJWTCookieForReq(req, webToken)
11869	req.Header.Set("Content-Type", contentType)
11870	rr = executeRequest(req)
11871	checkResponseCode(t, http.StatusOK, rr)
11872	b, contentType, _ = getMultipartFormData(form, "", "")
11873	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
11874	setJWTCookieForReq(req, webToken)
11875	req.Header.Set("Content-Type", contentType)
11876	rr = executeRequest(req)
11877	checkResponseCode(t, http.StatusOK, rr)
11878	b, contentType, _ = getMultipartFormData(form, "", "")
11879	req, _ = http.NewRequest(http.MethodPost, webUserPath+"/0", &b)
11880	setJWTCookieForReq(req, webToken)
11881	req.Header.Set("Content-Type", contentType)
11882	rr = executeRequest(req)
11883	checkResponseCode(t, http.StatusNotFound, rr)
11884	req, _ = http.NewRequest(http.MethodPost, webUserPath+"/aaa", &b)
11885	setJWTCookieForReq(req, webToken)
11886	req.Header.Set("Content-Type", contentType)
11887	rr = executeRequest(req)
11888	checkResponseCode(t, http.StatusNotFound, rr)
11889	req, _ = http.NewRequest(http.MethodDelete, path.Join(webUserPath, user.Username), nil)
11890	setJWTCookieForReq(req, webToken)
11891	rr = executeRequest(req)
11892	checkResponseCode(t, http.StatusForbidden, rr)
11893	assert.Contains(t, rr.Body.String(), "Invalid token")
11894	req, _ = http.NewRequest(http.MethodDelete, path.Join(webUserPath, user.Username), nil)
11895	setJWTCookieForReq(req, webToken)
11896	setCSRFHeaderForReq(req, csrfToken)
11897	rr = executeRequest(req)
11898	checkResponseCode(t, http.StatusOK, rr)
11899	req, _ = http.NewRequest(http.MethodDelete, path.Join(webUserPath, user1.Username), nil)
11900	setJWTCookieForReq(req, webToken)
11901	setCSRFHeaderForReq(req, csrfToken)
11902	rr = executeRequest(req)
11903	checkResponseCode(t, http.StatusOK, rr)
11904}
11905
11906func TestRenderDefenderPageMock(t *testing.T) {
11907	token, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
11908	assert.NoError(t, err)
11909	req, err := http.NewRequest(http.MethodGet, webDefenderPath, nil)
11910	assert.NoError(t, err)
11911	setJWTCookieForReq(req, token)
11912	rr := executeRequest(req)
11913	checkResponseCode(t, http.StatusOK, rr)
11914	assert.Contains(t, rr.Body.String(), "View and manage blocklist")
11915}
11916
11917func TestWebAdminBasicMock(t *testing.T) {
11918	token, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
11919	assert.NoError(t, err)
11920	admin := getTestAdmin()
11921	admin.Username = altAdminUsername
11922	admin.Password = altAdminPassword
11923	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
11924	assert.NoError(t, err)
11925	form := make(url.Values)
11926	form.Set("username", admin.Username)
11927	form.Set("password", "")
11928	form.Set("status", "1")
11929	form.Set("permissions", "*")
11930	form.Set("description", admin.Description)
11931	req, _ := http.NewRequest(http.MethodPost, webAdminPath, bytes.NewBuffer([]byte(form.Encode())))
11932	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11933	setJWTCookieForReq(req, token)
11934	rr := executeRequest(req)
11935	checkResponseCode(t, http.StatusForbidden, rr)
11936	assert.Contains(t, rr.Body.String(), "unable to verify form token")
11937
11938	form.Set(csrfFormToken, csrfToken)
11939	form.Set("status", "a")
11940	req, _ = http.NewRequest(http.MethodPost, webAdminPath, bytes.NewBuffer([]byte(form.Encode())))
11941	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11942	setJWTCookieForReq(req, token)
11943	rr = executeRequest(req)
11944	checkResponseCode(t, http.StatusOK, rr)
11945
11946	form.Set("status", "1")
11947	req, _ = http.NewRequest(http.MethodPost, webAdminPath, bytes.NewBuffer([]byte(form.Encode())))
11948	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11949	setJWTCookieForReq(req, token)
11950	rr = executeRequest(req)
11951	checkResponseCode(t, http.StatusOK, rr)
11952
11953	form.Set("password", admin.Password)
11954	req, _ = http.NewRequest(http.MethodPost, webAdminPath, bytes.NewBuffer([]byte(form.Encode())))
11955	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11956	setJWTCookieForReq(req, token)
11957	rr = executeRequest(req)
11958	checkResponseCode(t, http.StatusSeeOther, rr)
11959
11960	// add TOTP config
11961	configName, _, secret, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], altAdminUsername)
11962	assert.NoError(t, err)
11963	altToken, err := getJWTWebTokenFromTestServer(altAdminUsername, altAdminPassword)
11964	assert.NoError(t, err)
11965	adminTOTPConfig := dataprovider.TOTPConfig{
11966		Enabled:    true,
11967		ConfigName: configName,
11968		Secret:     kms.NewPlainSecret(secret),
11969	}
11970	asJSON, err := json.Marshal(adminTOTPConfig)
11971	assert.NoError(t, err)
11972	// no CSRF token
11973	req, err = http.NewRequest(http.MethodPost, webAdminTOTPSavePath, bytes.NewBuffer(asJSON))
11974	assert.NoError(t, err)
11975	setJWTCookieForReq(req, altToken)
11976	rr = executeRequest(req)
11977	checkResponseCode(t, http.StatusForbidden, rr)
11978	assert.Contains(t, rr.Body.String(), "Invalid token")
11979
11980	req, err = http.NewRequest(http.MethodPost, webAdminTOTPSavePath, bytes.NewBuffer(asJSON))
11981	assert.NoError(t, err)
11982	setJWTCookieForReq(req, altToken)
11983	setCSRFHeaderForReq(req, csrfToken)
11984	rr = executeRequest(req)
11985	checkResponseCode(t, http.StatusOK, rr)
11986
11987	admin, _, err = httpdtest.GetAdminByUsername(altAdminUsername, http.StatusOK)
11988	assert.NoError(t, err)
11989	assert.True(t, admin.Filters.TOTPConfig.Enabled)
11990	secretPayload := admin.Filters.TOTPConfig.Secret.GetPayload()
11991	assert.NotEmpty(t, secretPayload)
11992
11993	adminTOTPConfig = dataprovider.TOTPConfig{
11994		Enabled:    true,
11995		ConfigName: configName,
11996		Secret:     kms.NewEmptySecret(),
11997	}
11998	asJSON, err = json.Marshal(adminTOTPConfig)
11999	assert.NoError(t, err)
12000	req, err = http.NewRequest(http.MethodPost, webAdminTOTPSavePath, bytes.NewBuffer(asJSON))
12001	assert.NoError(t, err)
12002	setJWTCookieForReq(req, altToken)
12003	setCSRFHeaderForReq(req, csrfToken)
12004	rr = executeRequest(req)
12005	checkResponseCode(t, http.StatusOK, rr)
12006
12007	admin, _, err = httpdtest.GetAdminByUsername(altAdminUsername, http.StatusOK)
12008	assert.NoError(t, err)
12009	assert.True(t, admin.Filters.TOTPConfig.Enabled)
12010	assert.Equal(t, secretPayload, admin.Filters.TOTPConfig.Secret.GetPayload())
12011
12012	adminTOTPConfig = dataprovider.TOTPConfig{
12013		Enabled:    true,
12014		ConfigName: configName,
12015		Secret:     nil,
12016	}
12017	asJSON, err = json.Marshal(adminTOTPConfig)
12018	assert.NoError(t, err)
12019	req, err = http.NewRequest(http.MethodPost, webAdminTOTPSavePath, bytes.NewBuffer(asJSON))
12020	assert.NoError(t, err)
12021	setJWTCookieForReq(req, altToken)
12022	setCSRFHeaderForReq(req, csrfToken)
12023	rr = executeRequest(req)
12024	checkResponseCode(t, http.StatusOK, rr)
12025
12026	req, _ = http.NewRequest(http.MethodGet, webAdminsPath+"?qlimit=a", nil)
12027	setJWTCookieForReq(req, token)
12028	rr = executeRequest(req)
12029	checkResponseCode(t, http.StatusOK, rr)
12030	req, _ = http.NewRequest(http.MethodGet, webAdminsPath+"?qlimit=1", nil)
12031	setJWTCookieForReq(req, token)
12032	rr = executeRequest(req)
12033	checkResponseCode(t, http.StatusOK, rr)
12034
12035	req, _ = http.NewRequest(http.MethodGet, webAdminPath, nil)
12036	setJWTCookieForReq(req, token)
12037	rr = executeRequest(req)
12038	checkResponseCode(t, http.StatusOK, rr)
12039
12040	form.Set("password", "")
12041	req, _ = http.NewRequest(http.MethodPost, path.Join(webAdminPath, altAdminUsername), bytes.NewBuffer([]byte(form.Encode())))
12042	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
12043	setJWTCookieForReq(req, token)
12044	rr = executeRequest(req)
12045	checkResponseCode(t, http.StatusSeeOther, rr)
12046
12047	form.Set(csrfFormToken, "invalid csrf")
12048	req, _ = http.NewRequest(http.MethodPost, path.Join(webAdminPath, altAdminUsername), bytes.NewBuffer([]byte(form.Encode())))
12049	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
12050	setJWTCookieForReq(req, token)
12051	rr = executeRequest(req)
12052	checkResponseCode(t, http.StatusForbidden, rr)
12053	assert.Contains(t, rr.Body.String(), "unable to verify form token")
12054
12055	form.Set(csrfFormToken, csrfToken)
12056	form.Set("email", "not-an-email")
12057	req, _ = http.NewRequest(http.MethodPost, path.Join(webAdminPath, altAdminUsername), bytes.NewBuffer([]byte(form.Encode())))
12058	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
12059	setJWTCookieForReq(req, token)
12060	rr = executeRequest(req)
12061	checkResponseCode(t, http.StatusOK, rr)
12062
12063	form.Set("email", "")
12064	form.Set("status", "b")
12065	req, _ = http.NewRequest(http.MethodPost, path.Join(webAdminPath, altAdminUsername), bytes.NewBuffer([]byte(form.Encode())))
12066	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
12067	setJWTCookieForReq(req, token)
12068	rr = executeRequest(req)
12069	checkResponseCode(t, http.StatusOK, rr)
12070
12071	form.Set("email", "admin@example.com")
12072	form.Set("status", "0")
12073	req, _ = http.NewRequest(http.MethodPost, path.Join(webAdminPath, altAdminUsername), bytes.NewBuffer([]byte(form.Encode())))
12074	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
12075	setJWTCookieForReq(req, token)
12076	rr = executeRequest(req)
12077	checkResponseCode(t, http.StatusSeeOther, rr)
12078
12079	admin, _, err = httpdtest.GetAdminByUsername(altAdminUsername, http.StatusOK)
12080	assert.NoError(t, err)
12081	assert.True(t, admin.Filters.TOTPConfig.Enabled)
12082	assert.Equal(t, "admin@example.com", admin.Email)
12083	assert.Equal(t, 0, admin.Status)
12084
12085	req, _ = http.NewRequest(http.MethodPost, path.Join(webAdminPath, altAdminUsername+"1"), bytes.NewBuffer([]byte(form.Encode())))
12086	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
12087	setJWTCookieForReq(req, token)
12088	rr = executeRequest(req)
12089	checkResponseCode(t, http.StatusNotFound, rr)
12090
12091	req, _ = http.NewRequest(http.MethodGet, path.Join(webAdminPath, altAdminUsername), nil)
12092	setJWTCookieForReq(req, token)
12093	rr = executeRequest(req)
12094	checkResponseCode(t, http.StatusOK, rr)
12095
12096	req, _ = http.NewRequest(http.MethodGet, path.Join(webAdminPath, altAdminUsername+"1"), nil)
12097	setJWTCookieForReq(req, token)
12098	rr = executeRequest(req)
12099	checkResponseCode(t, http.StatusNotFound, rr)
12100
12101	req, _ = http.NewRequest(http.MethodDelete, path.Join(webAdminPath, altAdminUsername), nil)
12102	setJWTCookieForReq(req, token)
12103	setCSRFHeaderForReq(req, csrfToken)
12104	rr = executeRequest(req)
12105	checkResponseCode(t, http.StatusOK, rr)
12106
12107	_, err = httpdtest.RemoveAdmin(admin, http.StatusNotFound)
12108	assert.NoError(t, err)
12109
12110	req, _ = http.NewRequest(http.MethodDelete, path.Join(webAdminPath, defaultTokenAuthUser), nil)
12111	setJWTCookieForReq(req, token)
12112	setCSRFHeaderForReq(req, csrfToken)
12113	rr = executeRequest(req)
12114	checkResponseCode(t, http.StatusBadRequest, rr)
12115	assert.Contains(t, rr.Body.String(), "you cannot delete yourself")
12116
12117	req, _ = http.NewRequest(http.MethodDelete, path.Join(webAdminPath, defaultTokenAuthUser), nil)
12118	setJWTCookieForReq(req, token)
12119	rr = executeRequest(req)
12120	checkResponseCode(t, http.StatusForbidden, rr)
12121	assert.Contains(t, rr.Body.String(), "Invalid token")
12122}
12123
12124func TestWebAdminPermissions(t *testing.T) {
12125	admin := getTestAdmin()
12126	admin.Username = altAdminUsername
12127	admin.Password = altAdminPassword
12128	admin.Permissions = []string{dataprovider.PermAdminAddUsers}
12129	_, _, err := httpdtest.AddAdmin(admin, http.StatusCreated)
12130	assert.NoError(t, err)
12131
12132	token, err := getJWTWebToken(altAdminUsername, altAdminPassword)
12133	require.NoError(t, err)
12134
12135	req, err := http.NewRequest(http.MethodGet, httpBaseURL+webUserPath, nil)
12136	assert.NoError(t, err)
12137	setJWTCookieForReq(req, token)
12138	resp, err := httpclient.GetHTTPClient().Do(req)
12139	require.NoError(t, err)
12140	defer resp.Body.Close()
12141	assert.Equal(t, http.StatusOK, resp.StatusCode)
12142
12143	req, err = http.NewRequest(http.MethodGet, httpBaseURL+path.Join(webUserPath, "auser"), nil)
12144	assert.NoError(t, err)
12145	setJWTCookieForReq(req, token)
12146	resp, err = httpclient.GetHTTPClient().Do(req)
12147	require.NoError(t, err)
12148	defer resp.Body.Close()
12149	assert.Equal(t, http.StatusForbidden, resp.StatusCode)
12150
12151	req, err = http.NewRequest(http.MethodGet, httpBaseURL+webFolderPath, nil)
12152	assert.NoError(t, err)
12153	setJWTCookieForReq(req, token)
12154	resp, err = httpclient.GetHTTPClient().Do(req)
12155	require.NoError(t, err)
12156	defer resp.Body.Close()
12157	assert.Equal(t, http.StatusOK, resp.StatusCode)
12158
12159	req, err = http.NewRequest(http.MethodGet, httpBaseURL+webStatusPath, nil)
12160	assert.NoError(t, err)
12161	setJWTCookieForReq(req, token)
12162	resp, err = httpclient.GetHTTPClient().Do(req)
12163	require.NoError(t, err)
12164	defer resp.Body.Close()
12165	assert.Equal(t, http.StatusForbidden, resp.StatusCode)
12166
12167	req, err = http.NewRequest(http.MethodGet, httpBaseURL+webConnectionsPath, nil)
12168	assert.NoError(t, err)
12169	setJWTCookieForReq(req, token)
12170	resp, err = httpclient.GetHTTPClient().Do(req)
12171	require.NoError(t, err)
12172	defer resp.Body.Close()
12173	assert.Equal(t, http.StatusForbidden, resp.StatusCode)
12174
12175	req, err = http.NewRequest(http.MethodGet, httpBaseURL+webAdminPath, nil)
12176	assert.NoError(t, err)
12177	setJWTCookieForReq(req, token)
12178	resp, err = httpclient.GetHTTPClient().Do(req)
12179	require.NoError(t, err)
12180	defer resp.Body.Close()
12181	assert.Equal(t, http.StatusForbidden, resp.StatusCode)
12182
12183	req, err = http.NewRequest(http.MethodGet, httpBaseURL+path.Join(webAdminPath, "a"), nil)
12184	assert.NoError(t, err)
12185	setJWTCookieForReq(req, token)
12186	resp, err = httpclient.GetHTTPClient().Do(req)
12187	require.NoError(t, err)
12188	defer resp.Body.Close()
12189	assert.Equal(t, http.StatusForbidden, resp.StatusCode)
12190
12191	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
12192	assert.NoError(t, err)
12193}
12194
12195func TestAdminUpdateSelfMock(t *testing.T) {
12196	admin, _, err := httpdtest.GetAdminByUsername(defaultTokenAuthUser, http.StatusOK)
12197	assert.NoError(t, err)
12198	token, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
12199	assert.NoError(t, err)
12200	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
12201	assert.NoError(t, err)
12202	form := make(url.Values)
12203	form.Set("username", admin.Username)
12204	form.Set("password", admin.Password)
12205	form.Set("status", "0")
12206	form.Set("permissions", dataprovider.PermAdminAddUsers)
12207	form.Set("permissions", dataprovider.PermAdminCloseConnections)
12208	form.Set(csrfFormToken, csrfToken)
12209	req, _ := http.NewRequest(http.MethodPost, path.Join(webAdminPath, defaultTokenAuthUser), bytes.NewBuffer([]byte(form.Encode())))
12210	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
12211	setJWTCookieForReq(req, token)
12212	rr := executeRequest(req)
12213	checkResponseCode(t, http.StatusOK, rr)
12214	assert.Contains(t, rr.Body.String(), "You cannot remove these permissions to yourself")
12215
12216	form.Set("permissions", dataprovider.PermAdminAny)
12217	req, _ = http.NewRequest(http.MethodPost, path.Join(webAdminPath, defaultTokenAuthUser), bytes.NewBuffer([]byte(form.Encode())))
12218	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
12219	setJWTCookieForReq(req, token)
12220	rr = executeRequest(req)
12221	checkResponseCode(t, http.StatusOK, rr)
12222	assert.Contains(t, rr.Body.String(), "You cannot disable yourself")
12223}
12224
12225func TestWebMaintenanceMock(t *testing.T) {
12226	token, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
12227	assert.NoError(t, err)
12228	req, _ := http.NewRequest(http.MethodGet, webMaintenancePath, nil)
12229	setJWTCookieForReq(req, token)
12230	rr := executeRequest(req)
12231	checkResponseCode(t, http.StatusOK, rr)
12232	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
12233	assert.NoError(t, err)
12234
12235	form := make(url.Values)
12236	form.Set("mode", "a")
12237	b, contentType, _ := getMultipartFormData(form, "", "")
12238	req, _ = http.NewRequest(http.MethodPost, webRestorePath, &b)
12239	setJWTCookieForReq(req, token)
12240	req.Header.Set("Content-Type", contentType)
12241	rr = executeRequest(req)
12242	checkResponseCode(t, http.StatusForbidden, rr)
12243	assert.Contains(t, rr.Body.String(), "unable to verify form token")
12244
12245	form.Set(csrfFormToken, csrfToken)
12246	b, contentType, _ = getMultipartFormData(form, "", "")
12247	req, _ = http.NewRequest(http.MethodPost, webRestorePath, &b)
12248	setJWTCookieForReq(req, token)
12249	req.Header.Set("Content-Type", contentType)
12250	rr = executeRequest(req)
12251	checkResponseCode(t, http.StatusOK, rr)
12252
12253	form.Set("mode", "0")
12254	form.Set("quota", "a")
12255	b, contentType, _ = getMultipartFormData(form, "", "")
12256	req, _ = http.NewRequest(http.MethodPost, webRestorePath, &b)
12257	setJWTCookieForReq(req, token)
12258	req.Header.Set("Content-Type", contentType)
12259	rr = executeRequest(req)
12260	checkResponseCode(t, http.StatusOK, rr)
12261
12262	form.Set("quota", "0")
12263	b, contentType, _ = getMultipartFormData(form, "", "")
12264	req, _ = http.NewRequest(http.MethodPost, webRestorePath, &b)
12265	setJWTCookieForReq(req, token)
12266	req.Header.Set("Content-Type", contentType)
12267	rr = executeRequest(req)
12268	checkResponseCode(t, http.StatusOK, rr)
12269
12270	req, _ = http.NewRequest(http.MethodPost, webRestorePath+"?a=%3", &b)
12271	setJWTCookieForReq(req, token)
12272	req.Header.Set("Content-Type", contentType)
12273	rr = executeRequest(req)
12274	checkResponseCode(t, http.StatusOK, rr)
12275
12276	backupFilePath := filepath.Join(os.TempDir(), "backup.json")
12277	err = createTestFile(backupFilePath, 0)
12278	assert.NoError(t, err)
12279
12280	b, contentType, _ = getMultipartFormData(form, "backup_file", backupFilePath)
12281	req, _ = http.NewRequest(http.MethodPost, webRestorePath, &b)
12282	setJWTCookieForReq(req, token)
12283	req.Header.Set("Content-Type", contentType)
12284	rr = executeRequest(req)
12285	checkResponseCode(t, http.StatusOK, rr)
12286
12287	err = createTestFile(backupFilePath, 10)
12288	assert.NoError(t, err)
12289
12290	b, contentType, _ = getMultipartFormData(form, "backup_file", backupFilePath)
12291	req, _ = http.NewRequest(http.MethodPost, webRestorePath, &b)
12292	setJWTCookieForReq(req, token)
12293	req.Header.Set("Content-Type", contentType)
12294	rr = executeRequest(req)
12295	checkResponseCode(t, http.StatusOK, rr)
12296
12297	user := getTestUser()
12298	user.ID = 1
12299	user.Username = "test_user_web_restore"
12300	admin := getTestAdmin()
12301	admin.ID = 1
12302	admin.Username = "test_admin_web_restore"
12303
12304	apiKey := dataprovider.APIKey{
12305		Name:  "key name",
12306		KeyID: util.GenerateUniqueID(),
12307		Key:   fmt.Sprintf("%v.%v", util.GenerateUniqueID(), util.GenerateUniqueID()),
12308		Scope: dataprovider.APIKeyScopeAdmin,
12309	}
12310	backupData := dataprovider.BackupData{}
12311	backupData.Users = append(backupData.Users, user)
12312	backupData.Admins = append(backupData.Admins, admin)
12313	backupData.APIKeys = append(backupData.APIKeys, apiKey)
12314	backupContent, err := json.Marshal(backupData)
12315	assert.NoError(t, err)
12316	err = os.WriteFile(backupFilePath, backupContent, os.ModePerm)
12317	assert.NoError(t, err)
12318
12319	b, contentType, _ = getMultipartFormData(form, "backup_file", backupFilePath)
12320	req, _ = http.NewRequest(http.MethodPost, webRestorePath, &b)
12321	setJWTCookieForReq(req, token)
12322	req.Header.Set("Content-Type", contentType)
12323	rr = executeRequest(req)
12324	checkResponseCode(t, http.StatusOK, rr)
12325	assert.Contains(t, rr.Body.String(), "Your backup was successfully restored")
12326
12327	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
12328	assert.NoError(t, err)
12329	_, err = httpdtest.RemoveUser(user, http.StatusOK)
12330	assert.NoError(t, err)
12331
12332	admin, _, err = httpdtest.GetAdminByUsername(admin.Username, http.StatusOK)
12333	assert.NoError(t, err)
12334	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
12335	assert.NoError(t, err)
12336
12337	_, _, err = httpdtest.GetAPIKeyByID(apiKey.KeyID, http.StatusOK)
12338	assert.NoError(t, err)
12339	_, err = httpdtest.RemoveAPIKey(apiKey, http.StatusOK)
12340	assert.NoError(t, err)
12341
12342	err = os.Remove(backupFilePath)
12343	assert.NoError(t, err)
12344}
12345
12346func TestWebUserAddMock(t *testing.T) {
12347	webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
12348	assert.NoError(t, err)
12349	apiToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
12350	assert.NoError(t, err)
12351	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
12352	assert.NoError(t, err)
12353	user := getTestUser()
12354	user.UploadBandwidth = 32
12355	user.DownloadBandwidth = 64
12356	user.UID = 1000
12357	user.AdditionalInfo = "info"
12358	user.Description = "user dsc"
12359	user.Email = "test@test.com"
12360	mappedDir := filepath.Join(os.TempDir(), "mapped")
12361	folderName := filepath.Base(mappedDir)
12362	f := vfs.BaseVirtualFolder{
12363		Name:       folderName,
12364		MappedPath: mappedDir,
12365	}
12366	folderAsJSON, err := json.Marshal(f)
12367	assert.NoError(t, err)
12368	req, _ := http.NewRequest(http.MethodPost, folderPath, bytes.NewBuffer(folderAsJSON))
12369	setBearerForReq(req, apiToken)
12370	rr := executeRequest(req)
12371	checkResponseCode(t, http.StatusCreated, rr)
12372
12373	form := make(url.Values)
12374	form.Set(csrfFormToken, csrfToken)
12375	form.Set("username", user.Username)
12376	form.Set("email", user.Email)
12377	form.Set("home_dir", user.HomeDir)
12378	form.Set("password", user.Password)
12379	form.Set("status", strconv.Itoa(user.Status))
12380	form.Set("expiration_date", "")
12381	form.Set("permissions", "*")
12382	form.Set("sub_perm_path0", "/subdir")
12383	form.Set("sub_perm_permissions0", "list")
12384	form.Add("sub_perm_permissions0", "download")
12385	form.Set("vfolder_path", " /vdir")
12386	form.Set("vfolder_name", folderName)
12387	form.Set("vfolder_quota_size", "1024")
12388	form.Set("vfolder_quota_files", "2")
12389	form.Set("pattern_path0", "/dir2")
12390	form.Set("patterns0", "*.jpg,*.png")
12391	form.Set("pattern_type0", "allowed")
12392	form.Set("pattern_path1", "/dir1")
12393	form.Set("patterns1", "*.png")
12394	form.Set("pattern_type1", "allowed")
12395	form.Set("pattern_path2", "/dir1")
12396	form.Set("patterns2", "*.zip")
12397	form.Set("pattern_type2", "denied")
12398	form.Set("pattern_path3", "/dir3")
12399	form.Set("patterns3", "*.rar")
12400	form.Set("pattern_type3", "denied")
12401	form.Set("pattern_path4", "/dir2")
12402	form.Set("patterns4", "*.mkv")
12403	form.Set("pattern_type4", "denied")
12404	form.Set("additional_info", user.AdditionalInfo)
12405	form.Set("description", user.Description)
12406	form.Add("hooks", "external_auth_disabled")
12407	form.Set("disable_fs_checks", "checked")
12408	b, contentType, _ := getMultipartFormData(form, "", "")
12409	// test invalid url escape
12410	req, _ = http.NewRequest(http.MethodPost, webUserPath+"?a=%2", &b)
12411	setJWTCookieForReq(req, webToken)
12412	req.Header.Set("Content-Type", contentType)
12413	rr = executeRequest(req)
12414	checkResponseCode(t, http.StatusOK, rr)
12415	form.Set("public_keys", testPubKey)
12416	form.Add("public_keys", testPubKey1)
12417	form.Set("uid", strconv.FormatInt(int64(user.UID), 10))
12418	form.Set("gid", "a")
12419	b, contentType, _ = getMultipartFormData(form, "", "")
12420	// test invalid gid
12421	req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
12422	setJWTCookieForReq(req, webToken)
12423	req.Header.Set("Content-Type", contentType)
12424	rr = executeRequest(req)
12425	checkResponseCode(t, http.StatusOK, rr)
12426	form.Set("gid", "0")
12427	form.Set("max_sessions", "a")
12428	b, contentType, _ = getMultipartFormData(form, "", "")
12429	// test invalid max sessions
12430	req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
12431	setJWTCookieForReq(req, webToken)
12432	req.Header.Set("Content-Type", contentType)
12433	rr = executeRequest(req)
12434	checkResponseCode(t, http.StatusOK, rr)
12435	form.Set("max_sessions", "0")
12436	form.Set("quota_size", "a")
12437	b, contentType, _ = getMultipartFormData(form, "", "")
12438	// test invalid quota size
12439	req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
12440	setJWTCookieForReq(req, webToken)
12441	req.Header.Set("Content-Type", contentType)
12442	rr = executeRequest(req)
12443	checkResponseCode(t, http.StatusOK, rr)
12444	form.Set("quota_size", "0")
12445	form.Set("quota_files", "a")
12446	b, contentType, _ = getMultipartFormData(form, "", "")
12447	// test invalid quota files
12448	req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
12449	setJWTCookieForReq(req, webToken)
12450	req.Header.Set("Content-Type", contentType)
12451	rr = executeRequest(req)
12452	checkResponseCode(t, http.StatusOK, rr)
12453	form.Set("quota_files", "0")
12454	form.Set("upload_bandwidth", "a")
12455	b, contentType, _ = getMultipartFormData(form, "", "")
12456	// test invalid upload bandwidth
12457	req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
12458	setJWTCookieForReq(req, webToken)
12459	req.Header.Set("Content-Type", contentType)
12460	rr = executeRequest(req)
12461	checkResponseCode(t, http.StatusOK, rr)
12462	form.Set("upload_bandwidth", strconv.FormatInt(user.UploadBandwidth, 10))
12463	form.Set("download_bandwidth", "a")
12464	b, contentType, _ = getMultipartFormData(form, "", "")
12465	// test invalid download bandwidth
12466	req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
12467	setJWTCookieForReq(req, webToken)
12468	req.Header.Set("Content-Type", contentType)
12469	rr = executeRequest(req)
12470	checkResponseCode(t, http.StatusOK, rr)
12471	form.Set("download_bandwidth", strconv.FormatInt(user.DownloadBandwidth, 10))
12472	form.Set("status", "a")
12473	b, contentType, _ = getMultipartFormData(form, "", "")
12474	// test invalid status
12475	req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
12476	setJWTCookieForReq(req, webToken)
12477	req.Header.Set("Content-Type", contentType)
12478	rr = executeRequest(req)
12479	checkResponseCode(t, http.StatusOK, rr)
12480	form.Set("status", strconv.Itoa(user.Status))
12481	form.Set("expiration_date", "123")
12482	b, contentType, _ = getMultipartFormData(form, "", "")
12483	// test invalid expiration date
12484	req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
12485	setJWTCookieForReq(req, webToken)
12486	req.Header.Set("Content-Type", contentType)
12487	rr = executeRequest(req)
12488	checkResponseCode(t, http.StatusOK, rr)
12489	form.Set("expiration_date", "")
12490	form.Set("allowed_ip", "invalid,ip")
12491	b, contentType, _ = getMultipartFormData(form, "", "")
12492	// test invalid allowed_ip
12493	req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
12494	setJWTCookieForReq(req, webToken)
12495	req.Header.Set("Content-Type", contentType)
12496	rr = executeRequest(req)
12497	checkResponseCode(t, http.StatusOK, rr)
12498	form.Set("allowed_ip", "")
12499	form.Set("denied_ip", "192.168.1.2") // it should be 192.168.1.2/32
12500	b, contentType, _ = getMultipartFormData(form, "", "")
12501	// test invalid denied_ip
12502	req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
12503	setJWTCookieForReq(req, webToken)
12504	req.Header.Set("Content-Type", contentType)
12505	rr = executeRequest(req)
12506	checkResponseCode(t, http.StatusOK, rr)
12507	form.Set("denied_ip", "")
12508	// test invalid max file upload size
12509	form.Set("max_upload_file_size", "a")
12510	b, contentType, _ = getMultipartFormData(form, "", "")
12511	req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
12512	setJWTCookieForReq(req, webToken)
12513	req.Header.Set("Content-Type", contentType)
12514	rr = executeRequest(req)
12515	checkResponseCode(t, http.StatusOK, rr)
12516	form.Set("max_upload_file_size", "1000")
12517	// test invalid tls username
12518	form.Set("tls_username", "username")
12519	b, contentType, _ = getMultipartFormData(form, "", "")
12520	req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
12521	setJWTCookieForReq(req, webToken)
12522	req.Header.Set("Content-Type", contentType)
12523	rr = executeRequest(req)
12524	checkResponseCode(t, http.StatusOK, rr)
12525	assert.Contains(t, rr.Body.String(), "Validation error: invalid TLS username")
12526	form.Set("tls_username", string(sdk.TLSUsernameNone))
12527	form.Set(csrfFormToken, "invalid form token")
12528	b, contentType, _ = getMultipartFormData(form, "", "")
12529	req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
12530	setJWTCookieForReq(req, webToken)
12531	req.Header.Set("Content-Type", contentType)
12532	rr = executeRequest(req)
12533	checkResponseCode(t, http.StatusForbidden, rr)
12534	assert.Contains(t, rr.Body.String(), "unable to verify form token")
12535
12536	form.Set(csrfFormToken, csrfToken)
12537	b, contentType, _ = getMultipartFormData(form, "", "")
12538	req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
12539	setJWTCookieForReq(req, webToken)
12540	req.Header.Set("Content-Type", contentType)
12541	rr = executeRequest(req)
12542	checkResponseCode(t, http.StatusSeeOther, rr)
12543
12544	dbUser, err := dataprovider.UserExists(user.Username)
12545	assert.NoError(t, err)
12546	assert.NotEmpty(t, dbUser.Password)
12547	assert.True(t, dbUser.IsPasswordHashed())
12548	// the user already exists, was created with the above request
12549	b, contentType, _ = getMultipartFormData(form, "", "")
12550	req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
12551	setJWTCookieForReq(req, webToken)
12552	req.Header.Set("Content-Type", contentType)
12553	rr = executeRequest(req)
12554	checkResponseCode(t, http.StatusOK, rr)
12555	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
12556	setBearerForReq(req, apiToken)
12557	rr = executeRequest(req)
12558	checkResponseCode(t, http.StatusOK, rr)
12559	newUser := dataprovider.User{}
12560	err = render.DecodeJSON(rr.Body, &newUser)
12561	assert.NoError(t, err)
12562	assert.Equal(t, user.UID, newUser.UID)
12563	assert.Equal(t, user.UploadBandwidth, newUser.UploadBandwidth)
12564	assert.Equal(t, user.DownloadBandwidth, newUser.DownloadBandwidth)
12565	assert.Equal(t, int64(1000), newUser.Filters.MaxUploadFileSize)
12566	assert.Equal(t, user.AdditionalInfo, newUser.AdditionalInfo)
12567	assert.Equal(t, user.Description, newUser.Description)
12568	assert.True(t, newUser.Filters.Hooks.ExternalAuthDisabled)
12569	assert.False(t, newUser.Filters.Hooks.PreLoginDisabled)
12570	assert.False(t, newUser.Filters.Hooks.CheckPasswordDisabled)
12571	assert.True(t, newUser.Filters.DisableFsChecks)
12572	assert.False(t, newUser.Filters.AllowAPIKeyAuth)
12573	assert.Equal(t, user.Email, newUser.Email)
12574	assert.True(t, util.IsStringInSlice(testPubKey, newUser.PublicKeys))
12575	if val, ok := newUser.Permissions["/subdir"]; ok {
12576		assert.True(t, util.IsStringInSlice(dataprovider.PermListItems, val))
12577		assert.True(t, util.IsStringInSlice(dataprovider.PermDownload, val))
12578	} else {
12579		assert.Fail(t, "user permissions must contain /somedir", "actual: %v", newUser.Permissions)
12580	}
12581	assert.Len(t, newUser.PublicKeys, 2)
12582	assert.Len(t, newUser.VirtualFolders, 1)
12583	for _, v := range newUser.VirtualFolders {
12584		assert.Equal(t, v.VirtualPath, "/vdir")
12585		assert.Equal(t, v.Name, folderName)
12586		assert.Equal(t, v.MappedPath, mappedDir)
12587		assert.Equal(t, v.QuotaFiles, 2)
12588		assert.Equal(t, v.QuotaSize, int64(1024))
12589	}
12590	assert.Len(t, newUser.Filters.FilePatterns, 3)
12591	for _, filter := range newUser.Filters.FilePatterns {
12592		if filter.Path == "/dir1" {
12593			assert.Len(t, filter.DeniedPatterns, 1)
12594			assert.Len(t, filter.AllowedPatterns, 1)
12595			assert.True(t, util.IsStringInSlice("*.png", filter.AllowedPatterns))
12596			assert.True(t, util.IsStringInSlice("*.zip", filter.DeniedPatterns))
12597		}
12598		if filter.Path == "/dir2" {
12599			assert.Len(t, filter.DeniedPatterns, 1)
12600			assert.Len(t, filter.AllowedPatterns, 2)
12601			assert.True(t, util.IsStringInSlice("*.jpg", filter.AllowedPatterns))
12602			assert.True(t, util.IsStringInSlice("*.png", filter.AllowedPatterns))
12603			assert.True(t, util.IsStringInSlice("*.mkv", filter.DeniedPatterns))
12604		}
12605		if filter.Path == "/dir3" {
12606			assert.Len(t, filter.DeniedPatterns, 1)
12607			assert.Len(t, filter.AllowedPatterns, 0)
12608			assert.True(t, util.IsStringInSlice("*.rar", filter.DeniedPatterns))
12609		}
12610	}
12611	assert.Equal(t, sdk.TLSUsernameNone, newUser.Filters.TLSUsername)
12612	req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, newUser.Username), nil)
12613	setBearerForReq(req, apiToken)
12614	rr = executeRequest(req)
12615	checkResponseCode(t, http.StatusOK, rr)
12616	req, _ = http.NewRequest(http.MethodDelete, path.Join(folderPath, folderName), nil)
12617	setBearerForReq(req, apiToken)
12618	rr = executeRequest(req)
12619	checkResponseCode(t, http.StatusOK, rr)
12620}
12621
12622func TestWebUserUpdateMock(t *testing.T) {
12623	webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
12624	assert.NoError(t, err)
12625	apiToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
12626	assert.NoError(t, err)
12627	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
12628	assert.NoError(t, err)
12629	user := getTestUser()
12630	userAsJSON := getUserAsJSON(t, user)
12631	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
12632	setBearerForReq(req, apiToken)
12633	rr := executeRequest(req)
12634	checkResponseCode(t, http.StatusCreated, rr)
12635	// add TOTP config
12636	configName, _, secret, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], user.Username)
12637	assert.NoError(t, err)
12638	userToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
12639	assert.NoError(t, err)
12640	userTOTPConfig := sdk.TOTPConfig{
12641		Enabled:    true,
12642		ConfigName: configName,
12643		Secret:     kms.NewPlainSecret(secret),
12644		Protocols:  []string{common.ProtocolSSH, common.ProtocolFTP},
12645	}
12646	asJSON, err := json.Marshal(userTOTPConfig)
12647	assert.NoError(t, err)
12648	req, err = http.NewRequest(http.MethodPost, webClientTOTPSavePath, bytes.NewBuffer(asJSON))
12649	assert.NoError(t, err)
12650	setJWTCookieForReq(req, userToken)
12651	rr = executeRequest(req)
12652	checkResponseCode(t, http.StatusForbidden, rr)
12653	assert.Contains(t, rr.Body.String(), "Invalid token")
12654
12655	req, err = http.NewRequest(http.MethodPost, webClientTOTPSavePath, bytes.NewBuffer(asJSON))
12656	assert.NoError(t, err)
12657	setJWTCookieForReq(req, userToken)
12658	setCSRFHeaderForReq(req, csrfToken)
12659	rr = executeRequest(req)
12660	checkResponseCode(t, http.StatusOK, rr)
12661
12662	user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
12663	assert.NoError(t, err)
12664	assert.True(t, user.Filters.TOTPConfig.Enabled)
12665
12666	dbUser, err := dataprovider.UserExists(user.Username)
12667	assert.NoError(t, err)
12668	assert.NotEmpty(t, dbUser.Password)
12669	assert.True(t, dbUser.IsPasswordHashed())
12670	err = render.DecodeJSON(rr.Body, &user)
12671	assert.NoError(t, err)
12672	user.MaxSessions = 1
12673	user.QuotaFiles = 2
12674	user.QuotaSize = 3
12675	user.GID = 1000
12676	user.Filters.AllowAPIKeyAuth = true
12677	user.AdditionalInfo = "new additional info"
12678	user.Email = "user@example.com"
12679	form := make(url.Values)
12680	form.Set("username", user.Username)
12681	form.Set("email", user.Email)
12682	form.Set("password", "")
12683	form.Set("public_keys", testPubKey)
12684	form.Set("home_dir", user.HomeDir)
12685	form.Set("uid", "0")
12686	form.Set("gid", strconv.FormatInt(int64(user.GID), 10))
12687	form.Set("max_sessions", strconv.FormatInt(int64(user.MaxSessions), 10))
12688	form.Set("quota_size", strconv.FormatInt(user.QuotaSize, 10))
12689	form.Set("quota_files", strconv.FormatInt(int64(user.QuotaFiles), 10))
12690	form.Set("upload_bandwidth", "0")
12691	form.Set("download_bandwidth", "0")
12692	form.Set("permissions", "*")
12693	form.Set("sub_perm_path0", "/otherdir")
12694	form.Set("sub_perm_permissions0", "list")
12695	form.Add("sub_perm_permissions0", "upload")
12696	form.Set("status", strconv.Itoa(user.Status))
12697	form.Set("expiration_date", "2020-01-01 00:00:00")
12698	form.Set("allowed_ip", " 192.168.1.3/32, 192.168.2.0/24 ")
12699	form.Set("denied_ip", " 10.0.0.2/32 ")
12700	form.Set("pattern_path0", "/dir1")
12701	form.Set("patterns0", "*.zip")
12702	form.Set("pattern_type0", "denied")
12703	form.Set("ssh_login_methods", dataprovider.SSHLoginMethodKeyboardInteractive)
12704	form.Set("denied_protocols", common.ProtocolFTP)
12705	form.Set("max_upload_file_size", "100")
12706	form.Set("disconnect", "1")
12707	form.Set("additional_info", user.AdditionalInfo)
12708	form.Set("description", user.Description)
12709	form.Set("tls_username", string(sdk.TLSUsernameCN))
12710	form.Set("allow_api_key_auth", "1")
12711	b, contentType, _ := getMultipartFormData(form, "", "")
12712	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
12713	setJWTCookieForReq(req, webToken)
12714	req.Header.Set("Content-Type", contentType)
12715	rr = executeRequest(req)
12716	checkResponseCode(t, http.StatusForbidden, rr)
12717	assert.Contains(t, rr.Body.String(), "unable to verify form token")
12718
12719	form.Set(csrfFormToken, csrfToken)
12720	b, contentType, _ = getMultipartFormData(form, "", "")
12721	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
12722	setJWTCookieForReq(req, webToken)
12723	req.Header.Set("Content-Type", contentType)
12724	rr = executeRequest(req)
12725	checkResponseCode(t, http.StatusSeeOther, rr)
12726	dbUser, err = dataprovider.UserExists(user.Username)
12727	assert.NoError(t, err)
12728	assert.Empty(t, dbUser.Password)
12729	assert.False(t, dbUser.IsPasswordHashed())
12730
12731	form.Set("password", defaultPassword)
12732	b, contentType, _ = getMultipartFormData(form, "", "")
12733	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
12734	setJWTCookieForReq(req, webToken)
12735	req.Header.Set("Content-Type", contentType)
12736	rr = executeRequest(req)
12737	checkResponseCode(t, http.StatusSeeOther, rr)
12738	dbUser, err = dataprovider.UserExists(user.Username)
12739	assert.NoError(t, err)
12740	assert.NotEmpty(t, dbUser.Password)
12741	assert.True(t, dbUser.IsPasswordHashed())
12742	prevPwd := dbUser.Password
12743
12744	form.Set("password", redactedSecret)
12745	b, contentType, _ = getMultipartFormData(form, "", "")
12746	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
12747	setJWTCookieForReq(req, webToken)
12748	req.Header.Set("Content-Type", contentType)
12749	rr = executeRequest(req)
12750	checkResponseCode(t, http.StatusSeeOther, rr)
12751	dbUser, err = dataprovider.UserExists(user.Username)
12752	assert.NoError(t, err)
12753	assert.NotEmpty(t, dbUser.Password)
12754	assert.True(t, dbUser.IsPasswordHashed())
12755	assert.Equal(t, prevPwd, dbUser.Password)
12756	assert.True(t, dbUser.Filters.TOTPConfig.Enabled)
12757
12758	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
12759	setBearerForReq(req, apiToken)
12760	rr = executeRequest(req)
12761	checkResponseCode(t, http.StatusOK, rr)
12762	var updateUser dataprovider.User
12763	err = render.DecodeJSON(rr.Body, &updateUser)
12764	assert.NoError(t, err)
12765	assert.Equal(t, user.Email, updateUser.Email)
12766	assert.Equal(t, user.HomeDir, updateUser.HomeDir)
12767	assert.Equal(t, user.MaxSessions, updateUser.MaxSessions)
12768	assert.Equal(t, user.QuotaFiles, updateUser.QuotaFiles)
12769	assert.Equal(t, user.QuotaSize, updateUser.QuotaSize)
12770	assert.Equal(t, user.UID, updateUser.UID)
12771	assert.Equal(t, user.GID, updateUser.GID)
12772	assert.Equal(t, user.AdditionalInfo, updateUser.AdditionalInfo)
12773	assert.Equal(t, user.Description, updateUser.Description)
12774	assert.Equal(t, int64(100), updateUser.Filters.MaxUploadFileSize)
12775	assert.Equal(t, sdk.TLSUsernameCN, updateUser.Filters.TLSUsername)
12776	assert.True(t, updateUser.Filters.AllowAPIKeyAuth)
12777	assert.True(t, updateUser.Filters.TOTPConfig.Enabled)
12778
12779	if val, ok := updateUser.Permissions["/otherdir"]; ok {
12780		assert.True(t, util.IsStringInSlice(dataprovider.PermListItems, val))
12781		assert.True(t, util.IsStringInSlice(dataprovider.PermUpload, val))
12782	} else {
12783		assert.Fail(t, "user permissions must contains /otherdir", "actual: %v", updateUser.Permissions)
12784	}
12785	assert.True(t, util.IsStringInSlice("192.168.1.3/32", updateUser.Filters.AllowedIP))
12786	assert.True(t, util.IsStringInSlice("10.0.0.2/32", updateUser.Filters.DeniedIP))
12787	assert.True(t, util.IsStringInSlice(dataprovider.SSHLoginMethodKeyboardInteractive, updateUser.Filters.DeniedLoginMethods))
12788	assert.True(t, util.IsStringInSlice(common.ProtocolFTP, updateUser.Filters.DeniedProtocols))
12789	assert.True(t, util.IsStringInSlice("*.zip", updateUser.Filters.FilePatterns[0].DeniedPatterns))
12790	req, err = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
12791	assert.NoError(t, err)
12792	setBearerForReq(req, apiToken)
12793	rr = executeRequest(req)
12794	checkResponseCode(t, http.StatusOK, rr)
12795}
12796
12797func TestRenderFolderTemplateMock(t *testing.T) {
12798	token, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
12799	assert.NoError(t, err)
12800	req, err := http.NewRequest(http.MethodGet, webTemplateFolder, nil)
12801	assert.NoError(t, err)
12802	setJWTCookieForReq(req, token)
12803	rr := executeRequest(req)
12804	checkResponseCode(t, http.StatusOK, rr)
12805
12806	folder := vfs.BaseVirtualFolder{
12807		Name:        "templatefolder",
12808		MappedPath:  filepath.Join(os.TempDir(), "mapped"),
12809		Description: "template folder desc",
12810	}
12811	folder, _, err = httpdtest.AddFolder(folder, http.StatusCreated)
12812	assert.NoError(t, err)
12813
12814	req, err = http.NewRequest(http.MethodGet, webTemplateFolder+fmt.Sprintf("?from=%v", folder.Name), nil)
12815	assert.NoError(t, err)
12816	setJWTCookieForReq(req, token)
12817	rr = executeRequest(req)
12818	checkResponseCode(t, http.StatusOK, rr)
12819
12820	req, err = http.NewRequest(http.MethodGet, webTemplateFolder+"?from=unknown-folder", nil)
12821	assert.NoError(t, err)
12822	setJWTCookieForReq(req, token)
12823	rr = executeRequest(req)
12824	checkResponseCode(t, http.StatusNotFound, rr)
12825
12826	_, err = httpdtest.RemoveFolder(folder, http.StatusOK)
12827	assert.NoError(t, err)
12828}
12829
12830func TestRenderUserTemplateMock(t *testing.T) {
12831	token, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
12832	assert.NoError(t, err)
12833	req, err := http.NewRequest(http.MethodGet, webTemplateUser, nil)
12834	assert.NoError(t, err)
12835	setJWTCookieForReq(req, token)
12836	rr := executeRequest(req)
12837	checkResponseCode(t, http.StatusOK, rr)
12838
12839	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
12840	assert.NoError(t, err)
12841
12842	req, err = http.NewRequest(http.MethodGet, webTemplateUser+fmt.Sprintf("?from=%v", user.Username), nil)
12843	assert.NoError(t, err)
12844	setJWTCookieForReq(req, token)
12845	rr = executeRequest(req)
12846	checkResponseCode(t, http.StatusOK, rr)
12847
12848	req, err = http.NewRequest(http.MethodGet, webTemplateUser+"?from=unknown", nil)
12849	assert.NoError(t, err)
12850	setJWTCookieForReq(req, token)
12851	rr = executeRequest(req)
12852	checkResponseCode(t, http.StatusNotFound, rr)
12853
12854	_, err = httpdtest.RemoveUser(user, http.StatusOK)
12855	assert.NoError(t, err)
12856}
12857
12858func TestRenderWebCloneUserMock(t *testing.T) {
12859	token, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
12860	assert.NoError(t, err)
12861	user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
12862	assert.NoError(t, err)
12863
12864	req, err := http.NewRequest(http.MethodGet, webUserPath+fmt.Sprintf("?clone-from=%v", user.Username), nil)
12865	assert.NoError(t, err)
12866	setJWTCookieForReq(req, token)
12867	rr := executeRequest(req)
12868	checkResponseCode(t, http.StatusOK, rr)
12869
12870	req, err = http.NewRequest(http.MethodGet, webUserPath+fmt.Sprintf("?clone-from=%v", altAdminPassword), nil)
12871	assert.NoError(t, err)
12872	setJWTCookieForReq(req, token)
12873	rr = executeRequest(req)
12874	checkResponseCode(t, http.StatusNotFound, rr)
12875
12876	_, err = httpdtest.RemoveUser(user, http.StatusOK)
12877	assert.NoError(t, err)
12878}
12879
12880func TestUserTemplateWithFoldersMock(t *testing.T) {
12881	folder := vfs.BaseVirtualFolder{
12882		Name:        "vfolder",
12883		MappedPath:  filepath.Join(os.TempDir(), "mapped"),
12884		Description: "vfolder desc with spéciàl ch@rs",
12885	}
12886
12887	token, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
12888	assert.NoError(t, err)
12889	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
12890	assert.NoError(t, err)
12891	user := getTestUser()
12892	form := make(url.Values)
12893	form.Set("username", user.Username)
12894	form.Set("home_dir", filepath.Join(os.TempDir(), "%username%"))
12895	form.Set("uid", strconv.FormatInt(int64(user.UID), 10))
12896	form.Set("gid", strconv.FormatInt(int64(user.GID), 10))
12897	form.Set("max_sessions", strconv.FormatInt(int64(user.MaxSessions), 10))
12898	form.Set("quota_size", strconv.FormatInt(user.QuotaSize, 10))
12899	form.Set("quota_files", strconv.FormatInt(int64(user.QuotaFiles), 10))
12900	form.Set("upload_bandwidth", "0")
12901	form.Set("download_bandwidth", "0")
12902	form.Set("permissions", "*")
12903	form.Set("status", strconv.Itoa(user.Status))
12904	form.Set("expiration_date", "2020-01-01 00:00:00")
12905	form.Set("fs_provider", "0")
12906	form.Set("max_upload_file_size", "0")
12907	form.Set("description", "desc %username% %password%")
12908	form.Set("vfolder_path", "/vdir%username%")
12909	form.Set("vfolder_name", folder.Name)
12910	form.Set("vfolder_quota_size", "-1")
12911	form.Set("vfolder_quota_files", "-1")
12912	form.Add("tpl_username", "auser1")
12913	form.Add("tpl_password", "password1")
12914	form.Add("tpl_public_keys", " ")
12915	form.Add("tpl_username", "auser2")
12916	form.Add("tpl_password", "password2")
12917	form.Add("tpl_public_keys", testPubKey)
12918	form.Add("tpl_username", "auser1")
12919	form.Add("tpl_password", "password")
12920	form.Add("tpl_public_keys", "")
12921	b, contentType, _ := getMultipartFormData(form, "", "")
12922	req, _ := http.NewRequest(http.MethodPost, path.Join(webTemplateUser), &b)
12923	setJWTCookieForReq(req, token)
12924	req.Header.Set("Content-Type", contentType)
12925	rr := executeRequest(req)
12926	checkResponseCode(t, http.StatusForbidden, rr)
12927	require.Contains(t, rr.Body.String(), "unable to verify form token")
12928
12929	form.Set(csrfFormToken, csrfToken)
12930	b, contentType, _ = getMultipartFormData(form, "", "")
12931	req, _ = http.NewRequest(http.MethodPost, path.Join(webTemplateUser), &b)
12932	setJWTCookieForReq(req, token)
12933	req.Header.Set("Content-Type", contentType)
12934	rr = executeRequest(req)
12935	checkResponseCode(t, http.StatusBadRequest, rr)
12936	require.Contains(t, rr.Body.String(), "invalid folder mapped path")
12937
12938	folder, resp, err := httpdtest.AddFolder(folder, http.StatusCreated)
12939	assert.NoError(t, err, string(resp))
12940
12941	b, contentType, _ = getMultipartFormData(form, "", "")
12942	req, _ = http.NewRequest(http.MethodPost, path.Join(webTemplateUser), &b)
12943	setJWTCookieForReq(req, token)
12944	req.Header.Set("Content-Type", contentType)
12945	rr = executeRequest(req)
12946	checkResponseCode(t, http.StatusOK, rr)
12947
12948	var dump dataprovider.BackupData
12949	err = json.Unmarshal(rr.Body.Bytes(), &dump)
12950	assert.NoError(t, err)
12951	assert.Len(t, dump.Users, 2)
12952	assert.Len(t, dump.Folders, 1)
12953	user1 := dump.Users[0]
12954	user2 := dump.Users[1]
12955	folder1 := dump.Folders[0]
12956	assert.Equal(t, "auser1", user1.Username)
12957	assert.Equal(t, "auser2", user2.Username)
12958	assert.Equal(t, "desc auser1 password1", user1.Description)
12959	assert.Equal(t, "desc auser2 password2", user2.Description)
12960	assert.Equal(t, filepath.Join(os.TempDir(), user1.Username), user1.HomeDir)
12961	assert.Equal(t, filepath.Join(os.TempDir(), user2.Username), user2.HomeDir)
12962	assert.Equal(t, folder.Name, folder1.Name)
12963	assert.Equal(t, folder.MappedPath, folder1.MappedPath)
12964	assert.Equal(t, folder.Description, folder1.Description)
12965	assert.Len(t, user1.PublicKeys, 0)
12966	assert.Len(t, user2.PublicKeys, 1)
12967	assert.Len(t, user1.VirtualFolders, 1)
12968	assert.Len(t, user2.VirtualFolders, 1)
12969	assert.Equal(t, "/vdirauser1", user1.VirtualFolders[0].VirtualPath)
12970	assert.Equal(t, "/vdirauser2", user2.VirtualFolders[0].VirtualPath)
12971
12972	_, err = httpdtest.RemoveFolder(folder, http.StatusOK)
12973	assert.NoError(t, err)
12974}
12975
12976func TestUserTemplateMock(t *testing.T) {
12977	token, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
12978	assert.NoError(t, err)
12979	user := getTestUser()
12980	user.FsConfig.Provider = sdk.S3FilesystemProvider
12981	user.FsConfig.S3Config.Bucket = "test"
12982	user.FsConfig.S3Config.Region = "eu-central-1"
12983	user.FsConfig.S3Config.AccessKey = "%username%"
12984	user.FsConfig.S3Config.KeyPrefix = "somedir/subdir/"
12985	user.FsConfig.S3Config.UploadPartSize = 5
12986	user.FsConfig.S3Config.UploadConcurrency = 4
12987	user.FsConfig.S3Config.DownloadPartSize = 6
12988	user.FsConfig.S3Config.DownloadConcurrency = 3
12989	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
12990	assert.NoError(t, err)
12991	form := make(url.Values)
12992	form.Set(csrfFormToken, csrfToken)
12993	form.Set("username", user.Username)
12994	form.Set("home_dir", filepath.Join(os.TempDir(), "%username%"))
12995	form.Set("uid", "0")
12996	form.Set("gid", strconv.FormatInt(int64(user.GID), 10))
12997	form.Set("max_sessions", strconv.FormatInt(int64(user.MaxSessions), 10))
12998	form.Set("quota_size", strconv.FormatInt(user.QuotaSize, 10))
12999	form.Set("quota_files", strconv.FormatInt(int64(user.QuotaFiles), 10))
13000	form.Set("upload_bandwidth", "0")
13001	form.Set("download_bandwidth", "0")
13002	form.Set("permissions", "*")
13003	form.Set("status", strconv.Itoa(user.Status))
13004	form.Set("expiration_date", "2020-01-01 00:00:00")
13005	form.Set("allowed_ip", "")
13006	form.Set("denied_ip", "")
13007	form.Set("fs_provider", "1")
13008	form.Set("s3_bucket", user.FsConfig.S3Config.Bucket)
13009	form.Set("s3_region", user.FsConfig.S3Config.Region)
13010	form.Set("s3_access_key", "%username%")
13011	form.Set("s3_access_secret", "%password%")
13012	form.Set("s3_key_prefix", "base/%username%")
13013	form.Set("allowed_extensions", "/dir1::.jpg,.png")
13014	form.Set("denied_extensions", "/dir2::.zip")
13015	form.Set("max_upload_file_size", "0")
13016	form.Add("hooks", "external_auth_disabled")
13017	form.Add("hooks", "check_password_disabled")
13018	form.Set("disable_fs_checks", "checked")
13019	form.Set("s3_download_part_max_time", "0")
13020	// test invalid s3_upload_part_size
13021	form.Set("s3_upload_part_size", "a")
13022	b, contentType, _ := getMultipartFormData(form, "", "")
13023	req, _ := http.NewRequest(http.MethodPost, webTemplateUser, &b)
13024	setJWTCookieForReq(req, token)
13025	req.Header.Set("Content-Type", contentType)
13026	rr := executeRequest(req)
13027	checkResponseCode(t, http.StatusBadRequest, rr)
13028	form.Set("s3_upload_part_size", strconv.FormatInt(user.FsConfig.S3Config.UploadPartSize, 10))
13029	form.Set("s3_upload_concurrency", strconv.Itoa(user.FsConfig.S3Config.UploadConcurrency))
13030	form.Set("s3_download_part_size", strconv.FormatInt(user.FsConfig.S3Config.DownloadPartSize, 10))
13031	form.Set("s3_download_concurrency", strconv.Itoa(user.FsConfig.S3Config.DownloadConcurrency))
13032
13033	b, contentType, _ = getMultipartFormData(form, "", "")
13034	req, _ = http.NewRequest(http.MethodPost, webTemplateUser, &b)
13035	setJWTCookieForReq(req, token)
13036	req.Header.Set("Content-Type", contentType)
13037	rr = executeRequest(req)
13038	checkResponseCode(t, http.StatusBadRequest, rr)
13039
13040	form.Set("tpl_username", "user1")
13041	form.Set("tpl_password", "password1")
13042	form.Set("tpl_public_keys", "invalid-pkey")
13043	b, contentType, _ = getMultipartFormData(form, "", "")
13044	req, _ = http.NewRequest(http.MethodPost, webTemplateUser, &b)
13045	setJWTCookieForReq(req, token)
13046	req.Header.Set("Content-Type", contentType)
13047	rr = executeRequest(req)
13048	checkResponseCode(t, http.StatusBadRequest, rr)
13049	require.Contains(t, rr.Body.String(), "Error validating user")
13050
13051	form.Set("tpl_username", "user1")
13052	form.Set("tpl_password", " ")
13053	form.Set("tpl_public_keys", "")
13054	b, contentType, _ = getMultipartFormData(form, "", "")
13055	req, _ = http.NewRequest(http.MethodPost, webTemplateUser, &b)
13056	setJWTCookieForReq(req, token)
13057	req.Header.Set("Content-Type", contentType)
13058	rr = executeRequest(req)
13059	checkResponseCode(t, http.StatusBadRequest, rr)
13060	require.Contains(t, rr.Body.String(), "No valid users found, export is not possible")
13061
13062	form.Set("tpl_username", "user1")
13063	form.Set("tpl_password", "password1")
13064	form.Set("tpl_public_keys", " ")
13065	form.Add("tpl_username", "user2")
13066	form.Add("tpl_password", "password2")
13067	form.Add("tpl_public_keys", testPubKey)
13068	form.Add("tpl_username", "user3")
13069	form.Add("tpl_password", "")
13070	form.Add("tpl_public_keys", "")
13071	b, contentType, _ = getMultipartFormData(form, "", "")
13072	req, _ = http.NewRequest(http.MethodPost, webTemplateUser, &b)
13073	setJWTCookieForReq(req, token)
13074	req.Header.Set("Content-Type", contentType)
13075	rr = executeRequest(req)
13076	checkResponseCode(t, http.StatusOK, rr)
13077
13078	var dump dataprovider.BackupData
13079	err = json.Unmarshal(rr.Body.Bytes(), &dump)
13080	require.NoError(t, err)
13081	require.Len(t, dump.Users, 2)
13082	require.Len(t, dump.Admins, 0)
13083	require.Len(t, dump.Folders, 0)
13084	user1 := dump.Users[0]
13085	user2 := dump.Users[1]
13086	require.Equal(t, "user1", user1.Username)
13087	require.Equal(t, sdk.S3FilesystemProvider, user1.FsConfig.Provider)
13088	require.Equal(t, "user2", user2.Username)
13089	require.Equal(t, sdk.S3FilesystemProvider, user2.FsConfig.Provider)
13090	require.Len(t, user2.PublicKeys, 1)
13091	require.Equal(t, filepath.Join(os.TempDir(), user1.Username), user1.HomeDir)
13092	require.Equal(t, filepath.Join(os.TempDir(), user2.Username), user2.HomeDir)
13093	require.Equal(t, user1.Username, user1.FsConfig.S3Config.AccessKey)
13094	require.Equal(t, user2.Username, user2.FsConfig.S3Config.AccessKey)
13095	require.Equal(t, path.Join("base", user1.Username)+"/", user1.FsConfig.S3Config.KeyPrefix)
13096	require.Equal(t, path.Join("base", user2.Username)+"/", user2.FsConfig.S3Config.KeyPrefix)
13097	require.True(t, user1.FsConfig.S3Config.AccessSecret.IsEncrypted())
13098	err = user1.FsConfig.S3Config.AccessSecret.Decrypt()
13099	require.NoError(t, err)
13100	require.Equal(t, "password1", user1.FsConfig.S3Config.AccessSecret.GetPayload())
13101	require.True(t, user2.FsConfig.S3Config.AccessSecret.IsEncrypted())
13102	err = user2.FsConfig.S3Config.AccessSecret.Decrypt()
13103	require.NoError(t, err)
13104	require.Equal(t, "password2", user2.FsConfig.S3Config.AccessSecret.GetPayload())
13105	require.True(t, user1.Filters.Hooks.ExternalAuthDisabled)
13106	require.True(t, user1.Filters.Hooks.CheckPasswordDisabled)
13107	require.False(t, user1.Filters.Hooks.PreLoginDisabled)
13108	require.True(t, user2.Filters.Hooks.ExternalAuthDisabled)
13109	require.True(t, user2.Filters.Hooks.CheckPasswordDisabled)
13110	require.False(t, user2.Filters.Hooks.PreLoginDisabled)
13111	require.True(t, user1.Filters.DisableFsChecks)
13112	require.True(t, user2.Filters.DisableFsChecks)
13113}
13114
13115func TestFolderTemplateMock(t *testing.T) {
13116	folderName := "vfolder-template"
13117	mappedPath := filepath.Join(os.TempDir(), "%name%mapped%name%path")
13118	token, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
13119	assert.NoError(t, err)
13120	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
13121	assert.NoError(t, err)
13122	form := make(url.Values)
13123	form.Set("name", folderName)
13124	form.Set("mapped_path", mappedPath)
13125	form.Set("description", "desc folder %name%")
13126	form.Add("tpl_foldername", "folder1")
13127	form.Add("tpl_foldername", "folder2")
13128	form.Add("tpl_foldername", "folder3")
13129	form.Add("tpl_foldername", "folder1 ")
13130	form.Add("tpl_foldername", " ")
13131	b, contentType, _ := getMultipartFormData(form, "", "")
13132	req, _ := http.NewRequest(http.MethodPost, webTemplateFolder, &b)
13133	setJWTCookieForReq(req, token)
13134	req.Header.Set("Content-Type", contentType)
13135	rr := executeRequest(req)
13136	checkResponseCode(t, http.StatusForbidden, rr)
13137	assert.Contains(t, rr.Body.String(), "unable to verify form token")
13138
13139	form.Set(csrfFormToken, csrfToken)
13140	b, contentType, _ = getMultipartFormData(form, "", "")
13141	req, _ = http.NewRequest(http.MethodPost, webTemplateFolder+"?param=p%C3%AO%GG", &b)
13142	setJWTCookieForReq(req, token)
13143	req.Header.Set("Content-Type", contentType)
13144	rr = executeRequest(req)
13145	checkResponseCode(t, http.StatusBadRequest, rr)
13146	assert.Contains(t, rr.Body.String(), "Error parsing folders fields")
13147
13148	folder1 := "folder1"
13149	folder2 := "folder2"
13150	folder3 := "folder3"
13151	b, contentType, _ = getMultipartFormData(form, "", "")
13152	req, _ = http.NewRequest(http.MethodPost, webTemplateFolder, &b)
13153	setJWTCookieForReq(req, token)
13154	req.Header.Set("Content-Type", contentType)
13155	rr = executeRequest(req)
13156	checkResponseCode(t, http.StatusOK, rr)
13157
13158	var dump dataprovider.BackupData
13159	err = json.Unmarshal(rr.Body.Bytes(), &dump)
13160	require.NoError(t, err)
13161	require.Len(t, dump.Users, 0)
13162	require.Len(t, dump.Admins, 0)
13163	require.Len(t, dump.Folders, 3)
13164	require.Equal(t, folder1, dump.Folders[0].Name)
13165	require.Equal(t, "desc folder folder1", dump.Folders[0].Description)
13166	require.True(t, strings.HasSuffix(dump.Folders[0].MappedPath, "folder1mappedfolder1path"))
13167	require.Equal(t, folder2, dump.Folders[1].Name)
13168	require.Equal(t, "desc folder folder2", dump.Folders[1].Description)
13169	require.True(t, strings.HasSuffix(dump.Folders[1].MappedPath, "folder2mappedfolder2path"))
13170	require.Equal(t, folder3, dump.Folders[2].Name)
13171	require.Equal(t, "desc folder folder3", dump.Folders[2].Description)
13172	require.True(t, strings.HasSuffix(dump.Folders[2].MappedPath, "folder3mappedfolder3path"))
13173
13174	form.Set("fs_provider", "1")
13175	form.Set("s3_bucket", "bucket")
13176	form.Set("s3_region", "us-east-1")
13177	form.Set("s3_access_key", "%name%")
13178	form.Set("s3_access_secret", "pwd%name%")
13179	form.Set("s3_key_prefix", "base/%name%")
13180
13181	b, contentType, _ = getMultipartFormData(form, "", "")
13182	req, _ = http.NewRequest(http.MethodPost, webTemplateFolder, &b)
13183	setJWTCookieForReq(req, token)
13184	req.Header.Set("Content-Type", contentType)
13185	rr = executeRequest(req)
13186	checkResponseCode(t, http.StatusBadRequest, rr)
13187	assert.Contains(t, rr.Body.String(), "Error parsing folders fields")
13188
13189	form.Set("s3_upload_part_size", "5")
13190	form.Set("s3_upload_concurrency", "4")
13191	form.Set("s3_download_part_max_time", "0")
13192	form.Set("s3_download_part_size", "6")
13193	form.Set("s3_download_concurrency", "2")
13194	b, contentType, _ = getMultipartFormData(form, "", "")
13195	req, _ = http.NewRequest(http.MethodPost, webTemplateFolder, &b)
13196	setJWTCookieForReq(req, token)
13197	req.Header.Set("Content-Type", contentType)
13198	rr = executeRequest(req)
13199	checkResponseCode(t, http.StatusOK, rr)
13200
13201	dump = dataprovider.BackupData{}
13202	err = json.Unmarshal(rr.Body.Bytes(), &dump)
13203	require.NoError(t, err)
13204	require.Len(t, dump.Users, 0)
13205	require.Len(t, dump.Admins, 0)
13206	require.Len(t, dump.Folders, 3)
13207	require.Equal(t, folder1, dump.Folders[0].Name)
13208	require.Equal(t, folder1, dump.Folders[0].FsConfig.S3Config.AccessKey)
13209	err = dump.Folders[0].FsConfig.S3Config.AccessSecret.Decrypt()
13210	require.NoError(t, err)
13211	require.Equal(t, "pwd"+folder1, dump.Folders[0].FsConfig.S3Config.AccessSecret.GetPayload())
13212	require.Equal(t, "base/"+folder1+"/", dump.Folders[0].FsConfig.S3Config.KeyPrefix)
13213	require.Equal(t, folder2, dump.Folders[1].Name)
13214	require.Equal(t, folder2, dump.Folders[1].FsConfig.S3Config.AccessKey)
13215	err = dump.Folders[1].FsConfig.S3Config.AccessSecret.Decrypt()
13216	require.NoError(t, err)
13217	require.Equal(t, "pwd"+folder2, dump.Folders[1].FsConfig.S3Config.AccessSecret.GetPayload())
13218	require.Equal(t, "base/"+folder2+"/", dump.Folders[1].FsConfig.S3Config.KeyPrefix)
13219	require.Equal(t, folder3, dump.Folders[2].Name)
13220	require.Equal(t, folder3, dump.Folders[2].FsConfig.S3Config.AccessKey)
13221	err = dump.Folders[2].FsConfig.S3Config.AccessSecret.Decrypt()
13222	require.NoError(t, err)
13223	require.Equal(t, "pwd"+folder3, dump.Folders[2].FsConfig.S3Config.AccessSecret.GetPayload())
13224	require.Equal(t, "base/"+folder3+"/", dump.Folders[2].FsConfig.S3Config.KeyPrefix)
13225
13226	form.Set("tpl_foldername", " ")
13227	b, contentType, _ = getMultipartFormData(form, "", "")
13228	req, _ = http.NewRequest(http.MethodPost, webTemplateFolder, &b)
13229	setJWTCookieForReq(req, token)
13230	req.Header.Set("Content-Type", contentType)
13231	rr = executeRequest(req)
13232	checkResponseCode(t, http.StatusBadRequest, rr)
13233	assert.Contains(t, rr.Body.String(), "No folders to export")
13234
13235	form.Set("tpl_foldername", "name")
13236	form.Set("mapped_path", "relative-path")
13237	b, contentType, _ = getMultipartFormData(form, "", "")
13238	req, _ = http.NewRequest(http.MethodPost, webTemplateFolder, &b)
13239	setJWTCookieForReq(req, token)
13240	req.Header.Set("Content-Type", contentType)
13241	rr = executeRequest(req)
13242	checkResponseCode(t, http.StatusBadRequest, rr)
13243	assert.Contains(t, rr.Body.String(), "Error validating folder")
13244}
13245
13246func TestWebUserS3Mock(t *testing.T) {
13247	webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
13248	assert.NoError(t, err)
13249	apiToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
13250	assert.NoError(t, err)
13251	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
13252	assert.NoError(t, err)
13253	user := getTestUser()
13254	userAsJSON := getUserAsJSON(t, user)
13255	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
13256	setBearerForReq(req, apiToken)
13257	rr := executeRequest(req)
13258	checkResponseCode(t, http.StatusCreated, rr)
13259	err = render.DecodeJSON(rr.Body, &user)
13260	assert.NoError(t, err)
13261	user.FsConfig.Provider = sdk.S3FilesystemProvider
13262	user.FsConfig.S3Config.Bucket = "test"
13263	user.FsConfig.S3Config.Region = "eu-west-1"
13264	user.FsConfig.S3Config.AccessKey = "access-key"
13265	user.FsConfig.S3Config.AccessSecret = kms.NewPlainSecret("access-secret")
13266	user.FsConfig.S3Config.Endpoint = "http://127.0.0.1:9000/path?a=b"
13267	user.FsConfig.S3Config.StorageClass = "Standard"
13268	user.FsConfig.S3Config.KeyPrefix = "somedir/subdir/"
13269	user.FsConfig.S3Config.UploadPartSize = 5
13270	user.FsConfig.S3Config.UploadConcurrency = 4
13271	user.FsConfig.S3Config.DownloadPartMaxTime = 60
13272	user.FsConfig.S3Config.DownloadPartSize = 6
13273	user.FsConfig.S3Config.DownloadConcurrency = 3
13274	user.FsConfig.S3Config.ForcePathStyle = true
13275	user.FsConfig.S3Config.ACL = "public-read"
13276	user.Description = "s3 tèst user"
13277	form := make(url.Values)
13278	form.Set(csrfFormToken, csrfToken)
13279	form.Set("username", user.Username)
13280	form.Set("password", redactedSecret)
13281	form.Set("home_dir", user.HomeDir)
13282	form.Set("uid", "0")
13283	form.Set("gid", strconv.FormatInt(int64(user.GID), 10))
13284	form.Set("max_sessions", strconv.FormatInt(int64(user.MaxSessions), 10))
13285	form.Set("quota_size", strconv.FormatInt(user.QuotaSize, 10))
13286	form.Set("quota_files", strconv.FormatInt(int64(user.QuotaFiles), 10))
13287	form.Set("upload_bandwidth", "0")
13288	form.Set("download_bandwidth", "0")
13289	form.Set("permissions", "*")
13290	form.Set("status", strconv.Itoa(user.Status))
13291	form.Set("expiration_date", "2020-01-01 00:00:00")
13292	form.Set("allowed_ip", "")
13293	form.Set("denied_ip", "")
13294	form.Set("fs_provider", "1")
13295	form.Set("s3_bucket", user.FsConfig.S3Config.Bucket)
13296	form.Set("s3_region", user.FsConfig.S3Config.Region)
13297	form.Set("s3_access_key", user.FsConfig.S3Config.AccessKey)
13298	form.Set("s3_access_secret", user.FsConfig.S3Config.AccessSecret.GetPayload())
13299	form.Set("s3_storage_class", user.FsConfig.S3Config.StorageClass)
13300	form.Set("s3_acl", user.FsConfig.S3Config.ACL)
13301	form.Set("s3_endpoint", user.FsConfig.S3Config.Endpoint)
13302	form.Set("s3_key_prefix", user.FsConfig.S3Config.KeyPrefix)
13303	form.Set("pattern_path0", "/dir1")
13304	form.Set("patterns0", "*.jpg,*.png")
13305	form.Set("pattern_type0", "allowed")
13306	form.Set("pattern_path1", "/dir2")
13307	form.Set("patterns1", "*.zip")
13308	form.Set("pattern_type1", "denied")
13309	form.Set("max_upload_file_size", "0")
13310	form.Set("s3_force_path_style", "checked")
13311	form.Set("description", user.Description)
13312	form.Add("hooks", "pre_login_disabled")
13313	form.Add("allow_api_key_auth", "1")
13314	// test invalid s3_upload_part_size
13315	form.Set("s3_upload_part_size", "a")
13316	b, contentType, _ := getMultipartFormData(form, "", "")
13317	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13318	setJWTCookieForReq(req, webToken)
13319	req.Header.Set("Content-Type", contentType)
13320	rr = executeRequest(req)
13321	checkResponseCode(t, http.StatusOK, rr)
13322	// test invalid s3_upload_concurrency
13323	form.Set("s3_upload_part_size", strconv.FormatInt(user.FsConfig.S3Config.UploadPartSize, 10))
13324	form.Set("s3_upload_concurrency", "a")
13325	b, contentType, _ = getMultipartFormData(form, "", "")
13326	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13327	setJWTCookieForReq(req, webToken)
13328	req.Header.Set("Content-Type", contentType)
13329	rr = executeRequest(req)
13330	checkResponseCode(t, http.StatusOK, rr)
13331	// test invalid s3_download_part_size
13332	form.Set("s3_upload_concurrency", strconv.Itoa(user.FsConfig.S3Config.UploadConcurrency))
13333	form.Set("s3_download_part_size", "a")
13334	b, contentType, _ = getMultipartFormData(form, "", "")
13335	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13336	setJWTCookieForReq(req, webToken)
13337	req.Header.Set("Content-Type", contentType)
13338	rr = executeRequest(req)
13339	checkResponseCode(t, http.StatusOK, rr)
13340	// test invalid s3_download_concurrency
13341	form.Set("s3_download_part_size", strconv.FormatInt(user.FsConfig.S3Config.DownloadPartSize, 10))
13342	form.Set("s3_download_concurrency", "a")
13343	b, contentType, _ = getMultipartFormData(form, "", "")
13344	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13345	setJWTCookieForReq(req, webToken)
13346	req.Header.Set("Content-Type", contentType)
13347	rr = executeRequest(req)
13348	checkResponseCode(t, http.StatusOK, rr)
13349	// test invalid s3_download_part_max_time
13350	form.Set("s3_download_concurrency", strconv.Itoa(user.FsConfig.S3Config.DownloadConcurrency))
13351	form.Set("s3_download_part_max_time", "a")
13352	b, contentType, _ = getMultipartFormData(form, "", "")
13353	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13354	setJWTCookieForReq(req, webToken)
13355	req.Header.Set("Content-Type", contentType)
13356	rr = executeRequest(req)
13357	checkResponseCode(t, http.StatusOK, rr)
13358	// now add the user
13359	form.Set("s3_download_part_max_time", strconv.Itoa(user.FsConfig.S3Config.DownloadPartMaxTime))
13360	b, contentType, _ = getMultipartFormData(form, "", "")
13361	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13362	setJWTCookieForReq(req, webToken)
13363	req.Header.Set("Content-Type", contentType)
13364	rr = executeRequest(req)
13365	checkResponseCode(t, http.StatusSeeOther, rr)
13366	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
13367	setBearerForReq(req, apiToken)
13368	rr = executeRequest(req)
13369	checkResponseCode(t, http.StatusOK, rr)
13370	var updateUser dataprovider.User
13371	err = render.DecodeJSON(rr.Body, &updateUser)
13372	assert.NoError(t, err)
13373	assert.Equal(t, int64(1577836800000), updateUser.ExpirationDate)
13374	assert.Equal(t, updateUser.FsConfig.S3Config.Bucket, user.FsConfig.S3Config.Bucket)
13375	assert.Equal(t, updateUser.FsConfig.S3Config.Region, user.FsConfig.S3Config.Region)
13376	assert.Equal(t, updateUser.FsConfig.S3Config.AccessKey, user.FsConfig.S3Config.AccessKey)
13377	assert.Equal(t, updateUser.FsConfig.S3Config.StorageClass, user.FsConfig.S3Config.StorageClass)
13378	assert.Equal(t, updateUser.FsConfig.S3Config.ACL, user.FsConfig.S3Config.ACL)
13379	assert.Equal(t, updateUser.FsConfig.S3Config.Endpoint, user.FsConfig.S3Config.Endpoint)
13380	assert.Equal(t, updateUser.FsConfig.S3Config.KeyPrefix, user.FsConfig.S3Config.KeyPrefix)
13381	assert.Equal(t, updateUser.FsConfig.S3Config.UploadPartSize, user.FsConfig.S3Config.UploadPartSize)
13382	assert.Equal(t, updateUser.FsConfig.S3Config.UploadConcurrency, user.FsConfig.S3Config.UploadConcurrency)
13383	assert.Equal(t, updateUser.FsConfig.S3Config.DownloadPartMaxTime, user.FsConfig.S3Config.DownloadPartMaxTime)
13384	assert.Equal(t, updateUser.FsConfig.S3Config.DownloadPartSize, user.FsConfig.S3Config.DownloadPartSize)
13385	assert.Equal(t, updateUser.FsConfig.S3Config.DownloadConcurrency, user.FsConfig.S3Config.DownloadConcurrency)
13386	assert.True(t, updateUser.FsConfig.S3Config.ForcePathStyle)
13387	assert.Equal(t, 2, len(updateUser.Filters.FilePatterns))
13388	assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.S3Config.AccessSecret.GetStatus())
13389	assert.NotEmpty(t, updateUser.FsConfig.S3Config.AccessSecret.GetPayload())
13390	assert.Empty(t, updateUser.FsConfig.S3Config.AccessSecret.GetKey())
13391	assert.Empty(t, updateUser.FsConfig.S3Config.AccessSecret.GetAdditionalData())
13392	assert.Equal(t, user.Description, updateUser.Description)
13393	assert.True(t, updateUser.Filters.Hooks.PreLoginDisabled)
13394	assert.False(t, updateUser.Filters.Hooks.ExternalAuthDisabled)
13395	assert.False(t, updateUser.Filters.Hooks.CheckPasswordDisabled)
13396	assert.False(t, updateUser.Filters.DisableFsChecks)
13397	assert.True(t, updateUser.Filters.AllowAPIKeyAuth)
13398	// now check that a redacted password is not saved
13399	form.Set("s3_access_secret", redactedSecret)
13400	b, contentType, _ = getMultipartFormData(form, "", "")
13401	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13402	setJWTCookieForReq(req, webToken)
13403	req.Header.Set("Content-Type", contentType)
13404	rr = executeRequest(req)
13405	checkResponseCode(t, http.StatusSeeOther, rr)
13406	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
13407	setBearerForReq(req, apiToken)
13408	rr = executeRequest(req)
13409	checkResponseCode(t, http.StatusOK, rr)
13410
13411	var lastUpdatedUser dataprovider.User
13412	err = render.DecodeJSON(rr.Body, &lastUpdatedUser)
13413	assert.NoError(t, err)
13414	assert.Equal(t, kms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.S3Config.AccessSecret.GetStatus())
13415	assert.Equal(t, updateUser.FsConfig.S3Config.AccessSecret.GetPayload(), lastUpdatedUser.FsConfig.S3Config.AccessSecret.GetPayload())
13416	assert.Empty(t, lastUpdatedUser.FsConfig.S3Config.AccessSecret.GetKey())
13417	assert.Empty(t, lastUpdatedUser.FsConfig.S3Config.AccessSecret.GetAdditionalData())
13418	// now clear credentials
13419	form.Set("s3_access_key", "")
13420	form.Set("s3_access_secret", "")
13421	b, contentType, _ = getMultipartFormData(form, "", "")
13422	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13423	setJWTCookieForReq(req, webToken)
13424	req.Header.Set("Content-Type", contentType)
13425	rr = executeRequest(req)
13426	checkResponseCode(t, http.StatusSeeOther, rr)
13427	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
13428	setBearerForReq(req, apiToken)
13429	rr = executeRequest(req)
13430	checkResponseCode(t, http.StatusOK, rr)
13431	var userGet dataprovider.User
13432	err = render.DecodeJSON(rr.Body, &userGet)
13433	assert.NoError(t, err)
13434	assert.Nil(t, userGet.FsConfig.S3Config.AccessSecret)
13435
13436	req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
13437	setBearerForReq(req, apiToken)
13438	rr = executeRequest(req)
13439	checkResponseCode(t, http.StatusOK, rr)
13440}
13441
13442func TestWebUserGCSMock(t *testing.T) {
13443	webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
13444	assert.NoError(t, err)
13445	apiToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
13446	assert.NoError(t, err)
13447	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
13448	assert.NoError(t, err)
13449	user := getTestUser()
13450	userAsJSON := getUserAsJSON(t, user)
13451	req, err := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
13452	assert.NoError(t, err)
13453	setBearerForReq(req, apiToken)
13454	rr := executeRequest(req)
13455	checkResponseCode(t, http.StatusCreated, rr)
13456	err = render.DecodeJSON(rr.Body, &user)
13457	assert.NoError(t, err)
13458	credentialsFilePath := filepath.Join(os.TempDir(), "gcs.json")
13459	err = createTestFile(credentialsFilePath, 0)
13460	assert.NoError(t, err)
13461	user.FsConfig.Provider = sdk.GCSFilesystemProvider
13462	user.FsConfig.GCSConfig.Bucket = "test"
13463	user.FsConfig.GCSConfig.KeyPrefix = "somedir/subdir/"
13464	user.FsConfig.GCSConfig.StorageClass = "standard"
13465	user.FsConfig.GCSConfig.ACL = "publicReadWrite"
13466	form := make(url.Values)
13467	form.Set(csrfFormToken, csrfToken)
13468	form.Set("username", user.Username)
13469	form.Set("password", redactedSecret)
13470	form.Set("home_dir", user.HomeDir)
13471	form.Set("uid", "0")
13472	form.Set("gid", strconv.FormatInt(int64(user.GID), 10))
13473	form.Set("max_sessions", strconv.FormatInt(int64(user.MaxSessions), 10))
13474	form.Set("quota_size", strconv.FormatInt(user.QuotaSize, 10))
13475	form.Set("quota_files", strconv.FormatInt(int64(user.QuotaFiles), 10))
13476	form.Set("upload_bandwidth", "0")
13477	form.Set("download_bandwidth", "0")
13478	form.Set("permissions", "*")
13479	form.Set("status", strconv.Itoa(user.Status))
13480	form.Set("expiration_date", "2020-01-01 00:00:00")
13481	form.Set("allowed_ip", "")
13482	form.Set("denied_ip", "")
13483	form.Set("fs_provider", "2")
13484	form.Set("gcs_bucket", user.FsConfig.GCSConfig.Bucket)
13485	form.Set("gcs_storage_class", user.FsConfig.GCSConfig.StorageClass)
13486	form.Set("gcs_acl", user.FsConfig.GCSConfig.ACL)
13487	form.Set("gcs_key_prefix", user.FsConfig.GCSConfig.KeyPrefix)
13488	form.Set("pattern_path0", "/dir1")
13489	form.Set("patterns0", "*.jpg,*.png")
13490	form.Set("pattern_type0", "allowed")
13491	form.Set("max_upload_file_size", "0")
13492	b, contentType, _ := getMultipartFormData(form, "", "")
13493	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13494	setJWTCookieForReq(req, webToken)
13495	req.Header.Set("Content-Type", contentType)
13496	rr = executeRequest(req)
13497	checkResponseCode(t, http.StatusOK, rr)
13498	b, contentType, _ = getMultipartFormData(form, "gcs_credential_file", credentialsFilePath)
13499	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13500	setJWTCookieForReq(req, webToken)
13501	req.Header.Set("Content-Type", contentType)
13502	rr = executeRequest(req)
13503	checkResponseCode(t, http.StatusOK, rr)
13504	err = createTestFile(credentialsFilePath, 4096)
13505	assert.NoError(t, err)
13506	b, contentType, _ = getMultipartFormData(form, "gcs_credential_file", credentialsFilePath)
13507	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13508	setJWTCookieForReq(req, webToken)
13509	req.Header.Set("Content-Type", contentType)
13510	rr = executeRequest(req)
13511	checkResponseCode(t, http.StatusSeeOther, rr)
13512	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
13513	setBearerForReq(req, apiToken)
13514	rr = executeRequest(req)
13515	checkResponseCode(t, http.StatusOK, rr)
13516	var updateUser dataprovider.User
13517	err = render.DecodeJSON(rr.Body, &updateUser)
13518	assert.NoError(t, err)
13519	assert.Equal(t, int64(1577836800000), updateUser.ExpirationDate)
13520	assert.Equal(t, user.FsConfig.Provider, updateUser.FsConfig.Provider)
13521	assert.Equal(t, user.FsConfig.GCSConfig.Bucket, updateUser.FsConfig.GCSConfig.Bucket)
13522	assert.Equal(t, user.FsConfig.GCSConfig.StorageClass, updateUser.FsConfig.GCSConfig.StorageClass)
13523	assert.Equal(t, user.FsConfig.GCSConfig.ACL, updateUser.FsConfig.GCSConfig.ACL)
13524	assert.Equal(t, user.FsConfig.GCSConfig.KeyPrefix, updateUser.FsConfig.GCSConfig.KeyPrefix)
13525	if assert.Len(t, updateUser.Filters.FilePatterns, 1) {
13526		assert.Equal(t, "/dir1", updateUser.Filters.FilePatterns[0].Path)
13527		assert.Len(t, updateUser.Filters.FilePatterns[0].AllowedPatterns, 2)
13528		assert.Contains(t, updateUser.Filters.FilePatterns[0].AllowedPatterns, "*.png")
13529		assert.Contains(t, updateUser.Filters.FilePatterns[0].AllowedPatterns, "*.jpg")
13530	}
13531	form.Set("gcs_auto_credentials", "on")
13532	b, contentType, _ = getMultipartFormData(form, "", "")
13533	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13534	setJWTCookieForReq(req, webToken)
13535	req.Header.Set("Content-Type", contentType)
13536	rr = executeRequest(req)
13537	checkResponseCode(t, http.StatusSeeOther, rr)
13538	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
13539	setBearerForReq(req, apiToken)
13540	rr = executeRequest(req)
13541	checkResponseCode(t, http.StatusOK, rr)
13542	updateUser = dataprovider.User{}
13543	err = render.DecodeJSON(rr.Body, &updateUser)
13544	assert.NoError(t, err)
13545	assert.Equal(t, 1, updateUser.FsConfig.GCSConfig.AutomaticCredentials)
13546	req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
13547	setBearerForReq(req, apiToken)
13548	rr = executeRequest(req)
13549	checkResponseCode(t, http.StatusOK, rr)
13550	err = os.Remove(credentialsFilePath)
13551	assert.NoError(t, err)
13552}
13553
13554func TestWebUserAzureBlobMock(t *testing.T) {
13555	webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
13556	assert.NoError(t, err)
13557	apiToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
13558	assert.NoError(t, err)
13559	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
13560	assert.NoError(t, err)
13561	user := getTestUser()
13562	userAsJSON := getUserAsJSON(t, user)
13563	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
13564	setBearerForReq(req, apiToken)
13565	rr := executeRequest(req)
13566	checkResponseCode(t, http.StatusCreated, rr)
13567	err = render.DecodeJSON(rr.Body, &user)
13568	assert.NoError(t, err)
13569	user.FsConfig.Provider = sdk.AzureBlobFilesystemProvider
13570	user.FsConfig.AzBlobConfig.Container = "container"
13571	user.FsConfig.AzBlobConfig.AccountName = "aname"
13572	user.FsConfig.AzBlobConfig.AccountKey = kms.NewPlainSecret("access-skey")
13573	user.FsConfig.AzBlobConfig.Endpoint = "http://127.0.0.1:9000/path?b=c"
13574	user.FsConfig.AzBlobConfig.KeyPrefix = "somedir/subdir/"
13575	user.FsConfig.AzBlobConfig.UploadPartSize = 5
13576	user.FsConfig.AzBlobConfig.UploadConcurrency = 4
13577	user.FsConfig.AzBlobConfig.UseEmulator = true
13578	form := make(url.Values)
13579	form.Set(csrfFormToken, csrfToken)
13580	form.Set("username", user.Username)
13581	form.Set("password", redactedSecret)
13582	form.Set("home_dir", user.HomeDir)
13583	form.Set("uid", "0")
13584	form.Set("gid", strconv.FormatInt(int64(user.GID), 10))
13585	form.Set("max_sessions", strconv.FormatInt(int64(user.MaxSessions), 10))
13586	form.Set("quota_size", strconv.FormatInt(user.QuotaSize, 10))
13587	form.Set("quota_files", strconv.FormatInt(int64(user.QuotaFiles), 10))
13588	form.Set("upload_bandwidth", "0")
13589	form.Set("download_bandwidth", "0")
13590	form.Set("permissions", "*")
13591	form.Set("status", strconv.Itoa(user.Status))
13592	form.Set("expiration_date", "2020-01-01 00:00:00")
13593	form.Set("allowed_ip", "")
13594	form.Set("denied_ip", "")
13595	form.Set("fs_provider", "3")
13596	form.Set("az_container", user.FsConfig.AzBlobConfig.Container)
13597	form.Set("az_account_name", user.FsConfig.AzBlobConfig.AccountName)
13598	form.Set("az_account_key", user.FsConfig.AzBlobConfig.AccountKey.GetPayload())
13599	form.Set("az_endpoint", user.FsConfig.AzBlobConfig.Endpoint)
13600	form.Set("az_key_prefix", user.FsConfig.AzBlobConfig.KeyPrefix)
13601	form.Set("az_use_emulator", "checked")
13602	form.Set("pattern_path0", "/dir1")
13603	form.Set("patterns0", "*.jpg,*.png")
13604	form.Set("pattern_type0", "allowed")
13605	form.Set("pattern_path1", "/dir2")
13606	form.Set("patterns1", "*.zip")
13607	form.Set("pattern_type1", "denied")
13608	form.Set("max_upload_file_size", "0")
13609	// test invalid az_upload_part_size
13610	form.Set("az_upload_part_size", "a")
13611	b, contentType, _ := getMultipartFormData(form, "", "")
13612	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13613	setJWTCookieForReq(req, webToken)
13614	req.Header.Set("Content-Type", contentType)
13615	rr = executeRequest(req)
13616	checkResponseCode(t, http.StatusOK, rr)
13617	// test invalid az_upload_concurrency
13618	form.Set("az_upload_part_size", strconv.FormatInt(user.FsConfig.AzBlobConfig.UploadPartSize, 10))
13619	form.Set("az_upload_concurrency", "a")
13620	b, contentType, _ = getMultipartFormData(form, "", "")
13621	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13622	setJWTCookieForReq(req, webToken)
13623	req.Header.Set("Content-Type", contentType)
13624	rr = executeRequest(req)
13625	checkResponseCode(t, http.StatusOK, rr)
13626	// now add the user
13627	form.Set("az_upload_concurrency", strconv.Itoa(user.FsConfig.AzBlobConfig.UploadConcurrency))
13628	b, contentType, _ = getMultipartFormData(form, "", "")
13629	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13630	setJWTCookieForReq(req, webToken)
13631	req.Header.Set("Content-Type", contentType)
13632	rr = executeRequest(req)
13633	checkResponseCode(t, http.StatusSeeOther, rr)
13634	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
13635	setBearerForReq(req, apiToken)
13636	rr = executeRequest(req)
13637	checkResponseCode(t, http.StatusOK, rr)
13638	var updateUser dataprovider.User
13639	err = render.DecodeJSON(rr.Body, &updateUser)
13640	assert.NoError(t, err)
13641	assert.Equal(t, int64(1577836800000), updateUser.ExpirationDate)
13642	assert.Equal(t, updateUser.FsConfig.AzBlobConfig.Container, user.FsConfig.AzBlobConfig.Container)
13643	assert.Equal(t, updateUser.FsConfig.AzBlobConfig.AccountName, user.FsConfig.AzBlobConfig.AccountName)
13644	assert.Equal(t, updateUser.FsConfig.AzBlobConfig.Endpoint, user.FsConfig.AzBlobConfig.Endpoint)
13645	assert.Equal(t, updateUser.FsConfig.AzBlobConfig.KeyPrefix, user.FsConfig.AzBlobConfig.KeyPrefix)
13646	assert.Equal(t, updateUser.FsConfig.AzBlobConfig.UploadPartSize, user.FsConfig.AzBlobConfig.UploadPartSize)
13647	assert.Equal(t, updateUser.FsConfig.AzBlobConfig.UploadConcurrency, user.FsConfig.AzBlobConfig.UploadConcurrency)
13648	assert.Equal(t, 2, len(updateUser.Filters.FilePatterns))
13649	assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.AzBlobConfig.AccountKey.GetStatus())
13650	assert.NotEmpty(t, updateUser.FsConfig.AzBlobConfig.AccountKey.GetPayload())
13651	assert.Empty(t, updateUser.FsConfig.AzBlobConfig.AccountKey.GetKey())
13652	assert.Empty(t, updateUser.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
13653	// now check that a redacted password is not saved
13654	form.Set("az_account_key", redactedSecret+" ")
13655	b, contentType, _ = getMultipartFormData(form, "", "")
13656	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13657	setJWTCookieForReq(req, webToken)
13658	req.Header.Set("Content-Type", contentType)
13659	rr = executeRequest(req)
13660	checkResponseCode(t, http.StatusSeeOther, rr)
13661	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
13662	setBearerForReq(req, apiToken)
13663	rr = executeRequest(req)
13664	checkResponseCode(t, http.StatusOK, rr)
13665	var lastUpdatedUser dataprovider.User
13666	err = render.DecodeJSON(rr.Body, &lastUpdatedUser)
13667	assert.NoError(t, err)
13668	assert.Equal(t, kms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.AzBlobConfig.AccountKey.GetStatus())
13669	assert.Equal(t, updateUser.FsConfig.AzBlobConfig.AccountKey.GetPayload(), lastUpdatedUser.FsConfig.AzBlobConfig.AccountKey.GetPayload())
13670	assert.Empty(t, lastUpdatedUser.FsConfig.AzBlobConfig.AccountKey.GetKey())
13671	assert.Empty(t, lastUpdatedUser.FsConfig.AzBlobConfig.AccountKey.GetAdditionalData())
13672	// test SAS url
13673	user.FsConfig.AzBlobConfig.SASURL = kms.NewPlainSecret("sasurl")
13674	form.Set("az_account_name", "")
13675	form.Set("az_account_key", "")
13676	form.Set("az_container", "")
13677	form.Set("az_sas_url", user.FsConfig.AzBlobConfig.SASURL.GetPayload())
13678	b, contentType, _ = getMultipartFormData(form, "", "")
13679	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13680	setJWTCookieForReq(req, webToken)
13681	req.Header.Set("Content-Type", contentType)
13682	rr = executeRequest(req)
13683	checkResponseCode(t, http.StatusSeeOther, rr)
13684	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
13685	setBearerForReq(req, apiToken)
13686	rr = executeRequest(req)
13687	checkResponseCode(t, http.StatusOK, rr)
13688	updateUser = dataprovider.User{}
13689	err = render.DecodeJSON(rr.Body, &updateUser)
13690	assert.NoError(t, err)
13691	assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.AzBlobConfig.SASURL.GetStatus())
13692	assert.NotEmpty(t, updateUser.FsConfig.AzBlobConfig.SASURL.GetPayload())
13693	assert.Empty(t, updateUser.FsConfig.AzBlobConfig.SASURL.GetKey())
13694	assert.Empty(t, updateUser.FsConfig.AzBlobConfig.SASURL.GetAdditionalData())
13695	// now check that a redacted sas url is not saved
13696	form.Set("az_sas_url", redactedSecret)
13697	b, contentType, _ = getMultipartFormData(form, "", "")
13698	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13699	setJWTCookieForReq(req, webToken)
13700	req.Header.Set("Content-Type", contentType)
13701	rr = executeRequest(req)
13702	checkResponseCode(t, http.StatusSeeOther, rr)
13703	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
13704	setBearerForReq(req, apiToken)
13705	rr = executeRequest(req)
13706	checkResponseCode(t, http.StatusOK, rr)
13707	lastUpdatedUser = dataprovider.User{}
13708	err = render.DecodeJSON(rr.Body, &lastUpdatedUser)
13709	assert.NoError(t, err)
13710	assert.Equal(t, kms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.AzBlobConfig.SASURL.GetStatus())
13711	assert.Equal(t, updateUser.FsConfig.AzBlobConfig.SASURL.GetPayload(), lastUpdatedUser.FsConfig.AzBlobConfig.SASURL.GetPayload())
13712	assert.Empty(t, lastUpdatedUser.FsConfig.AzBlobConfig.SASURL.GetKey())
13713	assert.Empty(t, lastUpdatedUser.FsConfig.AzBlobConfig.SASURL.GetAdditionalData())
13714
13715	req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
13716	setBearerForReq(req, apiToken)
13717	rr = executeRequest(req)
13718	checkResponseCode(t, http.StatusOK, rr)
13719}
13720
13721func TestWebUserCryptMock(t *testing.T) {
13722	webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
13723	assert.NoError(t, err)
13724	apiToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
13725	assert.NoError(t, err)
13726	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
13727	assert.NoError(t, err)
13728	user := getTestUser()
13729	userAsJSON := getUserAsJSON(t, user)
13730	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
13731	setBearerForReq(req, apiToken)
13732	rr := executeRequest(req)
13733	checkResponseCode(t, http.StatusCreated, rr)
13734	err = render.DecodeJSON(rr.Body, &user)
13735	assert.NoError(t, err)
13736	user.FsConfig.Provider = sdk.CryptedFilesystemProvider
13737	user.FsConfig.CryptConfig.Passphrase = kms.NewPlainSecret("crypted passphrase")
13738	form := make(url.Values)
13739	form.Set(csrfFormToken, csrfToken)
13740	form.Set("username", user.Username)
13741	form.Set("password", redactedSecret)
13742	form.Set("home_dir", user.HomeDir)
13743	form.Set("uid", "0")
13744	form.Set("gid", strconv.FormatInt(int64(user.GID), 10))
13745	form.Set("max_sessions", strconv.FormatInt(int64(user.MaxSessions), 10))
13746	form.Set("quota_size", strconv.FormatInt(user.QuotaSize, 10))
13747	form.Set("quota_files", strconv.FormatInt(int64(user.QuotaFiles), 10))
13748	form.Set("upload_bandwidth", "0")
13749	form.Set("download_bandwidth", "0")
13750	form.Set("permissions", "*")
13751	form.Set("status", strconv.Itoa(user.Status))
13752	form.Set("expiration_date", "2020-01-01 00:00:00")
13753	form.Set("allowed_ip", "")
13754	form.Set("denied_ip", "")
13755	form.Set("fs_provider", "4")
13756	form.Set("crypt_passphrase", "")
13757	form.Set("pattern_path0", "/dir1")
13758	form.Set("patterns0", "*.jpg,*.png")
13759	form.Set("pattern_type0", "allowed")
13760	form.Set("pattern_path1", "/dir2")
13761	form.Set("patterns1", "*.zip")
13762	form.Set("pattern_type1", "denied")
13763	form.Set("max_upload_file_size", "0")
13764	// passphrase cannot be empty
13765	b, contentType, _ := getMultipartFormData(form, "", "")
13766	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13767	setJWTCookieForReq(req, webToken)
13768	req.Header.Set("Content-Type", contentType)
13769	rr = executeRequest(req)
13770	checkResponseCode(t, http.StatusOK, rr)
13771	form.Set("crypt_passphrase", user.FsConfig.CryptConfig.Passphrase.GetPayload())
13772	b, contentType, _ = getMultipartFormData(form, "", "")
13773	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13774	setJWTCookieForReq(req, webToken)
13775	req.Header.Set("Content-Type", contentType)
13776	rr = executeRequest(req)
13777	checkResponseCode(t, http.StatusSeeOther, rr)
13778	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
13779	setBearerForReq(req, apiToken)
13780	rr = executeRequest(req)
13781	checkResponseCode(t, http.StatusOK, rr)
13782	var updateUser dataprovider.User
13783	err = render.DecodeJSON(rr.Body, &updateUser)
13784	assert.NoError(t, err)
13785	assert.Equal(t, int64(1577836800000), updateUser.ExpirationDate)
13786	assert.Equal(t, 2, len(updateUser.Filters.FilePatterns))
13787	assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.CryptConfig.Passphrase.GetStatus())
13788	assert.NotEmpty(t, updateUser.FsConfig.CryptConfig.Passphrase.GetPayload())
13789	assert.Empty(t, updateUser.FsConfig.CryptConfig.Passphrase.GetKey())
13790	assert.Empty(t, updateUser.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
13791	// now check that a redacted password is not saved
13792	form.Set("crypt_passphrase", redactedSecret+" ")
13793	b, contentType, _ = getMultipartFormData(form, "", "")
13794	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13795	setJWTCookieForReq(req, webToken)
13796	req.Header.Set("Content-Type", contentType)
13797	rr = executeRequest(req)
13798	checkResponseCode(t, http.StatusSeeOther, rr)
13799	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
13800	setBearerForReq(req, apiToken)
13801	rr = executeRequest(req)
13802	checkResponseCode(t, http.StatusOK, rr)
13803	var lastUpdatedUser dataprovider.User
13804	err = render.DecodeJSON(rr.Body, &lastUpdatedUser)
13805	assert.NoError(t, err)
13806	assert.Equal(t, kms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.CryptConfig.Passphrase.GetStatus())
13807	assert.Equal(t, updateUser.FsConfig.CryptConfig.Passphrase.GetPayload(), lastUpdatedUser.FsConfig.CryptConfig.Passphrase.GetPayload())
13808	assert.Empty(t, lastUpdatedUser.FsConfig.CryptConfig.Passphrase.GetKey())
13809	assert.Empty(t, lastUpdatedUser.FsConfig.CryptConfig.Passphrase.GetAdditionalData())
13810	req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
13811	setBearerForReq(req, apiToken)
13812	rr = executeRequest(req)
13813	checkResponseCode(t, http.StatusOK, rr)
13814}
13815
13816func TestWebUserSFTPFsMock(t *testing.T) {
13817	webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
13818	assert.NoError(t, err)
13819	apiToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
13820	assert.NoError(t, err)
13821	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
13822	assert.NoError(t, err)
13823	user := getTestUser()
13824	userAsJSON := getUserAsJSON(t, user)
13825	req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
13826	setBearerForReq(req, apiToken)
13827	rr := executeRequest(req)
13828	checkResponseCode(t, http.StatusCreated, rr)
13829	err = render.DecodeJSON(rr.Body, &user)
13830	assert.NoError(t, err)
13831	user.FsConfig.Provider = sdk.SFTPFilesystemProvider
13832	user.FsConfig.SFTPConfig.Endpoint = "127.0.0.1:22"
13833	user.FsConfig.SFTPConfig.Username = "sftpuser"
13834	user.FsConfig.SFTPConfig.Password = kms.NewPlainSecret("pwd")
13835	user.FsConfig.SFTPConfig.PrivateKey = kms.NewPlainSecret(sftpPrivateKey)
13836	user.FsConfig.SFTPConfig.Fingerprints = []string{sftpPkeyFingerprint}
13837	user.FsConfig.SFTPConfig.Prefix = "/home/sftpuser"
13838	user.FsConfig.SFTPConfig.DisableCouncurrentReads = true
13839	user.FsConfig.SFTPConfig.BufferSize = 5
13840	form := make(url.Values)
13841	form.Set(csrfFormToken, csrfToken)
13842	form.Set("username", user.Username)
13843	form.Set("password", redactedSecret)
13844	form.Set("home_dir", user.HomeDir)
13845	form.Set("uid", "0")
13846	form.Set("gid", strconv.FormatInt(int64(user.GID), 10))
13847	form.Set("max_sessions", strconv.FormatInt(int64(user.MaxSessions), 10))
13848	form.Set("quota_size", strconv.FormatInt(user.QuotaSize, 10))
13849	form.Set("quota_files", strconv.FormatInt(int64(user.QuotaFiles), 10))
13850	form.Set("upload_bandwidth", "0")
13851	form.Set("download_bandwidth", "0")
13852	form.Set("permissions", "*")
13853	form.Set("status", strconv.Itoa(user.Status))
13854	form.Set("expiration_date", "2020-01-01 00:00:00")
13855	form.Set("allowed_ip", "")
13856	form.Set("denied_ip", "")
13857	form.Set("fs_provider", "5")
13858	form.Set("crypt_passphrase", "")
13859	form.Set("pattern_path0", "/dir1")
13860	form.Set("patterns0", "*.jpg,*.png")
13861	form.Set("pattern_type0", "allowed")
13862	form.Set("pattern_path1", "/dir2")
13863	form.Set("patterns1", "*.zip")
13864	form.Set("pattern_type1", "denied")
13865	form.Set("max_upload_file_size", "0")
13866	// empty sftpconfig
13867	b, contentType, _ := getMultipartFormData(form, "", "")
13868	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13869	setJWTCookieForReq(req, webToken)
13870	req.Header.Set("Content-Type", contentType)
13871	rr = executeRequest(req)
13872	checkResponseCode(t, http.StatusOK, rr)
13873	form.Set("sftp_endpoint", user.FsConfig.SFTPConfig.Endpoint)
13874	form.Set("sftp_username", user.FsConfig.SFTPConfig.Username)
13875	form.Set("sftp_password", user.FsConfig.SFTPConfig.Password.GetPayload())
13876	form.Set("sftp_private_key", user.FsConfig.SFTPConfig.PrivateKey.GetPayload())
13877	form.Set("sftp_fingerprints", user.FsConfig.SFTPConfig.Fingerprints[0])
13878	form.Set("sftp_prefix", user.FsConfig.SFTPConfig.Prefix)
13879	form.Set("sftp_disable_concurrent_reads", "true")
13880	form.Set("sftp_buffer_size", strconv.FormatInt(user.FsConfig.SFTPConfig.BufferSize, 10))
13881	b, contentType, _ = getMultipartFormData(form, "", "")
13882	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13883	setJWTCookieForReq(req, webToken)
13884	req.Header.Set("Content-Type", contentType)
13885	rr = executeRequest(req)
13886	checkResponseCode(t, http.StatusSeeOther, rr)
13887	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
13888	setBearerForReq(req, apiToken)
13889	rr = executeRequest(req)
13890	checkResponseCode(t, http.StatusOK, rr)
13891	var updateUser dataprovider.User
13892	err = render.DecodeJSON(rr.Body, &updateUser)
13893	assert.NoError(t, err)
13894	assert.Equal(t, int64(1577836800000), updateUser.ExpirationDate)
13895	assert.Equal(t, 2, len(updateUser.Filters.FilePatterns))
13896	assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.SFTPConfig.Password.GetStatus())
13897	assert.NotEmpty(t, updateUser.FsConfig.SFTPConfig.Password.GetPayload())
13898	assert.Empty(t, updateUser.FsConfig.SFTPConfig.Password.GetKey())
13899	assert.Empty(t, updateUser.FsConfig.SFTPConfig.Password.GetAdditionalData())
13900	assert.Equal(t, kms.SecretStatusSecretBox, updateUser.FsConfig.SFTPConfig.PrivateKey.GetStatus())
13901	assert.NotEmpty(t, updateUser.FsConfig.SFTPConfig.PrivateKey.GetPayload())
13902	assert.Empty(t, updateUser.FsConfig.SFTPConfig.PrivateKey.GetKey())
13903	assert.Empty(t, updateUser.FsConfig.SFTPConfig.PrivateKey.GetAdditionalData())
13904	assert.Equal(t, updateUser.FsConfig.SFTPConfig.Prefix, user.FsConfig.SFTPConfig.Prefix)
13905	assert.Equal(t, updateUser.FsConfig.SFTPConfig.Username, user.FsConfig.SFTPConfig.Username)
13906	assert.Equal(t, updateUser.FsConfig.SFTPConfig.Endpoint, user.FsConfig.SFTPConfig.Endpoint)
13907	assert.True(t, updateUser.FsConfig.SFTPConfig.DisableCouncurrentReads)
13908	assert.Len(t, updateUser.FsConfig.SFTPConfig.Fingerprints, 1)
13909	assert.Equal(t, user.FsConfig.SFTPConfig.BufferSize, updateUser.FsConfig.SFTPConfig.BufferSize)
13910	assert.Contains(t, updateUser.FsConfig.SFTPConfig.Fingerprints, sftpPkeyFingerprint)
13911	// now check that a redacted credentials are not saved
13912	form.Set("sftp_password", redactedSecret+" ")
13913	form.Set("sftp_private_key", redactedSecret)
13914	b, contentType, _ = getMultipartFormData(form, "", "")
13915	req, _ = http.NewRequest(http.MethodPost, path.Join(webUserPath, user.Username), &b)
13916	setJWTCookieForReq(req, webToken)
13917	req.Header.Set("Content-Type", contentType)
13918	rr = executeRequest(req)
13919	checkResponseCode(t, http.StatusSeeOther, rr)
13920	req, _ = http.NewRequest(http.MethodGet, path.Join(userPath, user.Username), nil)
13921	setBearerForReq(req, apiToken)
13922	rr = executeRequest(req)
13923	checkResponseCode(t, http.StatusOK, rr)
13924	var lastUpdatedUser dataprovider.User
13925	err = render.DecodeJSON(rr.Body, &lastUpdatedUser)
13926	assert.NoError(t, err)
13927	assert.Equal(t, kms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.SFTPConfig.Password.GetStatus())
13928	assert.Equal(t, updateUser.FsConfig.SFTPConfig.Password.GetPayload(), lastUpdatedUser.FsConfig.SFTPConfig.Password.GetPayload())
13929	assert.Empty(t, lastUpdatedUser.FsConfig.SFTPConfig.Password.GetKey())
13930	assert.Empty(t, lastUpdatedUser.FsConfig.SFTPConfig.Password.GetAdditionalData())
13931	assert.Equal(t, kms.SecretStatusSecretBox, lastUpdatedUser.FsConfig.SFTPConfig.PrivateKey.GetStatus())
13932	assert.Equal(t, updateUser.FsConfig.SFTPConfig.PrivateKey.GetPayload(), lastUpdatedUser.FsConfig.SFTPConfig.PrivateKey.GetPayload())
13933	assert.Empty(t, lastUpdatedUser.FsConfig.SFTPConfig.PrivateKey.GetKey())
13934	assert.Empty(t, lastUpdatedUser.FsConfig.SFTPConfig.PrivateKey.GetAdditionalData())
13935	req, _ = http.NewRequest(http.MethodDelete, path.Join(userPath, user.Username), nil)
13936	setBearerForReq(req, apiToken)
13937	rr = executeRequest(req)
13938	checkResponseCode(t, http.StatusOK, rr)
13939}
13940
13941func TestAddWebFoldersMock(t *testing.T) {
13942	webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
13943	assert.NoError(t, err)
13944	apiToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
13945	assert.NoError(t, err)
13946	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
13947	assert.NoError(t, err)
13948	mappedPath := filepath.Clean(os.TempDir())
13949	folderName := filepath.Base(mappedPath)
13950	folderDesc := "a simple desc"
13951	form := make(url.Values)
13952	form.Set("mapped_path", mappedPath)
13953	form.Set("name", folderName)
13954	form.Set("description", folderDesc)
13955	b, contentType, err := getMultipartFormData(form, "", "")
13956	assert.NoError(t, err)
13957	req, err := http.NewRequest(http.MethodPost, webFolderPath, &b)
13958	assert.NoError(t, err)
13959	req.Header.Set("Content-Type", contentType)
13960	setJWTCookieForReq(req, webToken)
13961	rr := executeRequest(req)
13962	checkResponseCode(t, http.StatusForbidden, rr)
13963	assert.Contains(t, rr.Body.String(), "unable to verify form token")
13964
13965	form.Set(csrfFormToken, csrfToken)
13966	b, contentType, err = getMultipartFormData(form, "", "")
13967	assert.NoError(t, err)
13968	req, err = http.NewRequest(http.MethodPost, webFolderPath, &b)
13969	assert.NoError(t, err)
13970	req.Header.Set("Content-Type", contentType)
13971	setJWTCookieForReq(req, webToken)
13972	rr = executeRequest(req)
13973	checkResponseCode(t, http.StatusSeeOther, rr)
13974	// adding the same folder will fail since the name must be unique
13975	b, contentType, err = getMultipartFormData(form, "", "")
13976	assert.NoError(t, err)
13977	req, err = http.NewRequest(http.MethodPost, webFolderPath, &b)
13978	assert.NoError(t, err)
13979	setJWTCookieForReq(req, webToken)
13980	req.Header.Set("Content-Type", contentType)
13981	rr = executeRequest(req)
13982	checkResponseCode(t, http.StatusOK, rr)
13983	// invalid form
13984	req, err = http.NewRequest(http.MethodPost, webFolderPath, strings.NewReader(form.Encode()))
13985	assert.NoError(t, err)
13986	setJWTCookieForReq(req, webToken)
13987	req.Header.Set("Content-Type", "text/plain; boundary=")
13988	rr = executeRequest(req)
13989	checkResponseCode(t, http.StatusOK, rr)
13990
13991	// now render the add folder page
13992	req, err = http.NewRequest(http.MethodGet, webFolderPath, nil)
13993	assert.NoError(t, err)
13994	setJWTCookieForReq(req, webToken)
13995	rr = executeRequest(req)
13996	checkResponseCode(t, http.StatusOK, rr)
13997
13998	var folder vfs.BaseVirtualFolder
13999	req, _ = http.NewRequest(http.MethodGet, path.Join(folderPath, folderName), nil)
14000	setBearerForReq(req, apiToken)
14001	rr = executeRequest(req)
14002	checkResponseCode(t, http.StatusOK, rr)
14003	err = render.DecodeJSON(rr.Body, &folder)
14004	assert.NoError(t, err)
14005	assert.Equal(t, mappedPath, folder.MappedPath)
14006	assert.Equal(t, folderName, folder.Name)
14007	assert.Equal(t, folderDesc, folder.Description)
14008	// cleanup
14009	req, _ = http.NewRequest(http.MethodDelete, path.Join(folderPath, folderName), nil)
14010	setBearerForReq(req, apiToken)
14011	rr = executeRequest(req)
14012	checkResponseCode(t, http.StatusOK, rr)
14013}
14014
14015func TestS3WebFolderMock(t *testing.T) {
14016	webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
14017	assert.NoError(t, err)
14018	apiToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
14019	assert.NoError(t, err)
14020	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
14021	assert.NoError(t, err)
14022	mappedPath := filepath.Clean(os.TempDir())
14023	folderName := filepath.Base(mappedPath)
14024	folderDesc := "a simple desc"
14025	S3Bucket := "test"
14026	S3Region := "eu-west-1"
14027	S3AccessKey := "access-key"
14028	S3AccessSecret := kms.NewPlainSecret("folder-access-secret")
14029	S3Endpoint := "http://127.0.0.1:9000/path?b=c"
14030	S3StorageClass := "Standard"
14031	S3ACL := "public-read-write"
14032	S3KeyPrefix := "somedir/subdir/"
14033	S3UploadPartSize := 5
14034	S3UploadConcurrency := 4
14035	S3MaxPartDownloadTime := 120
14036	S3DownloadPartSize := 6
14037	S3DownloadConcurrency := 3
14038	form := make(url.Values)
14039	form.Set("mapped_path", mappedPath)
14040	form.Set("name", folderName)
14041	form.Set("description", folderDesc)
14042	form.Set("fs_provider", "1")
14043	form.Set("s3_bucket", S3Bucket)
14044	form.Set("s3_region", S3Region)
14045	form.Set("s3_access_key", S3AccessKey)
14046	form.Set("s3_access_secret", S3AccessSecret.GetPayload())
14047	form.Set("s3_storage_class", S3StorageClass)
14048	form.Set("s3_acl", S3ACL)
14049	form.Set("s3_endpoint", S3Endpoint)
14050	form.Set("s3_key_prefix", S3KeyPrefix)
14051	form.Set("s3_upload_part_size", strconv.Itoa(S3UploadPartSize))
14052	form.Set("s3_download_part_max_time", strconv.Itoa(S3MaxPartDownloadTime))
14053	form.Set("s3_download_part_size", strconv.Itoa(S3DownloadPartSize))
14054	form.Set("s3_download_concurrency", strconv.Itoa(S3DownloadConcurrency))
14055	form.Set("s3_upload_concurrency", "a")
14056	form.Set(csrfFormToken, csrfToken)
14057	b, contentType, err := getMultipartFormData(form, "", "")
14058	assert.NoError(t, err)
14059	req, err := http.NewRequest(http.MethodPost, webFolderPath, &b)
14060	assert.NoError(t, err)
14061	setJWTCookieForReq(req, webToken)
14062	req.Header.Set("Content-Type", contentType)
14063	rr := executeRequest(req)
14064	checkResponseCode(t, http.StatusOK, rr)
14065
14066	form.Set("s3_upload_concurrency", strconv.Itoa(S3UploadConcurrency))
14067	b, contentType, err = getMultipartFormData(form, "", "")
14068	assert.NoError(t, err)
14069	req, err = http.NewRequest(http.MethodPost, webFolderPath, &b)
14070	assert.NoError(t, err)
14071	setJWTCookieForReq(req, webToken)
14072	req.Header.Set("Content-Type", contentType)
14073	rr = executeRequest(req)
14074	checkResponseCode(t, http.StatusSeeOther, rr)
14075
14076	var folder vfs.BaseVirtualFolder
14077	req, _ = http.NewRequest(http.MethodGet, path.Join(folderPath, folderName), nil)
14078	setBearerForReq(req, apiToken)
14079	rr = executeRequest(req)
14080	checkResponseCode(t, http.StatusOK, rr)
14081	err = render.DecodeJSON(rr.Body, &folder)
14082	assert.NoError(t, err)
14083	assert.Equal(t, mappedPath, folder.MappedPath)
14084	assert.Equal(t, folderName, folder.Name)
14085	assert.Equal(t, folderDesc, folder.Description)
14086	assert.Equal(t, sdk.S3FilesystemProvider, folder.FsConfig.Provider)
14087	assert.Equal(t, S3Bucket, folder.FsConfig.S3Config.Bucket)
14088	assert.Equal(t, S3Region, folder.FsConfig.S3Config.Region)
14089	assert.Equal(t, S3AccessKey, folder.FsConfig.S3Config.AccessKey)
14090	assert.NotEmpty(t, folder.FsConfig.S3Config.AccessSecret.GetPayload())
14091	assert.Equal(t, S3Endpoint, folder.FsConfig.S3Config.Endpoint)
14092	assert.Equal(t, S3StorageClass, folder.FsConfig.S3Config.StorageClass)
14093	assert.Equal(t, S3ACL, folder.FsConfig.S3Config.ACL)
14094	assert.Equal(t, S3KeyPrefix, folder.FsConfig.S3Config.KeyPrefix)
14095	assert.Equal(t, S3UploadConcurrency, folder.FsConfig.S3Config.UploadConcurrency)
14096	assert.Equal(t, int64(S3UploadPartSize), folder.FsConfig.S3Config.UploadPartSize)
14097	assert.Equal(t, S3MaxPartDownloadTime, folder.FsConfig.S3Config.DownloadPartMaxTime)
14098	assert.Equal(t, S3DownloadConcurrency, folder.FsConfig.S3Config.DownloadConcurrency)
14099	assert.Equal(t, int64(S3DownloadPartSize), folder.FsConfig.S3Config.DownloadPartSize)
14100	assert.False(t, folder.FsConfig.S3Config.ForcePathStyle)
14101	// update
14102	S3UploadConcurrency = 10
14103	form.Set("s3_upload_concurrency", "b")
14104	b, contentType, err = getMultipartFormData(form, "", "")
14105	assert.NoError(t, err)
14106	req, err = http.NewRequest(http.MethodPost, path.Join(webFolderPath, folderName), &b)
14107	assert.NoError(t, err)
14108	setJWTCookieForReq(req, webToken)
14109	req.Header.Set("Content-Type", contentType)
14110	rr = executeRequest(req)
14111	checkResponseCode(t, http.StatusOK, rr)
14112
14113	form.Set("s3_upload_concurrency", strconv.Itoa(S3UploadConcurrency))
14114	b, contentType, err = getMultipartFormData(form, "", "")
14115	assert.NoError(t, err)
14116	req, err = http.NewRequest(http.MethodPost, path.Join(webFolderPath, folderName), &b)
14117	assert.NoError(t, err)
14118	setJWTCookieForReq(req, webToken)
14119	req.Header.Set("Content-Type", contentType)
14120	rr = executeRequest(req)
14121	checkResponseCode(t, http.StatusSeeOther, rr)
14122
14123	folder = vfs.BaseVirtualFolder{}
14124	req, _ = http.NewRequest(http.MethodGet, path.Join(folderPath, folderName), nil)
14125	setBearerForReq(req, apiToken)
14126	rr = executeRequest(req)
14127	checkResponseCode(t, http.StatusOK, rr)
14128	err = render.DecodeJSON(rr.Body, &folder)
14129	assert.NoError(t, err)
14130	assert.Equal(t, mappedPath, folder.MappedPath)
14131	assert.Equal(t, folderName, folder.Name)
14132	assert.Equal(t, folderDesc, folder.Description)
14133	assert.Equal(t, sdk.S3FilesystemProvider, folder.FsConfig.Provider)
14134	assert.Equal(t, S3Bucket, folder.FsConfig.S3Config.Bucket)
14135	assert.Equal(t, S3Region, folder.FsConfig.S3Config.Region)
14136	assert.Equal(t, S3AccessKey, folder.FsConfig.S3Config.AccessKey)
14137	assert.NotEmpty(t, folder.FsConfig.S3Config.AccessSecret.GetPayload())
14138	assert.Equal(t, S3Endpoint, folder.FsConfig.S3Config.Endpoint)
14139	assert.Equal(t, S3StorageClass, folder.FsConfig.S3Config.StorageClass)
14140	assert.Equal(t, S3KeyPrefix, folder.FsConfig.S3Config.KeyPrefix)
14141	assert.Equal(t, S3UploadConcurrency, folder.FsConfig.S3Config.UploadConcurrency)
14142	assert.Equal(t, int64(S3UploadPartSize), folder.FsConfig.S3Config.UploadPartSize)
14143
14144	// cleanup
14145	req, _ = http.NewRequest(http.MethodDelete, path.Join(folderPath, folderName), nil)
14146	setBearerForReq(req, apiToken)
14147	rr = executeRequest(req)
14148	checkResponseCode(t, http.StatusOK, rr)
14149}
14150
14151func TestUpdateWebFolderMock(t *testing.T) {
14152	webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
14153	assert.NoError(t, err)
14154	apiToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
14155	assert.NoError(t, err)
14156	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
14157	assert.NoError(t, err)
14158	folderName := "vfolderupdate"
14159	folderDesc := "updated desc"
14160	folder := vfs.BaseVirtualFolder{
14161		Name:        folderName,
14162		MappedPath:  filepath.Join(os.TempDir(), "folderupdate"),
14163		Description: "dsc",
14164	}
14165	_, _, err = httpdtest.AddFolder(folder, http.StatusCreated)
14166	newMappedPath := folder.MappedPath + "1"
14167	assert.NoError(t, err)
14168	form := make(url.Values)
14169	form.Set("mapped_path", newMappedPath)
14170	form.Set("name", folderName)
14171	form.Set("description", folderDesc)
14172	form.Set(csrfFormToken, "")
14173	b, contentType, err := getMultipartFormData(form, "", "")
14174	assert.NoError(t, err)
14175	req, err := http.NewRequest(http.MethodPost, path.Join(webFolderPath, folderName), &b)
14176	assert.NoError(t, err)
14177	setJWTCookieForReq(req, webToken)
14178	req.Header.Set("Content-Type", contentType)
14179	rr := executeRequest(req)
14180	checkResponseCode(t, http.StatusForbidden, rr)
14181	assert.Contains(t, rr.Body.String(), "unable to verify form token")
14182
14183	form.Set(csrfFormToken, csrfToken)
14184	b, contentType, err = getMultipartFormData(form, "", "")
14185	assert.NoError(t, err)
14186	req, err = http.NewRequest(http.MethodPost, path.Join(webFolderPath, folderName), &b)
14187	assert.NoError(t, err)
14188	setJWTCookieForReq(req, webToken)
14189	req.Header.Set("Content-Type", contentType)
14190	rr = executeRequest(req)
14191	checkResponseCode(t, http.StatusSeeOther, rr)
14192
14193	req, _ = http.NewRequest(http.MethodGet, path.Join(folderPath, folderName), nil)
14194	setBearerForReq(req, apiToken)
14195	rr = executeRequest(req)
14196	checkResponseCode(t, http.StatusOK, rr)
14197	err = render.DecodeJSON(rr.Body, &folder)
14198	assert.NoError(t, err)
14199	assert.Equal(t, newMappedPath, folder.MappedPath)
14200	assert.Equal(t, folderName, folder.Name)
14201	assert.Equal(t, folderDesc, folder.Description)
14202
14203	// parse form error
14204	b, contentType, err = getMultipartFormData(form, "", "")
14205	assert.NoError(t, err)
14206	req, err = http.NewRequest(http.MethodPost, path.Join(webFolderPath, folderName)+"??a=a%B3%A2%G3", &b)
14207	assert.NoError(t, err)
14208	setJWTCookieForReq(req, webToken)
14209	req.Header.Set("Content-Type", contentType)
14210	rr = executeRequest(req)
14211	checkResponseCode(t, http.StatusOK, rr)
14212	assert.Contains(t, rr.Body.String(), "invalid URL escape")
14213
14214	b, contentType, err = getMultipartFormData(form, "", "")
14215	assert.NoError(t, err)
14216	req, err = http.NewRequest(http.MethodPost, path.Join(webFolderPath, folderName+"1"), &b)
14217	assert.NoError(t, err)
14218	setJWTCookieForReq(req, webToken)
14219	req.Header.Set("Content-Type", contentType)
14220	rr = executeRequest(req)
14221	checkResponseCode(t, http.StatusNotFound, rr)
14222
14223	form.Set("mapped_path", "arelative/path")
14224	b, contentType, err = getMultipartFormData(form, "", "")
14225	assert.NoError(t, err)
14226	req, err = http.NewRequest(http.MethodPost, path.Join(webFolderPath, folderName), &b)
14227	assert.NoError(t, err)
14228	setJWTCookieForReq(req, webToken)
14229	req.Header.Set("Content-Type", contentType)
14230	rr = executeRequest(req)
14231	checkResponseCode(t, http.StatusOK, rr)
14232
14233	// render update folder page
14234	req, err = http.NewRequest(http.MethodGet, path.Join(webFolderPath, folderName), nil)
14235	assert.NoError(t, err)
14236	setJWTCookieForReq(req, webToken)
14237	rr = executeRequest(req)
14238	checkResponseCode(t, http.StatusOK, rr)
14239
14240	req, err = http.NewRequest(http.MethodGet, path.Join(webFolderPath, folderName+"1"), nil)
14241	assert.NoError(t, err)
14242	setJWTCookieForReq(req, webToken)
14243	rr = executeRequest(req)
14244	checkResponseCode(t, http.StatusNotFound, rr)
14245
14246	req, _ = http.NewRequest(http.MethodDelete, path.Join(webFolderPath, folderName), nil)
14247	setJWTCookieForReq(req, webToken)
14248	rr = executeRequest(req)
14249	checkResponseCode(t, http.StatusForbidden, rr)
14250	assert.Contains(t, rr.Body.String(), "Invalid token")
14251
14252	req, _ = http.NewRequest(http.MethodDelete, path.Join(webFolderPath, folderName), nil)
14253	setJWTCookieForReq(req, apiToken) // api token is not accepted
14254	setCSRFHeaderForReq(req, csrfToken)
14255	rr = executeRequest(req)
14256	checkResponseCode(t, http.StatusFound, rr)
14257	assert.Equal(t, webLoginPath, rr.Header().Get("Location"))
14258
14259	req, _ = http.NewRequest(http.MethodDelete, path.Join(webFolderPath, folderName), nil)
14260	setJWTCookieForReq(req, webToken)
14261	setCSRFHeaderForReq(req, csrfToken)
14262	rr = executeRequest(req)
14263	checkResponseCode(t, http.StatusOK, rr)
14264}
14265
14266func TestWebFoldersMock(t *testing.T) {
14267	webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
14268	assert.NoError(t, err)
14269	apiToken, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
14270	assert.NoError(t, err)
14271	mappedPath1 := filepath.Join(os.TempDir(), "vfolder1")
14272	mappedPath2 := filepath.Join(os.TempDir(), "vfolder2")
14273	folderName1 := filepath.Base(mappedPath1)
14274	folderName2 := filepath.Base(mappedPath2)
14275	folderDesc1 := "vfolder1 desc"
14276	folderDesc2 := "vfolder2 desc"
14277	folders := []vfs.BaseVirtualFolder{
14278		{
14279			Name:        folderName1,
14280			MappedPath:  mappedPath1,
14281			Description: folderDesc1,
14282		},
14283		{
14284			Name:        folderName2,
14285			MappedPath:  mappedPath2,
14286			Description: folderDesc2,
14287		},
14288	}
14289	for _, folder := range folders {
14290		folderAsJSON, err := json.Marshal(folder)
14291		assert.NoError(t, err)
14292		req, err := http.NewRequest(http.MethodPost, folderPath, bytes.NewBuffer(folderAsJSON))
14293		assert.NoError(t, err)
14294		setBearerForReq(req, apiToken)
14295		rr := executeRequest(req)
14296		checkResponseCode(t, http.StatusCreated, rr)
14297	}
14298
14299	req, err := http.NewRequest(http.MethodGet, folderPath, nil)
14300	assert.NoError(t, err)
14301	setBearerForReq(req, apiToken)
14302	rr := executeRequest(req)
14303	checkResponseCode(t, http.StatusOK, rr)
14304	var foldersGet []vfs.BaseVirtualFolder
14305	err = render.DecodeJSON(rr.Body, &foldersGet)
14306	assert.NoError(t, err)
14307	numFound := 0
14308	for _, f := range foldersGet {
14309		if f.Name == folderName1 {
14310			assert.Equal(t, mappedPath1, f.MappedPath)
14311			assert.Equal(t, folderDesc1, f.Description)
14312			numFound++
14313		}
14314		if f.Name == folderName2 {
14315			assert.Equal(t, mappedPath2, f.MappedPath)
14316			assert.Equal(t, folderDesc2, f.Description)
14317			numFound++
14318		}
14319	}
14320	assert.Equal(t, 2, numFound)
14321
14322	req, err = http.NewRequest(http.MethodGet, webFoldersPath, nil)
14323	assert.NoError(t, err)
14324	setJWTCookieForReq(req, webToken)
14325	rr = executeRequest(req)
14326	checkResponseCode(t, http.StatusOK, rr)
14327	req, err = http.NewRequest(http.MethodGet, webFoldersPath+"?qlimit=a", nil)
14328	assert.NoError(t, err)
14329	setJWTCookieForReq(req, webToken)
14330	rr = executeRequest(req)
14331	checkResponseCode(t, http.StatusOK, rr)
14332	req, err = http.NewRequest(http.MethodGet, webFoldersPath+"?qlimit=1", nil)
14333	assert.NoError(t, err)
14334	setJWTCookieForReq(req, webToken)
14335	rr = executeRequest(req)
14336	checkResponseCode(t, http.StatusOK, rr)
14337
14338	for _, folder := range folders {
14339		req, _ := http.NewRequest(http.MethodDelete, path.Join(folderPath, folder.Name), nil)
14340		setBearerForReq(req, apiToken)
14341		rr := executeRequest(req)
14342		checkResponseCode(t, http.StatusOK, rr)
14343	}
14344}
14345
14346func TestAdminForgotPassword(t *testing.T) {
14347	smtpCfg := smtp.Config{
14348		Host:          "127.0.0.1",
14349		Port:          3525,
14350		TemplatesPath: "templates",
14351	}
14352	err := smtpCfg.Initialize("..")
14353	require.NoError(t, err)
14354
14355	a := getTestAdmin()
14356	a.Username = altAdminUsername
14357	a.Password = altAdminPassword
14358	admin, _, err := httpdtest.AddAdmin(a, http.StatusCreated)
14359	assert.NoError(t, err)
14360
14361	req, err := http.NewRequest(http.MethodGet, webAdminForgotPwdPath, nil)
14362	assert.NoError(t, err)
14363	rr := executeRequest(req)
14364	checkResponseCode(t, http.StatusOK, rr)
14365
14366	req, err = http.NewRequest(http.MethodGet, webAdminResetPwdPath, nil)
14367	assert.NoError(t, err)
14368	rr = executeRequest(req)
14369	checkResponseCode(t, http.StatusOK, rr)
14370
14371	req, err = http.NewRequest(http.MethodGet, webLoginPath, nil)
14372	assert.NoError(t, err)
14373	rr = executeRequest(req)
14374	checkResponseCode(t, http.StatusOK, rr)
14375
14376	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
14377	assert.NoError(t, err)
14378
14379	form := make(url.Values)
14380	form.Set("username", "")
14381	// no csrf token
14382	req, err = http.NewRequest(http.MethodPost, webAdminForgotPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14383	assert.NoError(t, err)
14384	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14385	rr = executeRequest(req)
14386	assert.Equal(t, http.StatusForbidden, rr.Code)
14387	// empty username
14388	form.Set(csrfFormToken, csrfToken)
14389	req, err = http.NewRequest(http.MethodPost, webAdminForgotPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14390	assert.NoError(t, err)
14391	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14392	rr = executeRequest(req)
14393	assert.Equal(t, http.StatusOK, rr.Code)
14394	assert.Contains(t, rr.Body.String(), "Username is mandatory")
14395
14396	lastResetCode = ""
14397	form.Set("username", altAdminUsername)
14398	req, err = http.NewRequest(http.MethodPost, webAdminForgotPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14399	assert.NoError(t, err)
14400	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14401	rr = executeRequest(req)
14402	assert.Equal(t, http.StatusFound, rr.Code)
14403	assert.GreaterOrEqual(t, len(lastResetCode), 20)
14404
14405	form = make(url.Values)
14406	req, err = http.NewRequest(http.MethodPost, webAdminResetPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14407	assert.NoError(t, err)
14408	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14409	rr = executeRequest(req)
14410	assert.Equal(t, http.StatusForbidden, rr.Code)
14411	// no password
14412	form.Set(csrfFormToken, csrfToken)
14413	req, err = http.NewRequest(http.MethodPost, webAdminResetPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14414	assert.NoError(t, err)
14415	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14416	rr = executeRequest(req)
14417	assert.Equal(t, http.StatusOK, rr.Code)
14418	assert.Contains(t, rr.Body.String(), "Please set a password")
14419	// no code
14420	form.Set("password", defaultPassword)
14421	req, err = http.NewRequest(http.MethodPost, webAdminResetPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14422	assert.NoError(t, err)
14423	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14424	rr = executeRequest(req)
14425	assert.Equal(t, http.StatusOK, rr.Code)
14426	assert.Contains(t, rr.Body.String(), "Please set a confirmation code")
14427	// ok
14428	form.Set("code", lastResetCode)
14429	req, err = http.NewRequest(http.MethodPost, webAdminResetPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14430	assert.NoError(t, err)
14431	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14432	rr = executeRequest(req)
14433	assert.Equal(t, http.StatusFound, rr.Code)
14434
14435	form.Set("username", altAdminUsername)
14436	req, err = http.NewRequest(http.MethodPost, webAdminForgotPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14437	assert.NoError(t, err)
14438	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14439	rr = executeRequest(req)
14440	assert.Equal(t, http.StatusFound, rr.Code)
14441	assert.GreaterOrEqual(t, len(lastResetCode), 20)
14442
14443	// not working smtp server
14444	smtpCfg = smtp.Config{
14445		Host:          "127.0.0.1",
14446		Port:          3526,
14447		TemplatesPath: "templates",
14448	}
14449	err = smtpCfg.Initialize("..")
14450	require.NoError(t, err)
14451
14452	form = make(url.Values)
14453	form.Set("username", altAdminUsername)
14454	form.Set(csrfFormToken, csrfToken)
14455	req, err = http.NewRequest(http.MethodPost, webAdminForgotPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14456	assert.NoError(t, err)
14457	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14458	rr = executeRequest(req)
14459	assert.Equal(t, http.StatusOK, rr.Code)
14460	assert.Contains(t, rr.Body.String(), "Unable to send confirmation code via email")
14461
14462	smtpCfg = smtp.Config{}
14463	err = smtpCfg.Initialize("..")
14464	require.NoError(t, err)
14465
14466	form.Set("username", altAdminUsername)
14467	form.Set(csrfFormToken, csrfToken)
14468	req, err = http.NewRequest(http.MethodPost, webAdminForgotPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14469	assert.NoError(t, err)
14470	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14471	rr = executeRequest(req)
14472	assert.Equal(t, http.StatusOK, rr.Code)
14473	assert.Contains(t, rr.Body.String(), "Unable to render password reset template")
14474
14475	req, err = http.NewRequest(http.MethodGet, webAdminForgotPwdPath, nil)
14476	assert.NoError(t, err)
14477	rr = executeRequest(req)
14478	checkResponseCode(t, http.StatusNotFound, rr)
14479
14480	req, err = http.NewRequest(http.MethodGet, webAdminResetPwdPath, nil)
14481	assert.NoError(t, err)
14482	rr = executeRequest(req)
14483	checkResponseCode(t, http.StatusNotFound, rr)
14484
14485	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
14486	assert.NoError(t, err)
14487}
14488
14489func TestUserForgotPassword(t *testing.T) {
14490	smtpCfg := smtp.Config{
14491		Host:          "127.0.0.1",
14492		Port:          3525,
14493		TemplatesPath: "templates",
14494	}
14495	err := smtpCfg.Initialize("..")
14496	require.NoError(t, err)
14497
14498	u := getTestUser()
14499	u.Email = "user@test.com"
14500	u.Filters.WebClient = []string{sdk.WebClientPasswordResetDisabled}
14501	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
14502	assert.NoError(t, err)
14503
14504	req, err := http.NewRequest(http.MethodGet, webClientForgotPwdPath, nil)
14505	assert.NoError(t, err)
14506	rr := executeRequest(req)
14507	checkResponseCode(t, http.StatusOK, rr)
14508
14509	req, err = http.NewRequest(http.MethodGet, webClientResetPwdPath, nil)
14510	assert.NoError(t, err)
14511	rr = executeRequest(req)
14512	checkResponseCode(t, http.StatusOK, rr)
14513
14514	req, err = http.NewRequest(http.MethodGet, webClientLoginPath, nil)
14515	assert.NoError(t, err)
14516	rr = executeRequest(req)
14517	checkResponseCode(t, http.StatusOK, rr)
14518
14519	form := make(url.Values)
14520	form.Set("username", "")
14521	// no csrf token
14522	req, err = http.NewRequest(http.MethodPost, webClientForgotPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14523	assert.NoError(t, err)
14524	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14525	rr = executeRequest(req)
14526	assert.Equal(t, http.StatusForbidden, rr.Code)
14527	// empty username
14528	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
14529	assert.NoError(t, err)
14530	form.Set(csrfFormToken, csrfToken)
14531	req, err = http.NewRequest(http.MethodPost, webClientForgotPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14532	assert.NoError(t, err)
14533	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14534	rr = executeRequest(req)
14535	assert.Equal(t, http.StatusOK, rr.Code)
14536	assert.Contains(t, rr.Body.String(), "Username is mandatory")
14537	// user cannot reset the password
14538	form.Set("username", user.Username)
14539	req, err = http.NewRequest(http.MethodPost, webClientForgotPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14540	assert.NoError(t, err)
14541	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14542	rr = executeRequest(req)
14543	assert.Equal(t, http.StatusOK, rr.Code)
14544	assert.Contains(t, rr.Body.String(), "You are not allowed to reset your password")
14545	user.Filters.WebClient = []string{sdk.WebClientAPIKeyAuthChangeDisabled}
14546	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
14547	assert.NoError(t, err)
14548
14549	lastResetCode = ""
14550	req, err = http.NewRequest(http.MethodPost, webClientForgotPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14551	assert.NoError(t, err)
14552	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14553	rr = executeRequest(req)
14554	assert.Equal(t, http.StatusFound, rr.Code)
14555	assert.GreaterOrEqual(t, len(lastResetCode), 20)
14556	// no csrf token
14557	form = make(url.Values)
14558	req, err = http.NewRequest(http.MethodPost, webClientResetPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14559	assert.NoError(t, err)
14560	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14561	rr = executeRequest(req)
14562	assert.Equal(t, http.StatusForbidden, rr.Code)
14563	// no password
14564	form.Set(csrfFormToken, csrfToken)
14565	req, err = http.NewRequest(http.MethodPost, webClientResetPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14566	assert.NoError(t, err)
14567	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14568	rr = executeRequest(req)
14569	assert.Equal(t, http.StatusOK, rr.Code)
14570	assert.Contains(t, rr.Body.String(), "Please set a password")
14571	// no code
14572	form.Set("password", altAdminPassword)
14573	req, err = http.NewRequest(http.MethodPost, webClientResetPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14574	assert.NoError(t, err)
14575	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14576	rr = executeRequest(req)
14577	assert.Equal(t, http.StatusOK, rr.Code)
14578	assert.Contains(t, rr.Body.String(), "Please set a confirmation code")
14579	// ok
14580	form.Set("code", lastResetCode)
14581	req, err = http.NewRequest(http.MethodPost, webClientResetPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14582	assert.NoError(t, err)
14583	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14584	rr = executeRequest(req)
14585	assert.Equal(t, http.StatusFound, rr.Code)
14586
14587	form = make(url.Values)
14588	form.Set(csrfFormToken, csrfToken)
14589	form.Set("username", user.Username)
14590	lastResetCode = ""
14591	req, err = http.NewRequest(http.MethodPost, webClientForgotPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14592	assert.NoError(t, err)
14593	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14594	rr = executeRequest(req)
14595	assert.Equal(t, http.StatusFound, rr.Code)
14596	assert.GreaterOrEqual(t, len(lastResetCode), 20)
14597
14598	smtpCfg = smtp.Config{}
14599	err = smtpCfg.Initialize("..")
14600	require.NoError(t, err)
14601
14602	req, err = http.NewRequest(http.MethodGet, webClientForgotPwdPath, nil)
14603	assert.NoError(t, err)
14604	rr = executeRequest(req)
14605	checkResponseCode(t, http.StatusNotFound, rr)
14606
14607	req, err = http.NewRequest(http.MethodGet, webClientResetPwdPath, nil)
14608	assert.NoError(t, err)
14609	rr = executeRequest(req)
14610	checkResponseCode(t, http.StatusNotFound, rr)
14611
14612	_, err = httpdtest.RemoveUser(user, http.StatusOK)
14613	assert.NoError(t, err)
14614	err = os.RemoveAll(user.GetHomeDir())
14615	assert.NoError(t, err)
14616	// user does not exist anymore
14617	form = make(url.Values)
14618	form.Set(csrfFormToken, csrfToken)
14619	form.Set("code", lastResetCode)
14620	form.Set("password", "pwd")
14621	req, err = http.NewRequest(http.MethodPost, webClientResetPwdPath, bytes.NewBuffer([]byte(form.Encode())))
14622	assert.NoError(t, err)
14623	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
14624	rr = executeRequest(req)
14625	assert.Equal(t, http.StatusOK, rr.Code)
14626	assert.Contains(t, rr.Body.String(), "Unable to associate the confirmation code with an existing user")
14627}
14628
14629func TestAPIForgotPassword(t *testing.T) {
14630	smtpCfg := smtp.Config{
14631		Host:          "127.0.0.1",
14632		Port:          3525,
14633		TemplatesPath: "templates",
14634	}
14635	err := smtpCfg.Initialize("..")
14636	require.NoError(t, err)
14637
14638	a := getTestAdmin()
14639	a.Username = altAdminUsername
14640	a.Password = altAdminPassword
14641	a.Email = ""
14642	admin, _, err := httpdtest.AddAdmin(a, http.StatusCreated)
14643	assert.NoError(t, err)
14644	// no email, forgot pwd will not work
14645	lastResetCode = ""
14646	req, err := http.NewRequest(http.MethodPost, path.Join(adminPath, altAdminUsername, "/forgot-password"), nil)
14647	assert.NoError(t, err)
14648	rr := executeRequest(req)
14649	checkResponseCode(t, http.StatusBadRequest, rr)
14650	assert.Contains(t, rr.Body.String(), "Your account does not have an email address")
14651
14652	admin.Email = "admin@test.com"
14653	admin, _, err = httpdtest.UpdateAdmin(admin, http.StatusOK)
14654	assert.NoError(t, err)
14655
14656	req, err = http.NewRequest(http.MethodPost, path.Join(adminPath, altAdminUsername, "/forgot-password"), nil)
14657	assert.NoError(t, err)
14658	rr = executeRequest(req)
14659	checkResponseCode(t, http.StatusOK, rr)
14660	assert.GreaterOrEqual(t, len(lastResetCode), 20)
14661
14662	// invalid JSON
14663	req, err = http.NewRequest(http.MethodPost, path.Join(adminPath, altAdminUsername, "/reset-password"), bytes.NewBuffer([]byte(`{`)))
14664	assert.NoError(t, err)
14665	rr = executeRequest(req)
14666	checkResponseCode(t, http.StatusBadRequest, rr)
14667
14668	resetReq := make(map[string]string)
14669	resetReq["code"] = lastResetCode
14670	resetReq["password"] = defaultPassword
14671	asJSON, err := json.Marshal(resetReq)
14672	assert.NoError(t, err)
14673
14674	// a user cannot use an admin code
14675	req, err = http.NewRequest(http.MethodPost, path.Join(userPath, defaultUsername, "/reset-password"), bytes.NewBuffer(asJSON))
14676	assert.NoError(t, err)
14677	rr = executeRequest(req)
14678	checkResponseCode(t, http.StatusBadRequest, rr)
14679	assert.Contains(t, rr.Body.String(), "Invalid confirmation code")
14680
14681	req, err = http.NewRequest(http.MethodPost, path.Join(adminPath, altAdminUsername, "/reset-password"), bytes.NewBuffer(asJSON))
14682	assert.NoError(t, err)
14683	rr = executeRequest(req)
14684	checkResponseCode(t, http.StatusOK, rr)
14685
14686	// the same code cannot be reused
14687	req, err = http.NewRequest(http.MethodPost, path.Join(adminPath, altAdminUsername, "/reset-password"), bytes.NewBuffer(asJSON))
14688	assert.NoError(t, err)
14689	rr = executeRequest(req)
14690	checkResponseCode(t, http.StatusBadRequest, rr)
14691	assert.Contains(t, rr.Body.String(), "Confirmation code not found")
14692
14693	admin, err = dataprovider.AdminExists(altAdminUsername)
14694	assert.NoError(t, err)
14695
14696	match, err := admin.CheckPassword(defaultPassword)
14697	assert.NoError(t, err)
14698	assert.True(t, match)
14699	lastResetCode = ""
14700	// now the same for a user
14701	u := getTestUser()
14702	user, _, err := httpdtest.AddUser(u, http.StatusCreated)
14703	assert.NoError(t, err)
14704	req, err = http.NewRequest(http.MethodPost, path.Join(userPath, defaultUsername, "/forgot-password"), nil)
14705	assert.NoError(t, err)
14706	rr = executeRequest(req)
14707	checkResponseCode(t, http.StatusBadRequest, rr)
14708	assert.Contains(t, rr.Body.String(), "Your account does not have an email address")
14709
14710	user.Email = "user@test.com"
14711	user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
14712	assert.NoError(t, err)
14713	req, err = http.NewRequest(http.MethodPost, path.Join(userPath, defaultUsername, "/forgot-password"), nil)
14714	assert.NoError(t, err)
14715	rr = executeRequest(req)
14716	checkResponseCode(t, http.StatusOK, rr)
14717	assert.GreaterOrEqual(t, len(lastResetCode), 20)
14718
14719	// invalid JSON
14720	req, err = http.NewRequest(http.MethodPost, path.Join(userPath, defaultUsername, "/reset-password"), bytes.NewBuffer([]byte(`{`)))
14721	assert.NoError(t, err)
14722	rr = executeRequest(req)
14723	checkResponseCode(t, http.StatusBadRequest, rr)
14724	// remove the reset password permission
14725	user.Filters.WebClient = []string{sdk.WebClientPasswordResetDisabled}
14726	_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
14727	assert.NoError(t, err)
14728
14729	resetReq["code"] = lastResetCode
14730	resetReq["password"] = altAdminPassword
14731	asJSON, err = json.Marshal(resetReq)
14732	assert.NoError(t, err)
14733	req, err = http.NewRequest(http.MethodPost, path.Join(userPath, defaultUsername, "/reset-password"), bytes.NewBuffer(asJSON))
14734	assert.NoError(t, err)
14735	rr = executeRequest(req)
14736	checkResponseCode(t, http.StatusBadRequest, rr)
14737	assert.Contains(t, rr.Body.String(), "You are not allowed to reset your password")
14738
14739	user.Filters.WebClient = []string{sdk.WebClientSharesDisabled}
14740	_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
14741	assert.NoError(t, err)
14742	req, err = http.NewRequest(http.MethodPost, path.Join(userPath, defaultUsername, "/reset-password"), bytes.NewBuffer(asJSON))
14743	assert.NoError(t, err)
14744	rr = executeRequest(req)
14745	checkResponseCode(t, http.StatusOK, rr)
14746	// the same code cannot be reused
14747	req, err = http.NewRequest(http.MethodPost, path.Join(userPath, defaultUsername, "/reset-password"), bytes.NewBuffer(asJSON))
14748	assert.NoError(t, err)
14749	rr = executeRequest(req)
14750	checkResponseCode(t, http.StatusBadRequest, rr)
14751	assert.Contains(t, rr.Body.String(), "Confirmation code not found")
14752
14753	user, err = dataprovider.UserExists(defaultUsername)
14754	assert.NoError(t, err)
14755	err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(altAdminPassword))
14756	assert.NoError(t, err)
14757
14758	lastResetCode = ""
14759	// a request for a missing admin/user will be silently ignored
14760	req, err = http.NewRequest(http.MethodPost, path.Join(adminPath, "missing-admin", "/forgot-password"), nil)
14761	assert.NoError(t, err)
14762	rr = executeRequest(req)
14763	checkResponseCode(t, http.StatusOK, rr)
14764	assert.Empty(t, lastResetCode)
14765
14766	req, err = http.NewRequest(http.MethodPost, path.Join(userPath, "missing-user", "/forgot-password"), nil)
14767	assert.NoError(t, err)
14768	rr = executeRequest(req)
14769	checkResponseCode(t, http.StatusOK, rr)
14770	assert.Empty(t, lastResetCode)
14771
14772	lastResetCode = ""
14773	req, err = http.NewRequest(http.MethodPost, path.Join(adminPath, altAdminUsername, "/forgot-password"), nil)
14774	assert.NoError(t, err)
14775	rr = executeRequest(req)
14776	checkResponseCode(t, http.StatusOK, rr)
14777	assert.GreaterOrEqual(t, len(lastResetCode), 20)
14778
14779	smtpCfg = smtp.Config{}
14780	err = smtpCfg.Initialize("..")
14781	require.NoError(t, err)
14782
14783	// without an smtp configuration reset password is not available
14784	req, err = http.NewRequest(http.MethodPost, path.Join(adminPath, altAdminUsername, "/forgot-password"), nil)
14785	assert.NoError(t, err)
14786	rr = executeRequest(req)
14787	checkResponseCode(t, http.StatusBadRequest, rr)
14788	assert.Contains(t, rr.Body.String(), "No SMTP configuration")
14789
14790	req, err = http.NewRequest(http.MethodPost, path.Join(userPath, defaultUsername, "/forgot-password"), nil)
14791	assert.NoError(t, err)
14792	rr = executeRequest(req)
14793	checkResponseCode(t, http.StatusBadRequest, rr)
14794	assert.Contains(t, rr.Body.String(), "No SMTP configuration")
14795
14796	_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
14797	assert.NoError(t, err)
14798	// the admin does not exist anymore
14799	resetReq["code"] = lastResetCode
14800	resetReq["password"] = altAdminPassword
14801	asJSON, err = json.Marshal(resetReq)
14802	assert.NoError(t, err)
14803	req, err = http.NewRequest(http.MethodPost, path.Join(adminPath, altAdminUsername, "/reset-password"), bytes.NewBuffer(asJSON))
14804	assert.NoError(t, err)
14805	rr = executeRequest(req)
14806	checkResponseCode(t, http.StatusBadRequest, rr)
14807	assert.Contains(t, rr.Body.String(), "Unable to associate the confirmation code with an existing admin")
14808
14809	_, err = httpdtest.RemoveUser(user, http.StatusOK)
14810	assert.NoError(t, err)
14811	err = os.RemoveAll(user.GetHomeDir())
14812	assert.NoError(t, err)
14813}
14814
14815func TestProviderClosedMock(t *testing.T) {
14816	token, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
14817	assert.NoError(t, err)
14818	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
14819	assert.NoError(t, err)
14820	dataprovider.Close()
14821	req, _ := http.NewRequest(http.MethodGet, webFoldersPath, nil)
14822	setJWTCookieForReq(req, token)
14823	rr := executeRequest(req)
14824	checkResponseCode(t, http.StatusInternalServerError, rr)
14825	req, _ = http.NewRequest(http.MethodGet, webUsersPath, nil)
14826	setJWTCookieForReq(req, token)
14827	rr = executeRequest(req)
14828	checkResponseCode(t, http.StatusInternalServerError, rr)
14829	req, _ = http.NewRequest(http.MethodGet, webUserPath+"/0", nil)
14830	setJWTCookieForReq(req, token)
14831	rr = executeRequest(req)
14832	checkResponseCode(t, http.StatusInternalServerError, rr)
14833	form := make(url.Values)
14834	form.Set(csrfFormToken, csrfToken)
14835	form.Set("username", "test")
14836	req, _ = http.NewRequest(http.MethodPost, webUserPath+"/0", strings.NewReader(form.Encode()))
14837	setJWTCookieForReq(req, token)
14838	rr = executeRequest(req)
14839	checkResponseCode(t, http.StatusInternalServerError, rr)
14840	req, _ = http.NewRequest(http.MethodGet, path.Join(webAdminPath, defaultTokenAuthUser), nil)
14841	setJWTCookieForReq(req, token)
14842	rr = executeRequest(req)
14843	checkResponseCode(t, http.StatusInternalServerError, rr)
14844
14845	req, _ = http.NewRequest(http.MethodPost, path.Join(webAdminPath, defaultTokenAuthUser), strings.NewReader(form.Encode()))
14846	setJWTCookieForReq(req, token)
14847	rr = executeRequest(req)
14848	checkResponseCode(t, http.StatusInternalServerError, rr)
14849
14850	req, _ = http.NewRequest(http.MethodGet, webAdminsPath, nil)
14851	setJWTCookieForReq(req, token)
14852	rr = executeRequest(req)
14853	checkResponseCode(t, http.StatusInternalServerError, rr)
14854
14855	req, _ = http.NewRequest(http.MethodGet, path.Join(webFolderPath, defaultTokenAuthUser), nil)
14856	setJWTCookieForReq(req, token)
14857	rr = executeRequest(req)
14858	checkResponseCode(t, http.StatusInternalServerError, rr)
14859
14860	req, _ = http.NewRequest(http.MethodPost, path.Join(webFolderPath, defaultTokenAuthUser), strings.NewReader(form.Encode()))
14861	setJWTCookieForReq(req, token)
14862	rr = executeRequest(req)
14863	checkResponseCode(t, http.StatusInternalServerError, rr)
14864
14865	err = config.LoadConfig(configDir, "")
14866	assert.NoError(t, err)
14867	providerConf := config.GetProviderConf()
14868	providerConf.CredentialsPath = credentialsPath
14869	err = os.RemoveAll(credentialsPath)
14870	assert.NoError(t, err)
14871	err = dataprovider.Initialize(providerConf, configDir, true)
14872	assert.NoError(t, err)
14873}
14874
14875func TestWebConnectionsMock(t *testing.T) {
14876	token, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
14877	assert.NoError(t, err)
14878	req, _ := http.NewRequest(http.MethodGet, webConnectionsPath, nil)
14879	setJWTCookieForReq(req, token)
14880	rr := executeRequest(req)
14881	checkResponseCode(t, http.StatusOK, rr)
14882
14883	req, _ = http.NewRequest(http.MethodDelete, path.Join(webConnectionsPath, "id"), nil)
14884	setJWTCookieForReq(req, token)
14885	rr = executeRequest(req)
14886	checkResponseCode(t, http.StatusForbidden, rr)
14887	assert.Contains(t, rr.Body.String(), "Invalid token")
14888
14889	req, _ = http.NewRequest(http.MethodDelete, path.Join(webConnectionsPath, "id"), nil)
14890	setJWTCookieForReq(req, token)
14891	setCSRFHeaderForReq(req, "csrfToken")
14892	rr = executeRequest(req)
14893	checkResponseCode(t, http.StatusForbidden, rr)
14894	assert.Contains(t, rr.Body.String(), "Invalid token")
14895
14896	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
14897	assert.NoError(t, err)
14898	req, _ = http.NewRequest(http.MethodDelete, path.Join(webConnectionsPath, "id"), nil)
14899	setJWTCookieForReq(req, token)
14900	setCSRFHeaderForReq(req, csrfToken)
14901	rr = executeRequest(req)
14902	checkResponseCode(t, http.StatusNotFound, rr)
14903}
14904
14905func TestGetWebStatusMock(t *testing.T) {
14906	token, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
14907	assert.NoError(t, err)
14908	req, _ := http.NewRequest(http.MethodGet, webStatusPath, nil)
14909	setJWTCookieForReq(req, token)
14910	rr := executeRequest(req)
14911	checkResponseCode(t, http.StatusOK, rr)
14912}
14913
14914func TestStaticFilesMock(t *testing.T) {
14915	req, err := http.NewRequest(http.MethodGet, "/static/favicon.ico", nil)
14916	assert.NoError(t, err)
14917	rr := executeRequest(req)
14918	checkResponseCode(t, http.StatusOK, rr)
14919
14920	req, err = http.NewRequest(http.MethodGet, "/openapi/openapi.yaml", nil)
14921	assert.NoError(t, err)
14922	rr = executeRequest(req)
14923	checkResponseCode(t, http.StatusOK, rr)
14924
14925	req, err = http.NewRequest(http.MethodGet, "/static", nil)
14926	assert.NoError(t, err)
14927	rr = executeRequest(req)
14928	checkResponseCode(t, http.StatusMovedPermanently, rr)
14929	location := rr.Header().Get("Location")
14930	assert.Equal(t, "/static/", location)
14931	req, err = http.NewRequest(http.MethodGet, location, nil)
14932	assert.NoError(t, err)
14933	rr = executeRequest(req)
14934	checkResponseCode(t, http.StatusOK, rr)
14935
14936	req, err = http.NewRequest(http.MethodGet, "/openapi", nil)
14937	assert.NoError(t, err)
14938	rr = executeRequest(req)
14939	checkResponseCode(t, http.StatusMovedPermanently, rr)
14940	location = rr.Header().Get("Location")
14941	assert.Equal(t, "/openapi/", location)
14942	req, err = http.NewRequest(http.MethodGet, location, nil)
14943	assert.NoError(t, err)
14944	rr = executeRequest(req)
14945	checkResponseCode(t, http.StatusOK, rr)
14946}
14947
14948func waitForUsersQuotaScan(t *testing.T, token string) {
14949	for {
14950		var scans []common.ActiveQuotaScan
14951		req, _ := http.NewRequest(http.MethodGet, quotaScanPath, nil)
14952		setBearerForReq(req, token)
14953		rr := executeRequest(req)
14954		checkResponseCode(t, http.StatusOK, rr)
14955		err := render.DecodeJSON(rr.Body, &scans)
14956
14957		if !assert.NoError(t, err, "Error getting active scans") {
14958			break
14959		}
14960		if len(scans) == 0 {
14961			break
14962		}
14963		time.Sleep(100 * time.Millisecond)
14964	}
14965}
14966
14967func waitForFoldersQuotaScanPath(t *testing.T, token string) {
14968	var scans []common.ActiveVirtualFolderQuotaScan
14969	for {
14970		req, _ := http.NewRequest(http.MethodGet, quotaScanVFolderPath, nil)
14971		setBearerForReq(req, token)
14972		rr := executeRequest(req)
14973		checkResponseCode(t, http.StatusOK, rr)
14974		err := render.DecodeJSON(rr.Body, &scans)
14975		if !assert.NoError(t, err, "Error getting active folders scans") {
14976			break
14977		}
14978		if len(scans) == 0 {
14979			break
14980		}
14981		time.Sleep(100 * time.Millisecond)
14982	}
14983}
14984
14985func waitTCPListening(address string) {
14986	for {
14987		conn, err := net.Dial("tcp", address)
14988		if err != nil {
14989			logger.WarnToConsole("tcp server %v not listening: %v", address, err)
14990			time.Sleep(100 * time.Millisecond)
14991			continue
14992		}
14993		logger.InfoToConsole("tcp server %v now listening", address)
14994		conn.Close()
14995		break
14996	}
14997}
14998
14999func startSMTPServer() {
15000	go func() {
15001		if err := smtpd.ListenAndServe(smtpServerAddr, func(remoteAddr net.Addr, from string, to []string, data []byte) error {
15002			re := regexp.MustCompile(`code is ".*?"`)
15003			code := strings.TrimPrefix(string(re.Find(data)), "code is ")
15004			lastResetCode = strings.ReplaceAll(code, "\"", "")
15005			return nil
15006		}, "SFTPGo test", "localhost"); err != nil {
15007			logger.ErrorToConsole("could not start SMTP server: %v", err)
15008			os.Exit(1)
15009		}
15010	}()
15011	waitTCPListening(smtpServerAddr)
15012}
15013
15014func getTestAdmin() dataprovider.Admin {
15015	return dataprovider.Admin{
15016		Username:    defaultTokenAuthUser,
15017		Password:    defaultTokenAuthPass,
15018		Status:      1,
15019		Permissions: []string{dataprovider.PermAdminAny},
15020		Email:       "admin@example.com",
15021		Description: "test admin",
15022	}
15023}
15024
15025func getTestUser() dataprovider.User {
15026	user := dataprovider.User{
15027		BaseUser: sdk.BaseUser{
15028			Username:    defaultUsername,
15029			Password:    defaultPassword,
15030			HomeDir:     filepath.Join(homeBasePath, defaultUsername),
15031			Status:      1,
15032			Description: "test user",
15033		},
15034	}
15035	user.Permissions = make(map[string][]string)
15036	user.Permissions["/"] = defaultPerms
15037	return user
15038}
15039
15040func getTestSFTPUser() dataprovider.User {
15041	u := getTestUser()
15042	u.Username = u.Username + "_sftp"
15043	u.FsConfig.Provider = sdk.SFTPFilesystemProvider
15044	u.FsConfig.SFTPConfig.Endpoint = sftpServerAddr
15045	u.FsConfig.SFTPConfig.Username = defaultUsername
15046	u.FsConfig.SFTPConfig.Password = kms.NewPlainSecret(defaultPassword)
15047	return u
15048}
15049
15050func getUserAsJSON(t *testing.T, user dataprovider.User) []byte {
15051	json, err := json.Marshal(user)
15052	assert.NoError(t, err)
15053	return json
15054}
15055
15056func getCSRFToken(url string) (string, error) {
15057	req, err := http.NewRequest(http.MethodGet, url, nil)
15058	if err != nil {
15059		return "", err
15060	}
15061	resp, err := httpclient.GetHTTPClient().Do(req)
15062	if err != nil {
15063		return "", err
15064	}
15065
15066	defer resp.Body.Close()
15067
15068	doc, err := html.Parse(resp.Body)
15069	if err != nil {
15070		return "", err
15071	}
15072
15073	var csrfToken string
15074	var f func(*html.Node)
15075
15076	f = func(n *html.Node) {
15077		if n.Type == html.ElementNode && n.Data == "input" {
15078			var name, value string
15079			for _, attr := range n.Attr {
15080				if attr.Key == "value" {
15081					value = attr.Val
15082				}
15083				if attr.Key == "name" {
15084					name = attr.Val
15085				}
15086			}
15087			if name == csrfFormToken {
15088				csrfToken = value
15089				return
15090			}
15091		}
15092
15093		for c := n.FirstChild; c != nil; c = c.NextSibling {
15094			f(c)
15095		}
15096	}
15097
15098	f(doc)
15099
15100	return csrfToken, nil
15101}
15102
15103func getLoginForm(username, password, csrfToken string) url.Values {
15104	form := make(url.Values)
15105	form.Set("username", username)
15106	form.Set("password", password)
15107	form.Set(csrfFormToken, csrfToken)
15108	return form
15109}
15110
15111func setCSRFHeaderForReq(req *http.Request, csrfToken string) {
15112	req.Header.Set("X-CSRF-TOKEN", csrfToken)
15113}
15114
15115func setBearerForReq(req *http.Request, jwtToken string) {
15116	req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", jwtToken))
15117}
15118
15119func setAPIKeyForReq(req *http.Request, apiKey, username string) {
15120	if username != "" {
15121		apiKey += "." + username
15122	}
15123	req.Header.Set("X-SFTPGO-API-KEY", apiKey)
15124}
15125
15126func setJWTCookieForReq(req *http.Request, jwtToken string) {
15127	req.Header.Set("Cookie", fmt.Sprintf("jwt=%v", jwtToken))
15128}
15129
15130func getJWTAPITokenFromTestServer(username, password string) (string, error) {
15131	req, _ := http.NewRequest(http.MethodGet, tokenPath, nil)
15132	req.SetBasicAuth(username, password)
15133	rr := executeRequest(req)
15134	if rr.Code != http.StatusOK {
15135		return "", fmt.Errorf("unexpected  status code %v", rr.Code)
15136	}
15137	responseHolder := make(map[string]interface{})
15138	err := render.DecodeJSON(rr.Body, &responseHolder)
15139	if err != nil {
15140		return "", err
15141	}
15142	return responseHolder["access_token"].(string), nil
15143}
15144
15145func getJWTAPIUserTokenFromTestServer(username, password string) (string, error) {
15146	req, _ := http.NewRequest(http.MethodGet, userTokenPath, nil)
15147	req.SetBasicAuth(username, password)
15148	rr := executeRequest(req)
15149	if rr.Code != http.StatusOK {
15150		return "", fmt.Errorf("unexpected  status code %v", rr.Code)
15151	}
15152	responseHolder := make(map[string]interface{})
15153	err := render.DecodeJSON(rr.Body, &responseHolder)
15154	if err != nil {
15155		return "", err
15156	}
15157	return responseHolder["access_token"].(string), nil
15158}
15159
15160func getJWTWebToken(username, password string) (string, error) {
15161	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
15162	if err != nil {
15163		return "", err
15164	}
15165	form := getLoginForm(username, password, csrfToken)
15166	req, _ := http.NewRequest(http.MethodPost, httpBaseURL+webLoginPath,
15167		bytes.NewBuffer([]byte(form.Encode())))
15168	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
15169	client := &http.Client{
15170		Timeout: 10 * time.Second,
15171		CheckRedirect: func(req *http.Request, via []*http.Request) error {
15172			return http.ErrUseLastResponse
15173		},
15174	}
15175	resp, err := client.Do(req)
15176	if err != nil {
15177		return "", err
15178	}
15179	defer resp.Body.Close()
15180
15181	if resp.StatusCode != http.StatusFound {
15182		return "", fmt.Errorf("unexpected  status code %v", resp.StatusCode)
15183	}
15184	cookie := resp.Header.Get("Set-Cookie")
15185	if strings.HasPrefix(cookie, "jwt=") {
15186		return cookie[4:], nil
15187	}
15188	return "", errors.New("no cookie found")
15189}
15190
15191func getJWTWebClientTokenFromTestServerWithAddr(username, password, remoteAddr string) (string, error) {
15192	csrfToken, err := getCSRFToken(httpBaseURL + webClientLoginPath)
15193	if err != nil {
15194		return "", err
15195	}
15196	form := getLoginForm(username, password, csrfToken)
15197	req, _ := http.NewRequest(http.MethodPost, webClientLoginPath, bytes.NewBuffer([]byte(form.Encode())))
15198	req.RemoteAddr = remoteAddr
15199	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
15200	rr := executeRequest(req)
15201	if rr.Code != http.StatusFound {
15202		return "", fmt.Errorf("unexpected  status code %v", rr)
15203	}
15204	return getCookieFromResponse(rr)
15205}
15206
15207func getCookieFromResponse(rr *httptest.ResponseRecorder) (string, error) {
15208	cookie := strings.Split(rr.Header().Get("Set-Cookie"), ";")
15209	if strings.HasPrefix(cookie[0], "jwt=") {
15210		return cookie[0][4:], nil
15211	}
15212	return "", errors.New("no cookie found")
15213}
15214
15215func getJWTWebClientTokenFromTestServer(username, password string) (string, error) {
15216	return getJWTWebClientTokenFromTestServerWithAddr(username, password, "")
15217}
15218
15219func getJWTWebTokenFromTestServer(username, password string) (string, error) {
15220	csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
15221	if err != nil {
15222		return "", err
15223	}
15224	form := getLoginForm(username, password, csrfToken)
15225	req, _ := http.NewRequest(http.MethodPost, webLoginPath, bytes.NewBuffer([]byte(form.Encode())))
15226	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
15227	rr := executeRequest(req)
15228	if rr.Code != http.StatusFound {
15229		return "", fmt.Errorf("unexpected  status code %v", rr)
15230	}
15231	return getCookieFromResponse(rr)
15232}
15233
15234func executeRequest(req *http.Request) *httptest.ResponseRecorder {
15235	rr := httptest.NewRecorder()
15236	testServer.Config.Handler.ServeHTTP(rr, req)
15237	return rr
15238}
15239
15240func checkResponseCode(t *testing.T, expected int, rr *httptest.ResponseRecorder) {
15241	assert.Equal(t, expected, rr.Code, rr.Body.String())
15242}
15243
15244func createTestFile(path string, size int64) error {
15245	baseDir := filepath.Dir(path)
15246	if _, err := os.Stat(baseDir); os.IsNotExist(err) {
15247		err = os.MkdirAll(baseDir, os.ModePerm)
15248		if err != nil {
15249			return err
15250		}
15251	}
15252	content := make([]byte, size)
15253	if size > 0 {
15254		_, err := rand.Read(content)
15255		if err != nil {
15256			return err
15257		}
15258	}
15259	return os.WriteFile(path, content, os.ModePerm)
15260}
15261
15262func getExitCodeScriptContent(exitCode int) []byte {
15263	content := []byte("#!/bin/sh\n\n")
15264	content = append(content, []byte(fmt.Sprintf("exit %v", exitCode))...)
15265	return content
15266}
15267
15268func getMultipartFormData(values url.Values, fileFieldName, filePath string) (bytes.Buffer, string, error) {
15269	var b bytes.Buffer
15270	w := multipart.NewWriter(&b)
15271	for k, v := range values {
15272		for _, s := range v {
15273			if err := w.WriteField(k, s); err != nil {
15274				return b, "", err
15275			}
15276		}
15277	}
15278	if len(fileFieldName) > 0 && len(filePath) > 0 {
15279		fw, err := w.CreateFormFile(fileFieldName, filepath.Base(filePath))
15280		if err != nil {
15281			return b, "", err
15282		}
15283		f, err := os.Open(filePath)
15284		if err != nil {
15285			return b, "", err
15286		}
15287		defer f.Close()
15288		if _, err = io.Copy(fw, f); err != nil {
15289			return b, "", err
15290		}
15291	}
15292	err := w.Close()
15293	return b, w.FormDataContentType(), err
15294}
15295
15296func generateTOTPPasscode(secret string) (string, error) {
15297	return totp.GenerateCodeCustom(secret, time.Now(), totp.ValidateOpts{
15298		Period:    30,
15299		Skew:      1,
15300		Digits:    otp.DigitsSix,
15301		Algorithm: otp.AlgorithmSHA1,
15302	})
15303}
15304
15305func BenchmarkSecretDecryption(b *testing.B) {
15306	s := kms.NewPlainSecret("test data")
15307	s.SetAdditionalData("username")
15308	err := s.Encrypt()
15309	require.NoError(b, err)
15310	for i := 0; i < b.N; i++ {
15311		err = s.Clone().Decrypt()
15312		require.NoError(b, err)
15313	}
15314}
15315