You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

api_admin_test.go 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. // Copyright 2017 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package integration
  4. import (
  5. "fmt"
  6. "net/http"
  7. "testing"
  8. "time"
  9. asymkey_model "code.gitea.io/gitea/models/asymkey"
  10. auth_model "code.gitea.io/gitea/models/auth"
  11. "code.gitea.io/gitea/models/unittest"
  12. user_model "code.gitea.io/gitea/models/user"
  13. "code.gitea.io/gitea/modules/json"
  14. "code.gitea.io/gitea/modules/setting"
  15. api "code.gitea.io/gitea/modules/structs"
  16. "code.gitea.io/gitea/tests"
  17. "github.com/gobwas/glob"
  18. "github.com/stretchr/testify/assert"
  19. )
  20. func TestAPIAdminCreateAndDeleteSSHKey(t *testing.T) {
  21. defer tests.PrepareTestEnv(t)()
  22. // user1 is an admin user
  23. session := loginUser(t, "user1")
  24. keyOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"})
  25. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteAdmin)
  26. urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys", keyOwner.Name)
  27. req := NewRequestWithValues(t, "POST", urlStr, map[string]string{
  28. "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM= nocomment\n",
  29. "title": "test-key",
  30. }).AddTokenAuth(token)
  31. resp := MakeRequest(t, req, http.StatusCreated)
  32. var newPublicKey api.PublicKey
  33. DecodeJSON(t, resp, &newPublicKey)
  34. unittest.AssertExistsAndLoadBean(t, &asymkey_model.PublicKey{
  35. ID: newPublicKey.ID,
  36. Name: newPublicKey.Title,
  37. Fingerprint: newPublicKey.Fingerprint,
  38. OwnerID: keyOwner.ID,
  39. })
  40. req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d", keyOwner.Name, newPublicKey.ID).
  41. AddTokenAuth(token)
  42. MakeRequest(t, req, http.StatusNoContent)
  43. unittest.AssertNotExistsBean(t, &asymkey_model.PublicKey{ID: newPublicKey.ID})
  44. }
  45. func TestAPIAdminDeleteMissingSSHKey(t *testing.T) {
  46. defer tests.PrepareTestEnv(t)()
  47. // user1 is an admin user
  48. token := getUserToken(t, "user1", auth_model.AccessTokenScopeWriteAdmin)
  49. req := NewRequestf(t, "DELETE", "/api/v1/admin/users/user1/keys/%d", unittest.NonexistentID).
  50. AddTokenAuth(token)
  51. MakeRequest(t, req, http.StatusNotFound)
  52. }
  53. func TestAPIAdminDeleteUnauthorizedKey(t *testing.T) {
  54. defer tests.PrepareTestEnv(t)()
  55. adminUsername := "user1"
  56. normalUsername := "user2"
  57. token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
  58. urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys", adminUsername)
  59. req := NewRequestWithValues(t, "POST", urlStr, map[string]string{
  60. "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM= nocomment\n",
  61. "title": "test-key",
  62. }).AddTokenAuth(token)
  63. resp := MakeRequest(t, req, http.StatusCreated)
  64. var newPublicKey api.PublicKey
  65. DecodeJSON(t, resp, &newPublicKey)
  66. token = getUserToken(t, normalUsername)
  67. req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d", adminUsername, newPublicKey.ID).
  68. AddTokenAuth(token)
  69. MakeRequest(t, req, http.StatusForbidden)
  70. }
  71. func TestAPISudoUser(t *testing.T) {
  72. defer tests.PrepareTestEnv(t)()
  73. adminUsername := "user1"
  74. normalUsername := "user2"
  75. token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeReadUser)
  76. req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user?sudo=%s", normalUsername)).
  77. AddTokenAuth(token)
  78. resp := MakeRequest(t, req, http.StatusOK)
  79. var user api.User
  80. DecodeJSON(t, resp, &user)
  81. assert.Equal(t, normalUsername, user.UserName)
  82. }
  83. func TestAPISudoUserForbidden(t *testing.T) {
  84. defer tests.PrepareTestEnv(t)()
  85. adminUsername := "user1"
  86. normalUsername := "user2"
  87. token := getUserToken(t, normalUsername, auth_model.AccessTokenScopeReadAdmin)
  88. req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user?sudo=%s", adminUsername)).
  89. AddTokenAuth(token)
  90. MakeRequest(t, req, http.StatusForbidden)
  91. }
  92. func TestAPIListUsers(t *testing.T) {
  93. defer tests.PrepareTestEnv(t)()
  94. adminUsername := "user1"
  95. token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeReadAdmin)
  96. req := NewRequest(t, "GET", "/api/v1/admin/users").
  97. AddTokenAuth(token)
  98. resp := MakeRequest(t, req, http.StatusOK)
  99. var users []api.User
  100. DecodeJSON(t, resp, &users)
  101. found := false
  102. for _, user := range users {
  103. if user.UserName == adminUsername {
  104. found = true
  105. }
  106. }
  107. assert.True(t, found)
  108. numberOfUsers := unittest.GetCount(t, &user_model.User{}, "type = 0")
  109. assert.Len(t, users, numberOfUsers)
  110. }
  111. func TestAPIListUsersNotLoggedIn(t *testing.T) {
  112. defer tests.PrepareTestEnv(t)()
  113. req := NewRequest(t, "GET", "/api/v1/admin/users")
  114. MakeRequest(t, req, http.StatusUnauthorized)
  115. }
  116. func TestAPIListUsersNonAdmin(t *testing.T) {
  117. defer tests.PrepareTestEnv(t)()
  118. nonAdminUsername := "user2"
  119. token := getUserToken(t, nonAdminUsername)
  120. req := NewRequest(t, "GET", "/api/v1/admin/users").
  121. AddTokenAuth(token)
  122. MakeRequest(t, req, http.StatusForbidden)
  123. }
  124. func TestAPICreateUserInvalidEmail(t *testing.T) {
  125. defer tests.PrepareTestEnv(t)()
  126. adminUsername := "user1"
  127. token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
  128. req := NewRequestWithValues(t, "POST", "/api/v1/admin/users", map[string]string{
  129. "email": "invalid_email@domain.com\r\n",
  130. "full_name": "invalid user",
  131. "login_name": "invalidUser",
  132. "must_change_password": "true",
  133. "password": "password",
  134. "send_notify": "true",
  135. "source_id": "0",
  136. "username": "invalidUser",
  137. }).AddTokenAuth(token)
  138. MakeRequest(t, req, http.StatusUnprocessableEntity)
  139. }
  140. func TestAPICreateAndDeleteUser(t *testing.T) {
  141. defer tests.PrepareTestEnv(t)()
  142. adminUsername := "user1"
  143. token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
  144. req := NewRequestWithValues(
  145. t,
  146. "POST",
  147. "/api/v1/admin/users",
  148. map[string]string{
  149. "email": "deleteme@domain.com",
  150. "full_name": "delete me",
  151. "login_name": "deleteme",
  152. "must_change_password": "true",
  153. "password": "password",
  154. "send_notify": "true",
  155. "source_id": "0",
  156. "username": "deleteme",
  157. },
  158. ).AddTokenAuth(token)
  159. MakeRequest(t, req, http.StatusCreated)
  160. req = NewRequest(t, "DELETE", "/api/v1/admin/users/deleteme").
  161. AddTokenAuth(token)
  162. MakeRequest(t, req, http.StatusNoContent)
  163. }
  164. func TestAPIEditUser(t *testing.T) {
  165. defer tests.PrepareTestEnv(t)()
  166. adminUsername := "user1"
  167. token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
  168. urlStr := fmt.Sprintf("/api/v1/admin/users/%s", "user2")
  169. req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{
  170. // required
  171. "login_name": "user2",
  172. "source_id": "0",
  173. // to change
  174. "full_name": "Full Name User 2",
  175. }).AddTokenAuth(token)
  176. MakeRequest(t, req, http.StatusOK)
  177. empty := ""
  178. req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
  179. LoginName: "user2",
  180. SourceID: 0,
  181. Email: &empty,
  182. }).AddTokenAuth(token)
  183. resp := MakeRequest(t, req, http.StatusBadRequest)
  184. errMap := make(map[string]any)
  185. json.Unmarshal(resp.Body.Bytes(), &errMap)
  186. assert.EqualValues(t, "e-mail invalid [email: ]", errMap["message"].(string))
  187. user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{LoginName: "user2"})
  188. assert.False(t, user2.IsRestricted)
  189. bTrue := true
  190. req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
  191. // required
  192. LoginName: "user2",
  193. SourceID: 0,
  194. // to change
  195. Restricted: &bTrue,
  196. }).AddTokenAuth(token)
  197. MakeRequest(t, req, http.StatusOK)
  198. user2 = unittest.AssertExistsAndLoadBean(t, &user_model.User{LoginName: "user2"})
  199. assert.True(t, user2.IsRestricted)
  200. }
  201. func TestAPICreateRepoForUser(t *testing.T) {
  202. defer tests.PrepareTestEnv(t)()
  203. adminUsername := "user1"
  204. token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
  205. req := NewRequestWithJSON(
  206. t,
  207. "POST",
  208. fmt.Sprintf("/api/v1/admin/users/%s/repos", adminUsername),
  209. &api.CreateRepoOption{
  210. Name: "admincreatedrepo",
  211. },
  212. ).AddTokenAuth(token)
  213. MakeRequest(t, req, http.StatusCreated)
  214. }
  215. func TestAPIRenameUser(t *testing.T) {
  216. defer tests.PrepareTestEnv(t)()
  217. adminUsername := "user1"
  218. token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
  219. urlStr := fmt.Sprintf("/api/v1/admin/users/%s/rename", "user2")
  220. req := NewRequestWithValues(t, "POST", urlStr, map[string]string{
  221. // required
  222. "new_name": "User2",
  223. }).AddTokenAuth(token)
  224. MakeRequest(t, req, http.StatusNoContent)
  225. urlStr = fmt.Sprintf("/api/v1/admin/users/%s/rename", "User2")
  226. req = NewRequestWithValues(t, "POST", urlStr, map[string]string{
  227. // required
  228. "new_name": "User2-2-2",
  229. }).AddTokenAuth(token)
  230. MakeRequest(t, req, http.StatusNoContent)
  231. req = NewRequestWithValues(t, "POST", urlStr, map[string]string{
  232. // required
  233. "new_name": "user1",
  234. }).AddTokenAuth(token)
  235. // the old user name still be used by with a redirect
  236. MakeRequest(t, req, http.StatusTemporaryRedirect)
  237. urlStr = fmt.Sprintf("/api/v1/admin/users/%s/rename", "User2-2-2")
  238. req = NewRequestWithValues(t, "POST", urlStr, map[string]string{
  239. // required
  240. "new_name": "user1",
  241. }).AddTokenAuth(token)
  242. MakeRequest(t, req, http.StatusUnprocessableEntity)
  243. req = NewRequestWithValues(t, "POST", urlStr, map[string]string{
  244. // required
  245. "new_name": "user2",
  246. }).AddTokenAuth(token)
  247. MakeRequest(t, req, http.StatusNoContent)
  248. }
  249. func TestAPICron(t *testing.T) {
  250. defer tests.PrepareTestEnv(t)()
  251. // user1 is an admin user
  252. session := loginUser(t, "user1")
  253. t.Run("List", func(t *testing.T) {
  254. defer tests.PrintCurrentTest(t)()
  255. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadAdmin)
  256. req := NewRequest(t, "GET", "/api/v1/admin/cron").
  257. AddTokenAuth(token)
  258. resp := MakeRequest(t, req, http.StatusOK)
  259. assert.Equal(t, "28", resp.Header().Get("X-Total-Count"))
  260. var crons []api.Cron
  261. DecodeJSON(t, resp, &crons)
  262. assert.Len(t, crons, 28)
  263. })
  264. t.Run("Execute", func(t *testing.T) {
  265. defer tests.PrintCurrentTest(t)()
  266. now := time.Now()
  267. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteAdmin)
  268. // Archive cleanup is harmless, because in the test environment there are none
  269. // and is thus an NOOP operation and therefore doesn't interfere with any other
  270. // tests.
  271. req := NewRequest(t, "POST", "/api/v1/admin/cron/archive_cleanup").
  272. AddTokenAuth(token)
  273. MakeRequest(t, req, http.StatusNoContent)
  274. // Check for the latest run time for this cron, to ensure it has been run.
  275. req = NewRequest(t, "GET", "/api/v1/admin/cron").
  276. AddTokenAuth(token)
  277. resp := MakeRequest(t, req, http.StatusOK)
  278. var crons []api.Cron
  279. DecodeJSON(t, resp, &crons)
  280. for _, cron := range crons {
  281. if cron.Name == "archive_cleanup" {
  282. assert.True(t, now.Before(cron.Prev))
  283. }
  284. }
  285. })
  286. }
  287. func TestAPICreateUser_NotAllowedEmailDomain(t *testing.T) {
  288. defer tests.PrepareTestEnv(t)()
  289. setting.Service.EmailDomainAllowList = []glob.Glob{glob.MustCompile("example.org")}
  290. defer func() {
  291. setting.Service.EmailDomainAllowList = []glob.Glob{}
  292. }()
  293. adminUsername := "user1"
  294. token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
  295. req := NewRequestWithValues(t, "POST", "/api/v1/admin/users", map[string]string{
  296. "email": "allowedUser1@example1.org",
  297. "login_name": "allowedUser1",
  298. "username": "allowedUser1",
  299. "password": "allowedUser1_pass",
  300. "must_change_password": "true",
  301. }).AddTokenAuth(token)
  302. resp := MakeRequest(t, req, http.StatusCreated)
  303. 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"))
  304. req = NewRequest(t, "DELETE", "/api/v1/admin/users/allowedUser1").AddTokenAuth(token)
  305. MakeRequest(t, req, http.StatusNoContent)
  306. }
  307. func TestAPIEditUser_NotAllowedEmailDomain(t *testing.T) {
  308. defer tests.PrepareTestEnv(t)()
  309. setting.Service.EmailDomainAllowList = []glob.Glob{glob.MustCompile("example.org")}
  310. defer func() {
  311. setting.Service.EmailDomainAllowList = []glob.Glob{}
  312. }()
  313. adminUsername := "user1"
  314. token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
  315. urlStr := fmt.Sprintf("/api/v1/admin/users/%s", "user2")
  316. newEmail := "user2@example1.com"
  317. req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
  318. LoginName: "user2",
  319. SourceID: 0,
  320. Email: &newEmail,
  321. }).AddTokenAuth(token)
  322. resp := MakeRequest(t, req, http.StatusOK)
  323. 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"))
  324. originalEmail := "user2@example.com"
  325. req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
  326. LoginName: "user2",
  327. SourceID: 0,
  328. Email: &originalEmail,
  329. }).AddTokenAuth(token)
  330. MakeRequest(t, req, http.StatusOK)
  331. }