diff options
author | zeripath <art27@cantab.net> | 2023-02-14 22:12:19 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-14 16:12:19 -0600 |
commit | aa1d95300ab1b34a3b4c9f5902ea821f2aa99f6e (patch) | |
tree | ac14d69e9e908ceba7826058115550315ee10c87 /cmd/admin.go | |
parent | 618c9118c1652fdeea2a2ae0d1459bb1fd3d5169 (diff) | |
download | gitea-aa1d95300ab1b34a3b4c9f5902ea821f2aa99f6e.tar.gz gitea-aa1d95300ab1b34a3b4c9f5902ea821f2aa99f6e.zip |
Add command to bulk set must-change-password (#22823)
As part of administration sometimes it is appropriate to forcibly tell
users to update their passwords.
This PR creates a new command `gitea admin user must-change-password`
which will set the `MustChangePassword` flag on the provided users.
Signed-off-by: Andrew Thornton <art27@cantab.net>
Diffstat (limited to 'cmd/admin.go')
-rw-r--r-- | cmd/admin.go | 406 |
1 files changed, 0 insertions, 406 deletions
diff --git a/cmd/admin.go b/cmd/admin.go index 318c212d08..b913b817bd 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -5,7 +5,6 @@ package cmd import ( - "context" "errors" "fmt" "os" @@ -16,20 +15,15 @@ import ( auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" - pwd "code.gitea.io/gitea/modules/password" repo_module "code.gitea.io/gitea/modules/repository" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/util" auth_service "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/auth/source/oauth2" "code.gitea.io/gitea/services/auth/source/smtp" repo_service "code.gitea.io/gitea/services/repository" - user_service "code.gitea.io/gitea/services/user" "github.com/urfave/cli" ) @@ -48,147 +42,6 @@ var ( }, } - subcmdUser = cli.Command{ - Name: "user", - Usage: "Modify users", - Subcommands: []cli.Command{ - microcmdUserCreate, - microcmdUserList, - microcmdUserChangePassword, - microcmdUserDelete, - microcmdUserGenerateAccessToken, - }, - } - - microcmdUserList = cli.Command{ - Name: "list", - Usage: "List users", - Action: runListUsers, - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "admin", - Usage: "List only admin users", - }, - }, - } - - 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", - }, - }, - } - - microcmdUserChangePassword = cli.Command{ - Name: "change-password", - Usage: "Change a user's password", - Action: runChangePassword, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "username,u", - Value: "", - Usage: "The user to change password for", - }, - cli.StringFlag{ - Name: "password,p", - Value: "", - Usage: "New password to set for user", - }, - }, - } - - microcmdUserDelete = cli.Command{ - Name: "delete", - Usage: "Delete specific user by id, name or email", - Flags: []cli.Flag{ - cli.Int64Flag{ - Name: "id", - Usage: "ID of user of the user to delete", - }, - cli.StringFlag{ - Name: "username,u", - Usage: "Username of the user to delete", - }, - cli.StringFlag{ - Name: "email,e", - Usage: "Email of the user to delete", - }, - cli.BoolFlag{ - Name: "purge", - Usage: "Purge user, all their repositories, organizations and comments", - }, - }, - Action: runDeleteUser, - } - - microcmdUserGenerateAccessToken = cli.Command{ - Name: "generate-access-token", - Usage: "Generate a access token for a specific user", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "username,u", - Usage: "Username", - }, - cli.StringFlag{ - Name: "token-name,t", - Usage: "Token name", - Value: "gitea-admin", - }, - cli.BoolFlag{ - Name: "raw", - Usage: "Display only the token value", - }, - cli.StringFlag{ - Name: "scopes", - Value: "", - Usage: "Comma separated list of scopes to apply to access token", - }, - }, - Action: runGenerateAccessToken, - } - subcmdRepoSyncReleases = cli.Command{ Name: "repo-sync-releases", Usage: "Synchronize repository releases with tags", @@ -486,265 +339,6 @@ var ( } ) -func runChangePassword(c *cli.Context) error { - if err := argsSet(c, "username", "password"); err != nil { - return err - } - - ctx, cancel := installSignals() - defer cancel() - - if err := initDB(ctx); err != nil { - return err - } - if len(c.String("password")) < setting.MinPasswordLength { - return fmt.Errorf("Password is not long enough. Needs to be at least %d", setting.MinPasswordLength) - } - - if !pwd.IsComplexEnough(c.String("password")) { - return errors.New("Password does not meet complexity requirements") - } - pwned, err := pwd.IsPwned(context.Background(), c.String("password")) - if err != nil { - return err - } - if pwned { - return errors.New("The password you chose is on a list of stolen passwords previously exposed in public data breaches. Please try again with a different password.\nFor more details, see https://haveibeenpwned.com/Passwords") - } - uname := c.String("username") - user, err := user_model.GetUserByName(ctx, uname) - if err != nil { - return err - } - if err = user.SetPassword(c.String("password")); err != nil { - return err - } - - if err = user_model.UpdateUserCols(ctx, user, "passwd", "passwd_hash_algo", "salt"); err != nil { - return err - } - - fmt.Printf("%s's password has been successfully updated!\n", user.Name) - return nil -} - -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 -} - -func runListUsers(c *cli.Context) error { - ctx, cancel := installSignals() - defer cancel() - - if err := initDB(ctx); err != nil { - return err - } - - users, err := user_model.GetAllUsers() - if err != nil { - return err - } - - w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0) - - if c.IsSet("admin") { - fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\n") - for _, u := range users { - if u.IsAdmin { - fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", u.ID, u.Name, u.Email, u.IsActive) - } - } - } else { - twofa := user_model.UserList(users).GetTwoFaStatus() - fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n") - for _, u := range users { - fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID]) - } - - } - - w.Flush() - return nil -} - -func runDeleteUser(c *cli.Context) error { - if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") { - return fmt.Errorf("You must provide the id, username or email of a user to delete") - } - - ctx, cancel := installSignals() - defer cancel() - - if err := initDB(ctx); err != nil { - return err - } - - if err := storage.Init(); err != nil { - return err - } - - var err error - var user *user_model.User - if c.IsSet("email") { - user, err = user_model.GetUserByEmail(c.String("email")) - } else if c.IsSet("username") { - user, err = user_model.GetUserByName(ctx, c.String("username")) - } else { - user, err = user_model.GetUserByID(ctx, c.Int64("id")) - } - if err != nil { - return err - } - if c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) { - return fmt.Errorf("The user %s who has email %s does not match the provided username %s", user.Name, c.String("email"), c.String("username")) - } - - if c.IsSet("id") && user.ID != c.Int64("id") { - return fmt.Errorf("The user %s does not match the provided id %d", user.Name, c.Int64("id")) - } - - return user_service.DeleteUser(ctx, user, c.Bool("purge")) -} - -func runGenerateAccessToken(c *cli.Context) error { - if !c.IsSet("username") { - return fmt.Errorf("You must provide the username to generate a token for them") - } - - ctx, cancel := installSignals() - defer cancel() - - if err := initDB(ctx); err != nil { - return err - } - - user, err := user_model.GetUserByName(ctx, c.String("username")) - if err != nil { - return err - } - - accessTokenScope, err := auth_model.AccessTokenScope(c.String("scopes")).Normalize() - if err != nil { - return err - } - - t := &auth_model.AccessToken{ - Name: c.String("token-name"), - UID: user.ID, - Scope: accessTokenScope, - } - - if err := auth_model.NewAccessToken(t); err != nil { - return err - } - - if c.Bool("raw") { - fmt.Printf("%s\n", t.Token) - } else { - fmt.Printf("Access token was successfully created: %s\n", t.Token) - } - - return nil -} - func runRepoSyncReleases(_ *cli.Context) error { ctx, cancel := installSignals() defer cancel() |