1package common_test 2 3import ( 4 "bytes" 5 "crypto/rand" 6 "fmt" 7 "io" 8 "math" 9 "net" 10 "net/http" 11 "os" 12 "path" 13 "path/filepath" 14 "runtime" 15 "strings" 16 "testing" 17 "time" 18 19 _ "github.com/go-sql-driver/mysql" 20 _ "github.com/lib/pq" 21 _ "github.com/mattn/go-sqlite3" 22 "github.com/mhale/smtpd" 23 "github.com/pkg/sftp" 24 "github.com/pquerna/otp" 25 "github.com/pquerna/otp/totp" 26 "github.com/rs/zerolog" 27 "github.com/stretchr/testify/assert" 28 "github.com/stretchr/testify/require" 29 "golang.org/x/crypto/ssh" 30 31 "github.com/drakkan/sftpgo/v2/common" 32 "github.com/drakkan/sftpgo/v2/config" 33 "github.com/drakkan/sftpgo/v2/dataprovider" 34 "github.com/drakkan/sftpgo/v2/httpclient" 35 "github.com/drakkan/sftpgo/v2/httpdtest" 36 "github.com/drakkan/sftpgo/v2/kms" 37 "github.com/drakkan/sftpgo/v2/logger" 38 "github.com/drakkan/sftpgo/v2/mfa" 39 "github.com/drakkan/sftpgo/v2/sdk" 40 "github.com/drakkan/sftpgo/v2/vfs" 41) 42 43const ( 44 configDir = ".." 45 httpAddr = "127.0.0.1:9999" 46 httpProxyAddr = "127.0.0.1:7777" 47 sftpServerAddr = "127.0.0.1:4022" 48 smtpServerAddr = "127.0.0.1:2525" 49 defaultUsername = "test_common_sftp" 50 defaultPassword = "test_password" 51 defaultSFTPUsername = "test_common_sftpfs_user" 52 osWindows = "windows" 53 testFileName = "test_file_common_sftp.dat" 54 testDir = "test_dir_common" 55) 56 57var ( 58 allPerms = []string{dataprovider.PermAny} 59 homeBasePath string 60 testFileContent = []byte("test data") 61) 62 63func TestMain(m *testing.M) { 64 homeBasePath = os.TempDir() 65 logFilePath := filepath.Join(configDir, "common_test.log") 66 logger.InitLogger(logFilePath, 5, 1, 28, false, false, zerolog.DebugLevel) 67 68 os.Setenv("SFTPGO_DATA_PROVIDER__CREATE_DEFAULT_ADMIN", "1") 69 os.Setenv("SFTPGO_DEFAULT_ADMIN_USERNAME", "admin") 70 os.Setenv("SFTPGO_DEFAULT_ADMIN_PASSWORD", "password") 71 err := config.LoadConfig(configDir, "") 72 if err != nil { 73 logger.ErrorToConsole("error loading configuration: %v", err) 74 os.Exit(1) 75 } 76 providerConf := config.GetProviderConf() 77 logger.InfoToConsole("Starting COMMON tests, provider: %v", providerConf.Driver) 78 79 err = common.Initialize(config.GetCommonConfig()) 80 if err != nil { 81 logger.WarnToConsole("error initializing common: %v", err) 82 os.Exit(1) 83 } 84 85 err = dataprovider.Initialize(providerConf, configDir, true) 86 if err != nil { 87 logger.ErrorToConsole("error initializing data provider: %v", err) 88 os.Exit(1) 89 } 90 91 httpConfig := config.GetHTTPConfig() 92 httpConfig.Timeout = 5 93 httpConfig.RetryMax = 0 94 httpConfig.Initialize(configDir) //nolint:errcheck 95 kmsConfig := config.GetKMSConfig() 96 err = kmsConfig.Initialize() 97 if err != nil { 98 logger.ErrorToConsole("error initializing kms: %v", err) 99 os.Exit(1) 100 } 101 mfaConfig := config.GetMFAConfig() 102 err = mfaConfig.Initialize() 103 if err != nil { 104 logger.ErrorToConsole("error initializing MFA: %v", err) 105 os.Exit(1) 106 } 107 108 sftpdConf := config.GetSFTPDConfig() 109 sftpdConf.Bindings[0].Port = 4022 110 sftpdConf.KeyboardInteractiveAuthentication = true 111 112 httpdConf := config.GetHTTPDConfig() 113 httpdConf.Bindings[0].Port = 4080 114 httpdtest.SetBaseURL("http://127.0.0.1:4080") 115 116 go func() { 117 if err := sftpdConf.Initialize(configDir); err != nil { 118 logger.ErrorToConsole("could not start SFTP server: %v", err) 119 os.Exit(1) 120 } 121 }() 122 123 go func() { 124 if err := httpdConf.Initialize(configDir); err != nil { 125 logger.ErrorToConsole("could not start HTTP server: %v", err) 126 os.Exit(1) 127 } 128 }() 129 130 go func() { 131 // start a test HTTP server to receive action notifications 132 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 133 fmt.Fprintf(w, "OK\n") 134 }) 135 http.HandleFunc("/404", func(w http.ResponseWriter, r *http.Request) { 136 w.WriteHeader(http.StatusNotFound) 137 fmt.Fprintf(w, "Not found\n") 138 }) 139 if err := http.ListenAndServe(httpAddr, nil); err != nil { 140 logger.ErrorToConsole("could not start HTTP notification server: %v", err) 141 os.Exit(1) 142 } 143 }() 144 145 go func() { 146 common.Config.ProxyProtocol = 2 147 listener, err := net.Listen("tcp", httpProxyAddr) 148 if err != nil { 149 logger.ErrorToConsole("error creating listener for proxy protocol server: %v", err) 150 os.Exit(1) 151 } 152 proxyListener, err := common.Config.GetProxyListener(listener) 153 if err != nil { 154 logger.ErrorToConsole("error creating proxy protocol listener: %v", err) 155 os.Exit(1) 156 } 157 common.Config.ProxyProtocol = 0 158 159 s := &http.Server{} 160 if err := s.Serve(proxyListener); err != nil { 161 logger.ErrorToConsole("could not start HTTP proxy protocol server: %v", err) 162 os.Exit(1) 163 } 164 }() 165 166 go func() { 167 if err := smtpd.ListenAndServe(smtpServerAddr, func(remoteAddr net.Addr, from string, to []string, data []byte) error { 168 return nil 169 }, "SFTPGo test", "localhost"); err != nil { 170 logger.ErrorToConsole("could not start SMTP server: %v", err) 171 os.Exit(1) 172 } 173 }() 174 175 waitTCPListening(httpAddr) 176 waitTCPListening(httpProxyAddr) 177 waitTCPListening(smtpServerAddr) 178 179 waitTCPListening(sftpdConf.Bindings[0].GetAddress()) 180 waitTCPListening(httpdConf.Bindings[0].GetAddress()) 181 182 exitCode := m.Run() 183 os.Remove(logFilePath) 184 os.Exit(exitCode) 185} 186 187func TestBaseConnection(t *testing.T) { 188 u := getTestUser() 189 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 190 assert.NoError(t, err) 191 err = os.RemoveAll(user.GetHomeDir()) 192 assert.NoError(t, err) 193 conn, client, err := getSftpClient(user) 194 if assert.NoError(t, err) { 195 defer conn.Close() 196 defer client.Close() 197 assert.NoError(t, checkBasicSFTP(client)) 198 _, err = client.ReadDir(testDir) 199 assert.ErrorIs(t, err, os.ErrNotExist) 200 err = client.RemoveDirectory(testDir) 201 assert.ErrorIs(t, err, os.ErrNotExist) 202 err = client.Mkdir(testDir) 203 assert.NoError(t, err) 204 err = client.Mkdir(testDir) 205 assert.Error(t, err) 206 info, err := client.Stat(testDir) 207 if assert.NoError(t, err) { 208 assert.True(t, info.IsDir()) 209 } 210 err = client.RemoveDirectory(testDir) 211 assert.NoError(t, err) 212 err = client.Remove(testFileName) 213 assert.ErrorIs(t, err, os.ErrNotExist) 214 f, err := client.Create(testFileName) 215 assert.NoError(t, err) 216 _, err = f.Write(testFileContent) 217 assert.NoError(t, err) 218 err = f.Close() 219 assert.NoError(t, err) 220 linkName := testFileName + ".link" 221 err = client.Symlink(testFileName, linkName) 222 assert.NoError(t, err) 223 err = client.Symlink(testFileName, testFileName) 224 assert.Error(t, err) 225 info, err = client.Stat(testFileName) 226 if assert.NoError(t, err) { 227 assert.Equal(t, int64(len(testFileContent)), info.Size()) 228 assert.False(t, info.IsDir()) 229 } 230 info, err = client.Lstat(linkName) 231 if assert.NoError(t, err) { 232 assert.NotEqual(t, int64(7), info.Size()) 233 assert.True(t, info.Mode()&os.ModeSymlink != 0) 234 assert.False(t, info.IsDir()) 235 } 236 err = client.RemoveDirectory(linkName) 237 if assert.Error(t, err) { 238 assert.Contains(t, err.Error(), "SSH_FX_FAILURE") 239 } 240 err = client.Remove(testFileName) 241 assert.NoError(t, err) 242 err = client.Remove(linkName) 243 assert.NoError(t, err) 244 err = client.Rename(testFileName, "test") 245 assert.ErrorIs(t, err, os.ErrNotExist) 246 f, err = client.Create(testFileName) 247 assert.NoError(t, err) 248 err = f.Close() 249 assert.NoError(t, err) 250 err = client.Rename(testFileName, testFileName+"1") 251 assert.NoError(t, err) 252 err = client.Remove(testFileName + "1") 253 assert.NoError(t, err) 254 err = client.RemoveDirectory("missing") 255 assert.Error(t, err) 256 } 257 258 _, err = httpdtest.RemoveUser(user, http.StatusOK) 259 assert.NoError(t, err) 260 err = os.RemoveAll(user.GetHomeDir()) 261 assert.NoError(t, err) 262} 263 264func TestSetStat(t *testing.T) { 265 u := getTestUser() 266 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 267 assert.NoError(t, err) 268 err = os.RemoveAll(user.GetHomeDir()) 269 assert.NoError(t, err) 270 conn, client, err := getSftpClient(user) 271 if assert.NoError(t, err) { 272 defer conn.Close() 273 defer client.Close() 274 f, err := client.Create(testFileName) 275 assert.NoError(t, err) 276 _, err = f.Write(testFileContent) 277 assert.NoError(t, err) 278 err = f.Close() 279 assert.NoError(t, err) 280 acmodTime := time.Now().Add(36 * time.Hour) 281 err = client.Chtimes(testFileName, acmodTime, acmodTime) 282 assert.NoError(t, err) 283 newFi, err := client.Lstat(testFileName) 284 assert.NoError(t, err) 285 diff := math.Abs(newFi.ModTime().Sub(acmodTime).Seconds()) 286 assert.LessOrEqual(t, diff, float64(1)) 287 if runtime.GOOS != osWindows { 288 err = client.Chown(testFileName, os.Getuid(), os.Getgid()) 289 assert.NoError(t, err) 290 } 291 newPerm := os.FileMode(0666) 292 err = client.Chmod(testFileName, newPerm) 293 assert.NoError(t, err) 294 newFi, err = client.Lstat(testFileName) 295 if assert.NoError(t, err) { 296 assert.Equal(t, newPerm, newFi.Mode().Perm()) 297 } 298 err = client.Truncate(testFileName, 2) 299 assert.NoError(t, err) 300 info, err := client.Stat(testFileName) 301 if assert.NoError(t, err) { 302 assert.Equal(t, int64(2), info.Size()) 303 } 304 err = client.Remove(testFileName) 305 assert.NoError(t, err) 306 307 err = client.Truncate(testFileName, 0) 308 assert.ErrorIs(t, err, os.ErrNotExist) 309 err = client.Chtimes(testFileName, acmodTime, acmodTime) 310 assert.ErrorIs(t, err, os.ErrNotExist) 311 if runtime.GOOS != osWindows { 312 err = client.Chown(testFileName, os.Getuid(), os.Getgid()) 313 assert.ErrorIs(t, err, os.ErrNotExist) 314 } 315 err = client.Chmod(testFileName, newPerm) 316 assert.ErrorIs(t, err, os.ErrNotExist) 317 } 318 _, err = httpdtest.RemoveUser(user, http.StatusOK) 319 assert.NoError(t, err) 320 err = os.RemoveAll(user.GetHomeDir()) 321 assert.NoError(t, err) 322} 323 324func TestChtimesOpenHandle(t *testing.T) { 325 localUser, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated) 326 assert.NoError(t, err) 327 sftpUser, _, err := httpdtest.AddUser(getTestSFTPUser(), http.StatusCreated) 328 assert.NoError(t, err) 329 u := getCryptFsUser() 330 u.Username += "_crypt" 331 cryptFsUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 332 assert.NoError(t, err) 333 334 for _, user := range []dataprovider.User{localUser, sftpUser, cryptFsUser} { 335 conn, client, err := getSftpClient(user) 336 if assert.NoError(t, err) { 337 defer conn.Close() 338 defer client.Close() 339 340 f, err := client.Create(testFileName) 341 assert.NoError(t, err, "user %v", user.Username) 342 f1, err := client.Create(testFileName + "1") 343 assert.NoError(t, err, "user %v", user.Username) 344 acmodTime := time.Now().Add(36 * time.Hour) 345 err = client.Chtimes(testFileName, acmodTime, acmodTime) 346 assert.NoError(t, err, "user %v", user.Username) 347 _, err = f.Write(testFileContent) 348 assert.NoError(t, err, "user %v", user.Username) 349 err = f.Close() 350 assert.NoError(t, err, "user %v", user.Username) 351 err = f1.Close() 352 assert.NoError(t, err, "user %v", user.Username) 353 info, err := client.Lstat(testFileName) 354 assert.NoError(t, err, "user %v", user.Username) 355 diff := math.Abs(info.ModTime().Sub(acmodTime).Seconds()) 356 assert.LessOrEqual(t, diff, float64(1), "user %v", user.Username) 357 info1, err := client.Lstat(testFileName + "1") 358 assert.NoError(t, err, "user %v", user.Username) 359 diff = math.Abs(info1.ModTime().Sub(acmodTime).Seconds()) 360 assert.Greater(t, diff, float64(86400), "user %v", user.Username) 361 } 362 } 363 364 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 365 assert.NoError(t, err) 366 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 367 assert.NoError(t, err) 368 err = os.RemoveAll(localUser.GetHomeDir()) 369 assert.NoError(t, err) 370 _, err = httpdtest.RemoveUser(cryptFsUser, http.StatusOK) 371 assert.NoError(t, err) 372 err = os.RemoveAll(cryptFsUser.GetHomeDir()) 373 assert.NoError(t, err) 374} 375 376func TestPermissionErrors(t *testing.T) { 377 user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated) 378 assert.NoError(t, err) 379 u := getTestSFTPUser() 380 subDir := "/sub" 381 u.Permissions[subDir] = nil 382 sftpUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 383 assert.NoError(t, err) 384 conn, client, err := getSftpClient(user) 385 if assert.NoError(t, err) { 386 defer conn.Close() 387 defer client.Close() 388 err = client.MkdirAll(path.Join(subDir, subDir)) 389 assert.NoError(t, err) 390 f, err := client.Create(path.Join(subDir, subDir, testFileName)) 391 if assert.NoError(t, err) { 392 _, err = f.Write(testFileContent) 393 assert.NoError(t, err) 394 err = f.Close() 395 assert.NoError(t, err) 396 } 397 } 398 conn, client, err = getSftpClient(sftpUser) 399 if assert.NoError(t, err) { 400 defer conn.Close() 401 defer client.Close() 402 assert.NoError(t, checkBasicSFTP(client)) 403 _, err = client.ReadDir(subDir) 404 assert.ErrorIs(t, err, os.ErrPermission) 405 err = client.Mkdir(path.Join(subDir, subDir)) 406 assert.ErrorIs(t, err, os.ErrPermission) 407 err = client.RemoveDirectory(path.Join(subDir, subDir)) 408 assert.ErrorIs(t, err, os.ErrPermission) 409 err = client.Symlink("test", path.Join(subDir, subDir)) 410 assert.ErrorIs(t, err, os.ErrPermission) 411 err = client.Chmod(path.Join(subDir, subDir), os.ModePerm) 412 assert.ErrorIs(t, err, os.ErrPermission) 413 err = client.Chown(path.Join(subDir, subDir), os.Getuid(), os.Getgid()) 414 assert.ErrorIs(t, err, os.ErrPermission) 415 err = client.Chtimes(path.Join(subDir, subDir), time.Now(), time.Now()) 416 assert.ErrorIs(t, err, os.ErrPermission) 417 err = client.Truncate(path.Join(subDir, subDir), 0) 418 assert.ErrorIs(t, err, os.ErrPermission) 419 err = client.Remove(path.Join(subDir, subDir, testFileName)) 420 assert.ErrorIs(t, err, os.ErrPermission) 421 } 422 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 423 assert.NoError(t, err) 424 _, err = httpdtest.RemoveUser(user, http.StatusOK) 425 assert.NoError(t, err) 426 err = os.RemoveAll(user.GetHomeDir()) 427 assert.NoError(t, err) 428} 429 430func TestFileNotAllowedErrors(t *testing.T) { 431 deniedDir := "/denied" 432 u := getTestUser() 433 u.Filters.FilePatterns = []sdk.PatternsFilter{ 434 { 435 Path: deniedDir, 436 DeniedPatterns: []string{"*.txt"}, 437 }, 438 } 439 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 440 assert.NoError(t, err) 441 conn, client, err := getSftpClient(user) 442 if assert.NoError(t, err) { 443 defer conn.Close() 444 defer client.Close() 445 testFile := filepath.Join(u.GetHomeDir(), deniedDir, "file.txt") 446 err = os.MkdirAll(filepath.Join(u.GetHomeDir(), deniedDir), os.ModePerm) 447 assert.NoError(t, err) 448 err = os.WriteFile(testFile, testFileContent, os.ModePerm) 449 assert.NoError(t, err) 450 err = client.Remove(path.Join(deniedDir, "file.txt")) 451 // the sftp client will try to remove the path as directory after receiving 452 // a permission denied error, so we get a generic failure here 453 assert.Error(t, err) 454 err = client.Rename(path.Join(deniedDir, "file.txt"), path.Join(deniedDir, "file1.txt")) 455 assert.ErrorIs(t, err, os.ErrPermission) 456 } 457 458 _, err = httpdtest.RemoveUser(user, http.StatusOK) 459 assert.NoError(t, err) 460 err = os.RemoveAll(user.GetHomeDir()) 461 assert.NoError(t, err) 462} 463 464func TestTruncateQuotaLimits(t *testing.T) { 465 u := getTestUser() 466 u.QuotaSize = 20 467 mappedPath1 := filepath.Join(os.TempDir(), "mapped1") 468 folderName1 := filepath.Base(mappedPath1) 469 vdirPath1 := "/vmapped1" 470 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 471 BaseVirtualFolder: vfs.BaseVirtualFolder{ 472 Name: folderName1, 473 MappedPath: mappedPath1, 474 }, 475 VirtualPath: vdirPath1, 476 QuotaFiles: 10, 477 }) 478 mappedPath2 := filepath.Join(os.TempDir(), "mapped2") 479 folderName2 := filepath.Base(mappedPath2) 480 vdirPath2 := "/vmapped2" 481 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 482 BaseVirtualFolder: vfs.BaseVirtualFolder{ 483 Name: folderName2, 484 MappedPath: mappedPath2, 485 }, 486 VirtualPath: vdirPath2, 487 QuotaFiles: -1, 488 QuotaSize: -1, 489 }) 490 localUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 491 assert.NoError(t, err) 492 u = getTestSFTPUser() 493 u.QuotaSize = 20 494 sftpUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 495 assert.NoError(t, err) 496 for _, user := range []dataprovider.User{localUser, sftpUser} { 497 conn, client, err := getSftpClient(user) 498 if assert.NoError(t, err) { 499 defer conn.Close() 500 defer client.Close() 501 f, err := client.OpenFile(testFileName, os.O_WRONLY) 502 if assert.NoError(t, err) { 503 n, err := f.Write(testFileContent) 504 assert.NoError(t, err) 505 assert.Equal(t, len(testFileContent), n) 506 err = f.Truncate(2) 507 assert.NoError(t, err) 508 expectedQuotaFiles := 0 509 expectedQuotaSize := int64(2) 510 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 511 assert.NoError(t, err) 512 assert.Equal(t, expectedQuotaFiles, user.UsedQuotaFiles) 513 assert.Equal(t, expectedQuotaSize, user.UsedQuotaSize) 514 _, err = f.Seek(expectedQuotaSize, io.SeekStart) 515 assert.NoError(t, err) 516 n, err = f.Write(testFileContent) 517 assert.NoError(t, err) 518 assert.Equal(t, len(testFileContent), n) 519 err = f.Truncate(5) 520 assert.NoError(t, err) 521 expectedQuotaSize = int64(5) 522 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 523 assert.NoError(t, err) 524 assert.Equal(t, expectedQuotaFiles, user.UsedQuotaFiles) 525 assert.Equal(t, expectedQuotaSize, user.UsedQuotaSize) 526 _, err = f.Seek(expectedQuotaSize, io.SeekStart) 527 assert.NoError(t, err) 528 n, err = f.Write(testFileContent) 529 assert.NoError(t, err) 530 assert.Equal(t, len(testFileContent), n) 531 err = f.Close() 532 assert.NoError(t, err) 533 expectedQuotaFiles = 1 534 expectedQuotaSize = int64(5) + int64(len(testFileContent)) 535 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 536 assert.NoError(t, err) 537 assert.Equal(t, expectedQuotaFiles, user.UsedQuotaFiles) 538 assert.Equal(t, expectedQuotaSize, user.UsedQuotaSize) 539 } 540 // now truncate by path 541 err = client.Truncate(testFileName, 5) 542 assert.NoError(t, err) 543 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 544 assert.NoError(t, err) 545 assert.Equal(t, 1, user.UsedQuotaFiles) 546 assert.Equal(t, int64(5), user.UsedQuotaSize) 547 // now open an existing file without truncate it, quota should not change 548 f, err = client.OpenFile(testFileName, os.O_WRONLY) 549 if assert.NoError(t, err) { 550 err = f.Close() 551 assert.NoError(t, err) 552 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 553 assert.NoError(t, err) 554 assert.Equal(t, 1, user.UsedQuotaFiles) 555 assert.Equal(t, int64(5), user.UsedQuotaSize) 556 } 557 // open the file truncating it 558 f, err = client.OpenFile(testFileName, os.O_WRONLY|os.O_TRUNC) 559 if assert.NoError(t, err) { 560 err = f.Close() 561 assert.NoError(t, err) 562 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 563 assert.NoError(t, err) 564 assert.Equal(t, 1, user.UsedQuotaFiles) 565 assert.Equal(t, int64(0), user.UsedQuotaSize) 566 } 567 // now test max write size 568 f, err = client.OpenFile(testFileName, os.O_WRONLY) 569 if assert.NoError(t, err) { 570 n, err := f.Write(testFileContent) 571 assert.NoError(t, err) 572 assert.Equal(t, len(testFileContent), n) 573 err = f.Truncate(11) 574 assert.NoError(t, err) 575 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 576 assert.NoError(t, err) 577 assert.Equal(t, 1, user.UsedQuotaFiles) 578 assert.Equal(t, int64(11), user.UsedQuotaSize) 579 _, err = f.Seek(int64(11), io.SeekStart) 580 assert.NoError(t, err) 581 n, err = f.Write(testFileContent) 582 assert.NoError(t, err) 583 assert.Equal(t, len(testFileContent), n) 584 err = f.Truncate(5) 585 assert.NoError(t, err) 586 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 587 assert.NoError(t, err) 588 assert.Equal(t, 1, user.UsedQuotaFiles) 589 assert.Equal(t, int64(5), user.UsedQuotaSize) 590 _, err = f.Seek(int64(5), io.SeekStart) 591 assert.NoError(t, err) 592 n, err = f.Write(testFileContent) 593 assert.NoError(t, err) 594 assert.Equal(t, len(testFileContent), n) 595 err = f.Truncate(12) 596 assert.NoError(t, err) 597 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 598 assert.NoError(t, err) 599 assert.Equal(t, 1, user.UsedQuotaFiles) 600 assert.Equal(t, int64(12), user.UsedQuotaSize) 601 _, err = f.Seek(int64(12), io.SeekStart) 602 assert.NoError(t, err) 603 _, err = f.Write(testFileContent) 604 if assert.Error(t, err) { 605 assert.Contains(t, err.Error(), common.ErrQuotaExceeded.Error()) 606 } 607 err = f.Close() 608 assert.Error(t, err) 609 // the file is deleted 610 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 611 assert.NoError(t, err) 612 assert.Equal(t, 0, user.UsedQuotaFiles) 613 assert.Equal(t, int64(0), user.UsedQuotaSize) 614 } 615 616 if user.Username == defaultUsername { 617 // basic test inside a virtual folder 618 vfileName1 := path.Join(vdirPath1, testFileName) 619 f, err = client.OpenFile(vfileName1, os.O_WRONLY) 620 if assert.NoError(t, err) { 621 n, err := f.Write(testFileContent) 622 assert.NoError(t, err) 623 assert.Equal(t, len(testFileContent), n) 624 err = f.Truncate(2) 625 assert.NoError(t, err) 626 expectedQuotaFiles := 0 627 expectedQuotaSize := int64(2) 628 fold, _, err := httpdtest.GetFolderByName(folderName1, http.StatusOK) 629 assert.NoError(t, err) 630 assert.Equal(t, expectedQuotaSize, fold.UsedQuotaSize) 631 assert.Equal(t, expectedQuotaFiles, fold.UsedQuotaFiles) 632 err = f.Close() 633 assert.NoError(t, err) 634 expectedQuotaFiles = 1 635 fold, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 636 assert.NoError(t, err) 637 assert.Equal(t, expectedQuotaSize, fold.UsedQuotaSize) 638 assert.Equal(t, expectedQuotaFiles, fold.UsedQuotaFiles) 639 } 640 err = client.Truncate(vfileName1, 1) 641 assert.NoError(t, err) 642 fold, _, err := httpdtest.GetFolderByName(folderName1, http.StatusOK) 643 assert.NoError(t, err) 644 assert.Equal(t, int64(1), fold.UsedQuotaSize) 645 assert.Equal(t, 1, fold.UsedQuotaFiles) 646 // now test on vdirPath2, the folder quota is included in the user's quota 647 vfileName2 := path.Join(vdirPath2, testFileName) 648 f, err = client.OpenFile(vfileName2, os.O_WRONLY) 649 if assert.NoError(t, err) { 650 n, err := f.Write(testFileContent) 651 assert.NoError(t, err) 652 assert.Equal(t, len(testFileContent), n) 653 err = f.Truncate(3) 654 assert.NoError(t, err) 655 expectedQuotaFiles := 0 656 expectedQuotaSize := int64(3) 657 fold, _, err := httpdtest.GetFolderByName(folderName2, http.StatusOK) 658 assert.NoError(t, err) 659 assert.Equal(t, expectedQuotaSize, fold.UsedQuotaSize) 660 assert.Equal(t, expectedQuotaFiles, fold.UsedQuotaFiles) 661 err = f.Close() 662 assert.NoError(t, err) 663 expectedQuotaFiles = 1 664 fold, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 665 assert.NoError(t, err) 666 assert.Equal(t, expectedQuotaSize, fold.UsedQuotaSize) 667 assert.Equal(t, expectedQuotaFiles, fold.UsedQuotaFiles) 668 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 669 assert.NoError(t, err) 670 assert.Equal(t, expectedQuotaFiles, user.UsedQuotaFiles) 671 assert.Equal(t, expectedQuotaSize, user.UsedQuotaSize) 672 } 673 674 // cleanup 675 err = os.RemoveAll(user.GetHomeDir()) 676 assert.NoError(t, err) 677 user.UsedQuotaFiles = 0 678 user.UsedQuotaSize = 0 679 _, err = httpdtest.UpdateQuotaUsage(user, "reset", http.StatusOK) 680 assert.NoError(t, err) 681 user.QuotaSize = 0 682 _, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 683 assert.NoError(t, err) 684 } 685 } 686 } 687 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 688 assert.NoError(t, err) 689 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 690 assert.NoError(t, err) 691 err = os.RemoveAll(localUser.GetHomeDir()) 692 assert.NoError(t, err) 693 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName1}, http.StatusOK) 694 assert.NoError(t, err) 695 err = os.RemoveAll(mappedPath1) 696 assert.NoError(t, err) 697 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName2}, http.StatusOK) 698 assert.NoError(t, err) 699 err = os.RemoveAll(mappedPath2) 700 assert.NoError(t, err) 701} 702 703func TestVirtualFoldersQuotaRenameOverwrite(t *testing.T) { 704 testFileSize := int64(131072) 705 testFileSize1 := int64(65537) 706 testFileName1 := "test_file1.dat" //nolint:goconst 707 u := getTestUser() 708 u.QuotaFiles = 0 709 u.QuotaSize = 0 710 mappedPath1 := filepath.Join(os.TempDir(), "vdir1") 711 folderName1 := filepath.Base(mappedPath1) 712 vdirPath1 := "/vdir1" //nolint:goconst 713 mappedPath2 := filepath.Join(os.TempDir(), "vdir2") 714 folderName2 := filepath.Base(mappedPath2) 715 vdirPath2 := "/vdir2" //nolint:goconst 716 mappedPath3 := filepath.Join(os.TempDir(), "vdir3") 717 folderName3 := filepath.Base(mappedPath3) 718 vdirPath3 := "/vdir3" 719 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 720 BaseVirtualFolder: vfs.BaseVirtualFolder{ 721 Name: folderName1, 722 MappedPath: mappedPath1, 723 }, 724 VirtualPath: vdirPath1, 725 QuotaFiles: 2, 726 QuotaSize: 0, 727 }) 728 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 729 BaseVirtualFolder: vfs.BaseVirtualFolder{ 730 MappedPath: mappedPath2, 731 Name: folderName2, 732 }, 733 VirtualPath: vdirPath2, 734 QuotaFiles: 0, 735 QuotaSize: testFileSize + testFileSize1 + 1, 736 }) 737 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 738 BaseVirtualFolder: vfs.BaseVirtualFolder{ 739 Name: folderName3, 740 MappedPath: mappedPath3, 741 }, 742 VirtualPath: vdirPath3, 743 QuotaFiles: 2, 744 QuotaSize: testFileSize * 2, 745 }) 746 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 747 assert.NoError(t, err) 748 conn, client, err := getSftpClient(user) 749 if assert.NoError(t, err) { 750 defer conn.Close() 751 defer client.Close() 752 err = writeSFTPFile(path.Join(vdirPath1, testFileName), testFileSize, client) 753 assert.NoError(t, err) 754 err = writeSFTPFile(path.Join(vdirPath2, testFileName), testFileSize, client) 755 assert.NoError(t, err) 756 err = writeSFTPFile(path.Join(vdirPath1, testFileName1), testFileSize1, client) 757 assert.NoError(t, err) 758 err = writeSFTPFile(path.Join(vdirPath2, testFileName1), testFileSize1, client) 759 assert.NoError(t, err) 760 err = writeSFTPFile(testFileName, testFileSize, client) 761 assert.NoError(t, err) 762 err = writeSFTPFile(testFileName1, testFileSize1, client) 763 assert.NoError(t, err) 764 err = writeSFTPFile(path.Join(vdirPath3, testFileName), testFileSize, client) 765 assert.NoError(t, err) 766 err = writeSFTPFile(path.Join(vdirPath3, testFileName+"1"), testFileSize, client) 767 assert.NoError(t, err) 768 err = client.Rename(testFileName, path.Join(vdirPath1, testFileName+".rename")) 769 assert.Error(t, err) 770 // we overwrite an existing file and we have unlimited size 771 err = client.Rename(testFileName, path.Join(vdirPath1, testFileName)) 772 assert.NoError(t, err) 773 // we have no space and we try to overwrite a bigger file with a smaller one, this should succeed 774 err = client.Rename(testFileName1, path.Join(vdirPath2, testFileName)) 775 assert.NoError(t, err) 776 err = writeSFTPFile(path.Join(vdirPath2, testFileName), testFileSize, client) 777 assert.NoError(t, err) 778 err = writeSFTPFile(testFileName, testFileSize, client) 779 assert.NoError(t, err) 780 // we have no space and we try to overwrite a smaller file with a bigger one, this should fail 781 err = client.Rename(testFileName, path.Join(vdirPath2, testFileName1)) 782 assert.Error(t, err) 783 fi, err := client.Stat(path.Join(vdirPath1, testFileName1)) 784 if assert.NoError(t, err) { 785 assert.Equal(t, testFileSize1, fi.Size()) 786 } 787 // we are overquota inside vdir3 size 2/2 and size 262144/262144 788 err = client.Rename(path.Join(vdirPath1, testFileName1), path.Join(vdirPath3, testFileName1+".rename")) 789 assert.Error(t, err) 790 // we overwrite an existing file and we have enough size 791 err = client.Rename(path.Join(vdirPath1, testFileName1), path.Join(vdirPath3, testFileName)) 792 assert.NoError(t, err) 793 testFileName2 := "test_file2.dat" 794 err = writeSFTPFile(testFileName2, testFileSize+testFileSize1, client) 795 assert.NoError(t, err) 796 // we overwrite an existing file and we haven't enough size 797 err = client.Rename(testFileName2, path.Join(vdirPath3, testFileName)) 798 assert.Error(t, err) 799 // now remove a file from vdir3, create a dir with 2 files and try to rename it in vdir3 800 // this will fail since the rename will result in 3 files inside vdir3 and quota limits only 801 // allow 2 total files there 802 err = client.Remove(path.Join(vdirPath3, testFileName+"1")) 803 assert.NoError(t, err) 804 aDir := "a dir" 805 err = client.Mkdir(aDir) 806 assert.NoError(t, err) 807 err = writeSFTPFile(path.Join(aDir, testFileName1), testFileSize1, client) 808 assert.NoError(t, err) 809 err = writeSFTPFile(path.Join(aDir, testFileName1+"1"), testFileSize1, client) 810 assert.NoError(t, err) 811 err = client.Rename(aDir, path.Join(vdirPath3, aDir)) 812 assert.Error(t, err) 813 } 814 _, err = httpdtest.RemoveUser(user, http.StatusOK) 815 assert.NoError(t, err) 816 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName1}, http.StatusOK) 817 assert.NoError(t, err) 818 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName2}, http.StatusOK) 819 assert.NoError(t, err) 820 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName3}, http.StatusOK) 821 assert.NoError(t, err) 822 err = os.RemoveAll(user.GetHomeDir()) 823 assert.NoError(t, err) 824 err = os.RemoveAll(mappedPath1) 825 assert.NoError(t, err) 826 err = os.RemoveAll(mappedPath2) 827 assert.NoError(t, err) 828 err = os.RemoveAll(mappedPath3) 829 assert.NoError(t, err) 830} 831 832func TestQuotaRenameOverwrite(t *testing.T) { 833 u := getTestUser() 834 u.QuotaFiles = 100 835 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 836 assert.NoError(t, err) 837 conn, client, err := getSftpClient(user) 838 if assert.NoError(t, err) { 839 defer conn.Close() 840 defer client.Close() 841 testFileSize := int64(131072) 842 testFileSize1 := int64(65537) 843 testFileName1 := "test_file1.dat" 844 err = writeSFTPFile(testFileName, testFileSize, client) 845 assert.NoError(t, err) 846 err = writeSFTPFile(testFileName1, testFileSize1, client) 847 assert.NoError(t, err) 848 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 849 assert.NoError(t, err) 850 assert.Equal(t, 2, user.UsedQuotaFiles) 851 assert.Equal(t, testFileSize+testFileSize1, user.UsedQuotaSize) 852 err = client.Rename(testFileName, testFileName1) 853 assert.NoError(t, err) 854 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 855 assert.NoError(t, err) 856 assert.Equal(t, 1, user.UsedQuotaFiles) 857 assert.Equal(t, testFileSize, user.UsedQuotaSize) 858 err = client.Remove(testFileName1) 859 assert.NoError(t, err) 860 err = writeSFTPFile(testFileName, testFileSize, client) 861 assert.NoError(t, err) 862 err = writeSFTPFile(testFileName1, testFileSize1, client) 863 assert.NoError(t, err) 864 err = client.Rename(testFileName1, testFileName) 865 assert.NoError(t, err) 866 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 867 assert.NoError(t, err) 868 assert.Equal(t, 1, user.UsedQuotaFiles) 869 assert.Equal(t, testFileSize1, user.UsedQuotaSize) 870 } 871 _, err = httpdtest.RemoveUser(user, http.StatusOK) 872 assert.NoError(t, err) 873 err = os.RemoveAll(user.GetHomeDir()) 874 assert.NoError(t, err) 875} 876 877func TestVirtualFoldersQuotaValues(t *testing.T) { 878 u := getTestUser() 879 u.QuotaFiles = 100 880 mappedPath1 := filepath.Join(os.TempDir(), "vdir1") 881 vdirPath1 := "/vdir1" 882 folderName1 := filepath.Base(mappedPath1) 883 mappedPath2 := filepath.Join(os.TempDir(), "vdir2") 884 vdirPath2 := "/vdir2" 885 folderName2 := filepath.Base(mappedPath2) 886 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 887 BaseVirtualFolder: vfs.BaseVirtualFolder{ 888 Name: folderName1, 889 MappedPath: mappedPath1, 890 }, 891 VirtualPath: vdirPath1, 892 // quota is included in the user's one 893 QuotaFiles: -1, 894 QuotaSize: -1, 895 }) 896 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 897 BaseVirtualFolder: vfs.BaseVirtualFolder{ 898 Name: folderName2, 899 MappedPath: mappedPath2, 900 }, 901 VirtualPath: vdirPath2, 902 // quota is unlimited and excluded from user's one 903 QuotaFiles: 0, 904 QuotaSize: 0, 905 }) 906 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 907 assert.NoError(t, err) 908 conn, client, err := getSftpClient(user) 909 if assert.NoError(t, err) { 910 defer conn.Close() 911 defer client.Close() 912 testFileSize := int64(131072) 913 err = writeSFTPFile(testFileName, testFileSize, client) 914 assert.NoError(t, err) 915 // we copy the same file two times to test quota update on file overwrite 916 err = writeSFTPFile(path.Join(vdirPath1, testFileName), testFileSize, client) 917 assert.NoError(t, err) 918 err = writeSFTPFile(path.Join(vdirPath1, testFileName), testFileSize, client) 919 assert.NoError(t, err) 920 err = writeSFTPFile(path.Join(vdirPath2, testFileName), testFileSize, client) 921 assert.NoError(t, err) 922 err = writeSFTPFile(path.Join(vdirPath2, testFileName), testFileSize, client) 923 assert.NoError(t, err) 924 expectedQuotaFiles := 2 925 expectedQuotaSize := testFileSize * 2 926 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 927 assert.NoError(t, err) 928 assert.Equal(t, expectedQuotaFiles, user.UsedQuotaFiles) 929 assert.Equal(t, expectedQuotaSize, user.UsedQuotaSize) 930 f, _, err := httpdtest.GetFolderByName(folderName1, http.StatusOK) 931 assert.NoError(t, err) 932 assert.Equal(t, testFileSize, f.UsedQuotaSize) 933 assert.Equal(t, 1, f.UsedQuotaFiles) 934 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 935 assert.NoError(t, err) 936 assert.Equal(t, testFileSize, f.UsedQuotaSize) 937 assert.Equal(t, 1, f.UsedQuotaFiles) 938 939 err = client.Remove(path.Join(vdirPath1, testFileName)) 940 assert.NoError(t, err) 941 err = client.Remove(path.Join(vdirPath2, testFileName)) 942 assert.NoError(t, err) 943 944 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 945 assert.NoError(t, err) 946 assert.Equal(t, int64(0), f.UsedQuotaSize) 947 assert.Equal(t, 0, f.UsedQuotaFiles) 948 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 949 assert.NoError(t, err) 950 assert.Equal(t, int64(0), f.UsedQuotaSize) 951 assert.Equal(t, 0, f.UsedQuotaFiles) 952 } 953 _, err = httpdtest.RemoveUser(user, http.StatusOK) 954 assert.NoError(t, err) 955 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName1}, http.StatusOK) 956 assert.NoError(t, err) 957 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName2}, http.StatusOK) 958 assert.NoError(t, err) 959 err = os.RemoveAll(user.GetHomeDir()) 960 assert.NoError(t, err) 961 err = os.RemoveAll(mappedPath1) 962 assert.NoError(t, err) 963 err = os.RemoveAll(mappedPath2) 964 assert.NoError(t, err) 965} 966 967func TestQuotaRenameInsideSameVirtualFolder(t *testing.T) { 968 u := getTestUser() 969 u.QuotaFiles = 100 970 mappedPath1 := filepath.Join(os.TempDir(), "vdir1") 971 vdirPath1 := "/vdir1" 972 folderName1 := filepath.Base(mappedPath1) 973 mappedPath2 := filepath.Join(os.TempDir(), "vdir2") 974 vdirPath2 := "/vdir2" 975 folderName2 := filepath.Base(mappedPath2) 976 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 977 BaseVirtualFolder: vfs.BaseVirtualFolder{ 978 Name: folderName1, 979 MappedPath: mappedPath1, 980 }, 981 VirtualPath: vdirPath1, 982 // quota is included in the user's one 983 QuotaFiles: -1, 984 QuotaSize: -1, 985 }) 986 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 987 BaseVirtualFolder: vfs.BaseVirtualFolder{ 988 Name: folderName2, 989 MappedPath: mappedPath2, 990 }, 991 VirtualPath: vdirPath2, 992 // quota is unlimited and excluded from user's one 993 QuotaFiles: 0, 994 QuotaSize: 0, 995 }) 996 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 997 assert.NoError(t, err) 998 conn, client, err := getSftpClient(user) 999 if assert.NoError(t, err) { 1000 defer conn.Close() 1001 defer client.Close() 1002 testFileName1 := "test_file1.dat" 1003 testFileSize := int64(131072) 1004 testFileSize1 := int64(65535) 1005 dir1 := "dir1" //nolint:goconst 1006 dir2 := "dir2" //nolint:goconst 1007 assert.NoError(t, err) 1008 err = client.Mkdir(path.Join(vdirPath1, dir1)) 1009 assert.NoError(t, err) 1010 err = client.Mkdir(path.Join(vdirPath1, dir2)) 1011 assert.NoError(t, err) 1012 err = client.Mkdir(path.Join(vdirPath2, dir1)) 1013 assert.NoError(t, err) 1014 err = client.Mkdir(path.Join(vdirPath2, dir2)) 1015 assert.NoError(t, err) 1016 err = writeSFTPFile(path.Join(vdirPath1, dir1, testFileName), testFileSize, client) 1017 assert.NoError(t, err) 1018 err = writeSFTPFile(path.Join(vdirPath1, dir2, testFileName1), testFileSize1, client) 1019 assert.NoError(t, err) 1020 err = writeSFTPFile(path.Join(vdirPath2, dir1, testFileName), testFileSize, client) 1021 assert.NoError(t, err) 1022 err = writeSFTPFile(path.Join(vdirPath2, dir2, testFileName1), testFileSize1, client) 1023 assert.NoError(t, err) 1024 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1025 assert.NoError(t, err) 1026 assert.Equal(t, 2, user.UsedQuotaFiles) 1027 assert.Equal(t, testFileSize+testFileSize1, user.UsedQuotaSize) 1028 f, _, err := httpdtest.GetFolderByName(folderName1, http.StatusOK) 1029 assert.NoError(t, err) 1030 assert.Equal(t, testFileSize+testFileSize1, f.UsedQuotaSize) 1031 assert.Equal(t, 2, f.UsedQuotaFiles) 1032 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1033 assert.NoError(t, err) 1034 assert.Equal(t, testFileSize+testFileSize1, f.UsedQuotaSize) 1035 assert.Equal(t, 2, f.UsedQuotaFiles) 1036 // initial files: 1037 // - vdir1/dir1/testFileName 1038 // - vdir1/dir2/testFileName1 1039 // - vdir2/dir1/testFileName 1040 // - vdir2/dir2/testFileName1 1041 // 1042 // rename a file inside vdir1 it is included inside user quota, so we have: 1043 // - vdir1/dir1/testFileName.rename 1044 // - vdir1/dir2/testFileName1 1045 // - vdir2/dir1/testFileName 1046 // - vdir2/dir2/testFileName1 1047 err = client.Rename(path.Join(vdirPath1, dir1, testFileName), path.Join(vdirPath1, dir1, testFileName+".rename")) 1048 assert.NoError(t, err) 1049 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1050 assert.NoError(t, err) 1051 assert.Equal(t, 2, user.UsedQuotaFiles) 1052 assert.Equal(t, testFileSize+testFileSize1, user.UsedQuotaSize) 1053 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1054 assert.NoError(t, err) 1055 assert.Equal(t, testFileSize+testFileSize1, f.UsedQuotaSize) 1056 assert.Equal(t, 2, f.UsedQuotaFiles) 1057 // rename a file inside vdir2, it isn't included inside user quota, so we have: 1058 // - vdir1/dir1/testFileName.rename 1059 // - vdir1/dir2/testFileName1 1060 // - vdir2/dir1/testFileName.rename 1061 // - vdir2/dir2/testFileName1 1062 err = client.Rename(path.Join(vdirPath2, dir1, testFileName), path.Join(vdirPath2, dir1, testFileName+".rename")) 1063 assert.NoError(t, err) 1064 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1065 assert.NoError(t, err) 1066 assert.Equal(t, 2, user.UsedQuotaFiles) 1067 assert.Equal(t, testFileSize+testFileSize1, user.UsedQuotaSize) 1068 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1069 assert.NoError(t, err) 1070 assert.Equal(t, testFileSize+testFileSize1, f.UsedQuotaSize) 1071 assert.Equal(t, 2, f.UsedQuotaFiles) 1072 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1073 assert.NoError(t, err) 1074 assert.Equal(t, testFileSize+testFileSize1, f.UsedQuotaSize) 1075 assert.Equal(t, 2, f.UsedQuotaFiles) 1076 // rename a file inside vdir2 overwriting an existing, we now have: 1077 // - vdir1/dir1/testFileName.rename 1078 // - vdir1/dir2/testFileName1 1079 // - vdir2/dir1/testFileName.rename (initial testFileName1) 1080 err = client.Rename(path.Join(vdirPath2, dir2, testFileName1), path.Join(vdirPath2, dir1, testFileName+".rename")) 1081 assert.NoError(t, err) 1082 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1083 assert.NoError(t, err) 1084 assert.Equal(t, 2, user.UsedQuotaFiles) 1085 assert.Equal(t, testFileSize+testFileSize1, user.UsedQuotaSize) 1086 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1087 assert.NoError(t, err) 1088 assert.Equal(t, testFileSize1, f.UsedQuotaSize) 1089 assert.Equal(t, 1, f.UsedQuotaFiles) 1090 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1091 assert.NoError(t, err) 1092 assert.Equal(t, testFileSize+testFileSize1, f.UsedQuotaSize) 1093 assert.Equal(t, 2, f.UsedQuotaFiles) 1094 // rename a file inside vdir1 overwriting an existing, we now have: 1095 // - vdir1/dir1/testFileName.rename (initial testFileName1) 1096 // - vdir2/dir1/testFileName.rename (initial testFileName1) 1097 err = client.Rename(path.Join(vdirPath1, dir2, testFileName1), path.Join(vdirPath1, dir1, testFileName+".rename")) 1098 assert.NoError(t, err) 1099 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1100 assert.NoError(t, err) 1101 assert.Equal(t, 1, user.UsedQuotaFiles) 1102 assert.Equal(t, testFileSize1, user.UsedQuotaSize) 1103 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1104 assert.NoError(t, err) 1105 assert.Equal(t, testFileSize1, f.UsedQuotaSize) 1106 assert.Equal(t, 1, f.UsedQuotaFiles) 1107 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1108 assert.NoError(t, err) 1109 assert.Equal(t, testFileSize1, f.UsedQuotaSize) 1110 assert.Equal(t, 1, f.UsedQuotaFiles) 1111 // rename a directory inside the same virtual folder, quota should not change 1112 err = client.RemoveDirectory(path.Join(vdirPath1, dir2)) 1113 assert.NoError(t, err) 1114 err = client.RemoveDirectory(path.Join(vdirPath2, dir2)) 1115 assert.NoError(t, err) 1116 err = client.Rename(path.Join(vdirPath1, dir1), path.Join(vdirPath1, dir2)) 1117 assert.NoError(t, err) 1118 err = client.Rename(path.Join(vdirPath2, dir1), path.Join(vdirPath2, dir2)) 1119 assert.NoError(t, err) 1120 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1121 assert.NoError(t, err) 1122 assert.Equal(t, 1, user.UsedQuotaFiles) 1123 assert.Equal(t, testFileSize1, user.UsedQuotaSize) 1124 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1125 assert.NoError(t, err) 1126 assert.Equal(t, testFileSize1, f.UsedQuotaSize) 1127 assert.Equal(t, 1, f.UsedQuotaFiles) 1128 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1129 assert.NoError(t, err) 1130 assert.Equal(t, testFileSize1, f.UsedQuotaSize) 1131 assert.Equal(t, 1, f.UsedQuotaFiles) 1132 } 1133 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1134 assert.NoError(t, err) 1135 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName1}, http.StatusOK) 1136 assert.NoError(t, err) 1137 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName2}, http.StatusOK) 1138 assert.NoError(t, err) 1139 err = os.RemoveAll(user.GetHomeDir()) 1140 assert.NoError(t, err) 1141 err = os.RemoveAll(mappedPath1) 1142 assert.NoError(t, err) 1143 err = os.RemoveAll(mappedPath2) 1144 assert.NoError(t, err) 1145} 1146 1147func TestQuotaRenameBetweenVirtualFolder(t *testing.T) { 1148 u := getTestUser() 1149 u.QuotaFiles = 100 1150 mappedPath1 := filepath.Join(os.TempDir(), "vdir1") 1151 folderName1 := filepath.Base(mappedPath1) 1152 vdirPath1 := "/vdir1" 1153 mappedPath2 := filepath.Join(os.TempDir(), "vdir2") 1154 folderName2 := filepath.Base(mappedPath2) 1155 vdirPath2 := "/vdir2" 1156 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 1157 BaseVirtualFolder: vfs.BaseVirtualFolder{ 1158 Name: folderName1, 1159 MappedPath: mappedPath1, 1160 }, 1161 VirtualPath: vdirPath1, 1162 // quota is included in the user's one 1163 QuotaFiles: -1, 1164 QuotaSize: -1, 1165 }) 1166 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 1167 BaseVirtualFolder: vfs.BaseVirtualFolder{ 1168 Name: folderName2, 1169 MappedPath: mappedPath2, 1170 }, 1171 VirtualPath: vdirPath2, 1172 // quota is unlimited and excluded from user's one 1173 QuotaFiles: 0, 1174 QuotaSize: 0, 1175 }) 1176 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 1177 assert.NoError(t, err) 1178 conn, client, err := getSftpClient(user) 1179 if assert.NoError(t, err) { 1180 defer conn.Close() 1181 defer client.Close() 1182 testFileName1 := "test_file1.dat" 1183 testFileSize := int64(131072) 1184 testFileSize1 := int64(65535) 1185 dir1 := "dir1" 1186 dir2 := "dir2" 1187 err = client.Mkdir(path.Join(vdirPath1, dir1)) 1188 assert.NoError(t, err) 1189 err = client.Mkdir(path.Join(vdirPath1, dir2)) 1190 assert.NoError(t, err) 1191 err = client.Mkdir(path.Join(vdirPath2, dir1)) 1192 assert.NoError(t, err) 1193 err = client.Mkdir(path.Join(vdirPath2, dir2)) 1194 assert.NoError(t, err) 1195 err = writeSFTPFile(path.Join(vdirPath1, dir1, testFileName), testFileSize, client) 1196 assert.NoError(t, err) 1197 err = writeSFTPFile(path.Join(vdirPath1, dir2, testFileName1), testFileSize1, client) 1198 assert.NoError(t, err) 1199 err = writeSFTPFile(path.Join(vdirPath2, dir1, testFileName), testFileSize, client) 1200 assert.NoError(t, err) 1201 err = writeSFTPFile(path.Join(vdirPath2, dir2, testFileName1), testFileSize1, client) 1202 assert.NoError(t, err) 1203 // initial files: 1204 // - vdir1/dir1/testFileName 1205 // - vdir1/dir2/testFileName1 1206 // - vdir2/dir1/testFileName 1207 // - vdir2/dir2/testFileName1 1208 // 1209 // rename a file from vdir1 to vdir2, vdir1 is included inside user quota, so we have: 1210 // - vdir1/dir1/testFileName 1211 // - vdir2/dir1/testFileName 1212 // - vdir2/dir2/testFileName1 1213 // - vdir2/dir1/testFileName1.rename 1214 err = client.Rename(path.Join(vdirPath1, dir2, testFileName1), path.Join(vdirPath2, dir1, testFileName1+".rename")) 1215 assert.NoError(t, err) 1216 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1217 assert.NoError(t, err) 1218 assert.Equal(t, 1, user.UsedQuotaFiles) 1219 assert.Equal(t, testFileSize, user.UsedQuotaSize) 1220 f, _, err := httpdtest.GetFolderByName(folderName1, http.StatusOK) 1221 assert.NoError(t, err) 1222 assert.Equal(t, testFileSize, f.UsedQuotaSize) 1223 assert.Equal(t, 1, f.UsedQuotaFiles) 1224 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1225 assert.NoError(t, err) 1226 assert.Equal(t, testFileSize+testFileSize1+testFileSize1, f.UsedQuotaSize) 1227 assert.Equal(t, 3, f.UsedQuotaFiles) 1228 // rename a file from vdir2 to vdir1, vdir2 is not included inside user quota, so we have: 1229 // - vdir1/dir1/testFileName 1230 // - vdir1/dir2/testFileName.rename 1231 // - vdir2/dir2/testFileName1 1232 // - vdir2/dir1/testFileName1.rename 1233 err = client.Rename(path.Join(vdirPath2, dir1, testFileName), path.Join(vdirPath1, dir2, testFileName+".rename")) 1234 assert.NoError(t, err) 1235 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1236 assert.NoError(t, err) 1237 assert.Equal(t, 2, user.UsedQuotaFiles) 1238 assert.Equal(t, testFileSize*2, user.UsedQuotaSize) 1239 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1240 assert.NoError(t, err) 1241 assert.Equal(t, testFileSize*2, f.UsedQuotaSize) 1242 assert.Equal(t, 2, f.UsedQuotaFiles) 1243 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1244 assert.NoError(t, err) 1245 assert.Equal(t, testFileSize1*2, f.UsedQuotaSize) 1246 assert.Equal(t, 2, f.UsedQuotaFiles) 1247 // rename a file from vdir1 to vdir2 overwriting an existing file, vdir1 is included inside user quota, so we have: 1248 // - vdir1/dir2/testFileName.rename 1249 // - vdir2/dir2/testFileName1 (is the initial testFileName) 1250 // - vdir2/dir1/testFileName1.rename 1251 err = client.Rename(path.Join(vdirPath1, dir1, testFileName), path.Join(vdirPath2, dir2, testFileName1)) 1252 assert.NoError(t, err) 1253 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1254 assert.NoError(t, err) 1255 assert.Equal(t, 1, user.UsedQuotaFiles) 1256 assert.Equal(t, testFileSize, user.UsedQuotaSize) 1257 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1258 assert.NoError(t, err) 1259 assert.Equal(t, testFileSize, f.UsedQuotaSize) 1260 assert.Equal(t, 1, f.UsedQuotaFiles) 1261 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1262 assert.NoError(t, err) 1263 assert.Equal(t, testFileSize1+testFileSize, f.UsedQuotaSize) 1264 assert.Equal(t, 2, f.UsedQuotaFiles) 1265 // rename a file from vdir2 to vdir1 overwriting an existing file, vdir2 is not included inside user quota, so we have: 1266 // - vdir1/dir2/testFileName.rename (is the initial testFileName1) 1267 // - vdir2/dir2/testFileName1 (is the initial testFileName) 1268 err = client.Rename(path.Join(vdirPath2, dir1, testFileName1+".rename"), path.Join(vdirPath1, dir2, testFileName+".rename")) 1269 assert.NoError(t, err) 1270 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1271 assert.NoError(t, err) 1272 assert.Equal(t, 1, user.UsedQuotaFiles) 1273 assert.Equal(t, testFileSize1, user.UsedQuotaSize) 1274 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1275 assert.NoError(t, err) 1276 assert.Equal(t, testFileSize1, f.UsedQuotaSize) 1277 assert.Equal(t, 1, f.UsedQuotaFiles) 1278 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1279 assert.NoError(t, err) 1280 assert.Equal(t, testFileSize, f.UsedQuotaSize) 1281 assert.Equal(t, 1, f.UsedQuotaFiles) 1282 1283 err = writeSFTPFile(path.Join(vdirPath1, dir2, testFileName), testFileSize, client) 1284 assert.NoError(t, err) 1285 err = writeSFTPFile(path.Join(vdirPath2, dir2, testFileName), testFileSize1, client) 1286 assert.NoError(t, err) 1287 err = writeSFTPFile(path.Join(vdirPath2, dir2, testFileName+"1.dupl"), testFileSize1, client) 1288 assert.NoError(t, err) 1289 err = client.RemoveDirectory(path.Join(vdirPath1, dir1)) 1290 assert.NoError(t, err) 1291 err = client.RemoveDirectory(path.Join(vdirPath2, dir1)) 1292 assert.NoError(t, err) 1293 // - vdir1/dir2/testFileName.rename (initial testFileName1) 1294 // - vdir1/dir2/testFileName 1295 // - vdir2/dir2/testFileName1 (initial testFileName) 1296 // - vdir2/dir2/testFileName (initial testFileName1) 1297 // - vdir2/dir2/testFileName1.dupl 1298 // rename directories between the two virtual folders 1299 err = client.Rename(path.Join(vdirPath2, dir2), path.Join(vdirPath1, dir1)) 1300 assert.NoError(t, err) 1301 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1302 assert.NoError(t, err) 1303 assert.Equal(t, 5, user.UsedQuotaFiles) 1304 assert.Equal(t, testFileSize1*3+testFileSize*2, user.UsedQuotaSize) 1305 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1306 assert.NoError(t, err) 1307 assert.Equal(t, testFileSize1*3+testFileSize*2, f.UsedQuotaSize) 1308 assert.Equal(t, 5, f.UsedQuotaFiles) 1309 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1310 assert.NoError(t, err) 1311 assert.Equal(t, int64(0), f.UsedQuotaSize) 1312 assert.Equal(t, 0, f.UsedQuotaFiles) 1313 // now move on vpath2 1314 err = client.Rename(path.Join(vdirPath1, dir2), path.Join(vdirPath2, dir1)) 1315 assert.NoError(t, err) 1316 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1317 assert.NoError(t, err) 1318 assert.Equal(t, 3, user.UsedQuotaFiles) 1319 assert.Equal(t, testFileSize1*2+testFileSize, user.UsedQuotaSize) 1320 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1321 assert.NoError(t, err) 1322 assert.Equal(t, testFileSize1*2+testFileSize, f.UsedQuotaSize) 1323 assert.Equal(t, 3, f.UsedQuotaFiles) 1324 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1325 assert.NoError(t, err) 1326 assert.Equal(t, testFileSize+testFileSize1, f.UsedQuotaSize) 1327 assert.Equal(t, 2, f.UsedQuotaFiles) 1328 } 1329 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1330 assert.NoError(t, err) 1331 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName1}, http.StatusOK) 1332 assert.NoError(t, err) 1333 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName2}, http.StatusOK) 1334 assert.NoError(t, err) 1335 err = os.RemoveAll(user.GetHomeDir()) 1336 assert.NoError(t, err) 1337 err = os.RemoveAll(mappedPath1) 1338 assert.NoError(t, err) 1339 err = os.RemoveAll(mappedPath2) 1340 assert.NoError(t, err) 1341} 1342 1343func TestQuotaRenameFromVirtualFolder(t *testing.T) { 1344 u := getTestUser() 1345 u.QuotaFiles = 100 1346 mappedPath1 := filepath.Join(os.TempDir(), "vdir1") 1347 folderName1 := filepath.Base(mappedPath1) 1348 vdirPath1 := "/vdir1" 1349 mappedPath2 := filepath.Join(os.TempDir(), "vdir2") 1350 folderName2 := filepath.Base(mappedPath2) 1351 vdirPath2 := "/vdir2" 1352 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 1353 BaseVirtualFolder: vfs.BaseVirtualFolder{ 1354 Name: folderName1, 1355 MappedPath: mappedPath1, 1356 }, 1357 VirtualPath: vdirPath1, 1358 // quota is included in the user's one 1359 QuotaFiles: -1, 1360 QuotaSize: -1, 1361 }) 1362 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 1363 BaseVirtualFolder: vfs.BaseVirtualFolder{ 1364 Name: folderName2, 1365 MappedPath: mappedPath2, 1366 }, 1367 VirtualPath: vdirPath2, 1368 // quota is unlimited and excluded from user's one 1369 QuotaFiles: 0, 1370 QuotaSize: 0, 1371 }) 1372 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 1373 assert.NoError(t, err) 1374 conn, client, err := getSftpClient(user) 1375 if assert.NoError(t, err) { 1376 defer conn.Close() 1377 defer client.Close() 1378 testFileName1 := "test_file1.dat" 1379 testFileSize := int64(131072) 1380 testFileSize1 := int64(65535) 1381 dir1 := "dir1" 1382 dir2 := "dir2" 1383 err = client.Mkdir(path.Join(vdirPath1, dir1)) 1384 assert.NoError(t, err) 1385 err = client.Mkdir(path.Join(vdirPath1, dir2)) 1386 assert.NoError(t, err) 1387 err = client.Mkdir(path.Join(vdirPath2, dir1)) 1388 assert.NoError(t, err) 1389 err = client.Mkdir(path.Join(vdirPath2, dir2)) 1390 assert.NoError(t, err) 1391 err = writeSFTPFile(path.Join(vdirPath1, dir1, testFileName), testFileSize, client) 1392 assert.NoError(t, err) 1393 err = writeSFTPFile(path.Join(vdirPath1, dir2, testFileName1), testFileSize1, client) 1394 assert.NoError(t, err) 1395 err = writeSFTPFile(path.Join(vdirPath2, dir1, testFileName), testFileSize, client) 1396 assert.NoError(t, err) 1397 err = writeSFTPFile(path.Join(vdirPath2, dir2, testFileName1), testFileSize1, client) 1398 assert.NoError(t, err) 1399 // initial files: 1400 // - vdir1/dir1/testFileName 1401 // - vdir1/dir2/testFileName1 1402 // - vdir2/dir1/testFileName 1403 // - vdir2/dir2/testFileName1 1404 // 1405 // rename a file from vdir1 to the user home dir, vdir1 is included in user quota so we have: 1406 // - testFileName 1407 // - vdir1/dir2/testFileName1 1408 // - vdir2/dir1/testFileName 1409 // - vdir2/dir2/testFileName1 1410 err = client.Rename(path.Join(vdirPath1, dir1, testFileName), path.Join(testFileName)) 1411 assert.NoError(t, err) 1412 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1413 assert.NoError(t, err) 1414 assert.Equal(t, 2, user.UsedQuotaFiles) 1415 assert.Equal(t, testFileSize+testFileSize1, user.UsedQuotaSize) 1416 f, _, err := httpdtest.GetFolderByName(folderName1, http.StatusOK) 1417 assert.NoError(t, err) 1418 assert.Equal(t, testFileSize1, f.UsedQuotaSize) 1419 assert.Equal(t, 1, f.UsedQuotaFiles) 1420 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1421 assert.NoError(t, err) 1422 assert.Equal(t, testFileSize+testFileSize1, f.UsedQuotaSize) 1423 assert.Equal(t, 2, f.UsedQuotaFiles) 1424 // rename a file from vdir2 to the user home dir, vdir2 is not included in user quota so we have: 1425 // - testFileName 1426 // - testFileName1 1427 // - vdir1/dir2/testFileName1 1428 // - vdir2/dir1/testFileName 1429 err = client.Rename(path.Join(vdirPath2, dir2, testFileName1), path.Join(testFileName1)) 1430 assert.NoError(t, err) 1431 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1432 assert.NoError(t, err) 1433 assert.Equal(t, 3, user.UsedQuotaFiles) 1434 assert.Equal(t, testFileSize+testFileSize1+testFileSize1, user.UsedQuotaSize) 1435 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1436 assert.NoError(t, err) 1437 assert.Equal(t, testFileSize1, f.UsedQuotaSize) 1438 assert.Equal(t, 1, f.UsedQuotaFiles) 1439 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1440 assert.NoError(t, err) 1441 assert.Equal(t, testFileSize, f.UsedQuotaSize) 1442 assert.Equal(t, 1, f.UsedQuotaFiles) 1443 // rename a file from vdir1 to the user home dir overwriting an existing file, vdir1 is included in user quota so we have: 1444 // - testFileName (initial testFileName1) 1445 // - testFileName1 1446 // - vdir2/dir1/testFileName 1447 err = client.Rename(path.Join(vdirPath1, dir2, testFileName1), path.Join(testFileName)) 1448 assert.NoError(t, err) 1449 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1450 assert.NoError(t, err) 1451 assert.Equal(t, 2, user.UsedQuotaFiles) 1452 assert.Equal(t, testFileSize1+testFileSize1, user.UsedQuotaSize) 1453 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1454 assert.NoError(t, err) 1455 assert.Equal(t, int64(0), f.UsedQuotaSize) 1456 assert.Equal(t, 0, f.UsedQuotaFiles) 1457 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1458 assert.NoError(t, err) 1459 assert.Equal(t, testFileSize, f.UsedQuotaSize) 1460 assert.Equal(t, 1, f.UsedQuotaFiles) 1461 // rename a file from vdir2 to the user home dir overwriting an existing file, vdir2 is not included in user quota so we have: 1462 // - testFileName (initial testFileName1) 1463 // - testFileName1 (initial testFileName) 1464 err = client.Rename(path.Join(vdirPath2, dir1, testFileName), path.Join(testFileName1)) 1465 assert.NoError(t, err) 1466 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1467 assert.NoError(t, err) 1468 assert.Equal(t, 2, user.UsedQuotaFiles) 1469 assert.Equal(t, testFileSize+testFileSize1, user.UsedQuotaSize) 1470 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1471 assert.NoError(t, err) 1472 assert.Equal(t, int64(0), f.UsedQuotaSize) 1473 assert.Equal(t, 0, f.UsedQuotaFiles) 1474 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1475 assert.NoError(t, err) 1476 assert.Equal(t, int64(0), f.UsedQuotaSize) 1477 assert.Equal(t, 0, f.UsedQuotaFiles) 1478 // dir rename 1479 err = writeSFTPFile(path.Join(vdirPath1, dir1, testFileName), testFileSize, client) 1480 assert.NoError(t, err) 1481 err = writeSFTPFile(path.Join(vdirPath1, dir1, testFileName1), testFileSize1, client) 1482 assert.NoError(t, err) 1483 err = writeSFTPFile(path.Join(vdirPath2, dir1, testFileName), testFileSize, client) 1484 assert.NoError(t, err) 1485 err = writeSFTPFile(path.Join(vdirPath2, dir1, testFileName1), testFileSize1, client) 1486 assert.NoError(t, err) 1487 // - testFileName (initial testFileName1) 1488 // - testFileName1 (initial testFileName) 1489 // - vdir1/dir1/testFileName 1490 // - vdir1/dir1/testFileName1 1491 // - dir1/testFileName 1492 // - dir1/testFileName1 1493 err = client.Rename(path.Join(vdirPath2, dir1), dir1) 1494 assert.NoError(t, err) 1495 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1496 assert.NoError(t, err) 1497 assert.Equal(t, 6, user.UsedQuotaFiles) 1498 assert.Equal(t, testFileSize*3+testFileSize1*3, user.UsedQuotaSize) 1499 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1500 assert.NoError(t, err) 1501 assert.Equal(t, testFileSize+testFileSize1, f.UsedQuotaSize) 1502 assert.Equal(t, 2, f.UsedQuotaFiles) 1503 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1504 assert.NoError(t, err) 1505 assert.Equal(t, int64(0), f.UsedQuotaSize) 1506 assert.Equal(t, 0, f.UsedQuotaFiles) 1507 // - testFileName (initial testFileName1) 1508 // - testFileName1 (initial testFileName) 1509 // - dir2/testFileName 1510 // - dir2/testFileName1 1511 // - dir1/testFileName 1512 // - dir1/testFileName1 1513 err = client.Rename(path.Join(vdirPath1, dir1), dir2) 1514 assert.NoError(t, err) 1515 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1516 assert.NoError(t, err) 1517 assert.Equal(t, 6, user.UsedQuotaFiles) 1518 assert.Equal(t, testFileSize*3+testFileSize1*3, user.UsedQuotaSize) 1519 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1520 assert.NoError(t, err) 1521 assert.Equal(t, int64(0), f.UsedQuotaSize) 1522 assert.Equal(t, 0, f.UsedQuotaFiles) 1523 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1524 assert.NoError(t, err) 1525 assert.Equal(t, int64(0), f.UsedQuotaSize) 1526 assert.Equal(t, 0, f.UsedQuotaFiles) 1527 } 1528 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1529 assert.NoError(t, err) 1530 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName1}, http.StatusOK) 1531 assert.NoError(t, err) 1532 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName2}, http.StatusOK) 1533 assert.NoError(t, err) 1534 err = os.RemoveAll(user.GetHomeDir()) 1535 assert.NoError(t, err) 1536 err = os.RemoveAll(mappedPath1) 1537 assert.NoError(t, err) 1538 err = os.RemoveAll(mappedPath2) 1539 assert.NoError(t, err) 1540} 1541 1542func TestQuotaRenameToVirtualFolder(t *testing.T) { 1543 u := getTestUser() 1544 u.QuotaFiles = 100 1545 mappedPath1 := filepath.Join(os.TempDir(), "vdir1") 1546 folderName1 := filepath.Base(mappedPath1) 1547 vdirPath1 := "/vdir1" 1548 mappedPath2 := filepath.Join(os.TempDir(), "vdir2") 1549 folderName2 := filepath.Base(mappedPath2) 1550 vdirPath2 := "/vdir2" 1551 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 1552 BaseVirtualFolder: vfs.BaseVirtualFolder{ 1553 Name: folderName1, 1554 MappedPath: mappedPath1, 1555 }, 1556 VirtualPath: vdirPath1, 1557 // quota is included in the user's one 1558 QuotaFiles: -1, 1559 QuotaSize: -1, 1560 }) 1561 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 1562 BaseVirtualFolder: vfs.BaseVirtualFolder{ 1563 Name: folderName2, 1564 MappedPath: mappedPath2, 1565 }, 1566 VirtualPath: vdirPath2, 1567 // quota is unlimited and excluded from user's one 1568 QuotaFiles: 0, 1569 QuotaSize: 0, 1570 }) 1571 u.Permissions[vdirPath1] = []string{dataprovider.PermListItems, dataprovider.PermDownload, dataprovider.PermUpload, 1572 dataprovider.PermOverwrite, dataprovider.PermDelete, dataprovider.PermCreateSymlinks, dataprovider.PermCreateDirs} 1573 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 1574 assert.NoError(t, err) 1575 conn, client, err := getSftpClient(user) 1576 if assert.NoError(t, err) { 1577 defer conn.Close() 1578 defer client.Close() 1579 testFileName1 := "test_file1.dat" 1580 testFileSize := int64(131072) 1581 testFileSize1 := int64(65535) 1582 dir1 := "dir1" 1583 dir2 := "dir2" 1584 err = client.Mkdir(path.Join(vdirPath1, dir1)) 1585 assert.NoError(t, err) 1586 err = client.Mkdir(path.Join(vdirPath1, dir2)) 1587 assert.NoError(t, err) 1588 err = client.Mkdir(path.Join(vdirPath2, dir1)) 1589 assert.NoError(t, err) 1590 err = client.Mkdir(path.Join(vdirPath2, dir2)) 1591 assert.NoError(t, err) 1592 err = writeSFTPFile(testFileName, testFileSize, client) 1593 assert.NoError(t, err) 1594 err = writeSFTPFile(testFileName1, testFileSize1, client) 1595 assert.NoError(t, err) 1596 // initial files: 1597 // - testFileName 1598 // - testFileName1 1599 // 1600 // rename a file from user home dir to vdir1, vdir1 is included in user quota so we have: 1601 // - testFileName 1602 // - /vdir1/dir1/testFileName1 1603 err = client.Rename(testFileName1, path.Join(vdirPath1, dir1, testFileName1)) 1604 assert.NoError(t, err) 1605 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1606 assert.NoError(t, err) 1607 assert.Equal(t, 2, user.UsedQuotaFiles) 1608 assert.Equal(t, testFileSize+testFileSize1, user.UsedQuotaSize) 1609 f, _, err := httpdtest.GetFolderByName(folderName1, http.StatusOK) 1610 assert.NoError(t, err) 1611 assert.Equal(t, testFileSize1, f.UsedQuotaSize) 1612 assert.Equal(t, 1, f.UsedQuotaFiles) 1613 // rename a file from user home dir to vdir2, vdir2 is not included in user quota so we have: 1614 // - /vdir2/dir1/testFileName 1615 // - /vdir1/dir1/testFileName1 1616 err = client.Rename(testFileName, path.Join(vdirPath2, dir1, testFileName)) 1617 assert.NoError(t, err) 1618 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1619 assert.NoError(t, err) 1620 assert.Equal(t, 1, user.UsedQuotaFiles) 1621 assert.Equal(t, testFileSize1, user.UsedQuotaSize) 1622 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1623 assert.NoError(t, err) 1624 assert.Equal(t, testFileSize, f.UsedQuotaSize) 1625 assert.Equal(t, 1, f.UsedQuotaFiles) 1626 // upload two new files to the user home dir so we have: 1627 // - testFileName 1628 // - testFileName1 1629 // - /vdir1/dir1/testFileName1 1630 // - /vdir2/dir1/testFileName 1631 err = writeSFTPFile(testFileName, testFileSize, client) 1632 assert.NoError(t, err) 1633 err = writeSFTPFile(testFileName1, testFileSize1, client) 1634 assert.NoError(t, err) 1635 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1636 assert.NoError(t, err) 1637 assert.Equal(t, 3, user.UsedQuotaFiles) 1638 assert.Equal(t, testFileSize+testFileSize1+testFileSize1, user.UsedQuotaSize) 1639 // rename a file from user home dir to vdir1 overwriting an existing file, vdir1 is included in user quota so we have: 1640 // - testFileName1 1641 // - /vdir1/dir1/testFileName1 (initial testFileName) 1642 // - /vdir2/dir1/testFileName 1643 err = client.Rename(testFileName, path.Join(vdirPath1, dir1, testFileName1)) 1644 assert.NoError(t, err) 1645 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1646 assert.NoError(t, err) 1647 assert.Equal(t, 2, user.UsedQuotaFiles) 1648 assert.Equal(t, testFileSize+testFileSize1, user.UsedQuotaSize) 1649 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1650 assert.NoError(t, err) 1651 assert.Equal(t, testFileSize, f.UsedQuotaSize) 1652 assert.Equal(t, 1, f.UsedQuotaFiles) 1653 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1654 assert.NoError(t, err) 1655 assert.Equal(t, testFileSize, f.UsedQuotaSize) 1656 assert.Equal(t, 1, f.UsedQuotaFiles) 1657 // rename a file from user home dir to vdir2 overwriting an existing file, vdir2 is not included in user quota so we have: 1658 // - /vdir1/dir1/testFileName1 (initial testFileName) 1659 // - /vdir2/dir1/testFileName (initial testFileName1) 1660 err = client.Rename(testFileName1, path.Join(vdirPath2, dir1, testFileName)) 1661 assert.NoError(t, err) 1662 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1663 assert.NoError(t, err) 1664 assert.Equal(t, 1, user.UsedQuotaFiles) 1665 assert.Equal(t, testFileSize, user.UsedQuotaSize) 1666 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1667 assert.NoError(t, err) 1668 assert.Equal(t, testFileSize, f.UsedQuotaSize) 1669 assert.Equal(t, 1, f.UsedQuotaFiles) 1670 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1671 assert.NoError(t, err) 1672 assert.Equal(t, testFileSize1, f.UsedQuotaSize) 1673 assert.Equal(t, 1, f.UsedQuotaFiles) 1674 1675 err = client.Mkdir(dir1) 1676 assert.NoError(t, err) 1677 err = writeSFTPFile(path.Join(dir1, testFileName), testFileSize, client) 1678 assert.NoError(t, err) 1679 err = writeSFTPFile(path.Join(dir1, testFileName1), testFileSize1, client) 1680 assert.NoError(t, err) 1681 // - /dir1/testFileName 1682 // - /dir1/testFileName1 1683 // - /vdir1/dir1/testFileName1 (initial testFileName) 1684 // - /vdir2/dir1/testFileName (initial testFileName1) 1685 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1686 assert.NoError(t, err) 1687 assert.Equal(t, 3, user.UsedQuotaFiles) 1688 assert.Equal(t, testFileSize*2+testFileSize1, user.UsedQuotaSize) 1689 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1690 assert.NoError(t, err) 1691 assert.Equal(t, testFileSize, f.UsedQuotaSize) 1692 assert.Equal(t, 1, f.UsedQuotaFiles) 1693 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1694 assert.NoError(t, err) 1695 assert.Equal(t, testFileSize1, f.UsedQuotaSize) 1696 assert.Equal(t, 1, f.UsedQuotaFiles) 1697 // - /vdir1/adir/testFileName 1698 // - /vdir1/adir/testFileName1 1699 // - /vdir1/dir1/testFileName1 (initial testFileName) 1700 // - /vdir2/dir1/testFileName (initial testFileName1) 1701 err = client.Rename(dir1, path.Join(vdirPath1, "adir")) 1702 assert.NoError(t, err) 1703 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1704 assert.NoError(t, err) 1705 assert.Equal(t, 3, user.UsedQuotaFiles) 1706 assert.Equal(t, testFileSize*2+testFileSize1, user.UsedQuotaSize) 1707 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1708 assert.NoError(t, err) 1709 assert.Equal(t, testFileSize*2+testFileSize1, f.UsedQuotaSize) 1710 assert.Equal(t, 3, f.UsedQuotaFiles) 1711 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1712 assert.NoError(t, err) 1713 assert.Equal(t, testFileSize1, f.UsedQuotaSize) 1714 assert.Equal(t, 1, f.UsedQuotaFiles) 1715 err = client.Mkdir(dir1) 1716 assert.NoError(t, err) 1717 err = writeSFTPFile(path.Join(dir1, testFileName), testFileSize, client) 1718 assert.NoError(t, err) 1719 err = writeSFTPFile(path.Join(dir1, testFileName1), testFileSize1, client) 1720 assert.NoError(t, err) 1721 // - /vdir1/adir/testFileName 1722 // - /vdir1/adir/testFileName1 1723 // - /vdir1/dir1/testFileName1 (initial testFileName) 1724 // - /vdir2/dir1/testFileName (initial testFileName1) 1725 // - /vdir2/adir/testFileName 1726 // - /vdir2/adir/testFileName1 1727 err = client.Rename(dir1, path.Join(vdirPath2, "adir")) 1728 assert.NoError(t, err) 1729 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 1730 assert.NoError(t, err) 1731 assert.Equal(t, 3, user.UsedQuotaFiles) 1732 assert.Equal(t, testFileSize*2+testFileSize1, user.UsedQuotaSize) 1733 f, _, err = httpdtest.GetFolderByName(folderName1, http.StatusOK) 1734 assert.NoError(t, err) 1735 assert.Equal(t, testFileSize*2+testFileSize1, f.UsedQuotaSize) 1736 assert.Equal(t, 3, f.UsedQuotaFiles) 1737 f, _, err = httpdtest.GetFolderByName(folderName2, http.StatusOK) 1738 assert.NoError(t, err) 1739 assert.Equal(t, testFileSize1*2+testFileSize, f.UsedQuotaSize) 1740 assert.Equal(t, 3, f.UsedQuotaFiles) 1741 } 1742 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1743 assert.NoError(t, err) 1744 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName1}, http.StatusOK) 1745 assert.NoError(t, err) 1746 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName2}, http.StatusOK) 1747 assert.NoError(t, err) 1748 err = os.RemoveAll(user.GetHomeDir()) 1749 assert.NoError(t, err) 1750 err = os.RemoveAll(mappedPath1) 1751 assert.NoError(t, err) 1752 err = os.RemoveAll(mappedPath2) 1753 assert.NoError(t, err) 1754} 1755 1756func TestVirtualFoldersLink(t *testing.T) { 1757 u := getTestUser() 1758 mappedPath1 := filepath.Join(os.TempDir(), "vdir1") 1759 folderName1 := filepath.Base(mappedPath1) 1760 vdirPath1 := "/vdir1" 1761 mappedPath2 := filepath.Join(os.TempDir(), "vdir2") 1762 folderName2 := filepath.Base(mappedPath2) 1763 vdirPath2 := "/vdir2" 1764 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 1765 BaseVirtualFolder: vfs.BaseVirtualFolder{ 1766 Name: folderName1, 1767 MappedPath: mappedPath1, 1768 }, 1769 VirtualPath: vdirPath1, 1770 // quota is included in the user's one 1771 QuotaFiles: -1, 1772 QuotaSize: -1, 1773 }) 1774 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 1775 BaseVirtualFolder: vfs.BaseVirtualFolder{ 1776 Name: folderName2, 1777 MappedPath: mappedPath2, 1778 }, 1779 VirtualPath: vdirPath2, 1780 // quota is unlimited and excluded from user's one 1781 QuotaFiles: 0, 1782 QuotaSize: 0, 1783 }) 1784 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 1785 assert.NoError(t, err) 1786 conn, client, err := getSftpClient(user) 1787 if assert.NoError(t, err) { 1788 defer conn.Close() 1789 defer client.Close() 1790 testFileSize := int64(131072) 1791 testDir := "adir" 1792 err = writeSFTPFile(testFileName, testFileSize, client) 1793 assert.NoError(t, err) 1794 err = writeSFTPFile(path.Join(vdirPath1, testFileName), testFileSize, client) 1795 assert.NoError(t, err) 1796 err = writeSFTPFile(path.Join(vdirPath2, testFileName), testFileSize, client) 1797 assert.NoError(t, err) 1798 err = client.Mkdir(path.Join(vdirPath1, testDir)) 1799 assert.NoError(t, err) 1800 err = client.Mkdir(path.Join(vdirPath2, testDir)) 1801 assert.NoError(t, err) 1802 err = client.Symlink(testFileName, testFileName+".link") 1803 assert.NoError(t, err) 1804 err = client.Symlink(path.Join(vdirPath1, testFileName), path.Join(vdirPath1, testFileName+".link")) 1805 assert.NoError(t, err) 1806 err = client.Symlink(path.Join(vdirPath1, testFileName), path.Join(vdirPath1, testDir, testFileName+".link")) 1807 assert.NoError(t, err) 1808 err = client.Symlink(path.Join(vdirPath2, testFileName), path.Join(vdirPath2, testFileName+".link")) 1809 assert.NoError(t, err) 1810 err = client.Symlink(path.Join(vdirPath2, testFileName), path.Join(vdirPath2, testDir, testFileName+".link")) 1811 assert.NoError(t, err) 1812 err = client.Symlink(testFileName, path.Join(vdirPath1, testFileName+".link1")) 1813 if assert.Error(t, err) { 1814 assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") 1815 } 1816 err = client.Symlink(testFileName, path.Join(vdirPath1, testDir, testFileName+".link1")) 1817 if assert.Error(t, err) { 1818 assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") 1819 } 1820 err = client.Symlink(testFileName, path.Join(vdirPath2, testFileName+".link1")) 1821 if assert.Error(t, err) { 1822 assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") 1823 } 1824 err = client.Symlink(testFileName, path.Join(vdirPath2, testDir, testFileName+".link1")) 1825 if assert.Error(t, err) { 1826 assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") 1827 } 1828 err = client.Symlink(path.Join(vdirPath1, testFileName), testFileName+".link1") 1829 if assert.Error(t, err) { 1830 assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") 1831 } 1832 err = client.Symlink(path.Join(vdirPath2, testFileName), testFileName+".link1") 1833 if assert.Error(t, err) { 1834 assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") 1835 } 1836 err = client.Symlink(path.Join(vdirPath1, testFileName), path.Join(vdirPath2, testDir, testFileName+".link1")) 1837 if assert.Error(t, err) { 1838 assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") 1839 } 1840 err = client.Symlink(path.Join(vdirPath2, testFileName), path.Join(vdirPath1, testFileName+".link1")) 1841 if assert.Error(t, err) { 1842 assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") 1843 } 1844 err = client.Symlink("/", "/roolink") 1845 assert.ErrorIs(t, err, os.ErrPermission) 1846 err = client.Symlink(testFileName, "/") 1847 assert.ErrorIs(t, err, os.ErrPermission) 1848 err = client.Symlink(testFileName, vdirPath1) 1849 if assert.Error(t, err) { 1850 assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") 1851 } 1852 err = client.Symlink(vdirPath1, testFileName+".link2") 1853 if assert.Error(t, err) { 1854 assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") 1855 } 1856 } 1857 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1858 assert.NoError(t, err) 1859 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName1}, http.StatusOK) 1860 assert.NoError(t, err) 1861 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName2}, http.StatusOK) 1862 assert.NoError(t, err) 1863 err = os.RemoveAll(user.GetHomeDir()) 1864 assert.NoError(t, err) 1865 err = os.RemoveAll(mappedPath1) 1866 assert.NoError(t, err) 1867 err = os.RemoveAll(mappedPath2) 1868 assert.NoError(t, err) 1869} 1870 1871func TestDirs(t *testing.T) { 1872 u := getTestUser() 1873 mappedPath := filepath.Join(os.TempDir(), "vdir") 1874 folderName := filepath.Base(mappedPath) 1875 vdirPath := "/path/vdir" 1876 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 1877 BaseVirtualFolder: vfs.BaseVirtualFolder{ 1878 Name: folderName, 1879 MappedPath: mappedPath, 1880 }, 1881 VirtualPath: vdirPath, 1882 }) 1883 u.Permissions["/subdir"] = []string{dataprovider.PermDownload, dataprovider.PermUpload, 1884 dataprovider.PermDelete, dataprovider.PermCreateDirs, dataprovider.PermRename, dataprovider.PermListItems} 1885 1886 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 1887 assert.NoError(t, err) 1888 conn, client, err := getSftpClient(user) 1889 if assert.NoError(t, err) { 1890 defer conn.Close() 1891 defer client.Close() 1892 info, err := client.ReadDir("/") 1893 if assert.NoError(t, err) { 1894 assert.Len(t, info, 1) 1895 assert.Equal(t, "path", info[0].Name()) 1896 } 1897 fi, err := client.Stat(path.Dir(vdirPath)) 1898 if assert.NoError(t, err) { 1899 assert.True(t, fi.IsDir()) 1900 } 1901 err = client.RemoveDirectory("/") 1902 assert.ErrorIs(t, err, os.ErrPermission) 1903 err = client.RemoveDirectory(vdirPath) 1904 assert.ErrorIs(t, err, os.ErrPermission) 1905 err = client.RemoveDirectory(path.Dir(vdirPath)) 1906 if assert.Error(t, err) { 1907 assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") 1908 } 1909 err = client.Mkdir(vdirPath) 1910 assert.ErrorIs(t, err, os.ErrPermission) 1911 err = client.Mkdir("adir") 1912 assert.NoError(t, err) 1913 err = client.Rename("/adir", path.Dir(vdirPath)) 1914 if assert.Error(t, err) { 1915 assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") 1916 } 1917 err = client.MkdirAll("/subdir/adir") 1918 assert.NoError(t, err) 1919 err = client.Rename("adir", "subdir/adir") 1920 if assert.Error(t, err) { 1921 assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") 1922 } 1923 err = writeSFTPFile("/subdir/afile.bin", 64, client) 1924 assert.NoError(t, err) 1925 err = writeSFTPFile("/afile.bin", 32, client) 1926 assert.NoError(t, err) 1927 err = client.Rename("afile.bin", "subdir/afile.bin") 1928 assert.ErrorIs(t, err, os.ErrPermission) 1929 err = client.Rename("afile.bin", "subdir/afile1.bin") 1930 assert.NoError(t, err) 1931 err = client.Rename(path.Dir(vdirPath), "renamed_vdir") 1932 if assert.Error(t, err) { 1933 assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") 1934 } 1935 } 1936 1937 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1938 assert.NoError(t, err) 1939 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName}, http.StatusOK) 1940 assert.NoError(t, err) 1941 err = os.RemoveAll(user.GetHomeDir()) 1942 assert.NoError(t, err) 1943 err = os.RemoveAll(mappedPath) 1944 assert.NoError(t, err) 1945} 1946 1947func TestCryptFsStat(t *testing.T) { 1948 user, _, err := httpdtest.AddUser(getCryptFsUser(), http.StatusCreated) 1949 assert.NoError(t, err) 1950 conn, client, err := getSftpClient(user) 1951 if assert.NoError(t, err) { 1952 defer conn.Close() 1953 defer client.Close() 1954 testFileSize := int64(4096) 1955 err = writeSFTPFile(testFileName, testFileSize, client) 1956 assert.NoError(t, err) 1957 info, err := client.Stat(testFileName) 1958 if assert.NoError(t, err) { 1959 assert.Equal(t, testFileSize, info.Size()) 1960 } 1961 info, err = os.Stat(filepath.Join(user.HomeDir, testFileName)) 1962 if assert.NoError(t, err) { 1963 assert.Greater(t, info.Size(), testFileSize) 1964 } 1965 } 1966 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1967 assert.NoError(t, err) 1968 err = os.RemoveAll(user.GetHomeDir()) 1969 assert.NoError(t, err) 1970} 1971 1972func TestFsPermissionErrors(t *testing.T) { 1973 if runtime.GOOS == osWindows { 1974 t.Skip("this test is not available on Windows") 1975 } 1976 user, _, err := httpdtest.AddUser(getCryptFsUser(), http.StatusCreated) 1977 assert.NoError(t, err) 1978 conn, client, err := getSftpClient(user) 1979 if assert.NoError(t, err) { 1980 defer conn.Close() 1981 defer client.Close() 1982 testDir := "tDir" 1983 err = client.Mkdir(testDir) 1984 assert.NoError(t, err) 1985 err = os.Chmod(user.GetHomeDir(), 0111) 1986 assert.NoError(t, err) 1987 1988 err = client.RemoveDirectory(testDir) 1989 assert.ErrorIs(t, err, os.ErrPermission) 1990 err = client.Rename(testDir, testDir+"1") 1991 assert.ErrorIs(t, err, os.ErrPermission) 1992 1993 err = os.Chmod(user.GetHomeDir(), os.ModePerm) 1994 assert.NoError(t, err) 1995 } 1996 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1997 assert.NoError(t, err) 1998 err = os.RemoveAll(user.GetHomeDir()) 1999 assert.NoError(t, err) 2000} 2001 2002func TestResolvePathError(t *testing.T) { 2003 u := getTestUser() 2004 u.HomeDir = "relative_path" 2005 conn := common.NewBaseConnection("", common.ProtocolFTP, "", "", u) 2006 testPath := "apath" 2007 _, err := conn.ListDir(testPath) 2008 assert.Error(t, err) 2009 err = conn.CreateDir(testPath) 2010 assert.Error(t, err) 2011 err = conn.RemoveDir(testPath) 2012 assert.Error(t, err) 2013 err = conn.Rename(testPath, testPath+"1") 2014 assert.Error(t, err) 2015 err = conn.CreateSymlink(testPath, testPath+".sym") 2016 assert.Error(t, err) 2017 _, err = conn.DoStat(testPath, 0) 2018 assert.Error(t, err) 2019 err = conn.SetStat(testPath, &common.StatAttributes{ 2020 Atime: time.Now(), 2021 Mtime: time.Now(), 2022 }) 2023 assert.Error(t, err) 2024 2025 u = getTestUser() 2026 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 2027 BaseVirtualFolder: vfs.BaseVirtualFolder{ 2028 MappedPath: "relative_mapped_path", 2029 }, 2030 VirtualPath: "/vpath", 2031 }) 2032 err = os.MkdirAll(u.HomeDir, os.ModePerm) 2033 assert.NoError(t, err) 2034 conn.User = u 2035 err = conn.Rename(testPath, "/vpath/subpath") 2036 assert.Error(t, err) 2037 2038 outHomePath := filepath.Join(os.TempDir(), testFileName) 2039 err = os.WriteFile(outHomePath, testFileContent, os.ModePerm) 2040 assert.NoError(t, err) 2041 err = os.Symlink(outHomePath, filepath.Join(u.HomeDir, testFileName+".link")) 2042 assert.NoError(t, err) 2043 err = os.WriteFile(filepath.Join(u.HomeDir, testFileName), testFileContent, os.ModePerm) 2044 assert.NoError(t, err) 2045 err = conn.CreateSymlink(testFileName, testFileName+".link") 2046 assert.Error(t, err) 2047 2048 err = os.RemoveAll(u.GetHomeDir()) 2049 assert.NoError(t, err) 2050 err = os.Remove(outHomePath) 2051 assert.NoError(t, err) 2052} 2053 2054func TestUserPasswordHashing(t *testing.T) { 2055 if config.GetProviderConf().Driver == dataprovider.MemoryDataProviderName { 2056 t.Skip("this test is not supported with the memory provider") 2057 } 2058 u := getTestUser() 2059 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 2060 assert.NoError(t, err) 2061 2062 err = dataprovider.Close() 2063 assert.NoError(t, err) 2064 err = config.LoadConfig(configDir, "") 2065 assert.NoError(t, err) 2066 providerConf := config.GetProviderConf() 2067 providerConf.PasswordHashing.Algo = dataprovider.HashingAlgoArgon2ID 2068 err = dataprovider.Initialize(providerConf, configDir, true) 2069 assert.NoError(t, err) 2070 2071 currentUser, err := dataprovider.UserExists(user.Username) 2072 assert.NoError(t, err) 2073 assert.True(t, strings.HasPrefix(currentUser.Password, "$2a$")) 2074 2075 conn, client, err := getSftpClient(user) 2076 if assert.NoError(t, err) { 2077 defer conn.Close() 2078 defer client.Close() 2079 err = checkBasicSFTP(client) 2080 assert.NoError(t, err) 2081 } 2082 2083 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2084 assert.NoError(t, err) 2085 err = os.RemoveAll(user.GetHomeDir()) 2086 assert.NoError(t, err) 2087 2088 u = getTestUser() 2089 user, _, err = httpdtest.AddUser(u, http.StatusCreated) 2090 assert.NoError(t, err) 2091 2092 currentUser, err = dataprovider.UserExists(user.Username) 2093 assert.NoError(t, err) 2094 assert.True(t, strings.HasPrefix(currentUser.Password, "$argon2id$")) 2095 2096 conn, client, err = getSftpClient(user) 2097 if assert.NoError(t, err) { 2098 defer conn.Close() 2099 defer client.Close() 2100 err = checkBasicSFTP(client) 2101 assert.NoError(t, err) 2102 } 2103 2104 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2105 assert.NoError(t, err) 2106 err = os.RemoveAll(user.GetHomeDir()) 2107 assert.NoError(t, err) 2108 2109 err = dataprovider.Close() 2110 assert.NoError(t, err) 2111 err = config.LoadConfig(configDir, "") 2112 assert.NoError(t, err) 2113 providerConf = config.GetProviderConf() 2114 err = dataprovider.Initialize(providerConf, configDir, true) 2115 assert.NoError(t, err) 2116} 2117 2118func TestDelayedQuotaUpdater(t *testing.T) { 2119 err := dataprovider.Close() 2120 assert.NoError(t, err) 2121 err = config.LoadConfig(configDir, "") 2122 assert.NoError(t, err) 2123 providerConf := config.GetProviderConf() 2124 providerConf.DelayedQuotaUpdate = 120 2125 err = dataprovider.Initialize(providerConf, configDir, true) 2126 assert.NoError(t, err) 2127 2128 u := getTestUser() 2129 u.QuotaFiles = 100 2130 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 2131 assert.NoError(t, err) 2132 2133 err = dataprovider.UpdateUserQuota(&user, 10, 6000, false) 2134 assert.NoError(t, err) 2135 files, size, err := dataprovider.GetUsedQuota(user.Username) 2136 assert.NoError(t, err) 2137 assert.Equal(t, 10, files) 2138 assert.Equal(t, int64(6000), size) 2139 2140 userGet, err := dataprovider.UserExists(user.Username) 2141 assert.NoError(t, err) 2142 assert.Equal(t, 0, userGet.UsedQuotaFiles) 2143 assert.Equal(t, int64(0), userGet.UsedQuotaSize) 2144 2145 err = dataprovider.UpdateUserQuota(&user, 10, 6000, true) 2146 assert.NoError(t, err) 2147 files, size, err = dataprovider.GetUsedQuota(user.Username) 2148 assert.NoError(t, err) 2149 assert.Equal(t, 10, files) 2150 assert.Equal(t, int64(6000), size) 2151 2152 userGet, err = dataprovider.UserExists(user.Username) 2153 assert.NoError(t, err) 2154 assert.Equal(t, 10, userGet.UsedQuotaFiles) 2155 assert.Equal(t, int64(6000), userGet.UsedQuotaSize) 2156 2157 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2158 assert.NoError(t, err) 2159 err = os.RemoveAll(user.GetHomeDir()) 2160 assert.NoError(t, err) 2161 2162 folder := vfs.BaseVirtualFolder{ 2163 Name: "folder", 2164 MappedPath: filepath.Join(os.TempDir(), "p"), 2165 } 2166 err = dataprovider.AddFolder(&folder) 2167 assert.NoError(t, err) 2168 2169 err = dataprovider.UpdateVirtualFolderQuota(&folder, 10, 6000, false) 2170 assert.NoError(t, err) 2171 files, size, err = dataprovider.GetUsedVirtualFolderQuota(folder.Name) 2172 assert.NoError(t, err) 2173 assert.Equal(t, 10, files) 2174 assert.Equal(t, int64(6000), size) 2175 2176 folderGet, err := dataprovider.GetFolderByName(folder.Name) 2177 assert.NoError(t, err) 2178 assert.Equal(t, 0, folderGet.UsedQuotaFiles) 2179 assert.Equal(t, int64(0), folderGet.UsedQuotaSize) 2180 2181 err = dataprovider.UpdateVirtualFolderQuota(&folder, 10, 6000, true) 2182 assert.NoError(t, err) 2183 files, size, err = dataprovider.GetUsedVirtualFolderQuota(folder.Name) 2184 assert.NoError(t, err) 2185 assert.Equal(t, 10, files) 2186 assert.Equal(t, int64(6000), size) 2187 2188 folderGet, err = dataprovider.GetFolderByName(folder.Name) 2189 assert.NoError(t, err) 2190 assert.Equal(t, 10, folderGet.UsedQuotaFiles) 2191 assert.Equal(t, int64(6000), folderGet.UsedQuotaSize) 2192 2193 err = dataprovider.DeleteFolder(folder.Name, "", "") 2194 assert.NoError(t, err) 2195 2196 err = dataprovider.Close() 2197 assert.NoError(t, err) 2198 err = config.LoadConfig(configDir, "") 2199 assert.NoError(t, err) 2200 providerConf = config.GetProviderConf() 2201 err = dataprovider.Initialize(providerConf, configDir, true) 2202 assert.NoError(t, err) 2203} 2204 2205func TestPasswordCaching(t *testing.T) { 2206 user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated) 2207 assert.NoError(t, err) 2208 found, match := dataprovider.CheckCachedPassword(user.Username, defaultPassword) 2209 assert.False(t, found) 2210 assert.False(t, match) 2211 2212 user.Password = "wrong" 2213 _, _, err = getSftpClient(user) 2214 assert.Error(t, err) 2215 found, match = dataprovider.CheckCachedPassword(user.Username, defaultPassword) 2216 assert.False(t, found) 2217 assert.False(t, match) 2218 user.Password = "" 2219 2220 conn, client, err := getSftpClient(user) 2221 if assert.NoError(t, err) { 2222 defer conn.Close() 2223 defer client.Close() 2224 err = checkBasicSFTP(client) 2225 assert.NoError(t, err) 2226 } 2227 found, match = dataprovider.CheckCachedPassword(user.Username, defaultPassword) 2228 assert.True(t, found) 2229 assert.True(t, match) 2230 2231 found, match = dataprovider.CheckCachedPassword(user.Username, defaultPassword+"_") 2232 assert.True(t, found) 2233 assert.False(t, match) 2234 2235 found, match = dataprovider.CheckCachedPassword(user.Username+"_", defaultPassword) 2236 assert.False(t, found) 2237 assert.False(t, match) 2238 2239 user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 2240 assert.NoError(t, err) 2241 found, match = dataprovider.CheckCachedPassword(user.Username, defaultPassword) 2242 assert.False(t, found) 2243 assert.False(t, match) 2244 2245 conn, client, err = getSftpClient(user) 2246 if assert.NoError(t, err) { 2247 defer conn.Close() 2248 defer client.Close() 2249 err = checkBasicSFTP(client) 2250 assert.NoError(t, err) 2251 } 2252 2253 found, match = dataprovider.CheckCachedPassword(user.Username, defaultPassword) 2254 assert.True(t, found) 2255 assert.True(t, match) 2256 2257 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2258 assert.NoError(t, err) 2259 err = os.RemoveAll(user.GetHomeDir()) 2260 assert.NoError(t, err) 2261 found, match = dataprovider.CheckCachedPassword(user.Username, defaultPassword) 2262 assert.False(t, found) 2263 assert.False(t, match) 2264} 2265 2266func TestSyncUploadAction(t *testing.T) { 2267 if runtime.GOOS == osWindows { 2268 t.Skip("this test is not available on Windows") 2269 } 2270 uploadScriptPath := filepath.Join(os.TempDir(), "upload.sh") 2271 common.Config.Actions.ExecuteOn = []string{"upload"} 2272 common.Config.Actions.ExecuteSync = []string{"upload"} 2273 common.Config.Actions.Hook = uploadScriptPath 2274 2275 user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated) 2276 assert.NoError(t, err) 2277 movedPath := filepath.Join(user.HomeDir, "moved.dat") 2278 err = os.WriteFile(uploadScriptPath, getUploadScriptContent(movedPath), 0755) 2279 assert.NoError(t, err) 2280 conn, client, err := getSftpClient(user) 2281 if assert.NoError(t, err) { 2282 defer conn.Close() 2283 defer client.Close() 2284 2285 size := int64(32768) 2286 err = writeSFTPFileNoCheck(testFileName, size, client) 2287 assert.NoError(t, err) 2288 _, err = client.Stat(testFileName) 2289 assert.Error(t, err) 2290 info, err := client.Stat(filepath.Base(movedPath)) 2291 if assert.NoError(t, err) { 2292 assert.Equal(t, size, info.Size()) 2293 } 2294 } 2295 2296 err = os.Remove(uploadScriptPath) 2297 assert.NoError(t, err) 2298 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2299 assert.NoError(t, err) 2300 err = os.RemoveAll(user.GetHomeDir()) 2301 assert.NoError(t, err) 2302 2303 common.Config.Actions.ExecuteOn = nil 2304 common.Config.Actions.ExecuteSync = nil 2305 common.Config.Actions.Hook = uploadScriptPath 2306} 2307 2308func TestQuotaTrackDisabled(t *testing.T) { 2309 err := dataprovider.Close() 2310 assert.NoError(t, err) 2311 err = config.LoadConfig(configDir, "") 2312 assert.NoError(t, err) 2313 providerConf := config.GetProviderConf() 2314 providerConf.TrackQuota = 0 2315 err = dataprovider.Initialize(providerConf, configDir, true) 2316 assert.NoError(t, err) 2317 2318 user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated) 2319 assert.NoError(t, err) 2320 conn, client, err := getSftpClient(user) 2321 if assert.NoError(t, err) { 2322 defer conn.Close() 2323 defer client.Close() 2324 err = writeSFTPFile(testFileName, 32, client) 2325 assert.NoError(t, err) 2326 err = client.Rename(testFileName, testFileName+"1") 2327 assert.NoError(t, err) 2328 } 2329 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2330 assert.NoError(t, err) 2331 err = os.RemoveAll(user.GetHomeDir()) 2332 assert.NoError(t, err) 2333 2334 err = dataprovider.Close() 2335 assert.NoError(t, err) 2336 err = config.LoadConfig(configDir, "") 2337 assert.NoError(t, err) 2338 providerConf = config.GetProviderConf() 2339 err = dataprovider.Initialize(providerConf, configDir, true) 2340 assert.NoError(t, err) 2341} 2342 2343func TestGetQuotaError(t *testing.T) { 2344 if dataprovider.GetProviderStatus().Driver == "memory" { 2345 t.Skip("this test is not available with the memory provider") 2346 } 2347 u := getTestUser() 2348 mappedPath := filepath.Join(os.TempDir(), "vdir") 2349 folderName := filepath.Base(mappedPath) 2350 vdirPath := "/vpath" 2351 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 2352 BaseVirtualFolder: vfs.BaseVirtualFolder{ 2353 Name: folderName, 2354 MappedPath: mappedPath, 2355 }, 2356 VirtualPath: vdirPath, 2357 QuotaSize: 0, 2358 QuotaFiles: 10, 2359 }) 2360 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 2361 assert.NoError(t, err) 2362 conn, client, err := getSftpClient(user) 2363 if assert.NoError(t, err) { 2364 defer conn.Close() 2365 defer client.Close() 2366 err = writeSFTPFile(testFileName, 32, client) 2367 assert.NoError(t, err) 2368 2369 err = dataprovider.Close() 2370 assert.NoError(t, err) 2371 2372 err = client.Rename(testFileName, path.Join(vdirPath, testFileName)) 2373 assert.Error(t, err) 2374 2375 err = config.LoadConfig(configDir, "") 2376 assert.NoError(t, err) 2377 providerConf := config.GetProviderConf() 2378 err = dataprovider.Initialize(providerConf, configDir, true) 2379 assert.NoError(t, err) 2380 } 2381 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2382 assert.NoError(t, err) 2383 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName}, http.StatusOK) 2384 assert.NoError(t, err) 2385 err = os.RemoveAll(user.GetHomeDir()) 2386 assert.NoError(t, err) 2387 err = os.RemoveAll(mappedPath) 2388 assert.NoError(t, err) 2389} 2390 2391func TestRetentionAPI(t *testing.T) { 2392 u := getTestUser() 2393 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 2394 assert.NoError(t, err) 2395 uploadPath := path.Join(testDir, testFileName) 2396 2397 conn, client, err := getSftpClient(user) 2398 if assert.NoError(t, err) { 2399 defer conn.Close() 2400 defer client.Close() 2401 2402 err = client.Mkdir(testDir) 2403 assert.NoError(t, err) 2404 err = writeSFTPFile(uploadPath, 32, client) 2405 assert.NoError(t, err) 2406 2407 folderRetention := []common.FolderRetention{ 2408 { 2409 Path: "/", 2410 Retention: 24, 2411 DeleteEmptyDirs: true, 2412 }, 2413 } 2414 _, err = httpdtest.StartRetentionCheck(user.Username, folderRetention, http.StatusAccepted) 2415 assert.NoError(t, err) 2416 2417 assert.Eventually(t, func() bool { 2418 return len(common.RetentionChecks.Get()) == 0 2419 }, 1000*time.Millisecond, 50*time.Millisecond) 2420 2421 _, err = client.Stat(uploadPath) 2422 assert.NoError(t, err) 2423 2424 err = client.Chtimes(uploadPath, time.Now().Add(-48*time.Hour), time.Now().Add(-48*time.Hour)) 2425 assert.NoError(t, err) 2426 2427 _, err = httpdtest.StartRetentionCheck(user.Username, folderRetention, http.StatusAccepted) 2428 assert.NoError(t, err) 2429 2430 assert.Eventually(t, func() bool { 2431 return len(common.RetentionChecks.Get()) == 0 2432 }, 1000*time.Millisecond, 50*time.Millisecond) 2433 2434 _, err = client.Stat(uploadPath) 2435 assert.ErrorIs(t, err, os.ErrNotExist) 2436 2437 _, err = client.Stat(testDir) 2438 assert.ErrorIs(t, err, os.ErrNotExist) 2439 2440 err = client.Mkdir(testDir) 2441 assert.NoError(t, err) 2442 err = writeSFTPFile(uploadPath, 32, client) 2443 assert.NoError(t, err) 2444 2445 folderRetention[0].DeleteEmptyDirs = false 2446 err = client.Chtimes(uploadPath, time.Now().Add(-48*time.Hour), time.Now().Add(-48*time.Hour)) 2447 assert.NoError(t, err) 2448 2449 _, err = httpdtest.StartRetentionCheck(user.Username, folderRetention, http.StatusAccepted) 2450 assert.NoError(t, err) 2451 2452 assert.Eventually(t, func() bool { 2453 return len(common.RetentionChecks.Get()) == 0 2454 }, 1000*time.Millisecond, 50*time.Millisecond) 2455 2456 _, err = client.Stat(uploadPath) 2457 assert.ErrorIs(t, err, os.ErrNotExist) 2458 2459 _, err = client.Stat(testDir) 2460 assert.NoError(t, err) 2461 2462 err = writeSFTPFile(uploadPath, 32, client) 2463 assert.NoError(t, err) 2464 err = client.Chtimes(uploadPath, time.Now().Add(-48*time.Hour), time.Now().Add(-48*time.Hour)) 2465 assert.NoError(t, err) 2466 } 2467 2468 // remove delete permissions to the user 2469 user.Permissions["/"+testDir] = []string{dataprovider.PermListItems, dataprovider.PermUpload, 2470 dataprovider.PermCreateDirs, dataprovider.PermChtimes} 2471 user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 2472 assert.NoError(t, err) 2473 2474 conn, client, err = getSftpClient(user) 2475 if assert.NoError(t, err) { 2476 defer conn.Close() 2477 defer client.Close() 2478 2479 innerUploadFilePath := path.Join("/"+testDir, testDir, testFileName) 2480 err = client.Mkdir(path.Join(testDir, testDir)) 2481 assert.NoError(t, err) 2482 2483 err = writeSFTPFile(innerUploadFilePath, 32, client) 2484 assert.NoError(t, err) 2485 err = client.Chtimes(innerUploadFilePath, time.Now().Add(-48*time.Hour), time.Now().Add(-48*time.Hour)) 2486 assert.NoError(t, err) 2487 2488 folderRetention := []common.FolderRetention{ 2489 { 2490 Path: "/missing", 2491 Retention: 24, 2492 }, 2493 { 2494 Path: "/" + testDir, 2495 Retention: 24, 2496 DeleteEmptyDirs: true, 2497 }, 2498 { 2499 Path: path.Dir(innerUploadFilePath), 2500 Retention: 0, 2501 IgnoreUserPermissions: true, 2502 }, 2503 } 2504 _, err = httpdtest.StartRetentionCheck(user.Username, folderRetention, http.StatusAccepted) 2505 assert.NoError(t, err) 2506 2507 assert.Eventually(t, func() bool { 2508 return len(common.RetentionChecks.Get()) == 0 2509 }, 1000*time.Millisecond, 50*time.Millisecond) 2510 2511 _, err = client.Stat(uploadPath) 2512 assert.NoError(t, err) 2513 _, err = client.Stat(innerUploadFilePath) 2514 assert.NoError(t, err) 2515 2516 folderRetention[1].IgnoreUserPermissions = true 2517 _, err = httpdtest.StartRetentionCheck(user.Username, folderRetention, http.StatusAccepted) 2518 assert.NoError(t, err) 2519 2520 assert.Eventually(t, func() bool { 2521 return len(common.RetentionChecks.Get()) == 0 2522 }, 1000*time.Millisecond, 50*time.Millisecond) 2523 2524 _, err = client.Stat(uploadPath) 2525 assert.ErrorIs(t, err, os.ErrNotExist) 2526 _, err = client.Stat(innerUploadFilePath) 2527 assert.NoError(t, err) 2528 2529 folderRetention = []common.FolderRetention{ 2530 2531 { 2532 Path: "/" + testDir, 2533 Retention: 24, 2534 DeleteEmptyDirs: true, 2535 IgnoreUserPermissions: true, 2536 }, 2537 } 2538 2539 _, err = httpdtest.StartRetentionCheck(user.Username, folderRetention, http.StatusAccepted) 2540 assert.NoError(t, err) 2541 2542 assert.Eventually(t, func() bool { 2543 return len(common.RetentionChecks.Get()) == 0 2544 }, 1000*time.Millisecond, 50*time.Millisecond) 2545 2546 _, err = client.Stat(innerUploadFilePath) 2547 assert.ErrorIs(t, err, os.ErrNotExist) 2548 } 2549 // finally test some errors removing files or folders 2550 if runtime.GOOS != osWindows { 2551 dirPath := filepath.Join(user.HomeDir, "adir", "sub") 2552 err := os.MkdirAll(dirPath, os.ModePerm) 2553 assert.NoError(t, err) 2554 filePath := filepath.Join(dirPath, "f.dat") 2555 err = os.WriteFile(filePath, nil, os.ModePerm) 2556 assert.NoError(t, err) 2557 2558 err = os.Chtimes(filePath, time.Now().Add(-72*time.Hour), time.Now().Add(-72*time.Hour)) 2559 assert.NoError(t, err) 2560 2561 err = os.Chmod(dirPath, 0001) 2562 assert.NoError(t, err) 2563 2564 folderRetention := []common.FolderRetention{ 2565 2566 { 2567 Path: "/adir", 2568 Retention: 24, 2569 DeleteEmptyDirs: true, 2570 IgnoreUserPermissions: true, 2571 }, 2572 } 2573 2574 _, err = httpdtest.StartRetentionCheck(user.Username, folderRetention, http.StatusAccepted) 2575 assert.NoError(t, err) 2576 2577 assert.Eventually(t, func() bool { 2578 return len(common.RetentionChecks.Get()) == 0 2579 }, 1000*time.Millisecond, 50*time.Millisecond) 2580 2581 err = os.Chmod(dirPath, 0555) 2582 assert.NoError(t, err) 2583 2584 _, err = httpdtest.StartRetentionCheck(user.Username, folderRetention, http.StatusAccepted) 2585 assert.NoError(t, err) 2586 2587 assert.Eventually(t, func() bool { 2588 return len(common.RetentionChecks.Get()) == 0 2589 }, 1000*time.Millisecond, 50*time.Millisecond) 2590 2591 err = os.Chmod(dirPath, os.ModePerm) 2592 assert.NoError(t, err) 2593 2594 _, err = httpdtest.StartRetentionCheck(user.Username, folderRetention, http.StatusAccepted) 2595 assert.NoError(t, err) 2596 2597 assert.Eventually(t, func() bool { 2598 return len(common.RetentionChecks.Get()) == 0 2599 }, 1000*time.Millisecond, 50*time.Millisecond) 2600 2601 assert.NoDirExists(t, dirPath) 2602 } 2603 2604 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2605 assert.NoError(t, err) 2606 err = os.RemoveAll(user.GetHomeDir()) 2607 assert.NoError(t, err) 2608} 2609 2610func TestRenameDir(t *testing.T) { 2611 u := getTestUser() 2612 testDir := "/dir-to-rename" 2613 u.Permissions[testDir] = []string{dataprovider.PermListItems, dataprovider.PermUpload} 2614 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 2615 assert.NoError(t, err) 2616 conn, client, err := getSftpClient(user) 2617 if assert.NoError(t, err) { 2618 defer conn.Close() 2619 defer client.Close() 2620 err = client.Mkdir(testDir) 2621 assert.NoError(t, err) 2622 err = writeSFTPFile(path.Join(testDir, testFileName), 32, client) 2623 assert.NoError(t, err) 2624 err = client.Rename(testDir, testDir+"_rename") 2625 assert.ErrorIs(t, err, os.ErrPermission) 2626 } 2627 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2628 assert.NoError(t, err) 2629 err = os.RemoveAll(user.GetHomeDir()) 2630 assert.NoError(t, err) 2631} 2632 2633func TestBuiltinKeyboardInteractiveAuthentication(t *testing.T) { 2634 u := getTestUser() 2635 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 2636 assert.NoError(t, err) 2637 authMethods := []ssh.AuthMethod{ 2638 ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) ([]string, error) { 2639 return []string{defaultPassword}, nil 2640 }), 2641 } 2642 conn, client, err := getCustomAuthSftpClient(user, authMethods) 2643 if assert.NoError(t, err) { 2644 defer conn.Close() 2645 defer client.Close() 2646 assert.NoError(t, checkBasicSFTP(client)) 2647 err = writeSFTPFile(testFileName, 4096, client) 2648 assert.NoError(t, err) 2649 } 2650 // add multi-factor authentication 2651 configName, _, secret, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], user.Username) 2652 assert.NoError(t, err) 2653 user.Password = defaultPassword 2654 user.Filters.TOTPConfig = sdk.TOTPConfig{ 2655 Enabled: true, 2656 ConfigName: configName, 2657 Secret: kms.NewPlainSecret(secret), 2658 Protocols: []string{common.ProtocolSSH}, 2659 } 2660 err = dataprovider.UpdateUser(&user, "", "") 2661 assert.NoError(t, err) 2662 passcode, err := generateTOTPPasscode(secret, otp.AlgorithmSHA1) 2663 assert.NoError(t, err) 2664 passwordAsked := false 2665 passcodeAsked := false 2666 authMethods = []ssh.AuthMethod{ 2667 ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) ([]string, error) { 2668 var answers []string 2669 if strings.HasPrefix(questions[0], "Password") { 2670 answers = append(answers, defaultPassword) 2671 passwordAsked = true 2672 } else { 2673 answers = append(answers, passcode) 2674 passcodeAsked = true 2675 } 2676 return answers, nil 2677 }), 2678 } 2679 conn, client, err = getCustomAuthSftpClient(user, authMethods) 2680 if assert.NoError(t, err) { 2681 defer conn.Close() 2682 defer client.Close() 2683 assert.NoError(t, checkBasicSFTP(client)) 2684 err = writeSFTPFile(testFileName, 4096, client) 2685 assert.NoError(t, err) 2686 } 2687 assert.True(t, passwordAsked) 2688 assert.True(t, passcodeAsked) 2689 2690 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2691 assert.NoError(t, err) 2692 err = os.RemoveAll(user.GetHomeDir()) 2693 assert.NoError(t, err) 2694} 2695 2696func TestRenameSymlink(t *testing.T) { 2697 u := getTestUser() 2698 testDir := "/dir-no-create-links" 2699 otherDir := "otherdir" 2700 u.Permissions[testDir] = []string{dataprovider.PermListItems, dataprovider.PermUpload, dataprovider.PermDelete, 2701 dataprovider.PermCreateDirs} 2702 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 2703 assert.NoError(t, err) 2704 conn, client, err := getSftpClient(user) 2705 if assert.NoError(t, err) { 2706 defer conn.Close() 2707 defer client.Close() 2708 err = client.Mkdir(otherDir) 2709 assert.NoError(t, err) 2710 err = client.Symlink(otherDir, otherDir+".link") 2711 assert.NoError(t, err) 2712 err = client.Rename(otherDir+".link", path.Join(testDir, "symlink")) 2713 assert.ErrorIs(t, err, os.ErrPermission) 2714 err = client.Rename(otherDir+".link", "allowed_link") 2715 assert.NoError(t, err) 2716 } 2717 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2718 assert.NoError(t, err) 2719 err = os.RemoveAll(user.GetHomeDir()) 2720 assert.NoError(t, err) 2721} 2722 2723func TestSplittedDeletePerms(t *testing.T) { 2724 u := getTestUser() 2725 u.Permissions["/"] = []string{dataprovider.PermListItems, dataprovider.PermUpload, dataprovider.PermDeleteDirs, 2726 dataprovider.PermCreateDirs} 2727 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 2728 assert.NoError(t, err) 2729 conn, client, err := getSftpClient(user) 2730 if assert.NoError(t, err) { 2731 defer conn.Close() 2732 defer client.Close() 2733 err = writeSFTPFile(testFileName, 4096, client) 2734 assert.NoError(t, err) 2735 err = client.Remove(testFileName) 2736 assert.Error(t, err) 2737 err = client.Mkdir(testDir) 2738 assert.NoError(t, err) 2739 err = client.RemoveDirectory(testDir) 2740 assert.NoError(t, err) 2741 } 2742 u.Permissions["/"] = []string{dataprovider.PermListItems, dataprovider.PermUpload, dataprovider.PermDeleteFiles, 2743 dataprovider.PermCreateDirs, dataprovider.PermOverwrite} 2744 _, _, err = httpdtest.UpdateUser(u, http.StatusOK, "") 2745 assert.NoError(t, err) 2746 2747 conn, client, err = getSftpClient(user) 2748 if assert.NoError(t, err) { 2749 defer conn.Close() 2750 defer client.Close() 2751 err = writeSFTPFile(testFileName, 4096, client) 2752 assert.NoError(t, err) 2753 err = client.Remove(testFileName) 2754 assert.NoError(t, err) 2755 err = client.Mkdir(testDir) 2756 assert.NoError(t, err) 2757 err = client.RemoveDirectory(testDir) 2758 assert.Error(t, err) 2759 } 2760 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2761 assert.NoError(t, err) 2762 err = os.RemoveAll(user.GetHomeDir()) 2763 assert.NoError(t, err) 2764} 2765 2766func TestSplittedRenamePerms(t *testing.T) { 2767 u := getTestUser() 2768 u.Permissions["/"] = []string{dataprovider.PermListItems, dataprovider.PermUpload, dataprovider.PermRenameDirs, 2769 dataprovider.PermCreateDirs} 2770 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 2771 assert.NoError(t, err) 2772 conn, client, err := getSftpClient(user) 2773 if assert.NoError(t, err) { 2774 defer conn.Close() 2775 defer client.Close() 2776 err = writeSFTPFile(testFileName, 4096, client) 2777 assert.NoError(t, err) 2778 err = client.Mkdir(testDir) 2779 assert.NoError(t, err) 2780 err = client.Rename(testFileName, testFileName+"_renamed") 2781 assert.Error(t, err) 2782 err = client.Rename(testDir, testDir+"_renamed") 2783 assert.NoError(t, err) 2784 } 2785 u.Permissions["/"] = []string{dataprovider.PermListItems, dataprovider.PermUpload, dataprovider.PermRenameFiles, 2786 dataprovider.PermCreateDirs, dataprovider.PermOverwrite} 2787 _, _, err = httpdtest.UpdateUser(u, http.StatusOK, "") 2788 assert.NoError(t, err) 2789 2790 conn, client, err = getSftpClient(user) 2791 if assert.NoError(t, err) { 2792 defer conn.Close() 2793 defer client.Close() 2794 err = writeSFTPFile(testFileName, 4096, client) 2795 assert.NoError(t, err) 2796 err = client.Mkdir(testDir) 2797 assert.NoError(t, err) 2798 err = client.Rename(testFileName, testFileName+"_renamed") 2799 assert.NoError(t, err) 2800 err = client.Rename(testDir, testDir+"_renamed") 2801 assert.Error(t, err) 2802 } 2803 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2804 assert.NoError(t, err) 2805 err = os.RemoveAll(user.GetHomeDir()) 2806 assert.NoError(t, err) 2807} 2808 2809func TestSFTPLoopError(t *testing.T) { 2810 user1 := getTestUser() 2811 user2 := getTestUser() 2812 user1.Username += "1" 2813 user2.Username += "2" 2814 // user1 is a local account with a virtual SFTP folder to user2 2815 // user2 has user1 as SFTP fs 2816 user1.VirtualFolders = append(user1.VirtualFolders, vfs.VirtualFolder{ 2817 BaseVirtualFolder: vfs.BaseVirtualFolder{ 2818 Name: "sftp", 2819 FsConfig: vfs.Filesystem{ 2820 Provider: sdk.SFTPFilesystemProvider, 2821 SFTPConfig: vfs.SFTPFsConfig{ 2822 SFTPFsConfig: sdk.SFTPFsConfig{ 2823 Endpoint: sftpServerAddr, 2824 Username: user2.Username, 2825 Password: kms.NewPlainSecret(defaultPassword), 2826 }, 2827 }, 2828 }, 2829 }, 2830 VirtualPath: "/vdir", 2831 }) 2832 2833 user2.FsConfig.Provider = sdk.SFTPFilesystemProvider 2834 user2.FsConfig.SFTPConfig = vfs.SFTPFsConfig{ 2835 SFTPFsConfig: sdk.SFTPFsConfig{ 2836 Endpoint: sftpServerAddr, 2837 Username: user1.Username, 2838 Password: kms.NewPlainSecret(defaultPassword), 2839 }, 2840 } 2841 2842 user1, resp, err := httpdtest.AddUser(user1, http.StatusCreated) 2843 assert.NoError(t, err, string(resp)) 2844 user2, resp, err = httpdtest.AddUser(user2, http.StatusCreated) 2845 assert.NoError(t, err, string(resp)) 2846 2847 user1.VirtualFolders[0].FsConfig.SFTPConfig.Password = kms.NewPlainSecret(defaultPassword) 2848 user2.FsConfig.SFTPConfig.Password = kms.NewPlainSecret(defaultPassword) 2849 2850 conn := common.NewBaseConnection("", common.ProtocolWebDAV, "", "", user1) 2851 _, _, err = conn.GetFsAndResolvedPath(user1.VirtualFolders[0].VirtualPath) 2852 assert.ErrorIs(t, err, os.ErrPermission) 2853 2854 conn = common.NewBaseConnection("", common.ProtocolSFTP, "", "", user1) 2855 _, _, err = conn.GetFsAndResolvedPath(user1.VirtualFolders[0].VirtualPath) 2856 if assert.Error(t, err) { 2857 assert.Contains(t, err.Error(), "SFTP loop") 2858 } 2859 conn = common.NewBaseConnection("", common.ProtocolFTP, "", "", user1) 2860 _, _, err = conn.GetFsAndResolvedPath(user1.VirtualFolders[0].VirtualPath) 2861 if assert.Error(t, err) { 2862 assert.Contains(t, err.Error(), "SFTP loop") 2863 } 2864 _, err = httpdtest.RemoveUser(user1, http.StatusOK) 2865 assert.NoError(t, err) 2866 err = os.RemoveAll(user1.GetHomeDir()) 2867 assert.NoError(t, err) 2868 _, err = httpdtest.RemoveUser(user2, http.StatusOK) 2869 assert.NoError(t, err) 2870 err = os.RemoveAll(user2.GetHomeDir()) 2871 assert.NoError(t, err) 2872 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: "sftp"}, http.StatusOK) 2873 assert.NoError(t, err) 2874} 2875 2876func TestNonLocalCrossRename(t *testing.T) { 2877 baseUser, resp, err := httpdtest.AddUser(getTestUser(), http.StatusCreated) 2878 assert.NoError(t, err, string(resp)) 2879 u := getTestUser() 2880 u.HomeDir += "_folders" 2881 u.Username += "_folders" 2882 mappedPathSFTP := filepath.Join(os.TempDir(), "sftp") 2883 folderNameSFTP := filepath.Base(mappedPathSFTP) 2884 vdirSFTPPath := "/vdir/sftp" 2885 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 2886 BaseVirtualFolder: vfs.BaseVirtualFolder{ 2887 Name: folderNameSFTP, 2888 FsConfig: vfs.Filesystem{ 2889 Provider: sdk.SFTPFilesystemProvider, 2890 SFTPConfig: vfs.SFTPFsConfig{ 2891 SFTPFsConfig: sdk.SFTPFsConfig{ 2892 Endpoint: sftpServerAddr, 2893 Username: baseUser.Username, 2894 Password: kms.NewPlainSecret(defaultPassword), 2895 }, 2896 }, 2897 }, 2898 }, 2899 VirtualPath: vdirSFTPPath, 2900 }) 2901 mappedPathCrypt := filepath.Join(os.TempDir(), "crypt") 2902 folderNameCrypt := filepath.Base(mappedPathCrypt) 2903 vdirCryptPath := "/vdir/crypt" 2904 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 2905 BaseVirtualFolder: vfs.BaseVirtualFolder{ 2906 Name: folderNameCrypt, 2907 FsConfig: vfs.Filesystem{ 2908 Provider: sdk.CryptedFilesystemProvider, 2909 CryptConfig: vfs.CryptFsConfig{ 2910 CryptFsConfig: sdk.CryptFsConfig{ 2911 Passphrase: kms.NewPlainSecret(defaultPassword), 2912 }, 2913 }, 2914 }, 2915 MappedPath: mappedPathCrypt, 2916 }, 2917 VirtualPath: vdirCryptPath, 2918 }) 2919 user, resp, err := httpdtest.AddUser(u, http.StatusCreated) 2920 assert.NoError(t, err, string(resp)) 2921 conn, client, err := getSftpClient(user) 2922 if assert.NoError(t, err) { 2923 defer conn.Close() 2924 defer client.Close() 2925 assert.NoError(t, checkBasicSFTP(client)) 2926 err = writeSFTPFile(testFileName, 4096, client) 2927 assert.NoError(t, err) 2928 err = writeSFTPFile(path.Join(vdirSFTPPath, testFileName), 8192, client) 2929 assert.NoError(t, err) 2930 err = writeSFTPFile(path.Join(vdirCryptPath, testFileName), 16384, client) 2931 assert.NoError(t, err) 2932 err = client.Rename(path.Join(vdirSFTPPath, testFileName), path.Join(vdirCryptPath, testFileName+".rename")) 2933 assert.ErrorIs(t, err, os.ErrPermission) 2934 err = client.Rename(path.Join(vdirCryptPath, testFileName), path.Join(vdirSFTPPath, testFileName+".rename")) 2935 assert.ErrorIs(t, err, os.ErrPermission) 2936 err = client.Rename(testFileName, path.Join(vdirCryptPath, testFileName+".rename")) 2937 assert.ErrorIs(t, err, os.ErrPermission) 2938 err = client.Rename(testFileName, path.Join(vdirSFTPPath, testFileName+".rename")) 2939 assert.ErrorIs(t, err, os.ErrPermission) 2940 err = client.Rename(path.Join(vdirSFTPPath, testFileName), testFileName+".rename") 2941 assert.ErrorIs(t, err, os.ErrPermission) 2942 err = client.Rename(path.Join(vdirCryptPath, testFileName), testFileName+".rename") 2943 assert.ErrorIs(t, err, os.ErrPermission) 2944 // rename on local fs or on the same folder must work 2945 err = client.Rename(testFileName, testFileName+".rename") 2946 assert.NoError(t, err) 2947 err = client.Rename(path.Join(vdirSFTPPath, testFileName), path.Join(vdirSFTPPath, testFileName+"_rename")) 2948 assert.NoError(t, err) 2949 err = client.Rename(path.Join(vdirCryptPath, testFileName), path.Join(vdirCryptPath, testFileName+"_rename")) 2950 assert.NoError(t, err) 2951 // renaming a virtual folder is not allowed 2952 err = client.Rename(vdirSFTPPath, vdirSFTPPath+"_rename") 2953 assert.ErrorIs(t, err, os.ErrPermission) 2954 err = client.Rename(vdirCryptPath, vdirCryptPath+"_rename") 2955 assert.ErrorIs(t, err, os.ErrPermission) 2956 err = client.Rename(vdirCryptPath, path.Join(vdirCryptPath, "rename")) 2957 assert.ErrorIs(t, err, os.ErrPermission) 2958 err = client.Mkdir(path.Join(vdirCryptPath, "subcryptdir")) 2959 assert.NoError(t, err) 2960 err = client.Rename(path.Join(vdirCryptPath, "subcryptdir"), vdirCryptPath) 2961 assert.ErrorIs(t, err, os.ErrPermission) 2962 // renaming root folder is not allowed 2963 err = client.Rename("/", "new_name") 2964 assert.ErrorIs(t, err, os.ErrPermission) 2965 // renaming a path to a virtual folder is not allowed 2966 err = client.Rename("/vdir", "new_vdir") 2967 if assert.Error(t, err) { 2968 assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") 2969 } 2970 } 2971 2972 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2973 assert.NoError(t, err) 2974 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderNameCrypt}, http.StatusOK) 2975 assert.NoError(t, err) 2976 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderNameSFTP}, http.StatusOK) 2977 assert.NoError(t, err) 2978 _, err = httpdtest.RemoveUser(baseUser, http.StatusOK) 2979 assert.NoError(t, err) 2980 err = os.RemoveAll(user.GetHomeDir()) 2981 assert.NoError(t, err) 2982 err = os.RemoveAll(baseUser.GetHomeDir()) 2983 assert.NoError(t, err) 2984 err = os.RemoveAll(mappedPathCrypt) 2985 assert.NoError(t, err) 2986 err = os.RemoveAll(mappedPathSFTP) 2987 assert.NoError(t, err) 2988} 2989 2990func TestNonLocalCrossRenameNonLocalBaseUser(t *testing.T) { 2991 baseUser, resp, err := httpdtest.AddUser(getTestUser(), http.StatusCreated) 2992 assert.NoError(t, err, string(resp)) 2993 u := getTestSFTPUser() 2994 mappedPathLocal := filepath.Join(os.TempDir(), "local") 2995 folderNameLocal := filepath.Base(mappedPathLocal) 2996 vdirLocalPath := "/vdir/local" 2997 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 2998 BaseVirtualFolder: vfs.BaseVirtualFolder{ 2999 Name: folderNameLocal, 3000 MappedPath: mappedPathLocal, 3001 }, 3002 VirtualPath: vdirLocalPath, 3003 }) 3004 mappedPathCrypt := filepath.Join(os.TempDir(), "crypt") 3005 folderNameCrypt := filepath.Base(mappedPathCrypt) 3006 vdirCryptPath := "/vdir/crypt" 3007 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 3008 BaseVirtualFolder: vfs.BaseVirtualFolder{ 3009 Name: folderNameCrypt, 3010 FsConfig: vfs.Filesystem{ 3011 Provider: sdk.CryptedFilesystemProvider, 3012 CryptConfig: vfs.CryptFsConfig{ 3013 CryptFsConfig: sdk.CryptFsConfig{ 3014 Passphrase: kms.NewPlainSecret(defaultPassword), 3015 }, 3016 }, 3017 }, 3018 MappedPath: mappedPathCrypt, 3019 }, 3020 VirtualPath: vdirCryptPath, 3021 }) 3022 user, resp, err := httpdtest.AddUser(u, http.StatusCreated) 3023 assert.NoError(t, err, string(resp)) 3024 conn, client, err := getSftpClient(user) 3025 if assert.NoError(t, err) { 3026 defer conn.Close() 3027 defer client.Close() 3028 assert.NoError(t, checkBasicSFTP(client)) 3029 err = writeSFTPFile(testFileName, 4096, client) 3030 assert.NoError(t, err) 3031 err = writeSFTPFile(path.Join(vdirLocalPath, testFileName), 8192, client) 3032 assert.NoError(t, err) 3033 err = writeSFTPFile(path.Join(vdirCryptPath, testFileName), 16384, client) 3034 assert.NoError(t, err) 3035 err = client.Rename(path.Join(vdirLocalPath, testFileName), path.Join(vdirCryptPath, testFileName+".rename")) 3036 assert.ErrorIs(t, err, os.ErrPermission) 3037 err = client.Rename(path.Join(vdirCryptPath, testFileName), path.Join(vdirLocalPath, testFileName+".rename")) 3038 assert.ErrorIs(t, err, os.ErrPermission) 3039 err = client.Rename(testFileName, path.Join(vdirCryptPath, testFileName+".rename")) 3040 assert.ErrorIs(t, err, os.ErrPermission) 3041 err = client.Rename(testFileName, path.Join(vdirLocalPath, testFileName+".rename")) 3042 assert.ErrorIs(t, err, os.ErrPermission) 3043 err = client.Rename(path.Join(vdirLocalPath, testFileName), testFileName+".rename") 3044 assert.ErrorIs(t, err, os.ErrPermission) 3045 err = client.Rename(path.Join(vdirCryptPath, testFileName), testFileName+".rename") 3046 assert.ErrorIs(t, err, os.ErrPermission) 3047 // rename on local fs or on the same folder must work 3048 err = client.Rename(testFileName, testFileName+".rename") 3049 assert.NoError(t, err) 3050 err = client.Rename(path.Join(vdirLocalPath, testFileName), path.Join(vdirLocalPath, testFileName+"_rename")) 3051 assert.NoError(t, err) 3052 err = client.Rename(path.Join(vdirCryptPath, testFileName), path.Join(vdirCryptPath, testFileName+"_rename")) 3053 assert.NoError(t, err) 3054 // renaming a virtual folder is not allowed 3055 err = client.Rename(vdirLocalPath, vdirLocalPath+"_rename") 3056 assert.ErrorIs(t, err, os.ErrPermission) 3057 err = client.Rename(vdirCryptPath, vdirCryptPath+"_rename") 3058 assert.ErrorIs(t, err, os.ErrPermission) 3059 // renaming root folder is not allowed 3060 err = client.Rename("/", "new_name") 3061 assert.ErrorIs(t, err, os.ErrPermission) 3062 // renaming a path to a virtual folder is not allowed 3063 err = client.Rename("/vdir", "new_vdir") 3064 if assert.Error(t, err) { 3065 assert.Contains(t, err.Error(), "SSH_FX_OP_UNSUPPORTED") 3066 } 3067 } 3068 3069 _, err = httpdtest.RemoveUser(user, http.StatusOK) 3070 assert.NoError(t, err) 3071 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderNameCrypt}, http.StatusOK) 3072 assert.NoError(t, err) 3073 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderNameLocal}, http.StatusOK) 3074 assert.NoError(t, err) 3075 _, err = httpdtest.RemoveUser(baseUser, http.StatusOK) 3076 assert.NoError(t, err) 3077 err = os.RemoveAll(user.GetHomeDir()) 3078 assert.NoError(t, err) 3079 err = os.RemoveAll(baseUser.GetHomeDir()) 3080 assert.NoError(t, err) 3081 err = os.RemoveAll(mappedPathCrypt) 3082 assert.NoError(t, err) 3083 err = os.RemoveAll(mappedPathLocal) 3084 assert.NoError(t, err) 3085} 3086 3087func TestProxyProtocol(t *testing.T) { 3088 resp, err := httpclient.Get(fmt.Sprintf("http://%v", httpProxyAddr)) 3089 if assert.NoError(t, err) { 3090 defer resp.Body.Close() 3091 assert.Equal(t, http.StatusBadRequest, resp.StatusCode) 3092 } 3093} 3094 3095func TestSetProtocol(t *testing.T) { 3096 conn := common.NewBaseConnection("id", "sshd_exec", "", "", dataprovider.User{BaseUser: sdk.BaseUser{HomeDir: os.TempDir()}}) 3097 conn.SetProtocol(common.ProtocolSCP) 3098 require.Equal(t, "SCP_id", conn.GetID()) 3099} 3100 3101func TestGetFsError(t *testing.T) { 3102 u := getTestUser() 3103 u.FsConfig.Provider = sdk.GCSFilesystemProvider 3104 u.FsConfig.GCSConfig.Bucket = "test" 3105 u.FsConfig.GCSConfig.Credentials = kms.NewPlainSecret("invalid JSON for credentials") 3106 conn := common.NewBaseConnection("", common.ProtocolFTP, "", "", u) 3107 _, _, err := conn.GetFsAndResolvedPath("/vpath") 3108 assert.Error(t, err) 3109} 3110 3111func waitTCPListening(address string) { 3112 for { 3113 conn, err := net.Dial("tcp", address) 3114 if err != nil { 3115 logger.WarnToConsole("tcp server %v not listening: %v", address, err) 3116 time.Sleep(100 * time.Millisecond) 3117 continue 3118 } 3119 logger.InfoToConsole("tcp server %v now listening", address) 3120 conn.Close() 3121 break 3122 } 3123} 3124 3125func checkBasicSFTP(client *sftp.Client) error { 3126 _, err := client.Getwd() 3127 if err != nil { 3128 return err 3129 } 3130 _, err = client.ReadDir(".") 3131 return err 3132} 3133 3134func getCustomAuthSftpClient(user dataprovider.User, authMethods []ssh.AuthMethod) (*ssh.Client, *sftp.Client, error) { 3135 var sftpClient *sftp.Client 3136 config := &ssh.ClientConfig{ 3137 User: user.Username, 3138 HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { 3139 return nil 3140 }, 3141 Auth: authMethods, 3142 } 3143 conn, err := ssh.Dial("tcp", sftpServerAddr, config) 3144 if err != nil { 3145 return conn, sftpClient, err 3146 } 3147 sftpClient, err = sftp.NewClient(conn) 3148 if err != nil { 3149 conn.Close() 3150 } 3151 return conn, sftpClient, err 3152} 3153 3154func getSftpClient(user dataprovider.User) (*ssh.Client, *sftp.Client, error) { 3155 var sftpClient *sftp.Client 3156 config := &ssh.ClientConfig{ 3157 User: user.Username, 3158 HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { 3159 return nil 3160 }, 3161 } 3162 if user.Password != "" { 3163 config.Auth = []ssh.AuthMethod{ssh.Password(user.Password)} 3164 } else { 3165 config.Auth = []ssh.AuthMethod{ssh.Password(defaultPassword)} 3166 } 3167 3168 conn, err := ssh.Dial("tcp", sftpServerAddr, config) 3169 if err != nil { 3170 return conn, sftpClient, err 3171 } 3172 sftpClient, err = sftp.NewClient(conn) 3173 if err != nil { 3174 conn.Close() 3175 } 3176 return conn, sftpClient, err 3177} 3178 3179func getTestUser() dataprovider.User { 3180 user := dataprovider.User{ 3181 BaseUser: sdk.BaseUser{ 3182 Username: defaultUsername, 3183 Password: defaultPassword, 3184 HomeDir: filepath.Join(homeBasePath, defaultUsername), 3185 Status: 1, 3186 ExpirationDate: 0, 3187 }, 3188 } 3189 user.Permissions = make(map[string][]string) 3190 user.Permissions["/"] = allPerms 3191 return user 3192} 3193 3194func getTestSFTPUser() dataprovider.User { 3195 u := getTestUser() 3196 u.Username = defaultSFTPUsername 3197 u.FsConfig.Provider = sdk.SFTPFilesystemProvider 3198 u.FsConfig.SFTPConfig.Endpoint = sftpServerAddr 3199 u.FsConfig.SFTPConfig.Username = defaultUsername 3200 u.FsConfig.SFTPConfig.Password = kms.NewPlainSecret(defaultPassword) 3201 return u 3202} 3203 3204func getCryptFsUser() dataprovider.User { 3205 u := getTestUser() 3206 u.FsConfig.Provider = sdk.CryptedFilesystemProvider 3207 u.FsConfig.CryptConfig.Passphrase = kms.NewPlainSecret(defaultPassword) 3208 return u 3209} 3210 3211func writeSFTPFile(name string, size int64, client *sftp.Client) error { 3212 err := writeSFTPFileNoCheck(name, size, client) 3213 if err != nil { 3214 return err 3215 } 3216 info, err := client.Stat(name) 3217 if err != nil { 3218 return err 3219 } 3220 if info.Size() != size { 3221 return fmt.Errorf("file size mismatch, wanted %v, actual %v", size, info.Size()) 3222 } 3223 return nil 3224} 3225 3226func writeSFTPFileNoCheck(name string, size int64, client *sftp.Client) error { 3227 content := make([]byte, size) 3228 _, err := rand.Read(content) 3229 if err != nil { 3230 return err 3231 } 3232 f, err := client.Create(name) 3233 if err != nil { 3234 return err 3235 } 3236 _, err = io.Copy(f, bytes.NewBuffer(content)) 3237 if err != nil { 3238 f.Close() 3239 return err 3240 } 3241 return f.Close() 3242} 3243 3244func getUploadScriptContent(movedPath string) []byte { 3245 content := []byte("#!/bin/sh\n\n") 3246 content = append(content, []byte("sleep 1\n")...) 3247 content = append(content, []byte(fmt.Sprintf("mv ${SFTPGO_ACTION_PATH} %v\n", movedPath))...) 3248 return content 3249} 3250 3251func generateTOTPPasscode(secret string, algo otp.Algorithm) (string, error) { 3252 return totp.GenerateCodeCustom(secret, time.Now(), totp.ValidateOpts{ 3253 Period: 30, 3254 Skew: 1, 3255 Digits: otp.DigitsSix, 3256 Algorithm: algo, 3257 }) 3258} 3259