admin.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. // Copyright (C) 2019 Nicola Murino
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Affero General Public License as published
  5. // by the Free Software Foundation, version 3.
  6. //
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU Affero General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU Affero General Public License
  13. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. package dataprovider
  15. import (
  16. "encoding/json"
  17. "errors"
  18. "fmt"
  19. "net"
  20. "os"
  21. "sort"
  22. "strconv"
  23. "strings"
  24. "github.com/alexedwards/argon2id"
  25. "github.com/sftpgo/sdk"
  26. passwordvalidator "github.com/wagslane/go-password-validator"
  27. "golang.org/x/crypto/bcrypt"
  28. "github.com/drakkan/sftpgo/v2/internal/kms"
  29. "github.com/drakkan/sftpgo/v2/internal/logger"
  30. "github.com/drakkan/sftpgo/v2/internal/mfa"
  31. "github.com/drakkan/sftpgo/v2/internal/util"
  32. )
  33. // Available permissions for SFTPGo admins
  34. const (
  35. PermAdminAny = "*"
  36. PermAdminAddUsers = "add_users"
  37. PermAdminChangeUsers = "edit_users"
  38. PermAdminDeleteUsers = "del_users"
  39. PermAdminViewUsers = "view_users"
  40. PermAdminViewConnections = "view_conns"
  41. PermAdminCloseConnections = "close_conns"
  42. PermAdminViewServerStatus = "view_status"
  43. PermAdminManageAdmins = "manage_admins"
  44. PermAdminManageGroups = "manage_groups"
  45. PermAdminManageFolders = "manage_folders"
  46. PermAdminManageAPIKeys = "manage_apikeys"
  47. PermAdminQuotaScans = "quota_scans"
  48. PermAdminManageSystem = "manage_system"
  49. PermAdminManageDefender = "manage_defender"
  50. PermAdminViewDefender = "view_defender"
  51. PermAdminRetentionChecks = "retention_checks"
  52. PermAdminViewEvents = "view_events"
  53. PermAdminManageEventRules = "manage_event_rules"
  54. PermAdminManageRoles = "manage_roles"
  55. PermAdminManageIPLists = "manage_ip_lists"
  56. )
  57. const (
  58. // GroupAddToUsersAsMembership defines that the admin's group will be added as membership group for new users
  59. GroupAddToUsersAsMembership = iota
  60. // GroupAddToUsersAsPrimary defines that the admin's group will be added as primary group for new users
  61. GroupAddToUsersAsPrimary
  62. // GroupAddToUsersAsSecondary defines that the admin's group will be added as secondary group for new users
  63. GroupAddToUsersAsSecondary
  64. )
  65. var (
  66. validAdminPerms = []string{PermAdminAny, PermAdminAddUsers, PermAdminChangeUsers, PermAdminDeleteUsers,
  67. PermAdminViewUsers, PermAdminManageFolders, PermAdminManageGroups, PermAdminViewConnections,
  68. PermAdminCloseConnections, PermAdminViewServerStatus, PermAdminManageAdmins, PermAdminManageRoles,
  69. PermAdminManageEventRules, PermAdminManageAPIKeys, PermAdminQuotaScans, PermAdminManageSystem,
  70. PermAdminManageDefender, PermAdminViewDefender, PermAdminManageIPLists, PermAdminRetentionChecks,
  71. PermAdminViewEvents}
  72. forbiddenPermsForRoleAdmins = []string{PermAdminAny, PermAdminManageAdmins, PermAdminManageSystem,
  73. PermAdminManageEventRules, PermAdminManageIPLists, PermAdminManageRoles}
  74. )
  75. // AdminTOTPConfig defines the time-based one time password configuration
  76. type AdminTOTPConfig struct {
  77. Enabled bool `json:"enabled,omitempty"`
  78. ConfigName string `json:"config_name,omitempty"`
  79. Secret *kms.Secret `json:"secret,omitempty"`
  80. }
  81. func (c *AdminTOTPConfig) validate(username string) error {
  82. if !c.Enabled {
  83. c.ConfigName = ""
  84. c.Secret = kms.NewEmptySecret()
  85. return nil
  86. }
  87. if c.ConfigName == "" {
  88. return util.NewValidationError("totp: config name is mandatory")
  89. }
  90. if !util.Contains(mfa.GetAvailableTOTPConfigNames(), c.ConfigName) {
  91. return util.NewValidationError(fmt.Sprintf("totp: config name %q not found", c.ConfigName))
  92. }
  93. if c.Secret.IsEmpty() {
  94. return util.NewValidationError("totp: secret is mandatory")
  95. }
  96. if c.Secret.IsPlain() {
  97. c.Secret.SetAdditionalData(username)
  98. if err := c.Secret.Encrypt(); err != nil {
  99. return util.NewValidationError(fmt.Sprintf("totp: unable to encrypt secret: %v", err))
  100. }
  101. }
  102. return nil
  103. }
  104. // AdminPreferences defines the admin preferences
  105. type AdminPreferences struct {
  106. // Allow to hide some sections from the user page.
  107. // These are not security settings and are not enforced server side
  108. // in any way. They are only intended to simplify the user page in
  109. // the WebAdmin UI.
  110. //
  111. // 1 means hide groups section
  112. // 2 means hide filesystem section, "users_base_dir" must be set in the config file otherwise this setting is ignored
  113. // 4 means hide virtual folders section
  114. // 8 means hide profile section
  115. // 16 means hide ACLs section
  116. // 32 means hide disk and bandwidth quota limits section
  117. // 64 means hide advanced settings section
  118. //
  119. // The settings can be combined
  120. HideUserPageSections int `json:"hide_user_page_sections,omitempty"`
  121. // Defines the default expiration for newly created users as number of days.
  122. // 0 means no expiration
  123. DefaultUsersExpiration int `json:"default_users_expiration,omitempty"`
  124. }
  125. // HideGroups returns true if the groups section should be hidden
  126. func (p *AdminPreferences) HideGroups() bool {
  127. return p.HideUserPageSections&1 != 0
  128. }
  129. // HideFilesystem returns true if the filesystem section should be hidden
  130. func (p *AdminPreferences) HideFilesystem() bool {
  131. return config.UsersBaseDir != "" && p.HideUserPageSections&2 != 0
  132. }
  133. // HideVirtualFolders returns true if the virtual folder section should be hidden
  134. func (p *AdminPreferences) HideVirtualFolders() bool {
  135. return p.HideUserPageSections&4 != 0
  136. }
  137. // HideProfile returns true if the profile section should be hidden
  138. func (p *AdminPreferences) HideProfile() bool {
  139. return p.HideUserPageSections&8 != 0
  140. }
  141. // HideACLs returns true if the ACLs section should be hidden
  142. func (p *AdminPreferences) HideACLs() bool {
  143. return p.HideUserPageSections&16 != 0
  144. }
  145. // HideDiskQuotaAndBandwidthLimits returns true if the disk quota and bandwidth limits
  146. // section should be hidden
  147. func (p *AdminPreferences) HideDiskQuotaAndBandwidthLimits() bool {
  148. return p.HideUserPageSections&32 != 0
  149. }
  150. // HideAdvancedSettings returns true if the advanced settings section should be hidden
  151. func (p *AdminPreferences) HideAdvancedSettings() bool {
  152. return p.HideUserPageSections&64 != 0
  153. }
  154. // VisibleUserPageSections returns the number of visible sections
  155. // in the user page
  156. func (p *AdminPreferences) VisibleUserPageSections() int {
  157. var result int
  158. if !p.HideProfile() {
  159. result++
  160. }
  161. if !p.HideACLs() {
  162. result++
  163. }
  164. if !p.HideDiskQuotaAndBandwidthLimits() {
  165. result++
  166. }
  167. if !p.HideAdvancedSettings() {
  168. result++
  169. }
  170. return result
  171. }
  172. // AdminFilters defines additional restrictions for SFTPGo admins
  173. // TODO: rename to AdminOptions in v3
  174. type AdminFilters struct {
  175. // only clients connecting from these IP/Mask are allowed.
  176. // IP/Mask must be in CIDR notation as defined in RFC 4632 and RFC 4291
  177. // for example "192.0.2.0/24" or "2001:db8::/32"
  178. AllowList []string `json:"allow_list,omitempty"`
  179. // API key auth allows to impersonate this administrator with an API key
  180. AllowAPIKeyAuth bool `json:"allow_api_key_auth,omitempty"`
  181. // A password change is required at the next login
  182. RequirePasswordChange bool `json:"require_password_change,omitempty"`
  183. // Require two factor authentication
  184. RequireTwoFactor bool `json:"require_two_factor"`
  185. // Time-based one time passwords configuration
  186. TOTPConfig AdminTOTPConfig `json:"totp_config,omitempty"`
  187. // Recovery codes to use if the user loses access to their second factor auth device.
  188. // Each code can only be used once, you should use these codes to login and disable or
  189. // reset 2FA for your account
  190. RecoveryCodes []RecoveryCode `json:"recovery_codes,omitempty"`
  191. Preferences AdminPreferences `json:"preferences"`
  192. }
  193. // AdminGroupMappingOptions defines the options for admin/group mapping
  194. type AdminGroupMappingOptions struct {
  195. AddToUsersAs int `json:"add_to_users_as,omitempty"`
  196. }
  197. func (o *AdminGroupMappingOptions) validate() error {
  198. if o.AddToUsersAs < GroupAddToUsersAsMembership || o.AddToUsersAs > GroupAddToUsersAsSecondary {
  199. return util.NewValidationError(fmt.Sprintf("Invalid mode to add groups to new users: %d", o.AddToUsersAs))
  200. }
  201. return nil
  202. }
  203. // GetUserGroupType returns the type for the matching user group
  204. func (o *AdminGroupMappingOptions) GetUserGroupType() int {
  205. switch o.AddToUsersAs {
  206. case GroupAddToUsersAsPrimary:
  207. return sdk.GroupTypePrimary
  208. case GroupAddToUsersAsSecondary:
  209. return sdk.GroupTypeSecondary
  210. default:
  211. return sdk.GroupTypeMembership
  212. }
  213. }
  214. // AdminGroupMapping defines the mapping between an SFTPGo admin and a group
  215. type AdminGroupMapping struct {
  216. Name string `json:"name"`
  217. Options AdminGroupMappingOptions `json:"options"`
  218. }
  219. // Admin defines a SFTPGo admin
  220. type Admin struct {
  221. // Database unique identifier
  222. ID int64 `json:"id"`
  223. // 1 enabled, 0 disabled (login is not allowed)
  224. Status int `json:"status"`
  225. // Username
  226. Username string `json:"username"`
  227. Password string `json:"password,omitempty"`
  228. Email string `json:"email,omitempty"`
  229. Permissions []string `json:"permissions"`
  230. Filters AdminFilters `json:"filters,omitempty"`
  231. Description string `json:"description,omitempty"`
  232. AdditionalInfo string `json:"additional_info,omitempty"`
  233. // Groups membership
  234. Groups []AdminGroupMapping `json:"groups,omitempty"`
  235. // Creation time as unix timestamp in milliseconds. It will be 0 for admins created before v2.2.0
  236. CreatedAt int64 `json:"created_at"`
  237. // last update time as unix timestamp in milliseconds
  238. UpdatedAt int64 `json:"updated_at"`
  239. // Last login as unix timestamp in milliseconds
  240. LastLogin int64 `json:"last_login"`
  241. // Role name. If set the admin can only administer users with the same role.
  242. // Role admins cannot have the following permissions:
  243. // - manage_admins
  244. // - manage_apikeys
  245. // - manage_system
  246. // - manage_event_rules
  247. // - manage_roles
  248. Role string `json:"role,omitempty"`
  249. }
  250. // CountUnusedRecoveryCodes returns the number of unused recovery codes
  251. func (a *Admin) CountUnusedRecoveryCodes() int {
  252. unused := 0
  253. for _, code := range a.Filters.RecoveryCodes {
  254. if !code.Used {
  255. unused++
  256. }
  257. }
  258. return unused
  259. }
  260. func (a *Admin) hashPassword() error {
  261. if a.Password != "" && !util.IsStringPrefixInSlice(a.Password, internalHashPwdPrefixes) {
  262. if config.PasswordValidation.Admins.MinEntropy > 0 {
  263. if err := passwordvalidator.Validate(a.Password, config.PasswordValidation.Admins.MinEntropy); err != nil {
  264. return util.NewI18nError(util.NewValidationError(err.Error()), util.I18nErrorPasswordComplexity)
  265. }
  266. }
  267. if config.PasswordHashing.Algo == HashingAlgoBcrypt {
  268. pwd, err := bcrypt.GenerateFromPassword([]byte(a.Password), config.PasswordHashing.BcryptOptions.Cost)
  269. if err != nil {
  270. return err
  271. }
  272. a.Password = string(pwd)
  273. } else {
  274. pwd, err := argon2id.CreateHash(a.Password, argon2Params)
  275. if err != nil {
  276. return err
  277. }
  278. a.Password = pwd
  279. }
  280. }
  281. return nil
  282. }
  283. func (a *Admin) hasRedactedSecret() bool {
  284. return a.Filters.TOTPConfig.Secret.IsRedacted()
  285. }
  286. func (a *Admin) validateRecoveryCodes() error {
  287. for i := 0; i < len(a.Filters.RecoveryCodes); i++ {
  288. code := &a.Filters.RecoveryCodes[i]
  289. if code.Secret.IsEmpty() {
  290. return util.NewValidationError("mfa: recovery code cannot be empty")
  291. }
  292. if code.Secret.IsPlain() {
  293. code.Secret.SetAdditionalData(a.Username)
  294. if err := code.Secret.Encrypt(); err != nil {
  295. return util.NewValidationError(fmt.Sprintf("mfa: unable to encrypt recovery code: %v", err))
  296. }
  297. }
  298. }
  299. return nil
  300. }
  301. func (a *Admin) validatePermissions() error {
  302. a.Permissions = util.RemoveDuplicates(a.Permissions, false)
  303. if len(a.Permissions) == 0 {
  304. return util.NewI18nError(
  305. util.NewValidationError("please grant some permissions to this admin"),
  306. util.I18nErrorPermissionsRequired,
  307. )
  308. }
  309. if util.Contains(a.Permissions, PermAdminAny) {
  310. a.Permissions = []string{PermAdminAny}
  311. }
  312. for _, perm := range a.Permissions {
  313. if !util.Contains(validAdminPerms, perm) {
  314. return util.NewValidationError(fmt.Sprintf("invalid permission: %q", perm))
  315. }
  316. if a.Role != "" {
  317. if util.Contains(forbiddenPermsForRoleAdmins, perm) {
  318. deniedPerms := strings.Join(forbiddenPermsForRoleAdmins, ",")
  319. return util.NewI18nError(
  320. util.NewValidationError(fmt.Sprintf("a role admin cannot have the following permissions: %q", deniedPerms)),
  321. util.I18nErrorRoleAdminPerms,
  322. util.I18nErrorArgs(map[string]any{
  323. "val": deniedPerms,
  324. }),
  325. )
  326. }
  327. }
  328. }
  329. return nil
  330. }
  331. func (a *Admin) validateGroups() error {
  332. hasPrimary := false
  333. for _, g := range a.Groups {
  334. if g.Name == "" {
  335. return util.NewValidationError("group name is mandatory")
  336. }
  337. if err := g.Options.validate(); err != nil {
  338. return err
  339. }
  340. if g.Options.AddToUsersAs == GroupAddToUsersAsPrimary {
  341. if hasPrimary {
  342. return util.NewI18nError(
  343. util.NewValidationError("only one primary group is allowed"),
  344. util.I18nErrorPrimaryGroup,
  345. )
  346. }
  347. hasPrimary = true
  348. }
  349. }
  350. return nil
  351. }
  352. func (a *Admin) validate() error {
  353. a.SetEmptySecretsIfNil()
  354. if a.Username == "" {
  355. return util.NewI18nError(util.NewValidationError("username is mandatory"), util.I18nErrorUsernameRequired)
  356. }
  357. if err := checkReservedUsernames(a.Username); err != nil {
  358. return util.NewI18nError(err, util.I18nErrorReservedUsername)
  359. }
  360. if a.Password == "" {
  361. return util.NewI18nError(util.NewValidationError("please set a password"), util.I18nErrorPasswordRequired)
  362. }
  363. if a.hasRedactedSecret() {
  364. return util.NewValidationError("cannot save an admin with a redacted secret")
  365. }
  366. if err := a.Filters.TOTPConfig.validate(a.Username); err != nil {
  367. return util.NewI18nError(err, util.I18nError2FAInvalid)
  368. }
  369. if err := a.validateRecoveryCodes(); err != nil {
  370. return util.NewI18nError(err, util.I18nErrorRecoveryCodesInvalid)
  371. }
  372. if config.NamingRules&1 == 0 && !usernameRegex.MatchString(a.Username) {
  373. return util.NewI18nError(
  374. util.NewValidationError(fmt.Sprintf("username %q is not valid, the following characters are allowed: a-zA-Z0-9-_.~", a.Username)),
  375. util.I18nErrorInvalidUser,
  376. )
  377. }
  378. if err := a.hashPassword(); err != nil {
  379. return err
  380. }
  381. if err := a.validatePermissions(); err != nil {
  382. return err
  383. }
  384. if a.Email != "" && !util.IsEmailValid(a.Email) {
  385. return util.NewI18nError(
  386. util.NewValidationError(fmt.Sprintf("email %q is not valid", a.Email)),
  387. util.I18nErrorInvalidEmail,
  388. )
  389. }
  390. a.Filters.AllowList = util.RemoveDuplicates(a.Filters.AllowList, false)
  391. for _, IPMask := range a.Filters.AllowList {
  392. _, _, err := net.ParseCIDR(IPMask)
  393. if err != nil {
  394. return util.NewI18nError(
  395. util.NewValidationError(fmt.Sprintf("could not parse allow list entry %q : %v", IPMask, err)),
  396. util.I18nErrorInvalidIPMask,
  397. )
  398. }
  399. }
  400. return a.validateGroups()
  401. }
  402. // GetGroupsAsString returns the user's groups as a string
  403. func (a *Admin) GetGroupsAsString() string {
  404. if len(a.Groups) == 0 {
  405. return ""
  406. }
  407. var groups []string
  408. for _, g := range a.Groups {
  409. groups = append(groups, g.Name)
  410. }
  411. sort.Strings(groups)
  412. return strings.Join(groups, ",")
  413. }
  414. // CheckPassword verifies the admin password
  415. func (a *Admin) CheckPassword(password string) (bool, error) {
  416. if config.PasswordCaching {
  417. found, match := cachedAdminPasswords.Check(a.Username, password, a.Password)
  418. if found {
  419. if !match {
  420. return false, ErrInvalidCredentials
  421. }
  422. return match, nil
  423. }
  424. }
  425. if strings.HasPrefix(a.Password, bcryptPwdPrefix) {
  426. if err := bcrypt.CompareHashAndPassword([]byte(a.Password), []byte(password)); err != nil {
  427. return false, ErrInvalidCredentials
  428. }
  429. cachedAdminPasswords.Add(a.Username, password, a.Password)
  430. return true, nil
  431. }
  432. match, err := argon2id.ComparePasswordAndHash(password, a.Password)
  433. if !match || err != nil {
  434. return false, ErrInvalidCredentials
  435. }
  436. if match && err == nil {
  437. cachedAdminPasswords.Add(a.Username, password, a.Password)
  438. }
  439. return match, err
  440. }
  441. // CanLoginFromIP returns true if login from the given IP is allowed
  442. func (a *Admin) CanLoginFromIP(ip string) bool {
  443. if len(a.Filters.AllowList) == 0 {
  444. return true
  445. }
  446. parsedIP := net.ParseIP(ip)
  447. if parsedIP == nil {
  448. return len(a.Filters.AllowList) == 0
  449. }
  450. for _, ipMask := range a.Filters.AllowList {
  451. _, network, err := net.ParseCIDR(ipMask)
  452. if err != nil {
  453. continue
  454. }
  455. if network.Contains(parsedIP) {
  456. return true
  457. }
  458. }
  459. return false
  460. }
  461. // CanLogin returns an error if the login is not allowed
  462. func (a *Admin) CanLogin(ip string) error {
  463. if a.Status != 1 {
  464. return fmt.Errorf("admin %q is disabled", a.Username)
  465. }
  466. if !a.CanLoginFromIP(ip) {
  467. return fmt.Errorf("login from IP %v not allowed", ip)
  468. }
  469. return nil
  470. }
  471. func (a *Admin) checkUserAndPass(password, ip string) error {
  472. if err := a.CanLogin(ip); err != nil {
  473. return err
  474. }
  475. if a.Password == "" || password == "" {
  476. return errors.New("credentials cannot be null or empty")
  477. }
  478. match, err := a.CheckPassword(password)
  479. if err != nil {
  480. return err
  481. }
  482. if !match {
  483. return ErrInvalidCredentials
  484. }
  485. return nil
  486. }
  487. // RenderAsJSON implements the renderer interface used within plugins
  488. func (a *Admin) RenderAsJSON(reload bool) ([]byte, error) {
  489. if reload {
  490. admin, err := provider.adminExists(a.Username)
  491. if err != nil {
  492. providerLog(logger.LevelError, "unable to reload admin before rendering as json: %v", err)
  493. return nil, err
  494. }
  495. admin.HideConfidentialData()
  496. return json.Marshal(admin)
  497. }
  498. a.HideConfidentialData()
  499. return json.Marshal(a)
  500. }
  501. // HideConfidentialData hides admin confidential data
  502. func (a *Admin) HideConfidentialData() {
  503. a.Password = ""
  504. if a.Filters.TOTPConfig.Secret != nil {
  505. a.Filters.TOTPConfig.Secret.Hide()
  506. }
  507. for _, code := range a.Filters.RecoveryCodes {
  508. if code.Secret != nil {
  509. code.Secret.Hide()
  510. }
  511. }
  512. a.SetNilSecretsIfEmpty()
  513. }
  514. // SetEmptySecretsIfNil sets the secrets to empty if nil
  515. func (a *Admin) SetEmptySecretsIfNil() {
  516. if a.Filters.TOTPConfig.Secret == nil {
  517. a.Filters.TOTPConfig.Secret = kms.NewEmptySecret()
  518. }
  519. }
  520. // SetNilSecretsIfEmpty set the secrets to nil if empty.
  521. // This is useful before rendering as JSON so the empty fields
  522. // will not be serialized.
  523. func (a *Admin) SetNilSecretsIfEmpty() {
  524. if a.Filters.TOTPConfig.Secret != nil && a.Filters.TOTPConfig.Secret.IsEmpty() {
  525. a.Filters.TOTPConfig.Secret = nil
  526. }
  527. }
  528. // HasPermission returns true if the admin has the specified permission
  529. func (a *Admin) HasPermission(perm string) bool {
  530. if util.Contains(a.Permissions, PermAdminAny) {
  531. return true
  532. }
  533. return util.Contains(a.Permissions, perm)
  534. }
  535. // GetPermissionsAsString returns permission as string
  536. func (a *Admin) GetPermissionsAsString() string {
  537. return strings.Join(a.Permissions, ", ")
  538. }
  539. // GetLastLoginAsString returns the last login as string
  540. func (a *Admin) GetLastLoginAsString() string {
  541. if a.LastLogin > 0 {
  542. return util.GetTimeFromMsecSinceEpoch(a.LastLogin).UTC().Format(iso8601UTCFormat)
  543. }
  544. return ""
  545. }
  546. // GetAllowedIPAsString returns the allowed IP as comma separated string
  547. func (a *Admin) GetAllowedIPAsString() string {
  548. return strings.Join(a.Filters.AllowList, ",")
  549. }
  550. // GetValidPerms returns the allowed admin permissions
  551. func (a *Admin) GetValidPerms() []string {
  552. return validAdminPerms
  553. }
  554. // CanManageMFA returns true if the admin can add a multi-factor authentication configuration
  555. func (a *Admin) CanManageMFA() bool {
  556. return len(mfa.GetAvailableTOTPConfigs()) > 0
  557. }
  558. // GetSignature returns a signature for this admin.
  559. // It will change after an update
  560. func (a *Admin) GetSignature() string {
  561. return strconv.FormatInt(a.UpdatedAt, 10)
  562. }
  563. func (a *Admin) getACopy() Admin {
  564. a.SetEmptySecretsIfNil()
  565. permissions := make([]string, len(a.Permissions))
  566. copy(permissions, a.Permissions)
  567. filters := AdminFilters{}
  568. filters.AllowList = make([]string, len(a.Filters.AllowList))
  569. filters.AllowAPIKeyAuth = a.Filters.AllowAPIKeyAuth
  570. filters.RequirePasswordChange = a.Filters.RequirePasswordChange
  571. filters.RequireTwoFactor = a.Filters.RequireTwoFactor
  572. filters.TOTPConfig.Enabled = a.Filters.TOTPConfig.Enabled
  573. filters.TOTPConfig.ConfigName = a.Filters.TOTPConfig.ConfigName
  574. filters.TOTPConfig.Secret = a.Filters.TOTPConfig.Secret.Clone()
  575. copy(filters.AllowList, a.Filters.AllowList)
  576. filters.RecoveryCodes = make([]RecoveryCode, 0)
  577. for _, code := range a.Filters.RecoveryCodes {
  578. if code.Secret == nil {
  579. code.Secret = kms.NewEmptySecret()
  580. }
  581. filters.RecoveryCodes = append(filters.RecoveryCodes, RecoveryCode{
  582. Secret: code.Secret.Clone(),
  583. Used: code.Used,
  584. })
  585. }
  586. filters.Preferences = AdminPreferences{
  587. HideUserPageSections: a.Filters.Preferences.HideUserPageSections,
  588. DefaultUsersExpiration: a.Filters.Preferences.DefaultUsersExpiration,
  589. }
  590. groups := make([]AdminGroupMapping, 0, len(a.Groups))
  591. for _, g := range a.Groups {
  592. groups = append(groups, AdminGroupMapping{
  593. Name: g.Name,
  594. Options: AdminGroupMappingOptions{
  595. AddToUsersAs: g.Options.AddToUsersAs,
  596. },
  597. })
  598. }
  599. return Admin{
  600. ID: a.ID,
  601. Status: a.Status,
  602. Username: a.Username,
  603. Password: a.Password,
  604. Email: a.Email,
  605. Permissions: permissions,
  606. Groups: groups,
  607. Filters: filters,
  608. AdditionalInfo: a.AdditionalInfo,
  609. Description: a.Description,
  610. LastLogin: a.LastLogin,
  611. CreatedAt: a.CreatedAt,
  612. UpdatedAt: a.UpdatedAt,
  613. Role: a.Role,
  614. }
  615. }
  616. func (a *Admin) setFromEnv() error {
  617. envUsername := strings.TrimSpace(os.Getenv("SFTPGO_DEFAULT_ADMIN_USERNAME"))
  618. envPassword := strings.TrimSpace(os.Getenv("SFTPGO_DEFAULT_ADMIN_PASSWORD"))
  619. if envUsername == "" || envPassword == "" {
  620. return errors.New(`to create the default admin you need to set the env vars "SFTPGO_DEFAULT_ADMIN_USERNAME" and "SFTPGO_DEFAULT_ADMIN_PASSWORD"`)
  621. }
  622. a.Username = envUsername
  623. a.Password = envPassword
  624. a.Status = 1
  625. a.Permissions = []string{PermAdminAny}
  626. return nil
  627. }