Add some tests to clarify the "must-change-password" behavior (#30693)

Follow  #30472:

When a user is created by command line `./gitea admin user create`:

Old behavior before #30472: the first user (admin or non-admin) doesn't
need to change password.

Revert to the old behavior before #30472
This commit is contained in:
wxiaoguang 2024-04-27 20:23:37 +08:00 коммит произвёл GitHub
Родитель dd301cae1c
Коммит d3cdef88ad
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
9 изменённых файлов: 81 добавлений и 17 удалений

Просмотреть файл

@ -35,7 +35,7 @@ var microcmdUserChangePassword = &cli.Command{
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "must-change-password", Name: "must-change-password",
Usage: "User must change password", Usage: "User must change password (can be disabled by --must-change-password=false)",
Value: true, Value: true,
}, },
}, },

Просмотреть файл

@ -4,6 +4,7 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
@ -48,7 +49,7 @@ var microcmdUserCreate = &cli.Command{
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "must-change-password", Name: "must-change-password",
Usage: "Set to false to prevent forcing the user to change their password after initial login", 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)",
DisableDefaultText: true, DisableDefaultText: true,
}, },
&cli.IntFlag{ &cli.IntFlag{
@ -91,11 +92,16 @@ func runCreateUser(c *cli.Context) error {
_, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n") _, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
} }
ctx, cancel := installSignals() ctx := c.Context
defer cancel() if !setting.IsInTesting {
// FIXME: need to refactor the "installSignals/initDB" related code later
if err := initDB(ctx); err != nil { // it doesn't make sense to call it in (almost) every command action function
return err var cancel context.CancelFunc
ctx, cancel = installSignals()
defer cancel()
if err := initDB(ctx); err != nil {
return err
}
} }
var password string var password string
@ -123,8 +129,8 @@ func runCreateUser(c *cli.Context) error {
if err != nil { if err != nil {
return fmt.Errorf("IsTableNotEmpty: %w", err) return fmt.Errorf("IsTableNotEmpty: %w", err)
} }
if !hasUserRecord && isAdmin { if !hasUserRecord {
// if this is the first admin being created, don't force to change password (keep the old behavior) // if this is the first one being created, don't force to change password (keep the old behavior)
mustChangePassword = false mustChangePassword = false
} }
} }

Просмотреть файл

@ -0,0 +1,44 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"fmt"
"strings"
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
)
func TestAdminUserCreate(t *testing.T) {
app := NewMainApp(AppVersion{})
reset := func() {
assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
}
type createCheck struct{ IsAdmin, MustChangePassword bool }
createUser := func(name, args string) createCheck {
assert.NoError(t, app.Run(strings.Fields(fmt.Sprintf("./gitea admin user create --username %s --email %s@gitea.local %s --password foobar", name, name, args))))
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name})
return createCheck{u.IsAdmin, u.MustChangePassword}
}
reset()
assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u", ""), "first non-admin user doesn't need to change password")
reset()
assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: false}, createUser("u", "--admin"), "first admin user doesn't need to change password")
reset()
assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: true}, createUser("u", "--admin --must-change-password"))
assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: true}, createUser("u2", "--admin"))
assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: false}, createUser("u3", "--admin --must-change-password=false"))
assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: true}, createUser("u4", ""))
assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u5", "--must-change-password=false"))
}

Просмотреть файл

