From 8b4ca4501a86b028a8da52f20b824a3701b1598a Mon Sep 17 00:00:00 2001
From: Earl Warren <contact@earl-warren.org>
Date: Wed, 24 Apr 2024 12:22:39 +0200
Subject: [PATCH 1/3] test(cli): admin user create and must change password
 value

(cherry picked from commit eb74846d7db769dc377b6c9276b04427bffc5a09)
---
 tests/integration/cmd_admin_test.go | 138 ++++++++++++++++++++++++++++
 1 file changed, 138 insertions(+)
 create mode 100644 tests/integration/cmd_admin_test.go

diff --git a/tests/integration/cmd_admin_test.go b/tests/integration/cmd_admin_test.go
new file mode 100644
index 0000000000..4bccee3a79
--- /dev/null
+++ b/tests/integration/cmd_admin_test.go
@@ -0,0 +1,138 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+	"net/url"
+	"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 Test_Cmd_AdminUser(t *testing.T) {
+	onGiteaRun(t, func(*testing.T, *url.URL) {
+		for _, testCase := range []struct {
+			name               string
+			options            []string
+			mustChangePassword bool
+		}{
+			{
+				name:               "default",
+				options:            []string{},
+				mustChangePassword: true,
+			},
+			{
+				name:               "--must-change-password=false",
+				options:            []string{"--must-change-password=false"},
+				mustChangePassword: false,
+			},
+			{
+				name:               "--must-change-password=true",
+				options:            []string{"--must-change-password=true"},
+				mustChangePassword: true,
+			},
+			{
+				name:               "--must-change-password",
+				options:            []string{"--must-change-password"},
+				mustChangePassword: true,
+			},
+		} {
+			t.Run(testCase.name, func(t *testing.T) {
+				name := "testuser"
+
+				options := []string{"user", "create", "--username", name, "--password", "password", "--email", name + "@example.com"}
+				options = append(options, testCase.options...)
+				output, err := runMainApp("admin", options...)
+				assert.NoError(t, err)
+				assert.Contains(t, output, "has been successfully created")
+				user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: name})
+				assert.Equal(t, testCase.mustChangePassword, user.MustChangePassword)
+
+				_, err = runMainApp("admin", "user", "delete", "--username", name)
+				assert.NoError(t, err)
+				unittest.AssertNotExistsBean(t, &user_model.User{Name: name})
+			})
+		}
+	})
+}
+
+func Test_Cmd_AdminFirstUser(t *testing.T) {
+	onGiteaRun(t, func(*testing.T, *url.URL) {
+		for _, testCase := range []struct {
+			name               string
+			options            []string
+			mustChangePassword bool
+			isAdmin            bool
+		}{
+			{
+				name:               "default",
+				options:            []string{},
+				mustChangePassword: false,
+				isAdmin:            false,
+			},
+			{
+				name:               "--must-change-password=false",
+				options:            []string{"--must-change-password=false"},
+				mustChangePassword: false,
+				isAdmin:            false,
+			},
+			{
+				name:               "--must-change-password=true",
+				options:            []string{"--must-change-password=true"},
+				mustChangePassword: true,
+				isAdmin:            false,
+			},
+			{
+				name:               "--must-change-password",
+				options:            []string{"--must-change-password"},
+				mustChangePassword: true,
+				isAdmin:            false,
+			},
+			{
+				name:               "--admin default",
+				options:            []string{"--admin"},
+				mustChangePassword: false,
+				isAdmin:            true,
+			},
+			{
+				name:               "--admin --must-change-password=false",
+				options:            []string{"--admin", "--must-change-password=false"},
+				mustChangePassword: false,
+				isAdmin:            true,
+			},
+			{
+				name:               "--admin --must-change-password=true",
+				options:            []string{"--admin", "--must-change-password=true"},
+				mustChangePassword: true,
+				isAdmin:            true,
+			},
+			{
+				name:               "--admin --must-change-password",
+				options:            []string{"--admin", "--must-change-password"},
+				mustChangePassword: true,
+				isAdmin:            true,
+			},
+		} {
+			t.Run(testCase.name, func(t *testing.T) {
+				db.GetEngine(db.DefaultContext).Exec("DELETE FROM `user`")
+				db.GetEngine(db.DefaultContext).Exec("DELETE FROM `email_address`")
+				assert.Equal(t, int64(0), user_model.CountUsers(db.DefaultContext, nil))
+				name := "testuser"
+
+				options := []string{"user", "create", "--username", name, "--password", "password", "--email", name + "@example.com"}
+				options = append(options, testCase.options...)
+				output, err := runMainApp("admin", options...)
+				assert.NoError(t, err)
+				assert.Contains(t, output, "has been successfully created")
+				user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: name})
+				assert.Equal(t, testCase.mustChangePassword, user.MustChangePassword)
+				assert.Equal(t, testCase.isAdmin, user.IsAdmin)
+			})
+		}
+	})
+}

