gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  1. // Copyright 2024 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package integration
  4. import (
  5. "bytes"
  6. "crypto/sha256"
  7. "encoding/hex"
  8. "encoding/xml"
  9. "fmt"
  10. "io"
  11. "net/http"
  12. "strings"
  13. "testing"
  14. "time"
  15. auth_model "code.gitea.io/gitea/models/auth"
  16. repo_model "code.gitea.io/gitea/models/repo"
  17. "code.gitea.io/gitea/models/unittest"
  18. user_model "code.gitea.io/gitea/models/user"
  19. "code.gitea.io/gitea/modules/json"
  20. "code.gitea.io/gitea/modules/storage"
  21. api "code.gitea.io/gitea/modules/structs"
  22. "code.gitea.io/gitea/routers/api/actions"
  23. actions_service "code.gitea.io/gitea/services/actions"
  24. "github.com/stretchr/testify/assert"
  25. "google.golang.org/protobuf/encoding/protojson"
  26. "google.golang.org/protobuf/reflect/protoreflect"
  27. "google.golang.org/protobuf/types/known/timestamppb"
  28. "google.golang.org/protobuf/types/known/wrapperspb"
  29. )
  30. func toProtoJSON(m protoreflect.ProtoMessage) io.Reader {
  31. resp, _ := protojson.Marshal(m)
  32. buf := bytes.Buffer{}
  33. buf.Write(resp)
  34. return &buf
  35. }
  36. func TestActionsArtifactV4UploadSingleFile(t *testing.T) {
  37. defer prepareTestEnvActionsArtifacts(t)()
  38. token, err := actions_service.CreateAuthorizationToken(48, 792, 193)
  39. assert.NoError(t, err)
  40. // acquire artifact upload url
  41. req := NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact", toProtoJSON(&actions.CreateArtifactRequest{
  42. Version: 4,
  43. Name: "artifact",
  44. WorkflowRunBackendId: "792",
  45. WorkflowJobRunBackendId: "193",
  46. })).AddTokenAuth(token)
  47. resp := MakeRequest(t, req, http.StatusOK)
  48. var uploadResp actions.CreateArtifactResponse
  49. protojson.Unmarshal(resp.Body.Bytes(), &uploadResp)
  50. assert.True(t, uploadResp.Ok)
  51. assert.Contains(t, uploadResp.SignedUploadUrl, "/twirp/github.actions.results.api.v1.ArtifactService/UploadArtifact")
  52. // get upload url
  53. idx := strings.Index(uploadResp.SignedUploadUrl, "/twirp/")
  54. url := uploadResp.SignedUploadUrl[idx:] + "&comp=block"
  55. // upload artifact chunk
  56. body := strings.Repeat("A", 1024)
  57. req = NewRequestWithBody(t, "PUT", url, strings.NewReader(body))
  58. MakeRequest(t, req, http.StatusCreated)
  59. t.Logf("Create artifact confirm")
  60. sha := sha256.Sum256([]byte(body))
  61. // confirm artifact upload
  62. req = NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/FinalizeArtifact", toProtoJSON(&actions.FinalizeArtifactRequest{
  63. Name: "artifact",
  64. Size: 1024,
  65. Hash: wrapperspb.String("sha256:" + hex.EncodeToString(sha[:])),
  66. WorkflowRunBackendId: "792",
  67. WorkflowJobRunBackendId: "193",
  68. })).
  69. AddTokenAuth(token)
  70. resp = MakeRequest(t, req, http.StatusOK)
  71. var finalizeResp actions.FinalizeArtifactResponse
  72. protojson.Unmarshal(resp.Body.Bytes(), &finalizeResp)
  73. assert.True(t, finalizeResp.Ok)
  74. }
  75. func TestActionsArtifactV4UploadSingleFileWrongChecksum(t *testing.T) {
  76. defer prepareTestEnvActionsArtifacts(t)()
  77. token, err := actions_service.CreateAuthorizationToken(48, 792, 193)
  78. assert.NoError(t, err)
  79. // acquire artifact upload url
  80. req := NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact", toProtoJSON(&actions.CreateArtifactRequest{
  81. Version: 4,
  82. Name: "artifact-invalid-checksum",
  83. WorkflowRunBackendId: "792",
  84. WorkflowJobRunBackendId: "193",
  85. })).AddTokenAuth(token)
  86. resp := MakeRequest(t, req, http.StatusOK)
  87. var uploadResp actions.CreateArtifactResponse
  88. protojson.Unmarshal(resp.Body.Bytes(), &uploadResp)
  89. assert.True(t, uploadResp.Ok)
  90. assert.Contains(t, uploadResp.SignedUploadUrl, "/twirp/github.actions.results.api.v1.ArtifactService/UploadArtifact")
  91. // get upload url
  92. idx := strings.Index(uploadResp.SignedUploadUrl, "/twirp/")
  93. url := uploadResp.SignedUploadUrl[idx:] + "&comp=block"
  94. // upload artifact chunk
  95. body := strings.Repeat("B", 1024)
  96. req = NewRequestWithBody(t, "PUT", url, strings.NewReader(body))
  97. MakeRequest(t, req, http.StatusCreated)
  98. t.Logf("Create artifact confirm")
  99. sha := sha256.Sum256([]byte(strings.Repeat("A", 1024)))
  100. // confirm artifact upload
  101. req = NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/FinalizeArtifact", toProtoJSON(&actions.FinalizeArtifactRequest{
  102. Name: "artifact-invalid-checksum",
  103. Size: 1024,
  104. Hash: wrapperspb.String("sha256:" + hex.EncodeToString(sha[:])),
  105. WorkflowRunBackendId: "792",
  106. WorkflowJobRunBackendId: "193",
  107. })).
  108. AddTokenAuth(token)
  109. MakeRequest(t, req, http.StatusInternalServerError)
  110. }
  111. func TestActionsArtifactV4UploadSingleFileWithRetentionDays(t *testing.T) {
  112. defer prepareTestEnvActionsArtifacts(t)()
  113. token, err := actions_service.CreateAuthorizationToken(48, 792, 193)
  114. assert.NoError(t, err)
  115. // acquire artifact upload url
  116. req := NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact", toProtoJSON(&actions.CreateArtifactRequest{
  117. Version: 4,
  118. ExpiresAt: timestamppb.New(time.Now().Add(5 * 24 * time.Hour)),
  119. Name: "artifactWithRetentionDays",
  120. WorkflowRunBackendId: "792",
  121. WorkflowJobRunBackendId: "193",
  122. })).AddTokenAuth(token)
  123. resp := MakeRequest(t, req, http.StatusOK)
  124. var uploadResp actions.CreateArtifactResponse
  125. protojson.Unmarshal(resp.Body.Bytes(), &uploadResp)
  126. assert.True(t, uploadResp.Ok)
  127. assert.Contains(t, uploadResp.SignedUploadUrl, "/twirp/github.actions.results.api.v1.ArtifactService/UploadArtifact")
  128. // get upload url
  129. idx := strings.Index(uploadResp.SignedUploadUrl, "/twirp/")
  130. url := uploadResp.SignedUploadUrl[idx:] + "&comp=block"
  131. // upload artifact chunk
  132. body := strings.Repeat("A", 1024)
  133. req = NewRequestWithBody(t, "PUT", url, strings.NewReader(body))
  134. MakeRequest(t, req, http.StatusCreated)
  135. t.Logf("Create artifact confirm")
  136. sha := sha256.Sum256([]byte(body))
  137. // confirm artifact upload
  138. req = NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/FinalizeArtifact", toProtoJSON(&actions.FinalizeArtifactRequest{
  139. Name: "artifactWithRetentionDays",
  140. Size: 1024,
  141. Hash: wrapperspb.String("sha256:" + hex.EncodeToString(sha[:])),
  142. WorkflowRunBackendId: "792",
  143. WorkflowJobRunBackendId: "193",
  144. })).
  145. AddTokenAuth(token)
  146. resp = MakeRequest(t, req, http.StatusOK)
  147. var finalizeResp actions.FinalizeArtifactResponse
  148. protojson.Unmarshal(resp.Body.Bytes(), &finalizeResp)
  149. assert.True(t, finalizeResp.Ok)
  150. }
  151. func TestActionsArtifactV4UploadSingleFileWithPotentialHarmfulBlockID(t *testing.T) {
  152. defer prepareTestEnvActionsArtifacts(t)()
  153. token, err := actions_service.CreateAuthorizationToken(48, 792, 193)
  154. assert.NoError(t, err)
  155. // acquire artifact upload url
  156. req := NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact", toProtoJSON(&actions.CreateArtifactRequest{
  157. Version: 4,
  158. Name: "artifactWithPotentialHarmfulBlockID",
  159. WorkflowRunBackendId: "792",
  160. WorkflowJobRunBackendId: "193",
  161. })).AddTokenAuth(token)
  162. resp := MakeRequest(t, req, http.StatusOK)
  163. var uploadResp actions.CreateArtifactResponse
  164. protojson.Unmarshal(resp.Body.Bytes(), &uploadResp)
  165. assert.True(t, uploadResp.Ok)
  166. assert.Contains(t, uploadResp.SignedUploadUrl, "/twirp/github.actions.results.api.v1.ArtifactService/UploadArtifact")
  167. // get upload urls
  168. idx := strings.Index(uploadResp.SignedUploadUrl, "/twirp/")
  169. url := uploadResp.SignedUploadUrl[idx:] + "&comp=block&blockid=%2f..%2fmyfile"
  170. blockListURL := uploadResp.SignedUploadUrl[idx:] + "&comp=blocklist"
  171. // upload artifact chunk
  172. body := strings.Repeat("A", 1024)
  173. req = NewRequestWithBody(t, "PUT", url, strings.NewReader(body))
  174. MakeRequest(t, req, http.StatusCreated)
  175. // verify that the exploit didn't work
  176. _, err = storage.Actions.Stat("myfile")
  177. assert.Error(t, err)
  178. // upload artifact blockList
  179. blockList := &actions.BlockList{
  180. Latest: []string{
  181. "/../myfile",
  182. },
  183. }
  184. rawBlockList, err := xml.Marshal(blockList)
  185. assert.NoError(t, err)
  186. req = NewRequestWithBody(t, "PUT", blockListURL, bytes.NewReader(rawBlockList))
  187. MakeRequest(t, req, http.StatusCreated)
  188. t.Logf("Create artifact confirm")
  189. sha := sha256.Sum256([]byte(body))
  190. // confirm artifact upload
  191. req = NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/FinalizeArtifact", toProtoJSON(&actions.FinalizeArtifactRequest{
  192. Name: "artifactWithPotentialHarmfulBlockID",
  193. Size: 1024,
  194. Hash: wrapperspb.String("sha256:" + hex.EncodeToString(sha[:])),
  195. WorkflowRunBackendId: "792",
  196. WorkflowJobRunBackendId: "193",
  197. })).
  198. AddTokenAuth(token)
  199. resp = MakeRequest(t, req, http.StatusOK)
  200. var finalizeResp actions.FinalizeArtifactResponse
  201. protojson.Unmarshal(resp.Body.Bytes(), &finalizeResp)
  202. assert.True(t, finalizeResp.Ok)
  203. }
  204. func TestActionsArtifactV4UploadSingleFileWithChunksOutOfOrder(t *testing.T) {
  205. defer prepareTestEnvActionsArtifacts(t)()
  206. token, err := actions_service.CreateAuthorizationToken(48, 792, 193)
  207. assert.NoError(t, err)
  208. // acquire artifact upload url
  209. req := NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact", toProtoJSON(&actions.CreateArtifactRequest{
  210. Version: 4,
  211. Name: "artifactWithChunksOutOfOrder",
  212. WorkflowRunBackendId: "792",
  213. WorkflowJobRunBackendId: "193",
  214. })).AddTokenAuth(token)
  215. resp := MakeRequest(t, req, http.StatusOK)
  216. var uploadResp actions.CreateArtifactResponse
  217. protojson.Unmarshal(resp.Body.Bytes(), &uploadResp)
  218. assert.True(t, uploadResp.Ok)
  219. assert.Contains(t, uploadResp.SignedUploadUrl, "/twirp/github.actions.results.api.v1.ArtifactService/UploadArtifact")
  220. // get upload urls
  221. idx := strings.Index(uploadResp.SignedUploadUrl, "/twirp/")
  222. block1URL := uploadResp.SignedUploadUrl[idx:] + "&comp=block&blockid=block1"
  223. block2URL := uploadResp.SignedUploadUrl[idx:] + "&comp=block&blockid=block2"
  224. blockListURL := uploadResp.SignedUploadUrl[idx:] + "&comp=blocklist"
  225. // upload artifact chunks
  226. bodyb := strings.Repeat("B", 1024)
  227. req = NewRequestWithBody(t, "PUT", block2URL, strings.NewReader(bodyb))
  228. MakeRequest(t, req, http.StatusCreated)
  229. bodya := strings.Repeat("A", 1024)
  230. req = NewRequestWithBody(t, "PUT", block1URL, strings.NewReader(bodya))
  231. MakeRequest(t, req, http.StatusCreated)
  232. // upload artifact blockList
  233. blockList := &actions.BlockList{
  234. Latest: []string{
  235. "block1",
  236. "block2",
  237. },
  238. }
  239. rawBlockList, err := xml.Marshal(blockList)
  240. assert.NoError(t, err)
  241. req = NewRequestWithBody(t, "PUT", blockListURL, bytes.NewReader(rawBlockList))
  242. MakeRequest(t, req, http.StatusCreated)
  243. t.Logf("Create artifact confirm")
  244. sha := sha256.Sum256([]byte(bodya + bodyb))
  245. // confirm artifact upload
  246. req = NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/FinalizeArtifact", toProtoJSON(&actions.FinalizeArtifactRequest{
  247. Name: "artifactWithChunksOutOfOrder",
  248. Size: 2048,
  249. Hash: wrapperspb.String("sha256:" + hex.EncodeToString(sha[:])),
  250. WorkflowRunBackendId: "792",
  251. WorkflowJobRunBackendId: "193",
  252. })).
  253. AddTokenAuth(token)
  254. resp = MakeRequest(t, req, http.StatusOK)
  255. var finalizeResp actions.FinalizeArtifactResponse
  256. protojson.Unmarshal(resp.Body.Bytes(), &finalizeResp)
  257. assert.True(t, finalizeResp.Ok)
  258. }
  259. func TestActionsArtifactV4DownloadSingle(t *testing.T) {
  260. defer prepareTestEnvActionsArtifacts(t)()
  261. token, err := actions_service.CreateAuthorizationToken(48, 792, 193)
  262. assert.NoError(t, err)
  263. // acquire artifact upload url
  264. req := NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/ListArtifacts", toProtoJSON(&actions.ListArtifactsRequest{
  265. NameFilter: wrapperspb.String("artifact-v4-download"),
  266. WorkflowRunBackendId: "792",
  267. WorkflowJobRunBackendId: "193",
  268. })).AddTokenAuth(token)
  269. resp := MakeRequest(t, req, http.StatusOK)
  270. var listResp actions.ListArtifactsResponse
  271. protojson.Unmarshal(resp.Body.Bytes(), &listResp)
  272. assert.Len(t, listResp.Artifacts, 1)
  273. // confirm artifact upload
  274. req = NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/GetSignedArtifactURL", toProtoJSON(&actions.GetSignedArtifactURLRequest{
  275. Name: "artifact-v4-download",
  276. WorkflowRunBackendId: "792",
  277. WorkflowJobRunBackendId: "193",
  278. })).
  279. AddTokenAuth(token)
  280. resp = MakeRequest(t, req, http.StatusOK)
  281. var finalizeResp actions.GetSignedArtifactURLResponse
  282. protojson.Unmarshal(resp.Body.Bytes(), &finalizeResp)
  283. assert.NotEmpty(t, finalizeResp.SignedUrl)
  284. req = NewRequest(t, "GET", finalizeResp.SignedUrl)
  285. resp = MakeRequest(t, req, http.StatusOK)
  286. body := strings.Repeat("D", 1024)
  287. assert.Equal(t, body, resp.Body.String())
  288. }
  289. func TestActionsArtifactV4RunDownloadSinglePublicApi(t *testing.T) {
  290. defer prepareTestEnvActionsArtifacts(t)()
  291. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
  292. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
  293. session := loginUser(t, user.Name)
  294. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  295. // confirm artifact can be listed and found by name
  296. req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/runs/792/artifacts?name=artifact-v4-download", repo.FullName()), nil).
  297. AddTokenAuth(token)
  298. resp := MakeRequest(t, req, http.StatusOK)
  299. var listResp api.ActionArtifactsResponse
  300. err := json.Unmarshal(resp.Body.Bytes(), &listResp)
  301. assert.NoError(t, err)
  302. assert.NotEmpty(t, listResp.Entries[0].ArchiveDownloadURL)
  303. assert.Equal(t, "artifact-v4-download", listResp.Entries[0].Name)
  304. // confirm artifact blob storage url can be retrieved
  305. req = NewRequestWithBody(t, "GET", listResp.Entries[0].ArchiveDownloadURL, nil).
  306. AddTokenAuth(token)
  307. resp = MakeRequest(t, req, http.StatusFound)
  308. // confirm artifact can be downloaded and has expected content
  309. req = NewRequestWithBody(t, "GET", resp.Header().Get("Location"), nil).
  310. AddTokenAuth(token)
  311. resp = MakeRequest(t, req, http.StatusOK)
  312. body := strings.Repeat("D", 1024)
  313. assert.Equal(t, body, resp.Body.String())
  314. }
  315. func TestActionsArtifactV4DownloadSinglePublicApi(t *testing.T) {
  316. defer prepareTestEnvActionsArtifacts(t)()
  317. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
  318. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
  319. session := loginUser(t, user.Name)
  320. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  321. // confirm artifact can be listed and found by name
  322. req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts?name=artifact-v4-download", repo.FullName()), nil).
  323. AddTokenAuth(token)
  324. resp := MakeRequest(t, req, http.StatusOK)
  325. var listResp api.ActionArtifactsResponse
  326. err := json.Unmarshal(resp.Body.Bytes(), &listResp)
  327. assert.NoError(t, err)
  328. assert.NotEmpty(t, listResp.Entries[0].ArchiveDownloadURL)
  329. assert.Equal(t, "artifact-v4-download", listResp.Entries[0].Name)
  330. // confirm artifact blob storage url can be retrieved
  331. req = NewRequestWithBody(t, "GET", listResp.Entries[0].ArchiveDownloadURL, nil).
  332. AddTokenAuth(token)
  333. resp = MakeRequest(t, req, http.StatusFound)
  334. blobLocation := resp.Header().Get("Location")
  335. // confirm artifact can be downloaded without token and has expected content
  336. req = NewRequestWithBody(t, "GET", blobLocation, nil)
  337. resp = MakeRequest(t, req, http.StatusOK)
  338. body := strings.Repeat("D", 1024)
  339. assert.Equal(t, body, resp.Body.String())
  340. // confirm artifact can not be downloaded without query
  341. req = NewRequestWithBody(t, "GET", blobLocation, nil)
  342. req.URL.RawQuery = ""
  343. _ = MakeRequest(t, req, http.StatusUnauthorized)
  344. }
  345. func TestActionsArtifactV4DownloadSinglePublicApiPrivateRepo(t *testing.T) {
  346. defer prepareTestEnvActionsArtifacts(t)()
  347. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
  348. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
  349. session := loginUser(t, user.Name)
  350. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  351. // confirm artifact can be listed and found by name
  352. req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts?name=artifact-v4-download", repo.FullName()), nil).
  353. AddTokenAuth(token)
  354. resp := MakeRequest(t, req, http.StatusOK)
  355. var listResp api.ActionArtifactsResponse
  356. err := json.Unmarshal(resp.Body.Bytes(), &listResp)
  357. assert.NoError(t, err)
  358. assert.Equal(t, int64(23), listResp.Entries[0].ID)
  359. assert.NotEmpty(t, listResp.Entries[0].ArchiveDownloadURL)
  360. assert.Equal(t, "artifact-v4-download", listResp.Entries[0].Name)
  361. // confirm artifact blob storage url can be retrieved
  362. req = NewRequestWithBody(t, "GET", listResp.Entries[0].ArchiveDownloadURL, nil).
  363. AddTokenAuth(token)
  364. resp = MakeRequest(t, req, http.StatusFound)
  365. blobLocation := resp.Header().Get("Location")
  366. // confirm artifact can be downloaded without token and has expected content
  367. req = NewRequestWithBody(t, "GET", blobLocation, nil)
  368. resp = MakeRequest(t, req, http.StatusOK)
  369. body := strings.Repeat("D", 1024)
  370. assert.Equal(t, body, resp.Body.String())
  371. // confirm artifact can not be downloaded without query
  372. req = NewRequestWithBody(t, "GET", blobLocation, nil)
  373. req.URL.RawQuery = ""
  374. _ = MakeRequest(t, req, http.StatusUnauthorized)
  375. }
  376. func TestActionsArtifactV4ListAndGetPublicApi(t *testing.T) {
  377. defer prepareTestEnvActionsArtifacts(t)()
  378. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
  379. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
  380. session := loginUser(t, user.Name)
  381. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  382. // confirm artifact can be listed
  383. req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts", repo.FullName()), nil).
  384. AddTokenAuth(token)
  385. resp := MakeRequest(t, req, http.StatusOK)
  386. var listResp api.ActionArtifactsResponse
  387. err := json.Unmarshal(resp.Body.Bytes(), &listResp)
  388. assert.NoError(t, err)
  389. for _, artifact := range listResp.Entries {
  390. assert.Contains(t, artifact.URL, fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), artifact.ID))
  391. assert.Contains(t, artifact.ArchiveDownloadURL, fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d/zip", repo.FullName(), artifact.ID))
  392. req = NewRequestWithBody(t, "GET", listResp.Entries[0].URL, nil).
  393. AddTokenAuth(token)
  394. resp = MakeRequest(t, req, http.StatusOK)
  395. var artifactResp api.ActionArtifact
  396. err := json.Unmarshal(resp.Body.Bytes(), &artifactResp)
  397. assert.NoError(t, err)
  398. assert.Equal(t, artifact.ID, artifactResp.ID)
  399. assert.Equal(t, artifact.Name, artifactResp.Name)
  400. assert.Equal(t, artifact.SizeInBytes, artifactResp.SizeInBytes)
  401. assert.Equal(t, artifact.URL, artifactResp.URL)
  402. assert.Equal(t, artifact.ArchiveDownloadURL, artifactResp.ArchiveDownloadURL)
  403. }
  404. }
  405. func TestActionsArtifactV4GetArtifactMismatchedRepoNotFound(t *testing.T) {
  406. defer prepareTestEnvActionsArtifacts(t)()
  407. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
  408. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
  409. session := loginUser(t, user.Name)
  410. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  411. // confirm artifacts of wrong repo is not visible
  412. req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), 22), nil).
  413. AddTokenAuth(token)
  414. MakeRequest(t, req, http.StatusNotFound)
  415. }
  416. func TestActionsArtifactV4DownloadArtifactMismatchedRepoNotFound(t *testing.T) {
  417. defer prepareTestEnvActionsArtifacts(t)()
  418. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
  419. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
  420. session := loginUser(t, user.Name)
  421. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  422. // confirm artifacts of wrong repo is not visible
  423. req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d/zip", repo.FullName(), 22), nil).
  424. AddTokenAuth(token)
  425. MakeRequest(t, req, http.StatusNotFound)
  426. }
  427. func TestActionsArtifactV4DownloadArtifactCorrectRepoFound(t *testing.T) {
  428. defer prepareTestEnvActionsArtifacts(t)()
  429. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
  430. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
  431. session := loginUser(t, user.Name)
  432. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  433. // confirm artifacts of correct repo is visible
  434. req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d/zip", repo.FullName(), 22), nil).
  435. AddTokenAuth(token)
  436. MakeRequest(t, req, http.StatusFound)
  437. }
  438. func TestActionsArtifactV4DownloadRawArtifactCorrectRepoMissingSignatureUnauthorized(t *testing.T) {
  439. defer prepareTestEnvActionsArtifacts(t)()
  440. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
  441. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
  442. session := loginUser(t, user.Name)
  443. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  444. // confirm cannot use the raw artifact endpoint even with a correct access token
  445. req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d/zip/raw", repo.FullName(), 22), nil).
  446. AddTokenAuth(token)
  447. MakeRequest(t, req, http.StatusUnauthorized)
  448. }
  449. func TestActionsArtifactV4Delete(t *testing.T) {
  450. defer prepareTestEnvActionsArtifacts(t)()
  451. token, err := actions_service.CreateAuthorizationToken(48, 792, 193)
  452. assert.NoError(t, err)
  453. // delete artifact by name
  454. req := NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/DeleteArtifact", toProtoJSON(&actions.DeleteArtifactRequest{
  455. Name: "artifact-v4-download",
  456. WorkflowRunBackendId: "792",
  457. WorkflowJobRunBackendId: "193",
  458. })).AddTokenAuth(token)
  459. resp := MakeRequest(t, req, http.StatusOK)
  460. var deleteResp actions.DeleteArtifactResponse
  461. protojson.Unmarshal(resp.Body.Bytes(), &deleteResp)
  462. assert.True(t, deleteResp.Ok)
  463. // confirm artifact is no longer accessible by GetSignedArtifactURL
  464. req = NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/GetSignedArtifactURL", toProtoJSON(&actions.GetSignedArtifactURLRequest{
  465. Name: "artifact-v4-download",
  466. WorkflowRunBackendId: "792",
  467. WorkflowJobRunBackendId: "193",
  468. })).
  469. AddTokenAuth(token)
  470. _ = MakeRequest(t, req, http.StatusNotFound)
  471. // confirm artifact is no longer enumerateable by ListArtifacts and returns length == 0 without error
  472. req = NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/ListArtifacts", toProtoJSON(&actions.ListArtifactsRequest{
  473. NameFilter: wrapperspb.String("artifact-v4-download"),
  474. WorkflowRunBackendId: "792",
  475. WorkflowJobRunBackendId: "193",
  476. })).AddTokenAuth(token)
  477. resp = MakeRequest(t, req, http.StatusOK)
  478. var listResp actions.ListArtifactsResponse
  479. protojson.Unmarshal(resp.Body.Bytes(), &listResp)
  480. assert.Empty(t, listResp.Artifacts)
  481. }
  482. func TestActionsArtifactV4DeletePublicApi(t *testing.T) {
  483. defer prepareTestEnvActionsArtifacts(t)()
  484. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
  485. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
  486. session := loginUser(t, user.Name)
  487. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  488. // confirm artifacts exists
  489. req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), 22), nil).
  490. AddTokenAuth(token)
  491. MakeRequest(t, req, http.StatusOK)
  492. // delete artifact by id
  493. req = NewRequestWithBody(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), 22), nil).
  494. AddTokenAuth(token)
  495. MakeRequest(t, req, http.StatusNoContent)
  496. // confirm artifacts has been deleted
  497. req = NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), 22), nil).
  498. AddTokenAuth(token)
  499. MakeRequest(t, req, http.StatusNotFound)
  500. }
  501. func TestActionsArtifactV4DeletePublicApiNotAllowedReadScope(t *testing.T) {
  502. defer prepareTestEnvActionsArtifacts(t)()
  503. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
  504. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
  505. session := loginUser(t, user.Name)
  506. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
  507. // confirm artifacts exists
  508. req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), 22), nil).
  509. AddTokenAuth(token)
  510. MakeRequest(t, req, http.StatusOK)
  511. // try delete artifact by id
  512. req = NewRequestWithBody(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), 22), nil).
  513. AddTokenAuth(token)
  514. MakeRequest(t, req, http.StatusForbidden)
  515. // confirm artifacts has not been deleted
  516. req = NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), 22), nil).
  517. AddTokenAuth(token)
  518. MakeRequest(t, req, http.StatusOK)
  519. }