folder.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. package vfs
  2. import (
  3. "fmt"
  4. "path/filepath"
  5. "strconv"
  6. "strings"
  7. "github.com/drakkan/sftpgo/utils"
  8. )
  9. // BaseVirtualFolder defines the path for the virtual folder and the used quota limits.
  10. // The same folder can be shared among multiple users and each user can have different
  11. // quota limits or a different virtual path.
  12. type BaseVirtualFolder struct {
  13. ID int64 `json:"id"`
  14. Name string `json:"name"`
  15. MappedPath string `json:"mapped_path,omitempty"`
  16. Description string `json:"description,omitempty"`
  17. UsedQuotaSize int64 `json:"used_quota_size"`
  18. // Used quota as number of files
  19. UsedQuotaFiles int `json:"used_quota_files"`
  20. // Last quota update as unix timestamp in milliseconds
  21. LastQuotaUpdate int64 `json:"last_quota_update"`
  22. // list of usernames associated with this virtual folder
  23. Users []string `json:"users,omitempty"`
  24. // Filesystem configuration details
  25. FsConfig Filesystem `json:"filesystem"`
  26. }
  27. // GetEncrytionAdditionalData returns the additional data to use for AEAD
  28. func (v *BaseVirtualFolder) GetEncrytionAdditionalData() string {
  29. return fmt.Sprintf("folder_%v", v.Name)
  30. }
  31. // GetGCSCredentialsFilePath returns the path for GCS credentials
  32. func (v *BaseVirtualFolder) GetGCSCredentialsFilePath() string {
  33. return filepath.Join(credentialsDirPath, "folders", fmt.Sprintf("%v_gcs_credentials.json", v.Name))
  34. }
  35. // GetACopy returns a copy
  36. func (v *BaseVirtualFolder) GetACopy() BaseVirtualFolder {
  37. users := make([]string, len(v.Users))
  38. copy(users, v.Users)
  39. return BaseVirtualFolder{
  40. ID: v.ID,
  41. Name: v.Name,
  42. Description: v.Description,
  43. MappedPath: v.MappedPath,
  44. UsedQuotaSize: v.UsedQuotaSize,
  45. UsedQuotaFiles: v.UsedQuotaFiles,
  46. LastQuotaUpdate: v.LastQuotaUpdate,
  47. Users: users,
  48. FsConfig: v.FsConfig.GetACopy(),
  49. }
  50. }
  51. // GetUsersAsString returns the list of users as comma separated string
  52. func (v *BaseVirtualFolder) GetUsersAsString() string {
  53. return strings.Join(v.Users, ",")
  54. }
  55. // GetQuotaSummary returns used quota and last update as string
  56. func (v *BaseVirtualFolder) GetQuotaSummary() string {
  57. var result string
  58. result = "Files: " + strconv.Itoa(v.UsedQuotaFiles)
  59. if v.UsedQuotaSize > 0 {
  60. result += ". Size: " + utils.ByteCountIEC(v.UsedQuotaSize)
  61. }
  62. if v.LastQuotaUpdate > 0 {
  63. t := utils.GetTimeFromMsecSinceEpoch(v.LastQuotaUpdate)
  64. result += fmt.Sprintf(". Last update: %v ", t.Format("2006-01-02 15:04")) // YYYY-MM-DD HH:MM
  65. }
  66. return result
  67. }
  68. // GetStorageDescrition returns the storage description
  69. func (v *BaseVirtualFolder) GetStorageDescrition() string {
  70. switch v.FsConfig.Provider {
  71. case LocalFilesystemProvider:
  72. return fmt.Sprintf("Local: %v", v.MappedPath)
  73. case S3FilesystemProvider:
  74. return fmt.Sprintf("S3: %v", v.FsConfig.S3Config.Bucket)
  75. case GCSFilesystemProvider:
  76. return fmt.Sprintf("GCS: %v", v.FsConfig.GCSConfig.Bucket)
  77. case AzureBlobFilesystemProvider:
  78. return fmt.Sprintf("AzBlob: %v", v.FsConfig.AzBlobConfig.Container)
  79. case CryptedFilesystemProvider:
  80. return fmt.Sprintf("Encrypted: %v", v.MappedPath)
  81. case SFTPFilesystemProvider:
  82. return fmt.Sprintf("SFTP: %v", v.FsConfig.SFTPConfig.Endpoint)
  83. default:
  84. return ""
  85. }
  86. }
  87. // IsLocalOrLocalCrypted returns true if the folder provider is local or local encrypted
  88. func (v *BaseVirtualFolder) IsLocalOrLocalCrypted() bool {
  89. return v.FsConfig.Provider == LocalFilesystemProvider || v.FsConfig.Provider == CryptedFilesystemProvider
  90. }
  91. // hideConfidentialData hides folder confidential data
  92. func (v *BaseVirtualFolder) hideConfidentialData() {
  93. switch v.FsConfig.Provider {
  94. case S3FilesystemProvider:
  95. v.FsConfig.S3Config.AccessSecret.Hide()
  96. case GCSFilesystemProvider:
  97. v.FsConfig.GCSConfig.Credentials.Hide()
  98. case AzureBlobFilesystemProvider:
  99. v.FsConfig.AzBlobConfig.AccountKey.Hide()
  100. case CryptedFilesystemProvider:
  101. v.FsConfig.CryptConfig.Passphrase.Hide()
  102. case SFTPFilesystemProvider:
  103. v.FsConfig.SFTPConfig.Password.Hide()
  104. v.FsConfig.SFTPConfig.PrivateKey.Hide()
  105. }
  106. }
  107. // PrepareForRendering prepares a folder for rendering.
  108. // It hides confidential data and set to nil the empty secrets
  109. // so they are not serialized
  110. func (v *BaseVirtualFolder) PrepareForRendering() {
  111. v.hideConfidentialData()
  112. v.FsConfig.SetEmptySecretsIfNil()
  113. }
  114. // HasRedactedSecret returns true if the folder has a redacted secret
  115. func (v *BaseVirtualFolder) HasRedactedSecret() bool {
  116. switch v.FsConfig.Provider {
  117. case S3FilesystemProvider:
  118. if v.FsConfig.S3Config.AccessSecret.IsRedacted() {
  119. return true
  120. }
  121. case GCSFilesystemProvider:
  122. if v.FsConfig.GCSConfig.Credentials.IsRedacted() {
  123. return true
  124. }
  125. case AzureBlobFilesystemProvider:
  126. if v.FsConfig.AzBlobConfig.AccountKey.IsRedacted() {
  127. return true
  128. }
  129. case CryptedFilesystemProvider:
  130. if v.FsConfig.CryptConfig.Passphrase.IsRedacted() {
  131. return true
  132. }
  133. case SFTPFilesystemProvider:
  134. if v.FsConfig.SFTPConfig.Password.IsRedacted() {
  135. return true
  136. }
  137. if v.FsConfig.SFTPConfig.PrivateKey.IsRedacted() {
  138. return true
  139. }
  140. }
  141. return false
  142. }
  143. // VirtualFolder defines a mapping between an SFTPGo exposed virtual path and a
  144. // filesystem path outside the user home directory.
  145. // The specified paths must be absolute and the virtual path cannot be "/",
  146. // it must be a sub directory. The parent directory for the specified virtual
  147. // path must exist. SFTPGo will try to automatically create any missing
  148. // parent directory for the configured virtual folders at user login.
  149. type VirtualFolder struct {
  150. BaseVirtualFolder
  151. VirtualPath string `json:"virtual_path"`
  152. // Maximum size allowed as bytes. 0 means unlimited, -1 included in user quota
  153. QuotaSize int64 `json:"quota_size"`
  154. // Maximum number of files allowed. 0 means unlimited, -1 included in user quota
  155. QuotaFiles int `json:"quota_files"`
  156. }
  157. // GetFilesystem returns the filesystem for this folder
  158. func (v *VirtualFolder) GetFilesystem(connectionID string, forbiddenSelfUsers []string) (Fs, error) {
  159. switch v.FsConfig.Provider {
  160. case S3FilesystemProvider:
  161. return NewS3Fs(connectionID, v.MappedPath, v.VirtualPath, v.FsConfig.S3Config)
  162. case GCSFilesystemProvider:
  163. config := v.FsConfig.GCSConfig
  164. config.CredentialFile = v.GetGCSCredentialsFilePath()
  165. return NewGCSFs(connectionID, v.MappedPath, v.VirtualPath, config)
  166. case AzureBlobFilesystemProvider:
  167. return NewAzBlobFs(connectionID, v.MappedPath, v.VirtualPath, v.FsConfig.AzBlobConfig)
  168. case CryptedFilesystemProvider:
  169. return NewCryptFs(connectionID, v.MappedPath, v.VirtualPath, v.FsConfig.CryptConfig)
  170. case SFTPFilesystemProvider:
  171. return NewSFTPFs(connectionID, v.VirtualPath, v.MappedPath, forbiddenSelfUsers, v.FsConfig.SFTPConfig)
  172. default:
  173. return NewOsFs(connectionID, v.MappedPath, v.VirtualPath), nil
  174. }
  175. }
  176. // ScanQuota scans the folder and returns the number of files and their size
  177. func (v *VirtualFolder) ScanQuota() (int, int64, error) {
  178. fs, err := v.GetFilesystem("", nil)
  179. if err != nil {
  180. return 0, 0, err
  181. }
  182. defer fs.Close()
  183. return fs.ScanRootDirContents()
  184. }
  185. // IsIncludedInUserQuota returns true if the virtual folder is included in user quota
  186. func (v *VirtualFolder) IsIncludedInUserQuota() bool {
  187. return v.QuotaFiles == -1 && v.QuotaSize == -1
  188. }
  189. // HasNoQuotaRestrictions returns true if no quota restrictions need to be applyed
  190. func (v *VirtualFolder) HasNoQuotaRestrictions(checkFiles bool) bool {
  191. if v.QuotaSize == 0 && (!checkFiles || v.QuotaFiles == 0) {
  192. return true
  193. }
  194. return false
  195. }
  196. // GetACopy returns a copy
  197. func (v *VirtualFolder) GetACopy() VirtualFolder {
  198. return VirtualFolder{
  199. BaseVirtualFolder: v.BaseVirtualFolder.GetACopy(),
  200. VirtualPath: v.VirtualPath,
  201. QuotaSize: v.QuotaSize,
  202. QuotaFiles: v.QuotaFiles,
  203. }
  204. }