summaryrefslogtreecommitdiffstats
path: root/tests/integration/api_admin_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'tests/integration/api_admin_test.go')
-rw-r--r--tests/integration/api_admin_test.go420
1 files changed, 420 insertions, 0 deletions
diff --git a/tests/integration/api_admin_test.go b/tests/integration/api_admin_test.go
new file mode 100644
index 00000000..5f8d360d
--- /dev/null
+++ b/tests/integration/api_admin_test.go
@@ -0,0 +1,420 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+ "time"
+
+ asymkey_model "code.gitea.io/gitea/models/asymkey"
+ auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/setting"
+ api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/tests"
+
+ "github.com/gobwas/glob"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestAPIAdminCreateAndDeleteSSHKey(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ // user1 is an admin user
+ session := loginUser(t, "user1")
+ keyOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"})
+
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteAdmin)
+ urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys", keyOwner.Name)
+ req := NewRequestWithValues(t, "POST", urlStr, map[string]string{
+ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM= nocomment\n",
+ "title": "test-key",
+ }).AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusCreated)
+
+ var newPublicKey api.PublicKey
+ DecodeJSON(t, resp, &newPublicKey)
+ unittest.AssertExistsAndLoadBean(t, &asymkey_model.PublicKey{
+ ID: newPublicKey.ID,
+ Name: newPublicKey.Title,
+ Fingerprint: newPublicKey.Fingerprint,
+ OwnerID: keyOwner.ID,
+ })
+
+ req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d", keyOwner.Name, newPublicKey.ID).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNoContent)
+ unittest.AssertNotExistsBean(t, &asymkey_model.PublicKey{ID: newPublicKey.ID})
+}
+
+func TestAPIAdminDeleteMissingSSHKey(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ // user1 is an admin user
+ token := getUserToken(t, "user1", auth_model.AccessTokenScopeWriteAdmin)
+ req := NewRequestf(t, "DELETE", "/api/v1/admin/users/user1/keys/%d", unittest.NonexistentID).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNotFound)
+}
+
+func TestAPIAdminDeleteUnauthorizedKey(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ adminUsername := "user1"
+ normalUsername := "user2"
+ token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
+
+ urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys", adminUsername)
+ req := NewRequestWithValues(t, "POST", urlStr, map[string]string{
+ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM= nocomment\n",
+ "title": "test-key",
+ }).AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusCreated)
+ var newPublicKey api.PublicKey
+ DecodeJSON(t, resp, &newPublicKey)
+
+ token = getUserToken(t, normalUsername)
+ req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d", adminUsername, newPublicKey.ID).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusForbidden)
+}
+
+func TestAPISudoUser(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ adminUsername := "user1"
+ normalUsername := "user2"
+ token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeReadUser)
+
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user?sudo=%s", normalUsername)).
+ AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ var user api.User
+ DecodeJSON(t, resp, &user)
+
+ assert.Equal(t, normalUsername, user.UserName)
+}
+
+func TestAPISudoUserForbidden(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ adminUsername := "user1"
+ normalUsername := "user2"
+
+ token := getUserToken(t, normalUsername, auth_model.AccessTokenScopeReadAdmin)
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user?sudo=%s", adminUsername)).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusForbidden)
+}
+
+func TestAPIListUsers(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ adminUsername := "user1"
+ token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeReadAdmin)
+
+ req := NewRequest(t, "GET", "/api/v1/admin/users").
+ AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ var users []api.User
+ DecodeJSON(t, resp, &users)
+
+ found := false
+ for _, user := range users {
+ if user.UserName == adminUsername {
+ found = true
+ }
+ }
+ assert.True(t, found)
+ numberOfUsers := unittest.GetCount(t, &user_model.User{}, "type = 0")
+ assert.Len(t, users, numberOfUsers)
+}
+
+func TestAPIListUsersNotLoggedIn(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ req := NewRequest(t, "GET", "/api/v1/admin/users")
+ MakeRequest(t, req, http.StatusUnauthorized)
+}
+
+func TestAPIListUsersNonAdmin(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ nonAdminUsername := "user2"
+ token := getUserToken(t, nonAdminUsername)
+ req := NewRequest(t, "GET", "/api/v1/admin/users").
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusForbidden)
+}
+
+func TestAPICreateUserInvalidEmail(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ adminUsername := "user1"
+ token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
+ req := NewRequestWithValues(t, "POST", "/api/v1/admin/users", map[string]string{
+ "email": "invalid_email@domain.com\r\n",
+ "full_name": "invalid user",
+ "login_name": "invalidUser",
+ "must_change_password": "true",
+ "password": "password",
+ "send_notify": "true",
+ "source_id": "0",
+ "username": "invalidUser",
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusUnprocessableEntity)
+}
+
+func TestAPICreateAndDeleteUser(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ adminUsername := "user1"
+ token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
+
+ req := NewRequestWithValues(
+ t,
+ "POST",
+ "/api/v1/admin/users",
+ map[string]string{
+ "email": "deleteme@domain.com",
+ "full_name": "delete me",
+ "login_name": "deleteme",
+ "must_change_password": "true",
+ "password": "password",
+ "send_notify": "true",
+ "source_id": "0",
+ "username": "deleteme",
+ },
+ ).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusCreated)
+
+ req = NewRequest(t, "DELETE", "/api/v1/admin/users/deleteme").
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNoContent)
+}
+
+func TestAPIEditUser(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ adminUsername := "user1"
+ token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
+ urlStr := fmt.Sprintf("/api/v1/admin/users/%s", "user2")
+
+ fullNameToChange := "Full Name User 2"
+ req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{
+ "full_name": fullNameToChange,
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusOK)
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{LoginName: "user2"})
+ assert.Equal(t, fullNameToChange, user2.FullName)
+
+ empty := ""
+ req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
+ Email: &empty,
+ }).AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusBadRequest)
+
+ errMap := make(map[string]any)
+ json.Unmarshal(resp.Body.Bytes(), &errMap)
+ assert.EqualValues(t, "e-mail invalid [email: ]", errMap["message"].(string))
+
+ user2 = unittest.AssertExistsAndLoadBean(t, &user_model.User{LoginName: "user2"})
+ assert.False(t, user2.IsRestricted)
+ bTrue := true
+ req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
+ Restricted: &bTrue,
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusOK)
+ user2 = unittest.AssertExistsAndLoadBean(t, &user_model.User{LoginName: "user2"})
+ assert.True(t, user2.IsRestricted)
+}
+
+func TestAPIEditUserWithLoginName(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ adminUsername := "user1"
+ token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
+ urlStr := fmt.Sprintf("/api/v1/admin/users/%s", "user2")
+
+ loginName := "user2"
+ loginSource := int64(0)
+
+ t.Run("login_name only", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
+ LoginName: &loginName,
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusUnprocessableEntity)
+ })
+
+ t.Run("source_id only", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
+ SourceID: &loginSource,
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusUnprocessableEntity)
+ })
+
+ t.Run("login_name & source_id", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
+ LoginName: &loginName,
+ SourceID: &loginSource,
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusOK)
+ })
+}
+
+func TestAPICreateRepoForUser(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ adminUsername := "user1"
+ token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
+
+ req := NewRequestWithJSON(
+ t,
+ "POST",
+ fmt.Sprintf("/api/v1/admin/users/%s/repos", adminUsername),
+ &api.CreateRepoOption{
+ Name: "admincreatedrepo",
+ },
+ ).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusCreated)
+}
+
+func TestAPIRenameUser(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ adminUsername := "user1"
+ token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
+ urlStr := fmt.Sprintf("/api/v1/admin/users/%s/rename", "user2")
+ req := NewRequestWithValues(t, "POST", urlStr, map[string]string{
+ // required
+ "new_name": "User2",
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNoContent)
+
+ urlStr = fmt.Sprintf("/api/v1/admin/users/%s/rename", "User2")
+ req = NewRequestWithValues(t, "POST", urlStr, map[string]string{
+ // required
+ "new_name": "User2-2-2",
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNoContent)
+
+ req = NewRequestWithValues(t, "POST", urlStr, map[string]string{
+ // required
+ "new_name": "user1",
+ }).AddTokenAuth(token)
+ // the old user name still be used by with a redirect
+ MakeRequest(t, req, http.StatusTemporaryRedirect)
+
+ urlStr = fmt.Sprintf("/api/v1/admin/users/%s/rename", "User2-2-2")
+ req = NewRequestWithValues(t, "POST", urlStr, map[string]string{
+ // required
+ "new_name": "user1",
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusUnprocessableEntity)
+
+ req = NewRequestWithValues(t, "POST", urlStr, map[string]string{
+ // required
+ "new_name": "user2",
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNoContent)
+}
+
+func TestAPICron(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ // user1 is an admin user
+ session := loginUser(t, "user1")
+
+ t.Run("List", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadAdmin)
+
+ req := NewRequest(t, "GET", "/api/v1/admin/cron").
+ AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+
+ assert.Equal(t, "28", resp.Header().Get("X-Total-Count"))
+
+ var crons []api.Cron
+ DecodeJSON(t, resp, &crons)
+ assert.Len(t, crons, 28)
+ })
+
+ t.Run("Execute", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ now := time.Now()
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteAdmin)
+ // Archive cleanup is harmless, because in the test environment there are none
+ // and is thus an NOOP operation and therefore doesn't interfere with any other
+ // tests.
+ req := NewRequest(t, "POST", "/api/v1/admin/cron/archive_cleanup").
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNoContent)
+
+ // Check for the latest run time for this cron, to ensure it has been run.
+ req = NewRequest(t, "GET", "/api/v1/admin/cron").
+ AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+
+ var crons []api.Cron
+ DecodeJSON(t, resp, &crons)
+
+ for _, cron := range crons {
+ if cron.Name == "archive_cleanup" {
+ assert.True(t, now.Before(cron.Prev))
+ }
+ }
+ })
+}
+
+func TestAPICreateUser_NotAllowedEmailDomain(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ setting.Service.EmailDomainAllowList = []glob.Glob{glob.MustCompile("example.org")}
+ defer func() {
+ setting.Service.EmailDomainAllowList = []glob.Glob{}
+ }()
+
+ adminUsername := "user1"
+ token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
+
+ req := NewRequestWithValues(t, "POST", "/api/v1/admin/users", map[string]string{
+ "email": "allowedUser1@example1.org",
+ "login_name": "allowedUser1",
+ "username": "allowedUser1",
+ "password": "allowedUser1_pass",
+ "must_change_password": "true",
+ }).AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusCreated)
+ assert.Equal(t, "the domain of user email allowedUser1@example1.org conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", resp.Header().Get("X-Gitea-Warning"))
+
+ req = NewRequest(t, "DELETE", "/api/v1/admin/users/allowedUser1").AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNoContent)
+}
+
+func TestAPIEditUser_NotAllowedEmailDomain(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ setting.Service.EmailDomainAllowList = []glob.Glob{glob.MustCompile("example.org")}
+ defer func() {
+ setting.Service.EmailDomainAllowList = []glob.Glob{}
+ }()
+
+ adminUsername := "user1"
+ token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
+ urlStr := fmt.Sprintf("/api/v1/admin/users/%s", "user2")
+
+ newEmail := "user2@example1.com"
+ req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
+ Email: &newEmail,
+ }).AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ assert.Equal(t, "the domain of user email user2@example1.com conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", resp.Header().Get("X-Gitea-Warning"))
+
+ originalEmail := "user2@example.com"
+ req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
+ Email: &originalEmail,
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusOK)
+}