compat.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. package dataprovider
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io/ioutil"
  6. "path/filepath"
  7. "github.com/drakkan/sftpgo/kms"
  8. "github.com/drakkan/sftpgo/logger"
  9. "github.com/drakkan/sftpgo/utils"
  10. "github.com/drakkan/sftpgo/vfs"
  11. )
  12. type compatUserV2 struct {
  13. ID int64 `json:"id"`
  14. Username string `json:"username"`
  15. Password string `json:"password,omitempty"`
  16. PublicKeys []string `json:"public_keys,omitempty"`
  17. HomeDir string `json:"home_dir"`
  18. UID int `json:"uid"`
  19. GID int `json:"gid"`
  20. MaxSessions int `json:"max_sessions"`
  21. QuotaSize int64 `json:"quota_size"`
  22. QuotaFiles int `json:"quota_files"`
  23. Permissions []string `json:"permissions"`
  24. UsedQuotaSize int64 `json:"used_quota_size"`
  25. UsedQuotaFiles int `json:"used_quota_files"`
  26. LastQuotaUpdate int64 `json:"last_quota_update"`
  27. UploadBandwidth int64 `json:"upload_bandwidth"`
  28. DownloadBandwidth int64 `json:"download_bandwidth"`
  29. ExpirationDate int64 `json:"expiration_date"`
  30. LastLogin int64 `json:"last_login"`
  31. Status int `json:"status"`
  32. }
  33. type compatS3FsConfigV4 struct {
  34. Bucket string `json:"bucket,omitempty"`
  35. KeyPrefix string `json:"key_prefix,omitempty"`
  36. Region string `json:"region,omitempty"`
  37. AccessKey string `json:"access_key,omitempty"`
  38. AccessSecret string `json:"access_secret,omitempty"`
  39. Endpoint string `json:"endpoint,omitempty"`
  40. StorageClass string `json:"storage_class,omitempty"`
  41. UploadPartSize int64 `json:"upload_part_size,omitempty"`
  42. UploadConcurrency int `json:"upload_concurrency,omitempty"`
  43. }
  44. type compatGCSFsConfigV4 struct {
  45. Bucket string `json:"bucket,omitempty"`
  46. KeyPrefix string `json:"key_prefix,omitempty"`
  47. CredentialFile string `json:"-"`
  48. Credentials []byte `json:"credentials,omitempty"`
  49. AutomaticCredentials int `json:"automatic_credentials,omitempty"`
  50. StorageClass string `json:"storage_class,omitempty"`
  51. }
  52. type compatAzBlobFsConfigV4 struct {
  53. Container string `json:"container,omitempty"`
  54. AccountName string `json:"account_name,omitempty"`
  55. AccountKey string `json:"account_key,omitempty"`
  56. Endpoint string `json:"endpoint,omitempty"`
  57. SASURL string `json:"sas_url,omitempty"`
  58. KeyPrefix string `json:"key_prefix,omitempty"`
  59. UploadPartSize int64 `json:"upload_part_size,omitempty"`
  60. UploadConcurrency int `json:"upload_concurrency,omitempty"`
  61. UseEmulator bool `json:"use_emulator,omitempty"`
  62. AccessTier string `json:"access_tier,omitempty"`
  63. }
  64. type compatFilesystemV4 struct {
  65. Provider FilesystemProvider `json:"provider"`
  66. S3Config compatS3FsConfigV4 `json:"s3config,omitempty"`
  67. GCSConfig compatGCSFsConfigV4 `json:"gcsconfig,omitempty"`
  68. AzBlobConfig compatAzBlobFsConfigV4 `json:"azblobconfig,omitempty"`
  69. }
  70. type compatUserV4 struct {
  71. ID int64 `json:"id"`
  72. Status int `json:"status"`
  73. Username string `json:"username"`
  74. ExpirationDate int64 `json:"expiration_date"`
  75. Password string `json:"password,omitempty"`
  76. PublicKeys []string `json:"public_keys,omitempty"`
  77. HomeDir string `json:"home_dir"`
  78. VirtualFolders []vfs.VirtualFolder `json:"virtual_folders,omitempty"`
  79. UID int `json:"uid"`
  80. GID int `json:"gid"`
  81. MaxSessions int `json:"max_sessions"`
  82. QuotaSize int64 `json:"quota_size"`
  83. QuotaFiles int `json:"quota_files"`
  84. Permissions map[string][]string `json:"permissions"`
  85. UsedQuotaSize int64 `json:"used_quota_size"`
  86. UsedQuotaFiles int `json:"used_quota_files"`
  87. LastQuotaUpdate int64 `json:"last_quota_update"`
  88. UploadBandwidth int64 `json:"upload_bandwidth"`
  89. DownloadBandwidth int64 `json:"download_bandwidth"`
  90. LastLogin int64 `json:"last_login"`
  91. Filters UserFilters `json:"filters"`
  92. FsConfig compatFilesystemV4 `json:"filesystem"`
  93. }
  94. type backupDataV4Compat struct {
  95. Users []compatUserV4 `json:"users"`
  96. Folders []vfs.BaseVirtualFolder `json:"folders"`
  97. }
  98. func createUserFromV4(u compatUserV4, fsConfig Filesystem) User {
  99. user := User{
  100. ID: u.ID,
  101. Status: u.Status,
  102. Username: u.Username,
  103. ExpirationDate: u.ExpirationDate,
  104. Password: u.Password,
  105. PublicKeys: u.PublicKeys,
  106. HomeDir: u.HomeDir,
  107. VirtualFolders: u.VirtualFolders,
  108. UID: u.UID,
  109. GID: u.GID,
  110. MaxSessions: u.MaxSessions,
  111. QuotaSize: u.QuotaSize,
  112. QuotaFiles: u.QuotaFiles,
  113. Permissions: u.Permissions,
  114. UsedQuotaSize: u.UsedQuotaSize,
  115. UsedQuotaFiles: u.UsedQuotaFiles,
  116. LastQuotaUpdate: u.LastQuotaUpdate,
  117. UploadBandwidth: u.UploadBandwidth,
  118. DownloadBandwidth: u.DownloadBandwidth,
  119. LastLogin: u.LastLogin,
  120. Filters: u.Filters,
  121. }
  122. user.FsConfig = fsConfig
  123. user.SetEmptySecretsIfNil()
  124. return user
  125. }
  126. func convertUserToV4(u User, fsConfig compatFilesystemV4) compatUserV4 {
  127. user := compatUserV4{
  128. ID: u.ID,
  129. Status: u.Status,
  130. Username: u.Username,
  131. ExpirationDate: u.ExpirationDate,
  132. Password: u.Password,
  133. PublicKeys: u.PublicKeys,
  134. HomeDir: u.HomeDir,
  135. VirtualFolders: u.VirtualFolders,
  136. UID: u.UID,
  137. GID: u.GID,
  138. MaxSessions: u.MaxSessions,
  139. QuotaSize: u.QuotaSize,
  140. QuotaFiles: u.QuotaFiles,
  141. Permissions: u.Permissions,
  142. UsedQuotaSize: u.UsedQuotaSize,
  143. UsedQuotaFiles: u.UsedQuotaFiles,
  144. LastQuotaUpdate: u.LastQuotaUpdate,
  145. UploadBandwidth: u.UploadBandwidth,
  146. DownloadBandwidth: u.DownloadBandwidth,
  147. LastLogin: u.LastLogin,
  148. Filters: u.Filters,
  149. }
  150. user.FsConfig = fsConfig
  151. return user
  152. }
  153. func getCGSCredentialsFromV4(config compatGCSFsConfigV4) (*kms.Secret, error) {
  154. secret := kms.NewEmptySecret()
  155. var err error
  156. if len(config.Credentials) > 0 {
  157. secret = kms.NewPlainSecret(string(config.Credentials))
  158. return secret, nil
  159. }
  160. if config.CredentialFile != "" {
  161. creds, err := ioutil.ReadFile(config.CredentialFile)
  162. if err != nil {
  163. return secret, err
  164. }
  165. secret = kms.NewPlainSecret(string(creds))
  166. return secret, nil
  167. }
  168. return secret, err
  169. }
  170. func getCGSCredentialsFromV6(config vfs.GCSFsConfig, username string) (string, error) {
  171. if config.Credentials == nil {
  172. config.Credentials = kms.NewEmptySecret()
  173. }
  174. if config.Credentials.IsEmpty() {
  175. config.CredentialFile = filepath.Join(credentialsDirPath, fmt.Sprintf("%v_gcs_credentials.json",
  176. username))
  177. creds, err := ioutil.ReadFile(config.CredentialFile)
  178. if err != nil {
  179. return "", err
  180. }
  181. err = json.Unmarshal(creds, &config.Credentials)
  182. if err != nil {
  183. return "", err
  184. }
  185. }
  186. if config.Credentials.IsEncrypted() {
  187. err := config.Credentials.Decrypt()
  188. if err != nil {
  189. return "", err
  190. }
  191. // in V4 GCS credentials were not encrypted
  192. return config.Credentials.GetPayload(), nil
  193. }
  194. return "", nil
  195. }
  196. func convertFsConfigToV4(fs Filesystem, username string) (compatFilesystemV4, error) {
  197. fsV4 := compatFilesystemV4{
  198. Provider: fs.Provider,
  199. S3Config: compatS3FsConfigV4{},
  200. AzBlobConfig: compatAzBlobFsConfigV4{},
  201. GCSConfig: compatGCSFsConfigV4{},
  202. }
  203. switch fs.Provider {
  204. case S3FilesystemProvider:
  205. fsV4.S3Config = compatS3FsConfigV4{
  206. Bucket: fs.S3Config.Bucket,
  207. KeyPrefix: fs.S3Config.KeyPrefix,
  208. Region: fs.S3Config.Region,
  209. AccessKey: fs.S3Config.AccessKey,
  210. AccessSecret: "",
  211. Endpoint: fs.S3Config.Endpoint,
  212. StorageClass: fs.S3Config.StorageClass,
  213. UploadPartSize: fs.S3Config.UploadPartSize,
  214. UploadConcurrency: fs.S3Config.UploadConcurrency,
  215. }
  216. if fs.S3Config.AccessSecret.IsEncrypted() {
  217. err := fs.S3Config.AccessSecret.Decrypt()
  218. if err != nil {
  219. return fsV4, err
  220. }
  221. secretV4, err := utils.EncryptData(fs.S3Config.AccessSecret.GetPayload())
  222. if err != nil {
  223. return fsV4, err
  224. }
  225. fsV4.S3Config.AccessSecret = secretV4
  226. }
  227. case AzureBlobFilesystemProvider:
  228. fsV4.AzBlobConfig = compatAzBlobFsConfigV4{
  229. Container: fs.AzBlobConfig.Container,
  230. AccountName: fs.AzBlobConfig.AccountName,
  231. AccountKey: "",
  232. Endpoint: fs.AzBlobConfig.Endpoint,
  233. SASURL: fs.AzBlobConfig.SASURL,
  234. KeyPrefix: fs.AzBlobConfig.KeyPrefix,
  235. UploadPartSize: fs.AzBlobConfig.UploadPartSize,
  236. UploadConcurrency: fs.AzBlobConfig.UploadConcurrency,
  237. UseEmulator: fs.AzBlobConfig.UseEmulator,
  238. AccessTier: fs.AzBlobConfig.AccessTier,
  239. }
  240. if fs.AzBlobConfig.AccountKey.IsEncrypted() {
  241. err := fs.AzBlobConfig.AccountKey.Decrypt()
  242. if err != nil {
  243. return fsV4, err
  244. }
  245. secretV4, err := utils.EncryptData(fs.AzBlobConfig.AccountKey.GetPayload())
  246. if err != nil {
  247. return fsV4, err
  248. }
  249. fsV4.AzBlobConfig.AccountKey = secretV4
  250. }
  251. case GCSFilesystemProvider:
  252. fsV4.GCSConfig = compatGCSFsConfigV4{
  253. Bucket: fs.GCSConfig.Bucket,
  254. KeyPrefix: fs.GCSConfig.KeyPrefix,
  255. CredentialFile: fs.GCSConfig.CredentialFile,
  256. AutomaticCredentials: fs.GCSConfig.AutomaticCredentials,
  257. StorageClass: fs.GCSConfig.StorageClass,
  258. }
  259. if fs.GCSConfig.AutomaticCredentials == 0 {
  260. creds, err := getCGSCredentialsFromV6(fs.GCSConfig, username)
  261. if err != nil {
  262. return fsV4, err
  263. }
  264. fsV4.GCSConfig.Credentials = []byte(creds)
  265. }
  266. case CryptedFilesystemProvider:
  267. // crypted provider was not supported in v4, the configuration will be lost
  268. fsV4.Provider = 0
  269. }
  270. return fsV4, nil
  271. }
  272. func convertFsConfigFromV4(compatFs compatFilesystemV4, username string) (Filesystem, error) {
  273. fsConfig := Filesystem{
  274. Provider: compatFs.Provider,
  275. S3Config: vfs.S3FsConfig{},
  276. AzBlobConfig: vfs.AzBlobFsConfig{},
  277. GCSConfig: vfs.GCSFsConfig{},
  278. }
  279. switch compatFs.Provider {
  280. case S3FilesystemProvider:
  281. fsConfig.S3Config = vfs.S3FsConfig{
  282. Bucket: compatFs.S3Config.Bucket,
  283. KeyPrefix: compatFs.S3Config.KeyPrefix,
  284. Region: compatFs.S3Config.Region,
  285. AccessKey: compatFs.S3Config.AccessKey,
  286. AccessSecret: kms.NewEmptySecret(),
  287. Endpoint: compatFs.S3Config.Endpoint,
  288. StorageClass: compatFs.S3Config.StorageClass,
  289. UploadPartSize: compatFs.S3Config.UploadPartSize,
  290. UploadConcurrency: compatFs.S3Config.UploadConcurrency,
  291. }
  292. if compatFs.S3Config.AccessSecret != "" {
  293. secret, err := kms.GetSecretFromCompatString(compatFs.S3Config.AccessSecret)
  294. if err != nil {
  295. providerLog(logger.LevelError, "unable to convert v4 filesystem for user %#v: %v", username, err)
  296. return fsConfig, err
  297. }
  298. fsConfig.S3Config.AccessSecret = secret
  299. }
  300. case AzureBlobFilesystemProvider:
  301. fsConfig.AzBlobConfig = vfs.AzBlobFsConfig{
  302. Container: compatFs.AzBlobConfig.Container,
  303. AccountName: compatFs.AzBlobConfig.AccountName,
  304. AccountKey: kms.NewEmptySecret(),
  305. Endpoint: compatFs.AzBlobConfig.Endpoint,
  306. SASURL: compatFs.AzBlobConfig.SASURL,
  307. KeyPrefix: compatFs.AzBlobConfig.KeyPrefix,
  308. UploadPartSize: compatFs.AzBlobConfig.UploadPartSize,
  309. UploadConcurrency: compatFs.AzBlobConfig.UploadConcurrency,
  310. UseEmulator: compatFs.AzBlobConfig.UseEmulator,
  311. AccessTier: compatFs.AzBlobConfig.AccessTier,
  312. }
  313. if compatFs.AzBlobConfig.AccountKey != "" {
  314. secret, err := kms.GetSecretFromCompatString(compatFs.AzBlobConfig.AccountKey)
  315. if err != nil {
  316. providerLog(logger.LevelError, "unable to convert v4 filesystem for user %#v: %v", username, err)
  317. return fsConfig, err
  318. }
  319. fsConfig.AzBlobConfig.AccountKey = secret
  320. }
  321. case GCSFilesystemProvider:
  322. fsConfig.GCSConfig = vfs.GCSFsConfig{
  323. Bucket: compatFs.GCSConfig.Bucket,
  324. KeyPrefix: compatFs.GCSConfig.KeyPrefix,
  325. CredentialFile: compatFs.GCSConfig.CredentialFile,
  326. AutomaticCredentials: compatFs.GCSConfig.AutomaticCredentials,
  327. StorageClass: compatFs.GCSConfig.StorageClass,
  328. }
  329. if compatFs.GCSConfig.AutomaticCredentials == 0 {
  330. compatFs.GCSConfig.CredentialFile = filepath.Join(credentialsDirPath, fmt.Sprintf("%v_gcs_credentials.json",
  331. username))
  332. }
  333. secret, err := getCGSCredentialsFromV4(compatFs.GCSConfig)
  334. if err != nil {
  335. providerLog(logger.LevelError, "unable to convert v4 filesystem for user %#v: %v", username, err)
  336. return fsConfig, err
  337. }
  338. fsConfig.GCSConfig.Credentials = secret
  339. }
  340. return fsConfig, nil
  341. }