user.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. package dataprovider
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "path/filepath"
  6. "strconv"
  7. "github.com/drakkan/sftpgo/utils"
  8. )
  9. // Available permissions for SFTP users
  10. const (
  11. // All permissions are granted
  12. PermAny = "*"
  13. // List items such as files and directories is allowed
  14. PermListItems = "list"
  15. // download files is allowed
  16. PermDownload = "download"
  17. // upload files is allowed
  18. PermUpload = "upload"
  19. // overwrite an existing file, while uploading, is allowed
  20. // upload permission is required to allow file overwrite
  21. PermOverwrite = "overwrite"
  22. // delete files or directories is allowed
  23. PermDelete = "delete"
  24. // rename files or directories is allowed
  25. PermRename = "rename"
  26. // create directories is allowed
  27. PermCreateDirs = "create_dirs"
  28. // create symbolic links is allowed
  29. PermCreateSymlinks = "create_symlinks"
  30. // changing file or directory permissions is allowed
  31. PermChmod = "chmod"
  32. // changing file or directory owner and group is allowed
  33. PermChown = "chown"
  34. // changing file or directory access and modification time is allowed
  35. PermChtimes = "chtimes"
  36. )
  37. // User defines an SFTP user
  38. type User struct {
  39. // Database unique identifier
  40. ID int64 `json:"id"`
  41. // 1 enabled, 0 disabled (login is not allowed)
  42. Status int `json:"status"`
  43. // Username
  44. Username string `json:"username"`
  45. // Account expiration date as unix timestamp in milliseconds. An expired account cannot login.
  46. // 0 means no expiration
  47. ExpirationDate int64 `json:"expiration_date"`
  48. // Password used for password authentication.
  49. // For users created using SFTPGo REST API the password is be stored using argon2id hashing algo.
  50. // Checking passwords stored with bcrypt, pbkdf2 and sha512crypt is supported too.
  51. Password string `json:"password,omitempty"`
  52. // PublicKeys used for public key authentication. At least one between password and a public key is mandatory
  53. PublicKeys []string `json:"public_keys,omitempty"`
  54. // The user cannot upload or download files outside this directory. Must be an absolute path
  55. HomeDir string `json:"home_dir"`
  56. // If sftpgo runs as root system user then the created files and directories will be assigned to this system UID
  57. UID int `json:"uid"`
  58. // If sftpgo runs as root system user then the created files and directories will be assigned to this system GID
  59. GID int `json:"gid"`
  60. // Maximum concurrent sessions. 0 means unlimited
  61. MaxSessions int `json:"max_sessions"`
  62. // Maximum size allowed as bytes. 0 means unlimited
  63. QuotaSize int64 `json:"quota_size"`
  64. // Maximum number of files allowed. 0 means unlimited
  65. QuotaFiles int `json:"quota_files"`
  66. // List of the granted permissions
  67. Permissions []string `json:"permissions"`
  68. // Used quota as bytes
  69. UsedQuotaSize int64 `json:"used_quota_size"`
  70. // Used quota as number of files
  71. UsedQuotaFiles int `json:"used_quota_files"`
  72. // Last quota update as unix timestamp in milliseconds
  73. LastQuotaUpdate int64 `json:"last_quota_update"`
  74. // Maximum upload bandwidth as KB/s, 0 means unlimited
  75. UploadBandwidth int64 `json:"upload_bandwidth"`
  76. // Maximum download bandwidth as KB/s, 0 means unlimited
  77. DownloadBandwidth int64 `json:"download_bandwidth"`
  78. // Last login as unix timestamp in milliseconds
  79. LastLogin int64 `json:"last_login"`
  80. }
  81. // HasPerm returns true if the user has the given permission or any permission
  82. func (u *User) HasPerm(permission string) bool {
  83. if utils.IsStringInSlice(PermAny, u.Permissions) {
  84. return true
  85. }
  86. return utils.IsStringInSlice(permission, u.Permissions)
  87. }
  88. // GetPermissionsAsJSON returns the permissions as json byte array
  89. func (u *User) GetPermissionsAsJSON() ([]byte, error) {
  90. return json.Marshal(u.Permissions)
  91. }
  92. // GetPublicKeysAsJSON returns the public keys as json byte array
  93. func (u *User) GetPublicKeysAsJSON() ([]byte, error) {
  94. return json.Marshal(u.PublicKeys)
  95. }
  96. // GetUID returns a validate uid, suitable for use with os.Chown
  97. func (u *User) GetUID() int {
  98. if u.UID <= 0 || u.UID > 65535 {
  99. return -1
  100. }
  101. return u.UID
  102. }
  103. // GetGID returns a validate gid, suitable for use with os.Chown
  104. func (u *User) GetGID() int {
  105. if u.GID <= 0 || u.GID > 65535 {
  106. return -1
  107. }
  108. return u.GID
  109. }
  110. // GetHomeDir returns the shortest path name equivalent to the user's home directory
  111. func (u *User) GetHomeDir() string {
  112. return filepath.Clean(u.HomeDir)
  113. }
  114. // HasQuotaRestrictions returns true if there is a quota restriction on number of files or size or both
  115. func (u *User) HasQuotaRestrictions() bool {
  116. return u.QuotaFiles > 0 || u.QuotaSize > 0
  117. }
  118. // GetRelativePath returns the path for a file relative to the user's home dir.
  119. // This is the path as seen by SFTP users
  120. func (u *User) GetRelativePath(path string) string {
  121. rel, err := filepath.Rel(u.GetHomeDir(), path)
  122. if err != nil {
  123. return ""
  124. }
  125. return "/" + filepath.ToSlash(rel)
  126. }
  127. // GetQuotaSummary returns used quota and limits if defined
  128. func (u *User) GetQuotaSummary() string {
  129. var result string
  130. result = "Files: " + strconv.Itoa(u.UsedQuotaFiles)
  131. if u.QuotaFiles > 0 {
  132. result += "/" + strconv.Itoa(u.QuotaFiles)
  133. }
  134. if u.UsedQuotaSize > 0 || u.QuotaSize > 0 {
  135. result += ". Size: " + utils.ByteCountSI(u.UsedQuotaSize)
  136. if u.QuotaSize > 0 {
  137. result += "/" + utils.ByteCountSI(u.QuotaSize)
  138. }
  139. }
  140. return result
  141. }
  142. // GetPermissionsAsString returns the user's permissions as comma separated string
  143. func (u *User) GetPermissionsAsString() string {
  144. var result string
  145. for _, p := range u.Permissions {
  146. if len(result) > 0 {
  147. result += ", "
  148. }
  149. result += p
  150. }
  151. return result
  152. }
  153. // GetBandwidthAsString returns bandwidth limits if defines
  154. func (u *User) GetBandwidthAsString() string {
  155. result := "Download: "
  156. if u.DownloadBandwidth > 0 {
  157. result += utils.ByteCountSI(u.DownloadBandwidth*1000) + "/s."
  158. } else {
  159. result += "ulimited."
  160. }
  161. result += " Upload: "
  162. if u.UploadBandwidth > 0 {
  163. result += utils.ByteCountSI(u.UploadBandwidth*1000) + "/s."
  164. } else {
  165. result += "ulimited."
  166. }
  167. return result
  168. }
  169. // GetInfoString returns user's info as string.
  170. // Number of public keys, max sessions, uid and gid are returned
  171. func (u *User) GetInfoString() string {
  172. var result string
  173. if u.LastLogin > 0 {
  174. t := utils.GetTimeFromMsecSinceEpoch(u.LastLogin)
  175. result += fmt.Sprintf("Last login: %v ", t.Format("2006-01-02 15:04:05")) // YYYY-MM-DD HH:MM:SS
  176. }
  177. if len(u.PublicKeys) > 0 {
  178. result += fmt.Sprintf("Public keys: %v ", len(u.PublicKeys))
  179. }
  180. if u.MaxSessions > 0 {
  181. result += fmt.Sprintf("Max sessions: %v ", u.MaxSessions)
  182. }
  183. if u.UID > 0 {
  184. result += fmt.Sprintf("UID: %v ", u.UID)
  185. }
  186. if u.GID > 0 {
  187. result += fmt.Sprintf("GID: %v ", u.GID)
  188. }
  189. return result
  190. }
  191. // GetExpirationDateAsString returns expiration date formatted as YYYY-MM-DD
  192. func (u *User) GetExpirationDateAsString() string {
  193. if u.ExpirationDate > 0 {
  194. t := utils.GetTimeFromMsecSinceEpoch(u.ExpirationDate)
  195. return t.Format("2006-01-02")
  196. }
  197. return ""
  198. }
  199. func (u *User) getACopy() User {
  200. pubKeys := make([]string, len(u.PublicKeys))
  201. copy(pubKeys, u.PublicKeys)
  202. permissions := make([]string, len(u.Permissions))
  203. copy(permissions, u.Permissions)
  204. return User{
  205. ID: u.ID,
  206. Username: u.Username,
  207. Password: u.Password,
  208. PublicKeys: pubKeys,
  209. HomeDir: u.HomeDir,
  210. UID: u.UID,
  211. GID: u.GID,
  212. MaxSessions: u.MaxSessions,
  213. QuotaSize: u.QuotaSize,
  214. QuotaFiles: u.QuotaFiles,
  215. Permissions: permissions,
  216. UsedQuotaSize: u.UsedQuotaSize,
  217. UsedQuotaFiles: u.UsedQuotaFiles,
  218. LastQuotaUpdate: u.LastQuotaUpdate,
  219. UploadBandwidth: u.UploadBandwidth,
  220. DownloadBandwidth: u.DownloadBandwidth,
  221. Status: u.Status,
  222. ExpirationDate: u.ExpirationDate,
  223. LastLogin: u.LastLogin,
  224. }
  225. }
  226. func (u *User) getNotificationFieldsAsSlice() []string {
  227. return []string{u.Username,
  228. strconv.FormatInt(u.ID, 10),
  229. strconv.FormatInt(int64(u.Status), 10),
  230. strconv.FormatInt(int64(u.ExpirationDate), 10),
  231. u.HomeDir,
  232. strconv.FormatInt(int64(u.UID), 10),
  233. strconv.FormatInt(int64(u.GID), 10),
  234. }
  235. }