1
0
mirror of https://github.com/drakkan/sftpgo.git synced 2025-11-29 22:08:10 +02:00
Files
sftpgo/httpd/httpd_test.go
Nicola Murino 1770da545d s3: upload concurrency is now configurable
Please note that if the upload bandwidth between the SFTP client and
SFTPGo is greater than the upload bandwidth between SFTPGo and S3 then
the SFTP client have to wait for the upload of the last parts to S3
after it ends the file upload to SFTPGo, and it may time out.
Keep this in mind if you customize parts size and upload concurrency
2020-03-13 19:13:58 +01:00

2372 lines
84 KiB
Go

package httpd_test
import (
"bytes"
"crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"testing"
"time"
"github.com/go-chi/render"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
"github.com/rs/zerolog"
"github.com/drakkan/sftpgo/config"
"github.com/drakkan/sftpgo/dataprovider"
"github.com/drakkan/sftpgo/httpd"
"github.com/drakkan/sftpgo/logger"
"github.com/drakkan/sftpgo/sftpd"
"github.com/drakkan/sftpgo/utils"
"github.com/drakkan/sftpgo/vfs"
)
const (
defaultUsername = "test_user"
defaultPassword = "test_password"
testPubKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC03jj0D+djk7pxIf/0OhrxrchJTRZklofJ1NoIu4752Sq02mdXmarMVsqJ1cAjV5LBVy3D1F5U6XW4rppkXeVtd04Pxb09ehtH0pRRPaoHHlALiJt8CoMpbKYMA8b3KXPPriGxgGomvtU2T2RMURSwOZbMtpsugfjYSWenyYX+VORYhylWnSXL961LTyC21ehd6d6QnW9G7E5hYMITMY9TuQZz3bROYzXiTsgN0+g6Hn7exFQp50p45StUMfV/SftCMdCxlxuyGny2CrN/vfjO7xxOo2uv7q1qm10Q46KPWJQv+pgZ/OfL+EDjy07n5QVSKHlbx+2nT4Q0EgOSQaCTYwn3YjtABfIxWwgAFdyj6YlPulCL22qU4MYhDcA6PSBwDdf8hvxBfvsiHdM+JcSHvv8/VeJhk6CmnZxGY0fxBupov27z3yEO8nAg8k+6PaUiW1MSUfuGMF/ktB8LOstXsEPXSszuyXiOv4DaryOXUiSn7bmRqKcEFlJusO6aZP0= nicola@p1"
logSender = "APITesting"
userPath = "/api/v1/user"
activeConnectionsPath = "/api/v1/connection"
quotaScanPath = "/api/v1/quota_scan"
versionPath = "/api/v1/version"
providerStatusPath = "/api/v1/providerstatus"
dumpDataPath = "/api/v1/dumpdata"
loadDataPath = "/api/v1/loaddata"
metricsPath = "/metrics"
webBasePath = "/web"
webUsersPath = "/web/users"
webUserPath = "/web/user"
webConnectionsPath = "/web/connections"
configDir = ".."
httpsCert = `-----BEGIN CERTIFICATE-----
MIICHTCCAaKgAwIBAgIUHnqw7QnB1Bj9oUsNpdb+ZkFPOxMwCgYIKoZIzj0EAwIw
RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDAyMDQwOTUzMDRaFw0zMDAyMDEw
OTUzMDRaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD
VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwdjAQBgcqhkjOPQIBBgUrgQQA
IgNiAARCjRMqJ85rzMC998X5z761nJ+xL3bkmGVqWvrJ51t5OxV0v25NsOgR82CA
NXUgvhVYs7vNFN+jxtb2aj6Xg+/2G/BNxkaFspIVCzgWkxiz7XE4lgUwX44FCXZM
3+JeUbKjUzBRMB0GA1UdDgQWBBRhLw+/o3+Z02MI/d4tmaMui9W16jAfBgNVHSME
GDAWgBRhLw+/o3+Z02MI/d4tmaMui9W16jAPBgNVHRMBAf8EBTADAQH/MAoGCCqG
SM49BAMCA2kAMGYCMQDqLt2lm8mE+tGgtjDmtFgdOcI72HSbRQ74D5rYTzgST1rY
/8wTi5xl8TiFUyLMUsICMQC5ViVxdXbhuG7gX6yEqSkMKZICHpO8hqFwOD/uaFVI
dV4vKmHUzwK/eIx+8Ay3neE=
-----END CERTIFICATE-----`
httpsKey = `-----BEGIN EC PARAMETERS-----
BgUrgQQAIg==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDCfMNsN6miEE3rVyUPwElfiJSWaR5huPCzUenZOfJT04GAcQdWvEju3
UM2lmBLIXpGgBwYFK4EEACKhZANiAARCjRMqJ85rzMC998X5z761nJ+xL3bkmGVq
WvrJ51t5OxV0v25NsOgR82CANXUgvhVYs7vNFN+jxtb2aj6Xg+/2G/BNxkaFspIV
CzgWkxiz7XE4lgUwX44FCXZM3+JeUbI=
-----END EC PRIVATE KEY-----`
)
var (
defaultPerms = []string{dataprovider.PermAny}
homeBasePath string
backupsPath string
credentialsPath string
testServer *httptest.Server
providerDriverName string
)
func TestMain(m *testing.M) {
homeBasePath = os.TempDir()
logfilePath := filepath.Join(configDir, "sftpgo_api_test.log")
logger.InitLogger(logfilePath, 5, 1, 28, false, zerolog.DebugLevel)
config.LoadConfig(configDir, "")
providerConf := config.GetProviderConf()
credentialsPath = filepath.Join(os.TempDir(), "test_credentials")
providerConf.CredentialsPath = credentialsPath
providerDriverName = providerConf.Driver
os.RemoveAll(credentialsPath)
err := dataprovider.Initialize(providerConf, configDir)
if err != nil {
logger.Warn(logSender, "", "error initializing data provider: %v", err)
os.Exit(1)
}
dataProvider := dataprovider.GetProvider()
httpdConf := config.GetHTTPDConfig()
httpdConf.BindPort = 8081
httpd.SetBaseURLAndCredentials("http://127.0.0.1:8081", "", "")
backupsPath = filepath.Join(os.TempDir(), "test_backups")
httpdConf.BackupsPath = backupsPath
os.MkdirAll(backupsPath, 0777)
sftpd.SetDataProvider(dataProvider)
httpd.SetDataProvider(dataProvider)
go func() {
if err := httpdConf.Initialize(configDir); err != nil {
logger.Error(logSender, "", "could not start HTTP server: %v", err)
}
}()
waitTCPListening(fmt.Sprintf("%s:%d", httpdConf.BindAddress, httpdConf.BindPort))
// now start an https server
certPath := filepath.Join(os.TempDir(), "test.crt")
keyPath := filepath.Join(os.TempDir(), "test.key")
ioutil.WriteFile(certPath, []byte(httpsCert), 0666)
ioutil.WriteFile(keyPath, []byte(httpsKey), 0666)
httpdConf.BindPort = 8443
httpdConf.CertificateFile = certPath
httpdConf.CertificateKeyFile = keyPath
go func() {
if err := httpdConf.Initialize(configDir); err != nil {
logger.Error(logSender, "", "could not start HTTPS server: %v", err)
}
}()
waitTCPListening(fmt.Sprintf("%s:%d", httpdConf.BindAddress, httpdConf.BindPort))
httpd.ReloadTLSCertificate()
testServer = httptest.NewServer(httpd.GetHTTPRouter())
defer testServer.Close()
exitCode := m.Run()
os.Remove(logfilePath)
os.RemoveAll(backupsPath)
os.RemoveAll(credentialsPath)
os.Remove(certPath)
os.Remove(keyPath)
os.Exit(exitCode)
}
func TestInitialization(t *testing.T) {
config.LoadConfig(configDir, "")
httpdConf := config.GetHTTPDConfig()
httpdConf.BackupsPath = "test_backups"
httpdConf.AuthUserFile = "invalid file"
err := httpdConf.Initialize(configDir)
if err == nil {
t.Error("Inizialize must fail")
}
httpdConf.BackupsPath = backupsPath
httpdConf.AuthUserFile = ""
httpdConf.CertificateFile = "invalid file"
httpdConf.CertificateKeyFile = "invalid file"
err = httpdConf.Initialize(configDir)
if err == nil {
t.Error("Inizialize must fail")
}
httpdConf.CertificateFile = ""
httpdConf.CertificateKeyFile = ""
httpdConf.TemplatesPath = "."
err = httpdConf.Initialize(configDir)
if err == nil {
t.Error("Inizialize must fail")
}
}
func TestBasicUserHandling(t *testing.T) {
user, _, err := httpd.AddUser(getTestUser(), http.StatusOK)
if err != nil {
t.Errorf("unable to add user: %v", err)
}
user.MaxSessions = 10
user.QuotaSize = 4096
user.QuotaFiles = 2
user.UploadBandwidth = 128
user.DownloadBandwidth = 64
user.ExpirationDate = utils.GetTimeAsMsSinceEpoch(time.Now())
user, _, err = httpd.UpdateUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to update user: %v", err)
}
users, _, err := httpd.GetUsers(0, 0, defaultUsername, http.StatusOK)
if err != nil {
t.Errorf("unable to get users: %v", err)
}
if len(users) != 1 {
t.Errorf("number of users mismatch, expected: 1, actual: %v", len(users))
}
_, err = httpd.RemoveUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to remove: %v", err)
}
}
func TestUserStatus(t *testing.T) {
u := getTestUser()
u.Status = 3
_, _, err := httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with bad status: %v", err)
}
u.Status = 0
user, _, err := httpd.AddUser(u, http.StatusOK)
if err != nil {
t.Errorf("unable to add user: %v", err)
}
user.Status = 2
_, _, err = httpd.UpdateUser(user, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error updating user with bad status: %v", err)
}
user.Status = 1
user, _, err = httpd.UpdateUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to update user: %v", err)
}
_, err = httpd.RemoveUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to remove: %v", err)
}
}
func TestAddUserNoCredentials(t *testing.T) {
u := getTestUser()
u.Password = ""
u.PublicKeys = []string{}
_, _, err := httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with no credentials: %v", err)
}
}
func TestAddUserNoUsername(t *testing.T) {
u := getTestUser()
u.Username = ""
_, _, err := httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with no home dir: %v", err)
}
}
func TestAddUserNoHomeDir(t *testing.T) {
u := getTestUser()
u.HomeDir = ""
_, _, err := httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with no home dir: %v", err)
}
}
func TestAddUserInvalidHomeDir(t *testing.T) {
u := getTestUser()
u.HomeDir = "relative_path"
_, _, err := httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid home dir: %v", err)
}
}
func TestAddUserNoPerms(t *testing.T) {
u := getTestUser()
u.Permissions = make(map[string][]string)
_, _, err := httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with no perms: %v", err)
}
u.Permissions["/"] = []string{}
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with no perms: %v", err)
}
}
func TestAddUserInvalidPerms(t *testing.T) {
u := getTestUser()
u.Permissions["/"] = []string{"invalidPerm"}
_, _, err := httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid perms: %v", err)
}
// permissions for root dir are mandatory
u.Permissions["/"] = []string{}
u.Permissions["/somedir"] = []string{dataprovider.PermAny}
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with no root dir perms: %v", err)
}
u.Permissions["/"] = []string{dataprovider.PermAny}
u.Permissions["/subdir/.."] = []string{dataprovider.PermAny}
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid dir perms: %v", err)
}
}
func TestAddUserInvalidFilters(t *testing.T) {
u := getTestUser()
u.Filters.AllowedIP = []string{"192.168.1.0/24", "192.168.2.0"}
_, _, err := httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid filters: %v", err)
}
u.Filters.AllowedIP = []string{}
u.Filters.DeniedIP = []string{"192.168.3.0/16", "invalid"}
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid filters: %v", err)
}
u.Filters.DeniedIP = []string{}
u.Filters.DeniedLoginMethods = []string{"invalid"}
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid filters: %v", err)
}
u.Filters.DeniedLoginMethods = []string{dataprovider.SSHLoginMethodKeyboardInteractive,
dataprovider.SSHLoginMethodPassword, dataprovider.SSHLoginMethodPublicKey}
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid filters: %v", err)
}
u.Filters.DeniedLoginMethods = []string{}
u.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
{
Path: "relative",
AllowedExtensions: []string{},
DeniedExtensions: []string{},
},
}
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid extensions filters: %v", err)
}
u.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
{
Path: "/",
AllowedExtensions: []string{},
DeniedExtensions: []string{},
},
}
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid extensions filters: %v", err)
}
u.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
{
Path: "/subdir",
AllowedExtensions: []string{".zip"},
DeniedExtensions: []string{},
},
{
Path: "/subdir",
AllowedExtensions: []string{".rar"},
DeniedExtensions: []string{".jpg"},
},
}
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid extensions filters: %v", err)
}
}
func TestAddUserInvalidFsConfig(t *testing.T) {
u := getTestUser()
u.FsConfig.Provider = 1
u.FsConfig.S3Config.Bucket = ""
_, _, err := httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid fs config: %v", err)
}
os.RemoveAll(credentialsPath)
os.MkdirAll(credentialsPath, 0700)
u.FsConfig.S3Config.Bucket = "test"
u.FsConfig.S3Config.Region = "eu-west-1"
u.FsConfig.S3Config.AccessKey = "access-key"
u.FsConfig.S3Config.AccessSecret = "access-secret"
u.FsConfig.S3Config.Endpoint = "http://127.0.0.1:9000/path?a=b"
u.FsConfig.S3Config.StorageClass = "Standard"
u.FsConfig.S3Config.KeyPrefix = "/somedir/subdir/"
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid fs config: %v", err)
}
u.FsConfig.S3Config.KeyPrefix = ""
u.FsConfig.S3Config.UploadPartSize = 3
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid fs config: %v", err)
}
u.FsConfig.S3Config.UploadPartSize = 0
u.FsConfig.S3Config.UploadConcurrency = -1
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid fs config: %v", err)
}
u = getTestUser()
u.FsConfig.Provider = 2
u.FsConfig.GCSConfig.Bucket = ""
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid fs config: %v", err)
}
u.FsConfig.GCSConfig.Bucket = "test"
u.FsConfig.GCSConfig.StorageClass = "Standard"
u.FsConfig.GCSConfig.KeyPrefix = "/somedir/subdir/"
u.FsConfig.GCSConfig.Credentials = base64.StdEncoding.EncodeToString([]byte("test"))
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid fs config: %v", err)
}
u.FsConfig.GCSConfig.KeyPrefix = "somedir/subdir/"
u.FsConfig.GCSConfig.Credentials = ""
u.FsConfig.GCSConfig.AutomaticCredentials = 0
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid fs config: %v", err)
}
u.FsConfig.GCSConfig.Credentials = "no base64 encoded"
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid fs config: %v", err)
}
}
func TestAddUserInvalidVirtualFolders(t *testing.T) {
u := getTestUser()
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "vdir",
MappedPath: filepath.Join(os.TempDir(), "mapped_dir"),
})
_, _, err := httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid virtual folder: %v", err)
}
u.VirtualFolders = nil
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/",
MappedPath: filepath.Join(os.TempDir(), "mapped_dir"),
})
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid virtual folder: %v", err)
}
u.VirtualFolders = nil
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir",
MappedPath: filepath.Join(u.GetHomeDir(), "mapped_dir"),
})
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid virtual folder: %v", err)
}
u.VirtualFolders = nil
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir",
MappedPath: u.GetHomeDir(),
})
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid virtual folder: %v", err)
}
u.VirtualFolders = nil
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir",
MappedPath: filepath.Join(u.GetHomeDir(), ".."),
})
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid virtual folder: %v", err)
}
u.VirtualFolders = nil
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir",
MappedPath: filepath.Join(os.TempDir(), "mapped_dir"),
})
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir",
MappedPath: filepath.Join(os.TempDir(), "mapped_dir1"),
})
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid virtual folder: %v", err)
}
u.VirtualFolders = nil
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir1",
MappedPath: filepath.Join(os.TempDir(), "mapped_dir"),
})
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir2",
MappedPath: filepath.Join(os.TempDir(), "mapped_dir"),
})
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid virtual folder: %v", err)
}
u.VirtualFolders = nil
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir1",
MappedPath: filepath.Join(os.TempDir(), "mapped_dir", "subdir"),
})
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir2",
MappedPath: filepath.Join(os.TempDir(), "mapped_dir"),
})
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid virtual folder: %v", err)
}
u.VirtualFolders = nil
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir1",
MappedPath: filepath.Join(os.TempDir(), "mapped_dir"),
})
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir2",
MappedPath: filepath.Join(os.TempDir(), "mapped_dir", "subdir"),
})
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid virtual folder: %v", err)
}
u.VirtualFolders = nil
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir1/subdir",
MappedPath: filepath.Join(os.TempDir(), "mapped_dir1"),
})
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir1/../vdir1",
MappedPath: filepath.Join(os.TempDir(), "mapped_dir2"),
})
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid virtual folder: %v", err)
}
u.VirtualFolders = nil
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir1/",
MappedPath: filepath.Join(os.TempDir(), "mapped_dir1"),
})
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir1/subdir",
MappedPath: filepath.Join(os.TempDir(), "mapped_dir2"),
})
_, _, err = httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid virtual folder: %v", err)
}
}
func TestUserPublicKey(t *testing.T) {
u := getTestUser()
invalidPubKey := "invalid"
validPubKey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC03jj0D+djk7pxIf/0OhrxrchJTRZklofJ1NoIu4752Sq02mdXmarMVsqJ1cAjV5LBVy3D1F5U6XW4rppkXeVtd04Pxb09ehtH0pRRPaoHHlALiJt8CoMpbKYMA8b3KXPPriGxgGomvtU2T2RMURSwOZbMtpsugfjYSWenyYX+VORYhylWnSXL961LTyC21ehd6d6QnW9G7E5hYMITMY9TuQZz3bROYzXiTsgN0+g6Hn7exFQp50p45StUMfV/SftCMdCxlxuyGny2CrN/vfjO7xxOo2uv7q1qm10Q46KPWJQv+pgZ/OfL+EDjy07n5QVSKHlbx+2nT4Q0EgOSQaCTYwn3YjtABfIxWwgAFdyj6YlPulCL22qU4MYhDcA6PSBwDdf8hvxBfvsiHdM+JcSHvv8/VeJhk6CmnZxGY0fxBupov27z3yEO8nAg8k+6PaUiW1MSUfuGMF/ktB8LOstXsEPXSszuyXiOv4DaryOXUiSn7bmRqKcEFlJusO6aZP0= nicola@p1"
u.PublicKeys = []string{invalidPubKey}
_, _, err := httpd.AddUser(u, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error adding user with invalid pub key: %v", err)
}
u.PublicKeys = []string{validPubKey}
user, _, err := httpd.AddUser(u, http.StatusOK)
if err != nil {
t.Errorf("unable to add user: %v", err)
}
user.PublicKeys = []string{validPubKey, invalidPubKey}
_, _, err = httpd.UpdateUser(user, http.StatusBadRequest)
if err != nil {
t.Errorf("update user with invalid public key must fail: %v", err)
}
user.PublicKeys = []string{validPubKey, validPubKey, validPubKey}
_, _, err = httpd.UpdateUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to update user: %v", err)
}
_, err = httpd.RemoveUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to remove: %v", err)
}
}
func TestUpdateUser(t *testing.T) {
user, _, err := httpd.AddUser(getTestUser(), http.StatusOK)
if err != nil {
t.Errorf("unable to add user: %v", err)
}
user.HomeDir = filepath.Join(homeBasePath, "testmod")
user.UID = 33
user.GID = 101
user.MaxSessions = 10
user.QuotaSize = 4096
user.QuotaFiles = 2
user.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermDelete, dataprovider.PermDownload}
user.Permissions["/subdir"] = []string{dataprovider.PermListItems, dataprovider.PermUpload}
user.Filters.AllowedIP = []string{"192.168.1.0/24", "192.168.2.0/24"}
user.Filters.DeniedIP = []string{"192.168.3.0/24", "192.168.4.0/24"}
user.Filters.DeniedLoginMethods = []string{dataprovider.SSHLoginMethodPassword}
user.Filters.FileExtensions = append(user.Filters.FileExtensions, dataprovider.ExtensionsFilter{
Path: "/subdir",
AllowedExtensions: []string{".zip", ".rar"},
DeniedExtensions: []string{".jpg", ".png"},
})
user.UploadBandwidth = 1024
user.DownloadBandwidth = 512
user.VirtualFolders = nil
user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir1",
MappedPath: filepath.Join(os.TempDir(), "mapped_dir1"),
})
user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
VirtualPath: "/vdir12/subdir",
MappedPath: filepath.Join(os.TempDir(), "mapped_dir2"),
})
user, _, err = httpd.UpdateUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to update user: %v", err)
}
user.Permissions["/subdir"] = []string{}
user, _, err = httpd.UpdateUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to update user: %v", err)
}
if len(user.Permissions["/subdir"]) > 0 {
t.Errorf("unexpected subdir permissions, must be empty")
}
_, err = httpd.RemoveUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to remove: %v", err)
}
}
func TestUserS3Config(t *testing.T) {
user, _, err := httpd.AddUser(getTestUser(), http.StatusOK)
if err != nil {
t.Errorf("unable to add user: %v", err)
}
user.FsConfig.Provider = 1
user.FsConfig.S3Config.Bucket = "test"
user.FsConfig.S3Config.Region = "us-east-1"
user.FsConfig.S3Config.AccessKey = "Server-Access-Key"
user.FsConfig.S3Config.AccessSecret = "Server-Access-Secret"
user.FsConfig.S3Config.Endpoint = "http://127.0.0.1:9000"
user.FsConfig.S3Config.UploadPartSize = 8
user, _, err = httpd.UpdateUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to update user: %v", err)
}
_, err = httpd.RemoveUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to remove: %v", err)
}
user.Password = defaultPassword
user.ID = 0
secret, _ := utils.EncryptData("Server-Access-Secret")
user.FsConfig.S3Config.AccessSecret = secret
user, _, err = httpd.AddUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to add user: %v", err)
}
user.FsConfig.Provider = 1
user.FsConfig.S3Config.Bucket = "test1"
user.FsConfig.S3Config.Region = "us-east-1"
user.FsConfig.S3Config.AccessKey = "Server-Access-Key1"
user.FsConfig.S3Config.Endpoint = "http://localhost:9000"
user.FsConfig.S3Config.KeyPrefix = "somedir/subdir"
user.FsConfig.S3Config.UploadConcurrency = 5
user, _, err = httpd.UpdateUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to update user: %v", err)
}
user.FsConfig.Provider = 0
user.FsConfig.S3Config.Bucket = ""
user.FsConfig.S3Config.Region = ""
user.FsConfig.S3Config.AccessKey = ""
user.FsConfig.S3Config.AccessSecret = ""
user.FsConfig.S3Config.Endpoint = ""
user.FsConfig.S3Config.KeyPrefix = ""
user.FsConfig.S3Config.UploadPartSize = 0
user.FsConfig.S3Config.UploadConcurrency = 0
user, _, err = httpd.UpdateUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to update user: %v", err)
}
// test user without access key and access secret (shared config state)
user.FsConfig.Provider = 1
user.FsConfig.S3Config.Bucket = "test1"
user.FsConfig.S3Config.Region = "us-east-1"
user.FsConfig.S3Config.AccessKey = ""
user.FsConfig.S3Config.AccessSecret = ""
user.FsConfig.S3Config.Endpoint = ""
user.FsConfig.S3Config.KeyPrefix = "somedir/subdir"
user.FsConfig.S3Config.UploadPartSize = 6
user.FsConfig.S3Config.UploadConcurrency = 4
user, _, err = httpd.UpdateUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to update user: %v", err)
}
_, err = httpd.RemoveUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to remove: %v", err)
}
}
func TestUserGCSConfig(t *testing.T) {
user, _, err := httpd.AddUser(getTestUser(), http.StatusOK)
if err != nil {
t.Errorf("unable to add user: %v", err)
}
os.RemoveAll(credentialsPath)
os.MkdirAll(credentialsPath, 0700)
user.FsConfig.Provider = 2
user.FsConfig.GCSConfig.Bucket = "test"
user.FsConfig.GCSConfig.Credentials = base64.StdEncoding.EncodeToString([]byte("fake credentials"))
user, _, err = httpd.UpdateUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to update user: %v", err)
}
_, err = httpd.RemoveUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to remove: %v", err)
}
user.Password = defaultPassword
user.ID = 0
// the user will be added since the credentials file is found
user, _, err = httpd.AddUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to add user: %v", err)
}
os.RemoveAll(credentialsPath)
os.MkdirAll(credentialsPath, 0700)
user.FsConfig.GCSConfig.Credentials = ""
user.FsConfig.GCSConfig.AutomaticCredentials = 1
user, _, err = httpd.UpdateUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to update user: %v", err)
}
user.FsConfig.Provider = 1
user.FsConfig.S3Config.Bucket = "test1"
user.FsConfig.S3Config.Region = "us-east-1"
user.FsConfig.S3Config.AccessKey = "Server-Access-Key1"
user.FsConfig.S3Config.AccessSecret = "secret"
user.FsConfig.S3Config.Endpoint = "http://localhost:9000"
user.FsConfig.S3Config.KeyPrefix = "somedir/subdir"
user, _, err = httpd.UpdateUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to update user: %v", err)
}
user.FsConfig.Provider = 2
user.FsConfig.GCSConfig.Bucket = "test1"
user.FsConfig.GCSConfig.Credentials = base64.StdEncoding.EncodeToString([]byte("fake credentials"))
user, _, err = httpd.UpdateUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to update user: %v", err)
}
_, err = httpd.RemoveUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to remove: %v", err)
}
}
func TestUpdateUserNoCredentials(t *testing.T) {
user, _, err := httpd.AddUser(getTestUser(), http.StatusOK)
if err != nil {
t.Errorf("unable to add user: %v", err)
}
user.Password = ""
user.PublicKeys = []string{}
// password and public key will be omitted from json serialization if empty and so they will remain unchanged
// and no validation error will be raised
_, _, err = httpd.UpdateUser(user, http.StatusOK)
if err != nil {
t.Errorf("unexpected error updating user with no credentials: %v", err)
}
_, err = httpd.RemoveUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to remove: %v", err)
}
}
func TestUpdateUserEmptyHomeDir(t *testing.T) {
user, _, err := httpd.AddUser(getTestUser(), http.StatusOK)
if err != nil {
t.Errorf("unable to add user: %v", err)
}
user.HomeDir = ""
_, _, err = httpd.UpdateUser(user, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error updating user with empty home dir: %v", err)
}
_, err = httpd.RemoveUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to remove: %v", err)
}
}
func TestUpdateUserInvalidHomeDir(t *testing.T) {
user, _, err := httpd.AddUser(getTestUser(), http.StatusOK)
if err != nil {
t.Errorf("unable to add user: %v", err)
}
user.HomeDir = "relative_path"
_, _, err = httpd.UpdateUser(user, http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error updating user with empty home dir: %v", err)
}
_, err = httpd.RemoveUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to remove: %v", err)
}
}
func TestUpdateNonExistentUser(t *testing.T) {
_, _, err := httpd.UpdateUser(getTestUser(), http.StatusNotFound)
if err != nil {
t.Errorf("unable to update user: %v", err)
}
}
func TestGetNonExistentUser(t *testing.T) {
_, _, err := httpd.GetUserByID(0, http.StatusNotFound)
if err != nil {
t.Errorf("unable to get user: %v", err)
}
}
func TestDeleteNonExistentUser(t *testing.T) {
_, err := httpd.RemoveUser(getTestUser(), http.StatusNotFound)
if err != nil {
t.Errorf("unable to remove user: %v", err)
}
}
func TestAddDuplicateUser(t *testing.T) {
user, _, err := httpd.AddUser(getTestUser(), http.StatusOK)
if err != nil {
t.Errorf("unable to add user: %v", err)
}
_, _, err = httpd.AddUser(getTestUser(), http.StatusInternalServerError)
if err != nil {
t.Errorf("unable to add second user: %v", err)
}
_, _, err = httpd.AddUser(getTestUser(), http.StatusOK)
if err == nil {
t.Errorf("adding a duplicate user must fail")
}
_, err = httpd.RemoveUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to remove user: %v", err)
}
}
func TestGetUsers(t *testing.T) {
user1, _, err := httpd.AddUser(getTestUser(), http.StatusOK)
if err != nil {
t.Errorf("unable to add user: %v", err)
}
u := getTestUser()
u.Username = defaultUsername + "1"
user2, _, err := httpd.AddUser(u, http.StatusOK)
if err != nil {
t.Errorf("unable to add second user: %v", err)
}
users, _, err := httpd.GetUsers(0, 0, "", http.StatusOK)
if err != nil {
t.Errorf("unable to get users: %v", err)
}
if len(users) < 2 {
t.Errorf("at least 2 users are expected")
}
users, _, err = httpd.GetUsers(1, 0, "", http.StatusOK)
if err != nil {
t.Errorf("unable to get users: %v", err)
}
if len(users) != 1 {
t.Errorf("1 user is expected")
}
users, _, err = httpd.GetUsers(1, 1, "", http.StatusOK)
if err != nil {
t.Errorf("unable to get users: %v", err)
}
if len(users) != 1 {
t.Errorf("1 user is expected")
}
_, _, err = httpd.GetUsers(1, 1, "", http.StatusInternalServerError)
if err == nil {
t.Errorf("get users must succeed, we requested a fail for a good request")
}
_, err = httpd.RemoveUser(user1, http.StatusOK)
if err != nil {
t.Errorf("unable to remove user: %v", err)
}
_, err = httpd.RemoveUser(user2, http.StatusOK)
if err != nil {
t.Errorf("unable to remove user: %v", err)
}
}
func TestGetQuotaScans(t *testing.T) {
_, _, err := httpd.GetQuotaScans(http.StatusOK)
if err != nil {
t.Errorf("unable to get quota scans: %v", err)
}
_, _, err = httpd.GetQuotaScans(http.StatusInternalServerError)
if err == nil {
t.Errorf("quota scan request must succeed, we requested to check a wrong status code")
}
}
func TestStartQuotaScan(t *testing.T) {
user, _, err := httpd.AddUser(getTestUser(), http.StatusOK)
if err != nil {
t.Errorf("unable to add user: %v", err)
}
_, err = httpd.StartQuotaScan(user, http.StatusCreated)
if err != nil {
t.Errorf("unable to start quota scan: %v", err)
}
_, err = httpd.RemoveUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to remove user: %v", err)
}
}
func TestGetVersion(t *testing.T) {
_, _, err := httpd.GetVersion(http.StatusOK)
if err != nil {
t.Errorf("unable to get version: %v", err)
}
_, _, err = httpd.GetVersion(http.StatusInternalServerError)
if err == nil {
t.Errorf("get version request must succeed, we requested to check a wrong status code")
}
}
func TestGetProviderStatus(t *testing.T) {
_, _, err := httpd.GetProviderStatus(http.StatusOK)
if err != nil {
t.Errorf("unable to get provider status: %v", err)
}
_, _, err = httpd.GetProviderStatus(http.StatusBadRequest)
if err == nil {
t.Errorf("get provider status request must succeed, we requested to check a wrong status code")
}
}
func TestGetConnections(t *testing.T) {
_, _, err := httpd.GetConnections(http.StatusOK)
if err != nil {
t.Errorf("unable to get sftp connections: %v", err)
}
_, _, err = httpd.GetConnections(http.StatusInternalServerError)
if err == nil {
t.Errorf("get sftp connections request must succeed, we requested to check a wrong status code")
}
}
func TestCloseActiveConnection(t *testing.T) {
_, err := httpd.CloseConnection("non_existent_id", http.StatusNotFound)
if err != nil {
t.Errorf("unexpected error closing non existent sftp connection: %v", err)
}
}
func TestUserBaseDir(t *testing.T) {
dataProvider := dataprovider.GetProvider()
dataprovider.Close(dataProvider)
config.LoadConfig(configDir, "")
providerConf := config.GetProviderConf()
providerConf.UsersBaseDir = homeBasePath
err := dataprovider.Initialize(providerConf, configDir)
if err != nil {
t.Errorf("error initializing data provider with users base dir")
}
httpd.SetDataProvider(dataprovider.GetProvider())
u := getTestUser()
u.HomeDir = ""
user, _, err := httpd.AddUser(getTestUser(), http.StatusOK)
if err != nil {
t.Errorf("unable to add user: %v", err)
}
if user.HomeDir != filepath.Join(providerConf.UsersBaseDir, u.Username) {
t.Errorf("invalid home dir: %v", user.HomeDir)
}
_, err = httpd.RemoveUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to remove: %v", err)
}
dataProvider = dataprovider.GetProvider()
dataprovider.Close(dataProvider)
config.LoadConfig(configDir, "")
providerConf = config.GetProviderConf()
providerConf.CredentialsPath = credentialsPath
os.RemoveAll(credentialsPath)
err = dataprovider.Initialize(providerConf, configDir)
if err != nil {
t.Errorf("error initializing data provider")
}
httpd.SetDataProvider(dataprovider.GetProvider())
sftpd.SetDataProvider(dataprovider.GetProvider())
}
func TestProviderErrors(t *testing.T) {
if providerDriverName == dataprovider.BoltDataProviderName {
t.Skip("skipping test provider errors for bolt provider")
}
dataProvider := dataprovider.GetProvider()
dataprovider.Close(dataProvider)
_, _, err := httpd.GetUserByID(0, http.StatusInternalServerError)
if err != nil {
t.Errorf("get user with provider closed must fail: %v", err)
}
_, _, err = httpd.GetUsers(1, 0, defaultUsername, http.StatusInternalServerError)
if err != nil {
t.Errorf("get users with provider closed must fail: %v", err)
}
_, _, err = httpd.UpdateUser(dataprovider.User{}, http.StatusInternalServerError)
if err != nil {
t.Errorf("update user with provider closed must fail: %v", err)
}
_, err = httpd.RemoveUser(dataprovider.User{}, http.StatusInternalServerError)
if err != nil {
t.Errorf("delete user with provider closed must fail: %v", err)
}
_, _, err = httpd.GetProviderStatus(http.StatusInternalServerError)
if err != nil {
t.Errorf("get provider status with provider closed must fail: %v", err)
}
_, _, err = httpd.Dumpdata("backup.json", "", http.StatusInternalServerError)
if err != nil {
t.Errorf("get provider status with provider closed must fail: %v", err)
}
user := getTestUser()
user.ID = 1
backupData := dataprovider.BackupData{}
backupData.Users = append(backupData.Users, user)
backupContent, _ := json.Marshal(backupData)
backupFilePath := filepath.Join(backupsPath, "backup.json")
ioutil.WriteFile(backupFilePath, backupContent, 0666)
_, _, err = httpd.Loaddata(backupFilePath, "", "", http.StatusInternalServerError)
if err != nil {
t.Errorf("get provider status with provider closed must fail: %v", err)
}
os.Remove(backupFilePath)
config.LoadConfig(configDir, "")
providerConf := config.GetProviderConf()
providerConf.CredentialsPath = credentialsPath
os.RemoveAll(credentialsPath)
err = dataprovider.Initialize(providerConf, configDir)
if err != nil {
t.Errorf("error initializing data provider")
}
httpd.SetDataProvider(dataprovider.GetProvider())
sftpd.SetDataProvider(dataprovider.GetProvider())
}
func TestDumpdata(t *testing.T) {
dataProvider := dataprovider.GetProvider()
dataprovider.Close(dataProvider)
config.LoadConfig(configDir, "")
providerConf := config.GetProviderConf()
err := dataprovider.Initialize(providerConf, configDir)
if err != nil {
t.Errorf("error initializing data provider")
}
httpd.SetDataProvider(dataprovider.GetProvider())
sftpd.SetDataProvider(dataprovider.GetProvider())
_, _, err = httpd.Dumpdata("", "", http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
_, _, err = httpd.Dumpdata(filepath.Join(backupsPath, "backup.json"), "", http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
_, _, err = httpd.Dumpdata("../backup.json", "", http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
_, _, err = httpd.Dumpdata("backup.json", "0", http.StatusOK)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
_, _, err = httpd.Dumpdata("backup.json", "1", http.StatusOK)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
os.Remove(filepath.Join(backupsPath, "backup.json"))
if runtime.GOOS != "windows" {
os.Chmod(backupsPath, 0001)
_, _, err = httpd.Dumpdata("bck.json", "", http.StatusInternalServerError)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
os.Chmod(backupsPath, 0755)
}
providerConf = config.GetProviderConf()
providerConf.CredentialsPath = credentialsPath
os.RemoveAll(credentialsPath)
err = dataprovider.Initialize(providerConf, configDir)
if err != nil {
t.Errorf("error initializing data provider: %v", err)
}
httpd.SetDataProvider(dataprovider.GetProvider())
sftpd.SetDataProvider(dataprovider.GetProvider())
}
func TestLoaddata(t *testing.T) {
user := getTestUser()
user.ID = 1
user.Username = "test_user_restore"
backupData := dataprovider.BackupData{}
backupData.Users = append(backupData.Users, user)
backupContent, _ := json.Marshal(backupData)
backupFilePath := filepath.Join(backupsPath, "backup.json")
ioutil.WriteFile(backupFilePath, backupContent, 0666)
_, _, err := httpd.Loaddata(backupFilePath, "a", "", http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
_, _, err = httpd.Loaddata(backupFilePath, "", "a", http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
_, _, err = httpd.Loaddata("backup.json", "1", "", http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
_, _, err = httpd.Loaddata(backupFilePath+"a", "1", "", http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if runtime.GOOS != "windows" {
os.Chmod(backupFilePath, 0111)
_, _, err = httpd.Loaddata(backupFilePath, "1", "", http.StatusInternalServerError)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
os.Chmod(backupFilePath, 0644)
}
// add user from backup
_, _, err = httpd.Loaddata(backupFilePath, "1", "", http.StatusOK)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
// update user from backup
_, _, err = httpd.Loaddata(backupFilePath, "2", "", http.StatusOK)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
users, _, err := httpd.GetUsers(1, 0, user.Username, http.StatusOK)
if err != nil {
t.Errorf("unable to get users: %v", err)
}
if len(users) != 1 {
t.Error("Unable to get restored user")
}
user = users[0]
_, err = httpd.RemoveUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to remove user: %v", err)
}
os.Remove(backupFilePath)
createTestFile(backupFilePath, 10485761)
_, _, err = httpd.Loaddata(backupFilePath, "1", "0", http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
os.Remove(backupFilePath)
createTestFile(backupFilePath, 65535)
_, _, err = httpd.Loaddata(backupFilePath, "1", "0", http.StatusBadRequest)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
os.Remove(backupFilePath)
}
func TestLoaddataMode(t *testing.T) {
user := getTestUser()
user.ID = 1
user.Username = "test_user_restore"
backupData := dataprovider.BackupData{}
backupData.Users = append(backupData.Users, user)
backupContent, _ := json.Marshal(backupData)
backupFilePath := filepath.Join(backupsPath, "backup.json")
ioutil.WriteFile(backupFilePath, backupContent, 0666)
_, _, err := httpd.Loaddata(backupFilePath, "0", "0", http.StatusOK)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
users, _, err := httpd.GetUsers(1, 0, user.Username, http.StatusOK)
if err != nil {
t.Errorf("unable to get users: %v", err)
}
if len(users) != 1 {
t.Error("Unable to get restored user")
}
user = users[0]
oldUploadBandwidth := user.UploadBandwidth
user.UploadBandwidth = oldUploadBandwidth + 128
user, _, err = httpd.UpdateUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to update user: %v", err)
}
_, _, err = httpd.Loaddata(backupFilePath, "0", "1", http.StatusOK)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
users, _, err = httpd.GetUsers(1, 0, user.Username, http.StatusOK)
if err != nil {
t.Errorf("unable to get users: %v", err)
}
if len(users) != 1 {
t.Error("Unable to get restored user")
}
user = users[0]
if user.UploadBandwidth == oldUploadBandwidth {
t.Error("user must not be modified")
}
_, err = httpd.RemoveUser(user, http.StatusOK)
if err != nil {
t.Errorf("unable to remove user: %v", err)
}
os.Remove(backupFilePath)
}
func TestHTTPSConnection(t *testing.T) {
client := &http.Client{
Timeout: 5 * time.Second,
}
_, err := client.Get("https://localhost:8443" + metricsPath)
if err == nil || (!strings.Contains(err.Error(), "certificate is not valid") &&
!strings.Contains(err.Error(), "certificate signed by unknown authority")) {
t.Errorf("unexpected error: %v", err)
}
}
// test using mock http server
func TestBasicUserHandlingMock(t *testing.T) {
user := getTestUser()
userAsJSON := getUserAsJSON(t, user)
req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err := render.DecodeJSON(rr.Body, &user)
if err != nil {
t.Errorf("Error get user: %v", err)
}
req, _ = http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusInternalServerError, rr.Code)
user.MaxSessions = 10
user.UploadBandwidth = 128
user.Permissions["/"] = []string{dataprovider.PermAny, dataprovider.PermDelete, dataprovider.PermDownload}
userAsJSON = getUserAsJSON(t, user)
req, _ = http.NewRequest(http.MethodPut, userPath+"/"+strconv.FormatInt(user.ID, 10), bytes.NewBuffer(userAsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
req, _ = http.NewRequest(http.MethodGet, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
var updatedUser dataprovider.User
err = render.DecodeJSON(rr.Body, &updatedUser)
if err != nil {
t.Errorf("Error decoding updated user: %v", err)
}
if user.MaxSessions != updatedUser.MaxSessions || user.UploadBandwidth != updatedUser.UploadBandwidth {
t.Errorf("Error modifying user actual: %v, %v", updatedUser.MaxSessions, updatedUser.UploadBandwidth)
}
if len(updatedUser.Permissions["/"]) != 1 {
t.Errorf("permissions other than any should be removed")
}
if !utils.IsStringInSlice(dataprovider.PermAny, updatedUser.Permissions["/"]) {
t.Errorf("permissions mismatch")
}
req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestGetUserByIdInvalidParamsMock(t *testing.T) {
req, _ := http.NewRequest(http.MethodGet, userPath+"/0", nil)
rr := executeRequest(req)
checkResponseCode(t, http.StatusNotFound, rr.Code)
req, _ = http.NewRequest(http.MethodGet, userPath+"/a", nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
}
func TestAddUserNoUsernameMock(t *testing.T) {
user := getTestUser()
user.Username = ""
userAsJSON := getUserAsJSON(t, user)
req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr := executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
}
func TestAddUserInvalidHomeDirMock(t *testing.T) {
user := getTestUser()
user.HomeDir = "relative_path"
userAsJSON := getUserAsJSON(t, user)
req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr := executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
}
func TestAddUserInvalidPermsMock(t *testing.T) {
user := getTestUser()
user.Permissions["/"] = []string{}
userAsJSON := getUserAsJSON(t, user)
req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr := executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
}
func TestAddUserInvalidJsonMock(t *testing.T) {
req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer([]byte("invalid json")))
rr := executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
}
func TestUpdateUserMock(t *testing.T) {
user := getTestUser()
userAsJSON := getUserAsJSON(t, user)
req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err := render.DecodeJSON(rr.Body, &user)
if err != nil {
t.Errorf("Error get user: %v", err)
}
// permissions should not change if empty or nil
permissions := user.Permissions
user.Permissions = make(map[string][]string)
userAsJSON = getUserAsJSON(t, user)
req, _ = http.NewRequest(http.MethodPut, userPath+"/"+strconv.FormatInt(user.ID, 10), bytes.NewBuffer(userAsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
req, _ = http.NewRequest(http.MethodGet, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
var updatedUser dataprovider.User
err = render.DecodeJSON(rr.Body, &updatedUser)
if err != nil {
t.Errorf("Error decoding updated user: %v", err)
}
for dir, perms := range permissions {
if actualPerms, ok := updatedUser.Permissions[dir]; ok {
for _, v := range actualPerms {
if !utils.IsStringInSlice(v, perms) {
t.Error("Permissions contents mismatch")
}
}
} else {
t.Error("Permissions directories mismatch")
}
}
req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestUserPermissionsMock(t *testing.T) {
user := getTestUser()
user.Permissions = make(map[string][]string)
user.Permissions["/somedir"] = []string{dataprovider.PermAny}
userAsJSON := getUserAsJSON(t, user)
req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr := executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
user.Permissions = make(map[string][]string)
user.Permissions["/"] = []string{dataprovider.PermAny}
user.Permissions[".."] = []string{dataprovider.PermAny}
userAsJSON = getUserAsJSON(t, user)
req, _ = http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
user.Permissions = make(map[string][]string)
user.Permissions["/"] = []string{dataprovider.PermAny}
userAsJSON = getUserAsJSON(t, user)
req, _ = http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err := render.DecodeJSON(rr.Body, &user)
if err != nil {
t.Errorf("Error get user: %v", err)
}
user.Permissions["/somedir"] = []string{"invalid"}
userAsJSON = getUserAsJSON(t, user)
req, _ = http.NewRequest(http.MethodPut, userPath+"/"+strconv.FormatInt(user.ID, 10), bytes.NewBuffer(userAsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
delete(user.Permissions, "/somedir")
user.Permissions["/somedir/.."] = []string{dataprovider.PermAny}
userAsJSON = getUserAsJSON(t, user)
req, _ = http.NewRequest(http.MethodPut, userPath+"/"+strconv.FormatInt(user.ID, 10), bytes.NewBuffer(userAsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
delete(user.Permissions, "/somedir/..")
user.Permissions["not_abs_path"] = []string{dataprovider.PermAny}
userAsJSON = getUserAsJSON(t, user)
req, _ = http.NewRequest(http.MethodPut, userPath+"/"+strconv.FormatInt(user.ID, 10), bytes.NewBuffer(userAsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
delete(user.Permissions, "not_abs_path")
user.Permissions["/somedir/../otherdir/"] = []string{dataprovider.PermListItems}
userAsJSON = getUserAsJSON(t, user)
req, _ = http.NewRequest(http.MethodPut, userPath+"/"+strconv.FormatInt(user.ID, 10), bytes.NewBuffer(userAsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
req, _ = http.NewRequest(http.MethodGet, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
var updatedUser dataprovider.User
err = render.DecodeJSON(rr.Body, &updatedUser)
if err != nil {
t.Errorf("Error decoding updated user: %v", err)
}
if val, ok := updatedUser.Permissions["/otherdir"]; ok {
if !utils.IsStringInSlice(dataprovider.PermListItems, val) {
t.Error("expected permission list not found")
}
if len(val) != 1 {
t.Errorf("Unexpected number of permissions, expected 1, actual: %v", len(val))
}
} else {
t.Errorf("expected dir not found in permissions")
}
req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestUpdateUserInvalidJsonMock(t *testing.T) {
user := getTestUser()
userAsJSON := getUserAsJSON(t, user)
req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err := render.DecodeJSON(rr.Body, &user)
if err != nil {
t.Errorf("Error get user: %v", err)
}
req, _ = http.NewRequest(http.MethodPut, userPath+"/"+strconv.FormatInt(user.ID, 10), bytes.NewBuffer([]byte("Invalid json")))
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestUpdateUserInvalidParamsMock(t *testing.T) {
user := getTestUser()
userAsJSON := getUserAsJSON(t, user)
req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err := render.DecodeJSON(rr.Body, &user)
if err != nil {
t.Errorf("Error get user: %v", err)
}
user.HomeDir = ""
userAsJSON = getUserAsJSON(t, user)
req, _ = http.NewRequest(http.MethodPut, userPath+"/"+strconv.FormatInt(user.ID, 10), bytes.NewBuffer(userAsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
userID := user.ID
user.ID = 0
userAsJSON = getUserAsJSON(t, user)
req, _ = http.NewRequest(http.MethodPut, userPath+"/"+strconv.FormatInt(userID, 10), bytes.NewBuffer(userAsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
user.ID = userID
req, _ = http.NewRequest(http.MethodPut, userPath+"/0", bytes.NewBuffer(userAsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusNotFound, rr.Code)
req, _ = http.NewRequest(http.MethodPut, userPath+"/a", bytes.NewBuffer(userAsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestGetUsersMock(t *testing.T) {
user := getTestUser()
userAsJSON := getUserAsJSON(t, user)
req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err := render.DecodeJSON(rr.Body, &user)
if err != nil {
t.Errorf("Error get user: %v", err)
}
req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=510&offset=0&order=ASC&username="+defaultUsername, nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
var users []dataprovider.User
err = render.DecodeJSON(rr.Body, &users)
if err != nil {
t.Errorf("Error decoding users: %v", err)
}
if len(users) != 1 {
t.Errorf("1 user is expected")
}
req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=a&offset=0&order=ASC", nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=1&offset=a&order=ASC", nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=1&offset=0&order=ASCa", nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestDeleteUserInvalidParamsMock(t *testing.T) {
req, _ := http.NewRequest(http.MethodDelete, userPath+"/0", nil)
rr := executeRequest(req)
checkResponseCode(t, http.StatusNotFound, rr.Code)
req, _ = http.NewRequest(http.MethodDelete, userPath+"/a", nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
}
func TestGetQuotaScansMock(t *testing.T) {
req, err := http.NewRequest("GET", quotaScanPath, nil)
if err != nil {
t.Errorf("error get quota scan: %v", err)
}
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestStartQuotaScanMock(t *testing.T) {
user := getTestUser()
userAsJSON := getUserAsJSON(t, user)
req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err := render.DecodeJSON(rr.Body, &user)
if err != nil {
t.Errorf("Error get user: %v", err)
}
_, err = os.Stat(user.HomeDir)
if err == nil {
os.Remove(user.HomeDir)
}
// simulate a duplicate quota scan
userAsJSON = getUserAsJSON(t, user)
sftpd.AddQuotaScan(user.Username)
req, _ = http.NewRequest(http.MethodPost, quotaScanPath, bytes.NewBuffer(userAsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusConflict, rr.Code)
sftpd.RemoveQuotaScan(user.Username)
userAsJSON = getUserAsJSON(t, user)
req, _ = http.NewRequest(http.MethodPost, quotaScanPath, bytes.NewBuffer(userAsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusCreated, rr.Code)
req, _ = http.NewRequest(http.MethodGet, quotaScanPath, nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
var scans []sftpd.ActiveQuotaScan
err = render.DecodeJSON(rr.Body, &scans)
if err != nil {
t.Errorf("Error get active scans: %v", err)
}
for len(scans) > 0 {
req, _ = http.NewRequest(http.MethodGet, quotaScanPath, nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err = render.DecodeJSON(rr.Body, &scans)
if err != nil {
t.Errorf("Error get active scans: %v", err)
break
}
time.Sleep(100 * time.Millisecond)
}
_, err = os.Stat(user.HomeDir)
if err != nil && os.IsNotExist(err) {
os.MkdirAll(user.HomeDir, 0777)
}
req, _ = http.NewRequest(http.MethodPost, quotaScanPath, bytes.NewBuffer(userAsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusCreated, rr.Code)
req, _ = http.NewRequest(http.MethodGet, quotaScanPath, nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err = render.DecodeJSON(rr.Body, &scans)
if err != nil {
t.Errorf("Error get active scans: %v", err)
}
for len(scans) > 0 {
req, _ = http.NewRequest(http.MethodGet, quotaScanPath, nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err = render.DecodeJSON(rr.Body, &scans)
if err != nil {
t.Errorf("Error get active scans: %v", err)
break
}
time.Sleep(100 * time.Millisecond)
}
req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
os.RemoveAll(user.GetHomeDir())
}
func TestStartQuotaScanBadUserMock(t *testing.T) {
user := getTestUser()
userAsJSON := getUserAsJSON(t, user)
req, _ := http.NewRequest(http.MethodPost, quotaScanPath, bytes.NewBuffer(userAsJSON))
rr := executeRequest(req)
checkResponseCode(t, http.StatusNotFound, rr.Code)
}
func TestStartQuotaScanNonExistentUserMock(t *testing.T) {
req, _ := http.NewRequest(http.MethodPost, quotaScanPath, bytes.NewBuffer([]byte("invalid json")))
rr := executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
}
func TestGetVersionMock(t *testing.T) {
req, _ := http.NewRequest(http.MethodGet, versionPath, nil)
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestGetConnectionsMock(t *testing.T) {
req, _ := http.NewRequest(http.MethodGet, activeConnectionsPath, nil)
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestDeleteActiveConnectionMock(t *testing.T) {
req, _ := http.NewRequest(http.MethodDelete, activeConnectionsPath+"/connectionID", nil)
rr := executeRequest(req)
checkResponseCode(t, http.StatusNotFound, rr.Code)
}
func TestNotFoundMock(t *testing.T) {
req, _ := http.NewRequest(http.MethodGet, "/non/existing/path", nil)
rr := executeRequest(req)
checkResponseCode(t, http.StatusNotFound, rr.Code)
}
func TestMethodNotAllowedMock(t *testing.T) {
req, _ := http.NewRequest(http.MethodPost, activeConnectionsPath, nil)
rr := executeRequest(req)
checkResponseCode(t, http.StatusMethodNotAllowed, rr.Code)
}
func TestMetricsMock(t *testing.T) {
req, _ := http.NewRequest(http.MethodGet, metricsPath, nil)
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestGetWebRootMock(t *testing.T) {
req, _ := http.NewRequest(http.MethodGet, "/", nil)
rr := executeRequest(req)
checkResponseCode(t, http.StatusMovedPermanently, rr.Code)
req, _ = http.NewRequest(http.MethodGet, webBasePath, nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusMovedPermanently, rr.Code)
}
func TestBasicWebUsersMock(t *testing.T) {
user := getTestUser()
userAsJSON := getUserAsJSON(t, user)
req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err := render.DecodeJSON(rr.Body, &user)
if err != nil {
t.Errorf("Error get user: %v", err)
}
user1 := getTestUser()
user1.Username += "1"
user1AsJSON := getUserAsJSON(t, user1)
req, _ = http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(user1AsJSON))
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err = render.DecodeJSON(rr.Body, &user1)
if err != nil {
t.Errorf("Error get user1: %v", err)
}
req, _ = http.NewRequest(http.MethodGet, webUsersPath, nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
req, _ = http.NewRequest(http.MethodGet, webUsersPath+"?qlimit=a", nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
req, _ = http.NewRequest(http.MethodGet, webUsersPath+"?qlimit=1", nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
req, _ = http.NewRequest(http.MethodGet, webUserPath, nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
req, _ = http.NewRequest(http.MethodGet, webUserPath+"/"+strconv.FormatInt(user.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
req, _ = http.NewRequest(http.MethodGet, webUserPath+"/0", nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusNotFound, rr.Code)
req, _ = http.NewRequest(http.MethodGet, webUserPath+"/a", nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
form := make(url.Values)
form.Set("username", user.Username)
b, contentType, _ := getMultipartFormData(form, "", "")
req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
b, contentType, _ = getMultipartFormData(form, "", "")
req, _ = http.NewRequest(http.MethodPost, webUserPath+"/"+strconv.FormatInt(user.ID, 10), &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
b, contentType, _ = getMultipartFormData(form, "", "")
req, _ = http.NewRequest(http.MethodPost, webUserPath+"/0", &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusNotFound, rr.Code)
req, _ = http.NewRequest(http.MethodPost, webUserPath+"/a", &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr.Code)
req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user1.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestWebUserAddMock(t *testing.T) {
user := getTestUser()
user.UploadBandwidth = 32
user.DownloadBandwidth = 64
user.UID = 1000
mappedDir := filepath.Join(os.TempDir(), "mapped")
form := make(url.Values)
form.Set("username", user.Username)
form.Set("home_dir", user.HomeDir)
form.Set("password", user.Password)
form.Set("status", strconv.Itoa(user.Status))
form.Set("expiration_date", "")
form.Set("permissions", "*")
form.Set("sub_dirs_permissions", " /subdir::list ,download ")
form.Set("virtual_folders", fmt.Sprintf(" /vdir:: %v ", mappedDir))
form.Set("allowed_extensions", "/dir1::.jpg,.png")
form.Set("denied_extensions", "/dir1::.zip")
b, contentType, _ := getMultipartFormData(form, "", "")
// test invalid url escape
req, _ := http.NewRequest(http.MethodPost, webUserPath+"?a=%2", &b)
req.Header.Set("Content-Type", contentType)
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
form.Set("public_keys", testPubKey)
form.Set("uid", strconv.FormatInt(int64(user.UID), 10))
form.Set("gid", "a")
b, contentType, _ = getMultipartFormData(form, "", "")
// test invalid gid
req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
form.Set("gid", "0")
form.Set("max_sessions", "a")
b, contentType, _ = getMultipartFormData(form, "", "")
// test invalid max sessions
req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
form.Set("max_sessions", "0")
form.Set("quota_size", "a")
b, contentType, _ = getMultipartFormData(form, "", "")
// test invalid quota size
req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
form.Set("quota_size", "0")
form.Set("quota_files", "a")
b, contentType, _ = getMultipartFormData(form, "", "")
// test invalid quota files
req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
form.Set("quota_files", "0")
form.Set("upload_bandwidth", "a")
b, contentType, _ = getMultipartFormData(form, "", "")
// test invalid upload bandwidth
req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
form.Set("upload_bandwidth", strconv.FormatInt(user.UploadBandwidth, 10))
form.Set("download_bandwidth", "a")
b, contentType, _ = getMultipartFormData(form, "", "")
// test invalid download bandwidth
req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
form.Set("download_bandwidth", strconv.FormatInt(user.DownloadBandwidth, 10))
form.Set("status", "a")
b, contentType, _ = getMultipartFormData(form, "", "")
// test invalid status
req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
form.Set("status", strconv.Itoa(user.Status))
form.Set("expiration_date", "123")
b, contentType, _ = getMultipartFormData(form, "", "")
// test invalid expiration date
req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
form.Set("expiration_date", "")
form.Set("allowed_ip", "invalid,ip")
b, contentType, _ = getMultipartFormData(form, "", "")
// test invalid allowed_ip
req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
form.Set("allowed_ip", "")
form.Set("denied_ip", "192.168.1.2") // it should be 192.168.1.2/32
b, contentType, _ = getMultipartFormData(form, "", "")
// test invalid denied_ip
req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
form.Set("denied_ip", "")
b, contentType, _ = getMultipartFormData(form, "", "")
req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusSeeOther, rr.Code)
// the user already exists, was created with the above request
b, contentType, _ = getMultipartFormData(form, "", "")
req, _ = http.NewRequest(http.MethodPost, webUserPath, &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=1&offset=0&order=ASC&username="+user.Username, nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
var users []dataprovider.User
err := render.DecodeJSON(rr.Body, &users)
if err != nil {
t.Errorf("Error decoding users: %v", err)
}
if len(users) != 1 {
t.Errorf("1 user is expected, actual: %v", len(users))
}
newUser := users[0]
if newUser.UID != user.UID {
t.Errorf("uid does not match")
}
if newUser.UploadBandwidth != user.UploadBandwidth {
t.Errorf("upload_bandwidth does not match")
}
if newUser.DownloadBandwidth != user.DownloadBandwidth {
t.Errorf("download_bandwidth does not match")
}
if !utils.IsStringInSlice(testPubKey, newUser.PublicKeys) {
t.Errorf("public_keys does not match")
}
if val, ok := newUser.Permissions["/subdir"]; ok {
if !utils.IsStringInSlice(dataprovider.PermListItems, val) || !utils.IsStringInSlice(dataprovider.PermDownload, val) {
t.Error("permssions for /subdir does not match")
}
} else {
t.Errorf("user permissions must contain /somedir, actual: %v", newUser.Permissions)
}
vfolderFoumd := false
for _, v := range newUser.VirtualFolders {
if v.VirtualPath == "/vdir" && v.MappedPath == mappedDir {
vfolderFoumd = true
}
}
if !vfolderFoumd {
t.Errorf("virtual folders must contain /vdir, actual: %+v", newUser.VirtualFolders)
}
extFilters := newUser.Filters.FileExtensions[0]
if !utils.IsStringInSlice(".zip", extFilters.DeniedExtensions) {
t.Errorf("unexpected denied extensions: %v", extFilters.DeniedExtensions)
}
req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(newUser.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestWebUserUpdateMock(t *testing.T) {
user := getTestUser()
userAsJSON := getUserAsJSON(t, user)
req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err := render.DecodeJSON(rr.Body, &user)
if err != nil {
t.Errorf("Error get user: %v", err)
}
user.MaxSessions = 1
user.QuotaFiles = 2
user.QuotaSize = 3
user.GID = 1000
form := make(url.Values)
form.Set("username", user.Username)
form.Set("home_dir", user.HomeDir)
form.Set("uid", "0")
form.Set("gid", strconv.FormatInt(int64(user.GID), 10))
form.Set("max_sessions", strconv.FormatInt(int64(user.MaxSessions), 10))
form.Set("quota_size", strconv.FormatInt(user.QuotaSize, 10))
form.Set("quota_files", strconv.FormatInt(int64(user.QuotaFiles), 10))
form.Set("upload_bandwidth", "0")
form.Set("download_bandwidth", "0")
form.Set("permissions", "*")
form.Set("sub_dirs_permissions", "/otherdir :: list ,upload ")
form.Set("status", strconv.Itoa(user.Status))
form.Set("expiration_date", "2020-01-01 00:00:00")
form.Set("allowed_ip", " 192.168.1.3/32, 192.168.2.0/24 ")
form.Set("denied_ip", " 10.0.0.2/32 ")
form.Set("denied_extensions", "/dir1::.zip")
form.Set("ssh_login_methods", dataprovider.SSHLoginMethodKeyboardInteractive)
b, contentType, _ := getMultipartFormData(form, "", "")
req, _ = http.NewRequest(http.MethodPost, webUserPath+"/"+strconv.FormatInt(user.ID, 10), &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusSeeOther, rr.Code)
req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=1&offset=0&order=ASC&username="+user.Username, nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
var users []dataprovider.User
render.DecodeJSON(rr.Body, &users)
if len(users) != 1 {
t.Errorf("1 user is expected")
}
updateUser := users[0]
if user.HomeDir != updateUser.HomeDir {
t.Errorf("home dir does not match")
}
if user.MaxSessions != updateUser.MaxSessions {
t.Errorf("max_sessions does not match")
}
if user.QuotaFiles != updateUser.QuotaFiles {
t.Errorf("quota_files does not match")
}
if user.QuotaSize != updateUser.QuotaSize {
t.Errorf("quota_size does not match")
}
if user.GID != updateUser.GID {
t.Errorf("gid does not match")
}
if val, ok := updateUser.Permissions["/otherdir"]; ok {
if !utils.IsStringInSlice(dataprovider.PermListItems, val) || !utils.IsStringInSlice(dataprovider.PermUpload, val) {
t.Error("permssions for /otherdir does not match")
}
} else {
t.Errorf("user permissions must contains /otherdir, actual: %v", updateUser.Permissions)
}
if !utils.IsStringInSlice("192.168.1.3/32", updateUser.Filters.AllowedIP) {
t.Errorf("Allowed IP/Mask does not match: %v", updateUser.Filters.AllowedIP)
}
if !utils.IsStringInSlice("10.0.0.2/32", updateUser.Filters.DeniedIP) {
t.Errorf("Denied IP/Mask does not match: %v", updateUser.Filters.DeniedIP)
}
if !utils.IsStringInSlice(dataprovider.SSHLoginMethodKeyboardInteractive, updateUser.Filters.DeniedLoginMethods) {
t.Errorf("Denied login methods does not match: %v", updateUser.Filters.DeniedLoginMethods)
}
if !utils.IsStringInSlice(".zip", updateUser.Filters.FileExtensions[0].DeniedExtensions) {
t.Errorf("unexpected extensions filter: %+v", updateUser.Filters.FileExtensions)
}
req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestWebUserS3Mock(t *testing.T) {
user := getTestUser()
userAsJSON := getUserAsJSON(t, user)
req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err := render.DecodeJSON(rr.Body, &user)
if err != nil {
t.Errorf("Error get user: %v", err)
}
user.FsConfig.Provider = 1
user.FsConfig.S3Config.Bucket = "test"
user.FsConfig.S3Config.Region = "eu-west-1"
user.FsConfig.S3Config.AccessKey = "access-key"
user.FsConfig.S3Config.AccessSecret = "access-secret"
user.FsConfig.S3Config.Endpoint = "http://127.0.0.1:9000/path?a=b"
user.FsConfig.S3Config.StorageClass = "Standard"
user.FsConfig.S3Config.KeyPrefix = "somedir/subdir/"
user.FsConfig.S3Config.UploadPartSize = 5
user.FsConfig.S3Config.UploadConcurrency = 4
form := make(url.Values)
form.Set("username", user.Username)
form.Set("home_dir", user.HomeDir)
form.Set("uid", "0")
form.Set("gid", strconv.FormatInt(int64(user.GID), 10))
form.Set("max_sessions", strconv.FormatInt(int64(user.MaxSessions), 10))
form.Set("quota_size", strconv.FormatInt(user.QuotaSize, 10))
form.Set("quota_files", strconv.FormatInt(int64(user.QuotaFiles), 10))
form.Set("upload_bandwidth", "0")
form.Set("download_bandwidth", "0")
form.Set("permissions", "*")
form.Set("sub_dirs_permissions", "")
form.Set("status", strconv.Itoa(user.Status))
form.Set("expiration_date", "2020-01-01 00:00:00")
form.Set("allowed_ip", "")
form.Set("denied_ip", "")
form.Set("fs_provider", "1")
form.Set("s3_bucket", user.FsConfig.S3Config.Bucket)
form.Set("s3_region", user.FsConfig.S3Config.Region)
form.Set("s3_access_key", user.FsConfig.S3Config.AccessKey)
form.Set("s3_access_secret", user.FsConfig.S3Config.AccessSecret)
form.Set("s3_storage_class", user.FsConfig.S3Config.StorageClass)
form.Set("s3_endpoint", user.FsConfig.S3Config.Endpoint)
form.Set("s3_key_prefix", user.FsConfig.S3Config.KeyPrefix)
form.Set("allowed_extensions", "/dir1::.jpg,.png")
form.Set("denied_extensions", "/dir2::.zip")
// test invalid s3_upload_part_size
form.Set("s3_upload_part_size", "a")
b, contentType, _ := getMultipartFormData(form, "", "")
req, _ = http.NewRequest(http.MethodPost, webUserPath+"/"+strconv.FormatInt(user.ID, 10), &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
// test invalid s3_concurrency
form.Set("s3_upload_part_size", strconv.FormatInt(user.FsConfig.S3Config.UploadPartSize, 10))
form.Set("s3_upload_concurrency", "a")
b, contentType, _ = getMultipartFormData(form, "", "")
req, _ = http.NewRequest(http.MethodPost, webUserPath+"/"+strconv.FormatInt(user.ID, 10), &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
// now add the user
form.Set("s3_upload_concurrency", strconv.Itoa(user.FsConfig.S3Config.UploadConcurrency))
b, contentType, _ = getMultipartFormData(form, "", "")
req, _ = http.NewRequest(http.MethodPost, webUserPath+"/"+strconv.FormatInt(user.ID, 10), &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusSeeOther, rr.Code)
req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=1&offset=0&order=ASC&username="+user.Username, nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
var users []dataprovider.User
err = render.DecodeJSON(rr.Body, &users)
if err != nil {
t.Errorf("Error decoding users: %v", err)
}
if len(users) != 1 {
t.Errorf("1 user is expected")
}
updateUser := users[0]
if updateUser.ExpirationDate != 1577836800000 {
t.Errorf("invalid expiration date: %v", updateUser.ExpirationDate)
}
if updateUser.FsConfig.S3Config.Bucket != user.FsConfig.S3Config.Bucket {
t.Error("s3 bucket mismatch")
}
if updateUser.FsConfig.S3Config.Region != user.FsConfig.S3Config.Region {
t.Error("s3 region mismatch")
}
if updateUser.FsConfig.S3Config.AccessKey != user.FsConfig.S3Config.AccessKey {
t.Error("s3 access key mismatch")
}
if !strings.HasPrefix(updateUser.FsConfig.S3Config.AccessSecret, "$aes$") {
t.Error("s3 access secret is not encrypted")
}
if updateUser.FsConfig.S3Config.StorageClass != user.FsConfig.S3Config.StorageClass {
t.Error("s3 storage class mismatch")
}
if updateUser.FsConfig.S3Config.Endpoint != user.FsConfig.S3Config.Endpoint {
t.Error("s3 endpoint mismatch")
}
if updateUser.FsConfig.S3Config.KeyPrefix != user.FsConfig.S3Config.KeyPrefix {
t.Error("s3 key prefix mismatch")
}
if updateUser.FsConfig.S3Config.UploadPartSize != user.FsConfig.S3Config.UploadPartSize {
t.Error("s3 upload part size mismatch")
}
if updateUser.FsConfig.S3Config.UploadConcurrency != user.FsConfig.S3Config.UploadConcurrency {
t.Error("s3 upload concurrency mismatch")
}
if len(updateUser.Filters.FileExtensions) != 2 {
t.Errorf("unexpected extensions filter: %+v", updateUser.Filters.FileExtensions)
}
req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestWebUserGCSMock(t *testing.T) {
user := getTestUser()
userAsJSON := getUserAsJSON(t, user)
req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err := render.DecodeJSON(rr.Body, &user)
if err != nil {
t.Errorf("Error get user: %v", err)
}
credentialsFilePath := filepath.Join(os.TempDir(), "gcs.json")
err = createTestFile(credentialsFilePath, 0)
if err != nil {
t.Errorf("unable to create credential test file: %v", err)
}
user.FsConfig.Provider = 2
user.FsConfig.GCSConfig.Bucket = "test"
user.FsConfig.GCSConfig.KeyPrefix = "somedir/subdir/"
user.FsConfig.GCSConfig.StorageClass = "standard"
form := make(url.Values)
form.Set("username", user.Username)
form.Set("home_dir", user.HomeDir)
form.Set("uid", "0")
form.Set("gid", strconv.FormatInt(int64(user.GID), 10))
form.Set("max_sessions", strconv.FormatInt(int64(user.MaxSessions), 10))
form.Set("quota_size", strconv.FormatInt(user.QuotaSize, 10))
form.Set("quota_files", strconv.FormatInt(int64(user.QuotaFiles), 10))
form.Set("upload_bandwidth", "0")
form.Set("download_bandwidth", "0")
form.Set("permissions", "*")
form.Set("sub_dirs_permissions", "")
form.Set("status", strconv.Itoa(user.Status))
form.Set("expiration_date", "2020-01-01 00:00:00")
form.Set("allowed_ip", "")
form.Set("denied_ip", "")
form.Set("fs_provider", "2")
form.Set("gcs_bucket", user.FsConfig.GCSConfig.Bucket)
form.Set("gcs_storage_class", user.FsConfig.GCSConfig.StorageClass)
form.Set("gcs_key_prefix", user.FsConfig.GCSConfig.KeyPrefix)
form.Set("allowed_extensions", "/dir1::.jpg,.png")
b, contentType, _ := getMultipartFormData(form, "", "")
req, _ = http.NewRequest(http.MethodPost, webUserPath+"/"+strconv.FormatInt(user.ID, 10), &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
b, contentType, _ = getMultipartFormData(form, "gcs_credential_file", credentialsFilePath)
req, _ = http.NewRequest(http.MethodPost, webUserPath+"/"+strconv.FormatInt(user.ID, 10), &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err = createTestFile(credentialsFilePath, 4096)
if err != nil {
t.Errorf("unable to create credential test file: %v", err)
}
b, contentType, _ = getMultipartFormData(form, "gcs_credential_file", credentialsFilePath)
req, _ = http.NewRequest(http.MethodPost, webUserPath+"/"+strconv.FormatInt(user.ID, 10), &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusSeeOther, rr.Code)
req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=1&offset=0&order=ASC&username="+user.Username, nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
var users []dataprovider.User
render.DecodeJSON(rr.Body, &users)
if len(users) != 1 {
t.Errorf("1 user is expected")
}
updateUser := users[0]
if updateUser.ExpirationDate != 1577836800000 {
t.Errorf("invalid expiration date: %v", updateUser.ExpirationDate)
}
if updateUser.FsConfig.Provider != user.FsConfig.Provider {
t.Error("fs provider mismatch")
}
if updateUser.FsConfig.GCSConfig.Bucket != user.FsConfig.GCSConfig.Bucket {
t.Error("GCS bucket mismatch")
}
if updateUser.FsConfig.GCSConfig.StorageClass != user.FsConfig.GCSConfig.StorageClass {
t.Error("GCS storage class mismatch")
}
if updateUser.FsConfig.GCSConfig.KeyPrefix != user.FsConfig.GCSConfig.KeyPrefix {
t.Error("GCS key prefix mismatch")
}
if updateUser.Filters.FileExtensions[0].Path != "/dir1" {
t.Errorf("unexpected extensions filter: %+v", updateUser.Filters.FileExtensions)
}
form.Set("gcs_auto_credentials", "on")
b, contentType, _ = getMultipartFormData(form, "", "")
req, _ = http.NewRequest(http.MethodPost, webUserPath+"/"+strconv.FormatInt(user.ID, 10), &b)
req.Header.Set("Content-Type", contentType)
rr = executeRequest(req)
checkResponseCode(t, http.StatusSeeOther, rr.Code)
req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=1&offset=0&order=ASC&username="+user.Username, nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
err = render.DecodeJSON(rr.Body, &users)
if err != nil {
t.Errorf("Error decoding users: %v", err)
}
if len(users) != 1 {
t.Errorf("1 user is expected")
}
updateUser = users[0]
if updateUser.FsConfig.GCSConfig.AutomaticCredentials != 1 {
t.Error("GCS automatic credentials mismatch")
}
req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
os.Remove(credentialsFilePath)
}
func TestProviderClosedMock(t *testing.T) {
if providerDriverName == dataprovider.BoltDataProviderName {
t.Skip("skipping test provider errors for bolt provider")
}
dataProvider := dataprovider.GetProvider()
dataprovider.Close(dataProvider)
req, _ := http.NewRequest(http.MethodGet, webUsersPath, nil)
rr := executeRequest(req)
checkResponseCode(t, http.StatusInternalServerError, rr.Code)
req, _ = http.NewRequest(http.MethodGet, webUserPath+"/0", nil)
rr = executeRequest(req)
checkResponseCode(t, http.StatusInternalServerError, rr.Code)
form := make(url.Values)
form.Set("username", "test")
req, _ = http.NewRequest(http.MethodPost, webUserPath+"/0", strings.NewReader(form.Encode()))
rr = executeRequest(req)
checkResponseCode(t, http.StatusInternalServerError, rr.Code)
config.LoadConfig(configDir, "")
providerConf := config.GetProviderConf()
providerConf.CredentialsPath = credentialsPath
os.RemoveAll(credentialsPath)
err := dataprovider.Initialize(providerConf, configDir)
if err != nil {
t.Errorf("error initializing data provider")
}
httpd.SetDataProvider(dataprovider.GetProvider())
sftpd.SetDataProvider(dataprovider.GetProvider())
}
func TestGetWebConnectionsMock(t *testing.T) {
req, _ := http.NewRequest(http.MethodGet, webConnectionsPath, nil)
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func TestStaticFilesMock(t *testing.T) {
req, _ := http.NewRequest(http.MethodGet, "/static/favicon.ico", nil)
rr := executeRequest(req)
checkResponseCode(t, http.StatusOK, rr.Code)
}
func waitTCPListening(address string) {
for {
conn, err := net.Dial("tcp", address)
if err != nil {
logger.WarnToConsole("tcp server %v not listening: %v\n", address, err)
time.Sleep(100 * time.Millisecond)
continue
}
logger.InfoToConsole("tcp server %v now listening\n", address)
defer conn.Close()
break
}
}
func getTestUser() dataprovider.User {
user := dataprovider.User{
Username: defaultUsername,
Password: defaultPassword,
HomeDir: filepath.Join(homeBasePath, defaultUsername),
Status: 1,
}
user.Permissions = make(map[string][]string)
user.Permissions["/"] = defaultPerms
return user
}
func getUserAsJSON(t *testing.T, user dataprovider.User) []byte {
json, err := json.Marshal(user)
if err != nil {
t.Errorf("error get user as json: %v", err)
return []byte("{}")
}
return json
}
func executeRequest(req *http.Request) *httptest.ResponseRecorder {
rr := httptest.NewRecorder()
testServer.Config.Handler.ServeHTTP(rr, req)
return rr
}
func checkResponseCode(t *testing.T, expected, actual int) {
if expected != actual {
t.Errorf("Expected response code %d. Got %d", expected, actual)
}
}
func createTestFile(path string, size int64) error {
baseDir := filepath.Dir(path)
if _, err := os.Stat(baseDir); os.IsNotExist(err) {
os.MkdirAll(baseDir, 0777)
}
content := make([]byte, size)
if size > 0 {
_, err := rand.Read(content)
if err != nil {
return err
}
}
return ioutil.WriteFile(path, content, 0666)
}
func getMultipartFormData(values url.Values, fileFieldName, filePath string) (bytes.Buffer, string, error) {
var b bytes.Buffer
w := multipart.NewWriter(&b)
for k, v := range values {
for _, s := range v {
if err := w.WriteField(k, s); err != nil {
return b, "", err
}
}
}
if len(fileFieldName) > 0 && len(filePath) > 0 {
fw, err := w.CreateFormFile(fileFieldName, filepath.Base(filePath))
if err != nil {
return b, "", err
}
f, err := os.Open(filePath)
if err != nil {
return b, "", err
}
if _, err = io.Copy(fw, f); err != nil {
return b, "", err
}
}
err := w.Close()
return b, w.FormDataContentType(), err
}