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

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