compat.go 13 KB

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