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.

gpg_git_test.go 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package integration
  4. import (
  5. "encoding/base64"
  6. "fmt"
  7. "net/url"
  8. "os"
  9. "testing"
  10. "code.gitea.io/gitea/models/unittest"
  11. user_model "code.gitea.io/gitea/models/user"
  12. "code.gitea.io/gitea/modules/process"
  13. "code.gitea.io/gitea/modules/setting"
  14. api "code.gitea.io/gitea/modules/structs"
  15. "code.gitea.io/gitea/tests"
  16. "github.com/stretchr/testify/assert"
  17. "golang.org/x/crypto/openpgp"
  18. "golang.org/x/crypto/openpgp/armor"
  19. )
  20. func TestGPGGit(t *testing.T) {
  21. defer tests.PrepareTestEnv(t)()
  22. username := "user2"
  23. // OK Set a new GPG home
  24. tmpDir := t.TempDir()
  25. err := os.Chmod(tmpDir, 0o700)
  26. assert.NoError(t, err)
  27. oldGNUPGHome := os.Getenv("GNUPGHOME")
  28. err = os.Setenv("GNUPGHOME", tmpDir)
  29. assert.NoError(t, err)
  30. defer os.Setenv("GNUPGHOME", oldGNUPGHome)
  31. // Need to create a root key
  32. rootKeyPair, err := importTestingKey(tmpDir, "gitea", "gitea@fake.local")
  33. assert.NoError(t, err)
  34. if err != nil {
  35. assert.FailNow(t, "Unable to import rootKeyPair")
  36. }
  37. rootKeyID := rootKeyPair.PrimaryKey.KeyIdShortString()
  38. oldKeyID := setting.Repository.Signing.SigningKey
  39. oldName := setting.Repository.Signing.SigningName
  40. oldEmail := setting.Repository.Signing.SigningEmail
  41. defer func() {
  42. setting.Repository.Signing.SigningKey = oldKeyID
  43. setting.Repository.Signing.SigningName = oldName
  44. setting.Repository.Signing.SigningEmail = oldEmail
  45. }()
  46. setting.Repository.Signing.SigningKey = rootKeyID
  47. setting.Repository.Signing.SigningName = "gitea"
  48. setting.Repository.Signing.SigningEmail = "gitea@fake.local"
  49. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: username})
  50. setting.Repository.Signing.InitialCommit = []string{"never"}
  51. setting.Repository.Signing.CRUDActions = []string{"never"}
  52. baseAPITestContext := NewAPITestContext(t, username, "repo1")
  53. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  54. u.Path = baseAPITestContext.GitPath()
  55. t.Run("Unsigned-Initial", func(t *testing.T) {
  56. defer tests.PrintCurrentTest(t)()
  57. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  58. t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
  59. t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
  60. assert.NotNil(t, branch.Commit)
  61. assert.NotNil(t, branch.Commit.Verification)
  62. assert.False(t, branch.Commit.Verification.Verified)
  63. assert.Empty(t, branch.Commit.Verification.Signature)
  64. }))
  65. t.Run("CreateCRUDFile-Never", crudActionCreateFile(
  66. t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) {
  67. assert.False(t, response.Verification.Verified)
  68. }))
  69. t.Run("CreateCRUDFile-Never", crudActionCreateFile(
  70. t, testCtx, user, "never", "never2", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) {
  71. assert.False(t, response.Verification.Verified)
  72. }))
  73. })
  74. }, false)
  75. setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
  76. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  77. u.Path = baseAPITestContext.GitPath()
  78. t.Run("Unsigned-Initial-CRUD-ParentSigned", func(t *testing.T) {
  79. defer tests.PrintCurrentTest(t)()
  80. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  81. t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
  82. t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) {
  83. assert.False(t, response.Verification.Verified)
  84. }))
  85. t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
  86. t, testCtx, user, "parentsigned", "parentsigned2", "signed-parent2.txt", func(t *testing.T, response api.FileResponse) {
  87. assert.False(t, response.Verification.Verified)
  88. }))
  89. })
  90. }, false)
  91. setting.Repository.Signing.CRUDActions = []string{"never"}
  92. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  93. u.Path = baseAPITestContext.GitPath()
  94. t.Run("Unsigned-Initial-CRUD-Never", func(t *testing.T) {
  95. defer tests.PrintCurrentTest(t)()
  96. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  97. t.Run("CreateCRUDFile-Never", crudActionCreateFile(
  98. t, testCtx, user, "parentsigned", "parentsigned-never", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) {
  99. assert.False(t, response.Verification.Verified)
  100. }))
  101. })
  102. }, false)
  103. setting.Repository.Signing.CRUDActions = []string{"always"}
  104. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  105. u.Path = baseAPITestContext.GitPath()
  106. t.Run("Unsigned-Initial-CRUD-Always", func(t *testing.T) {
  107. defer tests.PrintCurrentTest(t)()
  108. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  109. t.Run("CreateCRUDFile-Always", crudActionCreateFile(
  110. t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) {
  111. assert.NotNil(t, response.Verification)
  112. if response.Verification == nil {
  113. assert.FailNow(t, "no verification provided with response! %v", response)
  114. return
  115. }
  116. assert.True(t, response.Verification.Verified)
  117. if !response.Verification.Verified {
  118. t.FailNow()
  119. return
  120. }
  121. assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
  122. }))
  123. t.Run("CreateCRUDFile-ParentSigned-always", crudActionCreateFile(
  124. t, testCtx, user, "parentsigned", "parentsigned-always", "signed-parent2.txt", func(t *testing.T, response api.FileResponse) {
  125. assert.NotNil(t, response.Verification)
  126. if response.Verification == nil {
  127. assert.FailNow(t, "no verification provided with response! %v", response)
  128. return
  129. }
  130. assert.True(t, response.Verification.Verified)
  131. if !response.Verification.Verified {
  132. t.FailNow()
  133. return
  134. }
  135. assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
  136. }))
  137. })
  138. }, false)
  139. setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
  140. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  141. u.Path = baseAPITestContext.GitPath()
  142. t.Run("Unsigned-Initial-CRUD-ParentSigned", func(t *testing.T) {
  143. defer tests.PrintCurrentTest(t)()
  144. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  145. t.Run("CreateCRUDFile-Always-ParentSigned", crudActionCreateFile(
  146. t, testCtx, user, "always", "always-parentsigned", "signed-always-parentsigned.txt", func(t *testing.T, response api.FileResponse) {
  147. assert.NotNil(t, response.Verification)
  148. if response.Verification == nil {
  149. assert.FailNow(t, "no verification provided with response! %v", response)
  150. return
  151. }
  152. assert.True(t, response.Verification.Verified)
  153. if !response.Verification.Verified {
  154. t.FailNow()
  155. return
  156. }
  157. assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
  158. }))
  159. })
  160. }, false)
  161. setting.Repository.Signing.InitialCommit = []string{"always"}
  162. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  163. u.Path = baseAPITestContext.GitPath()
  164. t.Run("AlwaysSign-Initial", func(t *testing.T) {
  165. defer tests.PrintCurrentTest(t)()
  166. testCtx := NewAPITestContext(t, username, "initial-always")
  167. t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
  168. t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
  169. assert.NotNil(t, branch.Commit)
  170. if branch.Commit == nil {
  171. assert.FailNow(t, "no commit provided with branch! %v", branch)
  172. return
  173. }
  174. assert.NotNil(t, branch.Commit.Verification)
  175. if branch.Commit.Verification == nil {
  176. assert.FailNow(t, "no verification provided with branch commit! %v", branch.Commit)
  177. return
  178. }
  179. assert.True(t, branch.Commit.Verification.Verified)
  180. if !branch.Commit.Verification.Verified {
  181. t.FailNow()
  182. return
  183. }
  184. assert.Equal(t, "gitea@fake.local", branch.Commit.Verification.Signer.Email)
  185. }))
  186. })
  187. }, false)
  188. setting.Repository.Signing.CRUDActions = []string{"never"}
  189. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  190. u.Path = baseAPITestContext.GitPath()
  191. t.Run("AlwaysSign-Initial-CRUD-Never", func(t *testing.T) {
  192. defer tests.PrintCurrentTest(t)()
  193. testCtx := NewAPITestContext(t, username, "initial-always-never")
  194. t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
  195. t.Run("CreateCRUDFile-Never", crudActionCreateFile(
  196. t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) {
  197. assert.False(t, response.Verification.Verified)
  198. }))
  199. })
  200. }, false)
  201. setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
  202. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  203. u.Path = baseAPITestContext.GitPath()
  204. t.Run("AlwaysSign-Initial-CRUD-ParentSigned-On-Always", func(t *testing.T) {
  205. defer tests.PrintCurrentTest(t)()
  206. testCtx := NewAPITestContext(t, username, "initial-always-parent")
  207. t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
  208. t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
  209. t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) {
  210. assert.True(t, response.Verification.Verified)
  211. if !response.Verification.Verified {
  212. t.FailNow()
  213. return
  214. }
  215. assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
  216. }))
  217. })
  218. }, false)
  219. setting.Repository.Signing.CRUDActions = []string{"always"}
  220. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  221. u.Path = baseAPITestContext.GitPath()
  222. t.Run("AlwaysSign-Initial-CRUD-Always", func(t *testing.T) {
  223. defer tests.PrintCurrentTest(t)()
  224. testCtx := NewAPITestContext(t, username, "initial-always-always")
  225. t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
  226. t.Run("CreateCRUDFile-Always", crudActionCreateFile(
  227. t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) {
  228. assert.True(t, response.Verification.Verified)
  229. if !response.Verification.Verified {
  230. t.FailNow()
  231. return
  232. }
  233. assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
  234. }))
  235. })
  236. }, false)
  237. var pr api.PullRequest
  238. setting.Repository.Signing.Merges = []string{"commitssigned"}
  239. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  240. u.Path = baseAPITestContext.GitPath()
  241. t.Run("UnsignedMerging", func(t *testing.T) {
  242. defer tests.PrintCurrentTest(t)()
  243. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  244. var err error
  245. t.Run("CreatePullRequest", func(t *testing.T) {
  246. pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "never2")(t)
  247. assert.NoError(t, err)
  248. })
  249. t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
  250. t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
  251. assert.NotNil(t, branch.Commit)
  252. assert.NotNil(t, branch.Commit.Verification)
  253. assert.False(t, branch.Commit.Verification.Verified)
  254. assert.Empty(t, branch.Commit.Verification.Signature)
  255. }))
  256. })
  257. }, false)
  258. setting.Repository.Signing.Merges = []string{"basesigned"}
  259. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  260. u.Path = baseAPITestContext.GitPath()
  261. t.Run("BaseSignedMerging", func(t *testing.T) {
  262. defer tests.PrintCurrentTest(t)()
  263. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  264. var err error
  265. t.Run("CreatePullRequest", func(t *testing.T) {
  266. pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "parentsigned2")(t)
  267. assert.NoError(t, err)
  268. })
  269. t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
  270. t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
  271. assert.NotNil(t, branch.Commit)
  272. assert.NotNil(t, branch.Commit.Verification)
  273. assert.False(t, branch.Commit.Verification.Verified)
  274. assert.Empty(t, branch.Commit.Verification.Signature)
  275. }))
  276. })
  277. }, false)
  278. setting.Repository.Signing.Merges = []string{"commitssigned"}
  279. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  280. u.Path = baseAPITestContext.GitPath()
  281. t.Run("CommitsSignedMerging", func(t *testing.T) {
  282. defer tests.PrintCurrentTest(t)()
  283. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  284. var err error
  285. t.Run("CreatePullRequest", func(t *testing.T) {
  286. pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "always-parentsigned")(t)
  287. assert.NoError(t, err)
  288. })
  289. t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
  290. t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
  291. assert.NotNil(t, branch.Commit)
  292. assert.NotNil(t, branch.Commit.Verification)
  293. assert.True(t, branch.Commit.Verification.Verified)
  294. }))
  295. })
  296. }, false)
  297. }
  298. func crudActionCreateFile(t *testing.T, ctx APITestContext, user *user_model.User, from, to, path string, callback ...func(*testing.T, api.FileResponse)) func(*testing.T) {
  299. return doAPICreateFile(ctx, path, &api.CreateFileOptions{
  300. FileOptions: api.FileOptions{
  301. BranchName: from,
  302. NewBranchName: to,
  303. Message: fmt.Sprintf("from:%s to:%s path:%s", from, to, path),
  304. Author: api.Identity{
  305. Name: user.FullName,
  306. Email: user.Email,
  307. },
  308. Committer: api.Identity{
  309. Name: user.FullName,
  310. Email: user.Email,
  311. },
  312. },
  313. Content: base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("This is new text for %s", path))),
  314. }, callback...)
  315. }
  316. func importTestingKey(tmpDir, name, email string) (*openpgp.Entity, error) {
  317. if _, _, err := process.GetManager().Exec("gpg --import tests/integration/private-testing.key", "gpg", "--import", "tests/integration/private-testing.key"); err != nil {
  318. return nil, err
  319. }
  320. keyringFile, err := os.Open("tests/integration/private-testing.key")
  321. if err != nil {
  322. return nil, err
  323. }
  324. defer keyringFile.Close()
  325. block, err := armor.Decode(keyringFile)
  326. if err != nil {
  327. return nil, err
  328. }
  329. keyring, err := openpgp.ReadKeyRing(block.Body)
  330. if err != nil {
  331. return nil, fmt.Errorf("Keyring access failed: '%w'", err)
  332. }
  333. // There should only be one entity in this file.
  334. return keyring[0], nil
  335. }