gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  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. "strings"
  9. auth_model "code.gitea.io/gitea/models/auth"
  10. "code.gitea.io/gitea/models/db"
  11. user_model "code.gitea.io/gitea/models/user"
  12. pwd "code.gitea.io/gitea/modules/auth/password"
  13. "code.gitea.io/gitea/modules/optional"
  14. "code.gitea.io/gitea/modules/setting"
  15. "github.com/urfave/cli/v3"
  16. )
  17. func microcmdUserCreate() *cli.Command {
  18. return &cli.Command{
  19. Name: "create",
  20. Usage: "Create a new user in database",
  21. Action: runCreateUser,
  22. MutuallyExclusiveFlags: []cli.MutuallyExclusiveFlags{
  23. {
  24. Flags: [][]cli.Flag{
  25. {
  26. &cli.StringFlag{
  27. Name: "name",
  28. Usage: "Username. DEPRECATED: use username instead",
  29. },
  30. &cli.StringFlag{
  31. Name: "username",
  32. Usage: "Username",
  33. },
  34. },
  35. },
  36. Required: true,
  37. },
  38. },
  39. Flags: []cli.Flag{
  40. &cli.StringFlag{
  41. Name: "user-type",
  42. Usage: "Set user's type: individual or bot",
  43. Value: "individual",
  44. },
  45. &cli.StringFlag{
  46. Name: "password",
  47. Usage: "User password",
  48. },
  49. &cli.StringFlag{
  50. Name: "email",
  51. Usage: "User email address",
  52. Required: true,
  53. },
  54. &cli.BoolFlag{
  55. Name: "admin",
  56. Usage: "User is an admin",
  57. },
  58. &cli.BoolFlag{
  59. Name: "random-password",
  60. Usage: "Generate a random password for the user",
  61. },
  62. &cli.BoolFlag{
  63. Name: "must-change-password",
  64. 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)",
  65. HideDefault: true,
  66. },
  67. &cli.IntFlag{
  68. Name: "random-password-length",
  69. Usage: "Length of the random password to be generated",
  70. Value: 12,
  71. },
  72. &cli.BoolFlag{
  73. Name: "access-token",
  74. Usage: "Generate access token for the user",
  75. },
  76. &cli.StringFlag{
  77. Name: "access-token-name",
  78. Usage: `Name of the generated access token`,
  79. Value: "gitea-admin",
  80. },
  81. &cli.StringFlag{
  82. Name: "access-token-scopes",
  83. Usage: `Scopes of the generated access token, comma separated. Examples: "all", "public-only,read:issue", "write:repository,write:user"`,
  84. Value: "all",
  85. },
  86. &cli.BoolFlag{
  87. Name: "restricted",
  88. Usage: "Make a restricted user account",
  89. },
  90. &cli.StringFlag{
  91. Name: "fullname",
  92. Usage: `The full, human-readable name of the user`,
  93. },
  94. },
  95. }
  96. }
  97. func runCreateUser(ctx context.Context, c *cli.Command) error {
  98. // this command highly depends on the many setting options (create org, visibility, etc.), so it must have a full setting load first
  99. // duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future.
  100. setting.LoadSettings()
  101. userTypes := map[string]user_model.UserType{
  102. "individual": user_model.UserTypeIndividual,
  103. "bot": user_model.UserTypeBot,
  104. }
  105. userType, ok := userTypes[c.String("user-type")]
  106. if !ok {
  107. return fmt.Errorf("invalid user type: %s", c.String("user-type"))
  108. }
  109. if userType != user_model.UserTypeIndividual {
  110. // Some other commands like "change-password" also only support individual users.
  111. // It needs to clarify the "password" behavior for bot users in the future.
  112. // At the moment, we do not allow setting password for bot users.
  113. if c.IsSet("password") || c.IsSet("random-password") {
  114. return errors.New("password can only be set for individual users")
  115. }
  116. }
  117. if c.IsSet("password") && c.IsSet("random-password") {
  118. return errors.New("cannot set both -random-password and -password flags")
  119. }
  120. var username string
  121. if c.IsSet("username") {
  122. username = c.String("username")
  123. } else {
  124. username = c.String("name")
  125. _, _ = fmt.Fprintf(c.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
  126. }
  127. if !setting.IsInTesting {
  128. // FIXME: need to refactor the "initDB" related code later
  129. // it doesn't make sense to call it in (almost) every command action function
  130. if err := initDB(ctx); err != nil {
  131. return err
  132. }
  133. }
  134. var password string
  135. if c.IsSet("password") {
  136. password = c.String("password")
  137. } else if c.IsSet("random-password") {
  138. var err error
  139. password, err = pwd.Generate(c.Int("random-password-length"))
  140. if err != nil {
  141. return err
  142. }
  143. fmt.Printf("generated random password is '%s'\n", password)
  144. } else if userType == user_model.UserTypeIndividual {
  145. return errors.New("must set either password or random-password flag")
  146. }
  147. isAdmin := c.Bool("admin")
  148. mustChangePassword := true // always default to true
  149. if c.IsSet("must-change-password") {
  150. if userType != user_model.UserTypeIndividual {
  151. return errors.New("must-change-password flag can only be set for individual users")
  152. }
  153. // if the flag is set, use the value provided by the user
  154. mustChangePassword = c.Bool("must-change-password")
  155. } else if userType == user_model.UserTypeIndividual {
  156. // check whether there are users in the database
  157. hasUserRecord, err := db.IsTableNotEmpty(&user_model.User{})
  158. if err != nil {
  159. return fmt.Errorf("IsTableNotEmpty: %w", err)
  160. }
  161. if !hasUserRecord {
  162. // if this is the first one being created, don't force to change password (keep the old behavior)
  163. mustChangePassword = false
  164. }
  165. }
  166. restricted := optional.None[bool]()
  167. if c.IsSet("restricted") {
  168. restricted = optional.Some(c.Bool("restricted"))
  169. }
  170. // default user visibility in app.ini
  171. visibility := setting.Service.DefaultUserVisibilityMode
  172. u := &user_model.User{
  173. Name: username,
  174. Email: c.String("email"),
  175. IsAdmin: isAdmin,
  176. Type: userType,
  177. Passwd: password,
  178. MustChangePassword: mustChangePassword,
  179. Visibility: visibility,
  180. FullName: c.String("fullname"),
  181. }
  182. overwriteDefault := &user_model.CreateUserOverwriteOptions{
  183. IsActive: optional.Some(true),
  184. IsRestricted: restricted,
  185. }
  186. var accessTokenName string
  187. var accessTokenScope auth_model.AccessTokenScope
  188. if c.IsSet("access-token") {
  189. accessTokenName = strings.TrimSpace(c.String("access-token-name"))
  190. if accessTokenName == "" {
  191. return errors.New("access-token-name cannot be empty")
  192. }
  193. var err error
  194. accessTokenScope, err = auth_model.AccessTokenScope(c.String("access-token-scopes")).Normalize()
  195. if err != nil {
  196. return fmt.Errorf("invalid access token scope provided: %w", err)
  197. }
  198. if !accessTokenScope.HasPermissionScope() {
  199. return errors.New("access token does not have any permission")
  200. }
  201. } else if c.IsSet("access-token-name") || c.IsSet("access-token-scopes") {
  202. return errors.New("access-token-name and access-token-scopes flags are only valid when access-token flag is set")
  203. }
  204. // arguments should be prepared before creating the user & access token, in case there is anything wrong
  205. // create the user
  206. if err := user_model.CreateUser(ctx, u, &user_model.Meta{}, overwriteDefault); err != nil {
  207. return fmt.Errorf("CreateUser: %w", err)
  208. }
  209. fmt.Printf("New user '%s' has been successfully created!\n", username)
  210. // create the access token
  211. if accessTokenScope != "" {
  212. t := &auth_model.AccessToken{Name: accessTokenName, UID: u.ID, Scope: accessTokenScope}
  213. if err := auth_model.NewAccessToken(ctx, t); err != nil {
  214. return err
  215. }
  216. fmt.Printf("Access token was successfully created... %s\n", t.Token)
  217. }
  218. return nil
  219. }