user.go 31 KB

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