folder.go 8.5 KB

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