loading up the forgejo repo on tangled to test page performance
at forgejo 6.0 kB view raw
1// Copyright 2023 The Gitea Authors. All rights reserved. 2// SPDX-License-Identifier: MIT 3 4package cmd 5 6import ( 7 "errors" 8 "fmt" 9 "strings" 10 11 auth_model "forgejo.org/models/auth" 12 "forgejo.org/models/db" 13 user_model "forgejo.org/models/user" 14 pwd "forgejo.org/modules/auth/password" 15 "forgejo.org/modules/optional" 16 "forgejo.org/modules/setting" 17 18 "github.com/urfave/cli/v2" 19) 20 21var microcmdUserCreate = &cli.Command{ 22 Name: "create", 23 Usage: "Create a new user in database", 24 Action: runCreateUser, 25 Flags: []cli.Flag{ 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 &cli.StringFlag{ 35 Name: "password", 36 Usage: "User password", 37 }, 38 &cli.StringFlag{ 39 Name: "email", 40 Usage: "User email address", 41 }, 42 &cli.BoolFlag{ 43 Name: "admin", 44 Usage: "User is an admin", 45 }, 46 &cli.BoolFlag{ 47 Name: "random-password", 48 Usage: "Generate a random password for the user", 49 }, 50 &cli.BoolFlag{ 51 Name: "must-change-password", 52 Usage: "Set this option to false to prevent forcing the user to change their password after initial login", 53 Value: true, 54 DisableDefaultText: true, 55 }, 56 &cli.IntFlag{ 57 Name: "random-password-length", 58 Usage: "Length of the random password to be generated", 59 Value: 12, 60 }, 61 &cli.BoolFlag{ 62 Name: "access-token", 63 Usage: "Generate access token for the user", 64 }, 65 &cli.StringFlag{ 66 Name: "access-token-name", 67 Usage: `Name of the generated access token`, 68 Value: "gitea-admin", 69 }, 70 &cli.StringFlag{ 71 Name: "access-token-scopes", 72 Usage: `Scopes of the generated access token, comma separated. Examples: "all", "public-only,read:issue", "write:repository,write:user"`, 73 Value: "all", 74 }, 75 &cli.BoolFlag{ 76 Name: "restricted", 77 Usage: "Make a restricted user account", 78 }, 79 }, 80} 81 82func runCreateUser(c *cli.Context) error { 83 // this command highly depends on the many setting options (create org, visibility, etc.), so it must have a full setting load first 84 // duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future. 85 setting.LoadSettings() 86 87 if err := argsSet(c, "email"); err != nil { 88 return err 89 } 90 91 if c.IsSet("name") && c.IsSet("username") { 92 return errors.New("cannot set both --name and --username flags") 93 } 94 if !c.IsSet("name") && !c.IsSet("username") { 95 return errors.New("one of --name or --username flags must be set") 96 } 97 98 if c.IsSet("password") && c.IsSet("random-password") { 99 return errors.New("cannot set both -random-password and -password flags") 100 } 101 102 var username string 103 if c.IsSet("username") { 104 username = c.String("username") 105 } else { 106 username = c.String("name") 107 _, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n") 108 } 109 110 ctx, cancel := installSignals() 111 defer cancel() 112 113 if err := initDB(ctx); err != nil { 114 return err 115 } 116 117 var password string 118 if c.IsSet("password") { 119 password = c.String("password") 120 } else if c.IsSet("random-password") { 121 var err error 122 password, err = pwd.Generate(c.Int("random-password-length")) 123 if err != nil { 124 return err 125 } 126 fmt.Printf("generated random password is '%s'\n", password) 127 } else { 128 return errors.New("must set either password or random-password flag") 129 } 130 131 isAdmin := c.Bool("admin") 132 mustChangePassword := true // always default to true 133 if c.IsSet("must-change-password") { 134 // if the flag is set, use the value provided by the user 135 mustChangePassword = c.Bool("must-change-password") 136 } else { 137 // check whether there are users in the database 138 hasUserRecord, err := db.IsTableNotEmpty(&user_model.User{}) 139 if err != nil { 140 return fmt.Errorf("IsTableNotEmpty: %w", err) 141 } 142 if !hasUserRecord { 143 // if this is the first admin being created, don't force to change password (keep the old behavior) 144 mustChangePassword = false 145 } 146 } 147 148 restricted := optional.None[bool]() 149 150 if c.IsSet("restricted") { 151 restricted = optional.Some(c.Bool("restricted")) 152 } 153 154 // default user visibility in app.ini 155 visibility := setting.Service.DefaultUserVisibilityMode 156 157 u := &user_model.User{ 158 Name: username, 159 Email: c.String("email"), 160 Passwd: password, 161 IsAdmin: isAdmin, 162 MustChangePassword: mustChangePassword, 163 Visibility: visibility, 164 } 165 166 overwriteDefault := &user_model.CreateUserOverwriteOptions{ 167 IsActive: optional.Some(true), 168 IsRestricted: restricted, 169 } 170 171 var accessTokenName string 172 var accessTokenScope auth_model.AccessTokenScope 173 if c.IsSet("access-token") { 174 accessTokenName = strings.TrimSpace(c.String("access-token-name")) 175 if accessTokenName == "" { 176 return errors.New("access-token-name cannot be empty") 177 } 178 var err error 179 accessTokenScope, err = auth_model.AccessTokenScope(c.String("access-token-scopes")).Normalize() 180 if err != nil { 181 return fmt.Errorf("invalid access token scope provided: %w", err) 182 } 183 if !accessTokenScope.HasPermissionScope() { 184 return errors.New("access token does not have any permission") 185 } 186 } else if c.IsSet("access-token-name") || c.IsSet("access-token-scopes") { 187 return errors.New("access-token-name and access-token-scopes flags are only valid when access-token flag is set") 188 } 189 190 // arguments should be prepared before creating the user & access token, in case there is anything wrong 191 192 // create the user 193 if err := user_model.CreateUser(ctx, u, overwriteDefault); err != nil { 194 return fmt.Errorf("CreateUser: %w", err) 195 } 196 fmt.Printf("New user '%s' has been successfully created!\n", username) 197 198 // create the access token 199 if accessTokenScope != "" { 200 t := &auth_model.AccessToken{Name: accessTokenName, UID: u.ID, Scope: accessTokenScope} 201 if err := auth_model.NewAccessToken(ctx, t); err != nil { 202 return err 203 } 204 fmt.Printf("Access token was successfully created... %s\n", t.Token) 205 } 206 return nil 207}