user.go 36 KB

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