@ -112,13 +112,18 @@ func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context)
} }
} }
func NewMainApp(version, versionExtra string) *cli.App { type AppVersion struct {
Version string
Extra string
}
func NewMainApp(appVer AppVersion) *cli.App {
app := cli.NewApp() app := cli.NewApp()
app.Name = "Gitea" app.Name = "Gitea"
app.HelpName = "gitea" app.HelpName = "gitea"
app.Usage = "A painless self-hosted Git service" app.Usage = "A painless self-hosted Git service"
app.Description = `Gitea program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.` app.Description = `Gitea program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.`
app.Version = version + versionExtra app.Version = appVer.Version + appVer.Extra
app.EnableBashCompletion = true app.EnableBashCompletion = true
// these sub-commands need to use config file // these sub-commands need to use config file

Просмотреть файл

@ -28,7 +28,7 @@ func makePathOutput(workPath, customPath, customConf string) string {
} }
func newTestApp(testCmdAction func(ctx *cli.Context) error) *cli.App { func newTestApp(testCmdAction func(ctx *cli.Context) error) *cli.App {
app := NewMainApp("version", "version-extra") app := NewMainApp(AppVersion{})
testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction} testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction}
prepareSubcommandWithConfig(testCmd, appGlobalFlags()) prepareSubcommandWithConfig(testCmd, appGlobalFlags())
app.Commands = append(app.Commands, testCmd) app.Commands = append(app.Commands, testCmd)

Просмотреть файл

@ -42,7 +42,7 @@ func main() {
log.GetManager().Close() log.GetManager().Close()
os.Exit(code) os.Exit(code)
} }
app := cmd.NewMainApp(Version, formatBuiltWith()) app := cmd.NewMainApp(cmd.AppVersion{Version: Version, Extra: formatBuiltWith()})
_ = cmd.RunMainApp(app, os.Args...) // all errors should have been handled by the RunMainApp _ = cmd.RunMainApp(app, os.Args...) // all errors should have been handled by the RunMainApp
log.GetManager().Close() log.GetManager().Close()
} }

Просмотреть файл

@ -6,7 +6,6 @@ package unittest
import ( import (
"context" "context"
"fmt" "fmt"
"log"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -18,6 +17,7 @@ import (
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/setting/config" "code.gitea.io/gitea/modules/setting/config"
"code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/storage"
@ -46,6 +46,14 @@ func fatalTestError(fmtStr string, args ...any) {
// InitSettings initializes config provider and load common settings for tests // InitSettings initializes config provider and load common settings for tests
func InitSettings() { func InitSettings() {
setting.IsInTesting = true
log.OsExiter = func(code int) {
if code != 0 {
// non-zero exit code (log.Fatal) shouldn't occur during testing, if it happens, show a full stacktrace for more details
panic(fmt.Errorf("non-zero exit code during testing: %d", code))
}
os.Exit(0)
}
if setting.CustomConf == "" { if setting.CustomConf == "" {
setting.CustomConf = filepath.Join(setting.CustomPath, "conf/app-unittest-tmp.ini") setting.CustomConf = filepath.Join(setting.CustomPath, "conf/app-unittest-tmp.ini")
_ = os.Remove(setting.CustomConf) _ = os.Remove(setting.CustomConf)
@ -54,7 +62,7 @@ func InitSettings() {
setting.LoadCommonSettings() setting.LoadCommonSettings()
if err := setting.PrepareAppDataPath(); err != nil { if err := setting.PrepareAppDataPath(); err != nil {
log.Fatalf("Can not prepare APP_DATA_PATH: %v", err) log.Fatal("Can not prepare APP_DATA_PATH: %v", err)
} }
// register the dummy hash algorithm function used in the test fixtures // register the dummy hash algorithm function used in the test fixtures
_ = hash.Register("dummy", hash.NewDummyHasher) _ = hash.Register("dummy", hash.NewDummyHasher)

Просмотреть файл

@ -57,11 +57,13 @@ func Critical(format string, v ...any) {
Log(1, ERROR, format, v...) Log(1, ERROR, format, v...)
} }
var OsExiter = os.Exit
// Fatal records fatal log and exit process // Fatal records fatal log and exit process
func Fatal(format string, v ...any) { func Fatal(format string, v ...any) {
Log(1, FATAL, format, v...) Log(1, FATAL, format, v...)
GetManager().Close() GetManager().Close()
os.Exit(1) OsExiter(1)
} }
func GetLogger(name string) Logger { func GetLogger(name string) Logger {

Просмотреть файл

@ -46,7 +46,6 @@ func InitTest(requireGitea bool) {
// TODO: Speedup tests that rely on the event source ticker, confirm whether there is any bug or failure. // TODO: Speedup tests that rely on the event source ticker, confirm whether there is any bug or failure.
// setting.UI.Notification.EventSourceUpdateTime = time.Second // setting.UI.Notification.EventSourceUpdateTime = time.Second
setting.IsInTesting = true
setting.AppWorkPath = giteaRoot setting.AppWorkPath = giteaRoot
setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom") setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom")
if requireGitea { if requireGitea {