You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

admin_user_create.go 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // Copyright 2023 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package cmd
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. auth_model "code.gitea.io/gitea/models/auth"
  9. "code.gitea.io/gitea/models/db"
  10. user_model "code.gitea.io/gitea/models/user"
  11. pwd "code.gitea.io/gitea/modules/auth/password"
  12. "code.gitea.io/gitea/modules/optional"
  13. "code.gitea.io/gitea/modules/setting"
  14. "github.com/urfave/cli/v2"
  15. )
  16. var microcmdUserCreate = &cli.Command{
  17. Name: "create",
  18. Usage: "Create a new user in database",
  19. Action: runCreateUser,
  20. Flags: []cli.Flag{
  21. &cli.StringFlag{
  22. Name: "name",
  23. Usage: "Username. DEPRECATED: use username instead",
  24. },
  25. &cli.StringFlag{
  26. Name: "username",
  27. Usage: "Username",
  28. },
  29. &cli.StringFlag{
  30. Name: "password",
  31. Usage: "User password",
  32. },
  33. &cli.StringFlag{
  34. Name: "email",
  35. Usage: "User email address",
  36. },
  37. &cli.BoolFlag{
  38. Name: "admin",
  39. Usage: "User is an admin",
  40. },
  41. &cli.BoolFlag{
  42. Name: "random-password",
  43. Usage: "Generate a random password for the user",
  44. },
  45. &cli.BoolFlag{
  46. Name: "must-change-password",
  47. Usage: "User must change password after initial login, defaults to true for all users except the first one (can be disabled by --must-change-password=false)",
  48. DisableDefaultText: true,
  49. },
  50. &cli.IntFlag{
  51. Name: "random-password-length",
  52. Usage: "Length of the random password to be generated",
  53. Value: 12,
  54. },
  55. &cli.BoolFlag{
  56. Name: "access-token",
  57. Usage: "Generate access token for the user",
  58. },
  59. &cli.BoolFlag{
  60. Name: "restricted",
  61. Usage: "Make a restricted user account",
  62. },
  63. },
  64. }
  65. func runCreateUser(c *cli.Context) error {
  66. if err := argsSet(c, "email"); err != nil {
  67. return err
  68. }
  69. if c.IsSet("name") && c.IsSet("username") {
  70. return errors.New("cannot set both --name and --username flags")
  71. }
  72. if !c.IsSet("name") && !c.IsSet("username") {
  73. return errors.New("one of --name or --username flags must be set")
  74. }
  75. if c.IsSet("password") && c.IsSet("random-password") {
  76. return errors.New("cannot set both -random-password and -password flags")
  77. }
  78. var username string
  79. if c.IsSet("username") {
  80. username = c.String("username")
  81. } else {
  82. username = c.String("name")
  83. _, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
  84. }
  85. ctx := c.Context
  86. if !setting.IsInTesting {
  87. // FIXME: need to refactor the "installSignals/initDB" related code later
  88. // it doesn't make sense to call it in (almost) every command action function
  89. var cancel context.CancelFunc
  90. ctx, cancel = installSignals()
  91. defer cancel()
  92. if err := initDB(ctx); err != nil {
  93. return err
  94. }
  95. }
  96. var password string
  97. if c.IsSet("password") {
  98. password = c.String("password")
  99. } else if c.IsSet("random-password") {
  100. var err error
  101. password, err = pwd.Generate(c.Int("random-password-length"))
  102. if err != nil {
  103. return err
  104. }
  105. fmt.Printf("generated random password is '%s'\n", password)
  106. } else {
  107. return errors.New("must set either password or random-password flag")
  108. }
  109. isAdmin := c.Bool("admin")
  110. mustChangePassword := true // always default to true
  111. if c.IsSet("must-change-password") {
  112. // if the flag is set, use the value provided by the user
  113. mustChangePassword = c.Bool("must-change-password")
  114. } else {
  115. // check whether there are users in the database
  116. hasUserRecord, err := db.IsTableNotEmpty(&user_model.User{})
  117. if err != nil {
  118. return fmt.Errorf("IsTableNotEmpty: %w", err)
  119. }
  120. if !hasUserRecord {
  121. // if this is the first one being created, don't force to change password (keep the old behavior)
  122. mustChangePassword = false
  123. }
  124. }
  125. restricted := optional.None[bool]()
  126. if c.IsSet("restricted") {
  127. restricted = optional.Some(c.Bool("restricted"))
  128. }
  129. // default user visibility in app.ini
  130. visibility := setting.Service.DefaultUserVisibilityMode
  131. u := &user_model.User{
  132. Name: username,
  133. Email: c.String("email"),
  134. Passwd: password,
  135. IsAdmin: isAdmin,
  136. MustChangePassword: mustChangePassword,
  137. Visibility: visibility,
  138. }
  139. overwriteDefault := &user_model.CreateUserOverwriteOptions{
  140. IsActive: optional.Some(true),
  141. IsRestricted: restricted,
  142. }
  143. if err := user_model.CreateUser(ctx, u, overwriteDefault); err != nil {
  144. return fmt.Errorf("CreateUser: %w", err)
  145. }
  146. if c.Bool("access-token") {
  147. t := &auth_model.AccessToken{
  148. Name: "gitea-admin",
  149. UID: u.ID,
  150. }
  151. if err := auth_model.NewAccessToken(ctx, t); err != nil {
  152. return err
  153. }
  154. fmt.Printf("Access token was successfully created... %s\n", t.Token)
  155. }
  156. fmt.Printf("New user '%s' has been successfully created!\n", username)
  157. return nil
  158. }