1
0

folder.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // Copyright (C) 2019 Nicola Murino
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Affero General Public License as published
  5. // by the Free Software Foundation, version 3.
  6. //
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU Affero General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU Affero General Public License
  13. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. package vfs
  15. import (
  16. "errors"
  17. "fmt"
  18. "strings"
  19. "github.com/rs/xid"
  20. "github.com/sftpgo/sdk"
  21. )
  22. // BaseVirtualFolder defines the path for the virtual folder and the used quota limits.
  23. // The same folder can be shared among multiple users and each user can have different
  24. // quota limits or a different virtual path.
  25. type BaseVirtualFolder struct {
  26. ID int64 `json:"id"`
  27. Name string `json:"name"`
  28. MappedPath string `json:"mapped_path,omitempty"`
  29. Description string `json:"description,omitempty"`
  30. UsedQuotaSize int64 `json:"used_quota_size"`
  31. // Used quota as number of files
  32. UsedQuotaFiles int `json:"used_quota_files"`
  33. // Last quota update as unix timestamp in milliseconds
  34. LastQuotaUpdate int64 `json:"last_quota_update"`
  35. // list of usernames associated with this virtual folder
  36. Users []string `json:"users,omitempty"`
  37. // list of group names associated with this virtual folder
  38. Groups []string `json:"groups,omitempty"`
  39. // Filesystem configuration details
  40. FsConfig Filesystem `json:"filesystem"`
  41. }
  42. // GetEncryptionAdditionalData returns the additional data to use for AEAD
  43. func (v *BaseVirtualFolder) GetEncryptionAdditionalData() string {
  44. return fmt.Sprintf("folder_%v", v.Name)
  45. }
  46. // GetACopy returns a copy
  47. func (v *BaseVirtualFolder) GetACopy() BaseVirtualFolder {
  48. users := make([]string, len(v.Users))
  49. copy(users, v.Users)
  50. groups := make([]string, len(v.Groups))
  51. copy(groups, v.Groups)
  52. return BaseVirtualFolder{
  53. ID: v.ID,
  54. Name: v.Name,
  55. Description: v.Description,
  56. MappedPath: v.MappedPath,
  57. UsedQuotaSize: v.UsedQuotaSize,
  58. UsedQuotaFiles: v.UsedQuotaFiles,
  59. LastQuotaUpdate: v.LastQuotaUpdate,
  60. Users: users,
  61. Groups: v.Groups,
  62. FsConfig: v.FsConfig.GetACopy(),
  63. }
  64. }
  65. // IsLocalOrLocalCrypted returns true if the folder provider is local or local encrypted
  66. func (v *BaseVirtualFolder) IsLocalOrLocalCrypted() bool {
  67. return v.FsConfig.Provider == sdk.LocalFilesystemProvider || v.FsConfig.Provider == sdk.CryptedFilesystemProvider
  68. }
  69. // hideConfidentialData hides folder confidential data
  70. func (v *BaseVirtualFolder) hideConfidentialData() {
  71. switch v.FsConfig.Provider {
  72. case sdk.S3FilesystemProvider:
  73. v.FsConfig.S3Config.HideConfidentialData()
  74. case sdk.GCSFilesystemProvider:
  75. v.FsConfig.GCSConfig.HideConfidentialData()
  76. case sdk.AzureBlobFilesystemProvider:
  77. v.FsConfig.AzBlobConfig.HideConfidentialData()
  78. case sdk.CryptedFilesystemProvider:
  79. v.FsConfig.CryptConfig.HideConfidentialData()
  80. case sdk.SFTPFilesystemProvider:
  81. v.FsConfig.SFTPConfig.HideConfidentialData()
  82. case sdk.HTTPFilesystemProvider:
  83. v.FsConfig.HTTPConfig.HideConfidentialData()
  84. }
  85. }
  86. // PrepareForRendering prepares a folder for rendering.
  87. // It hides confidential data and set to nil the empty secrets
  88. // so they are not serialized
  89. func (v *BaseVirtualFolder) PrepareForRendering() {
  90. v.hideConfidentialData()
  91. v.FsConfig.SetEmptySecretsIfNil()
  92. }
  93. // HasRedactedSecret returns true if the folder has a redacted secret
  94. func (v *BaseVirtualFolder) HasRedactedSecret() bool {
  95. return v.FsConfig.HasRedactedSecret()
  96. }
  97. // hasPathPlaceholder returns true if the folder has a path placeholder
  98. func (v *BaseVirtualFolder) hasPathPlaceholder() bool {
  99. placeholder := "%username%"
  100. switch v.FsConfig.Provider {
  101. case sdk.S3FilesystemProvider:
  102. return strings.Contains(v.FsConfig.S3Config.KeyPrefix, placeholder)
  103. case sdk.GCSFilesystemProvider:
  104. return strings.Contains(v.FsConfig.GCSConfig.KeyPrefix, placeholder)
  105. case sdk.AzureBlobFilesystemProvider:
  106. return strings.Contains(v.FsConfig.AzBlobConfig.KeyPrefix, placeholder)
  107. case sdk.SFTPFilesystemProvider:
  108. return strings.Contains(v.FsConfig.SFTPConfig.Prefix, placeholder)
  109. case sdk.LocalFilesystemProvider, sdk.CryptedFilesystemProvider:
  110. return strings.Contains(v.MappedPath, placeholder)
  111. }
  112. return false
  113. }
  114. // VirtualFolder defines a mapping between an SFTPGo virtual path and a
  115. // filesystem path outside the user home directory.
  116. // The specified paths must be absolute and the virtual path cannot be "/",
  117. // it must be a sub directory. The parent directory for the specified virtual
  118. // path must exist. SFTPGo will try to automatically create any missing
  119. // parent directory for the configured virtual folders at user login.
  120. type VirtualFolder struct {
  121. BaseVirtualFolder
  122. VirtualPath string `json:"virtual_path"`
  123. // Maximum size allowed as bytes. 0 means unlimited, -1 included in user quota
  124. QuotaSize int64 `json:"quota_size"`
  125. // Maximum number of files allowed. 0 means unlimited, -1 included in user quota
  126. QuotaFiles int `json:"quota_files"`
  127. }
  128. // GetFilesystem returns the filesystem for this folder
  129. func (v *VirtualFolder) GetFilesystem(connectionID string, forbiddenSelfUsers []string) (Fs, error) {
  130. switch v.FsConfig.Provider {
  131. case sdk.S3FilesystemProvider:
  132. return NewS3Fs(connectionID, v.MappedPath, v.VirtualPath, v.FsConfig.S3Config)
  133. case sdk.GCSFilesystemProvider:
  134. return NewGCSFs(connectionID, v.MappedPath, v.VirtualPath, v.FsConfig.GCSConfig)
  135. case sdk.AzureBlobFilesystemProvider:
  136. return NewAzBlobFs(connectionID, v.MappedPath, v.VirtualPath, v.FsConfig.AzBlobConfig)
  137. case sdk.CryptedFilesystemProvider:
  138. return NewCryptFs(connectionID, v.MappedPath, v.VirtualPath, v.FsConfig.CryptConfig)
  139. case sdk.SFTPFilesystemProvider:
  140. return NewSFTPFs(connectionID, v.VirtualPath, v.MappedPath, forbiddenSelfUsers, v.FsConfig.SFTPConfig)
  141. case sdk.HTTPFilesystemProvider:
  142. return NewHTTPFs(connectionID, v.MappedPath, v.VirtualPath, v.FsConfig.HTTPConfig)
  143. default:
  144. return NewOsFs(connectionID, v.MappedPath, v.VirtualPath, &v.FsConfig.OSConfig), nil
  145. }
  146. }
  147. // ScanQuota scans the folder and returns the number of files and their size
  148. func (v *VirtualFolder) ScanQuota() (int, int64, error) {
  149. if v.hasPathPlaceholder() {
  150. return 0, 0, errors.New("cannot scan quota: this folder has a path placeholder")
  151. }
  152. fs, err := v.GetFilesystem(xid.New().String(), nil)
  153. if err != nil {
  154. return 0, 0, err
  155. }
  156. defer fs.Close()
  157. return fs.ScanRootDirContents()
  158. }
  159. // IsIncludedInUserQuota returns true if the virtual folder is included in user quota
  160. func (v *VirtualFolder) IsIncludedInUserQuota() bool {
  161. return v.QuotaFiles == -1 && v.QuotaSize == -1
  162. }
  163. // HasNoQuotaRestrictions returns true if no quota restrictions need to be applyed
  164. func (v *VirtualFolder) HasNoQuotaRestrictions(checkFiles bool) bool {
  165. if v.QuotaSize == 0 && (!checkFiles || v.QuotaFiles == 0) {
  166. return true
  167. }
  168. return false
  169. }
  170. // GetACopy returns a copy
  171. func (v *VirtualFolder) GetACopy() VirtualFolder {
  172. return VirtualFolder{
  173. BaseVirtualFolder: v.BaseVirtualFolder.GetACopy(),
  174. VirtualPath: v.VirtualPath,
  175. QuotaSize: v.QuotaSize,
  176. QuotaFiles: v.QuotaFiles,
  177. }
  178. }