1package ftpd_test 2 3import ( 4 "crypto/rand" 5 "crypto/sha256" 6 "crypto/tls" 7 "encoding/hex" 8 "encoding/json" 9 "fmt" 10 "io" 11 "net" 12 "net/http" 13 "os" 14 "os/exec" 15 "path" 16 "path/filepath" 17 "runtime" 18 "strconv" 19 "testing" 20 "time" 21 22 ftpserver "github.com/fclairamb/ftpserverlib" 23 "github.com/jlaffaye/ftp" 24 "github.com/pquerna/otp" 25 "github.com/pquerna/otp/totp" 26 "github.com/rs/zerolog" 27 "github.com/stretchr/testify/assert" 28 "github.com/stretchr/testify/require" 29 30 "github.com/drakkan/sftpgo/v2/common" 31 "github.com/drakkan/sftpgo/v2/config" 32 "github.com/drakkan/sftpgo/v2/dataprovider" 33 "github.com/drakkan/sftpgo/v2/ftpd" 34 "github.com/drakkan/sftpgo/v2/httpdtest" 35 "github.com/drakkan/sftpgo/v2/kms" 36 "github.com/drakkan/sftpgo/v2/logger" 37 "github.com/drakkan/sftpgo/v2/mfa" 38 "github.com/drakkan/sftpgo/v2/sdk" 39 "github.com/drakkan/sftpgo/v2/sftpd" 40 "github.com/drakkan/sftpgo/v2/vfs" 41) 42 43const ( 44 logSender = "ftpdTesting" 45 ftpServerAddr = "127.0.0.1:2121" 46 sftpServerAddr = "127.0.0.1:2122" 47 ftpSrvAddrTLS = "127.0.0.1:2124" // ftp server with implicit tls 48 defaultUsername = "test_user_ftp" 49 defaultPassword = "test_password" 50 configDir = ".." 51 osWindows = "windows" 52 ftpsCert = `-----BEGIN CERTIFICATE----- 53MIICHTCCAaKgAwIBAgIUHnqw7QnB1Bj9oUsNpdb+ZkFPOxMwCgYIKoZIzj0EAwIw 54RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu 55dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDAyMDQwOTUzMDRaFw0zMDAyMDEw 56OTUzMDRaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD 57VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwdjAQBgcqhkjOPQIBBgUrgQQA 58IgNiAARCjRMqJ85rzMC998X5z761nJ+xL3bkmGVqWvrJ51t5OxV0v25NsOgR82CA 59NXUgvhVYs7vNFN+jxtb2aj6Xg+/2G/BNxkaFspIVCzgWkxiz7XE4lgUwX44FCXZM 603+JeUbKjUzBRMB0GA1UdDgQWBBRhLw+/o3+Z02MI/d4tmaMui9W16jAfBgNVHSME 61GDAWgBRhLw+/o3+Z02MI/d4tmaMui9W16jAPBgNVHRMBAf8EBTADAQH/MAoGCCqG 62SM49BAMCA2kAMGYCMQDqLt2lm8mE+tGgtjDmtFgdOcI72HSbRQ74D5rYTzgST1rY 63/8wTi5xl8TiFUyLMUsICMQC5ViVxdXbhuG7gX6yEqSkMKZICHpO8hqFwOD/uaFVI 64dV4vKmHUzwK/eIx+8Ay3neE= 65-----END CERTIFICATE-----` 66 ftpsKey = `-----BEGIN EC PARAMETERS----- 67BgUrgQQAIg== 68-----END EC PARAMETERS----- 69-----BEGIN EC PRIVATE KEY----- 70MIGkAgEBBDCfMNsN6miEE3rVyUPwElfiJSWaR5huPCzUenZOfJT04GAcQdWvEju3 71UM2lmBLIXpGgBwYFK4EEACKhZANiAARCjRMqJ85rzMC998X5z761nJ+xL3bkmGVq 72WvrJ51t5OxV0v25NsOgR82CANXUgvhVYs7vNFN+jxtb2aj6Xg+/2G/BNxkaFspIV 73CzgWkxiz7XE4lgUwX44FCXZM3+JeUbI= 74-----END EC PRIVATE KEY-----` 75 caCRT = `-----BEGIN CERTIFICATE----- 76MIIE5jCCAs6gAwIBAgIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwhDZXJ0 77QXV0aDAeFw0yMTAxMDIyMTIwNTVaFw0yMjA3MDIyMTMwNTJaMBMxETAPBgNVBAMT 78CENlcnRBdXRoMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4Tiho5xW 79AC15JRkMwfp3/TJwI2As7MY5dele5cmdr5bHAE+sRKqC+Ti88OJWCV5saoyax/1S 80CjxJlQMZMl169P1QYJskKjdG2sdv6RLWLMgwSNRRjxp/Bw9dHdiEb9MjLgu28Jro 819peQkHcRHeMf5hM9WvlIJGrdzbC4hUehmqggcqgARainBkYjf0SwuWxHeu4nMqkp 82Ak5tcSTLCjHfEFHZ9Te0TIPG5YkWocQKyeLgu4lvuU+DD2W2lym+YVUtRMGs1Env 83k7p+N0DcGU26qfzZ2sF5ZXkqm7dBsGQB9pIxwc2Q8T1dCIyP9OQCKVILdc5aVFf1 84cryQFHYzYNNZXFlIBims5VV5Mgfp8ESHQSue+v6n6ykecLEyKt1F1Y/MWY/nWUSI 858zdq83jdBAZVjo9MSthxVn57/06s/hQca65IpcTZV2gX0a+eRlAVqaRbAhL3LaZe 86bYsW3WHKoUOftwemuep3nL51TzlXZVL7Oz/ClGaEOsnGG9KFO6jh+W768qC0zLQI 87CdE7v2Zex98sZteHCg9fGJHIaYoF0aJG5P3WI5oZf2fy7UIYN9ADLFZiorCXAZEh 88CSU6mDoRViZ4RGR9GZxbDZ9KYn7O8M/KCR72bkQg73TlMsk1zSXEw0MKLUjtsw6c 89rZ0Jt8t3sRatHO3JrYHALMt9vZfyNCZp0IsCAwEAAaNFMEMwDgYDVR0PAQH/BAQD 90AgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFO1yCNAGr/zQTJIi8lw3 91w5OiuBvMMA0GCSqGSIb3DQEBCwUAA4ICAQA6gCNuM7r8mnx674dm31GxBjQy5ZwB 927CxDzYEvL/oiZ3Tv3HlPfN2LAAsJUfGnghh9DOytenL2CTZWjl/emP5eijzmlP+9 93zva5I6CIMCf/eDDVsRdO244t0o4uG7+At0IgSDM3bpVaVb4RHZNjEziYChsEYY8d 94HK6iwuRSvFniV6yhR/Vj1Ymi9yZ5xclqseLXiQnUB0PkfIk23+7s42cXB16653fH 95O/FsPyKBLiKJArizLYQc12aP3QOrYoYD9+fAzIIzew7A5C0aanZCGzkuFpO6TRlD 96Tb7ry9Gf0DfPpCgxraH8tOcmnqp/ka3hjqo/SRnnTk0IFrmmLdarJvjD46rKwBo4 97MjyAIR1mQ5j8GTlSFBmSgETOQ/EYvO3FPLmra1Fh7L+DvaVzTpqI9fG3TuyyY+Ri 98Fby4ycTOGSZOe5Fh8lqkX5Y47mCUJ3zHzOA1vUJy2eTlMRGpu47Eb1++Vm6EzPUP 992EF5aD+zwcssh+atZvQbwxpgVqVcyLt91RSkKkmZQslh0rnlTb68yxvUnD3zw7So 100o6TAf9UvwVMEvdLT9NnFd6hwi2jcNte/h538GJwXeBb8EkfpqLKpTKyicnOdkamZ 1017E9zY8SHNRYMwB9coQ/W8NvufbCgkvOoLyMXk5edbXofXl3PhNGOlraWbghBnzf5 102r3rwjFsQOoZotA== 103-----END CERTIFICATE-----` 104 caCRL = `-----BEGIN X509 CRL----- 105MIICpzCBkAIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwhDZXJ0QXV0aBcN 106MjEwMTAyMjEzNDA1WhcNMjMwMTAyMjEzNDA1WjAkMCICEQC+l04DbHWMyC3fG09k 107VXf+Fw0yMTAxMDIyMTM0MDVaoCMwITAfBgNVHSMEGDAWgBTtcgjQBq/80EySIvJc 108N8OTorgbzDANBgkqhkiG9w0BAQsFAAOCAgEAEJ7z+uNc8sqtxlOhSdTGDzX/xput 109E857kFQkSlMnU2whQ8c+XpYrBLA5vIZJNSSwohTpM4+zVBX/bJpmu3wqqaArRO9/ 110YcW5mQk9Anvb4WjQW1cHmtNapMTzoC9AiYt/OWPfy+P6JCgCr4Hy6LgQyIRL6bM9 111VYTalolOm1qa4Y5cIeT7iHq/91mfaqo8/6MYRjLl8DOTROpmw8OS9bCXkzGKdCat 112AbAzwkQUSauyoCQ10rpX+Y64w9ng3g4Dr20aCqPf5osaqplEJ2HTK8ljDTidlslv 1139anQj8ax3Su89vI8+hK+YbfVQwrThabgdSjQsn+veyx8GlP8WwHLAQ379KjZjWg+ 114OlOSwBeU1vTdP0QcB8X5C2gVujAyuQekbaV86xzIBOj7vZdfHZ6ee30TZ2FKiMyg 1157/N2OqW0w77ChsjB4MSHJCfuTgIeg62GzuZXLM+Q2Z9LBdtm4Byg+sm/P52adOEg 116gVb2Zf4KSvsAmA0PIBlu449/QXUFcMxzLFy7mwTeZj2B4Ln0Hm0szV9f9R8MwMtB 117SyLYxVH+mgqaR6Jkk22Q/yYyLPaELfafX5gp/AIXG8n0zxfVaTvK3auSgb1Q6ZLS 1185QH9dSIsmZHlPq7GoSXmKpMdjUL8eaky/IMteioyXgsBiATzl5L2dsw6MTX3MDF0 119QbDK+MzhmbKfDxs= 120-----END X509 CRL-----` 121 client1Crt = `-----BEGIN CERTIFICATE----- 122MIIEITCCAgmgAwIBAgIRAIppZHoj1hM80D7WzTEKLuAwDQYJKoZIhvcNAQELBQAw 123EzERMA8GA1UEAxMIQ2VydEF1dGgwHhcNMjEwMTAyMjEyMzEwWhcNMjIwNzAyMjEz 124MDUxWjASMRAwDgYDVQQDEwdjbGllbnQxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 125MIIBCgKCAQEAoKbYY9MdF2kF/nhBESIiZTdVYtA8XL9xrIZyDj9EnCiTxHiVbJtH 126XVwszqSl5TRrotPmnmAQcX3r8OCk+z+RQZ0QQj257P3kG6q4rNnOcWCS5xEd20jP 127yhQ3m+hMGfZsotNTQze1ochuQgLUN6IPyPxZkH22ia3jX4iu1eo/QxeLYHj1UHw4 1283Cii9yE+j5kPUC21xmnrGKdUrB55NYLXHx6yTIqYR5znSOVB8oJi18/hwdZmH859 129DHhm0Hx1HrS+jbjI3+CMorZJ3WUyNf+CkiVLD3xYutPbxzEpwiqkG/XYzLH0habT 130cDcILo18n+o3jvem2KWBrDhyairjIDscwQIDAQABo3EwbzAOBgNVHQ8BAf8EBAMC 131A7gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBSJ5GIv 132zIrE4ZSQt2+CGblKTDswizAfBgNVHSMEGDAWgBTtcgjQBq/80EySIvJcN8OTorgb 133zDANBgkqhkiG9w0BAQsFAAOCAgEALh4f5GhvNYNou0Ab04iQBbLEdOu2RlbK1B5n 134K9P/umYenBHMY/z6HT3+6tpcHsDuqE8UVdq3f3Gh4S2Gu9m8PRitT+cJ3gdo9Plm 1353rD4ufn/s6rGg3ppydXcedm17492tbccUDWOBZw3IO/ASVq13WPgT0/Kev7cPq0k 136sSdSNhVeXqx8Myc2/d+8GYyzbul2Kpfa7h9i24sK49E9ftnSmsIvngONo08eT1T0 1373wAOyK2981LIsHaAWcneShKFLDB6LeXIT9oitOYhiykhFlBZ4M1GNlSNfhQ8IIQP 138xbqMNXCLkW4/BtLhGEEcg0QVso6Kudl9rzgTfQknrdF7pHp6rS46wYUjoSyIY6dl 139oLmnoAVJX36J3QPWelePI9e07X2wrTfiZWewwgw3KNRWjd6/zfPLe7GoqXnK1S2z 140PT8qMfCaTwKTtUkzXuTFvQ8bAo2My/mS8FOcpkt2oQWeOsADHAUX7fz5BCoa2DL3 141k/7Mh4gVT+JYZEoTwCFuYHgMWFWe98naqHi9lB4yR981p1QgXgxO7qBeipagKY1F 142LlH1iwXUqZ3MZnkNA+4e1Fglsw3sa/rC+L98HnznJ/YbTfQbCP6aQ1qcOymrjMud 1437MrFwqZjtd/SK4Qx1VpK6jGEAtPgWBTUS3p9ayg6lqjMBjsmySWfvRsDQbq6P5Ct 144O/e3EH8= 145-----END CERTIFICATE-----` 146 client1Key = `-----BEGIN RSA PRIVATE KEY----- 147MIIEpAIBAAKCAQEAoKbYY9MdF2kF/nhBESIiZTdVYtA8XL9xrIZyDj9EnCiTxHiV 148bJtHXVwszqSl5TRrotPmnmAQcX3r8OCk+z+RQZ0QQj257P3kG6q4rNnOcWCS5xEd 14920jPyhQ3m+hMGfZsotNTQze1ochuQgLUN6IPyPxZkH22ia3jX4iu1eo/QxeLYHj1 150UHw43Cii9yE+j5kPUC21xmnrGKdUrB55NYLXHx6yTIqYR5znSOVB8oJi18/hwdZm 151H859DHhm0Hx1HrS+jbjI3+CMorZJ3WUyNf+CkiVLD3xYutPbxzEpwiqkG/XYzLH0 152habTcDcILo18n+o3jvem2KWBrDhyairjIDscwQIDAQABAoIBAEBSjVFqtbsp0byR 153aXvyrtLX1Ng7h++at2jca85Ihq//jyqbHTje8zPuNAKI6eNbmb0YGr5OuEa4pD9N 154ssDmMsKSoG/lRwwcm7h4InkSvBWpFShvMgUaohfHAHzsBYxfnh+TfULsi0y7c2n6 155t/2OZcOTRkkUDIITnXYiw93ibHHv2Mv2bBDu35kGrcK+c2dN5IL5ZjTjMRpbJTe2 15644RBJbdTxHBVSgoGBnugF+s2aEma6Ehsj70oyfoVpM6Aed5kGge0A5zA1JO7WCn9 157Ay/DzlULRXHjJIoRWd2NKvx5n3FNppUc9vJh2plRHalRooZ2+MjSf8HmXlvG2Hpb 158ScvmWgECgYEA1G+A/2KnxWsr/7uWIJ7ClcGCiNLdk17Pv3DZ3G4qUsU2ITftfIbb 159tU0Q/b19na1IY8Pjy9ptP7t74/hF5kky97cf1FA8F+nMj/k4+wO8QDI8OJfzVzh9 160PwielA5vbE+xmvis5Hdp8/od1Yrc/rPSy2TKtPFhvsqXjqoUmOAjDP8CgYEAwZjH 1619dt1sc2lx/rMxihlWEzQ3JPswKW9/LJAmbRBoSWF9FGNjbX7uhWtXRKJkzb8ZAwa 16288azluNo2oftbDD/+jw8b2cDgaJHlLAkSD4O1D1RthW7/LKD15qZ/oFsRb13NV85 163ZNKtwslXGbfVNyGKUVFm7fVA8vBAOUey+LKDFj8CgYEAg8WWstOzVdYguMTXXuyb 164ruEV42FJaDyLiSirOvxq7GTAKuLSQUg1yMRBIeQEo2X1XU0JZE3dLodRVhuO4EXP 165g7Dn4X7Th9HSvgvNuIacowWGLWSz4Qp9RjhGhXhezUSx2nseY6le46PmFavJYYSR 1664PBofMyt4PcyA6Cknh+KHmkCgYEAnTriG7ETE0a7v4DXUpB4TpCEiMCy5Xs2o8Z5 167ZNva+W+qLVUWq+MDAIyechqeFSvxK6gRM69LJ96lx+XhU58wJiFJzAhT9rK/g+jS 168bsHH9WOfu0xHkuHA5hgvvV2Le9B2wqgFyva4HJy82qxMxCu/VG/SMqyfBS9OWbb7 169ibQhdq0CgYAl53LUWZsFSZIth1vux2LVOsI8C3X1oiXDGpnrdlQ+K7z57hq5EsRq 170GC+INxwXbvKNqp5h0z2MvmKYPDlGVTgw8f8JjM7TkN17ERLcydhdRrMONUryZpo8 1711xTob+8blyJgfxZUIAKbMbMbIiU0WAF0rfD/eJJwS4htOW/Hfv4TGA== 172-----END RSA PRIVATE KEY-----` 173 // client 2 crt is revoked 174 client2Crt = `-----BEGIN CERTIFICATE----- 175MIIEITCCAgmgAwIBAgIRAL6XTgNsdYzILd8bT2RVd/4wDQYJKoZIhvcNAQELBQAw 176EzERMA8GA1UEAxMIQ2VydEF1dGgwHhcNMjEwMTAyMjEyMzIwWhcNMjIwNzAyMjEz 177MDUxWjASMRAwDgYDVQQDEwdjbGllbnQyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 178MIIBCgKCAQEA6xjW5KQR3/OFQtV5M75WINqQ4AzXSu6DhSz/yumaaQZP/UxY+6hi 179jcrFzGo9MMie/Sza8DhkXOFAl2BelUubrOeB2cl+/Gr8OCyRi2Gv6j3zCsuN/4jQ 180tNaoez/IbkDvI3l/ZpzBtnuNY2RiemGgHuORXHRVf3qVlsw+npBIRW5rM2HkO/xG 181oZjeBErWVu390Lyn+Gvk2TqQDnkutWnxUC60/zPlHhXZ4BwaFAekbSnjsSDB1YFM 182s8HwW4oBryoxdj3/+/qLrBHt75IdLw3T7/V1UDJQM3EvSQOr12w4egpldhtsC871 183nnBQZeY6qA5feffIwwg/6lJm70o6S6OX6wIDAQABo3EwbzAOBgNVHQ8BAf8EBAMC 184A7gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBTB84v5 185t9HqhLhMODbn6oYkEQt3KzAfBgNVHSMEGDAWgBTtcgjQBq/80EySIvJcN8OTorgb 186zDANBgkqhkiG9w0BAQsFAAOCAgEALGtBCve5k8tToL3oLuXp/oSik6ovIB/zq4I/ 1874zNMYPU31+ZWz6aahysgx1JL1yqTa3Qm8o2tu52MbnV10dM7CIw7c/cYa+c+OPcG 1885LF97kp13X+r2axy+CmwM86b4ILaDGs2Qyai6VB6k7oFUve+av5o7aUrNFpqGCJz 189HWdtHZSVA3JMATzy0TfWanwkzreqfdw7qH0yZ9bDURlBKAVWrqnCstva9jRuv+AI 190eqxr/4Ro986TFjJdoAP3Vr16CPg7/B6GA/KmsBWJrpeJdPWq4i2gpLKvYZoy89qD 191mUZf34RbzcCtV4NvV1DadGnt4us0nvLrvS5rL2+2uWD09kZYq9RbLkvgzF/cY0fz 192i7I1bi5XQ+alWe0uAk5ZZL/D+GTRYUX1AWwCqwJxmHrMxcskMyO9pXvLyuSWRDLo 193YNBrbX9nLcfJzVCp+X+9sntTHjs4l6Cw+fLepJIgtgqdCHtbhTiv68vSM6cgb4br 1946n2xrXRKuioiWFOrTSRr+oalZh8dGJ/xvwY8IbWknZAvml9mf1VvfE7Ma5P777QM 195fsbYVTq0Y3R/5hIWsC3HA5z6MIM8L1oRe/YyhP3CTmrCHkVKyDOosGXpGz+JVcyo 196cfYkY5A3yFKB2HaCwZSfwFmRhxkrYWGEbHv3Cd9YkZs1J3hNhGFZyVMC9Uh0S85a 1976zdDidU= 198-----END CERTIFICATE-----` 199 client2Key = `-----BEGIN RSA PRIVATE KEY----- 200MIIEpAIBAAKCAQEA6xjW5KQR3/OFQtV5M75WINqQ4AzXSu6DhSz/yumaaQZP/UxY 201+6hijcrFzGo9MMie/Sza8DhkXOFAl2BelUubrOeB2cl+/Gr8OCyRi2Gv6j3zCsuN 202/4jQtNaoez/IbkDvI3l/ZpzBtnuNY2RiemGgHuORXHRVf3qVlsw+npBIRW5rM2Hk 203O/xGoZjeBErWVu390Lyn+Gvk2TqQDnkutWnxUC60/zPlHhXZ4BwaFAekbSnjsSDB 2041YFMs8HwW4oBryoxdj3/+/qLrBHt75IdLw3T7/V1UDJQM3EvSQOr12w4egpldhts 205C871nnBQZeY6qA5feffIwwg/6lJm70o6S6OX6wIDAQABAoIBAFatstVb1KdQXsq0 206cFpui8zTKOUiduJOrDkWzTygAmlEhYtrccdfXu7OWz0x0lvBLDVGK3a0I/TGrAzj 2074BuFY+FM/egxTVt9in6fmA3et4BS1OAfCryzUdfK6RV//8L+t+zJZ/qKQzWnugpy 208QYjDo8ifuMFwtvEoXizaIyBNLAhEp9hnrv+Tyi2O2gahPvCHsD48zkyZRCHYRstD 209NH5cIrwz9/RJgPO1KI+QsJE7Nh7stR0sbr+5TPU4fnsL2mNhMUF2TJrwIPrc1yp+ 210YIUjdnh3SO88j4TQT3CIrWi8i4pOy6N0dcVn3gpCRGaqAKyS2ZYUj+yVtLO4KwxZ 211SZ1lNvECgYEA78BrF7f4ETfWSLcBQ3qxfLs7ibB6IYo2x25685FhZjD+zLXM1AKb 212FJHEXUm3mUYrFJK6AFEyOQnyGKBOLs3S6oTAswMPbTkkZeD1Y9O6uv0AHASLZnK6 213pC6ub0eSRF5LUyTQ55Jj8D7QsjXJueO8v+G5ihWhNSN9tB2UA+8NBmkCgYEA+weq 214cvoeMIEMBQHnNNLy35bwfqrceGyPIRBcUIvzQfY1vk7KW6DYOUzC7u+WUzy/hA52 215DjXVVhua2eMQ9qqtOav7djcMc2W9RbLowxvno7K5qiCss013MeWk64TCWy+WMp5A 216AVAtOliC3hMkIKqvR2poqn+IBTh1449agUJQqTMCgYEAu06IHGq1GraV6g9XpGF5 217wqoAlMzUTdnOfDabRilBf/YtSr+J++ThRcuwLvXFw7CnPZZ4TIEjDJ7xjj3HdxeE 218fYYjineMmNd40UNUU556F1ZLvJfsVKizmkuCKhwvcMx+asGrmA+tlmds4p3VMS50 219KzDtpKzLWlmU/p/RINWlRmkCgYBy0pHTn7aZZx2xWKqCDg+L2EXPGqZX6wgZDpu7 220OBifzlfM4ctL2CmvI/5yPmLbVgkgBWFYpKUdiujsyyEiQvWTUKhn7UwjqKDHtcsk 221G6p7xS+JswJrzX4885bZJ9Oi1AR2yM3sC9l0O7I4lDbNPmWIXBLeEhGMmcPKv/Kc 22291Ff4wKBgQCF3ur+Vt0PSU0ucrPVHjCe7tqazm0LJaWbPXL1Aw0pzdM2EcNcW/MA 223w0kqpr7MgJ94qhXCBcVcfPuFN9fBOadM3UBj1B45Cz3pptoK+ScI8XKno6jvVK/p 224xr5cb9VBRBtB9aOKVfuRhpatAfS2Pzm2Htae9lFn7slGPUmu2hkjDw== 225-----END RSA PRIVATE KEY-----` 226 testFileName = "test_file_ftp.dat" 227 testDLFileName = "test_download_ftp.dat" 228 tlsClient1Username = "client1" 229 tlsClient2Username = "client2" 230) 231 232var ( 233 allPerms = []string{dataprovider.PermAny} 234 homeBasePath string 235 hookCmdPath string 236 extAuthPath string 237 preLoginPath string 238 postConnectPath string 239 preDownloadPath string 240 preUploadPath string 241 logFilePath string 242 caCrtPath string 243 caCRLPath string 244) 245 246func TestMain(m *testing.M) { 247 logFilePath = filepath.Join(configDir, "sftpgo_ftpd_test.log") 248 bannerFileName := "banner_file" 249 bannerFile := filepath.Join(configDir, bannerFileName) 250 logger.InitLogger(logFilePath, 5, 1, 28, false, false, zerolog.DebugLevel) 251 err := os.WriteFile(bannerFile, []byte("SFTPGo test ready\nsimple banner line\n"), os.ModePerm) 252 if err != nil { 253 logger.ErrorToConsole("error creating banner file: %v", err) 254 } 255 // we run the test cases with UploadMode atomic and resume support. The non atomic code path 256 // simply does not execute some code so if it works in atomic mode will 257 // work in non atomic mode too 258 os.Setenv("SFTPGO_COMMON__UPLOAD_MODE", "2") 259 os.Setenv("SFTPGO_DATA_PROVIDER__CREATE_DEFAULT_ADMIN", "1") 260 os.Setenv("SFTPGO_DEFAULT_ADMIN_USERNAME", "admin") 261 os.Setenv("SFTPGO_DEFAULT_ADMIN_PASSWORD", "password") 262 err = config.LoadConfig(configDir, "") 263 if err != nil { 264 logger.ErrorToConsole("error loading configuration: %v", err) 265 os.Exit(1) 266 } 267 providerConf := config.GetProviderConf() 268 logger.InfoToConsole("Starting FTPD tests, provider: %v", providerConf.Driver) 269 270 commonConf := config.GetCommonConfig() 271 homeBasePath = os.TempDir() 272 if runtime.GOOS != osWindows { 273 commonConf.Actions.ExecuteOn = []string{"download", "upload", "rename", "delete"} 274 commonConf.Actions.Hook = hookCmdPath 275 hookCmdPath, err = exec.LookPath("true") 276 if err != nil { 277 logger.Warn(logSender, "", "unable to get hook command: %v", err) 278 logger.WarnToConsole("unable to get hook command: %v", err) 279 } 280 } 281 282 certPath := filepath.Join(os.TempDir(), "test_ftpd.crt") 283 keyPath := filepath.Join(os.TempDir(), "test_ftpd.key") 284 caCrtPath = filepath.Join(os.TempDir(), "test_ftpd_ca.crt") 285 caCRLPath = filepath.Join(os.TempDir(), "test_ftpd_crl.crt") 286 err = writeCerts(certPath, keyPath, caCrtPath, caCRLPath) 287 if err != nil { 288 os.Exit(1) 289 } 290 291 err = common.Initialize(commonConf) 292 if err != nil { 293 logger.WarnToConsole("error initializing common: %v", err) 294 os.Exit(1) 295 } 296 err = dataprovider.Initialize(providerConf, configDir, true) 297 if err != nil { 298 logger.ErrorToConsole("error initializing data provider: %v", err) 299 os.Exit(1) 300 } 301 302 httpConfig := config.GetHTTPConfig() 303 httpConfig.Initialize(configDir) //nolint:errcheck 304 305 kmsConfig := config.GetKMSConfig() 306 err = kmsConfig.Initialize() 307 if err != nil { 308 logger.ErrorToConsole("error initializing kms: %v", err) 309 os.Exit(1) 310 } 311 mfaConfig := config.GetMFAConfig() 312 err = mfaConfig.Initialize() 313 if err != nil { 314 logger.ErrorToConsole("error initializing MFA: %v", err) 315 os.Exit(1) 316 } 317 318 httpdConf := config.GetHTTPDConfig() 319 httpdConf.Bindings[0].Port = 8079 320 httpdtest.SetBaseURL("http://127.0.0.1:8079") 321 322 ftpdConf := config.GetFTPDConfig() 323 ftpdConf.Bindings = []ftpd.Binding{ 324 { 325 Port: 2121, 326 ClientAuthType: 2, 327 }, 328 } 329 ftpdConf.PassivePortRange.Start = 0 330 ftpdConf.PassivePortRange.End = 0 331 ftpdConf.BannerFile = bannerFileName 332 ftpdConf.CertificateFile = certPath 333 ftpdConf.CertificateKeyFile = keyPath 334 ftpdConf.CACertificates = []string{caCrtPath} 335 ftpdConf.CARevocationLists = []string{caCRLPath} 336 ftpdConf.EnableSite = true 337 338 // required to test sftpfs 339 sftpdConf := config.GetSFTPDConfig() 340 sftpdConf.Bindings = []sftpd.Binding{ 341 { 342 Port: 2122, 343 }, 344 } 345 hostKeyPath := filepath.Join(os.TempDir(), "id_ed25519") 346 sftpdConf.HostKeys = []string{hostKeyPath} 347 348 extAuthPath = filepath.Join(homeBasePath, "extauth.sh") 349 preLoginPath = filepath.Join(homeBasePath, "prelogin.sh") 350 postConnectPath = filepath.Join(homeBasePath, "postconnect.sh") 351 preDownloadPath = filepath.Join(homeBasePath, "predownload.sh") 352 preUploadPath = filepath.Join(homeBasePath, "preupload.sh") 353 354 status := ftpd.GetStatus() 355 if status.IsActive { 356 logger.ErrorToConsole("ftpd is already active") 357 os.Exit(1) 358 } 359 360 go func() { 361 logger.Debug(logSender, "", "initializing FTP server with config %+v", ftpdConf) 362 if err := ftpdConf.Initialize(configDir); err != nil { 363 logger.ErrorToConsole("could not start FTP server: %v", err) 364 os.Exit(1) 365 } 366 }() 367 368 go func() { 369 logger.Debug(logSender, "", "initializing SFTP server with config %+v", sftpdConf) 370 if err := sftpdConf.Initialize(configDir); err != nil { 371 logger.ErrorToConsole("could not start SFTP server: %v", err) 372 os.Exit(1) 373 } 374 }() 375 376 go func() { 377 if err := httpdConf.Initialize(configDir); err != nil { 378 logger.ErrorToConsole("could not start HTTP server: %v", err) 379 os.Exit(1) 380 } 381 }() 382 383 waitTCPListening(ftpdConf.Bindings[0].GetAddress()) 384 waitTCPListening(httpdConf.Bindings[0].GetAddress()) 385 waitTCPListening(sftpdConf.Bindings[0].GetAddress()) 386 ftpd.ReloadCertificateMgr() //nolint:errcheck 387 388 ftpdConf = config.GetFTPDConfig() 389 ftpdConf.Bindings = []ftpd.Binding{ 390 { 391 Port: 2124, 392 TLSMode: 2, 393 }, 394 } 395 ftpdConf.CertificateFile = certPath 396 ftpdConf.CertificateKeyFile = keyPath 397 ftpdConf.CACertificates = []string{caCrtPath} 398 ftpdConf.CARevocationLists = []string{caCRLPath} 399 ftpdConf.EnableSite = false 400 ftpdConf.DisableActiveMode = true 401 ftpdConf.CombineSupport = 1 402 ftpdConf.HASHSupport = 1 403 404 go func() { 405 logger.Debug(logSender, "", "initializing FTP server with config %+v", ftpdConf) 406 if err := ftpdConf.Initialize(configDir); err != nil { 407 logger.ErrorToConsole("could not start FTP server: %v", err) 408 os.Exit(1) 409 } 410 }() 411 412 waitTCPListening(ftpdConf.Bindings[0].GetAddress()) 413 waitNoConnections() 414 415 exitCode := m.Run() 416 os.Remove(logFilePath) 417 os.Remove(bannerFile) 418 os.Remove(extAuthPath) 419 os.Remove(preLoginPath) 420 os.Remove(postConnectPath) 421 os.Remove(preDownloadPath) 422 os.Remove(preUploadPath) 423 os.Remove(certPath) 424 os.Remove(keyPath) 425 os.Remove(caCrtPath) 426 os.Remove(caCRLPath) 427 os.Remove(hostKeyPath) 428 os.Remove(hostKeyPath + ".pub") 429 os.Exit(exitCode) 430} 431 432func TestInitializationFailure(t *testing.T) { 433 ftpdConf := config.GetFTPDConfig() 434 ftpdConf.Bindings = []ftpd.Binding{} 435 ftpdConf.CertificateFile = filepath.Join(os.TempDir(), "test_ftpd.crt") 436 ftpdConf.CertificateKeyFile = filepath.Join(os.TempDir(), "test_ftpd.key") 437 err := ftpdConf.Initialize(configDir) 438 require.EqualError(t, err, common.ErrNoBinding.Error()) 439 ftpdConf.Bindings = []ftpd.Binding{ 440 { 441 Port: 0, 442 }, 443 { 444 Port: 2121, 445 }, 446 } 447 ftpdConf.BannerFile = "a-missing-file" 448 err = ftpdConf.Initialize(configDir) 449 require.Error(t, err) 450 451 ftpdConf.BannerFile = "" 452 ftpdConf.Bindings[1].TLSMode = 10 453 err = ftpdConf.Initialize(configDir) 454 require.Error(t, err) 455 456 ftpdConf.CertificateFile = "" 457 ftpdConf.CertificateKeyFile = "" 458 ftpdConf.Bindings[1].TLSMode = 1 459 err = ftpdConf.Initialize(configDir) 460 require.Error(t, err) 461 462 certPath := filepath.Join(os.TempDir(), "test_ftpd.crt") 463 keyPath := filepath.Join(os.TempDir(), "test_ftpd.key") 464 ftpdConf.CertificateFile = certPath 465 ftpdConf.CertificateKeyFile = keyPath 466 ftpdConf.CACertificates = []string{"invalid ca cert"} 467 err = ftpdConf.Initialize(configDir) 468 require.Error(t, err) 469 470 ftpdConf.CACertificates = nil 471 ftpdConf.CARevocationLists = []string{""} 472 err = ftpdConf.Initialize(configDir) 473 require.Error(t, err) 474 475 ftpdConf.CACertificates = []string{caCrtPath} 476 ftpdConf.CARevocationLists = []string{caCRLPath} 477 ftpdConf.Bindings[1].ForcePassiveIP = "127001" 478 err = ftpdConf.Initialize(configDir) 479 require.Error(t, err) 480 require.Contains(t, err.Error(), "the provided passive IP \"127001\" is not valid") 481 ftpdConf.Bindings[1].ForcePassiveIP = "" 482 err = ftpdConf.Initialize(configDir) 483 require.Error(t, err) 484} 485 486func TestBasicFTPHandling(t *testing.T) { 487 u := getTestUser() 488 u.QuotaSize = 6553600 489 localUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 490 assert.NoError(t, err) 491 u = getTestSFTPUser() 492 u.QuotaSize = 6553600 493 sftpUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 494 assert.NoError(t, err) 495 496 for _, user := range []dataprovider.User{localUser, sftpUser} { 497 client, err := getFTPClient(user, true, nil) 498 if assert.NoError(t, err) { 499 if user.Username == defaultUsername { 500 assert.Len(t, common.Connections.GetStats(), 1) 501 } else { 502 assert.Len(t, common.Connections.GetStats(), 2) 503 } 504 testFilePath := filepath.Join(homeBasePath, testFileName) 505 testFileSize := int64(65535) 506 expectedQuotaSize := testFileSize 507 expectedQuotaFiles := 1 508 err = createTestFile(testFilePath, testFileSize) 509 assert.NoError(t, err) 510 511 err = checkBasicFTP(client) 512 assert.NoError(t, err) 513 err = ftpUploadFile(testFilePath, path.Join("/missing_dir", testFileName), testFileSize, client, 0) 514 assert.Error(t, err) 515 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 516 assert.NoError(t, err) 517 // overwrite an existing file 518 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 519 assert.NoError(t, err) 520 localDownloadPath := filepath.Join(homeBasePath, testDLFileName) 521 err = ftpDownloadFile(testFileName, localDownloadPath, testFileSize, client, 0) 522 assert.NoError(t, err) 523 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 524 assert.NoError(t, err) 525 assert.Equal(t, expectedQuotaFiles, user.UsedQuotaFiles) 526 assert.Equal(t, expectedQuotaSize, user.UsedQuotaSize) 527 err = client.Rename(testFileName, testFileName+"1") 528 assert.NoError(t, err) 529 err = client.Delete(testFileName) 530 assert.Error(t, err) 531 err = client.Delete(testFileName + "1") 532 assert.NoError(t, err) 533 user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK) 534 assert.NoError(t, err) 535 assert.Equal(t, expectedQuotaFiles-1, user.UsedQuotaFiles) 536 assert.Equal(t, expectedQuotaSize-testFileSize, user.UsedQuotaSize) 537 curDir, err := client.CurrentDir() 538 if assert.NoError(t, err) { 539 assert.Equal(t, "/", curDir) 540 } 541 testDir := "testDir" 542 err = client.MakeDir(testDir) 543 assert.NoError(t, err) 544 err = client.ChangeDir(testDir) 545 assert.NoError(t, err) 546 curDir, err = client.CurrentDir() 547 if assert.NoError(t, err) { 548 assert.Equal(t, path.Join("/", testDir), curDir) 549 } 550 res, err := client.List(path.Join("/", testDir)) 551 assert.NoError(t, err) 552 if assert.Len(t, res, 2) { 553 assert.Equal(t, ".", res[0].Name) 554 assert.Equal(t, "..", res[1].Name) 555 } 556 res, err = client.List(path.Join("/")) 557 assert.NoError(t, err) 558 if assert.Len(t, res, 2) { 559 assert.Equal(t, ".", res[0].Name) 560 assert.Equal(t, testDir, res[1].Name) 561 } 562 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 563 assert.NoError(t, err) 564 size, err := client.FileSize(path.Join("/", testDir, testFileName)) 565 assert.NoError(t, err) 566 assert.Equal(t, testFileSize, size) 567 err = client.ChangeDirToParent() 568 assert.NoError(t, err) 569 curDir, err = client.CurrentDir() 570 if assert.NoError(t, err) { 571 assert.Equal(t, "/", curDir) 572 } 573 err = client.Delete(path.Join("/", testDir, testFileName)) 574 assert.NoError(t, err) 575 err = client.Delete(testDir) 576 assert.Error(t, err) 577 err = client.RemoveDir(testDir) 578 assert.NoError(t, err) 579 580 err = os.Remove(testFilePath) 581 assert.NoError(t, err) 582 err = os.Remove(localDownloadPath) 583 assert.NoError(t, err) 584 err = client.Quit() 585 assert.NoError(t, err) 586 } 587 } 588 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 589 assert.NoError(t, err) 590 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 591 assert.NoError(t, err) 592 err = os.RemoveAll(localUser.GetHomeDir()) 593 assert.NoError(t, err) 594 assert.Eventually(t, func() bool { return len(common.Connections.GetStats()) == 0 }, 1*time.Second, 50*time.Millisecond) 595 assert.Eventually(t, func() bool { return common.Connections.GetClientConnections() == 0 }, 1000*time.Millisecond, 596 50*time.Millisecond) 597} 598 599func TestMultiFactorAuth(t *testing.T) { 600 u := getTestUser() 601 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 602 assert.NoError(t, err) 603 604 configName, _, secret, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], user.Username) 605 assert.NoError(t, err) 606 user.Password = defaultPassword 607 user.Filters.TOTPConfig = sdk.TOTPConfig{ 608 Enabled: true, 609 ConfigName: configName, 610 Secret: kms.NewPlainSecret(secret), 611 Protocols: []string{common.ProtocolFTP}, 612 } 613 err = dataprovider.UpdateUser(&user, "", "") 614 assert.NoError(t, err) 615 616 user.Password = defaultPassword 617 _, err = getFTPClient(user, true, nil) 618 if assert.Error(t, err) { 619 assert.Contains(t, err.Error(), dataprovider.ErrInvalidCredentials.Error()) 620 } 621 passcode, err := generateTOTPPasscode(secret, otp.AlgorithmSHA1) 622 assert.NoError(t, err) 623 user.Password = defaultPassword + passcode 624 client, err := getFTPClient(user, true, nil) 625 if assert.NoError(t, err) { 626 err = checkBasicFTP(client) 627 assert.NoError(t, err) 628 err := client.Quit() 629 assert.NoError(t, err) 630 } 631 // reusing the same passcode should not work 632 _, err = getFTPClient(user, true, nil) 633 if assert.Error(t, err) { 634 assert.Contains(t, err.Error(), dataprovider.ErrInvalidCredentials.Error()) 635 } 636 637 _, err = httpdtest.RemoveUser(user, http.StatusOK) 638 assert.NoError(t, err) 639 err = os.RemoveAll(user.GetHomeDir()) 640 assert.NoError(t, err) 641} 642 643func TestLoginInvalidCredentials(t *testing.T) { 644 u := getTestUser() 645 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 646 assert.NoError(t, err) 647 user.Username = "wrong username" 648 _, err = getFTPClient(user, false, nil) 649 if assert.Error(t, err) { 650 assert.Contains(t, err.Error(), dataprovider.ErrInvalidCredentials.Error()) 651 } 652 user.Username = u.Username 653 user.Password = "wrong pwd" 654 _, err = getFTPClient(user, false, nil) 655 if assert.Error(t, err) { 656 assert.Contains(t, err.Error(), dataprovider.ErrInvalidCredentials.Error()) 657 } 658 _, err = httpdtest.RemoveUser(user, http.StatusOK) 659 assert.NoError(t, err) 660} 661 662func TestLoginNonExistentUser(t *testing.T) { 663 user := getTestUser() 664 _, err := getFTPClient(user, false, nil) 665 assert.Error(t, err) 666} 667 668func TestLoginExternalAuth(t *testing.T) { 669 if runtime.GOOS == osWindows { 670 t.Skip("this test is not available on Windows") 671 } 672 u := getTestUser() 673 err := dataprovider.Close() 674 assert.NoError(t, err) 675 err = config.LoadConfig(configDir, "") 676 assert.NoError(t, err) 677 providerConf := config.GetProviderConf() 678 err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm) 679 assert.NoError(t, err) 680 providerConf.ExternalAuthHook = extAuthPath 681 providerConf.ExternalAuthScope = 0 682 err = dataprovider.Initialize(providerConf, configDir, true) 683 assert.NoError(t, err) 684 client, err := getFTPClient(u, true, nil) 685 if assert.NoError(t, err) { 686 err = checkBasicFTP(client) 687 assert.NoError(t, err) 688 err := client.Quit() 689 assert.NoError(t, err) 690 } 691 u.Username = defaultUsername + "1" 692 client, err = getFTPClient(u, true, nil) 693 if !assert.Error(t, err) { 694 err := client.Quit() 695 assert.NoError(t, err) 696 } 697 698 user, _, err := httpdtest.GetUserByUsername(defaultUsername, http.StatusOK) 699 assert.NoError(t, err) 700 assert.Equal(t, defaultUsername, user.Username) 701 _, err = httpdtest.RemoveUser(user, http.StatusOK) 702 assert.NoError(t, err) 703 err = os.RemoveAll(user.GetHomeDir()) 704 assert.NoError(t, err) 705 706 err = dataprovider.Close() 707 assert.NoError(t, err) 708 err = config.LoadConfig(configDir, "") 709 assert.NoError(t, err) 710 providerConf = config.GetProviderConf() 711 err = dataprovider.Initialize(providerConf, configDir, true) 712 assert.NoError(t, err) 713 err = os.Remove(extAuthPath) 714 assert.NoError(t, err) 715} 716 717func TestPreLoginHook(t *testing.T) { 718 if runtime.GOOS == osWindows { 719 t.Skip("this test is not available on Windows") 720 } 721 u := getTestUser() 722 err := dataprovider.Close() 723 assert.NoError(t, err) 724 err = config.LoadConfig(configDir, "") 725 assert.NoError(t, err) 726 providerConf := config.GetProviderConf() 727 err = os.WriteFile(preLoginPath, getPreLoginScriptContent(u, false), os.ModePerm) 728 assert.NoError(t, err) 729 providerConf.PreLoginHook = preLoginPath 730 err = dataprovider.Initialize(providerConf, configDir, true) 731 assert.NoError(t, err) 732 _, _, err = httpdtest.GetUserByUsername(defaultUsername, http.StatusNotFound) 733 assert.NoError(t, err) 734 client, err := getFTPClient(u, false, nil) 735 if assert.NoError(t, err) { 736 err = checkBasicFTP(client) 737 assert.NoError(t, err) 738 err := client.Quit() 739 assert.NoError(t, err) 740 } 741 742 user, _, err := httpdtest.GetUserByUsername(defaultUsername, http.StatusOK) 743 assert.NoError(t, err) 744 745 // test login with an existing user 746 client, err = getFTPClient(user, true, nil) 747 if assert.NoError(t, err) { 748 err = checkBasicFTP(client) 749 assert.NoError(t, err) 750 err := client.Quit() 751 assert.NoError(t, err) 752 } 753 754 err = os.WriteFile(preLoginPath, getPreLoginScriptContent(user, true), os.ModePerm) 755 assert.NoError(t, err) 756 client, err = getFTPClient(u, false, nil) 757 if !assert.Error(t, err) { 758 err := client.Quit() 759 assert.NoError(t, err) 760 } 761 user.Status = 0 762 err = os.WriteFile(preLoginPath, getPreLoginScriptContent(user, false), os.ModePerm) 763 assert.NoError(t, err) 764 client, err = getFTPClient(u, false, nil) 765 if !assert.Error(t, err, "pre-login script returned a disabled user, login must fail") { 766 err := client.Quit() 767 assert.NoError(t, err) 768 } 769 770 _, err = httpdtest.RemoveUser(user, http.StatusOK) 771 assert.NoError(t, err) 772 err = os.RemoveAll(user.GetHomeDir()) 773 assert.NoError(t, err) 774 err = dataprovider.Close() 775 assert.NoError(t, err) 776 err = config.LoadConfig(configDir, "") 777 assert.NoError(t, err) 778 providerConf = config.GetProviderConf() 779 err = dataprovider.Initialize(providerConf, configDir, true) 780 assert.NoError(t, err) 781 err = os.Remove(preLoginPath) 782 assert.NoError(t, err) 783} 784 785func TestPreDownloadHook(t *testing.T) { 786 if runtime.GOOS == osWindows { 787 t.Skip("this test is not available on Windows") 788 } 789 oldExecuteOn := common.Config.Actions.ExecuteOn 790 oldHook := common.Config.Actions.Hook 791 792 common.Config.Actions.ExecuteOn = []string{common.OperationPreDownload} 793 common.Config.Actions.Hook = preDownloadPath 794 795 user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated) 796 assert.NoError(t, err) 797 err = os.WriteFile(preDownloadPath, getExitCodeScriptContent(0), os.ModePerm) 798 assert.NoError(t, err) 799 testFilePath := filepath.Join(homeBasePath, testFileName) 800 testFileSize := int64(65535) 801 err = createTestFile(testFilePath, testFileSize) 802 assert.NoError(t, err) 803 804 client, err := getFTPClient(user, true, nil) 805 if assert.NoError(t, err) { 806 err = checkBasicFTP(client) 807 assert.NoError(t, err) 808 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 809 assert.NoError(t, err) 810 localDownloadPath := filepath.Join(homeBasePath, testDLFileName) 811 err = ftpDownloadFile(testFileName, localDownloadPath, testFileSize, client, 0) 812 assert.NoError(t, err) 813 err := client.Quit() 814 assert.NoError(t, err) 815 err = os.Remove(localDownloadPath) 816 assert.NoError(t, err) 817 } 818 // now return an error from the pre-download hook 819 err = os.WriteFile(preDownloadPath, getExitCodeScriptContent(1), os.ModePerm) 820 assert.NoError(t, err) 821 client, err = getFTPClient(user, true, nil) 822 if assert.NoError(t, err) { 823 err = checkBasicFTP(client) 824 assert.NoError(t, err) 825 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 826 assert.NoError(t, err) 827 localDownloadPath := filepath.Join(homeBasePath, testDLFileName) 828 err = ftpDownloadFile(testFileName, localDownloadPath, testFileSize, client, 0) 829 if assert.Error(t, err) { 830 assert.Contains(t, err.Error(), "permission denied") 831 } 832 err := client.Quit() 833 assert.NoError(t, err) 834 err = os.Remove(localDownloadPath) 835 assert.NoError(t, err) 836 } 837 838 _, err = httpdtest.RemoveUser(user, http.StatusOK) 839 assert.NoError(t, err) 840 err = os.RemoveAll(user.GetHomeDir()) 841 assert.NoError(t, err) 842 err = os.Remove(testFilePath) 843 assert.NoError(t, err) 844 845 common.Config.Actions.ExecuteOn = oldExecuteOn 846 common.Config.Actions.Hook = oldHook 847} 848 849func TestPreUploadHook(t *testing.T) { 850 if runtime.GOOS == osWindows { 851 t.Skip("this test is not available on Windows") 852 } 853 oldExecuteOn := common.Config.Actions.ExecuteOn 854 oldHook := common.Config.Actions.Hook 855 856 common.Config.Actions.ExecuteOn = []string{common.OperationPreUpload} 857 common.Config.Actions.Hook = preUploadPath 858 859 user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated) 860 assert.NoError(t, err) 861 err = os.WriteFile(preUploadPath, getExitCodeScriptContent(0), os.ModePerm) 862 assert.NoError(t, err) 863 testFilePath := filepath.Join(homeBasePath, testFileName) 864 testFileSize := int64(65535) 865 err = createTestFile(testFilePath, testFileSize) 866 assert.NoError(t, err) 867 868 client, err := getFTPClient(user, true, nil) 869 if assert.NoError(t, err) { 870 err = checkBasicFTP(client) 871 assert.NoError(t, err) 872 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 873 assert.NoError(t, err) 874 localDownloadPath := filepath.Join(homeBasePath, testDLFileName) 875 err = ftpDownloadFile(testFileName, localDownloadPath, testFileSize, client, 0) 876 assert.NoError(t, err) 877 err := client.Quit() 878 assert.NoError(t, err) 879 err = os.Remove(localDownloadPath) 880 assert.NoError(t, err) 881 } 882 // now return an error from the pre-upload hook 883 err = os.WriteFile(preUploadPath, getExitCodeScriptContent(1), os.ModePerm) 884 assert.NoError(t, err) 885 client, err = getFTPClient(user, true, nil) 886 if assert.NoError(t, err) { 887 err = checkBasicFTP(client) 888 assert.NoError(t, err) 889 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 890 if assert.Error(t, err) { 891 assert.Contains(t, err.Error(), ftpserver.ErrFileNameNotAllowed.Error()) 892 } 893 err = ftpUploadFile(testFilePath, testFileName+"1", testFileSize, client, 0) 894 if assert.Error(t, err) { 895 assert.Contains(t, err.Error(), ftpserver.ErrFileNameNotAllowed.Error()) 896 } 897 err := client.Quit() 898 assert.NoError(t, err) 899 } 900 901 _, err = httpdtest.RemoveUser(user, http.StatusOK) 902 assert.NoError(t, err) 903 err = os.RemoveAll(user.GetHomeDir()) 904 assert.NoError(t, err) 905 err = os.Remove(testFilePath) 906 assert.NoError(t, err) 907 908 common.Config.Actions.ExecuteOn = oldExecuteOn 909 common.Config.Actions.Hook = oldHook 910} 911 912func TestPostConnectHook(t *testing.T) { 913 if runtime.GOOS == osWindows { 914 t.Skip("this test is not available on Windows") 915 } 916 common.Config.PostConnectHook = postConnectPath 917 918 u := getTestUser() 919 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 920 assert.NoError(t, err) 921 err = os.WriteFile(postConnectPath, getExitCodeScriptContent(0), os.ModePerm) 922 assert.NoError(t, err) 923 client, err := getFTPClient(user, true, nil) 924 if assert.NoError(t, err) { 925 err = checkBasicFTP(client) 926 assert.NoError(t, err) 927 err := client.Quit() 928 assert.NoError(t, err) 929 } 930 err = os.WriteFile(postConnectPath, getExitCodeScriptContent(1), os.ModePerm) 931 assert.NoError(t, err) 932 client, err = getFTPClient(user, true, nil) 933 if !assert.Error(t, err) { 934 err := client.Quit() 935 assert.NoError(t, err) 936 } 937 938 common.Config.PostConnectHook = "http://127.0.0.1:8079/healthz" 939 940 client, err = getFTPClient(user, false, nil) 941 if assert.NoError(t, err) { 942 err = checkBasicFTP(client) 943 assert.NoError(t, err) 944 err := client.Quit() 945 assert.NoError(t, err) 946 } 947 948 common.Config.PostConnectHook = "http://127.0.0.1:8079/notfound" 949 950 client, err = getFTPClient(user, true, nil) 951 if !assert.Error(t, err) { 952 err := client.Quit() 953 assert.NoError(t, err) 954 } 955 956 _, err = httpdtest.RemoveUser(user, http.StatusOK) 957 assert.NoError(t, err) 958 err = os.RemoveAll(user.GetHomeDir()) 959 assert.NoError(t, err) 960 961 common.Config.PostConnectHook = "" 962} 963 964//nolint:dupl 965func TestMaxConnections(t *testing.T) { 966 oldValue := common.Config.MaxTotalConnections 967 common.Config.MaxTotalConnections = 1 968 969 assert.Eventually(t, func() bool { 970 return common.Connections.GetClientConnections() == 0 971 }, 1000*time.Millisecond, 50*time.Millisecond) 972 973 user := getTestUser() 974 err := dataprovider.AddUser(&user, "", "") 975 assert.NoError(t, err) 976 user.Password = "" 977 client, err := getFTPClient(user, true, nil) 978 if assert.NoError(t, err) { 979 err = checkBasicFTP(client) 980 assert.NoError(t, err) 981 _, err = getFTPClient(user, false, nil) 982 assert.Error(t, err) 983 err = client.Quit() 984 assert.NoError(t, err) 985 } 986 err = dataprovider.DeleteUser(user.Username, "", "") 987 assert.NoError(t, err) 988 err = os.RemoveAll(user.GetHomeDir()) 989 assert.NoError(t, err) 990 991 common.Config.MaxTotalConnections = oldValue 992} 993 994//nolint:dupl 995func TestMaxPerHostConnections(t *testing.T) { 996 oldValue := common.Config.MaxPerHostConnections 997 common.Config.MaxPerHostConnections = 1 998 999 assert.Eventually(t, func() bool { 1000 return common.Connections.GetClientConnections() == 0 1001 }, 1000*time.Millisecond, 50*time.Millisecond) 1002 1003 user := getTestUser() 1004 err := dataprovider.AddUser(&user, "", "") 1005 assert.NoError(t, err) 1006 user.Password = "" 1007 client, err := getFTPClient(user, true, nil) 1008 if assert.NoError(t, err) { 1009 err = checkBasicFTP(client) 1010 assert.NoError(t, err) 1011 _, err = getFTPClient(user, false, nil) 1012 assert.Error(t, err) 1013 err = client.Quit() 1014 assert.NoError(t, err) 1015 } 1016 err = dataprovider.DeleteUser(user.Username, "", "") 1017 assert.NoError(t, err) 1018 err = os.RemoveAll(user.GetHomeDir()) 1019 assert.NoError(t, err) 1020 1021 common.Config.MaxPerHostConnections = oldValue 1022} 1023 1024func TestRateLimiter(t *testing.T) { 1025 oldConfig := config.GetCommonConfig() 1026 1027 cfg := config.GetCommonConfig() 1028 cfg.DefenderConfig.Enabled = true 1029 cfg.DefenderConfig.Threshold = 5 1030 cfg.DefenderConfig.ScoreLimitExceeded = 3 1031 cfg.RateLimitersConfig = []common.RateLimiterConfig{ 1032 { 1033 Average: 1, 1034 Period: 1000, 1035 Burst: 1, 1036 Type: 2, 1037 Protocols: []string{common.ProtocolFTP}, 1038 GenerateDefenderEvents: true, 1039 EntriesSoftLimit: 100, 1040 EntriesHardLimit: 150, 1041 }, 1042 } 1043 1044 err := common.Initialize(cfg) 1045 assert.NoError(t, err) 1046 1047 user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated) 1048 assert.NoError(t, err) 1049 1050 client, err := getFTPClient(user, false, nil) 1051 if assert.NoError(t, err) { 1052 err = checkBasicFTP(client) 1053 assert.NoError(t, err) 1054 err = client.Quit() 1055 assert.NoError(t, err) 1056 } 1057 1058 _, err = getFTPClient(user, true, nil) 1059 if assert.Error(t, err) { 1060 assert.Contains(t, err.Error(), "rate limit exceed") 1061 } 1062 1063 _, err = getFTPClient(user, false, nil) 1064 if assert.Error(t, err) { 1065 assert.Contains(t, err.Error(), "rate limit exceed") 1066 } 1067 1068 _, err = getFTPClient(user, true, nil) 1069 if assert.Error(t, err) { 1070 assert.Contains(t, err.Error(), "banned client IP") 1071 } 1072 1073 err = dataprovider.DeleteUser(user.Username, "", "") 1074 assert.NoError(t, err) 1075 err = os.RemoveAll(user.GetHomeDir()) 1076 assert.NoError(t, err) 1077 1078 err = common.Initialize(oldConfig) 1079 assert.NoError(t, err) 1080} 1081 1082func TestDefender(t *testing.T) { 1083 oldConfig := config.GetCommonConfig() 1084 1085 cfg := config.GetCommonConfig() 1086 cfg.DefenderConfig.Enabled = true 1087 cfg.DefenderConfig.Threshold = 3 1088 cfg.DefenderConfig.ScoreLimitExceeded = 2 1089 1090 err := common.Initialize(cfg) 1091 assert.NoError(t, err) 1092 1093 user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated) 1094 assert.NoError(t, err) 1095 client, err := getFTPClient(user, false, nil) 1096 if assert.NoError(t, err) { 1097 err = checkBasicFTP(client) 1098 assert.NoError(t, err) 1099 err = client.Quit() 1100 assert.NoError(t, err) 1101 } 1102 1103 for i := 0; i < 3; i++ { 1104 user.Password = "wrong_pwd" 1105 _, err = getFTPClient(user, false, nil) 1106 assert.Error(t, err) 1107 } 1108 1109 user.Password = defaultPassword 1110 _, err = getFTPClient(user, false, nil) 1111 if assert.Error(t, err) { 1112 assert.Contains(t, err.Error(), "banned client IP") 1113 } 1114 1115 err = dataprovider.DeleteUser(user.Username, "", "") 1116 assert.NoError(t, err) 1117 err = os.RemoveAll(user.GetHomeDir()) 1118 assert.NoError(t, err) 1119 1120 err = common.Initialize(oldConfig) 1121 assert.NoError(t, err) 1122} 1123 1124func TestMaxSessions(t *testing.T) { 1125 u := getTestUser() 1126 u.MaxSessions = 1 1127 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 1128 assert.NoError(t, err) 1129 client, err := getFTPClient(user, true, nil) 1130 if assert.NoError(t, err) { 1131 err = checkBasicFTP(client) 1132 assert.NoError(t, err) 1133 _, err = getFTPClient(user, false, nil) 1134 assert.Error(t, err) 1135 err = client.Quit() 1136 assert.NoError(t, err) 1137 } 1138 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1139 assert.NoError(t, err) 1140 err = os.RemoveAll(user.GetHomeDir()) 1141 assert.NoError(t, err) 1142} 1143 1144func TestZeroBytesTransfers(t *testing.T) { 1145 u := getTestUser() 1146 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 1147 assert.NoError(t, err) 1148 for _, useTLS := range []bool{true, false} { 1149 client, err := getFTPClient(user, useTLS, nil) 1150 if assert.NoError(t, err) { 1151 testFileName := "testfilename" 1152 err = checkBasicFTP(client) 1153 assert.NoError(t, err) 1154 localDownloadPath := filepath.Join(homeBasePath, "empty_download") 1155 err = os.WriteFile(localDownloadPath, []byte(""), os.ModePerm) 1156 assert.NoError(t, err) 1157 err = ftpUploadFile(localDownloadPath, testFileName, 0, client, 0) 1158 assert.NoError(t, err) 1159 size, err := client.FileSize(testFileName) 1160 assert.NoError(t, err) 1161 assert.Equal(t, int64(0), size) 1162 err = os.Remove(localDownloadPath) 1163 assert.NoError(t, err) 1164 assert.NoFileExists(t, localDownloadPath) 1165 err = ftpDownloadFile(testFileName, localDownloadPath, 0, client, 0) 1166 assert.NoError(t, err) 1167 assert.FileExists(t, localDownloadPath) 1168 err = client.Quit() 1169 assert.NoError(t, err) 1170 err = os.Remove(localDownloadPath) 1171 assert.NoError(t, err) 1172 } 1173 } 1174 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1175 assert.NoError(t, err) 1176 err = os.RemoveAll(user.GetHomeDir()) 1177 assert.NoError(t, err) 1178} 1179 1180func TestDownloadErrors(t *testing.T) { 1181 u := getTestUser() 1182 u.QuotaFiles = 1 1183 subDir1 := "sub1" 1184 subDir2 := "sub2" 1185 u.Permissions[path.Join("/", subDir1)] = []string{dataprovider.PermListItems} 1186 u.Permissions[path.Join("/", subDir2)] = []string{dataprovider.PermListItems, dataprovider.PermUpload, 1187 dataprovider.PermDelete, dataprovider.PermDownload} 1188 u.Filters.FilePatterns = []sdk.PatternsFilter{ 1189 { 1190 Path: "/sub2", 1191 AllowedPatterns: []string{}, 1192 DeniedPatterns: []string{"*.jpg", "*.zip"}, 1193 }, 1194 } 1195 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 1196 assert.NoError(t, err) 1197 client, err := getFTPClient(user, true, nil) 1198 if assert.NoError(t, err) { 1199 testFilePath1 := filepath.Join(user.HomeDir, subDir1, "file.zip") 1200 testFilePath2 := filepath.Join(user.HomeDir, subDir2, "file.zip") 1201 testFilePath3 := filepath.Join(user.HomeDir, subDir2, "file.jpg") 1202 err = os.MkdirAll(filepath.Dir(testFilePath1), os.ModePerm) 1203 assert.NoError(t, err) 1204 err = os.MkdirAll(filepath.Dir(testFilePath2), os.ModePerm) 1205 assert.NoError(t, err) 1206 err = os.WriteFile(testFilePath1, []byte("file1"), os.ModePerm) 1207 assert.NoError(t, err) 1208 err = os.WriteFile(testFilePath2, []byte("file2"), os.ModePerm) 1209 assert.NoError(t, err) 1210 err = os.WriteFile(testFilePath3, []byte("file3"), os.ModePerm) 1211 assert.NoError(t, err) 1212 localDownloadPath := filepath.Join(homeBasePath, testDLFileName) 1213 err = ftpDownloadFile(path.Join("/", subDir1, "file.zip"), localDownloadPath, 5, client, 0) 1214 assert.Error(t, err) 1215 err = ftpDownloadFile(path.Join("/", subDir2, "file.zip"), localDownloadPath, 5, client, 0) 1216 assert.Error(t, err) 1217 err = ftpDownloadFile(path.Join("/", subDir2, "file.jpg"), localDownloadPath, 5, client, 0) 1218 assert.Error(t, err) 1219 err = ftpDownloadFile("/missing.zip", localDownloadPath, 5, client, 0) 1220 assert.Error(t, err) 1221 err = client.Quit() 1222 assert.NoError(t, err) 1223 err = os.Remove(localDownloadPath) 1224 assert.NoError(t, err) 1225 } 1226 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1227 assert.NoError(t, err) 1228 err = os.RemoveAll(user.GetHomeDir()) 1229 assert.NoError(t, err) 1230} 1231 1232func TestUploadErrors(t *testing.T) { 1233 u := getTestUser() 1234 u.QuotaSize = 65535 1235 subDir1 := "sub1" 1236 subDir2 := "sub2" 1237 u.Permissions[path.Join("/", subDir1)] = []string{dataprovider.PermListItems} 1238 u.Permissions[path.Join("/", subDir2)] = []string{dataprovider.PermListItems, dataprovider.PermUpload, 1239 dataprovider.PermDelete} 1240 u.Filters.FilePatterns = []sdk.PatternsFilter{ 1241 { 1242 Path: "/sub2", 1243 AllowedPatterns: []string{}, 1244 DeniedPatterns: []string{"*.zip"}, 1245 }, 1246 } 1247 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 1248 assert.NoError(t, err) 1249 client, err := getFTPClient(user, true, nil) 1250 if assert.NoError(t, err) { 1251 testFilePath := filepath.Join(homeBasePath, testFileName) 1252 testFileSize := user.QuotaSize 1253 err = createTestFile(testFilePath, testFileSize) 1254 assert.NoError(t, err) 1255 err = client.MakeDir(subDir1) 1256 assert.NoError(t, err) 1257 err = client.MakeDir(subDir2) 1258 assert.NoError(t, err) 1259 err = client.ChangeDir(subDir1) 1260 assert.NoError(t, err) 1261 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 1262 assert.Error(t, err) 1263 err = client.ChangeDirToParent() 1264 assert.NoError(t, err) 1265 err = client.ChangeDir(subDir2) 1266 assert.NoError(t, err) 1267 err = ftpUploadFile(testFilePath, testFileName+".zip", testFileSize, client, 0) 1268 assert.Error(t, err) 1269 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 1270 assert.NoError(t, err) 1271 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 1272 assert.Error(t, err) 1273 err = client.ChangeDir("/") 1274 assert.NoError(t, err) 1275 err = ftpUploadFile(testFilePath, subDir1, testFileSize, client, 0) 1276 assert.Error(t, err) 1277 // overquota 1278 err = ftpUploadFile(testFilePath, testFileName+"1", testFileSize, client, 0) 1279 assert.Error(t, err) 1280 err = client.Delete(path.Join("/", subDir2, testFileName)) 1281 assert.NoError(t, err) 1282 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 1283 assert.NoError(t, err) 1284 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 1285 assert.Error(t, err) 1286 err = client.Quit() 1287 assert.NoError(t, err) 1288 err = os.Remove(testFilePath) 1289 assert.NoError(t, err) 1290 } 1291 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1292 assert.NoError(t, err) 1293 err = os.RemoveAll(user.GetHomeDir()) 1294 assert.NoError(t, err) 1295} 1296 1297func TestSFTPBuffered(t *testing.T) { 1298 u := getTestUser() 1299 localUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 1300 assert.NoError(t, err) 1301 u = getTestSFTPUser() 1302 u.QuotaFiles = 100 1303 u.FsConfig.SFTPConfig.BufferSize = 2 1304 u.HomeDir = filepath.Join(os.TempDir(), u.Username) 1305 sftpUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 1306 assert.NoError(t, err) 1307 client, err := getFTPClient(sftpUser, true, nil) 1308 if assert.NoError(t, err) { 1309 testFilePath := filepath.Join(homeBasePath, testFileName) 1310 testFileSize := int64(65535) 1311 expectedQuotaSize := testFileSize 1312 expectedQuotaFiles := 1 1313 err = createTestFile(testFilePath, testFileSize) 1314 assert.NoError(t, err) 1315 err = checkBasicFTP(client) 1316 assert.NoError(t, err) 1317 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 1318 assert.NoError(t, err) 1319 // overwrite an existing file 1320 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 1321 assert.NoError(t, err) 1322 localDownloadPath := filepath.Join(homeBasePath, testDLFileName) 1323 err = ftpDownloadFile(testFileName, localDownloadPath, testFileSize, client, 0) 1324 assert.NoError(t, err) 1325 user, _, err := httpdtest.GetUserByUsername(sftpUser.Username, http.StatusOK) 1326 assert.NoError(t, err) 1327 assert.Equal(t, expectedQuotaFiles, user.UsedQuotaFiles) 1328 assert.Equal(t, expectedQuotaSize, user.UsedQuotaSize) 1329 1330 data := []byte("test data") 1331 err = os.WriteFile(testFilePath, data, os.ModePerm) 1332 assert.NoError(t, err) 1333 err = ftpUploadFile(testFilePath, testFileName, int64(len(data)), client, 0) 1334 assert.NoError(t, err) 1335 err = ftpUploadFile(testFilePath, testFileName, int64(len(data)+5), client, 5) 1336 if assert.Error(t, err) { 1337 assert.Contains(t, err.Error(), "operation unsupported") 1338 } 1339 err = ftpDownloadFile(testFileName, localDownloadPath, int64(4), client, 5) 1340 assert.NoError(t, err) 1341 readed, err := os.ReadFile(localDownloadPath) 1342 assert.NoError(t, err) 1343 assert.Equal(t, []byte("data"), readed) 1344 // try to append to a file, it should fail 1345 // now append to a file 1346 srcFile, err := os.Open(testFilePath) 1347 if assert.NoError(t, err) { 1348 err = client.Append(testFileName, srcFile) 1349 if assert.Error(t, err) { 1350 assert.Contains(t, err.Error(), "operation unsupported") 1351 } 1352 err = srcFile.Close() 1353 assert.NoError(t, err) 1354 size, err := client.FileSize(testFileName) 1355 assert.NoError(t, err) 1356 assert.Equal(t, int64(len(data)), size) 1357 err = ftpDownloadFile(testFileName, localDownloadPath, int64(len(data)), client, 0) 1358 assert.NoError(t, err) 1359 } 1360 1361 err = os.Remove(testFilePath) 1362 assert.NoError(t, err) 1363 err = os.Remove(localDownloadPath) 1364 assert.NoError(t, err) 1365 err = client.Quit() 1366 assert.NoError(t, err) 1367 } 1368 1369 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 1370 assert.NoError(t, err) 1371 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 1372 assert.NoError(t, err) 1373 err = os.RemoveAll(localUser.GetHomeDir()) 1374 assert.NoError(t, err) 1375 err = os.RemoveAll(sftpUser.GetHomeDir()) 1376 assert.NoError(t, err) 1377} 1378 1379func TestResume(t *testing.T) { 1380 u := getTestUser() 1381 localUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 1382 assert.NoError(t, err) 1383 sftpUser, _, err := httpdtest.AddUser(getTestSFTPUser(), http.StatusCreated) 1384 assert.NoError(t, err) 1385 for _, user := range []dataprovider.User{localUser, sftpUser} { 1386 client, err := getFTPClient(user, true, nil) 1387 if assert.NoError(t, err) { 1388 testFilePath := filepath.Join(homeBasePath, testFileName) 1389 data := []byte("test data") 1390 err = os.WriteFile(testFilePath, data, os.ModePerm) 1391 assert.NoError(t, err) 1392 err = ftpUploadFile(testFilePath, testFileName, int64(len(data)), client, 0) 1393 assert.NoError(t, err) 1394 err = ftpUploadFile(testFilePath, testFileName, int64(len(data)+5), client, 5) 1395 assert.NoError(t, err) 1396 readed, err := os.ReadFile(filepath.Join(user.GetHomeDir(), testFileName)) 1397 assert.NoError(t, err) 1398 assert.Equal(t, "test test data", string(readed)) 1399 localDownloadPath := filepath.Join(homeBasePath, testDLFileName) 1400 err = ftpDownloadFile(testFileName, localDownloadPath, int64(len(data)), client, 5) 1401 assert.NoError(t, err) 1402 readed, err = os.ReadFile(localDownloadPath) 1403 assert.NoError(t, err) 1404 assert.Equal(t, data, readed) 1405 err = client.Delete(testFileName) 1406 assert.NoError(t, err) 1407 err = ftpUploadFile(testFilePath, testFileName, int64(len(data)), client, 0) 1408 assert.NoError(t, err) 1409 // now append to a file 1410 srcFile, err := os.Open(testFilePath) 1411 if assert.NoError(t, err) { 1412 err = client.Append(testFileName, srcFile) 1413 assert.NoError(t, err) 1414 err = srcFile.Close() 1415 assert.NoError(t, err) 1416 size, err := client.FileSize(testFileName) 1417 assert.NoError(t, err) 1418 assert.Equal(t, int64(2*len(data)), size) 1419 err = ftpDownloadFile(testFileName, localDownloadPath, int64(2*len(data)), client, 0) 1420 assert.NoError(t, err) 1421 readed, err = os.ReadFile(localDownloadPath) 1422 assert.NoError(t, err) 1423 expected := append(data, data...) 1424 assert.Equal(t, expected, readed) 1425 } 1426 err = client.Quit() 1427 assert.NoError(t, err) 1428 err = os.Remove(testFilePath) 1429 assert.NoError(t, err) 1430 err = os.Remove(localDownloadPath) 1431 assert.NoError(t, err) 1432 if user.Username == defaultUsername { 1433 err = os.RemoveAll(user.GetHomeDir()) 1434 assert.NoError(t, err) 1435 } 1436 } 1437 } 1438 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 1439 assert.NoError(t, err) 1440 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 1441 assert.NoError(t, err) 1442 err = os.RemoveAll(localUser.GetHomeDir()) 1443 assert.NoError(t, err) 1444} 1445 1446//nolint:dupl 1447func TestDeniedLoginMethod(t *testing.T) { 1448 u := getTestUser() 1449 u.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodPassword} 1450 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 1451 assert.NoError(t, err) 1452 _, err = getFTPClient(user, false, nil) 1453 assert.Error(t, err) 1454 user.Filters.DeniedLoginMethods = []string{dataprovider.SSHLoginMethodPublicKey, dataprovider.SSHLoginMethodKeyAndPassword} 1455 user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 1456 assert.NoError(t, err) 1457 client, err := getFTPClient(user, true, nil) 1458 if assert.NoError(t, err) { 1459 assert.NoError(t, checkBasicFTP(client)) 1460 err = client.Quit() 1461 assert.NoError(t, err) 1462 } 1463 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1464 assert.NoError(t, err) 1465 err = os.RemoveAll(user.GetHomeDir()) 1466 assert.NoError(t, err) 1467} 1468 1469//nolint:dupl 1470func TestDeniedProtocols(t *testing.T) { 1471 u := getTestUser() 1472 u.Filters.DeniedProtocols = []string{common.ProtocolFTP} 1473 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 1474 assert.NoError(t, err) 1475 _, err = getFTPClient(user, false, nil) 1476 assert.Error(t, err) 1477 user.Filters.DeniedProtocols = []string{common.ProtocolSSH, common.ProtocolWebDAV} 1478 user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 1479 assert.NoError(t, err) 1480 client, err := getFTPClient(user, true, nil) 1481 if assert.NoError(t, err) { 1482 assert.NoError(t, checkBasicFTP(client)) 1483 err = client.Quit() 1484 assert.NoError(t, err) 1485 } 1486 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1487 assert.NoError(t, err) 1488 err = os.RemoveAll(user.GetHomeDir()) 1489 assert.NoError(t, err) 1490} 1491 1492func TestQuotaLimits(t *testing.T) { 1493 u := getTestUser() 1494 u.QuotaFiles = 1 1495 localUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 1496 assert.NoError(t, err) 1497 u = getTestSFTPUser() 1498 u.QuotaFiles = 1 1499 sftpUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 1500 assert.NoError(t, err) 1501 for _, user := range []dataprovider.User{localUser, sftpUser} { 1502 testFileSize := int64(65535) 1503 testFilePath := filepath.Join(homeBasePath, testFileName) 1504 err = createTestFile(testFilePath, testFileSize) 1505 assert.NoError(t, err) 1506 testFileSize1 := int64(131072) 1507 testFileName1 := "test_file1.dat" 1508 testFilePath1 := filepath.Join(homeBasePath, testFileName1) 1509 err = createTestFile(testFilePath1, testFileSize1) 1510 assert.NoError(t, err) 1511 testFileSize2 := int64(32768) 1512 testFileName2 := "test_file2.dat" 1513 testFilePath2 := filepath.Join(homeBasePath, testFileName2) 1514 err = createTestFile(testFilePath2, testFileSize2) 1515 assert.NoError(t, err) 1516 // test quota files 1517 client, err := getFTPClient(user, false, nil) 1518 if assert.NoError(t, err) { 1519 err = ftpUploadFile(testFilePath, testFileName+".quota", testFileSize, client, 0) 1520 assert.NoError(t, err) 1521 err = ftpUploadFile(testFilePath, testFileName+".quota1", testFileSize, client, 0) 1522 assert.Error(t, err) 1523 err = client.Rename(testFileName+".quota", testFileName) 1524 assert.NoError(t, err) 1525 err = client.Quit() 1526 assert.NoError(t, err) 1527 } 1528 // test quota size 1529 user.QuotaSize = testFileSize - 1 1530 user.QuotaFiles = 0 1531 user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 1532 assert.NoError(t, err) 1533 client, err = getFTPClient(user, true, nil) 1534 if assert.NoError(t, err) { 1535 err = ftpUploadFile(testFilePath, testFileName+".quota", testFileSize, client, 0) 1536 assert.Error(t, err) 1537 err = client.Rename(testFileName, testFileName+".quota") 1538 assert.NoError(t, err) 1539 err = client.Quit() 1540 assert.NoError(t, err) 1541 } 1542 // now test quota limits while uploading the current file, we have 1 bytes remaining 1543 user.QuotaSize = testFileSize + 1 1544 user.QuotaFiles = 0 1545 user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 1546 assert.NoError(t, err) 1547 client, err = getFTPClient(user, false, nil) 1548 if assert.NoError(t, err) { 1549 err = ftpUploadFile(testFilePath1, testFileName1, testFileSize1, client, 0) 1550 assert.Error(t, err) 1551 _, err = client.FileSize(testFileName1) 1552 assert.Error(t, err) 1553 err = client.Rename(testFileName+".quota", testFileName) 1554 assert.NoError(t, err) 1555 // overwriting an existing file will work if the resulting size is lesser or equal than the current one 1556 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 1557 assert.NoError(t, err) 1558 err = ftpUploadFile(testFilePath2, testFileName, testFileSize2, client, 0) 1559 assert.NoError(t, err) 1560 err = ftpUploadFile(testFilePath1, testFileName, testFileSize1, client, 0) 1561 assert.Error(t, err) 1562 err = ftpUploadFile(testFilePath1, testFileName, testFileSize1, client, 10) 1563 assert.Error(t, err) 1564 err = ftpUploadFile(testFilePath2, testFileName, testFileSize2, client, 0) 1565 assert.NoError(t, err) 1566 err = client.Quit() 1567 assert.NoError(t, err) 1568 } 1569 1570 err = os.Remove(testFilePath) 1571 assert.NoError(t, err) 1572 err = os.Remove(testFilePath1) 1573 assert.NoError(t, err) 1574 err = os.Remove(testFilePath2) 1575 assert.NoError(t, err) 1576 if user.Username == defaultUsername { 1577 err = os.RemoveAll(user.GetHomeDir()) 1578 assert.NoError(t, err) 1579 user.QuotaFiles = 0 1580 user.QuotaSize = 0 1581 _, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 1582 assert.NoError(t, err) 1583 } 1584 } 1585 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 1586 assert.NoError(t, err) 1587 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 1588 assert.NoError(t, err) 1589 err = os.RemoveAll(localUser.GetHomeDir()) 1590 assert.NoError(t, err) 1591} 1592 1593func TestUploadMaxSize(t *testing.T) { 1594 testFileSize := int64(65535) 1595 u := getTestUser() 1596 u.Filters.MaxUploadFileSize = testFileSize + 1 1597 localUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 1598 assert.NoError(t, err) 1599 u = getTestSFTPUser() 1600 u.Filters.MaxUploadFileSize = testFileSize + 1 1601 sftpUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 1602 assert.NoError(t, err) 1603 for _, user := range []dataprovider.User{localUser, sftpUser} { 1604 testFilePath := filepath.Join(homeBasePath, testFileName) 1605 err = createTestFile(testFilePath, testFileSize) 1606 assert.NoError(t, err) 1607 testFileSize1 := int64(131072) 1608 testFileName1 := "test_file1.dat" 1609 testFilePath1 := filepath.Join(homeBasePath, testFileName1) 1610 err = createTestFile(testFilePath1, testFileSize1) 1611 assert.NoError(t, err) 1612 client, err := getFTPClient(user, false, nil) 1613 if assert.NoError(t, err) { 1614 err = ftpUploadFile(testFilePath1, testFileName1, testFileSize1, client, 0) 1615 assert.Error(t, err) 1616 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 1617 assert.NoError(t, err) 1618 // now test overwrite an existing file with a size bigger than the allowed one 1619 err = createTestFile(filepath.Join(user.GetHomeDir(), testFileName1), testFileSize1) 1620 assert.NoError(t, err) 1621 err = ftpUploadFile(testFilePath1, testFileName1, testFileSize1, client, 0) 1622 assert.Error(t, err) 1623 err = client.Quit() 1624 assert.NoError(t, err) 1625 } 1626 err = os.Remove(testFilePath) 1627 assert.NoError(t, err) 1628 err = os.Remove(testFilePath1) 1629 assert.NoError(t, err) 1630 if user.Username == defaultUsername { 1631 err = os.RemoveAll(user.GetHomeDir()) 1632 assert.NoError(t, err) 1633 user.Filters.MaxUploadFileSize = 65536000 1634 _, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 1635 assert.NoError(t, err) 1636 } 1637 } 1638 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 1639 assert.NoError(t, err) 1640 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 1641 assert.NoError(t, err) 1642 err = os.RemoveAll(localUser.GetHomeDir()) 1643 assert.NoError(t, err) 1644} 1645 1646func TestLoginWithIPilters(t *testing.T) { 1647 u := getTestUser() 1648 u.Filters.DeniedIP = []string{"192.167.0.0/24", "172.18.0.0/16"} 1649 u.Filters.AllowedIP = []string{"172.19.0.0/16"} 1650 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 1651 assert.NoError(t, err) 1652 client, err := getFTPClient(user, true, nil) 1653 if !assert.Error(t, err) { 1654 err = client.Quit() 1655 assert.NoError(t, err) 1656 } 1657 1658 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1659 assert.NoError(t, err) 1660 err = os.RemoveAll(user.GetHomeDir()) 1661 assert.NoError(t, err) 1662} 1663 1664func TestLoginWithDatabaseCredentials(t *testing.T) { 1665 u := getTestUser() 1666 u.FsConfig.Provider = sdk.GCSFilesystemProvider 1667 u.FsConfig.GCSConfig.Bucket = "test" 1668 u.FsConfig.GCSConfig.Credentials = kms.NewPlainSecret(`{ "type": "service_account" }`) 1669 1670 providerConf := config.GetProviderConf() 1671 providerConf.PreferDatabaseCredentials = true 1672 credentialsFile := filepath.Join(providerConf.CredentialsPath, fmt.Sprintf("%v_gcs_credentials.json", u.Username)) 1673 if !filepath.IsAbs(credentialsFile) { 1674 credentialsFile = filepath.Join(configDir, credentialsFile) 1675 } 1676 1677 assert.NoError(t, dataprovider.Close()) 1678 1679 err := dataprovider.Initialize(providerConf, configDir, true) 1680 assert.NoError(t, err) 1681 1682 if _, err = os.Stat(credentialsFile); err == nil { 1683 // remove the credentials file 1684 assert.NoError(t, os.Remove(credentialsFile)) 1685 } 1686 1687 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 1688 assert.NoError(t, err) 1689 assert.Equal(t, kms.SecretStatusSecretBox, user.FsConfig.GCSConfig.Credentials.GetStatus()) 1690 assert.NotEmpty(t, user.FsConfig.GCSConfig.Credentials.GetPayload()) 1691 assert.Empty(t, user.FsConfig.GCSConfig.Credentials.GetAdditionalData()) 1692 assert.Empty(t, user.FsConfig.GCSConfig.Credentials.GetKey()) 1693 1694 assert.NoFileExists(t, credentialsFile) 1695 1696 client, err := getFTPClient(user, false, nil) 1697 if assert.NoError(t, err) { 1698 err = client.Quit() 1699 assert.NoError(t, err) 1700 } 1701 1702 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1703 assert.NoError(t, err) 1704 err = os.RemoveAll(user.GetHomeDir()) 1705 assert.NoError(t, err) 1706 1707 assert.NoError(t, dataprovider.Close()) 1708 assert.NoError(t, config.LoadConfig(configDir, "")) 1709 providerConf = config.GetProviderConf() 1710 assert.NoError(t, dataprovider.Initialize(providerConf, configDir, true)) 1711} 1712 1713func TestLoginInvalidFs(t *testing.T) { 1714 u := getTestUser() 1715 u.FsConfig.Provider = sdk.GCSFilesystemProvider 1716 u.FsConfig.GCSConfig.Bucket = "test" 1717 u.FsConfig.GCSConfig.Credentials = kms.NewPlainSecret("invalid JSON for credentials") 1718 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 1719 assert.NoError(t, err) 1720 1721 providerConf := config.GetProviderConf() 1722 credentialsFile := filepath.Join(providerConf.CredentialsPath, fmt.Sprintf("%v_gcs_credentials.json", u.Username)) 1723 if !filepath.IsAbs(credentialsFile) { 1724 credentialsFile = filepath.Join(configDir, credentialsFile) 1725 } 1726 1727 // now remove the credentials file so the filesystem creation will fail 1728 err = os.Remove(credentialsFile) 1729 assert.NoError(t, err) 1730 1731 client, err := getFTPClient(user, false, nil) 1732 if !assert.Error(t, err) { 1733 err = client.Quit() 1734 assert.NoError(t, err) 1735 } 1736 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1737 assert.NoError(t, err) 1738 err = os.RemoveAll(user.GetHomeDir()) 1739 assert.NoError(t, err) 1740} 1741 1742func TestClientClose(t *testing.T) { 1743 u := getTestUser() 1744 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 1745 assert.NoError(t, err) 1746 client, err := getFTPClient(user, true, nil) 1747 if assert.NoError(t, err) { 1748 err = checkBasicFTP(client) 1749 assert.NoError(t, err) 1750 stats := common.Connections.GetStats() 1751 if assert.Len(t, stats, 1) { 1752 common.Connections.Close(stats[0].ConnectionID) 1753 assert.Eventually(t, func() bool { return len(common.Connections.GetStats()) == 0 }, 1754 1*time.Second, 50*time.Millisecond) 1755 } 1756 } 1757 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1758 assert.NoError(t, err) 1759 err = os.RemoveAll(user.GetHomeDir()) 1760 assert.NoError(t, err) 1761} 1762 1763func TestRename(t *testing.T) { 1764 u := getTestUser() 1765 localUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 1766 assert.NoError(t, err) 1767 sftpUser, _, err := httpdtest.AddUser(getTestSFTPUser(), http.StatusCreated) 1768 assert.NoError(t, err) 1769 for _, user := range []dataprovider.User{localUser, sftpUser} { 1770 testDir := "adir" 1771 testFilePath := filepath.Join(homeBasePath, testFileName) 1772 testFileSize := int64(65535) 1773 err = createTestFile(testFilePath, testFileSize) 1774 assert.NoError(t, err) 1775 client, err := getFTPClient(user, false, nil) 1776 if assert.NoError(t, err) { 1777 err = checkBasicFTP(client) 1778 assert.NoError(t, err) 1779 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 1780 assert.NoError(t, err) 1781 err = client.MakeDir(testDir) 1782 assert.NoError(t, err) 1783 err = client.Rename(testFileName, path.Join("missing", testFileName)) 1784 assert.Error(t, err) 1785 err = client.Rename(testFileName, path.Join(testDir, testFileName)) 1786 assert.NoError(t, err) 1787 size, err := client.FileSize(path.Join(testDir, testFileName)) 1788 assert.NoError(t, err) 1789 assert.Equal(t, testFileSize, size) 1790 if runtime.GOOS != osWindows { 1791 otherDir := "dir" 1792 err = client.MakeDir(otherDir) 1793 assert.NoError(t, err) 1794 err = client.MakeDir(path.Join(otherDir, testDir)) 1795 assert.NoError(t, err) 1796 code, response, err := client.SendCustomCommand(fmt.Sprintf("SITE CHMOD 0001 %v", otherDir)) 1797 assert.NoError(t, err) 1798 assert.Equal(t, ftp.StatusCommandOK, code) 1799 assert.Equal(t, "SITE CHMOD command successful", response) 1800 err = client.Rename(testDir, path.Join(otherDir, testDir)) 1801 assert.Error(t, err) 1802 1803 code, response, err = client.SendCustomCommand(fmt.Sprintf("SITE CHMOD 755 %v", otherDir)) 1804 assert.NoError(t, err) 1805 assert.Equal(t, ftp.StatusCommandOK, code) 1806 assert.Equal(t, "SITE CHMOD command successful", response) 1807 } 1808 err = client.Quit() 1809 assert.NoError(t, err) 1810 } 1811 user.Permissions[path.Join("/", testDir)] = []string{dataprovider.PermListItems} 1812 user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 1813 assert.NoError(t, err) 1814 client, err = getFTPClient(user, false, nil) 1815 if assert.NoError(t, err) { 1816 err = client.Rename(path.Join(testDir, testFileName), testFileName) 1817 assert.Error(t, err) 1818 err := client.Quit() 1819 assert.NoError(t, err) 1820 } 1821 1822 err = os.Remove(testFilePath) 1823 assert.NoError(t, err) 1824 if user.Username == defaultUsername { 1825 user.Permissions = make(map[string][]string) 1826 user.Permissions["/"] = allPerms 1827 user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 1828 assert.NoError(t, err) 1829 err = os.RemoveAll(user.GetHomeDir()) 1830 assert.NoError(t, err) 1831 } 1832 } 1833 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 1834 assert.NoError(t, err) 1835 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 1836 assert.NoError(t, err) 1837 err = os.RemoveAll(localUser.GetHomeDir()) 1838 assert.NoError(t, err) 1839} 1840 1841func TestSymlink(t *testing.T) { 1842 u := getTestUser() 1843 localUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 1844 assert.NoError(t, err) 1845 sftpUser, _, err := httpdtest.AddUser(getTestSFTPUser(), http.StatusCreated) 1846 assert.NoError(t, err) 1847 testFilePath := filepath.Join(homeBasePath, testFileName) 1848 testFileSize := int64(65535) 1849 for _, user := range []dataprovider.User{localUser, sftpUser} { 1850 err = createTestFile(testFilePath, testFileSize) 1851 assert.NoError(t, err) 1852 client, err := getFTPClient(user, false, nil) 1853 if assert.NoError(t, err) { 1854 err = checkBasicFTP(client) 1855 assert.NoError(t, err) 1856 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 1857 assert.NoError(t, err) 1858 code, _, err := client.SendCustomCommand(fmt.Sprintf("SITE SYMLINK %v %v", testFileName, testFileName+".link")) 1859 assert.NoError(t, err) 1860 assert.Equal(t, ftp.StatusCommandOK, code) 1861 1862 if runtime.GOOS != osWindows { 1863 testDir := "adir" 1864 otherDir := "dir" 1865 err = client.MakeDir(otherDir) 1866 assert.NoError(t, err) 1867 err = client.MakeDir(path.Join(otherDir, testDir)) 1868 assert.NoError(t, err) 1869 code, response, err := client.SendCustomCommand(fmt.Sprintf("SITE CHMOD 0001 %v", otherDir)) 1870 assert.NoError(t, err) 1871 assert.Equal(t, ftp.StatusCommandOK, code) 1872 assert.Equal(t, "SITE CHMOD command successful", response) 1873 code, _, err = client.SendCustomCommand(fmt.Sprintf("SITE SYMLINK %v %v", testDir, path.Join(otherDir, testDir))) 1874 assert.NoError(t, err) 1875 assert.Equal(t, ftp.StatusFileUnavailable, code) 1876 1877 code, response, err = client.SendCustomCommand(fmt.Sprintf("SITE CHMOD 755 %v", otherDir)) 1878 assert.NoError(t, err) 1879 assert.Equal(t, ftp.StatusCommandOK, code) 1880 assert.Equal(t, "SITE CHMOD command successful", response) 1881 } 1882 err = client.Quit() 1883 assert.NoError(t, err) 1884 if user.Username == defaultUsername { 1885 err = os.RemoveAll(user.GetHomeDir()) 1886 assert.NoError(t, err) 1887 } 1888 } 1889 err = os.Remove(testFilePath) 1890 assert.NoError(t, err) 1891 } 1892 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 1893 assert.NoError(t, err) 1894 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 1895 assert.NoError(t, err) 1896 err = os.RemoveAll(localUser.GetHomeDir()) 1897 assert.NoError(t, err) 1898} 1899 1900func TestStat(t *testing.T) { 1901 u := getTestUser() 1902 u.Permissions["/subdir"] = []string{dataprovider.PermUpload} 1903 localUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 1904 assert.NoError(t, err) 1905 sftpUser, _, err := httpdtest.AddUser(getTestSFTPUser(), http.StatusCreated) 1906 assert.NoError(t, err) 1907 1908 for _, user := range []dataprovider.User{localUser, sftpUser} { 1909 client, err := getFTPClient(user, false, nil) 1910 if assert.NoError(t, err) { 1911 subDir := "subdir" 1912 testFilePath := filepath.Join(homeBasePath, testFileName) 1913 testFileSize := int64(65535) 1914 err = createTestFile(testFilePath, testFileSize) 1915 assert.NoError(t, err) 1916 err = client.MakeDir(subDir) 1917 assert.NoError(t, err) 1918 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 1919 assert.NoError(t, err) 1920 err = ftpUploadFile(testFilePath, path.Join("/", subDir, testFileName), testFileSize, client, 0) 1921 assert.Error(t, err) 1922 size, err := client.FileSize(testFileName) 1923 assert.NoError(t, err) 1924 assert.Equal(t, testFileSize, size) 1925 _, err = client.FileSize(path.Join("/", subDir, testFileName)) 1926 assert.Error(t, err) 1927 _, err = client.FileSize("missing file") 1928 assert.Error(t, err) 1929 err = client.Quit() 1930 assert.NoError(t, err) 1931 1932 err = os.Remove(testFilePath) 1933 assert.NoError(t, err) 1934 if user.Username == defaultUsername { 1935 err = os.RemoveAll(user.GetHomeDir()) 1936 assert.NoError(t, err) 1937 } 1938 } 1939 } 1940 1941 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 1942 assert.NoError(t, err) 1943 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 1944 assert.NoError(t, err) 1945 err = os.RemoveAll(localUser.GetHomeDir()) 1946 assert.NoError(t, err) 1947} 1948 1949func TestUploadOverwriteVfolder(t *testing.T) { 1950 u := getTestUser() 1951 vdir := "/vdir" 1952 mappedPath := filepath.Join(os.TempDir(), "vdir") 1953 folderName := filepath.Base(mappedPath) 1954 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 1955 BaseVirtualFolder: vfs.BaseVirtualFolder{ 1956 Name: folderName, 1957 MappedPath: mappedPath, 1958 }, 1959 VirtualPath: vdir, 1960 QuotaSize: -1, 1961 QuotaFiles: -1, 1962 }) 1963 err := os.MkdirAll(mappedPath, os.ModePerm) 1964 assert.NoError(t, err) 1965 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 1966 assert.NoError(t, err) 1967 client, err := getFTPClient(user, false, nil) 1968 if assert.NoError(t, err) { 1969 testFilePath := filepath.Join(homeBasePath, testFileName) 1970 testFileSize := int64(65535) 1971 err = createTestFile(testFilePath, testFileSize) 1972 assert.NoError(t, err) 1973 err = ftpUploadFile(testFilePath, path.Join(vdir, testFileName), testFileSize, client, 0) 1974 assert.NoError(t, err) 1975 folder, _, err := httpdtest.GetFolderByName(folderName, http.StatusOK) 1976 assert.NoError(t, err) 1977 assert.Equal(t, testFileSize, folder.UsedQuotaSize) 1978 assert.Equal(t, 1, folder.UsedQuotaFiles) 1979 err = ftpUploadFile(testFilePath, path.Join(vdir, testFileName), testFileSize, client, 0) 1980 assert.NoError(t, err) 1981 folder, _, err = httpdtest.GetFolderByName(folderName, http.StatusOK) 1982 assert.NoError(t, err) 1983 assert.Equal(t, testFileSize, folder.UsedQuotaSize) 1984 assert.Equal(t, 1, folder.UsedQuotaFiles) 1985 err = client.Quit() 1986 assert.NoError(t, err) 1987 err = os.Remove(testFilePath) 1988 assert.NoError(t, err) 1989 } 1990 _, err = httpdtest.RemoveUser(user, http.StatusOK) 1991 assert.NoError(t, err) 1992 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName}, http.StatusOK) 1993 assert.NoError(t, err) 1994 err = os.RemoveAll(user.GetHomeDir()) 1995 assert.NoError(t, err) 1996 err = os.RemoveAll(mappedPath) 1997 assert.NoError(t, err) 1998} 1999 2000func TestAllocateAvailable(t *testing.T) { 2001 u := getTestUser() 2002 mappedPath := filepath.Join(os.TempDir(), "vdir") 2003 folderName := filepath.Base(mappedPath) 2004 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 2005 BaseVirtualFolder: vfs.BaseVirtualFolder{ 2006 Name: folderName, 2007 MappedPath: mappedPath, 2008 }, 2009 VirtualPath: "/vdir", 2010 QuotaSize: 110, 2011 }) 2012 err := os.MkdirAll(mappedPath, os.ModePerm) 2013 assert.NoError(t, err) 2014 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 2015 assert.NoError(t, err) 2016 client, err := getFTPClient(user, false, nil) 2017 if assert.NoError(t, err) { 2018 code, response, err := client.SendCustomCommand("allo 2000000") 2019 assert.NoError(t, err) 2020 assert.Equal(t, ftp.StatusCommandOK, code) 2021 assert.Equal(t, "Done !", response) 2022 2023 code, response, err = client.SendCustomCommand("AVBL /vdir") 2024 assert.NoError(t, err) 2025 assert.Equal(t, ftp.StatusFile, code) 2026 assert.Equal(t, "110", response) 2027 2028 code, _, err = client.SendCustomCommand("AVBL") 2029 assert.NoError(t, err) 2030 assert.Equal(t, ftp.StatusFile, code) 2031 2032 err = client.Quit() 2033 assert.NoError(t, err) 2034 } 2035 user.QuotaSize = 100 2036 user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 2037 assert.NoError(t, err) 2038 client, err = getFTPClient(user, false, nil) 2039 if assert.NoError(t, err) { 2040 testFilePath := filepath.Join(homeBasePath, testFileName) 2041 testFileSize := user.QuotaSize - 1 2042 err = createTestFile(testFilePath, testFileSize) 2043 assert.NoError(t, err) 2044 code, response, err := client.SendCustomCommand("allo 99") 2045 assert.NoError(t, err) 2046 assert.Equal(t, ftp.StatusCommandOK, code) 2047 assert.Equal(t, "Done !", response) 2048 code, response, err = client.SendCustomCommand("allo 100") 2049 assert.NoError(t, err) 2050 assert.Equal(t, ftp.StatusCommandOK, code) 2051 assert.Equal(t, "Done !", response) 2052 code, response, err = client.SendCustomCommand("allo 150") 2053 assert.NoError(t, err) 2054 assert.Equal(t, ftp.StatusFileUnavailable, code) 2055 assert.Contains(t, response, ftpserver.ErrStorageExceeded.Error()) 2056 2057 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 2058 assert.NoError(t, err) 2059 2060 code, response, err = client.SendCustomCommand("AVBL") 2061 assert.NoError(t, err) 2062 assert.Equal(t, ftp.StatusFile, code) 2063 assert.Equal(t, "1", response) 2064 2065 // we still have space in vdir 2066 code, response, err = client.SendCustomCommand("allo 50") 2067 assert.NoError(t, err) 2068 assert.Equal(t, ftp.StatusCommandOK, code) 2069 assert.Equal(t, "Done !", response) 2070 err = ftpUploadFile(testFilePath, path.Join("/vdir", testFileName), testFileSize, client, 0) 2071 assert.NoError(t, err) 2072 code, response, err = client.SendCustomCommand("allo 50") 2073 assert.NoError(t, err) 2074 assert.Equal(t, ftp.StatusFileUnavailable, code) 2075 assert.Contains(t, response, ftpserver.ErrStorageExceeded.Error()) 2076 2077 err = client.Quit() 2078 assert.NoError(t, err) 2079 err = os.Remove(testFilePath) 2080 assert.NoError(t, err) 2081 } 2082 2083 user.Filters.MaxUploadFileSize = 100 2084 user.QuotaSize = 0 2085 user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 2086 assert.NoError(t, err) 2087 client, err = getFTPClient(user, false, nil) 2088 if assert.NoError(t, err) { 2089 code, response, err := client.SendCustomCommand("allo 99") 2090 assert.NoError(t, err) 2091 assert.Equal(t, ftp.StatusCommandOK, code) 2092 assert.Equal(t, "Done !", response) 2093 code, response, err = client.SendCustomCommand("allo 150") 2094 assert.NoError(t, err) 2095 assert.Equal(t, ftp.StatusFileUnavailable, code) 2096 assert.Contains(t, response, ftpserver.ErrStorageExceeded.Error()) 2097 2098 code, response, err = client.SendCustomCommand("AVBL") 2099 assert.NoError(t, err) 2100 assert.Equal(t, ftp.StatusFile, code) 2101 assert.Equal(t, "100", response) 2102 2103 err = client.Quit() 2104 assert.NoError(t, err) 2105 } 2106 2107 user.QuotaSize = 50 2108 user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 2109 assert.NoError(t, err) 2110 client, err = getFTPClient(user, false, nil) 2111 if assert.NoError(t, err) { 2112 code, response, err := client.SendCustomCommand("AVBL") 2113 assert.NoError(t, err) 2114 assert.Equal(t, ftp.StatusFile, code) 2115 assert.Equal(t, "0", response) 2116 } 2117 2118 user.QuotaSize = 1000 2119 user.Filters.MaxUploadFileSize = 1 2120 user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 2121 assert.NoError(t, err) 2122 client, err = getFTPClient(user, false, nil) 2123 if assert.NoError(t, err) { 2124 code, response, err := client.SendCustomCommand("AVBL") 2125 assert.NoError(t, err) 2126 assert.Equal(t, ftp.StatusFile, code) 2127 assert.Equal(t, "1", response) 2128 } 2129 2130 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2131 assert.NoError(t, err) 2132 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName}, http.StatusOK) 2133 assert.NoError(t, err) 2134 err = os.RemoveAll(user.GetHomeDir()) 2135 assert.NoError(t, err) 2136 err = os.RemoveAll(mappedPath) 2137 assert.NoError(t, err) 2138} 2139 2140func TestAvailableSFTPFs(t *testing.T) { 2141 u := getTestUser() 2142 localUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 2143 assert.NoError(t, err) 2144 sftpUser, _, err := httpdtest.AddUser(getTestSFTPUser(), http.StatusCreated) 2145 assert.NoError(t, err) 2146 client, err := getFTPClient(sftpUser, false, nil) 2147 if assert.NoError(t, err) { 2148 code, response, err := client.SendCustomCommand("AVBL /") 2149 assert.NoError(t, err) 2150 assert.Equal(t, ftp.StatusFile, code) 2151 avblSize, err := strconv.ParseInt(response, 10, 64) 2152 assert.NoError(t, err) 2153 assert.Greater(t, avblSize, int64(0)) 2154 2155 err = client.Quit() 2156 assert.NoError(t, err) 2157 } 2158 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 2159 assert.NoError(t, err) 2160 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 2161 assert.NoError(t, err) 2162 err = os.RemoveAll(localUser.GetHomeDir()) 2163 assert.NoError(t, err) 2164} 2165 2166func TestChtimes(t *testing.T) { 2167 u := getTestUser() 2168 localUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 2169 assert.NoError(t, err) 2170 sftpUser, _, err := httpdtest.AddUser(getTestSFTPUser(), http.StatusCreated) 2171 assert.NoError(t, err) 2172 2173 for _, user := range []dataprovider.User{localUser, sftpUser} { 2174 client, err := getFTPClient(user, false, nil) 2175 if assert.NoError(t, err) { 2176 testFilePath := filepath.Join(homeBasePath, testFileName) 2177 testFileSize := int64(65535) 2178 err = createTestFile(testFilePath, testFileSize) 2179 assert.NoError(t, err) 2180 err = checkBasicFTP(client) 2181 assert.NoError(t, err) 2182 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 2183 assert.NoError(t, err) 2184 2185 mtime := time.Now().Format("20060102150405") 2186 code, response, err := client.SendCustomCommand(fmt.Sprintf("MFMT %v %v", mtime, testFileName)) 2187 assert.NoError(t, err) 2188 assert.Equal(t, ftp.StatusFile, code) 2189 assert.Equal(t, fmt.Sprintf("Modify=%v; %v", mtime, testFileName), response) 2190 err = client.Quit() 2191 assert.NoError(t, err) 2192 2193 err = os.Remove(testFilePath) 2194 assert.NoError(t, err) 2195 if user.Username == defaultUsername { 2196 err = os.RemoveAll(user.GetHomeDir()) 2197 assert.NoError(t, err) 2198 } 2199 } 2200 } 2201 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 2202 assert.NoError(t, err) 2203 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 2204 assert.NoError(t, err) 2205 err = os.RemoveAll(localUser.GetHomeDir()) 2206 assert.NoError(t, err) 2207} 2208 2209func TestChown(t *testing.T) { 2210 if runtime.GOOS == osWindows { 2211 t.Skip("chown is not supported on Windows") 2212 } 2213 user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated) 2214 assert.NoError(t, err) 2215 client, err := getFTPClient(user, true, nil) 2216 if assert.NoError(t, err) { 2217 testFilePath := filepath.Join(homeBasePath, testFileName) 2218 testFileSize := int64(131072) 2219 err = createTestFile(testFilePath, testFileSize) 2220 assert.NoError(t, err) 2221 err = checkBasicFTP(client) 2222 assert.NoError(t, err) 2223 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 2224 assert.NoError(t, err) 2225 code, response, err := client.SendCustomCommand(fmt.Sprintf("SITE CHOWN 1000:1000 %v", testFileName)) 2226 assert.NoError(t, err) 2227 assert.Equal(t, ftp.StatusFileUnavailable, code) 2228 assert.Equal(t, "Couldn't chown: operation unsupported", response) 2229 err = client.Quit() 2230 assert.NoError(t, err) 2231 2232 err = os.Remove(testFilePath) 2233 assert.NoError(t, err) 2234 } 2235 2236 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2237 assert.NoError(t, err) 2238 err = os.RemoveAll(user.GetHomeDir()) 2239 assert.NoError(t, err) 2240} 2241 2242func TestChmod(t *testing.T) { 2243 if runtime.GOOS == osWindows { 2244 t.Skip("chmod is partially supported on Windows") 2245 } 2246 u := getTestUser() 2247 localUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 2248 assert.NoError(t, err) 2249 sftpUser, _, err := httpdtest.AddUser(getTestSFTPUser(), http.StatusCreated) 2250 assert.NoError(t, err) 2251 for _, user := range []dataprovider.User{localUser, sftpUser} { 2252 client, err := getFTPClient(user, true, nil) 2253 if assert.NoError(t, err) { 2254 testFilePath := filepath.Join(homeBasePath, testFileName) 2255 testFileSize := int64(131072) 2256 err = createTestFile(testFilePath, testFileSize) 2257 assert.NoError(t, err) 2258 err = checkBasicFTP(client) 2259 assert.NoError(t, err) 2260 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 2261 assert.NoError(t, err) 2262 2263 code, response, err := client.SendCustomCommand(fmt.Sprintf("SITE CHMOD 600 %v", testFileName)) 2264 assert.NoError(t, err) 2265 assert.Equal(t, ftp.StatusCommandOK, code) 2266 assert.Equal(t, "SITE CHMOD command successful", response) 2267 2268 fi, err := os.Stat(filepath.Join(user.HomeDir, testFileName)) 2269 if assert.NoError(t, err) { 2270 assert.Equal(t, os.FileMode(0600), fi.Mode().Perm()) 2271 } 2272 err = client.Quit() 2273 assert.NoError(t, err) 2274 2275 err = os.Remove(testFilePath) 2276 assert.NoError(t, err) 2277 if user.Username == defaultUsername { 2278 err = os.RemoveAll(user.GetHomeDir()) 2279 assert.NoError(t, err) 2280 } 2281 } 2282 } 2283 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 2284 assert.NoError(t, err) 2285 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 2286 assert.NoError(t, err) 2287 err = os.RemoveAll(localUser.GetHomeDir()) 2288 assert.NoError(t, err) 2289} 2290 2291func TestCombineDisabled(t *testing.T) { 2292 u := getTestUser() 2293 localUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 2294 assert.NoError(t, err) 2295 sftpUser, _, err := httpdtest.AddUser(getTestSFTPUser(), http.StatusCreated) 2296 assert.NoError(t, err) 2297 for _, user := range []dataprovider.User{localUser, sftpUser} { 2298 client, err := getFTPClient(user, true, nil) 2299 if assert.NoError(t, err) { 2300 err = checkBasicFTP(client) 2301 assert.NoError(t, err) 2302 2303 code, response, err := client.SendCustomCommand("COMB file file.1 file.2") 2304 assert.NoError(t, err) 2305 assert.Equal(t, ftp.StatusNotImplemented, code) 2306 assert.Equal(t, "COMB support is disabled", response) 2307 2308 err = client.Quit() 2309 assert.NoError(t, err) 2310 } 2311 } 2312 2313 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 2314 assert.NoError(t, err) 2315 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 2316 assert.NoError(t, err) 2317 err = os.RemoveAll(localUser.GetHomeDir()) 2318 assert.NoError(t, err) 2319} 2320 2321func TestActiveModeDisabled(t *testing.T) { 2322 u := getTestUser() 2323 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 2324 assert.NoError(t, err) 2325 client, err := getFTPClientImplicitTLS(user) 2326 if assert.NoError(t, err) { 2327 err = checkBasicFTP(client) 2328 assert.NoError(t, err) 2329 code, response, err := client.SendCustomCommand("PORT 10,2,0,2,4,31") 2330 assert.NoError(t, err) 2331 assert.Equal(t, ftp.StatusNotAvailable, code) 2332 assert.Equal(t, "PORT command is disabled", response) 2333 2334 code, response, err = client.SendCustomCommand("EPRT |1|132.235.1.2|6275|") 2335 assert.NoError(t, err) 2336 assert.Equal(t, ftp.StatusNotAvailable, code) 2337 assert.Equal(t, "EPRT command is disabled", response) 2338 2339 err = client.Quit() 2340 assert.NoError(t, err) 2341 } 2342 2343 client, err = getFTPClient(user, false, nil) 2344 if assert.NoError(t, err) { 2345 code, response, err := client.SendCustomCommand("PORT 10,2,0,2,4,31") 2346 assert.NoError(t, err) 2347 assert.Equal(t, ftp.StatusBadArguments, code) 2348 assert.Equal(t, "Your request does not meet the configured security requirements", response) 2349 2350 code, response, err = client.SendCustomCommand("EPRT |1|132.235.1.2|6275|") 2351 assert.NoError(t, err) 2352 assert.Equal(t, ftp.StatusBadArguments, code) 2353 assert.Equal(t, "Your request does not meet the configured security requirements", response) 2354 2355 err = client.Quit() 2356 assert.NoError(t, err) 2357 } 2358 2359 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2360 assert.NoError(t, err) 2361 err = os.RemoveAll(user.GetHomeDir()) 2362 assert.NoError(t, err) 2363} 2364 2365func TestSITEDisabled(t *testing.T) { 2366 u := getTestUser() 2367 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 2368 assert.NoError(t, err) 2369 client, err := getFTPClientImplicitTLS(user) 2370 if assert.NoError(t, err) { 2371 err = checkBasicFTP(client) 2372 assert.NoError(t, err) 2373 2374 code, response, err := client.SendCustomCommand("SITE CHMOD 600 afile.txt") 2375 assert.NoError(t, err) 2376 assert.Equal(t, ftp.StatusBadCommand, code) 2377 assert.Equal(t, "SITE support is disabled", response) 2378 2379 err = client.Quit() 2380 assert.NoError(t, err) 2381 } 2382 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2383 assert.NoError(t, err) 2384 err = os.RemoveAll(user.GetHomeDir()) 2385 assert.NoError(t, err) 2386} 2387 2388func TestHASH(t *testing.T) { 2389 u := getTestUser() 2390 localUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 2391 assert.NoError(t, err) 2392 sftpUser, _, err := httpdtest.AddUser(getTestSFTPUser(), http.StatusCreated) 2393 assert.NoError(t, err) 2394 u = getTestUserWithCryptFs() 2395 u.Username += "_crypt" 2396 cryptUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 2397 assert.NoError(t, err) 2398 for _, user := range []dataprovider.User{localUser, sftpUser, cryptUser} { 2399 client, err := getFTPClientImplicitTLS(user) 2400 if assert.NoError(t, err) { 2401 testFilePath := filepath.Join(homeBasePath, testFileName) 2402 testFileSize := int64(131072) 2403 err = createTestFile(testFilePath, testFileSize) 2404 assert.NoError(t, err) 2405 err = checkBasicFTP(client) 2406 assert.NoError(t, err) 2407 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 2408 assert.NoError(t, err) 2409 2410 h := sha256.New() 2411 f, err := os.Open(testFilePath) 2412 assert.NoError(t, err) 2413 _, err = io.Copy(h, f) 2414 assert.NoError(t, err) 2415 hash := hex.EncodeToString(h.Sum(nil)) 2416 err = f.Close() 2417 assert.NoError(t, err) 2418 2419 code, response, err := client.SendCustomCommand(fmt.Sprintf("XSHA256 %v", testFileName)) 2420 assert.NoError(t, err) 2421 assert.Equal(t, ftp.StatusRequestedFileActionOK, code) 2422 assert.Contains(t, response, hash) 2423 2424 code, response, err = client.SendCustomCommand(fmt.Sprintf("HASH %v", testFileName)) 2425 assert.NoError(t, err) 2426 assert.Equal(t, ftp.StatusFile, code) 2427 assert.Contains(t, response, hash) 2428 2429 err = client.Quit() 2430 assert.NoError(t, err) 2431 2432 err = os.Remove(testFilePath) 2433 assert.NoError(t, err) 2434 if user.Username == defaultUsername { 2435 err = os.RemoveAll(user.GetHomeDir()) 2436 assert.NoError(t, err) 2437 } 2438 } 2439 } 2440 2441 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 2442 assert.NoError(t, err) 2443 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 2444 assert.NoError(t, err) 2445 err = os.RemoveAll(localUser.GetHomeDir()) 2446 assert.NoError(t, err) 2447 _, err = httpdtest.RemoveUser(cryptUser, http.StatusOK) 2448 assert.NoError(t, err) 2449 err = os.RemoveAll(cryptUser.GetHomeDir()) 2450 assert.NoError(t, err) 2451} 2452 2453func TestCombine(t *testing.T) { 2454 u := getTestUser() 2455 localUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 2456 assert.NoError(t, err) 2457 sftpUser, _, err := httpdtest.AddUser(getTestSFTPUser(), http.StatusCreated) 2458 assert.NoError(t, err) 2459 for _, user := range []dataprovider.User{localUser, sftpUser} { 2460 client, err := getFTPClientImplicitTLS(user) 2461 if assert.NoError(t, err) { 2462 testFilePath := filepath.Join(homeBasePath, testFileName) 2463 testFileSize := int64(131072) 2464 err = createTestFile(testFilePath, testFileSize) 2465 assert.NoError(t, err) 2466 err = checkBasicFTP(client) 2467 assert.NoError(t, err) 2468 err = ftpUploadFile(testFilePath, testFileName+".1", testFileSize, client, 0) 2469 assert.NoError(t, err) 2470 err = ftpUploadFile(testFilePath, testFileName+".2", testFileSize, client, 0) 2471 assert.NoError(t, err) 2472 2473 code, response, err := client.SendCustomCommand(fmt.Sprintf("COMB %v %v %v", testFileName, testFileName+".1", testFileName+".2")) 2474 assert.NoError(t, err) 2475 if user.Username == defaultUsername { 2476 assert.Equal(t, ftp.StatusRequestedFileActionOK, code) 2477 assert.Equal(t, "COMB succeeded!", response) 2478 } else { 2479 assert.Equal(t, ftp.StatusFileUnavailable, code) 2480 assert.Contains(t, response, "COMB is not supported for this filesystem") 2481 } 2482 2483 err = client.Quit() 2484 assert.NoError(t, err) 2485 2486 err = os.Remove(testFilePath) 2487 assert.NoError(t, err) 2488 if user.Username == defaultUsername { 2489 err = os.RemoveAll(user.GetHomeDir()) 2490 assert.NoError(t, err) 2491 } 2492 } 2493 } 2494 2495 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 2496 assert.NoError(t, err) 2497 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 2498 assert.NoError(t, err) 2499 err = os.RemoveAll(localUser.GetHomeDir()) 2500 assert.NoError(t, err) 2501} 2502 2503func TestClientCertificateAuthRevokedCert(t *testing.T) { 2504 u := getTestUser() 2505 u.Username = tlsClient2Username 2506 u.Filters.TLSUsername = sdk.TLSUsernameCN 2507 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 2508 assert.NoError(t, err) 2509 tlsConfig := &tls.Config{ 2510 ServerName: "localhost", 2511 InsecureSkipVerify: true, // use this for tests only 2512 MinVersion: tls.VersionTLS12, 2513 } 2514 tlsCert, err := tls.X509KeyPair([]byte(client2Crt), []byte(client2Key)) 2515 assert.NoError(t, err) 2516 tlsConfig.Certificates = append(tlsConfig.Certificates, tlsCert) 2517 _, err = getFTPClient(user, true, tlsConfig) 2518 if assert.Error(t, err) { 2519 assert.Contains(t, err.Error(), "bad certificate") 2520 } 2521 2522 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2523 assert.NoError(t, err) 2524 err = os.RemoveAll(user.GetHomeDir()) 2525 assert.NoError(t, err) 2526} 2527 2528func TestClientCertificateAuth(t *testing.T) { 2529 u := getTestUser() 2530 u.Username = tlsClient1Username 2531 u.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodPassword} 2532 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 2533 assert.NoError(t, err) 2534 tlsConfig := &tls.Config{ 2535 ServerName: "localhost", 2536 InsecureSkipVerify: true, // use this for tests only 2537 MinVersion: tls.VersionTLS12, 2538 } 2539 tlsCert, err := tls.X509KeyPair([]byte(client1Crt), []byte(client1Key)) 2540 assert.NoError(t, err) 2541 tlsConfig.Certificates = append(tlsConfig.Certificates, tlsCert) 2542 // TLS username is not enabled, mutual TLS should fail 2543 _, err = getFTPClient(user, true, tlsConfig) 2544 if assert.Error(t, err) { 2545 assert.Contains(t, err.Error(), "login method password is not allowed") 2546 } 2547 2548 user.Filters.TLSUsername = sdk.TLSUsernameCN 2549 user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 2550 assert.NoError(t, err) 2551 client, err := getFTPClient(user, true, tlsConfig) 2552 if assert.NoError(t, err) { 2553 err = checkBasicFTP(client) 2554 assert.NoError(t, err) 2555 err = client.Quit() 2556 assert.NoError(t, err) 2557 } 2558 2559 // now use a valid certificate with a CN different from username 2560 u = getTestUser() 2561 u.Username = tlsClient2Username 2562 u.Filters.TLSUsername = sdk.TLSUsernameCN 2563 u.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodPassword} 2564 user2, _, err := httpdtest.AddUser(u, http.StatusCreated) 2565 assert.NoError(t, err) 2566 _, err = getFTPClient(user2, true, tlsConfig) 2567 if assert.Error(t, err) { 2568 assert.Contains(t, err.Error(), "does not match username") 2569 } 2570 2571 // now disable certificate authentication 2572 user.Filters.DeniedLoginMethods = append(user.Filters.DeniedLoginMethods, dataprovider.LoginMethodTLSCertificate, 2573 dataprovider.LoginMethodTLSCertificateAndPwd) 2574 user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 2575 assert.NoError(t, err) 2576 _, err = getFTPClient(user, true, tlsConfig) 2577 if assert.Error(t, err) { 2578 assert.Contains(t, err.Error(), "login method TLSCertificate+password is not allowed") 2579 } 2580 2581 // disable FTP protocol 2582 user.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodPassword} 2583 user.Filters.DeniedProtocols = append(user.Filters.DeniedProtocols, common.ProtocolFTP) 2584 user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "") 2585 assert.NoError(t, err) 2586 _, err = getFTPClient(user, true, tlsConfig) 2587 if assert.Error(t, err) { 2588 assert.Contains(t, err.Error(), "protocol FTP is not allowed") 2589 } 2590 2591 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2592 assert.NoError(t, err) 2593 err = os.RemoveAll(user.GetHomeDir()) 2594 assert.NoError(t, err) 2595 2596 _, err = httpdtest.RemoveUser(user2, http.StatusOK) 2597 assert.NoError(t, err) 2598 err = os.RemoveAll(user2.GetHomeDir()) 2599 assert.NoError(t, err) 2600 2601 _, err = getFTPClient(user, true, tlsConfig) 2602 assert.Error(t, err) 2603} 2604 2605func TestClientCertificateAndPwdAuth(t *testing.T) { 2606 u := getTestUser() 2607 u.Username = tlsClient1Username 2608 u.Filters.TLSUsername = sdk.TLSUsernameCN 2609 u.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodPassword, dataprovider.LoginMethodTLSCertificate} 2610 user, _, err := httpdtest.AddUser(u, http.StatusCreated) 2611 assert.NoError(t, err) 2612 tlsConfig := &tls.Config{ 2613 ServerName: "localhost", 2614 InsecureSkipVerify: true, // use this for tests only 2615 MinVersion: tls.VersionTLS12, 2616 } 2617 tlsCert, err := tls.X509KeyPair([]byte(client1Crt), []byte(client1Key)) 2618 assert.NoError(t, err) 2619 tlsConfig.Certificates = append(tlsConfig.Certificates, tlsCert) 2620 client, err := getFTPClient(user, true, tlsConfig) 2621 if assert.NoError(t, err) { 2622 err = checkBasicFTP(client) 2623 assert.NoError(t, err) 2624 err = client.Quit() 2625 assert.NoError(t, err) 2626 } 2627 2628 _, err = getFTPClient(user, true, nil) 2629 if assert.Error(t, err) { 2630 assert.Contains(t, err.Error(), "login method password is not allowed") 2631 } 2632 user.Password = defaultPassword + "1" 2633 _, err = getFTPClient(user, true, tlsConfig) 2634 if assert.Error(t, err) { 2635 assert.Contains(t, err.Error(), "invalid credentials") 2636 } 2637 2638 tlsCert, err = tls.X509KeyPair([]byte(client2Crt), []byte(client2Key)) 2639 assert.NoError(t, err) 2640 tlsConfig.Certificates = []tls.Certificate{tlsCert} 2641 _, err = getFTPClient(user, true, tlsConfig) 2642 if assert.Error(t, err) { 2643 assert.Contains(t, err.Error(), "bad certificate") 2644 } 2645 2646 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2647 assert.NoError(t, err) 2648 err = os.RemoveAll(user.GetHomeDir()) 2649 assert.NoError(t, err) 2650} 2651 2652func TestExternatAuthWithClientCert(t *testing.T) { 2653 if runtime.GOOS == osWindows { 2654 t.Skip("this test is not available on Windows") 2655 } 2656 u := getTestUser() 2657 u.Username = tlsClient1Username 2658 u.Filters.DeniedLoginMethods = append(u.Filters.DeniedLoginMethods, dataprovider.LoginMethodPassword) 2659 u.Filters.TLSUsername = sdk.TLSUsernameCN 2660 err := dataprovider.Close() 2661 assert.NoError(t, err) 2662 err = config.LoadConfig(configDir, "") 2663 assert.NoError(t, err) 2664 providerConf := config.GetProviderConf() 2665 err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm) 2666 assert.NoError(t, err) 2667 providerConf.ExternalAuthHook = extAuthPath 2668 providerConf.ExternalAuthScope = 8 2669 err = dataprovider.Initialize(providerConf, configDir, true) 2670 assert.NoError(t, err) 2671 2672 // external auth not called, auth scope is 8 2673 _, err = getFTPClient(u, true, nil) 2674 assert.Error(t, err) 2675 _, _, err = httpdtest.GetUserByUsername(u.Username, http.StatusNotFound) 2676 assert.NoError(t, err) 2677 2678 tlsConfig := &tls.Config{ 2679 ServerName: "localhost", 2680 InsecureSkipVerify: true, // use this for tests only 2681 MinVersion: tls.VersionTLS12, 2682 } 2683 tlsCert, err := tls.X509KeyPair([]byte(client1Crt), []byte(client1Key)) 2684 assert.NoError(t, err) 2685 tlsConfig.Certificates = append(tlsConfig.Certificates, tlsCert) 2686 client, err := getFTPClient(u, true, tlsConfig) 2687 if assert.NoError(t, err) { 2688 err = checkBasicFTP(client) 2689 assert.NoError(t, err) 2690 err := client.Quit() 2691 assert.NoError(t, err) 2692 } 2693 2694 user, _, err := httpdtest.GetUserByUsername(u.Username, http.StatusOK) 2695 assert.NoError(t, err) 2696 assert.Equal(t, u.Username, user.Username) 2697 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2698 assert.NoError(t, err) 2699 err = os.RemoveAll(user.GetHomeDir()) 2700 assert.NoError(t, err) 2701 2702 u.Username = tlsClient2Username 2703 _, err = getFTPClient(u, true, tlsConfig) 2704 if assert.Error(t, err) { 2705 assert.Contains(t, err.Error(), "invalid credentials") 2706 } 2707 2708 err = dataprovider.Close() 2709 assert.NoError(t, err) 2710 err = config.LoadConfig(configDir, "") 2711 assert.NoError(t, err) 2712 providerConf = config.GetProviderConf() 2713 err = dataprovider.Initialize(providerConf, configDir, true) 2714 assert.NoError(t, err) 2715 err = os.Remove(extAuthPath) 2716 assert.NoError(t, err) 2717} 2718 2719func TestPreLoginHookWithClientCert(t *testing.T) { 2720 if runtime.GOOS == osWindows { 2721 t.Skip("this test is not available on Windows") 2722 } 2723 u := getTestUser() 2724 u.Username = tlsClient1Username 2725 u.Filters.DeniedLoginMethods = append(u.Filters.DeniedLoginMethods, dataprovider.LoginMethodPassword) 2726 u.Filters.TLSUsername = sdk.TLSUsernameCN 2727 err := dataprovider.Close() 2728 assert.NoError(t, err) 2729 err = config.LoadConfig(configDir, "") 2730 assert.NoError(t, err) 2731 providerConf := config.GetProviderConf() 2732 err = os.WriteFile(preLoginPath, getPreLoginScriptContent(u, false), os.ModePerm) 2733 assert.NoError(t, err) 2734 providerConf.PreLoginHook = preLoginPath 2735 err = dataprovider.Initialize(providerConf, configDir, true) 2736 assert.NoError(t, err) 2737 _, _, err = httpdtest.GetUserByUsername(tlsClient1Username, http.StatusNotFound) 2738 assert.NoError(t, err) 2739 tlsConfig := &tls.Config{ 2740 ServerName: "localhost", 2741 InsecureSkipVerify: true, // use this for tests only 2742 MinVersion: tls.VersionTLS12, 2743 } 2744 tlsCert, err := tls.X509KeyPair([]byte(client1Crt), []byte(client1Key)) 2745 assert.NoError(t, err) 2746 tlsConfig.Certificates = append(tlsConfig.Certificates, tlsCert) 2747 client, err := getFTPClient(u, true, tlsConfig) 2748 if assert.NoError(t, err) { 2749 err = checkBasicFTP(client) 2750 assert.NoError(t, err) 2751 err := client.Quit() 2752 assert.NoError(t, err) 2753 } 2754 2755 user, _, err := httpdtest.GetUserByUsername(tlsClient1Username, http.StatusOK) 2756 assert.NoError(t, err) 2757 2758 // test login with an existing user 2759 client, err = getFTPClient(user, true, tlsConfig) 2760 if assert.NoError(t, err) { 2761 err = checkBasicFTP(client) 2762 assert.NoError(t, err) 2763 err := client.Quit() 2764 assert.NoError(t, err) 2765 } 2766 2767 u.Username = tlsClient2Username 2768 err = os.WriteFile(preLoginPath, getPreLoginScriptContent(u, false), os.ModePerm) 2769 assert.NoError(t, err) 2770 _, err = getFTPClient(u, true, tlsConfig) 2771 if assert.Error(t, err) { 2772 assert.Contains(t, err.Error(), "does not match username") 2773 } 2774 2775 user2, _, err := httpdtest.GetUserByUsername(tlsClient2Username, http.StatusOK) 2776 assert.NoError(t, err) 2777 _, err = httpdtest.RemoveUser(user2, http.StatusOK) 2778 assert.NoError(t, err) 2779 err = os.RemoveAll(user2.GetHomeDir()) 2780 assert.NoError(t, err) 2781 2782 _, err = httpdtest.RemoveUser(user, http.StatusOK) 2783 assert.NoError(t, err) 2784 err = os.RemoveAll(user.GetHomeDir()) 2785 assert.NoError(t, err) 2786 err = dataprovider.Close() 2787 assert.NoError(t, err) 2788 err = config.LoadConfig(configDir, "") 2789 assert.NoError(t, err) 2790 providerConf = config.GetProviderConf() 2791 err = dataprovider.Initialize(providerConf, configDir, true) 2792 assert.NoError(t, err) 2793 err = os.Remove(preLoginPath) 2794 assert.NoError(t, err) 2795} 2796 2797func TestNestedVirtualFolders(t *testing.T) { 2798 u := getTestUser() 2799 localUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 2800 assert.NoError(t, err) 2801 u = getTestSFTPUser() 2802 mappedPathCrypt := filepath.Join(os.TempDir(), "crypt") 2803 folderNameCrypt := filepath.Base(mappedPathCrypt) 2804 vdirCryptPath := "/vdir/crypt" 2805 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 2806 BaseVirtualFolder: vfs.BaseVirtualFolder{ 2807 Name: folderNameCrypt, 2808 FsConfig: vfs.Filesystem{ 2809 Provider: sdk.CryptedFilesystemProvider, 2810 CryptConfig: vfs.CryptFsConfig{ 2811 CryptFsConfig: sdk.CryptFsConfig{ 2812 Passphrase: kms.NewPlainSecret(defaultPassword), 2813 }, 2814 }, 2815 }, 2816 MappedPath: mappedPathCrypt, 2817 }, 2818 VirtualPath: vdirCryptPath, 2819 }) 2820 mappedPath := filepath.Join(os.TempDir(), "local") 2821 folderName := filepath.Base(mappedPath) 2822 vdirPath := "/vdir/local" 2823 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 2824 BaseVirtualFolder: vfs.BaseVirtualFolder{ 2825 Name: folderName, 2826 MappedPath: mappedPath, 2827 }, 2828 VirtualPath: vdirPath, 2829 }) 2830 mappedPathNested := filepath.Join(os.TempDir(), "nested") 2831 folderNameNested := filepath.Base(mappedPathNested) 2832 vdirNestedPath := "/vdir/crypt/nested" 2833 u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{ 2834 BaseVirtualFolder: vfs.BaseVirtualFolder{ 2835 Name: folderNameNested, 2836 MappedPath: mappedPathNested, 2837 }, 2838 VirtualPath: vdirNestedPath, 2839 QuotaFiles: -1, 2840 QuotaSize: -1, 2841 }) 2842 sftpUser, _, err := httpdtest.AddUser(u, http.StatusCreated) 2843 assert.NoError(t, err) 2844 client, err := getFTPClient(sftpUser, false, nil) 2845 if assert.NoError(t, err) { 2846 err = checkBasicFTP(client) 2847 assert.NoError(t, err) 2848 testFilePath := filepath.Join(homeBasePath, testFileName) 2849 testFileSize := int64(65535) 2850 err = createTestFile(testFilePath, testFileSize) 2851 assert.NoError(t, err) 2852 localDownloadPath := filepath.Join(homeBasePath, testDLFileName) 2853 2854 err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0) 2855 assert.NoError(t, err) 2856 err = ftpDownloadFile(testFileName, localDownloadPath, testFileSize, client, 0) 2857 assert.NoError(t, err) 2858 err = ftpUploadFile(testFilePath, path.Join("/vdir", testFileName), testFileSize, client, 0) 2859 assert.NoError(t, err) 2860 err = ftpDownloadFile(path.Join("/vdir", testFileName), localDownloadPath, testFileSize, client, 0) 2861 assert.NoError(t, err) 2862 err = ftpUploadFile(testFilePath, path.Join(vdirPath, testFileName), testFileSize, client, 0) 2863 assert.NoError(t, err) 2864 err = ftpDownloadFile(path.Join(vdirPath, testFileName), localDownloadPath, testFileSize, client, 0) 2865 assert.NoError(t, err) 2866 err = ftpUploadFile(testFilePath, path.Join(vdirCryptPath, testFileName), testFileSize, client, 0) 2867 assert.NoError(t, err) 2868 err = ftpDownloadFile(path.Join(vdirCryptPath, testFileName), localDownloadPath, testFileSize, client, 0) 2869 assert.NoError(t, err) 2870 err = ftpUploadFile(testFilePath, path.Join(vdirNestedPath, testFileName), testFileSize, client, 0) 2871 assert.NoError(t, err) 2872 err = ftpDownloadFile(path.Join(vdirNestedPath, testFileName), localDownloadPath, testFileSize, client, 0) 2873 assert.NoError(t, err) 2874 2875 err = client.Quit() 2876 assert.NoError(t, err) 2877 err = os.Remove(testFilePath) 2878 assert.NoError(t, err) 2879 err = os.Remove(localDownloadPath) 2880 assert.NoError(t, err) 2881 } 2882 2883 _, err = httpdtest.RemoveUser(sftpUser, http.StatusOK) 2884 assert.NoError(t, err) 2885 _, err = httpdtest.RemoveUser(localUser, http.StatusOK) 2886 assert.NoError(t, err) 2887 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderNameCrypt}, http.StatusOK) 2888 assert.NoError(t, err) 2889 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName}, http.StatusOK) 2890 assert.NoError(t, err) 2891 _, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderNameNested}, http.StatusOK) 2892 assert.NoError(t, err) 2893 err = os.RemoveAll(mappedPathCrypt) 2894 assert.NoError(t, err) 2895 err = os.RemoveAll(mappedPath) 2896 assert.NoError(t, err) 2897 err = os.RemoveAll(mappedPathNested) 2898 assert.NoError(t, err) 2899 err = os.RemoveAll(localUser.GetHomeDir()) 2900 assert.NoError(t, err) 2901 assert.Eventually(t, func() bool { return len(common.Connections.GetStats()) == 0 }, 1*time.Second, 50*time.Millisecond) 2902 assert.Eventually(t, func() bool { return common.Connections.GetClientConnections() == 0 }, 1000*time.Millisecond, 2903 50*time.Millisecond) 2904} 2905 2906func checkBasicFTP(client *ftp.ServerConn) error { 2907 _, err := client.CurrentDir() 2908 if err != nil { 2909 return err 2910 } 2911 err = client.NoOp() 2912 if err != nil { 2913 return err 2914 } 2915 _, err = client.List(".") 2916 if err != nil { 2917 return err 2918 } 2919 return nil 2920} 2921 2922func ftpUploadFile(localSourcePath string, remoteDestPath string, expectedSize int64, client *ftp.ServerConn, offset uint64) error { 2923 srcFile, err := os.Open(localSourcePath) 2924 if err != nil { 2925 return err 2926 } 2927 defer srcFile.Close() 2928 if offset > 0 { 2929 err = client.StorFrom(remoteDestPath, srcFile, offset) 2930 } else { 2931 err = client.Stor(remoteDestPath, srcFile) 2932 } 2933 if err != nil { 2934 return err 2935 } 2936 if expectedSize > 0 { 2937 size, err := client.FileSize(remoteDestPath) 2938 if err != nil { 2939 return err 2940 } 2941 if size != expectedSize { 2942 return fmt.Errorf("uploaded file size does not match, actual: %v, expected: %v", size, expectedSize) 2943 } 2944 } 2945 return nil 2946} 2947 2948func ftpDownloadFile(remoteSourcePath string, localDestPath string, expectedSize int64, client *ftp.ServerConn, offset uint64) error { 2949 downloadDest, err := os.Create(localDestPath) 2950 if err != nil { 2951 return err 2952 } 2953 defer downloadDest.Close() 2954 var r *ftp.Response 2955 if offset > 0 { 2956 r, err = client.RetrFrom(remoteSourcePath, offset) 2957 } else { 2958 r, err = client.Retr(remoteSourcePath) 2959 } 2960 if err != nil { 2961 return err 2962 } 2963 defer r.Close() 2964 2965 written, err := io.Copy(downloadDest, r) 2966 if err != nil { 2967 return err 2968 } 2969 if written != expectedSize { 2970 return fmt.Errorf("downloaded file size does not match, actual: %v, expected: %v", written, expectedSize) 2971 } 2972 return nil 2973} 2974 2975func getFTPClientImplicitTLS(user dataprovider.User) (*ftp.ServerConn, error) { 2976 ftpOptions := []ftp.DialOption{ftp.DialWithTimeout(5 * time.Second)} 2977 tlsConfig := &tls.Config{ 2978 ServerName: "localhost", 2979 InsecureSkipVerify: true, // use this for tests only 2980 MinVersion: tls.VersionTLS12, 2981 } 2982 ftpOptions = append(ftpOptions, ftp.DialWithTLS(tlsConfig)) 2983 ftpOptions = append(ftpOptions, ftp.DialWithDisabledEPSV(true)) 2984 client, err := ftp.Dial(ftpSrvAddrTLS, ftpOptions...) 2985 if err != nil { 2986 return nil, err 2987 } 2988 pwd := defaultPassword 2989 if user.Password != "" { 2990 pwd = user.Password 2991 } 2992 err = client.Login(user.Username, pwd) 2993 if err != nil { 2994 return nil, err 2995 } 2996 return client, err 2997} 2998 2999func getFTPClient(user dataprovider.User, useTLS bool, tlsConfig *tls.Config) (*ftp.ServerConn, error) { 3000 ftpOptions := []ftp.DialOption{ftp.DialWithTimeout(5 * time.Second)} 3001 if useTLS { 3002 if tlsConfig == nil { 3003 tlsConfig = &tls.Config{ 3004 ServerName: "localhost", 3005 InsecureSkipVerify: true, // use this for tests only 3006 MinVersion: tls.VersionTLS12, 3007 } 3008 } 3009 ftpOptions = append(ftpOptions, ftp.DialWithExplicitTLS(tlsConfig)) 3010 } 3011 client, err := ftp.Dial(ftpServerAddr, ftpOptions...) 3012 if err != nil { 3013 return nil, err 3014 } 3015 pwd := defaultPassword 3016 if user.Password != "" { 3017 pwd = user.Password 3018 } 3019 err = client.Login(user.Username, pwd) 3020 if err != nil { 3021 return nil, err 3022 } 3023 return client, err 3024} 3025 3026func waitTCPListening(address string) { 3027 for { 3028 conn, err := net.Dial("tcp", address) 3029 if err != nil { 3030 logger.WarnToConsole("tcp server %v not listening: %v", address, err) 3031 time.Sleep(100 * time.Millisecond) 3032 continue 3033 } 3034 logger.InfoToConsole("tcp server %v now listening", address) 3035 conn.Close() 3036 break 3037 } 3038} 3039 3040func waitNoConnections() { 3041 time.Sleep(50 * time.Millisecond) 3042 for len(common.Connections.GetStats()) > 0 { 3043 time.Sleep(50 * time.Millisecond) 3044 } 3045} 3046 3047func getTestUser() dataprovider.User { 3048 user := dataprovider.User{ 3049 BaseUser: sdk.BaseUser{ 3050 Username: defaultUsername, 3051 Password: defaultPassword, 3052 HomeDir: filepath.Join(homeBasePath, defaultUsername), 3053 Status: 1, 3054 ExpirationDate: 0, 3055 }, 3056 } 3057 user.Permissions = make(map[string][]string) 3058 user.Permissions["/"] = allPerms 3059 return user 3060} 3061 3062func getTestSFTPUser() dataprovider.User { 3063 u := getTestUser() 3064 u.Username = u.Username + "_sftp" 3065 u.FsConfig.Provider = sdk.SFTPFilesystemProvider 3066 u.FsConfig.SFTPConfig.Endpoint = sftpServerAddr 3067 u.FsConfig.SFTPConfig.Username = defaultUsername 3068 u.FsConfig.SFTPConfig.Password = kms.NewPlainSecret(defaultPassword) 3069 return u 3070} 3071 3072func getExtAuthScriptContent(user dataprovider.User, nonJSONResponse bool, username string) []byte { 3073 extAuthContent := []byte("#!/bin/sh\n\n") 3074 extAuthContent = append(extAuthContent, []byte(fmt.Sprintf("if test \"$SFTPGO_AUTHD_USERNAME\" = \"%v\"; then\n", user.Username))...) 3075 if len(username) > 0 { 3076 user.Username = username 3077 } 3078 u, _ := json.Marshal(user) 3079 if nonJSONResponse { 3080 extAuthContent = append(extAuthContent, []byte("echo 'text response'\n")...) 3081 } else { 3082 extAuthContent = append(extAuthContent, []byte(fmt.Sprintf("echo '%v'\n", string(u)))...) 3083 } 3084 extAuthContent = append(extAuthContent, []byte("else\n")...) 3085 if nonJSONResponse { 3086 extAuthContent = append(extAuthContent, []byte("echo 'text response'\n")...) 3087 } else { 3088 extAuthContent = append(extAuthContent, []byte("echo '{\"username\":\"\"}'\n")...) 3089 } 3090 extAuthContent = append(extAuthContent, []byte("fi\n")...) 3091 return extAuthContent 3092} 3093 3094func getPreLoginScriptContent(user dataprovider.User, nonJSONResponse bool) []byte { 3095 content := []byte("#!/bin/sh\n\n") 3096 if nonJSONResponse { 3097 content = append(content, []byte("echo 'text response'\n")...) 3098 return content 3099 } 3100 if len(user.Username) > 0 { 3101 u, _ := json.Marshal(user) 3102 content = append(content, []byte(fmt.Sprintf("echo '%v'\n", string(u)))...) 3103 } 3104 return content 3105} 3106 3107func getExitCodeScriptContent(exitCode int) []byte { 3108 content := []byte("#!/bin/sh\n\n") 3109 content = append(content, []byte(fmt.Sprintf("exit %v", exitCode))...) 3110 return content 3111} 3112 3113func createTestFile(path string, size int64) error { 3114 baseDir := filepath.Dir(path) 3115 if _, err := os.Stat(baseDir); os.IsNotExist(err) { 3116 err = os.MkdirAll(baseDir, os.ModePerm) 3117 if err != nil { 3118 return err 3119 } 3120 } 3121 content := make([]byte, size) 3122 _, err := rand.Read(content) 3123 if err != nil { 3124 return err 3125 } 3126 return os.WriteFile(path, content, os.ModePerm) 3127} 3128 3129func writeCerts(certPath, keyPath, caCrtPath, caCRLPath string) error { 3130 err := os.WriteFile(certPath, []byte(ftpsCert), os.ModePerm) 3131 if err != nil { 3132 logger.ErrorToConsole("error writing FTPS certificate: %v", err) 3133 return err 3134 } 3135 err = os.WriteFile(keyPath, []byte(ftpsKey), os.ModePerm) 3136 if err != nil { 3137 logger.ErrorToConsole("error writing FTPS private key: %v", err) 3138 return err 3139 } 3140 err = os.WriteFile(caCrtPath, []byte(caCRT), os.ModePerm) 3141 if err != nil { 3142 logger.ErrorToConsole("error writing FTPS CA crt: %v", err) 3143 return err 3144 } 3145 err = os.WriteFile(caCRLPath, []byte(caCRL), os.ModePerm) 3146 if err != nil { 3147 logger.ErrorToConsole("error writing FTPS CRL: %v", err) 3148 return err 3149 } 3150 return nil 3151} 3152 3153func generateTOTPPasscode(secret string, algo otp.Algorithm) (string, error) { 3154 return totp.GenerateCodeCustom(secret, time.Now(), totp.ValidateOpts{ 3155 Period: 30, 3156 Skew: 1, 3157 Digits: otp.DigitsSix, 3158 Algorithm: algo, 3159 }) 3160} 3161