選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

gpg_git_test.go 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  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. 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 := createGPGKey(tmpDir, "gitea", "gitea@fake.local")
  35. assert.NoError(t, err)
  36. rootKeyID := rootKeyPair.PrimaryKey.KeyIdShortString()
  37. oldKeyID := setting.Repository.Signing.SigningKey
  38. oldName := setting.Repository.Signing.SigningName
  39. oldEmail := setting.Repository.Signing.SigningEmail
  40. defer func() {
  41. setting.Repository.Signing.SigningKey = oldKeyID
  42. setting.Repository.Signing.SigningName = oldName
  43. setting.Repository.Signing.SigningEmail = oldEmail
  44. }()
  45. setting.Repository.Signing.SigningKey = rootKeyID
  46. setting.Repository.Signing.SigningName = "gitea"
  47. setting.Repository.Signing.SigningEmail = "gitea@fake.local"
  48. user := models.AssertExistsAndLoadBean(t, &models.User{Name: username}).(*models.User)
  49. setting.Repository.Signing.InitialCommit = []string{"never"}
  50. setting.Repository.Signing.CRUDActions = []string{"never"}
  51. baseAPITestContext := NewAPITestContext(t, username, "repo1")
  52. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  53. u.Path = baseAPITestContext.GitPath()
  54. t.Run("Unsigned-Initial", func(t *testing.T) {
  55. PrintCurrentTest(t)
  56. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  57. t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
  58. t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
  59. assert.NotNil(t, branch.Commit)
  60. assert.NotNil(t, branch.Commit.Verification)
  61. assert.False(t, branch.Commit.Verification.Verified)
  62. assert.Empty(t, branch.Commit.Verification.Signature)
  63. }))
  64. t.Run("CreateCRUDFile-Never", crudActionCreateFile(
  65. t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) {
  66. assert.False(t, response.Verification.Verified)
  67. }))
  68. t.Run("CreateCRUDFile-Never", crudActionCreateFile(
  69. t, testCtx, user, "never", "never2", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) {
  70. assert.False(t, response.Verification.Verified)
  71. }))
  72. })
  73. }, false)
  74. setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
  75. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  76. u.Path = baseAPITestContext.GitPath()
  77. t.Run("Unsigned-Initial-CRUD-ParentSigned", func(t *testing.T) {
  78. PrintCurrentTest(t)
  79. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  80. t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
  81. t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) {
  82. assert.False(t, response.Verification.Verified)
  83. }))
  84. t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
  85. t, testCtx, user, "parentsigned", "parentsigned2", "signed-parent2.txt", func(t *testing.T, response api.FileResponse) {
  86. assert.False(t, response.Verification.Verified)
  87. }))
  88. })
  89. }, false)
  90. setting.Repository.Signing.CRUDActions = []string{"never"}
  91. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  92. u.Path = baseAPITestContext.GitPath()
  93. t.Run("Unsigned-Initial-CRUD-Never", func(t *testing.T) {
  94. PrintCurrentTest(t)
  95. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  96. t.Run("CreateCRUDFile-Never", crudActionCreateFile(
  97. t, testCtx, user, "parentsigned", "parentsigned-never", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) {
  98. assert.False(t, response.Verification.Verified)
  99. }))
  100. })
  101. }, false)
  102. setting.Repository.Signing.CRUDActions = []string{"always"}
  103. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  104. u.Path = baseAPITestContext.GitPath()
  105. t.Run("Unsigned-Initial-CRUD-Always", func(t *testing.T) {
  106. PrintCurrentTest(t)
  107. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  108. t.Run("CreateCRUDFile-Always", crudActionCreateFile(
  109. t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) {
  110. assert.True(t, response.Verification.Verified)
  111. assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
  112. }))
  113. t.Run("CreateCRUDFile-ParentSigned-always", crudActionCreateFile(
  114. t, testCtx, user, "parentsigned", "parentsigned-always", "signed-parent2.txt", func(t *testing.T, response api.FileResponse) {
  115. assert.True(t, response.Verification.Verified)
  116. assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
  117. }))
  118. })
  119. }, false)
  120. setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
  121. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  122. u.Path = baseAPITestContext.GitPath()
  123. t.Run("Unsigned-Initial-CRUD-ParentSigned", func(t *testing.T) {
  124. PrintCurrentTest(t)
  125. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  126. t.Run("CreateCRUDFile-Always-ParentSigned", crudActionCreateFile(
  127. t, testCtx, user, "always", "always-parentsigned", "signed-always-parentsigned.txt", func(t *testing.T, response api.FileResponse) {
  128. assert.True(t, response.Verification.Verified)
  129. assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
  130. }))
  131. })
  132. }, false)
  133. setting.Repository.Signing.InitialCommit = []string{"always"}
  134. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  135. u.Path = baseAPITestContext.GitPath()
  136. t.Run("AlwaysSign-Initial", func(t *testing.T) {
  137. PrintCurrentTest(t)
  138. testCtx := NewAPITestContext(t, username, "initial-always")
  139. t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
  140. t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
  141. assert.NotNil(t, branch.Commit)
  142. assert.NotNil(t, branch.Commit.Verification)
  143. assert.True(t, branch.Commit.Verification.Verified)
  144. assert.Equal(t, "gitea@fake.local", branch.Commit.Verification.Signer.Email)
  145. }))
  146. })
  147. }, false)
  148. setting.Repository.Signing.CRUDActions = []string{"never"}
  149. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  150. u.Path = baseAPITestContext.GitPath()
  151. t.Run("AlwaysSign-Initial-CRUD-Never", func(t *testing.T) {
  152. PrintCurrentTest(t)
  153. testCtx := NewAPITestContext(t, username, "initial-always")
  154. t.Run("CreateCRUDFile-Never", crudActionCreateFile(
  155. t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) {
  156. assert.False(t, response.Verification.Verified)
  157. }))
  158. })
  159. }, false)
  160. setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
  161. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  162. u.Path = baseAPITestContext.GitPath()
  163. t.Run("AlwaysSign-Initial-CRUD-ParentSigned-On-Always", func(t *testing.T) {
  164. PrintCurrentTest(t)
  165. testCtx := NewAPITestContext(t, username, "initial-always")
  166. t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
  167. t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) {
  168. assert.True(t, response.Verification.Verified)
  169. assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
  170. }))
  171. })
  172. }, false)
  173. setting.Repository.Signing.CRUDActions = []string{"always"}
  174. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  175. u.Path = baseAPITestContext.GitPath()
  176. t.Run("AlwaysSign-Initial-CRUD-Always", func(t *testing.T) {
  177. PrintCurrentTest(t)
  178. testCtx := NewAPITestContext(t, username, "initial-always")
  179. t.Run("CreateCRUDFile-Always", crudActionCreateFile(
  180. t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) {
  181. assert.True(t, response.Verification.Verified)
  182. assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
  183. }))
  184. })
  185. }, false)
  186. var pr api.PullRequest
  187. setting.Repository.Signing.Merges = []string{"commitssigned"}
  188. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  189. u.Path = baseAPITestContext.GitPath()
  190. t.Run("UnsignedMerging", func(t *testing.T) {
  191. PrintCurrentTest(t)
  192. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  193. var err error
  194. t.Run("CreatePullRequest", func(t *testing.T) {
  195. pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "never2")(t)
  196. assert.NoError(t, err)
  197. })
  198. t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
  199. t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
  200. assert.NotNil(t, branch.Commit)
  201. assert.NotNil(t, branch.Commit.Verification)
  202. assert.False(t, branch.Commit.Verification.Verified)
  203. assert.Empty(t, branch.Commit.Verification.Signature)
  204. }))
  205. })
  206. }, false)
  207. setting.Repository.Signing.Merges = []string{"basesigned"}
  208. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  209. u.Path = baseAPITestContext.GitPath()
  210. t.Run("BaseSignedMerging", func(t *testing.T) {
  211. PrintCurrentTest(t)
  212. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  213. var err error
  214. t.Run("CreatePullRequest", func(t *testing.T) {
  215. pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "parentsigned2")(t)
  216. assert.NoError(t, err)
  217. })
  218. t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
  219. t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
  220. assert.NotNil(t, branch.Commit)
  221. assert.NotNil(t, branch.Commit.Verification)
  222. assert.False(t, branch.Commit.Verification.Verified)
  223. assert.Empty(t, branch.Commit.Verification.Signature)
  224. }))
  225. })
  226. }, false)
  227. setting.Repository.Signing.Merges = []string{"commitssigned"}
  228. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  229. u.Path = baseAPITestContext.GitPath()
  230. t.Run("CommitsSignedMerging", func(t *testing.T) {
  231. PrintCurrentTest(t)
  232. testCtx := NewAPITestContext(t, username, "initial-unsigned")
  233. var err error
  234. t.Run("CreatePullRequest", func(t *testing.T) {
  235. pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "always-parentsigned")(t)
  236. assert.NoError(t, err)
  237. })
  238. t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
  239. t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
  240. assert.NotNil(t, branch.Commit)
  241. assert.NotNil(t, branch.Commit.Verification)
  242. assert.True(t, branch.Commit.Verification.Verified)
  243. }))
  244. })
  245. }, false)
  246. }
  247. func crudActionCreateFile(t *testing.T, ctx APITestContext, user *models.User, from, to, path string, callback ...func(*testing.T, api.FileResponse)) func(*testing.T) {
  248. return doAPICreateFile(ctx, path, &api.CreateFileOptions{
  249. FileOptions: api.FileOptions{
  250. BranchName: from,
  251. NewBranchName: to,
  252. Message: fmt.Sprintf("from:%s to:%s path:%s", from, to, path),
  253. Author: api.Identity{
  254. Name: user.FullName,
  255. Email: user.Email,
  256. },
  257. Committer: api.Identity{
  258. Name: user.FullName,
  259. Email: user.Email,
  260. },
  261. },
  262. Content: base64.StdEncoding.EncodeToString([]byte("This is new text")),
  263. }, callback...)
  264. }
  265. func createGPGKey(tmpDir, name, email string) (*openpgp.Entity, error) {
  266. keyPair, err := openpgp.NewEntity(name, "test", email, nil)
  267. if err != nil {
  268. return nil, err
  269. }
  270. for _, id := range keyPair.Identities {
  271. err := id.SelfSignature.SignUserId(id.UserId.Id, keyPair.PrimaryKey, keyPair.PrivateKey, nil)
  272. if err != nil {
  273. return nil, err
  274. }
  275. }
  276. keyFile := filepath.Join(tmpDir, "temporary.key")
  277. keyWriter, err := os.Create(keyFile)
  278. if err != nil {
  279. return nil, err
  280. }
  281. defer keyWriter.Close()
  282. defer os.Remove(keyFile)
  283. w, err := armor.Encode(keyWriter, openpgp.PrivateKeyType, nil)
  284. if err != nil {
  285. return nil, err
  286. }
  287. defer w.Close()
  288. keyPair.SerializePrivate(w, nil)
  289. if err := w.Close(); err != nil {
  290. return nil, err
  291. }
  292. if err := keyWriter.Close(); err != nil {
  293. return nil, err
  294. }
  295. if _, _, err := process.GetManager().Exec("gpg --import temporary.key", "gpg", "--import", keyFile); err != nil {
  296. return nil, err
  297. }
  298. return keyPair, nil
  299. }