mirror of
https://codeberg.org/forgejo/forgejo
synced 2024-11-30 22:06:11 +01:00
61b89747ed
This PR refactors and improves the password hashing code within gitea and makes it possible for server administrators to set the password hashing parameters In addition it takes the opportunity to adjust the settings for `pbkdf2` in order to make the hashing a little stronger. The majority of this work was inspired by PR #14751 and I would like to thank @boppy for their work on this. Thanks to @gusted for the suggestion to adjust the `pbkdf2` hashing parameters. Close #14751 --------- Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: delvh <dev.lh@web.de> Co-authored-by: John Olheiser <john.olheiser@gmail.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
170 lines
4 KiB
Go
170 lines
4 KiB
Go
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
|
|
auth_model "code.gitea.io/gitea/models/auth"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
pwd "code.gitea.io/gitea/modules/auth/password"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
"code.gitea.io/gitea/modules/util"
|
|
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
var microcmdUserCreate = cli.Command{
|
|
Name: "create",
|
|
Usage: "Create a new user in database",
|
|
Action: runCreateUser,
|
|
Flags: []cli.Flag{
|
|
cli.StringFlag{
|
|
Name: "name",
|
|
Usage: "Username. DEPRECATED: use username instead",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "username",
|
|
Usage: "Username",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "password",
|
|
Usage: "User password",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "email",
|
|
Usage: "User email address",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "admin",
|
|
Usage: "User is an admin",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "random-password",
|
|
Usage: "Generate a random password for the user",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "must-change-password",
|
|
Usage: "Set this option to false to prevent forcing the user to change their password after initial login, (Default: true)",
|
|
},
|
|
cli.IntFlag{
|
|
Name: "random-password-length",
|
|
Usage: "Length of the random password to be generated",
|
|
Value: 12,
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "access-token",
|
|
Usage: "Generate access token for the user",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "restricted",
|
|
Usage: "Make a restricted user account",
|
|
},
|
|
},
|
|
}
|
|
|
|
func runCreateUser(c *cli.Context) error {
|
|
if err := argsSet(c, "email"); err != nil {
|
|
return err
|
|
}
|
|
|
|
if c.IsSet("name") && c.IsSet("username") {
|
|
return errors.New("Cannot set both --name and --username flags")
|
|
}
|
|
if !c.IsSet("name") && !c.IsSet("username") {
|
|
return errors.New("One of --name or --username flags must be set")
|
|
}
|
|
|
|
if c.IsSet("password") && c.IsSet("random-password") {
|
|
return errors.New("cannot set both -random-password and -password flags")
|
|
}
|
|
|
|
var username string
|
|
if c.IsSet("username") {
|
|
username = c.String("username")
|
|
} else {
|
|
username = c.String("name")
|
|
fmt.Fprintf(os.Stderr, "--name flag is deprecated. Use --username instead.\n")
|
|
}
|
|
|
|
ctx, cancel := installSignals()
|
|
defer cancel()
|
|
|
|
if err := initDB(ctx); err != nil {
|
|
return err
|
|
}
|
|
|
|
var password string
|
|
if c.IsSet("password") {
|
|
password = c.String("password")
|
|
} else if c.IsSet("random-password") {
|
|
var err error
|
|
password, err = pwd.Generate(c.Int("random-password-length"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Printf("generated random password is '%s'\n", password)
|
|
} else {
|
|
return errors.New("must set either password or random-password flag")
|
|
}
|
|
|
|
// always default to true
|
|
changePassword := true
|
|
|
|
// If this is the first user being created.
|
|
// Take it as the admin and don't force a password update.
|
|
if n := user_model.CountUsers(nil); n == 0 {
|
|
changePassword = false
|
|
}
|
|
|
|
if c.IsSet("must-change-password") {
|
|
changePassword = c.Bool("must-change-password")
|
|
}
|
|
|
|
restricted := util.OptionalBoolNone
|
|
|
|
if c.IsSet("restricted") {
|
|
restricted = util.OptionalBoolOf(c.Bool("restricted"))
|
|
}
|
|
|
|
// default user visibility in app.ini
|
|
visibility := setting.Service.DefaultUserVisibilityMode
|
|
|
|
u := &user_model.User{
|
|
Name: username,
|
|
Email: c.String("email"),
|
|
Passwd: password,
|
|
IsAdmin: c.Bool("admin"),
|
|
MustChangePassword: changePassword,
|
|
Visibility: visibility,
|
|
}
|
|
|
|
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
|
IsActive: util.OptionalBoolTrue,
|
|
IsRestricted: restricted,
|
|
}
|
|
|
|
if err := user_model.CreateUser(u, overwriteDefault); err != nil {
|
|
return fmt.Errorf("CreateUser: %w", err)
|
|
}
|
|
|
|
if c.Bool("access-token") {
|
|
t := &auth_model.AccessToken{
|
|
Name: "gitea-admin",
|
|
UID: u.ID,
|
|
}
|
|
|
|
if err := auth_model.NewAccessToken(t); err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("Access token was successfully created... %s\n", t.Token)
|
|
}
|
|
|
|
fmt.Printf("New user '%s' has been successfully created!\n", username)
|
|
return nil
|
|
}
|