2019-07-27 07:38:09 +00:00
|
|
|
package sftpd
|
|
|
|
|
|
|
|
import (
|
2019-08-24 12:41:15 +00:00
|
|
|
"bytes"
|
2020-01-12 07:25:08 +00:00
|
|
|
"errors"
|
2019-08-24 12:41:15 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
2019-10-10 07:04:17 +00:00
|
|
|
"net"
|
2019-08-04 07:37:58 +00:00
|
|
|
"os"
|
2020-05-24 21:31:14 +00:00
|
|
|
"os/exec"
|
2020-06-07 21:30:18 +00:00
|
|
|
"path"
|
2020-01-19 06:41:05 +00:00
|
|
|
"path/filepath"
|
2019-07-27 19:19:30 +00:00
|
|
|
"runtime"
|
2020-01-10 18:20:22 +00:00
|
|
|
"sync"
|
2019-07-27 07:38:09 +00:00
|
|
|
"testing"
|
2019-08-24 12:41:15 +00:00
|
|
|
"time"
|
2019-08-04 07:37:58 +00:00
|
|
|
|
2020-05-06 17:36:34 +00:00
|
|
|
"github.com/eikenb/pipeat"
|
|
|
|
"github.com/pkg/sftp"
|
|
|
|
"github.com/stretchr/testify/assert"
|
2020-05-15 18:08:53 +00:00
|
|
|
"golang.org/x/crypto/ssh"
|
2020-05-06 17:36:34 +00:00
|
|
|
|
2019-08-04 10:35:33 +00:00
|
|
|
"github.com/drakkan/sftpgo/dataprovider"
|
2019-10-07 16:19:01 +00:00
|
|
|
"github.com/drakkan/sftpgo/utils"
|
2020-01-19 06:41:05 +00:00
|
|
|
"github.com/drakkan/sftpgo/vfs"
|
2019-07-27 07:38:09 +00:00
|
|
|
)
|
|
|
|
|
2020-05-06 17:36:34 +00:00
|
|
|
const osWindows = "windows"
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
type MockChannel struct {
|
2019-11-26 21:26:42 +00:00
|
|
|
Buffer *bytes.Buffer
|
|
|
|
StdErrBuffer *bytes.Buffer
|
|
|
|
ReadError error
|
|
|
|
WriteError error
|
|
|
|
ShortWriteErr bool
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *MockChannel) Read(data []byte) (int, error) {
|
|
|
|
if c.ReadError != nil {
|
|
|
|
return 0, c.ReadError
|
|
|
|
}
|
|
|
|
return c.Buffer.Read(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *MockChannel) Write(data []byte) (int, error) {
|
|
|
|
if c.WriteError != nil {
|
|
|
|
return 0, c.WriteError
|
|
|
|
}
|
2019-11-26 21:26:42 +00:00
|
|
|
if c.ShortWriteErr {
|
|
|
|
return 0, nil
|
|
|
|
}
|
2019-08-24 12:41:15 +00:00
|
|
|
return c.Buffer.Write(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *MockChannel) Close() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *MockChannel) CloseWrite() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *MockChannel) SendRequest(name string, wantReply bool, payload []byte) (bool, error) {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *MockChannel) Stderr() io.ReadWriter {
|
|
|
|
return c.StdErrBuffer
|
|
|
|
}
|
|
|
|
|
2020-01-19 06:41:05 +00:00
|
|
|
// MockOsFs mockable OsFs
|
|
|
|
type MockOsFs struct {
|
2020-01-19 12:58:55 +00:00
|
|
|
vfs.Fs
|
2020-01-19 06:41:05 +00:00
|
|
|
err error
|
|
|
|
statErr error
|
|
|
|
isAtomicUploadSupported bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// Name returns the name for the Fs implementation
|
|
|
|
func (fs MockOsFs) Name() string {
|
|
|
|
return "mockOsFs"
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsUploadResumeSupported returns true if upload resume is supported
|
|
|
|
func (MockOsFs) IsUploadResumeSupported() bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsAtomicUploadSupported returns true if atomic upload is supported
|
|
|
|
func (fs MockOsFs) IsAtomicUploadSupported() bool {
|
|
|
|
return fs.isAtomicUploadSupported
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stat returns a FileInfo describing the named file
|
|
|
|
func (fs MockOsFs) Stat(name string) (os.FileInfo, error) {
|
|
|
|
if fs.statErr != nil {
|
|
|
|
return nil, fs.statErr
|
|
|
|
}
|
|
|
|
return os.Stat(name)
|
|
|
|
}
|
|
|
|
|
2020-06-07 21:30:18 +00:00
|
|
|
// Lstat returns a FileInfo describing the named file
|
|
|
|
func (fs MockOsFs) Lstat(name string) (os.FileInfo, error) {
|
|
|
|
if fs.statErr != nil {
|
|
|
|
return nil, fs.statErr
|
|
|
|
}
|
|
|
|
return os.Lstat(name)
|
|
|
|
}
|
|
|
|
|
2020-01-19 06:41:05 +00:00
|
|
|
// Remove removes the named file or (empty) directory.
|
|
|
|
func (fs MockOsFs) Remove(name string, isDir bool) error {
|
|
|
|
if fs.err != nil {
|
|
|
|
return fs.err
|
|
|
|
}
|
|
|
|
return os.Remove(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rename renames (moves) source to target
|
|
|
|
func (fs MockOsFs) Rename(source, target string) error {
|
|
|
|
if fs.err != nil {
|
|
|
|
return fs.err
|
|
|
|
}
|
|
|
|
return os.Rename(source, target)
|
|
|
|
}
|
|
|
|
|
2020-01-19 12:58:55 +00:00
|
|
|
func newMockOsFs(err, statErr error, atomicUpload bool, connectionID, rootDir string) vfs.Fs {
|
2020-01-19 06:41:05 +00:00
|
|
|
return &MockOsFs{
|
2020-02-23 10:30:26 +00:00
|
|
|
Fs: vfs.NewOsFs(connectionID, rootDir, nil),
|
2020-01-19 06:41:05 +00:00
|
|
|
err: err,
|
|
|
|
statErr: statErr,
|
|
|
|
isAtomicUploadSupported: atomicUpload,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-25 17:36:33 +00:00
|
|
|
func TestNewActionNotification(t *testing.T) {
|
|
|
|
user := dataprovider.User{
|
|
|
|
Username: "username",
|
|
|
|
}
|
|
|
|
user.FsConfig.Provider = 0
|
|
|
|
user.FsConfig.S3Config = vfs.S3FsConfig{
|
|
|
|
Bucket: "s3bucket",
|
|
|
|
Endpoint: "endpoint",
|
|
|
|
}
|
|
|
|
user.FsConfig.GCSConfig = vfs.GCSFsConfig{
|
|
|
|
Bucket: "gcsbucket",
|
|
|
|
}
|
2020-04-03 17:25:38 +00:00
|
|
|
a := newActionNotification(user, operationDownload, "path", "target", "", 123, nil)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, user.Username, a.Username)
|
|
|
|
assert.Equal(t, 0, len(a.Bucket))
|
|
|
|
assert.Equal(t, 0, len(a.Endpoint))
|
|
|
|
|
2020-03-25 17:36:33 +00:00
|
|
|
user.FsConfig.Provider = 1
|
2020-04-03 17:25:38 +00:00
|
|
|
a = newActionNotification(user, operationDownload, "path", "target", "", 123, nil)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "s3bucket", a.Bucket)
|
|
|
|
assert.Equal(t, "endpoint", a.Endpoint)
|
|
|
|
|
2020-03-25 17:36:33 +00:00
|
|
|
user.FsConfig.Provider = 2
|
2020-04-03 17:25:38 +00:00
|
|
|
a = newActionNotification(user, operationDownload, "path", "target", "", 123, nil)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "gcsbucket", a.Bucket)
|
|
|
|
assert.Equal(t, 0, len(a.Endpoint))
|
2020-03-25 17:36:33 +00:00
|
|
|
}
|
|
|
|
|
2019-07-27 07:38:09 +00:00
|
|
|
func TestWrongActions(t *testing.T) {
|
|
|
|
actionsCopy := actions
|
2020-05-06 17:36:34 +00:00
|
|
|
|
2019-07-27 19:19:30 +00:00
|
|
|
badCommand := "/bad/command"
|
2020-05-06 17:36:34 +00:00
|
|
|
if runtime.GOOS == osWindows {
|
2019-07-27 19:19:30 +00:00
|
|
|
badCommand = "C:\\bad\\command"
|
|
|
|
}
|
2019-07-27 07:38:09 +00:00
|
|
|
actions = Actions{
|
2020-05-24 13:29:39 +00:00
|
|
|
ExecuteOn: []string{operationDownload},
|
|
|
|
Hook: badCommand,
|
2019-07-27 07:38:09 +00:00
|
|
|
}
|
2020-03-25 17:36:33 +00:00
|
|
|
user := dataprovider.User{
|
|
|
|
Username: "username",
|
|
|
|
}
|
2020-04-03 17:25:38 +00:00
|
|
|
err := executeAction(newActionNotification(user, operationDownload, "path", "", "", 0, nil))
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "action with bad command must fail")
|
|
|
|
|
2020-04-03 17:25:38 +00:00
|
|
|
err = executeAction(newActionNotification(user, operationDelete, "path", "", "", 0, nil))
|
2020-05-24 13:29:39 +00:00
|
|
|
assert.EqualError(t, err, errUnconfiguredAction.Error())
|
|
|
|
actions.Hook = "http://foo\x7f.com/"
|
2020-04-03 17:25:38 +00:00
|
|
|
err = executeAction(newActionNotification(user, operationDownload, "path", "", "", 0, nil))
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "action with bad url must fail")
|
|
|
|
|
2020-05-24 13:29:39 +00:00
|
|
|
actions.Hook = ""
|
|
|
|
err = executeAction(newActionNotification(user, operationDownload, "path", "", "", 0, nil))
|
|
|
|
assert.Error(t, err, errNoHook.Error())
|
|
|
|
|
|
|
|
actions.Hook = "relative path"
|
|
|
|
err = executeNotificationCommand(newActionNotification(user, operationDownload, "path", "", "", 0, nil))
|
|
|
|
assert.EqualError(t, err, fmt.Sprintf("invalid notification command %#v", actions.Hook))
|
|
|
|
|
2019-07-27 07:38:09 +00:00
|
|
|
actions = actionsCopy
|
|
|
|
}
|
|
|
|
|
2020-01-31 22:26:56 +00:00
|
|
|
func TestActionHTTP(t *testing.T) {
|
|
|
|
actionsCopy := actions
|
2020-05-24 21:31:14 +00:00
|
|
|
|
2020-01-31 22:26:56 +00:00
|
|
|
actions = Actions{
|
2020-05-24 13:29:39 +00:00
|
|
|
ExecuteOn: []string{operationDownload},
|
|
|
|
Hook: "http://127.0.0.1:8080/",
|
2020-01-31 22:26:56 +00:00
|
|
|
}
|
2020-03-25 17:36:33 +00:00
|
|
|
user := dataprovider.User{
|
|
|
|
Username: "username",
|
|
|
|
}
|
2020-04-03 17:25:38 +00:00
|
|
|
err := executeAction(newActionNotification(user, operationDownload, "path", "", "", 0, nil))
|
2020-05-24 21:31:14 +00:00
|
|
|
assert.EqualError(t, err, errUnexpectedHTTResponse.Error())
|
|
|
|
|
|
|
|
actions = actionsCopy
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPreDeleteAction(t *testing.T) {
|
2020-06-07 21:30:18 +00:00
|
|
|
if runtime.GOOS == osWindows {
|
|
|
|
t.Skip("this test is not available on Windows")
|
|
|
|
}
|
2020-05-24 21:31:14 +00:00
|
|
|
actionsCopy := actions
|
|
|
|
|
|
|
|
hookCmd, err := exec.LookPath("true")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
actions = Actions{
|
|
|
|
ExecuteOn: []string{operationPreDelete},
|
|
|
|
Hook: hookCmd,
|
|
|
|
}
|
|
|
|
homeDir := filepath.Join(os.TempDir(), "test_user")
|
2020-06-08 17:40:17 +00:00
|
|
|
err = os.MkdirAll(homeDir, os.ModePerm)
|
2020-05-24 21:31:14 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
user := dataprovider.User{
|
|
|
|
Username: "username",
|
|
|
|
HomeDir: homeDir,
|
|
|
|
}
|
|
|
|
user.Permissions = make(map[string][]string)
|
|
|
|
user.Permissions["/"] = []string{dataprovider.PermAny}
|
|
|
|
c := Connection{
|
|
|
|
fs: vfs.NewOsFs("id", homeDir, nil),
|
|
|
|
User: user,
|
|
|
|
}
|
|
|
|
testfile := filepath.Join(user.HomeDir, "testfile")
|
|
|
|
request := sftp.NewRequest("Remove", "/testfile")
|
|
|
|
err = ioutil.WriteFile(testfile, []byte("test"), 0666)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
2020-05-24 21:31:14 +00:00
|
|
|
err = c.handleSFTPRemove(testfile, request)
|
|
|
|
assert.EqualError(t, err, sftp.ErrSSHFxOk.Error())
|
|
|
|
assert.FileExists(t, testfile)
|
|
|
|
|
|
|
|
os.RemoveAll(homeDir)
|
2020-01-31 22:26:56 +00:00
|
|
|
|
|
|
|
actions = actionsCopy
|
|
|
|
}
|
|
|
|
|
2019-07-27 07:38:09 +00:00
|
|
|
func TestRemoveNonexistentTransfer(t *testing.T) {
|
|
|
|
transfer := Transfer{}
|
|
|
|
err := removeTransfer(&transfer)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "remove nonexistent transfer must fail")
|
2019-07-27 07:38:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestRemoveNonexistentQuotaScan(t *testing.T) {
|
|
|
|
err := RemoveQuotaScan("username")
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "remove nonexistent quota scan must fail")
|
2019-07-27 07:38:09 +00:00
|
|
|
}
|
2019-08-04 07:37:58 +00:00
|
|
|
|
|
|
|
func TestGetOSOpenFlags(t *testing.T) {
|
|
|
|
var flags sftp.FileOpenFlags
|
|
|
|
flags.Write = true
|
|
|
|
flags.Excl = true
|
2019-08-04 09:02:38 +00:00
|
|
|
osFlags := getOSOpenFlags(flags)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NotEqual(t, 0, osFlags&os.O_WRONLY)
|
|
|
|
assert.NotEqual(t, 0, osFlags&os.O_EXCL)
|
|
|
|
|
2019-10-09 15:33:30 +00:00
|
|
|
flags.Append = true
|
|
|
|
// append flag should be ignored to allow resume
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NotEqual(t, 0, osFlags&os.O_WRONLY)
|
|
|
|
assert.NotEqual(t, 0, osFlags&os.O_EXCL)
|
2019-08-04 07:37:58 +00:00
|
|
|
}
|
|
|
|
|
2019-10-09 15:33:30 +00:00
|
|
|
func TestUploadResumeInvalidOffset(t *testing.T) {
|
2020-05-06 17:36:34 +00:00
|
|
|
testfile := "testfile" //nolint:goconst
|
|
|
|
file, err := os.Create(testfile)
|
|
|
|
assert.NoError(t, err)
|
2019-10-09 15:33:30 +00:00
|
|
|
transfer := Transfer{
|
|
|
|
file: file,
|
|
|
|
path: file.Name(),
|
|
|
|
start: time.Now(),
|
|
|
|
bytesSent: 0,
|
|
|
|
bytesReceived: 0,
|
|
|
|
user: dataprovider.User{
|
|
|
|
Username: "testuser",
|
|
|
|
},
|
|
|
|
connectionID: "",
|
|
|
|
transferType: transferUpload,
|
|
|
|
lastActivity: time.Now(),
|
|
|
|
isNewFile: false,
|
|
|
|
protocol: protocolSFTP,
|
|
|
|
transferError: nil,
|
|
|
|
isFinished: false,
|
|
|
|
minWriteOffset: 10,
|
2020-01-10 18:20:22 +00:00
|
|
|
lock: new(sync.Mutex),
|
2019-10-09 15:33:30 +00:00
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
_, err = transfer.WriteAt([]byte("test"), 0)
|
|
|
|
assert.Error(t, err, "upload with invalid offset must fail")
|
2020-01-12 07:25:08 +00:00
|
|
|
err = transfer.Close()
|
2020-05-06 17:36:34 +00:00
|
|
|
if assert.Error(t, err) {
|
|
|
|
assert.Contains(t, err.Error(), "Invalid write offset")
|
2020-01-12 07:25:08 +00:00
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
err = os.Remove(testfile)
|
|
|
|
assert.NoError(t, err)
|
2019-08-04 07:37:58 +00:00
|
|
|
}
|
2019-08-04 10:35:33 +00:00
|
|
|
|
2020-01-10 18:20:22 +00:00
|
|
|
func TestReadWriteErrors(t *testing.T) {
|
|
|
|
testfile := "testfile"
|
2020-05-06 17:36:34 +00:00
|
|
|
file, err := os.Create(testfile)
|
|
|
|
assert.NoError(t, err)
|
2020-01-10 18:20:22 +00:00
|
|
|
transfer := Transfer{
|
|
|
|
file: file,
|
|
|
|
path: file.Name(),
|
|
|
|
start: time.Now(),
|
|
|
|
bytesSent: 0,
|
|
|
|
bytesReceived: 0,
|
|
|
|
user: dataprovider.User{
|
|
|
|
Username: "testuser",
|
|
|
|
},
|
|
|
|
connectionID: "",
|
|
|
|
transferType: transferDownload,
|
|
|
|
lastActivity: time.Now(),
|
|
|
|
isNewFile: false,
|
|
|
|
protocol: protocolSFTP,
|
|
|
|
transferError: nil,
|
|
|
|
isFinished: false,
|
|
|
|
minWriteOffset: 0,
|
|
|
|
lock: new(sync.Mutex),
|
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
err = file.Close()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
_, err = transfer.WriteAt([]byte("test"), 0)
|
|
|
|
assert.Error(t, err, "writing to closed file must fail")
|
2020-01-10 18:20:22 +00:00
|
|
|
buf := make([]byte, 32768)
|
|
|
|
_, err = transfer.ReadAt(buf, 0)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "reading from a closed file must fail")
|
2020-01-10 18:20:22 +00:00
|
|
|
err = transfer.Close()
|
2020-05-20 18:17:59 +00:00
|
|
|
assert.Error(t, err)
|
2020-05-06 17:36:34 +00:00
|
|
|
|
|
|
|
r, _, err := pipeat.Pipe()
|
|
|
|
assert.NoError(t, err)
|
2020-01-19 06:41:05 +00:00
|
|
|
transfer = Transfer{
|
|
|
|
readerAt: r,
|
|
|
|
writerAt: nil,
|
|
|
|
start: time.Now(),
|
|
|
|
bytesSent: 0,
|
|
|
|
bytesReceived: 0,
|
|
|
|
user: dataprovider.User{
|
|
|
|
Username: "testuser",
|
|
|
|
},
|
|
|
|
connectionID: "",
|
|
|
|
transferType: transferDownload,
|
|
|
|
lastActivity: time.Now(),
|
|
|
|
isNewFile: false,
|
|
|
|
protocol: protocolSFTP,
|
|
|
|
transferError: nil,
|
|
|
|
isFinished: false,
|
|
|
|
lock: new(sync.Mutex),
|
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
err = transfer.closeIO()
|
|
|
|
assert.NoError(t, err)
|
2020-01-19 06:41:05 +00:00
|
|
|
_, err = transfer.ReadAt(buf, 0)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "reading from a closed pipe must fail")
|
|
|
|
|
|
|
|
r, w, err := pipeat.Pipe()
|
|
|
|
assert.NoError(t, err)
|
2020-01-19 06:41:05 +00:00
|
|
|
transfer = Transfer{
|
|
|
|
readerAt: nil,
|
2020-05-19 17:17:43 +00:00
|
|
|
writerAt: vfs.NewPipeWriter(w),
|
2020-01-19 06:41:05 +00:00
|
|
|
start: time.Now(),
|
|
|
|
bytesSent: 0,
|
|
|
|
bytesReceived: 0,
|
|
|
|
user: dataprovider.User{
|
|
|
|
Username: "testuser",
|
|
|
|
},
|
|
|
|
connectionID: "",
|
|
|
|
transferType: transferDownload,
|
|
|
|
lastActivity: time.Now(),
|
|
|
|
isNewFile: false,
|
|
|
|
protocol: protocolSFTP,
|
|
|
|
transferError: nil,
|
|
|
|
isFinished: false,
|
|
|
|
lock: new(sync.Mutex),
|
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
err = r.Close()
|
|
|
|
assert.NoError(t, err)
|
2020-05-19 17:17:43 +00:00
|
|
|
errFake := fmt.Errorf("fake upload error")
|
|
|
|
go func() {
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
transfer.writerAt.Done(errFake)
|
|
|
|
}()
|
2020-05-06 17:36:34 +00:00
|
|
|
err = transfer.closeIO()
|
2020-05-19 17:17:43 +00:00
|
|
|
assert.EqualError(t, err, errFake.Error())
|
2020-01-19 06:41:05 +00:00
|
|
|
_, err = transfer.WriteAt([]byte("test"), 0)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "writing to closed pipe must fail")
|
|
|
|
|
|
|
|
err = os.Remove(testfile)
|
|
|
|
assert.NoError(t, err)
|
2020-01-19 06:41:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestTransferCancelFn(t *testing.T) {
|
|
|
|
testfile := "testfile"
|
2020-05-06 17:36:34 +00:00
|
|
|
file, err := os.Create(testfile)
|
|
|
|
assert.NoError(t, err)
|
2020-01-19 06:41:05 +00:00
|
|
|
isCancelled := false
|
|
|
|
cancelFn := func() {
|
|
|
|
isCancelled = true
|
|
|
|
}
|
|
|
|
transfer := Transfer{
|
|
|
|
file: file,
|
|
|
|
cancelFn: cancelFn,
|
|
|
|
path: file.Name(),
|
|
|
|
start: time.Now(),
|
|
|
|
bytesSent: 0,
|
|
|
|
bytesReceived: 0,
|
|
|
|
user: dataprovider.User{
|
|
|
|
Username: "testuser",
|
|
|
|
},
|
|
|
|
connectionID: "",
|
|
|
|
transferType: transferDownload,
|
|
|
|
lastActivity: time.Now(),
|
|
|
|
isNewFile: false,
|
|
|
|
protocol: protocolSFTP,
|
|
|
|
transferError: nil,
|
|
|
|
isFinished: false,
|
|
|
|
minWriteOffset: 0,
|
|
|
|
lock: new(sync.Mutex),
|
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
errFake := errors.New("fake error, this will trigger cancelFn")
|
|
|
|
transfer.TransferError(errFake)
|
|
|
|
err = transfer.Close()
|
|
|
|
assert.EqualError(t, err, errFake.Error())
|
|
|
|
assert.True(t, isCancelled, "cancelFn not called!")
|
|
|
|
|
|
|
|
err = os.Remove(testfile)
|
|
|
|
assert.NoError(t, err)
|
2020-01-19 06:41:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestMockFsErrors(t *testing.T) {
|
|
|
|
errFake := errors.New("fake error")
|
2020-01-19 12:58:55 +00:00
|
|
|
fs := newMockOsFs(errFake, errFake, false, "123", os.TempDir())
|
2020-01-19 06:41:05 +00:00
|
|
|
u := dataprovider.User{}
|
2020-05-06 17:36:34 +00:00
|
|
|
u.Username = "test_username"
|
2020-01-19 06:41:05 +00:00
|
|
|
u.Permissions = make(map[string][]string)
|
|
|
|
u.Permissions["/"] = []string{dataprovider.PermAny}
|
|
|
|
u.HomeDir = os.TempDir()
|
|
|
|
c := Connection{
|
|
|
|
fs: fs,
|
|
|
|
User: u,
|
|
|
|
}
|
|
|
|
testfile := filepath.Join(u.HomeDir, "testfile")
|
|
|
|
request := sftp.NewRequest("Remove", testfile)
|
2020-05-06 17:36:34 +00:00
|
|
|
err := ioutil.WriteFile(testfile, []byte("test"), 0666)
|
|
|
|
assert.NoError(t, err)
|
2020-01-19 06:41:05 +00:00
|
|
|
_, err = c.Filewrite(request)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, sftp.ErrSSHFxFailure.Error())
|
|
|
|
|
2020-01-19 06:41:05 +00:00
|
|
|
var flags sftp.FileOpenFlags
|
|
|
|
flags.Write = true
|
|
|
|
flags.Trunc = false
|
|
|
|
flags.Append = true
|
2020-06-07 21:30:18 +00:00
|
|
|
_, err = c.handleSFTPUploadToExistingFile(flags, testfile, testfile, 0, "/testfile")
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, sftp.ErrSSHFxOpUnsupported.Error())
|
|
|
|
|
2020-06-07 21:30:18 +00:00
|
|
|
fs = newMockOsFs(errFake, nil, false, "123", os.TempDir())
|
|
|
|
c.fs = fs
|
|
|
|
err = c.handleSFTPRemove(testfile, request)
|
|
|
|
assert.EqualError(t, err, sftp.ErrSSHFxFailure.Error())
|
|
|
|
|
2020-06-26 21:38:29 +00:00
|
|
|
request = sftp.NewRequest("Rename", filepath.Base(testfile))
|
|
|
|
request.Target = filepath.Base(testfile) + "1"
|
|
|
|
err = c.handleSFTPRename(testfile, testfile+"1", request)
|
|
|
|
assert.EqualError(t, err, sftp.ErrSSHFxFailure.Error())
|
|
|
|
|
2020-05-06 17:36:34 +00:00
|
|
|
err = os.Remove(testfile)
|
|
|
|
assert.NoError(t, err)
|
2020-01-10 18:20:22 +00:00
|
|
|
}
|
|
|
|
|
2019-08-04 10:35:33 +00:00
|
|
|
func TestUploadFiles(t *testing.T) {
|
|
|
|
oldUploadMode := uploadMode
|
|
|
|
uploadMode = uploadModeAtomic
|
2020-01-19 06:41:05 +00:00
|
|
|
c := Connection{
|
2020-02-23 10:30:26 +00:00
|
|
|
fs: vfs.NewOsFs("123", os.TempDir(), nil),
|
2020-01-19 06:41:05 +00:00
|
|
|
}
|
2019-08-04 10:35:33 +00:00
|
|
|
var flags sftp.FileOpenFlags
|
|
|
|
flags.Write = true
|
|
|
|
flags.Trunc = true
|
2020-06-07 21:30:18 +00:00
|
|
|
_, err := c.handleSFTPUploadToExistingFile(flags, "missing_path", "other_missing_path", 0, "/missing_path")
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "upload to existing file must fail if one or both paths are invalid")
|
|
|
|
|
2019-08-04 10:35:33 +00:00
|
|
|
uploadMode = uploadModeStandard
|
2020-06-07 21:30:18 +00:00
|
|
|
_, err = c.handleSFTPUploadToExistingFile(flags, "missing_path", "other_missing_path", 0, "/missing_path")
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "upload to existing file must fail if one or both paths are invalid")
|
|
|
|
|
2019-08-04 10:35:33 +00:00
|
|
|
missingFile := "missing/relative/file.txt"
|
2020-05-06 17:36:34 +00:00
|
|
|
if runtime.GOOS == osWindows {
|
2019-08-04 10:35:33 +00:00
|
|
|
missingFile = "missing\\relative\\file.txt"
|
|
|
|
}
|
2020-06-07 21:30:18 +00:00
|
|
|
_, err = c.handleSFTPUploadToNewFile(".", missingFile, "/missing")
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "upload new file in missing path must fail")
|
|
|
|
|
2020-01-23 09:19:56 +00:00
|
|
|
c.fs = newMockOsFs(nil, nil, false, "123", os.TempDir())
|
2020-05-06 17:36:34 +00:00
|
|
|
f, err := ioutil.TempFile("", "temp")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
err = f.Close()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
2020-06-07 21:30:18 +00:00
|
|
|
_, err = c.handleSFTPUploadToExistingFile(flags, f.Name(), f.Name(), 123, f.Name())
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
if assert.Equal(t, 1, len(activeTransfers)) {
|
|
|
|
transfer := activeTransfers[0]
|
|
|
|
assert.Equal(t, int64(123), transfer.initialSize)
|
|
|
|
err = transfer.Close()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, 0, len(activeTransfers))
|
|
|
|
}
|
|
|
|
err = os.Remove(f.Name())
|
|
|
|
assert.NoError(t, err)
|
2019-08-04 10:35:33 +00:00
|
|
|
uploadMode = oldUploadMode
|
|
|
|
}
|
|
|
|
|
2019-08-11 12:53:37 +00:00
|
|
|
func TestWithInvalidHome(t *testing.T) {
|
2019-08-04 10:35:33 +00:00
|
|
|
u := dataprovider.User{}
|
2020-05-06 17:36:34 +00:00
|
|
|
u.HomeDir = "home_rel_path" //nolint:goconst
|
2020-04-09 21:32:42 +00:00
|
|
|
_, err := loginUser(u, dataprovider.SSHLoginMethodPassword, "", nil)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "login a user with an invalid home_dir must fail")
|
|
|
|
|
2020-01-19 12:58:55 +00:00
|
|
|
u.HomeDir = os.TempDir()
|
2020-05-06 17:36:34 +00:00
|
|
|
fs, err := u.GetFilesystem("123")
|
|
|
|
assert.NoError(t, err)
|
2019-08-11 12:53:37 +00:00
|
|
|
c := Connection{
|
|
|
|
User: u,
|
2020-01-19 06:41:05 +00:00
|
|
|
fs: fs,
|
2019-08-11 12:53:37 +00:00
|
|
|
}
|
2020-01-19 12:58:55 +00:00
|
|
|
_, err = c.fs.ResolvePath("../upper_path")
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "tested path is not a home subdir")
|
2019-08-04 10:35:33 +00:00
|
|
|
}
|
2019-08-24 12:41:15 +00:00
|
|
|
|
|
|
|
func TestSFTPCmdTargetPath(t *testing.T) {
|
|
|
|
u := dataprovider.User{}
|
2020-05-06 17:36:34 +00:00
|
|
|
if runtime.GOOS == osWindows {
|
2020-01-19 06:41:05 +00:00
|
|
|
u.HomeDir = "C:\\invalid_home"
|
|
|
|
} else {
|
|
|
|
u.HomeDir = "/invalid_home"
|
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
u.Username = "testuser"
|
2019-12-25 17:20:19 +00:00
|
|
|
u.Permissions = make(map[string][]string)
|
|
|
|
u.Permissions["/"] = []string{dataprovider.PermAny}
|
2020-05-06 17:36:34 +00:00
|
|
|
fs, err := u.GetFilesystem("123")
|
|
|
|
assert.NoError(t, err)
|
2019-08-24 12:41:15 +00:00
|
|
|
connection := Connection{
|
|
|
|
User: u,
|
2020-01-19 06:41:05 +00:00
|
|
|
fs: fs,
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
_, err = connection.getSFTPCmdTargetPath("invalid_path")
|
|
|
|
assert.EqualError(t, err, sftp.ErrSSHFxNoSuchFile.Error())
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
|
2019-11-15 11:15:07 +00:00
|
|
|
func TestGetSFTPErrorFromOSError(t *testing.T) {
|
|
|
|
err := os.ErrNotExist
|
2020-02-23 10:30:26 +00:00
|
|
|
fs := vfs.NewOsFs("", os.TempDir(), nil)
|
2020-01-19 06:41:05 +00:00
|
|
|
err = vfs.GetSFTPError(fs, err)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, sftp.ErrSSHFxNoSuchFile.Error())
|
|
|
|
|
2019-11-15 11:15:07 +00:00
|
|
|
err = os.ErrPermission
|
2020-01-19 06:41:05 +00:00
|
|
|
err = vfs.GetSFTPError(fs, err)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, sftp.ErrSSHFxPermissionDenied.Error())
|
2020-01-19 06:41:05 +00:00
|
|
|
err = vfs.GetSFTPError(fs, nil)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
2019-11-15 11:15:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestSetstatModeIgnore(t *testing.T) {
|
|
|
|
originalMode := setstatMode
|
|
|
|
setstatMode = 1
|
|
|
|
connection := Connection{}
|
|
|
|
err := connection.handleSFTPSetstat("invalid", nil)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
2019-11-15 11:15:07 +00:00
|
|
|
setstatMode = originalMode
|
|
|
|
}
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
func TestSFTPGetUsedQuota(t *testing.T) {
|
|
|
|
u := dataprovider.User{}
|
|
|
|
u.HomeDir = "home_rel_path"
|
|
|
|
u.Username = "test_invalid_user"
|
|
|
|
u.QuotaSize = 4096
|
|
|
|
u.QuotaFiles = 1
|
2019-12-25 17:20:19 +00:00
|
|
|
u.Permissions = make(map[string][]string)
|
|
|
|
u.Permissions["/"] = []string{dataprovider.PermAny}
|
2019-08-24 12:41:15 +00:00
|
|
|
connection := Connection{
|
|
|
|
User: u,
|
|
|
|
}
|
2020-06-16 20:49:18 +00:00
|
|
|
quotaResult := connection.hasSpace(false, "/")
|
|
|
|
assert.False(t, quotaResult.HasSpace)
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
|
2019-11-18 22:30:37 +00:00
|
|
|
func TestSupportedSSHCommands(t *testing.T) {
|
|
|
|
cmds := GetSupportedSSHCommands()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, len(supportedSSHCommands), len(cmds))
|
|
|
|
|
2019-11-18 22:30:37 +00:00
|
|
|
for _, c := range cmds {
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.True(t, utils.IsStringInSlice(c, supportedSSHCommands))
|
2019-11-18 22:30:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSSHCommandPath(t *testing.T) {
|
|
|
|
buf := make([]byte, 65535)
|
|
|
|
stdErrBuf := make([]byte, 65535)
|
|
|
|
mockSSHChannel := MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: nil,
|
|
|
|
}
|
|
|
|
connection := Connection{
|
|
|
|
channel: &mockSSHChannel,
|
|
|
|
}
|
|
|
|
sshCommand := sshCommand{
|
|
|
|
command: "test",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{},
|
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "", sshCommand.getDestPath())
|
|
|
|
|
2019-11-18 22:30:37 +00:00
|
|
|
sshCommand.args = []string{"-t", "/tmp/../path"}
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "/path", sshCommand.getDestPath())
|
|
|
|
|
2019-11-18 22:30:37 +00:00
|
|
|
sshCommand.args = []string{"-t", "/tmp/"}
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "/tmp/", sshCommand.getDestPath())
|
|
|
|
|
2019-11-18 22:30:37 +00:00
|
|
|
sshCommand.args = []string{"-t", "tmp/"}
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "/tmp/", sshCommand.getDestPath())
|
|
|
|
|
2019-11-26 21:26:42 +00:00
|
|
|
sshCommand.args = []string{"-t", "/tmp/../../../path"}
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "/path", sshCommand.getDestPath())
|
|
|
|
|
2019-11-26 21:26:42 +00:00
|
|
|
sshCommand.args = []string{"-t", ".."}
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "/", sshCommand.getDestPath())
|
|
|
|
|
2020-01-05 10:41:25 +00:00
|
|
|
sshCommand.args = []string{"-t", "."}
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "/", sshCommand.getDestPath())
|
|
|
|
|
2020-01-05 10:41:25 +00:00
|
|
|
sshCommand.args = []string{"-t", "//"}
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "/", sshCommand.getDestPath())
|
|
|
|
|
2020-01-05 10:41:25 +00:00
|
|
|
sshCommand.args = []string{"-t", "../.."}
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "/", sshCommand.getDestPath())
|
|
|
|
|
2020-01-05 10:41:25 +00:00
|
|
|
sshCommand.args = []string{"-t", "/.."}
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "/", sshCommand.getDestPath())
|
|
|
|
|
2020-02-08 22:33:06 +00:00
|
|
|
sshCommand.args = []string{"-f", "/a space.txt"}
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "/a space.txt", sshCommand.getDestPath())
|
2020-02-08 22:33:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestSSHParseCommandPayload(t *testing.T) {
|
2020-02-14 15:17:32 +00:00
|
|
|
cmd := "command -a -f /ab\\ à/some\\ spaces\\ \\ \\(\\).txt"
|
2020-02-08 22:33:06 +00:00
|
|
|
name, args, _ := parseCommandPayload(cmd)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "command", name)
|
|
|
|
assert.Equal(t, 3, len(args))
|
|
|
|
assert.Equal(t, "/ab à/some spaces ().txt", args[2])
|
|
|
|
|
2020-02-14 15:17:32 +00:00
|
|
|
_, _, err := parseCommandPayload("")
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "parsing invalid command must fail")
|
2019-11-18 22:30:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestSSHCommandErrors(t *testing.T) {
|
|
|
|
buf := make([]byte, 65535)
|
|
|
|
stdErrBuf := make([]byte, 65535)
|
|
|
|
readErr := fmt.Errorf("test read error")
|
|
|
|
mockSSHChannel := MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: readErr,
|
|
|
|
}
|
|
|
|
server, client := net.Pipe()
|
2020-05-06 17:36:34 +00:00
|
|
|
defer func() {
|
|
|
|
err := server.Close()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}()
|
|
|
|
defer func() {
|
|
|
|
err := client.Close()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}()
|
2019-12-25 17:20:19 +00:00
|
|
|
user := dataprovider.User{}
|
|
|
|
user.Permissions = make(map[string][]string)
|
|
|
|
user.Permissions["/"] = []string{dataprovider.PermAny}
|
2020-05-06 17:36:34 +00:00
|
|
|
fs, err := user.GetFilesystem("123")
|
|
|
|
assert.NoError(t, err)
|
2019-11-18 22:30:37 +00:00
|
|
|
connection := Connection{
|
|
|
|
channel: &mockSSHChannel,
|
|
|
|
netConn: client,
|
2019-12-25 17:20:19 +00:00
|
|
|
User: user,
|
2020-01-19 06:41:05 +00:00
|
|
|
fs: fs,
|
2019-11-18 22:30:37 +00:00
|
|
|
}
|
|
|
|
cmd := sshCommand{
|
|
|
|
command: "md5sum",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{},
|
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
err = cmd.handle()
|
|
|
|
assert.Error(t, err, "ssh command must fail, we are sending a fake error")
|
|
|
|
|
2019-11-18 22:30:37 +00:00
|
|
|
cmd = sshCommand{
|
|
|
|
command: "md5sum",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"/../../test_file.dat"},
|
|
|
|
}
|
|
|
|
err = cmd.handle()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
|
|
|
|
|
2019-11-26 21:26:42 +00:00
|
|
|
cmd = sshCommand{
|
|
|
|
command: "git-receive-pack",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"/../../testrepo"},
|
|
|
|
}
|
|
|
|
err = cmd.handle()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
|
|
|
|
|
2020-06-13 20:48:51 +00:00
|
|
|
cmd.connection.User.HomeDir = filepath.Clean(os.TempDir())
|
2019-11-26 21:26:42 +00:00
|
|
|
cmd.connection.User.QuotaFiles = 1
|
|
|
|
cmd.connection.User.UsedQuotaFiles = 2
|
2020-05-06 17:36:34 +00:00
|
|
|
fs, err = cmd.connection.User.GetFilesystem("123")
|
|
|
|
assert.NoError(t, err)
|
2020-01-19 12:58:55 +00:00
|
|
|
cmd.connection.fs = fs
|
2019-11-26 21:26:42 +00:00
|
|
|
err = cmd.handle()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, errQuotaExceeded.Error())
|
|
|
|
|
2019-11-26 21:26:42 +00:00
|
|
|
cmd.connection.User.QuotaFiles = 0
|
|
|
|
cmd.connection.User.UsedQuotaFiles = 0
|
2019-12-25 17:20:19 +00:00
|
|
|
cmd.connection.User.Permissions = make(map[string][]string)
|
|
|
|
cmd.connection.User.Permissions["/"] = []string{dataprovider.PermListItems}
|
2019-11-26 21:26:42 +00:00
|
|
|
err = cmd.handle()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, errPermissionDenied.Error())
|
|
|
|
|
2019-12-25 17:20:19 +00:00
|
|
|
cmd.connection.User.Permissions["/"] = []string{dataprovider.PermAny}
|
2019-11-26 21:26:42 +00:00
|
|
|
cmd.command = "invalid_command"
|
|
|
|
command, err := cmd.getSystemCommand()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
2019-11-26 21:26:42 +00:00
|
|
|
err = cmd.executeSystemCommand(command)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "invalid command must fail")
|
|
|
|
|
2019-11-26 21:26:42 +00:00
|
|
|
command, err = cmd.getSystemCommand()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
_, err = command.cmd.StderrPipe()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
2019-11-26 21:26:42 +00:00
|
|
|
err = cmd.executeSystemCommand(command)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "command must fail, pipe was already assigned")
|
|
|
|
|
2019-11-26 21:26:42 +00:00
|
|
|
err = cmd.executeSystemCommand(command)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "command must fail, pipe was already assigned")
|
|
|
|
|
2019-11-26 21:26:42 +00:00
|
|
|
command, err = cmd.getSystemCommand()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
_, err = command.cmd.StdoutPipe()
|
|
|
|
assert.NoError(t, err)
|
2019-11-26 21:26:42 +00:00
|
|
|
err = cmd.executeSystemCommand(command)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "command must fail, pipe was already assigned")
|
2020-06-13 20:48:51 +00:00
|
|
|
|
|
|
|
cmd = sshCommand{
|
|
|
|
command: "sftpgo-remove",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"/../../src"},
|
|
|
|
}
|
|
|
|
err = cmd.handle()
|
|
|
|
assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
|
|
|
|
|
|
|
|
cmd = sshCommand{
|
|
|
|
command: "sftpgo-copy",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"/../../test_src", "."},
|
|
|
|
}
|
|
|
|
err = cmd.handle()
|
|
|
|
assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
|
|
|
|
cmd.connection.fs = fs
|
|
|
|
_, _, err = cmd.resolveCopyPaths(".", "../adir")
|
|
|
|
assert.Error(t, err)
|
|
|
|
cmd = sshCommand{
|
|
|
|
command: "sftpgo-copy",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"src", "dst"},
|
|
|
|
}
|
|
|
|
cmd.connection.User.Permissions = make(map[string][]string)
|
|
|
|
cmd.connection.User.Permissions["/"] = []string{dataprovider.PermDownload}
|
2020-06-26 21:38:29 +00:00
|
|
|
src, dst, err := cmd.getCopyPaths()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.False(t, cmd.hasCopyPermissions(src, dst, nil))
|
|
|
|
|
2020-06-13 20:48:51 +00:00
|
|
|
cmd.connection.User.Permissions = make(map[string][]string)
|
|
|
|
cmd.connection.User.Permissions["/"] = []string{dataprovider.PermAny}
|
|
|
|
if runtime.GOOS != osWindows {
|
|
|
|
aDir := filepath.Join(os.TempDir(), "adir")
|
|
|
|
err = os.MkdirAll(aDir, os.ModePerm)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
tmpFile := filepath.Join(aDir, "testcopy")
|
|
|
|
err = ioutil.WriteFile(tmpFile, []byte("aaa"), os.ModePerm)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
err = os.Chmod(aDir, 0001)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
err = cmd.checkCopyDestination(tmpFile)
|
|
|
|
assert.Error(t, err)
|
|
|
|
err = os.Chmod(aDir, os.ModePerm)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
err = os.Remove(tmpFile)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}
|
2019-11-26 21:26:42 +00:00
|
|
|
}
|
|
|
|
|
2020-03-01 21:10:29 +00:00
|
|
|
func TestCommandsWithExtensionsFilter(t *testing.T) {
|
|
|
|
buf := make([]byte, 65535)
|
|
|
|
stdErrBuf := make([]byte, 65535)
|
|
|
|
mockSSHChannel := MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
}
|
|
|
|
server, client := net.Pipe()
|
|
|
|
defer server.Close()
|
|
|
|
defer client.Close()
|
|
|
|
user := dataprovider.User{
|
|
|
|
Username: "test",
|
|
|
|
HomeDir: os.TempDir(),
|
|
|
|
Status: 1,
|
|
|
|
}
|
|
|
|
user.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
|
2020-03-02 09:13:49 +00:00
|
|
|
{
|
2020-03-01 21:10:29 +00:00
|
|
|
Path: "/subdir",
|
|
|
|
AllowedExtensions: []string{".jpg"},
|
|
|
|
DeniedExtensions: []string{},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-05-06 17:36:34 +00:00
|
|
|
fs, err := user.GetFilesystem("123")
|
|
|
|
assert.NoError(t, err)
|
2020-03-01 21:10:29 +00:00
|
|
|
connection := Connection{
|
|
|
|
channel: &mockSSHChannel,
|
|
|
|
netConn: client,
|
|
|
|
User: user,
|
|
|
|
fs: fs,
|
|
|
|
}
|
|
|
|
cmd := sshCommand{
|
|
|
|
command: "md5sum",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"subdir/test.png"},
|
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
err = cmd.handleHashCommands()
|
|
|
|
assert.EqualError(t, err, errPermissionDenied.Error())
|
|
|
|
|
2020-03-01 21:10:29 +00:00
|
|
|
cmd = sshCommand{
|
|
|
|
command: "rsync",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
|
|
|
|
}
|
|
|
|
_, err = cmd.getSystemCommand()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, errUnsupportedConfig.Error())
|
|
|
|
|
2020-03-01 21:10:29 +00:00
|
|
|
cmd = sshCommand{
|
|
|
|
command: "git-receive-pack",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"/subdir"},
|
|
|
|
}
|
|
|
|
_, err = cmd.getSystemCommand()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, errUnsupportedConfig.Error())
|
|
|
|
|
2020-03-01 21:10:29 +00:00
|
|
|
cmd = sshCommand{
|
|
|
|
command: "git-receive-pack",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"/subdir/dir"},
|
|
|
|
}
|
|
|
|
_, err = cmd.getSystemCommand()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, errUnsupportedConfig.Error())
|
|
|
|
|
2020-03-01 21:10:29 +00:00
|
|
|
cmd = sshCommand{
|
|
|
|
command: "git-receive-pack",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"/adir/subdir"},
|
|
|
|
}
|
|
|
|
_, err = cmd.getSystemCommand()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
2020-03-01 21:10:29 +00:00
|
|
|
}
|
|
|
|
|
2020-01-19 06:41:05 +00:00
|
|
|
func TestSSHCommandsRemoteFs(t *testing.T) {
|
|
|
|
buf := make([]byte, 65535)
|
|
|
|
stdErrBuf := make([]byte, 65535)
|
|
|
|
mockSSHChannel := MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
}
|
|
|
|
server, client := net.Pipe()
|
2020-05-06 17:36:34 +00:00
|
|
|
defer func() {
|
|
|
|
err := server.Close()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}()
|
|
|
|
defer func() {
|
|
|
|
err := client.Close()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}()
|
2020-01-19 06:41:05 +00:00
|
|
|
user := dataprovider.User{}
|
|
|
|
user.FsConfig = dataprovider.Filesystem{
|
2020-05-06 17:36:34 +00:00
|
|
|
Provider: 1,
|
|
|
|
S3Config: vfs.S3FsConfig{
|
|
|
|
Bucket: "s3bucket",
|
|
|
|
Endpoint: "endpoint",
|
|
|
|
Region: "eu-west-1",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
fs, err := user.GetFilesystem("123")
|
|
|
|
assert.NoError(t, err)
|
2020-01-19 06:41:05 +00:00
|
|
|
connection := Connection{
|
|
|
|
channel: &mockSSHChannel,
|
|
|
|
netConn: client,
|
|
|
|
User: user,
|
|
|
|
fs: fs,
|
|
|
|
}
|
|
|
|
cmd := sshCommand{
|
|
|
|
command: "md5sum",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{},
|
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
err = cmd.handleHashCommands()
|
|
|
|
assert.Error(t, err, "command must fail for a non local filesystem")
|
|
|
|
|
2020-01-19 06:41:05 +00:00
|
|
|
command, err := cmd.getSystemCommand()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
2020-01-19 06:41:05 +00:00
|
|
|
err = cmd.executeSystemCommand(command)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "command must fail for a non local filesystem")
|
2020-06-13 20:48:51 +00:00
|
|
|
cmd = sshCommand{
|
|
|
|
command: "sftpgo-copy",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{},
|
|
|
|
}
|
|
|
|
err = cmd.handeSFTPGoCopy()
|
|
|
|
assert.Error(t, err)
|
|
|
|
cmd = sshCommand{
|
|
|
|
command: "sftpgo-remove",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{},
|
|
|
|
}
|
|
|
|
err = cmd.handeSFTPGoRemove()
|
|
|
|
assert.Error(t, err)
|
2020-01-19 06:41:05 +00:00
|
|
|
}
|
|
|
|
|
2020-02-24 17:54:35 +00:00
|
|
|
func TestGitVirtualFolders(t *testing.T) {
|
|
|
|
permissions := make(map[string][]string)
|
|
|
|
permissions["/"] = []string{dataprovider.PermAny}
|
|
|
|
user := dataprovider.User{
|
|
|
|
Permissions: permissions,
|
|
|
|
HomeDir: os.TempDir(),
|
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
fs, err := user.GetFilesystem("123")
|
|
|
|
assert.NoError(t, err)
|
2020-02-24 17:54:35 +00:00
|
|
|
conn := Connection{
|
|
|
|
User: user,
|
|
|
|
fs: fs,
|
|
|
|
}
|
|
|
|
cmd := sshCommand{
|
|
|
|
command: "git-receive-pack",
|
|
|
|
connection: conn,
|
|
|
|
args: []string{"/vdir"},
|
|
|
|
}
|
|
|
|
cmd.connection.User.VirtualFolders = append(cmd.connection.User.VirtualFolders, vfs.VirtualFolder{
|
2020-06-07 21:30:18 +00:00
|
|
|
BaseVirtualFolder: vfs.BaseVirtualFolder{
|
|
|
|
MappedPath: os.TempDir(),
|
|
|
|
},
|
2020-02-24 17:54:35 +00:00
|
|
|
VirtualPath: "/vdir",
|
|
|
|
})
|
2020-05-06 17:36:34 +00:00
|
|
|
_, err = cmd.getSystemCommand()
|
2020-06-15 21:32:12 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
cmd.args = []string{"/"}
|
|
|
|
_, err = cmd.getSystemCommand()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, errUnsupportedConfig.Error())
|
2020-06-15 21:32:12 +00:00
|
|
|
cmd.args = []string{"/vdir1"}
|
|
|
|
_, err = cmd.getSystemCommand()
|
|
|
|
assert.NoError(t, err)
|
2020-05-06 17:36:34 +00:00
|
|
|
|
2020-02-24 17:54:35 +00:00
|
|
|
cmd.connection.User.VirtualFolders = nil
|
|
|
|
cmd.connection.User.VirtualFolders = append(cmd.connection.User.VirtualFolders, vfs.VirtualFolder{
|
2020-06-07 21:30:18 +00:00
|
|
|
BaseVirtualFolder: vfs.BaseVirtualFolder{
|
|
|
|
MappedPath: os.TempDir(),
|
|
|
|
},
|
2020-02-24 17:54:35 +00:00
|
|
|
VirtualPath: "/vdir",
|
|
|
|
})
|
|
|
|
cmd.args = []string{"/vdir/subdir"}
|
|
|
|
_, err = cmd.getSystemCommand()
|
2020-06-15 21:32:12 +00:00
|
|
|
assert.NoError(t, err)
|
2020-05-06 17:36:34 +00:00
|
|
|
|
2020-02-24 17:54:35 +00:00
|
|
|
cmd.args = []string{"/adir/subdir"}
|
|
|
|
_, err = cmd.getSystemCommand()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
2020-02-24 17:54:35 +00:00
|
|
|
}
|
|
|
|
|
2019-11-29 14:24:56 +00:00
|
|
|
func TestRsyncOptions(t *testing.T) {
|
2019-12-25 17:20:19 +00:00
|
|
|
permissions := make(map[string][]string)
|
|
|
|
permissions["/"] = []string{dataprovider.PermAny}
|
2020-01-19 06:41:05 +00:00
|
|
|
user := dataprovider.User{
|
|
|
|
Permissions: permissions,
|
|
|
|
HomeDir: os.TempDir(),
|
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
fs, err := user.GetFilesystem("123")
|
|
|
|
assert.NoError(t, err)
|
2019-11-29 14:24:56 +00:00
|
|
|
conn := Connection{
|
2020-01-19 06:41:05 +00:00
|
|
|
User: user,
|
|
|
|
fs: fs,
|
2019-11-29 14:24:56 +00:00
|
|
|
}
|
|
|
|
sshCmd := sshCommand{
|
|
|
|
command: "rsync",
|
|
|
|
connection: conn,
|
|
|
|
args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
|
|
|
|
}
|
|
|
|
cmd, err := sshCmd.getSystemCommand()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.True(t, utils.IsStringInSlice("--safe-links", cmd.cmd.Args),
|
|
|
|
"--safe-links must be added if the user has the create symlinks permission")
|
|
|
|
|
2019-12-25 17:20:19 +00:00
|
|
|
permissions["/"] = []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermCreateDirs,
|
|
|
|
dataprovider.PermListItems, dataprovider.PermOverwrite, dataprovider.PermDelete, dataprovider.PermRename}
|
2020-01-19 06:41:05 +00:00
|
|
|
user.Permissions = permissions
|
2020-05-06 17:36:34 +00:00
|
|
|
fs, err = user.GetFilesystem("123")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
2019-11-29 14:24:56 +00:00
|
|
|
conn = Connection{
|
2020-01-19 06:41:05 +00:00
|
|
|
User: user,
|
|
|
|
fs: fs,
|
2019-11-29 14:24:56 +00:00
|
|
|
}
|
|
|
|
sshCmd = sshCommand{
|
|
|
|
command: "rsync",
|
|
|
|
connection: conn,
|
|
|
|
args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
|
|
|
|
}
|
|
|
|
cmd, err = sshCmd.getSystemCommand()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.True(t, utils.IsStringInSlice("--munge-links", cmd.cmd.Args),
|
|
|
|
"--munge-links must be added if the user has the create symlinks permission")
|
|
|
|
|
2020-02-24 17:54:35 +00:00
|
|
|
sshCmd.connection.User.VirtualFolders = append(sshCmd.connection.User.VirtualFolders, vfs.VirtualFolder{
|
2020-06-07 21:30:18 +00:00
|
|
|
BaseVirtualFolder: vfs.BaseVirtualFolder{
|
|
|
|
MappedPath: os.TempDir(),
|
|
|
|
},
|
2020-02-24 17:54:35 +00:00
|
|
|
VirtualPath: "/vdir",
|
|
|
|
})
|
|
|
|
_, err = sshCmd.getSystemCommand()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, errUnsupportedConfig.Error())
|
2019-11-29 14:24:56 +00:00
|
|
|
}
|
|
|
|
|
2020-06-18 20:38:03 +00:00
|
|
|
func TestSpaceForCrossRename(t *testing.T) {
|
|
|
|
if runtime.GOOS == osWindows {
|
|
|
|
t.Skip("this test is not available on Windows")
|
|
|
|
}
|
|
|
|
permissions := make(map[string][]string)
|
|
|
|
permissions["/"] = []string{dataprovider.PermAny}
|
|
|
|
user := dataprovider.User{
|
|
|
|
Permissions: permissions,
|
|
|
|
HomeDir: os.TempDir(),
|
|
|
|
}
|
|
|
|
fs, err := user.GetFilesystem("123")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
conn := Connection{
|
|
|
|
User: user,
|
|
|
|
fs: fs,
|
|
|
|
}
|
|
|
|
quotaResult := vfs.QuotaCheckResult{
|
|
|
|
HasSpace: true,
|
|
|
|
}
|
|
|
|
assert.False(t, conn.hasSpaceForCrossRename(quotaResult, -1, filepath.Join(os.TempDir(), "a missing file")))
|
|
|
|
testDir := filepath.Join(os.TempDir(), "dir")
|
|
|
|
err = os.MkdirAll(testDir, os.ModePerm)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
err = ioutil.WriteFile(filepath.Join(testDir, "afile"), []byte("content"), os.ModePerm)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
err = os.Chmod(testDir, 0001)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.False(t, conn.hasSpaceForCrossRename(quotaResult, -1, testDir))
|
|
|
|
err = os.Chmod(testDir, os.ModePerm)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
err = os.RemoveAll(testDir)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}
|
|
|
|
|
2020-06-15 21:32:12 +00:00
|
|
|
func TestSystemCommandSizeForPath(t *testing.T) {
|
|
|
|
permissions := make(map[string][]string)
|
|
|
|
permissions["/"] = []string{dataprovider.PermAny}
|
|
|
|
user := dataprovider.User{
|
|
|
|
Permissions: permissions,
|
|
|
|
HomeDir: os.TempDir(),
|
|
|
|
}
|
|
|
|
fs, err := user.GetFilesystem("123")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
conn := Connection{
|
|
|
|
User: user,
|
|
|
|
fs: fs,
|
|
|
|
}
|
|
|
|
sshCmd := sshCommand{
|
|
|
|
command: "rsync",
|
|
|
|
connection: conn,
|
|
|
|
args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
|
|
|
|
}
|
|
|
|
_, _, err = sshCmd.getSizeForPath("missing path")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
testDir := filepath.Join(os.TempDir(), "dir")
|
|
|
|
err = os.MkdirAll(testDir, os.ModePerm)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
testFile := filepath.Join(testDir, "testfile")
|
|
|
|
err = ioutil.WriteFile(testFile, []byte("test content"), os.ModePerm)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
err = os.Symlink(testFile, testFile+".link")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
numFiles, size, err := sshCmd.getSizeForPath(testFile + ".link")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, 0, numFiles)
|
|
|
|
assert.Equal(t, int64(0), size)
|
|
|
|
numFiles, size, err = sshCmd.getSizeForPath(testFile)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, 1, numFiles)
|
|
|
|
assert.Equal(t, int64(12), size)
|
|
|
|
if runtime.GOOS != osWindows {
|
|
|
|
err = os.Chmod(testDir, 0001)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
_, _, err = sshCmd.getSizeForPath(testFile)
|
|
|
|
assert.Error(t, err)
|
|
|
|
err = os.Chmod(testDir, os.ModePerm)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}
|
|
|
|
err = os.RemoveAll(testDir)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}
|
|
|
|
|
2019-11-26 21:26:42 +00:00
|
|
|
func TestSystemCommandErrors(t *testing.T) {
|
|
|
|
buf := make([]byte, 65535)
|
|
|
|
stdErrBuf := make([]byte, 65535)
|
|
|
|
readErr := fmt.Errorf("test read error")
|
|
|
|
writeErr := fmt.Errorf("test write error")
|
|
|
|
mockSSHChannel := MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: nil,
|
|
|
|
WriteError: writeErr,
|
|
|
|
}
|
|
|
|
server, client := net.Pipe()
|
2020-05-06 17:36:34 +00:00
|
|
|
defer func() {
|
|
|
|
err := server.Close()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}()
|
|
|
|
defer func() {
|
|
|
|
err := client.Close()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}()
|
2019-12-25 17:20:19 +00:00
|
|
|
permissions := make(map[string][]string)
|
|
|
|
permissions["/"] = []string{dataprovider.PermAny}
|
2020-06-15 21:32:12 +00:00
|
|
|
homeDir := filepath.Join(os.TempDir(), "adir")
|
|
|
|
err := os.MkdirAll(homeDir, os.ModePerm)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
err = ioutil.WriteFile(filepath.Join(homeDir, "afile"), []byte("content"), os.ModePerm)
|
|
|
|
assert.NoError(t, err)
|
2020-01-19 06:41:05 +00:00
|
|
|
user := dataprovider.User{
|
|
|
|
Permissions: permissions,
|
2020-06-15 21:32:12 +00:00
|
|
|
HomeDir: homeDir,
|
2020-01-19 06:41:05 +00:00
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
fs, err := user.GetFilesystem("123")
|
|
|
|
assert.NoError(t, err)
|
2019-11-26 21:26:42 +00:00
|
|
|
connection := Connection{
|
|
|
|
channel: &mockSSHChannel,
|
|
|
|
netConn: client,
|
2020-01-19 06:41:05 +00:00
|
|
|
User: user,
|
|
|
|
fs: fs,
|
2019-11-26 21:26:42 +00:00
|
|
|
}
|
2020-06-15 21:32:12 +00:00
|
|
|
var sshCmd sshCommand
|
|
|
|
if runtime.GOOS == osWindows {
|
|
|
|
sshCmd = sshCommand{
|
|
|
|
command: "dir",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"/"},
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sshCmd = sshCommand{
|
|
|
|
command: "ls",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"/"},
|
|
|
|
}
|
2019-11-26 21:26:42 +00:00
|
|
|
}
|
|
|
|
systemCmd, err := sshCmd.getSystemCommand()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
2019-11-26 21:26:42 +00:00
|
|
|
systemCmd.cmd.Dir = os.TempDir()
|
2020-06-15 21:32:12 +00:00
|
|
|
// FIXME: the command completes but the fake client is unable to read the response
|
2020-05-06 17:36:34 +00:00
|
|
|
// no error is reported in this case. We can see that the expected code is executed
|
|
|
|
// reading the test coverage
|
|
|
|
sshCmd.executeSystemCommand(systemCmd) //nolint:errcheck
|
2019-11-26 21:26:42 +00:00
|
|
|
|
|
|
|
mockSSHChannel = MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: readErr,
|
|
|
|
WriteError: nil,
|
|
|
|
}
|
|
|
|
sshCmd.connection.channel = &mockSSHChannel
|
2020-01-10 18:20:22 +00:00
|
|
|
transfer := Transfer{
|
|
|
|
transferType: transferDownload,
|
|
|
|
lock: new(sync.Mutex)}
|
2019-11-26 21:26:42 +00:00
|
|
|
destBuff := make([]byte, 65535)
|
|
|
|
dst := bytes.NewBuffer(destBuff)
|
2020-06-18 20:38:03 +00:00
|
|
|
_, err = transfer.copyFromReaderToWriter(dst, sshCmd.connection.channel)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, readErr.Error())
|
2019-11-26 21:26:42 +00:00
|
|
|
|
|
|
|
mockSSHChannel = MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: nil,
|
|
|
|
WriteError: nil,
|
|
|
|
}
|
|
|
|
sshCmd.connection.channel = &mockSSHChannel
|
2020-06-18 20:38:03 +00:00
|
|
|
transfer.maxWriteSize = 1
|
|
|
|
_, err = transfer.copyFromReaderToWriter(dst, sshCmd.connection.channel)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, errQuotaExceeded.Error())
|
2019-11-26 21:26:42 +00:00
|
|
|
|
|
|
|
mockSSHChannel = MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: nil,
|
|
|
|
WriteError: nil,
|
|
|
|
ShortWriteErr: true,
|
|
|
|
}
|
|
|
|
sshCmd.connection.channel = &mockSSHChannel
|
2020-06-18 20:38:03 +00:00
|
|
|
_, err = transfer.copyFromReaderToWriter(sshCmd.connection.channel, dst)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, io.ErrShortWrite.Error())
|
2020-06-18 20:38:03 +00:00
|
|
|
transfer.maxWriteSize = -1
|
|
|
|
_, err = transfer.copyFromReaderToWriter(sshCmd.connection.channel, dst)
|
2020-06-15 21:32:12 +00:00
|
|
|
assert.EqualError(t, err, errQuotaExceeded.Error())
|
|
|
|
err = os.RemoveAll(homeDir)
|
|
|
|
assert.NoError(t, err)
|
2019-11-18 22:30:37 +00:00
|
|
|
}
|
|
|
|
|
2020-01-23 09:19:56 +00:00
|
|
|
func TestTransferUpdateQuota(t *testing.T) {
|
|
|
|
transfer := Transfer{
|
|
|
|
transferType: transferUpload,
|
|
|
|
bytesReceived: 123,
|
|
|
|
lock: new(sync.Mutex)}
|
|
|
|
transfer.TransferError(errors.New("fake error"))
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.False(t, transfer.updateQuota(1))
|
2020-01-23 09:19:56 +00:00
|
|
|
}
|
|
|
|
|
2019-11-18 22:30:37 +00:00
|
|
|
func TestGetConnectionInfo(t *testing.T) {
|
|
|
|
c := ConnectionStatus{
|
|
|
|
Username: "test_user",
|
|
|
|
ConnectionID: "123",
|
|
|
|
ClientVersion: "client",
|
|
|
|
RemoteAddress: "127.0.0.1:1234",
|
|
|
|
Protocol: protocolSSH,
|
|
|
|
SSHCommand: "sha1sum /test_file.dat",
|
|
|
|
}
|
|
|
|
info := c.GetConnectionInfo()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Contains(t, info, "sha1sum /test_file.dat")
|
2019-11-18 22:30:37 +00:00
|
|
|
}
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
func TestSCPFileMode(t *testing.T) {
|
|
|
|
mode := getFileModeAsString(0, true)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "0755", mode)
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
mode = getFileModeAsString(0700, true)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "0700", mode)
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
mode = getFileModeAsString(0750, true)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "0750", mode)
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
mode = getFileModeAsString(0777, true)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "0777", mode)
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
mode = getFileModeAsString(0640, false)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "0640", mode)
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
mode = getFileModeAsString(0600, false)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "0600", mode)
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
mode = getFileModeAsString(0, false)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "0644", mode)
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
fileMode := uint32(0777)
|
|
|
|
fileMode = fileMode | uint32(os.ModeSetgid)
|
|
|
|
fileMode = fileMode | uint32(os.ModeSetuid)
|
|
|
|
fileMode = fileMode | uint32(os.ModeSticky)
|
|
|
|
mode = getFileModeAsString(os.FileMode(fileMode), false)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "7777", mode)
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
fileMode = uint32(0644)
|
|
|
|
fileMode = fileMode | uint32(os.ModeSetgid)
|
|
|
|
mode = getFileModeAsString(os.FileMode(fileMode), false)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "4644", mode)
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
fileMode = uint32(0600)
|
|
|
|
fileMode = fileMode | uint32(os.ModeSetuid)
|
|
|
|
mode = getFileModeAsString(os.FileMode(fileMode), false)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "2600", mode)
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
fileMode = uint32(0044)
|
|
|
|
fileMode = fileMode | uint32(os.ModeSticky)
|
|
|
|
mode = getFileModeAsString(os.FileMode(fileMode), false)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Equal(t, "1044", mode)
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestSCPParseUploadMessage(t *testing.T) {
|
|
|
|
buf := make([]byte, 65535)
|
|
|
|
stdErrBuf := make([]byte, 65535)
|
|
|
|
mockSSHChannel := MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: nil,
|
|
|
|
}
|
2019-09-11 14:29:56 +00:00
|
|
|
connection := Connection{
|
|
|
|
channel: &mockSSHChannel,
|
2020-04-22 10:26:18 +00:00
|
|
|
fs: vfs.NewOsFs("", os.TempDir(), nil),
|
2019-09-11 14:29:56 +00:00
|
|
|
}
|
2019-08-24 12:41:15 +00:00
|
|
|
scpCommand := scpCommand{
|
2019-11-18 22:30:37 +00:00
|
|
|
sshCommand: sshCommand{
|
|
|
|
command: "scp",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"-t", "/tmp"},
|
|
|
|
},
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
_, _, err := scpCommand.parseUploadMessage("invalid")
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "parsing invalid upload message must fail")
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
_, _, err = scpCommand.parseUploadMessage("D0755 0")
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "parsing incomplete upload message must fail")
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
_, _, err = scpCommand.parseUploadMessage("D0755 invalidsize testdir")
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "parsing upload message with invalid size must fail")
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
_, _, err = scpCommand.parseUploadMessage("D0755 0 ")
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "parsing upload message with invalid name must fail")
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestSCPProtocolMessages(t *testing.T) {
|
|
|
|
buf := make([]byte, 65535)
|
|
|
|
stdErrBuf := make([]byte, 65535)
|
|
|
|
readErr := fmt.Errorf("test read error")
|
|
|
|
writeErr := fmt.Errorf("test write error")
|
|
|
|
mockSSHChannel := MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: readErr,
|
|
|
|
WriteError: writeErr,
|
|
|
|
}
|
2019-09-11 14:29:56 +00:00
|
|
|
connection := Connection{
|
|
|
|
channel: &mockSSHChannel,
|
|
|
|
}
|
2019-08-24 12:41:15 +00:00
|
|
|
scpCommand := scpCommand{
|
2019-11-18 22:30:37 +00:00
|
|
|
sshCommand: sshCommand{
|
|
|
|
command: "scp",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"-t", "/tmp"},
|
|
|
|
},
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
_, err := scpCommand.readProtocolMessage()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, readErr.Error())
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
err = scpCommand.sendConfirmationMessage()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, writeErr.Error())
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
err = scpCommand.sendProtocolMessage("E\n")
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, writeErr.Error())
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
_, err = scpCommand.getNextUploadProtocolMessage()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, readErr.Error())
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
mockSSHChannel = MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer([]byte("T1183832947 0 1183833773 0\n")),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: nil,
|
|
|
|
WriteError: writeErr,
|
|
|
|
}
|
2019-09-11 14:29:56 +00:00
|
|
|
scpCommand.connection.channel = &mockSSHChannel
|
2019-08-24 12:41:15 +00:00
|
|
|
_, err = scpCommand.getNextUploadProtocolMessage()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, writeErr.Error())
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
respBuffer := []byte{0x02}
|
|
|
|
protocolErrorMsg := "protocol error msg"
|
|
|
|
respBuffer = append(respBuffer, protocolErrorMsg...)
|
|
|
|
respBuffer = append(respBuffer, 0x0A)
|
|
|
|
mockSSHChannel = MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(respBuffer),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: nil,
|
|
|
|
WriteError: nil,
|
|
|
|
}
|
2019-09-11 14:29:56 +00:00
|
|
|
scpCommand.connection.channel = &mockSSHChannel
|
2019-08-24 12:41:15 +00:00
|
|
|
err = scpCommand.readConfirmationMessage()
|
2020-05-06 17:36:34 +00:00
|
|
|
if assert.Error(t, err) {
|
|
|
|
assert.Equal(t, protocolErrorMsg, err.Error())
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSCPTestDownloadProtocolMessages(t *testing.T) {
|
|
|
|
buf := make([]byte, 65535)
|
|
|
|
stdErrBuf := make([]byte, 65535)
|
|
|
|
readErr := fmt.Errorf("test read error")
|
|
|
|
writeErr := fmt.Errorf("test write error")
|
|
|
|
mockSSHChannel := MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: readErr,
|
|
|
|
WriteError: writeErr,
|
|
|
|
}
|
2019-09-11 14:29:56 +00:00
|
|
|
connection := Connection{
|
|
|
|
channel: &mockSSHChannel,
|
|
|
|
}
|
2019-08-24 12:41:15 +00:00
|
|
|
scpCommand := scpCommand{
|
2019-11-18 22:30:37 +00:00
|
|
|
sshCommand: sshCommand{
|
|
|
|
command: "scp",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"-f", "-p", "/tmp"},
|
|
|
|
},
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
path := "testDir"
|
2020-06-08 17:40:17 +00:00
|
|
|
err := os.Mkdir(path, os.ModePerm)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
stat, err := os.Stat(path)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
err = scpCommand.sendDownloadProtocolMessages(path, stat)
|
|
|
|
assert.EqualError(t, err, writeErr.Error())
|
2019-08-24 12:41:15 +00:00
|
|
|
|
|
|
|
mockSSHChannel = MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: readErr,
|
|
|
|
WriteError: nil,
|
|
|
|
}
|
|
|
|
|
|
|
|
err = scpCommand.sendDownloadProtocolMessages(path, stat)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, readErr.Error())
|
2019-08-24 12:41:15 +00:00
|
|
|
|
|
|
|
mockSSHChannel = MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: readErr,
|
|
|
|
WriteError: writeErr,
|
|
|
|
}
|
|
|
|
scpCommand.args = []string{"-f", "/tmp"}
|
2019-09-11 14:29:56 +00:00
|
|
|
scpCommand.connection.channel = &mockSSHChannel
|
2019-08-24 12:41:15 +00:00
|
|
|
err = scpCommand.sendDownloadProtocolMessages(path, stat)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, writeErr.Error())
|
2019-08-24 12:41:15 +00:00
|
|
|
|
|
|
|
mockSSHChannel = MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: readErr,
|
|
|
|
WriteError: nil,
|
|
|
|
}
|
2019-09-11 14:29:56 +00:00
|
|
|
scpCommand.connection.channel = &mockSSHChannel
|
2019-08-24 12:41:15 +00:00
|
|
|
err = scpCommand.sendDownloadProtocolMessages(path, stat)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, readErr.Error())
|
|
|
|
|
|
|
|
err = os.Remove(path)
|
|
|
|
assert.NoError(t, err)
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestSCPCommandHandleErrors(t *testing.T) {
|
|
|
|
buf := make([]byte, 65535)
|
|
|
|
stdErrBuf := make([]byte, 65535)
|
|
|
|
readErr := fmt.Errorf("test read error")
|
|
|
|
writeErr := fmt.Errorf("test write error")
|
|
|
|
mockSSHChannel := MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: readErr,
|
|
|
|
WriteError: writeErr,
|
|
|
|
}
|
2019-10-10 07:04:17 +00:00
|
|
|
server, client := net.Pipe()
|
2020-05-06 17:36:34 +00:00
|
|
|
defer func() {
|
|
|
|
err := server.Close()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}()
|
|
|
|
defer func() {
|
|
|
|
err := client.Close()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}()
|
2019-09-11 14:29:56 +00:00
|
|
|
connection := Connection{
|
|
|
|
channel: &mockSSHChannel,
|
2019-10-10 07:04:17 +00:00
|
|
|
netConn: client,
|
2019-09-11 14:29:56 +00:00
|
|
|
}
|
2019-08-24 12:41:15 +00:00
|
|
|
scpCommand := scpCommand{
|
2019-11-18 22:30:37 +00:00
|
|
|
sshCommand: sshCommand{
|
|
|
|
command: "scp",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"-f", "/tmp"},
|
|
|
|
},
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
err := scpCommand.handle()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, readErr.Error())
|
2019-08-24 12:41:15 +00:00
|
|
|
scpCommand.args = []string{"-i", "/tmp"}
|
|
|
|
err = scpCommand.handle()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "invalid scp command must fail")
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
|
2020-01-19 06:41:05 +00:00
|
|
|
func TestSCPErrorsMockFs(t *testing.T) {
|
|
|
|
errFake := errors.New("fake error")
|
2020-05-06 17:36:34 +00:00
|
|
|
fs := newMockOsFs(errFake, errFake, false, "1234", os.TempDir())
|
2020-01-19 06:41:05 +00:00
|
|
|
u := dataprovider.User{}
|
|
|
|
u.Username = "test"
|
|
|
|
u.Permissions = make(map[string][]string)
|
|
|
|
u.Permissions["/"] = []string{dataprovider.PermAny}
|
|
|
|
u.HomeDir = os.TempDir()
|
|
|
|
buf := make([]byte, 65535)
|
|
|
|
stdErrBuf := make([]byte, 65535)
|
|
|
|
mockSSHChannel := MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
}
|
|
|
|
server, client := net.Pipe()
|
2020-05-06 17:36:34 +00:00
|
|
|
defer func() {
|
|
|
|
err := server.Close()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}()
|
|
|
|
defer func() {
|
|
|
|
err := client.Close()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}()
|
2020-01-19 06:41:05 +00:00
|
|
|
connection := Connection{
|
|
|
|
channel: &mockSSHChannel,
|
|
|
|
netConn: client,
|
|
|
|
fs: fs,
|
|
|
|
User: u,
|
|
|
|
}
|
|
|
|
scpCommand := scpCommand{
|
|
|
|
sshCommand: sshCommand{
|
|
|
|
command: "scp",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"-r", "-t", "/tmp"},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
err := scpCommand.handleUpload("test", 0)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, errFake.Error())
|
|
|
|
|
2020-01-19 06:41:05 +00:00
|
|
|
testfile := filepath.Join(u.HomeDir, "testfile")
|
2020-05-06 17:36:34 +00:00
|
|
|
err = ioutil.WriteFile(testfile, []byte("test"), 0666)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
stat, err := os.Stat(u.HomeDir)
|
|
|
|
assert.NoError(t, err)
|
2020-01-19 06:41:05 +00:00
|
|
|
err = scpCommand.handleRecursiveDownload(u.HomeDir, stat)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, errFake.Error())
|
|
|
|
|
2020-01-19 12:58:55 +00:00
|
|
|
scpCommand.sshCommand.connection.fs = newMockOsFs(errFake, nil, true, "123", os.TempDir())
|
2020-01-19 06:41:05 +00:00
|
|
|
err = scpCommand.handleUpload(filepath.Base(testfile), 0)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, errFake.Error())
|
|
|
|
|
2020-06-07 21:30:18 +00:00
|
|
|
err = scpCommand.handleUploadFile(testfile, testfile, 0, false, 4, "/testfile")
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
err = os.Remove(testfile)
|
|
|
|
assert.NoError(t, err)
|
2020-01-19 06:41:05 +00:00
|
|
|
}
|
|
|
|
|
2019-08-24 20:44:01 +00:00
|
|
|
func TestSCPRecursiveDownloadErrors(t *testing.T) {
|
2019-08-24 12:41:15 +00:00
|
|
|
buf := make([]byte, 65535)
|
|
|
|
stdErrBuf := make([]byte, 65535)
|
|
|
|
readErr := fmt.Errorf("test read error")
|
|
|
|
writeErr := fmt.Errorf("test write error")
|
|
|
|
mockSSHChannel := MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: readErr,
|
|
|
|
WriteError: writeErr,
|
|
|
|
}
|
2019-10-10 07:04:17 +00:00
|
|
|
server, client := net.Pipe()
|
2020-05-06 17:36:34 +00:00
|
|
|
defer func() {
|
|
|
|
err := server.Close()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}()
|
|
|
|
defer func() {
|
|
|
|
err := client.Close()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}()
|
2019-09-11 14:29:56 +00:00
|
|
|
connection := Connection{
|
|
|
|
channel: &mockSSHChannel,
|
2019-10-10 07:04:17 +00:00
|
|
|
netConn: client,
|
2020-02-23 10:30:26 +00:00
|
|
|
fs: vfs.NewOsFs("123", os.TempDir(), nil),
|
2019-09-11 14:29:56 +00:00
|
|
|
}
|
2019-08-24 12:41:15 +00:00
|
|
|
scpCommand := scpCommand{
|
2019-11-18 22:30:37 +00:00
|
|
|
sshCommand: sshCommand{
|
|
|
|
command: "scp",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"-r", "-f", "/tmp"},
|
|
|
|
},
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
path := "testDir"
|
2020-06-08 17:40:17 +00:00
|
|
|
err := os.Mkdir(path, os.ModePerm)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
stat, err := os.Stat(path)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
err = scpCommand.handleRecursiveDownload("invalid_dir", stat)
|
|
|
|
assert.EqualError(t, err, writeErr.Error())
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
mockSSHChannel = MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: nil,
|
|
|
|
WriteError: nil,
|
|
|
|
}
|
2019-09-11 14:29:56 +00:00
|
|
|
scpCommand.connection.channel = &mockSSHChannel
|
2019-08-24 12:41:15 +00:00
|
|
|
err = scpCommand.handleRecursiveDownload("invalid_dir", stat)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "recursive upload download must fail for a non existing dir")
|
2019-08-24 12:41:15 +00:00
|
|
|
|
2020-05-06 17:36:34 +00:00
|
|
|
err = os.Remove(path)
|
|
|
|
assert.NoError(t, err)
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
|
2019-08-24 20:44:01 +00:00
|
|
|
func TestSCPRecursiveUploadErrors(t *testing.T) {
|
2019-08-24 12:41:15 +00:00
|
|
|
buf := make([]byte, 65535)
|
|
|
|
stdErrBuf := make([]byte, 65535)
|
|
|
|
readErr := fmt.Errorf("test read error")
|
|
|
|
writeErr := fmt.Errorf("test write error")
|
|
|
|
mockSSHChannel := MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: readErr,
|
|
|
|
WriteError: writeErr,
|
|
|
|
}
|
2019-09-11 14:29:56 +00:00
|
|
|
connection := Connection{
|
|
|
|
channel: &mockSSHChannel,
|
|
|
|
}
|
2019-08-24 12:41:15 +00:00
|
|
|
scpCommand := scpCommand{
|
2019-11-18 22:30:37 +00:00
|
|
|
sshCommand: sshCommand{
|
|
|
|
command: "scp",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"-r", "-t", "/tmp"},
|
|
|
|
},
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
err := scpCommand.handleRecursiveUpload()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "recursive upload must fail, we send a fake error message")
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
mockSSHChannel = MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: readErr,
|
|
|
|
WriteError: nil,
|
|
|
|
}
|
2019-09-11 14:29:56 +00:00
|
|
|
scpCommand.connection.channel = &mockSSHChannel
|
2019-08-24 12:41:15 +00:00
|
|
|
err = scpCommand.handleRecursiveUpload()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "recursive upload must fail, we send a fake error message")
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestSCPCreateDirs(t *testing.T) {
|
|
|
|
buf := make([]byte, 65535)
|
|
|
|
stdErrBuf := make([]byte, 65535)
|
|
|
|
u := dataprovider.User{}
|
|
|
|
u.HomeDir = "home_rel_path"
|
|
|
|
u.Username = "test"
|
2019-12-25 17:20:19 +00:00
|
|
|
u.Permissions = make(map[string][]string)
|
|
|
|
u.Permissions["/"] = []string{dataprovider.PermAny}
|
2019-08-24 12:41:15 +00:00
|
|
|
mockSSHChannel := MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: nil,
|
|
|
|
WriteError: nil,
|
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
fs, err := u.GetFilesystem("123")
|
|
|
|
assert.NoError(t, err)
|
2019-09-11 14:29:56 +00:00
|
|
|
connection := Connection{
|
|
|
|
User: u,
|
|
|
|
channel: &mockSSHChannel,
|
2020-01-19 06:41:05 +00:00
|
|
|
fs: fs,
|
2019-09-11 14:29:56 +00:00
|
|
|
}
|
2019-08-24 12:41:15 +00:00
|
|
|
scpCommand := scpCommand{
|
2019-11-18 22:30:37 +00:00
|
|
|
sshCommand: sshCommand{
|
|
|
|
command: "scp",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"-r", "-t", "/tmp"},
|
|
|
|
},
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
err = scpCommand.handleCreateDir("invalid_dir")
|
|
|
|
assert.Error(t, err, "create invalid dir must fail")
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestSCPDownloadFileData(t *testing.T) {
|
|
|
|
testfile := "testfile"
|
|
|
|
buf := make([]byte, 65535)
|
|
|
|
readErr := fmt.Errorf("test read error")
|
|
|
|
writeErr := fmt.Errorf("test write error")
|
|
|
|
stdErrBuf := make([]byte, 65535)
|
|
|
|
mockSSHChannelReadErr := MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: readErr,
|
|
|
|
WriteError: nil,
|
|
|
|
}
|
|
|
|
mockSSHChannelWriteErr := MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: nil,
|
|
|
|
WriteError: writeErr,
|
|
|
|
}
|
2019-09-11 14:29:56 +00:00
|
|
|
connection := Connection{
|
|
|
|
channel: &mockSSHChannelReadErr,
|
|
|
|
}
|
2019-08-24 12:41:15 +00:00
|
|
|
scpCommand := scpCommand{
|
2019-11-18 22:30:37 +00:00
|
|
|
sshCommand: sshCommand{
|
|
|
|
command: "scp",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"-r", "-f", "/tmp"},
|
|
|
|
},
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
err := ioutil.WriteFile(testfile, []byte("test"), 0666)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
stat, err := os.Stat(testfile)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
err = scpCommand.sendDownloadFileData(testfile, stat, nil)
|
|
|
|
assert.EqualError(t, err, readErr.Error())
|
|
|
|
|
2019-09-11 14:29:56 +00:00
|
|
|
scpCommand.connection.channel = &mockSSHChannelWriteErr
|
2019-08-24 12:41:15 +00:00
|
|
|
err = scpCommand.sendDownloadFileData(testfile, stat, nil)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, writeErr.Error())
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
scpCommand.args = []string{"-r", "-p", "-f", "/tmp"}
|
|
|
|
err = scpCommand.sendDownloadFileData(testfile, stat, nil)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, writeErr.Error())
|
|
|
|
|
2019-09-11 14:29:56 +00:00
|
|
|
scpCommand.connection.channel = &mockSSHChannelReadErr
|
2019-08-24 12:41:15 +00:00
|
|
|
err = scpCommand.sendDownloadFileData(testfile, stat, nil)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, readErr.Error())
|
|
|
|
|
|
|
|
err = os.Remove(testfile)
|
|
|
|
assert.NoError(t, err)
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestSCPUploadFiledata(t *testing.T) {
|
|
|
|
testfile := "testfile"
|
|
|
|
buf := make([]byte, 65535)
|
|
|
|
stdErrBuf := make([]byte, 65535)
|
|
|
|
readErr := fmt.Errorf("test read error")
|
|
|
|
writeErr := fmt.Errorf("test write error")
|
|
|
|
mockSSHChannel := MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: readErr,
|
|
|
|
WriteError: writeErr,
|
|
|
|
}
|
2019-09-11 14:29:56 +00:00
|
|
|
connection := Connection{
|
|
|
|
User: dataprovider.User{
|
|
|
|
Username: "testuser",
|
|
|
|
},
|
|
|
|
protocol: protocolSCP,
|
|
|
|
channel: &mockSSHChannel,
|
2020-04-22 10:26:18 +00:00
|
|
|
fs: vfs.NewOsFs("", os.TempDir(), nil),
|
2019-09-11 14:29:56 +00:00
|
|
|
}
|
2019-08-24 12:41:15 +00:00
|
|
|
scpCommand := scpCommand{
|
2019-11-18 22:30:37 +00:00
|
|
|
sshCommand: sshCommand{
|
|
|
|
command: "scp",
|
|
|
|
connection: connection,
|
|
|
|
args: []string{"-r", "-t", "/tmp"},
|
|
|
|
},
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
file, err := os.Create(testfile)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
transfer := Transfer{
|
2019-10-09 15:33:30 +00:00
|
|
|
file: file,
|
|
|
|
path: file.Name(),
|
|
|
|
start: time.Now(),
|
|
|
|
bytesSent: 0,
|
|
|
|
bytesReceived: 0,
|
|
|
|
user: scpCommand.connection.User,
|
|
|
|
connectionID: "",
|
|
|
|
transferType: transferDownload,
|
|
|
|
lastActivity: time.Now(),
|
|
|
|
isNewFile: true,
|
|
|
|
protocol: connection.protocol,
|
|
|
|
transferError: nil,
|
|
|
|
isFinished: false,
|
|
|
|
minWriteOffset: 0,
|
2020-01-10 18:20:22 +00:00
|
|
|
lock: new(sync.Mutex),
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
|
|
|
addTransfer(&transfer)
|
2020-05-06 17:36:34 +00:00
|
|
|
err = scpCommand.getUploadFileData(2, &transfer)
|
|
|
|
assert.Error(t, err, "upload must fail, we send a fake write error message")
|
|
|
|
|
2019-08-24 12:41:15 +00:00
|
|
|
mockSSHChannel = MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: readErr,
|
|
|
|
WriteError: nil,
|
|
|
|
}
|
2019-09-11 14:29:56 +00:00
|
|
|
scpCommand.connection.channel = &mockSSHChannel
|
2020-05-06 17:36:34 +00:00
|
|
|
file, err = os.Create(testfile)
|
|
|
|
assert.NoError(t, err)
|
2019-08-24 12:41:15 +00:00
|
|
|
transfer.file = file
|
2020-01-12 07:25:08 +00:00
|
|
|
transfer.isFinished = false
|
2019-08-24 12:41:15 +00:00
|
|
|
addTransfer(&transfer)
|
|
|
|
err = scpCommand.getUploadFileData(2, &transfer)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "upload must fail, we send a fake read error message")
|
2019-08-24 12:41:15 +00:00
|
|
|
|
|
|
|
respBuffer := []byte("12")
|
|
|
|
respBuffer = append(respBuffer, 0x02)
|
|
|
|
mockSSHChannel = MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(respBuffer),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: nil,
|
|
|
|
WriteError: nil,
|
|
|
|
}
|
2019-09-11 14:29:56 +00:00
|
|
|
scpCommand.connection.channel = &mockSSHChannel
|
2020-05-06 17:36:34 +00:00
|
|
|
file, err = os.Create(testfile)
|
|
|
|
assert.NoError(t, err)
|
2019-08-24 12:41:15 +00:00
|
|
|
transfer.file = file
|
2020-01-12 07:25:08 +00:00
|
|
|
transfer.isFinished = false
|
2019-08-24 12:41:15 +00:00
|
|
|
addTransfer(&transfer)
|
|
|
|
err = scpCommand.getUploadFileData(2, &transfer)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err, "upload must fail, we have not enough data to read")
|
2019-08-24 12:41:15 +00:00
|
|
|
|
|
|
|
// the file is already closed so we have an error on trasfer closing
|
|
|
|
mockSSHChannel = MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: nil,
|
|
|
|
WriteError: nil,
|
|
|
|
}
|
|
|
|
addTransfer(&transfer)
|
|
|
|
err = scpCommand.getUploadFileData(0, &transfer)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.EqualError(t, err, errTransferClosed.Error())
|
|
|
|
|
2020-02-19 10:26:40 +00:00
|
|
|
mockSSHChannel = MockChannel{
|
|
|
|
Buffer: bytes.NewBuffer(buf),
|
|
|
|
StdErrBuffer: bytes.NewBuffer(stdErrBuf),
|
|
|
|
ReadError: nil,
|
|
|
|
WriteError: nil,
|
|
|
|
}
|
|
|
|
addTransfer(&transfer)
|
|
|
|
err = scpCommand.getUploadFileData(2, &transfer)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.True(t, errors.Is(err, os.ErrClosed))
|
|
|
|
|
2020-01-12 07:25:08 +00:00
|
|
|
err = os.Remove(testfile)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
2019-08-24 12:41:15 +00:00
|
|
|
}
|
2019-09-10 16:47:21 +00:00
|
|
|
|
|
|
|
func TestUploadError(t *testing.T) {
|
2019-10-09 15:33:30 +00:00
|
|
|
oldUploadMode := uploadMode
|
|
|
|
uploadMode = uploadModeAtomic
|
2019-09-10 16:47:21 +00:00
|
|
|
connection := Connection{
|
|
|
|
User: dataprovider.User{
|
|
|
|
Username: "testuser",
|
|
|
|
},
|
|
|
|
protocol: protocolSCP,
|
|
|
|
}
|
|
|
|
testfile := "testfile"
|
|
|
|
fileTempName := "temptestfile"
|
2020-05-06 17:36:34 +00:00
|
|
|
file, err := os.Create(fileTempName)
|
|
|
|
assert.NoError(t, err)
|
2019-09-10 16:47:21 +00:00
|
|
|
transfer := Transfer{
|
2019-10-09 15:33:30 +00:00
|
|
|
file: file,
|
|
|
|
path: testfile,
|
|
|
|
start: time.Now(),
|
|
|
|
bytesSent: 0,
|
|
|
|
bytesReceived: 100,
|
|
|
|
user: connection.User,
|
|
|
|
connectionID: "",
|
|
|
|
transferType: transferUpload,
|
|
|
|
lastActivity: time.Now(),
|
|
|
|
isNewFile: true,
|
|
|
|
protocol: connection.protocol,
|
|
|
|
transferError: nil,
|
|
|
|
isFinished: false,
|
|
|
|
minWriteOffset: 0,
|
2020-01-10 18:20:22 +00:00
|
|
|
lock: new(sync.Mutex),
|
2019-09-10 16:47:21 +00:00
|
|
|
}
|
|
|
|
addTransfer(&transfer)
|
2020-01-12 07:25:08 +00:00
|
|
|
errFake := errors.New("fake error")
|
|
|
|
transfer.TransferError(errFake)
|
2020-05-06 17:36:34 +00:00
|
|
|
err = transfer.Close()
|
|
|
|
assert.EqualError(t, err, errFake.Error())
|
|
|
|
assert.Equal(t, int64(0), transfer.bytesReceived)
|
|
|
|
|
|
|
|
assert.NoFileExists(t, testfile)
|
|
|
|
assert.NoFileExists(t, fileTempName)
|
|
|
|
|
2019-10-09 15:33:30 +00:00
|
|
|
uploadMode = oldUploadMode
|
2019-09-10 16:47:21 +00:00
|
|
|
}
|
2019-10-07 16:19:01 +00:00
|
|
|
|
|
|
|
func TestConnectionStatusStruct(t *testing.T) {
|
|
|
|
var transfers []connectionTransfer
|
|
|
|
transferUL := connectionTransfer{
|
|
|
|
OperationType: operationUpload,
|
|
|
|
StartTime: utils.GetTimeAsMsSinceEpoch(time.Now()),
|
|
|
|
Size: 123,
|
|
|
|
LastActivity: utils.GetTimeAsMsSinceEpoch(time.Now()),
|
|
|
|
Path: "/test.upload",
|
|
|
|
}
|
|
|
|
transferDL := connectionTransfer{
|
|
|
|
OperationType: operationDownload,
|
|
|
|
StartTime: utils.GetTimeAsMsSinceEpoch(time.Now()),
|
|
|
|
Size: 123,
|
|
|
|
LastActivity: utils.GetTimeAsMsSinceEpoch(time.Now()),
|
|
|
|
Path: "/test.download",
|
|
|
|
}
|
|
|
|
transfers = append(transfers, transferUL)
|
|
|
|
transfers = append(transfers, transferDL)
|
|
|
|
c := ConnectionStatus{
|
|
|
|
Username: "test",
|
|
|
|
ConnectionID: "123",
|
|
|
|
ClientVersion: "fakeClient-1.0.0",
|
|
|
|
RemoteAddress: "127.0.0.1:1234",
|
|
|
|
ConnectionTime: utils.GetTimeAsMsSinceEpoch(time.Now()),
|
|
|
|
LastActivity: utils.GetTimeAsMsSinceEpoch(time.Now()),
|
|
|
|
Protocol: "SFTP",
|
|
|
|
Transfers: transfers,
|
|
|
|
}
|
|
|
|
durationString := c.GetConnectionDuration()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NotEqual(t, 0, len(durationString))
|
|
|
|
|
2019-10-07 16:19:01 +00:00
|
|
|
transfersString := c.GetTransfersAsString()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NotEqual(t, 0, len(transfersString))
|
|
|
|
|
2019-10-07 16:19:01 +00:00
|
|
|
connInfo := c.GetConnectionInfo()
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NotEqual(t, 0, len(connInfo))
|
2019-10-07 16:19:01 +00:00
|
|
|
}
|
2019-11-12 06:37:47 +00:00
|
|
|
|
2020-02-28 23:02:06 +00:00
|
|
|
func TestProxyProtocolVersion(t *testing.T) {
|
|
|
|
c := Configuration{
|
|
|
|
ProxyProtocol: 1,
|
|
|
|
}
|
2020-05-06 17:36:34 +00:00
|
|
|
proxyListener, err := c.getProxyListener(nil)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Nil(t, proxyListener.Policy)
|
|
|
|
|
2020-02-28 23:02:06 +00:00
|
|
|
c.ProxyProtocol = 2
|
2020-03-01 22:12:28 +00:00
|
|
|
proxyListener, _ = c.getProxyListener(nil)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.NotNil(t, proxyListener.Policy)
|
|
|
|
|
2020-03-01 22:12:28 +00:00
|
|
|
c.ProxyProtocol = 1
|
|
|
|
c.ProxyAllowed = []string{"invalid"}
|
2020-05-06 17:36:34 +00:00
|
|
|
_, err = c.getProxyListener(nil)
|
|
|
|
assert.Error(t, err)
|
|
|
|
|
2020-03-01 22:12:28 +00:00
|
|
|
c.ProxyProtocol = 2
|
|
|
|
_, err = c.getProxyListener(nil)
|
2020-05-06 17:36:34 +00:00
|
|
|
assert.Error(t, err)
|
2020-02-28 23:02:06 +00:00
|
|
|
}
|
2020-05-15 18:08:53 +00:00
|
|
|
|
|
|
|
func TestLoadHostKeys(t *testing.T) {
|
2020-06-08 16:45:04 +00:00
|
|
|
configDir := ".."
|
|
|
|
serverConfig := &ssh.ServerConfig{}
|
2020-05-15 18:08:53 +00:00
|
|
|
c := Configuration{}
|
2020-05-16 21:26:44 +00:00
|
|
|
c.HostKeys = []string{".", "missing file"}
|
2020-06-08 16:45:04 +00:00
|
|
|
err := c.checkAndLoadHostKeys(configDir, serverConfig)
|
2020-05-15 18:08:53 +00:00
|
|
|
assert.Error(t, err)
|
|
|
|
testfile := filepath.Join(os.TempDir(), "invalidkey")
|
|
|
|
err = ioutil.WriteFile(testfile, []byte("some bytes"), 0666)
|
|
|
|
assert.NoError(t, err)
|
2020-05-16 21:26:44 +00:00
|
|
|
c.HostKeys = []string{testfile}
|
2020-06-08 16:45:04 +00:00
|
|
|
err = c.checkAndLoadHostKeys(configDir, serverConfig)
|
2020-05-15 18:08:53 +00:00
|
|
|
assert.Error(t, err)
|
|
|
|
err = os.Remove(testfile)
|
|
|
|
assert.NoError(t, err)
|
2020-06-08 16:45:04 +00:00
|
|
|
keysDir := filepath.Join(os.TempDir(), "keys")
|
2020-06-08 17:40:17 +00:00
|
|
|
err = os.MkdirAll(keysDir, os.ModePerm)
|
2020-06-08 16:45:04 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
rsaKeyName := filepath.Join(keysDir, defaultPrivateRSAKeyName)
|
|
|
|
ecdsaKeyName := filepath.Join(keysDir, defaultPrivateECDSAKeyName)
|
|
|
|
nonDefaultKeyName := filepath.Join(keysDir, "akey")
|
|
|
|
c.HostKeys = []string{nonDefaultKeyName, rsaKeyName, ecdsaKeyName}
|
|
|
|
err = c.checkAndLoadHostKeys(configDir, serverConfig)
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.FileExists(t, rsaKeyName)
|
|
|
|
assert.FileExists(t, ecdsaKeyName)
|
|
|
|
assert.NoFileExists(t, nonDefaultKeyName)
|
|
|
|
err = os.Remove(rsaKeyName)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
err = os.Remove(ecdsaKeyName)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
if runtime.GOOS != osWindows {
|
|
|
|
err = os.Chmod(keysDir, 0551)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
c.HostKeys = nil
|
|
|
|
err = c.checkAndLoadHostKeys(keysDir, serverConfig)
|
|
|
|
assert.Error(t, err)
|
|
|
|
c.HostKeys = []string{rsaKeyName, ecdsaKeyName}
|
|
|
|
err = c.checkAndLoadHostKeys(configDir, serverConfig)
|
|
|
|
assert.Error(t, err)
|
|
|
|
c.HostKeys = []string{ecdsaKeyName, rsaKeyName}
|
|
|
|
err = c.checkAndLoadHostKeys(configDir, serverConfig)
|
|
|
|
assert.Error(t, err)
|
|
|
|
err = os.Chmod(keysDir, 0755)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}
|
|
|
|
err = os.RemoveAll(keysDir)
|
|
|
|
assert.NoError(t, err)
|
2020-05-15 18:08:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestCertCheckerInitErrors(t *testing.T) {
|
|
|
|
c := Configuration{}
|
2020-05-16 13:15:32 +00:00
|
|
|
c.TrustedUserCAKeys = []string{".", "missing file"}
|
2020-05-15 18:08:53 +00:00
|
|
|
err := c.initializeCertChecker("")
|
|
|
|
assert.Error(t, err)
|
|
|
|
testfile := filepath.Join(os.TempDir(), "invalidkey")
|
|
|
|
err = ioutil.WriteFile(testfile, []byte("some bytes"), 0666)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
c.TrustedUserCAKeys = []string{testfile}
|
|
|
|
err = c.initializeCertChecker("")
|
|
|
|
assert.Error(t, err)
|
|
|
|
err = os.Remove(testfile)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}
|
2020-06-07 21:30:18 +00:00
|
|
|
|
|
|
|
func TestUpdateQuotaAfterRenameMissingFile(t *testing.T) {
|
|
|
|
user := dataprovider.User{
|
|
|
|
Username: "username",
|
|
|
|
HomeDir: filepath.Join(os.TempDir(), "home"),
|
|
|
|
}
|
|
|
|
mappedPath := filepath.Join(os.TempDir(), "vdir")
|
|
|
|
user.Permissions = make(map[string][]string)
|
|
|
|
user.Permissions["/"] = []string{dataprovider.PermAny}
|
|
|
|
user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
|
|
|
|
BaseVirtualFolder: vfs.BaseVirtualFolder{
|
|
|
|
MappedPath: mappedPath,
|
|
|
|
},
|
|
|
|
VirtualPath: "/vdir",
|
|
|
|
})
|
|
|
|
c := Connection{
|
|
|
|
fs: vfs.NewOsFs("id", os.TempDir(), nil),
|
|
|
|
User: user,
|
|
|
|
}
|
|
|
|
request := sftp.NewRequest("Rename", "/testfile")
|
|
|
|
request.Filepath = "/dir"
|
|
|
|
request.Target = path.Join("vdir", "dir")
|
2020-06-08 16:45:04 +00:00
|
|
|
if runtime.GOOS != osWindows {
|
2020-06-07 21:30:18 +00:00
|
|
|
testDirPath := filepath.Join(mappedPath, "dir")
|
2020-06-08 17:40:17 +00:00
|
|
|
err := os.MkdirAll(testDirPath, os.ModePerm)
|
2020-06-07 21:30:18 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
err = os.Chmod(testDirPath, 0001)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
err = c.updateQuotaAfterRename(request, testDirPath, 0)
|
|
|
|
assert.Error(t, err)
|
2020-06-08 17:40:17 +00:00
|
|
|
err = os.Chmod(testDirPath, os.ModePerm)
|
2020-06-07 21:30:18 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
err = os.RemoveAll(testDirPath)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}
|
|
|
|
request.Target = "/testfile1"
|
|
|
|
request.Filepath = path.Join("vdir", "file")
|
|
|
|
err := c.updateQuotaAfterRename(request, filepath.Join(os.TempDir(), "vdir", "file"), 0)
|
|
|
|
assert.Error(t, err)
|
|
|
|
}
|
2020-06-13 21:49:28 +00:00
|
|
|
|
|
|
|
func TestRenamePermission(t *testing.T) {
|
|
|
|
permissions := make(map[string][]string)
|
|
|
|
permissions["/"] = []string{dataprovider.PermAny}
|
|
|
|
permissions["/dir1"] = []string{dataprovider.PermRename}
|
|
|
|
permissions["/dir2"] = []string{dataprovider.PermUpload}
|
|
|
|
permissions["/dir3"] = []string{dataprovider.PermDelete}
|
|
|
|
permissions["/dir4"] = []string{dataprovider.PermListItems}
|
2020-06-26 21:38:29 +00:00
|
|
|
permissions["/dir5"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload}
|
|
|
|
permissions["/dir6"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload,
|
|
|
|
dataprovider.PermListItems, dataprovider.PermCreateSymlinks}
|
2020-06-13 21:49:28 +00:00
|
|
|
|
|
|
|
user := dataprovider.User{
|
|
|
|
Permissions: permissions,
|
|
|
|
HomeDir: os.TempDir(),
|
|
|
|
}
|
|
|
|
fs, err := user.GetFilesystem("123")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
conn := Connection{
|
|
|
|
User: user,
|
|
|
|
fs: fs,
|
|
|
|
}
|
|
|
|
request := sftp.NewRequest("Rename", "/testfile")
|
2020-06-26 21:38:29 +00:00
|
|
|
request.Target = "/dir1/testfile"
|
|
|
|
// rename is granted on Source and Target
|
|
|
|
assert.True(t, conn.isRenamePermitted("", request.Filepath, request.Target, nil))
|
2020-06-13 21:49:28 +00:00
|
|
|
request.Target = "/dir4/testfile"
|
|
|
|
// rename is not granted on Target
|
2020-06-26 21:38:29 +00:00
|
|
|
assert.False(t, conn.isRenamePermitted("", request.Filepath, request.Target, nil))
|
|
|
|
request = sftp.NewRequest("Rename", "/dir1/testfile")
|
|
|
|
request.Target = "/dir2/testfile" //nolint:goconst
|
|
|
|
// rename is granted on Source but not on Target
|
|
|
|
assert.False(t, conn.isRenamePermitted("", request.Filepath, request.Target, nil))
|
2020-06-13 21:49:28 +00:00
|
|
|
request = sftp.NewRequest("Rename", "/dir4/testfile")
|
|
|
|
request.Target = "/dir1/testfile"
|
2020-06-26 21:38:29 +00:00
|
|
|
// rename is granted on Target but not on Source
|
|
|
|
assert.False(t, conn.isRenamePermitted("", request.Filepath, request.Target, nil))
|
2020-06-13 21:49:28 +00:00
|
|
|
request = sftp.NewRequest("Rename", "/dir4/testfile")
|
|
|
|
request.Target = "/testfile"
|
2020-06-26 21:38:29 +00:00
|
|
|
// rename is granted on Target but not on Source
|
|
|
|
assert.False(t, conn.isRenamePermitted("", request.Filepath, request.Target, nil))
|
2020-06-13 21:49:28 +00:00
|
|
|
request = sftp.NewRequest("Rename", "/dir3/testfile")
|
|
|
|
request.Target = "/dir2/testfile"
|
2020-06-18 20:38:03 +00:00
|
|
|
// delete is granted on Source and Upload on Target, the target is a file this is enough
|
2020-06-26 21:38:29 +00:00
|
|
|
assert.True(t, conn.isRenamePermitted("", request.Filepath, request.Target, nil))
|
2020-06-13 21:49:28 +00:00
|
|
|
request = sftp.NewRequest("Rename", "/dir2/testfile")
|
|
|
|
request.Target = "/dir3/testfile"
|
2020-06-26 21:38:29 +00:00
|
|
|
assert.False(t, conn.isRenamePermitted("", request.Filepath, request.Target, nil))
|
2020-06-18 20:38:03 +00:00
|
|
|
tmpDir := filepath.Join(os.TempDir(), "dir")
|
2020-06-26 21:38:29 +00:00
|
|
|
tmpDirLink := filepath.Join(os.TempDir(), "link")
|
2020-06-18 20:38:03 +00:00
|
|
|
err = os.Mkdir(tmpDir, os.ModePerm)
|
|
|
|
assert.NoError(t, err)
|
2020-06-26 21:38:29 +00:00
|
|
|
err = os.Symlink(tmpDir, tmpDirLink)
|
|
|
|
assert.NoError(t, err)
|
2020-06-18 20:38:03 +00:00
|
|
|
request.Filepath = "/dir"
|
|
|
|
request.Target = "/dir2/dir"
|
|
|
|
// the source is a dir and the target has no createDirs perm
|
2020-06-26 21:38:29 +00:00
|
|
|
info, err := os.Lstat(tmpDir)
|
|
|
|
if assert.NoError(t, err) {
|
|
|
|
assert.False(t, conn.isRenamePermitted(tmpDir, request.Filepath, request.Target, info))
|
|
|
|
conn.User.Permissions["/dir2"] = []string{dataprovider.PermUpload, dataprovider.PermCreateDirs}
|
|
|
|
// the source is a dir and the target has createDirs perm
|
|
|
|
assert.True(t, conn.isRenamePermitted(tmpDir, request.Filepath, request.Target, info))
|
|
|
|
|
|
|
|
request = sftp.NewRequest("Rename", "/testfile")
|
|
|
|
request.Target = "/dir5/testfile"
|
|
|
|
// the source is a dir and the target has createDirs and upload perm
|
|
|
|
assert.True(t, conn.isRenamePermitted(tmpDir, request.Filepath, request.Target, info))
|
|
|
|
}
|
|
|
|
info, err = os.Lstat(tmpDirLink)
|
|
|
|
if assert.NoError(t, err) {
|
|
|
|
assert.True(t, info.Mode()&os.ModeSymlink == os.ModeSymlink)
|
|
|
|
// the source is a symlink and the target has createDirs and upload perm
|
|
|
|
assert.False(t, conn.isRenamePermitted(tmpDir, request.Filepath, request.Target, info))
|
|
|
|
}
|
2020-06-18 20:38:03 +00:00
|
|
|
err = os.RemoveAll(tmpDir)
|
|
|
|
assert.NoError(t, err)
|
2020-06-26 21:38:29 +00:00
|
|
|
err = os.Remove(tmpDirLink)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
conn.User.VirtualFolders = append(conn.User.VirtualFolders, vfs.VirtualFolder{
|
|
|
|
BaseVirtualFolder: vfs.BaseVirtualFolder{
|
|
|
|
MappedPath: os.TempDir(),
|
|
|
|
},
|
|
|
|
VirtualPath: "/dir1",
|
|
|
|
})
|
|
|
|
request = sftp.NewRequest("Rename", "/dir1")
|
|
|
|
request.Target = "/dir2/testfile"
|
|
|
|
// renaming a virtual folder is not allowed
|
|
|
|
assert.False(t, conn.isRenamePermitted("", request.Filepath, request.Target, nil))
|
|
|
|
err = conn.checkRecursiveRenameDirPermissions("invalid", "invalid")
|
|
|
|
assert.Error(t, err)
|
|
|
|
dir3 := filepath.Join(conn.User.HomeDir, "dir3")
|
|
|
|
dir6 := filepath.Join(conn.User.HomeDir, "dir6")
|
|
|
|
err = os.MkdirAll(filepath.Join(dir3, "subdir"), os.ModePerm)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
err = ioutil.WriteFile(filepath.Join(dir3, "subdir", "testfile"), []byte("test"), os.ModePerm)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
err = conn.checkRecursiveRenameDirPermissions(dir3, dir6)
|
|
|
|
assert.NoError(t, err)
|
2020-07-10 17:20:37 +00:00
|
|
|
err = os.RemoveAll(dir3)
|
|
|
|
assert.NoError(t, err)
|
2020-06-26 21:38:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestRecursiveCopyErrors(t *testing.T) {
|
|
|
|
permissions := make(map[string][]string)
|
|
|
|
permissions["/"] = []string{dataprovider.PermAny}
|
|
|
|
user := dataprovider.User{
|
|
|
|
Permissions: permissions,
|
|
|
|
HomeDir: os.TempDir(),
|
|
|
|
}
|
|
|
|
fs, err := user.GetFilesystem("123")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
conn := Connection{
|
|
|
|
User: user,
|
|
|
|
fs: fs,
|
|
|
|
}
|
|
|
|
sshCmd := sshCommand{
|
|
|
|
command: "sftpgo-copy",
|
|
|
|
connection: conn,
|
|
|
|
args: []string{"adir", "another"},
|
|
|
|
}
|
|
|
|
// try to copy a missing directory
|
|
|
|
err = sshCmd.checkRecursiveCopyPermissions("adir", "another", "/another")
|
|
|
|
assert.Error(t, err)
|
2020-06-13 21:49:28 +00:00
|
|
|
}
|
2020-06-29 16:53:33 +00:00
|
|
|
|
|
|
|
func TestSSHMappedError(t *testing.T) {
|
|
|
|
user := dataprovider.User{
|
|
|
|
HomeDir: os.TempDir(),
|
|
|
|
}
|
|
|
|
fs, err := user.GetFilesystem("123")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
conn := Connection{
|
|
|
|
User: user,
|
|
|
|
fs: fs,
|
|
|
|
}
|
|
|
|
sshCommand := sshCommand{
|
|
|
|
command: "test",
|
|
|
|
connection: conn,
|
|
|
|
args: []string{},
|
|
|
|
}
|
|
|
|
err = sshCommand.getMappedError(os.ErrNotExist)
|
|
|
|
assert.EqualError(t, err, errNotExist.Error())
|
|
|
|
err = sshCommand.getMappedError(os.ErrPermission)
|
|
|
|
assert.EqualError(t, err, errPermissionDenied.Error())
|
|
|
|
err = sshCommand.getMappedError(os.ErrInvalid)
|
|
|
|
assert.EqualError(t, err, errGenericFailure.Error())
|
|
|
|
err = sshCommand.getMappedError(os.ErrNoDeadline)
|
|
|
|
assert.EqualError(t, err, errGenericFailure.Error())
|
|
|
|
}
|