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

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