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 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  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. "path/filepath"
  12. "testing"
  13. "code.gitea.io/gitea/models"
  14. "code.gitea.io/gitea/modules/process"
  15. "code.gitea.io/gitea/modules/setting"
  16. api "code.gitea.io/gitea/modules/structs"
  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. onGiteaRun(t, testGPGGit)
  23. }
  24. func testGPGGit(t *testing.T, u *url.URL) {
  25. username := "user2"
  26. baseAPITestContext := NewAPITestContext(t, username, "repo1")
  27. u.Path = baseAPITestContext.GitPath()
  28. // OK Set a new GPG home
  29. tmpDir, err := ioutil.TempDir("", "temp-gpg")
  30. assert.NoError(t, err)
  31. defer os.RemoveAll(tmpDir)
  32. err = os.Chmod(tmpDir, 0700)
  33. assert.NoError(t, err)
  34. oldGNUPGHome := os.Getenv("GNUPGHOME")
  35. err = os.Setenv("GNUPGHOME", tmpDir)
  36. assert.NoError(t, err)
  37. defer os.Setenv("GNUPGHOME", oldGNUPGHome)
  38. // Need to create a root key
  39. rootKeyPair, err := createGPGKey(tmpDir, "gitea", "gitea@fake.local")
  40. assert.NoError(t, err)
  41. rootKeyID := rootKeyPair.PrimaryKey.KeyIdShortString()
  42. oldKeyID := setting.Repository.Signing.SigningKey
  43. oldName := setting.Repository.Signing.SigningName
  44. oldEmail := setting.Repository.Signing.SigningEmail
  45. defer func() {
  46. setting.Repository.Signing.SigningKey = oldKeyID
  47. setting.Repository.Signing.SigningName = oldName
  48. setting.Repository.Signing.SigningEmail = oldEmail
  49. }()
  50. setting.Repository.Signing.SigningKey = rootKeyID
  51. setting.Repository.Signing.SigningName = "gitea"
  52. setting.Repository.Signing.SigningEmail = "gitea@fake.local"
  53. user := models.AssertExistsAndLoadBean(t, &models.User{Name: username}).(*models.User)
  54. t.Run("Unsigned-Initial", func(t *testing.T) {
  55. PrintCurrentTest(t)
  56. setting.Repository.Signing.InitialCommit = []string{"never"}
  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. setting.Repository.Signing.CRUDActions = []string{"never"}
  66. t.Run("CreateCRUDFile-Never", crudActionCreateFile(
  67. t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) {
  68. assert.False(t, response.Verification.Verified)
  69. }))
  70. t.Run("CreateCRUDFile-Never", crudActionCreateFile(
  71. t, testCtx, user, "never", "never2", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) {
  72. assert.False(t, response.Verification.Verified)
  73. }))
  74. setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
  75. t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
  76. t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) {
  77. assert.False(t, response.Verification.Verified)
  78. }))
  79. t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
  80. t, testCtx, user, "parentsigned", "parentsigned2", "signed-parent2.txt", func(t *testing.T, response api.FileResponse) {
  81. assert.False(t, response.Verification.Verified)
  82. }))
  83. setting.Repository.Signing.CRUDActions = []string{"never"}
  84. t.Run("CreateCRUDFile-Never", crudActionCreateFile(
  85. t, testCtx, user, "parentsigned", "parentsigned-never", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) {
  86. assert.False(t, response.Verification.Verified)
  87. }))
  88. setting.Repository.Signing.CRUDActions = []string{"always"}
  89. t.Run("CreateCRUDFile-Always", crudActionCreateFile(
  90. t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) {
  91. assert.True(t, response.Verification.Verified)
  92. assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
  93. }))
  94. t.Run("CreateCRUDFile-ParentSigned-always", crudActionCreateFile(
  95. t, testCtx, user, "parentsigned", "parentsigned-always", "signed-parent2.txt", func(t *testing.T, response api.FileResponse) {
  96. assert.True(t, response.Verification.Verified)
  97. assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
  98. }))
  99. setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
  100. t.Run("CreateCRUDFile-Always-ParentSigned", crudActionCreateFile(
  101. t, testCtx, user, "always", "always-parentsigned", "signed-always-parentsigned.txt", func(t *testing.T, response api.FileResponse) {
  102. assert.True(t, response.Verification.Verified)
  103. assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
  104. }))
  105. })
  106. t.Run("AlwaysSign-Initial", func(t *testing.T) {
  107. PrintCurrentTest(t)
  108. setting.Repository.Signing.InitialCommit = []string{"always"}
  109. testCtx := NewAPITestContext(t, username, "initial-always")
  110. t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
  111. t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
  112. assert.NotNil(t, branch.Commit)
  113. assert.NotNil(t, branch.Commit.Verification)
  114. assert.True(t, branch.Commit.Verification.Verified)
  115. assert.Equal(t, "gitea@fake.local", branch.Commit.Verification.Signer.Email)
  116. }))
  117. setting.Repository.Signing.CRUDActions = []string{"never"}
  118. t.Run("CreateCRUDFile-Never", crudActionCreateFile(
  119. t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) {
  120. assert.False(t, response.Verification.Verified)
  121. }))
  122. setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
  123. t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
  124. t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) {
  125. assert.True(t, response.Verification.Verified)
  126. assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
  127. }))
  128. setting.Repository.Signing.CRUDActions = []string{"always"}
  129. t.Run("CreateCRUDFile-Always", crudActionCreateFile(
  130. t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) {
  131. assert.True(t, response.Verification.Verified)
  132. assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
  133. }))
  134. })
  135. t.Run("UnsignedMerging", func(t *testing.T) {
  136. PrintCurrentTest(t)
  137. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  138. var pr api.PullRequest
  139. var err error
  140. t.Run("CreatePullRequest", func(t *testing.T) {
  141. pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "never2")(t)
  142. assert.NoError(t, err)
  143. })
  144. setting.Repository.Signing.Merges = []string{"commitssigned"}
  145. t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
  146. t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
  147. assert.NotNil(t, branch.Commit)
  148. assert.NotNil(t, branch.Commit.Verification)
  149. assert.False(t, branch.Commit.Verification.Verified)
  150. assert.Empty(t, branch.Commit.Verification.Signature)
  151. }))
  152. setting.Repository.Signing.Merges = []string{"basesigned"}
  153. t.Run("CreatePullRequest", func(t *testing.T) {
  154. pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "parentsigned2")(t)
  155. assert.NoError(t, err)
  156. })
  157. t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
  158. t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
  159. assert.NotNil(t, branch.Commit)
  160. assert.NotNil(t, branch.Commit.Verification)
  161. assert.False(t, branch.Commit.Verification.Verified)
  162. assert.Empty(t, branch.Commit.Verification.Signature)
  163. }))
  164. setting.Repository.Signing.Merges = []string{"commitssigned"}
  165. t.Run("CreatePullRequest", func(t *testing.T) {
  166. pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "always-parentsigned")(t)
  167. assert.NoError(t, err)
  168. })
  169. t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
  170. t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
  171. assert.NotNil(t, branch.Commit)
  172. assert.NotNil(t, branch.Commit.Verification)
  173. assert.True(t, branch.Commit.Verification.Verified)
  174. }))
  175. })
  176. }
  177. func crudActionCreateFile(t *testing.T, ctx APITestContext, user *models.User, from, to, path string, callback ...func(*testing.T, api.FileResponse)) func(*testing.T) {
  178. return doAPICreateFile(ctx, path, &api.CreateFileOptions{
  179. FileOptions: api.FileOptions{
  180. BranchName: from,
  181. NewBranchName: to,
  182. Message: fmt.Sprintf("from:%s to:%s path:%s", from, to, path),
  183. Author: api.Identity{
  184. Name: user.FullName,
  185. Email: user.Email,
  186. },
  187. Committer: api.Identity{
  188. Name: user.FullName,
  189. Email: user.Email,
  190. },
  191. },
  192. Content: base64.StdEncoding.EncodeToString([]byte("This is new text")),
  193. }, callback...)
  194. }
  195. func createGPGKey(tmpDir, name, email string) (*openpgp.Entity, error) {
  196. keyPair, err := openpgp.NewEntity(name, "test", email, nil)
  197. if err != nil {
  198. return nil, err
  199. }
  200. for _, id := range keyPair.Identities {
  201. err := id.SelfSignature.SignUserId(id.UserId.Id, keyPair.PrimaryKey, keyPair.PrivateKey, nil)
  202. if err != nil {
  203. return nil, err
  204. }
  205. }
  206. keyFile := filepath.Join(tmpDir, "temporary.key")
  207. keyWriter, err := os.Create(keyFile)
  208. if err != nil {
  209. return nil, err
  210. }
  211. defer keyWriter.Close()
  212. defer os.Remove(keyFile)
  213. w, err := armor.Encode(keyWriter, openpgp.PrivateKeyType, nil)
  214. if err != nil {
  215. return nil, err
  216. }
  217. defer w.Close()
  218. keyPair.SerializePrivate(w, nil)
  219. if err := w.Close(); err != nil {
  220. return nil, err
  221. }
  222. if err := keyWriter.Close(); err != nil {
  223. return nil, err
  224. }
  225. if _, _, err := process.GetManager().Exec("gpg --import temporary.key", "gpg", "--import", keyFile); err != nil {
  226. return nil, err
  227. }
  228. return keyPair, nil
  229. }