From 21a853aac74e0fd42f19e2a43af7dac50bef8d44 Mon Sep 17 00:00:00 2001
From: Earl Warren <contact@earl-warren.org>
Date: Wed, 24 Apr 2024 12:24:19 +0200
Subject: [PATCH 2/3] test(cli): admin user change-password
 --must-change-password

(cherry picked from commit b9424e634f0ee1818d5b7d1e1f701fd67d0adbad)
---
 tests/integration/cmd_admin_test.go | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/tests/integration/cmd_admin_test.go b/tests/integration/cmd_admin_test.go
index 4bccee3a79..6a85460450 100644
--- a/tests/integration/cmd_admin_test.go
+++ b/tests/integration/cmd_admin_test.go
@@ -53,6 +53,14 @@ func Test_Cmd_AdminUser(t *testing.T) {
 				user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: name})
 				assert.Equal(t, testCase.mustChangePassword, user.MustChangePassword)
 
+				options = []string{"user", "change-password", "--username", name, "--password", "password"}
+				options = append(options, testCase.options...)
+				output, err = runMainApp("admin", options...)
+				assert.NoError(t, err)
+				assert.Contains(t, output, "has been successfully updated")
+				user = unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: name})
+				assert.Equal(t, testCase.mustChangePassword, user.MustChangePassword)
+
 				_, err = runMainApp("admin", "user", "delete", "--username", name)
 				assert.NoError(t, err)
 				unittest.AssertNotExistsBean(t, &user_model.User{Name: name})

From a1f29d9a0e1a4b1292ef79cdb814dac133e6ee23 Mon Sep 17 00:00:00 2001
From: Earl Warren <contact@earl-warren.org>
Date: Wed, 24 Apr 2024 12:27:20 +0200
Subject: [PATCH 3/3] fix(cli): admin user create first user never require a
 password change

Fixes: https://codeberg.org/forgejo/forgejo/issues/3399
(cherry picked from commit ec334239e08a755c9ff5d9c461bf456d498256b8)
---
 RELEASE-NOTES.md         | 1 +
 cmd/admin_user_create.go | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 2ad10ff564..8a9f280fbe 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -11,6 +11,7 @@ This is a bug fix release. See the documentation for more information on the [up
 In addition to the following notable bug fixes, you can browse the [full list of commits](https://codeberg.org/forgejo/forgejo/compare/v7.0.0...v7.0.1) included in this release.
 
 * **Bug fixes:**
+  * The regression in the [`fogejo admin user create`](https://forgejo.org/docs/v7.0/admin/command-line/#admin-user-create) CLI command [is fixed](https://codeberg.org/forgejo/forgejo/issues/3399) and it is backward compatible.
 
 ## 7.0.0
 
diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go
index caafef536c..dfc484aeb2 100644
--- a/cmd/admin_user_create.go
+++ b/cmd/admin_user_create.go
@@ -124,7 +124,7 @@ func runCreateUser(c *cli.Context) error {
 		if err != nil {
 			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)
 			mustChangePassword = false
 		}