user.go 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043
  1. package dataprovider
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "net"
  7. "os"
  8. "path"
  9. "path/filepath"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "golang.org/x/net/webdav"
  14. "github.com/drakkan/sftpgo/kms"
  15. "github.com/drakkan/sftpgo/logger"
  16. "github.com/drakkan/sftpgo/utils"
  17. "github.com/drakkan/sftpgo/vfs"
  18. )
  19. // Available permissions for SFTPGo users
  20. const (
  21. // All permissions are granted
  22. PermAny = "*"
  23. // List items such as files and directories is allowed
  24. PermListItems = "list"
  25. // download files is allowed
  26. PermDownload = "download"
  27. // upload files is allowed
  28. PermUpload = "upload"
  29. // overwrite an existing file, while uploading, is allowed
  30. // upload permission is required to allow file overwrite
  31. PermOverwrite = "overwrite"
  32. // delete files or directories is allowed
  33. PermDelete = "delete"
  34. // rename files or directories is allowed
  35. PermRename = "rename"
  36. // create directories is allowed
  37. PermCreateDirs = "create_dirs"
  38. // create symbolic links is allowed
  39. PermCreateSymlinks = "create_symlinks"
  40. // changing file or directory permissions is allowed
  41. PermChmod = "chmod"
  42. // changing file or directory owner and group is allowed
  43. PermChown = "chown"
  44. // changing file or directory access and modification time is allowed
  45. PermChtimes = "chtimes"
  46. )
  47. // Available login methods
  48. const (
  49. LoginMethodNoAuthTryed = "no_auth_tryed"
  50. LoginMethodPassword = "password"
  51. SSHLoginMethodPublicKey = "publickey"
  52. SSHLoginMethodKeyboardInteractive = "keyboard-interactive"
  53. SSHLoginMethodKeyAndPassword = "publickey+password"
  54. SSHLoginMethodKeyAndKeyboardInt = "publickey+keyboard-interactive"
  55. LoginMethodTLSCertificate = "TLSCertificate"
  56. LoginMethodTLSCertificateAndPwd = "TLSCertificate+password"
  57. )
  58. // TLSUsername defines the TLS certificate attribute to use as username
  59. type TLSUsername string
  60. // Supported certificate attributes to use as username
  61. const (
  62. TLSUsernameNone TLSUsername = "None"
  63. TLSUsernameCN TLSUsername = "CommonName"
  64. )
  65. var (
  66. errNoMatchingVirtualFolder = errors.New("no matching virtual folder found")
  67. )
  68. // CachedUser adds fields useful for caching to a SFTPGo user
  69. type CachedUser struct {
  70. User User
  71. Expiration time.Time
  72. Password string
  73. LockSystem webdav.LockSystem
  74. }
  75. // IsExpired returns true if the cached user is expired
  76. func (c *CachedUser) IsExpired() bool {
  77. if c.Expiration.IsZero() {
  78. return false
  79. }
  80. return c.Expiration.Before(time.Now())
  81. }
  82. // ExtensionsFilter defines filters based on file extensions.
  83. // These restrictions do not apply to files listing for performance reasons, so
  84. // a denied file cannot be downloaded/overwritten/renamed but will still be
  85. // in the list of files.
  86. // System commands such as Git and rsync interacts with the filesystem directly
  87. // and they are not aware about these restrictions so they are not allowed
  88. // inside paths with extensions filters
  89. type ExtensionsFilter struct {
  90. // Virtual path, if no other specific filter is defined, the filter apply for
  91. // sub directories too.
  92. // For example if filters are defined for the paths "/" and "/sub" then the
  93. // filters for "/" are applied for any file outside the "/sub" directory
  94. Path string `json:"path"`
  95. // only files with these, case insensitive, extensions are allowed.
  96. // Shell like expansion is not supported so you have to specify ".jpg" and
  97. // not "*.jpg". If you want shell like patterns use pattern filters
  98. AllowedExtensions []string `json:"allowed_extensions,omitempty"`
  99. // files with these, case insensitive, extensions are not allowed.
  100. // Denied file extensions are evaluated before the allowed ones
  101. DeniedExtensions []string `json:"denied_extensions,omitempty"`
  102. }
  103. // PatternsFilter defines filters based on shell like patterns.
  104. // These restrictions do not apply to files listing for performance reasons, so
  105. // a denied file cannot be downloaded/overwritten/renamed but will still be
  106. // in the list of files.
  107. // System commands such as Git and rsync interacts with the filesystem directly
  108. // and they are not aware about these restrictions so they are not allowed
  109. // inside paths with extensions filters
  110. type PatternsFilter struct {
  111. // Virtual path, if no other specific filter is defined, the filter apply for
  112. // sub directories too.
  113. // For example if filters are defined for the paths "/" and "/sub" then the
  114. // filters for "/" are applied for any file outside the "/sub" directory
  115. Path string `json:"path"`
  116. // files with these, case insensitive, patterns are allowed.
  117. // Denied file patterns are evaluated before the allowed ones
  118. AllowedPatterns []string `json:"allowed_patterns,omitempty"`
  119. // files with these, case insensitive, patterns are not allowed.
  120. // Denied file patterns are evaluated before the allowed ones
  121. DeniedPatterns []string `json:"denied_patterns,omitempty"`
  122. }
  123. // UserFilters defines additional restrictions for a user
  124. type UserFilters struct {
  125. // only clients connecting from these IP/Mask are allowed.
  126. // IP/Mask must be in CIDR notation as defined in RFC 4632 and RFC 4291
  127. // for example "192.0.2.0/24" or "2001:db8::/32"
  128. AllowedIP []string `json:"allowed_ip,omitempty"`
  129. // clients connecting from these IP/Mask are not allowed.
  130. // Denied rules will be evaluated before allowed ones
  131. DeniedIP []string `json:"denied_ip,omitempty"`
  132. // these login methods are not allowed.
  133. // If null or empty any available login method is allowed
  134. DeniedLoginMethods []string `json:"denied_login_methods,omitempty"`
  135. // these protocols are not allowed.
  136. // If null or empty any available protocol is allowed
  137. DeniedProtocols []string `json:"denied_protocols,omitempty"`
  138. // filters based on file extensions.
  139. // Please note that these restrictions can be easily bypassed.
  140. FileExtensions []ExtensionsFilter `json:"file_extensions,omitempty"`
  141. // filter based on shell patterns
  142. FilePatterns []PatternsFilter `json:"file_patterns,omitempty"`
  143. // max size allowed for a single upload, 0 means unlimited
  144. MaxUploadFileSize int64 `json:"max_upload_file_size,omitempty"`
  145. // TLS certificate attribute to use as username.
  146. // For FTP clients it must match the name provided using the
  147. // "USER" command
  148. TLSUsername TLSUsername `json:"tls_username,omitempty"`
  149. }
  150. // User defines a SFTPGo user
  151. type User struct {
  152. // Database unique identifier
  153. ID int64 `json:"id"`
  154. // 1 enabled, 0 disabled (login is not allowed)
  155. Status int `json:"status"`
  156. // Username
  157. Username string `json:"username"`
  158. // Account expiration date as unix timestamp in milliseconds. An expired account cannot login.
  159. // 0 means no expiration
  160. ExpirationDate int64 `json:"expiration_date"`
  161. // Password used for password authentication.
  162. // For users created using SFTPGo REST API the password is be stored using argon2id hashing algo.
  163. // Checking passwords stored with bcrypt, pbkdf2, md5crypt and sha512crypt is supported too.
  164. Password string `json:"password,omitempty"`
  165. // PublicKeys used for public key authentication. At least one between password and a public key is mandatory
  166. PublicKeys []string `json:"public_keys,omitempty"`
  167. // The user cannot upload or download files outside this directory. Must be an absolute path
  168. HomeDir string `json:"home_dir"`
  169. // Mapping between virtual paths and virtual folders
  170. VirtualFolders []vfs.VirtualFolder `json:"virtual_folders,omitempty"`
  171. // If sftpgo runs as root system user then the created files and directories will be assigned to this system UID
  172. UID int `json:"uid"`
  173. // If sftpgo runs as root system user then the created files and directories will be assigned to this system GID
  174. GID int `json:"gid"`
  175. // Maximum concurrent sessions. 0 means unlimited
  176. MaxSessions int `json:"max_sessions"`
  177. // Maximum size allowed as bytes. 0 means unlimited
  178. QuotaSize int64 `json:"quota_size"`
  179. // Maximum number of files allowed. 0 means unlimited
  180. QuotaFiles int `json:"quota_files"`
  181. // List of the granted permissions
  182. Permissions map[string][]string `json:"permissions"`
  183. // Used quota as bytes
  184. UsedQuotaSize int64 `json:"used_quota_size"`
  185. // Used quota as number of files
  186. UsedQuotaFiles int `json:"used_quota_files"`
  187. // Last quota update as unix timestamp in milliseconds
  188. LastQuotaUpdate int64 `json:"last_quota_update"`
  189. // Maximum upload bandwidth as KB/s, 0 means unlimited
  190. UploadBandwidth int64 `json:"upload_bandwidth"`
  191. // Maximum download bandwidth as KB/s, 0 means unlimited
  192. DownloadBandwidth int64 `json:"download_bandwidth"`
  193. // Last login as unix timestamp in milliseconds
  194. LastLogin int64 `json:"last_login"`
  195. // Additional restrictions
  196. Filters UserFilters `json:"filters"`
  197. // Filesystem configuration details
  198. FsConfig vfs.Filesystem `json:"filesystem"`
  199. // optional description, for example full name
  200. Description string `json:"description,omitempty"`
  201. // free form text field for external systems
  202. AdditionalInfo string `json:"additional_info,omitempty"`
  203. // we store the filesystem here using the base path as key.
  204. fsCache map[string]vfs.Fs `json:"-"`
  205. }
  206. // GetFilesystem returns the base filesystem for this user
  207. func (u *User) GetFilesystem(connectionID string) (fs vfs.Fs, err error) {
  208. fs, err = u.getRootFs(connectionID)
  209. if err != nil {
  210. return fs, err
  211. }
  212. u.fsCache = make(map[string]vfs.Fs)
  213. u.fsCache["/"] = fs
  214. return fs, err
  215. }
  216. func (u *User) getRootFs(connectionID string) (fs vfs.Fs, err error) {
  217. switch u.FsConfig.Provider {
  218. case vfs.S3FilesystemProvider:
  219. return vfs.NewS3Fs(connectionID, u.GetHomeDir(), "", u.FsConfig.S3Config)
  220. case vfs.GCSFilesystemProvider:
  221. config := u.FsConfig.GCSConfig
  222. config.CredentialFile = u.GetGCSCredentialsFilePath()
  223. return vfs.NewGCSFs(connectionID, u.GetHomeDir(), "", config)
  224. case vfs.AzureBlobFilesystemProvider:
  225. return vfs.NewAzBlobFs(connectionID, u.GetHomeDir(), "", u.FsConfig.AzBlobConfig)
  226. case vfs.CryptedFilesystemProvider:
  227. return vfs.NewCryptFs(connectionID, u.GetHomeDir(), "", u.FsConfig.CryptConfig)
  228. case vfs.SFTPFilesystemProvider:
  229. return vfs.NewSFTPFs(connectionID, "", u.FsConfig.SFTPConfig)
  230. default:
  231. return vfs.NewOsFs(connectionID, u.GetHomeDir(), ""), nil
  232. }
  233. }
  234. // CheckFsRoot check the root directory for the main fs and the virtual folders.
  235. // It returns an error if the main filesystem cannot be created
  236. func (u *User) CheckFsRoot(connectionID string) error {
  237. fs, err := u.GetFilesystemForPath("/", connectionID)
  238. if err != nil {
  239. logger.Warn(logSender, connectionID, "could not create main filesystem for user %#v err: %v", u.Username, err)
  240. return err
  241. }
  242. fs.CheckRootPath(u.Username, u.GetUID(), u.GetGID())
  243. for idx := range u.VirtualFolders {
  244. v := &u.VirtualFolders[idx]
  245. fs, err = u.GetFilesystemForPath(v.VirtualPath, connectionID)
  246. if err == nil {
  247. fs.CheckRootPath(u.Username, u.GetUID(), u.GetGID())
  248. }
  249. // now check intermediary folders
  250. fs, err = u.GetFilesystemForPath(path.Dir(v.VirtualPath), connectionID)
  251. if err == nil && !fs.HasVirtualFolders() {
  252. fsPath, err := fs.ResolvePath(v.VirtualPath)
  253. if err != nil {
  254. continue
  255. }
  256. err = fs.MkdirAll(fsPath, u.GetUID(), u.GetGID())
  257. logger.Debug(logSender, connectionID, "create intermediary dir to %#v, path %#v, err: %v",
  258. v.VirtualPath, fsPath, err)
  259. }
  260. }
  261. return nil
  262. }
  263. // HideConfidentialData hides user confidential data
  264. func (u *User) HideConfidentialData() {
  265. u.Password = ""
  266. switch u.FsConfig.Provider {
  267. case vfs.S3FilesystemProvider:
  268. u.FsConfig.S3Config.AccessSecret.Hide()
  269. case vfs.GCSFilesystemProvider:
  270. u.FsConfig.GCSConfig.Credentials.Hide()
  271. case vfs.AzureBlobFilesystemProvider:
  272. u.FsConfig.AzBlobConfig.AccountKey.Hide()
  273. case vfs.CryptedFilesystemProvider:
  274. u.FsConfig.CryptConfig.Passphrase.Hide()
  275. case vfs.SFTPFilesystemProvider:
  276. u.FsConfig.SFTPConfig.Password.Hide()
  277. u.FsConfig.SFTPConfig.PrivateKey.Hide()
  278. }
  279. for idx := range u.VirtualFolders {
  280. folder := &u.VirtualFolders[idx]
  281. folder.HideConfidentialData()
  282. }
  283. }
  284. func (u *User) hasRedactedSecret() bool {
  285. switch u.FsConfig.Provider {
  286. case vfs.S3FilesystemProvider:
  287. if u.FsConfig.S3Config.AccessSecret.IsRedacted() {
  288. return true
  289. }
  290. case vfs.GCSFilesystemProvider:
  291. if u.FsConfig.GCSConfig.Credentials.IsRedacted() {
  292. return true
  293. }
  294. case vfs.AzureBlobFilesystemProvider:
  295. if u.FsConfig.AzBlobConfig.AccountKey.IsRedacted() {
  296. return true
  297. }
  298. case vfs.CryptedFilesystemProvider:
  299. if u.FsConfig.CryptConfig.Passphrase.IsRedacted() {
  300. return true
  301. }
  302. case vfs.SFTPFilesystemProvider:
  303. if u.FsConfig.SFTPConfig.Password.IsRedacted() {
  304. return true
  305. }
  306. if u.FsConfig.SFTPConfig.PrivateKey.IsRedacted() {
  307. return true
  308. }
  309. }
  310. for idx := range u.VirtualFolders {
  311. folder := &u.VirtualFolders[idx]
  312. if folder.HasRedactedSecret() {
  313. return true
  314. }
  315. }
  316. return false
  317. }
  318. // CloseFs closes the underlying filesystems
  319. func (u *User) CloseFs() error {
  320. if u.fsCache == nil {
  321. return nil
  322. }
  323. var err error
  324. for _, fs := range u.fsCache {
  325. errClose := fs.Close()
  326. if err == nil {
  327. err = errClose
  328. }
  329. }
  330. return err
  331. }
  332. // IsPasswordHashed returns true if the password is hashed
  333. func (u *User) IsPasswordHashed() bool {
  334. return utils.IsStringPrefixInSlice(u.Password, hashPwdPrefixes)
  335. }
  336. // IsTLSUsernameVerificationEnabled returns true if we need to extract the username
  337. // from the client TLS certificate
  338. func (u *User) IsTLSUsernameVerificationEnabled() bool {
  339. if u.Filters.TLSUsername != "" {
  340. return u.Filters.TLSUsername != TLSUsernameNone
  341. }
  342. return false
  343. }
  344. // SetEmptySecrets sets to empty any user secret
  345. func (u *User) SetEmptySecrets() {
  346. u.FsConfig.S3Config.AccessSecret = kms.NewEmptySecret()
  347. u.FsConfig.GCSConfig.Credentials = kms.NewEmptySecret()
  348. u.FsConfig.AzBlobConfig.AccountKey = kms.NewEmptySecret()
  349. u.FsConfig.CryptConfig.Passphrase = kms.NewEmptySecret()
  350. u.FsConfig.SFTPConfig.Password = kms.NewEmptySecret()
  351. u.FsConfig.SFTPConfig.PrivateKey = kms.NewEmptySecret()
  352. for idx := range u.VirtualFolders {
  353. folder := &u.VirtualFolders[idx]
  354. folder.FsConfig.SetEmptySecretsIfNil()
  355. }
  356. }
  357. // GetPermissionsForPath returns the permissions for the given path.
  358. // The path must be a SFTPGo exposed path
  359. func (u *User) GetPermissionsForPath(p string) []string {
  360. permissions := []string{}
  361. if perms, ok := u.Permissions["/"]; ok {
  362. // if only root permissions are defined returns them unconditionally
  363. if len(u.Permissions) == 1 {
  364. return perms
  365. }
  366. // fallback permissions
  367. permissions = perms
  368. }
  369. dirsForPath := utils.GetDirsForVirtualPath(p)
  370. // dirsForPath contains all the dirs for a given path in reverse order
  371. // for example if the path is: /1/2/3/4 it contains:
  372. // [ "/1/2/3/4", "/1/2/3", "/1/2", "/1", "/" ]
  373. // so the first match is the one we are interested to
  374. for idx := range dirsForPath {
  375. if perms, ok := u.Permissions[dirsForPath[idx]]; ok {
  376. permissions = perms
  377. break
  378. }
  379. }
  380. return permissions
  381. }
  382. // GetFilesystemForPath returns the filesystem for the given path
  383. func (u *User) GetFilesystemForPath(virtualPath, connectionID string) (vfs.Fs, error) {
  384. if u.fsCache == nil {
  385. u.fsCache = make(map[string]vfs.Fs)
  386. }
  387. if virtualPath != "" && virtualPath != "/" && len(u.VirtualFolders) > 0 {
  388. folder, err := u.GetVirtualFolderForPath(virtualPath)
  389. if err == nil {
  390. if fs, ok := u.fsCache[folder.VirtualPath]; ok {
  391. return fs, nil
  392. }
  393. fs, err := folder.GetFilesystem(connectionID)
  394. if err == nil {
  395. u.fsCache[folder.VirtualPath] = fs
  396. }
  397. return fs, err
  398. }
  399. }
  400. if val, ok := u.fsCache["/"]; ok {
  401. return val, nil
  402. }
  403. return u.GetFilesystem(connectionID)
  404. }
  405. // GetVirtualFolderForPath returns the virtual folder containing the specified virtual path.
  406. // If the path is not inside a virtual folder an error is returned
  407. func (u *User) GetVirtualFolderForPath(virtualPath string) (vfs.VirtualFolder, error) {
  408. var folder vfs.VirtualFolder
  409. if len(u.VirtualFolders) == 0 {
  410. return folder, errNoMatchingVirtualFolder
  411. }
  412. dirsForPath := utils.GetDirsForVirtualPath(virtualPath)
  413. for index := range dirsForPath {
  414. for idx := range u.VirtualFolders {
  415. v := &u.VirtualFolders[idx]
  416. if v.VirtualPath == dirsForPath[index] {
  417. return *v, nil
  418. }
  419. }
  420. }
  421. return folder, errNoMatchingVirtualFolder
  422. }
  423. // ScanQuota scans the user home dir and virtual folders, included in its quota,
  424. // and returns the number of files and their size
  425. func (u *User) ScanQuota() (int, int64, error) {
  426. fs, err := u.getRootFs("")
  427. if err != nil {
  428. return 0, 0, err
  429. }
  430. defer fs.Close()
  431. numFiles, size, err := fs.ScanRootDirContents()
  432. if err != nil {
  433. return numFiles, size, err
  434. }
  435. for idx := range u.VirtualFolders {
  436. v := &u.VirtualFolders[idx]
  437. if !v.IsIncludedInUserQuota() {
  438. continue
  439. }
  440. num, s, err := v.ScanQuota()
  441. if err != nil {
  442. return numFiles, size, err
  443. }
  444. numFiles += num
  445. size += s
  446. }
  447. return numFiles, size, nil
  448. }
  449. // GetVirtualFoldersInPath returns the virtual folders inside virtualPath including
  450. // any parents
  451. func (u *User) GetVirtualFoldersInPath(virtualPath string) map[string]bool {
  452. result := make(map[string]bool)
  453. for idx := range u.VirtualFolders {
  454. v := &u.VirtualFolders[idx]
  455. dirsForPath := utils.GetDirsForVirtualPath(v.VirtualPath)
  456. for index := range dirsForPath {
  457. d := dirsForPath[index]
  458. if d == "/" {
  459. continue
  460. }
  461. if path.Dir(d) == virtualPath {
  462. result[d] = true
  463. }
  464. }
  465. }
  466. return result
  467. }
  468. // AddVirtualDirs adds virtual folders, if defined, to the given files list
  469. func (u *User) AddVirtualDirs(list []os.FileInfo, virtualPath string) []os.FileInfo {
  470. if len(u.VirtualFolders) == 0 {
  471. return list
  472. }
  473. for dir := range u.GetVirtualFoldersInPath(virtualPath) {
  474. fi := vfs.NewFileInfo(dir, true, 0, time.Now(), false)
  475. found := false
  476. for index := range list {
  477. if list[index].Name() == fi.Name() {
  478. list[index] = fi
  479. found = true
  480. break
  481. }
  482. }
  483. if !found {
  484. list = append(list, fi)
  485. }
  486. }
  487. return list
  488. }
  489. // IsMappedPath returns true if the specified filesystem path has a virtual folder mapping.
  490. // The filesystem path must be cleaned before calling this method
  491. func (u *User) IsMappedPath(fsPath string) bool {
  492. for idx := range u.VirtualFolders {
  493. v := &u.VirtualFolders[idx]
  494. if fsPath == v.MappedPath {
  495. return true
  496. }
  497. }
  498. return false
  499. }
  500. // IsVirtualFolder returns true if the specified virtual path is a virtual folder
  501. func (u *User) IsVirtualFolder(virtualPath string) bool {
  502. for idx := range u.VirtualFolders {
  503. v := &u.VirtualFolders[idx]
  504. if virtualPath == v.VirtualPath {
  505. return true
  506. }
  507. }
  508. return false
  509. }
  510. // HasVirtualFoldersInside returns true if there are virtual folders inside the
  511. // specified virtual path. We assume that path are cleaned
  512. func (u *User) HasVirtualFoldersInside(virtualPath string) bool {
  513. if virtualPath == "/" && len(u.VirtualFolders) > 0 {
  514. return true
  515. }
  516. for idx := range u.VirtualFolders {
  517. v := &u.VirtualFolders[idx]
  518. if len(v.VirtualPath) > len(virtualPath) {
  519. if strings.HasPrefix(v.VirtualPath, virtualPath+"/") {
  520. return true
  521. }
  522. }
  523. }
  524. return false
  525. }
  526. // HasPermissionsInside returns true if the specified virtualPath has no permissions itself and
  527. // no subdirs with defined permissions
  528. func (u *User) HasPermissionsInside(virtualPath string) bool {
  529. for dir := range u.Permissions {
  530. if dir == virtualPath {
  531. return true
  532. } else if len(dir) > len(virtualPath) {
  533. if strings.HasPrefix(dir, virtualPath+"/") {
  534. return true
  535. }
  536. }
  537. }
  538. return false
  539. }
  540. // HasPerm returns true if the user has the given permission or any permission
  541. func (u *User) HasPerm(permission, path string) bool {
  542. perms := u.GetPermissionsForPath(path)
  543. if utils.IsStringInSlice(PermAny, perms) {
  544. return true
  545. }
  546. return utils.IsStringInSlice(permission, perms)
  547. }
  548. // HasPerms return true if the user has all the given permissions
  549. func (u *User) HasPerms(permissions []string, path string) bool {
  550. perms := u.GetPermissionsForPath(path)
  551. if utils.IsStringInSlice(PermAny, perms) {
  552. return true
  553. }
  554. for _, permission := range permissions {
  555. if !utils.IsStringInSlice(permission, perms) {
  556. return false
  557. }
  558. }
  559. return true
  560. }
  561. // HasNoQuotaRestrictions returns true if no quota restrictions need to be applyed
  562. func (u *User) HasNoQuotaRestrictions(checkFiles bool) bool {
  563. if u.QuotaSize == 0 && (!checkFiles || u.QuotaFiles == 0) {
  564. return true
  565. }
  566. return false
  567. }
  568. // IsLoginMethodAllowed returns true if the specified login method is allowed
  569. func (u *User) IsLoginMethodAllowed(loginMethod string, partialSuccessMethods []string) bool {
  570. if len(u.Filters.DeniedLoginMethods) == 0 {
  571. return true
  572. }
  573. if len(partialSuccessMethods) == 1 {
  574. for _, method := range u.GetNextAuthMethods(partialSuccessMethods, true) {
  575. if method == loginMethod {
  576. return true
  577. }
  578. }
  579. }
  580. if utils.IsStringInSlice(loginMethod, u.Filters.DeniedLoginMethods) {
  581. return false
  582. }
  583. return true
  584. }
  585. // GetNextAuthMethods returns the list of authentications methods that
  586. // can continue for multi-step authentication
  587. func (u *User) GetNextAuthMethods(partialSuccessMethods []string, isPasswordAuthEnabled bool) []string {
  588. var methods []string
  589. if len(partialSuccessMethods) != 1 {
  590. return methods
  591. }
  592. if partialSuccessMethods[0] != SSHLoginMethodPublicKey {
  593. return methods
  594. }
  595. for _, method := range u.GetAllowedLoginMethods() {
  596. if method == SSHLoginMethodKeyAndPassword && isPasswordAuthEnabled {
  597. methods = append(methods, LoginMethodPassword)
  598. }
  599. if method == SSHLoginMethodKeyAndKeyboardInt {
  600. methods = append(methods, SSHLoginMethodKeyboardInteractive)
  601. }
  602. }
  603. return methods
  604. }
  605. // IsPartialAuth returns true if the specified login method is a step for
  606. // a multi-step Authentication.
  607. // We support publickey+password and publickey+keyboard-interactive, so
  608. // only publickey can returns partial success.
  609. // We can have partial success if only multi-step Auth methods are enabled
  610. func (u *User) IsPartialAuth(loginMethod string) bool {
  611. if loginMethod != SSHLoginMethodPublicKey {
  612. return false
  613. }
  614. for _, method := range u.GetAllowedLoginMethods() {
  615. if method == LoginMethodTLSCertificate || method == LoginMethodTLSCertificateAndPwd {
  616. continue
  617. }
  618. if !utils.IsStringInSlice(method, SSHMultiStepsLoginMethods) {
  619. return false
  620. }
  621. }
  622. return true
  623. }
  624. // GetAllowedLoginMethods returns the allowed login methods
  625. func (u *User) GetAllowedLoginMethods() []string {
  626. var allowedMethods []string
  627. for _, method := range ValidLoginMethods {
  628. if !utils.IsStringInSlice(method, u.Filters.DeniedLoginMethods) {
  629. allowedMethods = append(allowedMethods, method)
  630. }
  631. }
  632. return allowedMethods
  633. }
  634. // IsFileAllowed returns true if the specified file is allowed by the file restrictions filters
  635. func (u *User) IsFileAllowed(virtualPath string) bool {
  636. return u.isFilePatternAllowed(virtualPath) && u.isFileExtensionAllowed(virtualPath)
  637. }
  638. func (u *User) isFileExtensionAllowed(virtualPath string) bool {
  639. if len(u.Filters.FileExtensions) == 0 {
  640. return true
  641. }
  642. dirsForPath := utils.GetDirsForVirtualPath(path.Dir(virtualPath))
  643. var filter ExtensionsFilter
  644. for _, dir := range dirsForPath {
  645. for _, f := range u.Filters.FileExtensions {
  646. if f.Path == dir {
  647. filter = f
  648. break
  649. }
  650. }
  651. if filter.Path != "" {
  652. break
  653. }
  654. }
  655. if filter.Path != "" {
  656. toMatch := strings.ToLower(virtualPath)
  657. for _, denied := range filter.DeniedExtensions {
  658. if strings.HasSuffix(toMatch, denied) {
  659. return false
  660. }
  661. }
  662. for _, allowed := range filter.AllowedExtensions {
  663. if strings.HasSuffix(toMatch, allowed) {
  664. return true
  665. }
  666. }
  667. return len(filter.AllowedExtensions) == 0
  668. }
  669. return true
  670. }
  671. func (u *User) isFilePatternAllowed(virtualPath string) bool {
  672. if len(u.Filters.FilePatterns) == 0 {
  673. return true
  674. }
  675. dirsForPath := utils.GetDirsForVirtualPath(path.Dir(virtualPath))
  676. var filter PatternsFilter
  677. for _, dir := range dirsForPath {
  678. for _, f := range u.Filters.FilePatterns {
  679. if f.Path == dir {
  680. filter = f
  681. break
  682. }
  683. }
  684. if filter.Path != "" {
  685. break
  686. }
  687. }
  688. if filter.Path != "" {
  689. toMatch := strings.ToLower(path.Base(virtualPath))
  690. for _, denied := range filter.DeniedPatterns {
  691. matched, err := path.Match(denied, toMatch)
  692. if err != nil || matched {
  693. return false
  694. }
  695. }
  696. for _, allowed := range filter.AllowedPatterns {
  697. matched, err := path.Match(allowed, toMatch)
  698. if err == nil && matched {
  699. return true
  700. }
  701. }
  702. return len(filter.AllowedPatterns) == 0
  703. }
  704. return true
  705. }
  706. // IsLoginFromAddrAllowed returns true if the login is allowed from the specified remoteAddr.
  707. // If AllowedIP is defined only the specified IP/Mask can login.
  708. // If DeniedIP is defined the specified IP/Mask cannot login.
  709. // If an IP is both allowed and denied then login will be denied
  710. func (u *User) IsLoginFromAddrAllowed(remoteAddr string) bool {
  711. if len(u.Filters.AllowedIP) == 0 && len(u.Filters.DeniedIP) == 0 {
  712. return true
  713. }
  714. remoteIP := net.ParseIP(utils.GetIPFromRemoteAddress(remoteAddr))
  715. // if remoteIP is invalid we allow login, this should never happen
  716. if remoteIP == nil {
  717. logger.Warn(logSender, "", "login allowed for invalid IP. remote address: %#v", remoteAddr)
  718. return true
  719. }
  720. for _, IPMask := range u.Filters.DeniedIP {
  721. _, IPNet, err := net.ParseCIDR(IPMask)
  722. if err != nil {
  723. return false
  724. }
  725. if IPNet.Contains(remoteIP) {
  726. return false
  727. }
  728. }
  729. for _, IPMask := range u.Filters.AllowedIP {
  730. _, IPNet, err := net.ParseCIDR(IPMask)
  731. if err != nil {
  732. return false
  733. }
  734. if IPNet.Contains(remoteIP) {
  735. return true
  736. }
  737. }
  738. return len(u.Filters.AllowedIP) == 0
  739. }
  740. // GetPermissionsAsJSON returns the permissions as json byte array
  741. func (u *User) GetPermissionsAsJSON() ([]byte, error) {
  742. return json.Marshal(u.Permissions)
  743. }
  744. // GetPublicKeysAsJSON returns the public keys as json byte array
  745. func (u *User) GetPublicKeysAsJSON() ([]byte, error) {
  746. return json.Marshal(u.PublicKeys)
  747. }
  748. // GetFiltersAsJSON returns the filters as json byte array
  749. func (u *User) GetFiltersAsJSON() ([]byte, error) {
  750. return json.Marshal(u.Filters)
  751. }
  752. // GetFsConfigAsJSON returns the filesystem config as json byte array
  753. func (u *User) GetFsConfigAsJSON() ([]byte, error) {
  754. return json.Marshal(u.FsConfig)
  755. }
  756. // GetUID returns a validate uid, suitable for use with os.Chown
  757. func (u *User) GetUID() int {
  758. if u.UID <= 0 || u.UID > 65535 {
  759. return -1
  760. }
  761. return u.UID
  762. }
  763. // GetGID returns a validate gid, suitable for use with os.Chown
  764. func (u *User) GetGID() int {
  765. if u.GID <= 0 || u.GID > 65535 {
  766. return -1
  767. }
  768. return u.GID
  769. }
  770. // GetHomeDir returns the shortest path name equivalent to the user's home directory
  771. func (u *User) GetHomeDir() string {
  772. return filepath.Clean(u.HomeDir)
  773. }
  774. // HasQuotaRestrictions returns true if there is a quota restriction on number of files or size or both
  775. func (u *User) HasQuotaRestrictions() bool {
  776. return u.QuotaFiles > 0 || u.QuotaSize > 0
  777. }
  778. // GetQuotaSummary returns used quota and limits if defined
  779. func (u *User) GetQuotaSummary() string {
  780. var result string
  781. result = "Files: " + strconv.Itoa(u.UsedQuotaFiles)
  782. if u.QuotaFiles > 0 {
  783. result += "/" + strconv.Itoa(u.QuotaFiles)
  784. }
  785. if u.UsedQuotaSize > 0 || u.QuotaSize > 0 {
  786. result += ". Size: " + utils.ByteCountIEC(u.UsedQuotaSize)
  787. if u.QuotaSize > 0 {
  788. result += "/" + utils.ByteCountIEC(u.QuotaSize)
  789. }
  790. }
  791. return result
  792. }
  793. // GetPermissionsAsString returns the user's permissions as comma separated string
  794. func (u *User) GetPermissionsAsString() string {
  795. result := ""
  796. for dir, perms := range u.Permissions {
  797. dirPerms := strings.Join(perms, ", ")
  798. dp := fmt.Sprintf("%#v: %#v", dir, dirPerms)
  799. if dir == "/" {
  800. if result != "" {
  801. result = dp + ", " + result
  802. } else {
  803. result = dp
  804. }
  805. } else {
  806. if result != "" {
  807. result += ", "
  808. }
  809. result += dp
  810. }
  811. }
  812. return result
  813. }
  814. // GetBandwidthAsString returns bandwidth limits if defines
  815. func (u *User) GetBandwidthAsString() string {
  816. result := "Download: "
  817. if u.DownloadBandwidth > 0 {
  818. result += utils.ByteCountIEC(u.DownloadBandwidth*1000) + "/s."
  819. } else {
  820. result += "unlimited."
  821. }
  822. result += " Upload: "
  823. if u.UploadBandwidth > 0 {
  824. result += utils.ByteCountIEC(u.UploadBandwidth*1000) + "/s."
  825. } else {
  826. result += "unlimited."
  827. }
  828. return result
  829. }
  830. // GetInfoString returns user's info as string.
  831. // Storage provider, number of public keys, max sessions, uid,
  832. // gid, denied and allowed IP/Mask are returned
  833. func (u *User) GetInfoString() string {
  834. var result string
  835. if u.LastLogin > 0 {
  836. t := utils.GetTimeFromMsecSinceEpoch(u.LastLogin)
  837. result += fmt.Sprintf("Last login: %v ", t.Format("2006-01-02 15:04:05")) // YYYY-MM-DD HH:MM:SS
  838. }
  839. switch u.FsConfig.Provider {
  840. case vfs.S3FilesystemProvider:
  841. result += "Storage: S3 "
  842. case vfs.GCSFilesystemProvider:
  843. result += "Storage: GCS "
  844. case vfs.AzureBlobFilesystemProvider:
  845. result += "Storage: Azure "
  846. case vfs.CryptedFilesystemProvider:
  847. result += "Storage: Encrypted "
  848. case vfs.SFTPFilesystemProvider:
  849. result += "Storage: SFTP "
  850. }
  851. if len(u.PublicKeys) > 0 {
  852. result += fmt.Sprintf("Public keys: %v ", len(u.PublicKeys))
  853. }
  854. if u.MaxSessions > 0 {
  855. result += fmt.Sprintf("Max sessions: %v ", u.MaxSessions)
  856. }
  857. if u.UID > 0 {
  858. result += fmt.Sprintf("UID: %v ", u.UID)
  859. }
  860. if u.GID > 0 {
  861. result += fmt.Sprintf("GID: %v ", u.GID)
  862. }
  863. if len(u.Filters.DeniedIP) > 0 {
  864. result += fmt.Sprintf("Denied IP/Mask: %v ", len(u.Filters.DeniedIP))
  865. }
  866. if len(u.Filters.AllowedIP) > 0 {
  867. result += fmt.Sprintf("Allowed IP/Mask: %v ", len(u.Filters.AllowedIP))
  868. }
  869. return result
  870. }
  871. // GetExpirationDateAsString returns expiration date formatted as YYYY-MM-DD
  872. func (u *User) GetExpirationDateAsString() string {
  873. if u.ExpirationDate > 0 {
  874. t := utils.GetTimeFromMsecSinceEpoch(u.ExpirationDate)
  875. return t.Format("2006-01-02")
  876. }
  877. return ""
  878. }
  879. // GetAllowedIPAsString returns the allowed IP as comma separated string
  880. func (u *User) GetAllowedIPAsString() string {
  881. return strings.Join(u.Filters.AllowedIP, ",")
  882. }
  883. // GetDeniedIPAsString returns the denied IP as comma separated string
  884. func (u *User) GetDeniedIPAsString() string {
  885. return strings.Join(u.Filters.DeniedIP, ",")
  886. }
  887. // SetEmptySecretsIfNil sets the secrets to empty if nil
  888. func (u *User) SetEmptySecretsIfNil() {
  889. u.FsConfig.SetEmptySecretsIfNil()
  890. for idx := range u.VirtualFolders {
  891. vfolder := &u.VirtualFolders[idx]
  892. vfolder.FsConfig.SetEmptySecretsIfNil()
  893. }
  894. }
  895. func (u *User) getACopy() User {
  896. u.SetEmptySecretsIfNil()
  897. pubKeys := make([]string, len(u.PublicKeys))
  898. copy(pubKeys, u.PublicKeys)
  899. virtualFolders := make([]vfs.VirtualFolder, 0, len(u.VirtualFolders))
  900. for idx := range u.VirtualFolders {
  901. vfolder := u.VirtualFolders[idx].GetACopy()
  902. virtualFolders = append(virtualFolders, vfolder)
  903. }
  904. permissions := make(map[string][]string)
  905. for k, v := range u.Permissions {
  906. perms := make([]string, len(v))
  907. copy(perms, v)
  908. permissions[k] = perms
  909. }
  910. filters := UserFilters{}
  911. filters.MaxUploadFileSize = u.Filters.MaxUploadFileSize
  912. filters.TLSUsername = u.Filters.TLSUsername
  913. filters.AllowedIP = make([]string, len(u.Filters.AllowedIP))
  914. copy(filters.AllowedIP, u.Filters.AllowedIP)
  915. filters.DeniedIP = make([]string, len(u.Filters.DeniedIP))
  916. copy(filters.DeniedIP, u.Filters.DeniedIP)
  917. filters.DeniedLoginMethods = make([]string, len(u.Filters.DeniedLoginMethods))
  918. copy(filters.DeniedLoginMethods, u.Filters.DeniedLoginMethods)
  919. filters.FileExtensions = make([]ExtensionsFilter, len(u.Filters.FileExtensions))
  920. copy(filters.FileExtensions, u.Filters.FileExtensions)
  921. filters.FilePatterns = make([]PatternsFilter, len(u.Filters.FilePatterns))
  922. copy(filters.FilePatterns, u.Filters.FilePatterns)
  923. filters.DeniedProtocols = make([]string, len(u.Filters.DeniedProtocols))
  924. copy(filters.DeniedProtocols, u.Filters.DeniedProtocols)
  925. return User{
  926. ID: u.ID,
  927. Username: u.Username,
  928. Password: u.Password,
  929. PublicKeys: pubKeys,
  930. HomeDir: u.HomeDir,
  931. VirtualFolders: virtualFolders,
  932. UID: u.UID,
  933. GID: u.GID,
  934. MaxSessions: u.MaxSessions,
  935. QuotaSize: u.QuotaSize,
  936. QuotaFiles: u.QuotaFiles,
  937. Permissions: permissions,
  938. UsedQuotaSize: u.UsedQuotaSize,
  939. UsedQuotaFiles: u.UsedQuotaFiles,
  940. LastQuotaUpdate: u.LastQuotaUpdate,
  941. UploadBandwidth: u.UploadBandwidth,
  942. DownloadBandwidth: u.DownloadBandwidth,
  943. Status: u.Status,
  944. ExpirationDate: u.ExpirationDate,
  945. LastLogin: u.LastLogin,
  946. Filters: filters,
  947. FsConfig: u.FsConfig.GetACopy(),
  948. AdditionalInfo: u.AdditionalInfo,
  949. Description: u.Description,
  950. }
  951. }
  952. func (u *User) getNotificationFieldsAsSlice(action string) []string {
  953. return []string{action, u.Username,
  954. strconv.FormatInt(u.ID, 10),
  955. strconv.FormatInt(int64(u.Status), 10),
  956. strconv.FormatInt(u.ExpirationDate, 10),
  957. u.HomeDir,
  958. strconv.FormatInt(int64(u.UID), 10),
  959. strconv.FormatInt(int64(u.GID), 10),
  960. }
  961. }
  962. // GetEncrytionAdditionalData returns the additional data to use for AEAD
  963. func (u *User) GetEncrytionAdditionalData() string {
  964. return u.Username
  965. }
  966. // GetGCSCredentialsFilePath returns the path for GCS credentials
  967. func (u *User) GetGCSCredentialsFilePath() string {
  968. return filepath.Join(credentialsDirPath, fmt.Sprintf("%v_gcs_credentials.json", u.Username))
  969. }