diff --git a/cmd/serv.go b/cmd/serv.go
index 21a69b24d6..ca0354d06c 100644
--- a/cmd/serv.go
+++ b/cmd/serv.go
@@ -234,19 +234,20 @@ func runServ(c *cli.Context) error {
// Check deploy key or user key.
if key.Type == models.KeyTypeDeploy {
- if key.Mode < requestedMode {
- fail("Key permission denied", "Cannot push with deployment key: %d", key.ID)
- }
-
- // Check if this deploy key belongs to current repository.
- has, err := private.HasDeployKey(key.ID, repo.ID)
+ // Now we have to get the deploy key for this repo
+ deployKey, err := private.GetDeployKey(key.ID, repo.ID)
if err != nil {
fail("Key access denied", "Failed to access internal api: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
}
- if !has {
+
+ if deployKey == nil {
fail("Key access denied", "Deploy key access denied: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
}
+ if deployKey.Mode < requestedMode {
+ fail("Key permission denied", "Cannot push with read-only deployment key: %d to repo_id: %d", key.ID, repo.ID)
+ }
+
// Update deploy key activity.
if err = private.UpdateDeployKeyUpdated(key.ID, repo.ID); err != nil {
fail("Internal error", "UpdateDeployKey: %v", err)
diff --git a/integrations/api_helper_for_declarative_test.go b/integrations/api_helper_for_declarative_test.go
new file mode 100644
index 0000000000..32a4ce8047
--- /dev/null
+++ b/integrations/api_helper_for_declarative_test.go
@@ -0,0 +1,152 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package integrations
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "testing"
+
+ api "code.gitea.io/sdk/gitea"
+ "github.com/stretchr/testify/assert"
+)
+
+type APITestContext struct {
+ Reponame string
+ Session *TestSession
+ Token string
+ Username string
+ ExpectedCode int
+}
+
+func NewAPITestContext(t *testing.T, username, reponame string) APITestContext {
+ session := loginUser(t, username)
+ token := getTokenForLoggedInUser(t, session)
+ return APITestContext{
+ Session: session,
+ Token: token,
+ Username: username,
+ Reponame: reponame,
+ }
+}
+
+func (ctx APITestContext) GitPath() string {
+ return fmt.Sprintf("%s/%s.git", ctx.Username, ctx.Reponame)
+}
+
+func doAPICreateRepository(ctx APITestContext, empty bool, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
+ return func(t *testing.T) {
+ createRepoOption := &api.CreateRepoOption{
+ AutoInit: !empty,
+ Description: "Temporary repo",
+ Name: ctx.Reponame,
+ Private: true,
+ Gitignores: "",
+ License: "WTFPL",
+ Readme: "Default",
+ }
+ req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos?token="+ctx.Token, createRepoOption)
+ if ctx.ExpectedCode != 0 {
+ ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
+ return
+ }
+ resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
+
+ var repository api.Repository
+ DecodeJSON(t, resp, &repository)
+ if len(callback) > 0 {
+ callback[0](t, repository)
+ }
+ }
+}
+
+func doAPIGetRepository(ctx APITestContext, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
+ return func(t *testing.T) {
+ urlStr := fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", ctx.Username, ctx.Reponame, ctx.Token)
+
+ req := NewRequest(t, "GET", urlStr)
+ if ctx.ExpectedCode != 0 {
+ ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
+ return
+ }
+ resp := ctx.Session.MakeRequest(t, req, http.StatusOK)
+
+ var repository api.Repository
+ DecodeJSON(t, resp, &repository)
+ if len(callback) > 0 {
+ callback[0](t, repository)
+ }
+ }
+}
+
+func doAPIDeleteRepository(ctx APITestContext) func(*testing.T) {
+ return func(t *testing.T) {
+ urlStr := fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", ctx.Username, ctx.Reponame, ctx.Token)
+
+ req := NewRequest(t, "DELETE", urlStr)
+ if ctx.ExpectedCode != 0 {
+ ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
+ return
+ }
+ ctx.Session.MakeRequest(t, req, http.StatusNoContent)
+ }
+}
+
+func doAPICreateUserKey(ctx APITestContext, keyname, keyFile string, callback ...func(*testing.T, api.PublicKey)) func(*testing.T) {
+ return func(t *testing.T) {
+ urlStr := fmt.Sprintf("/api/v1/user/keys?token=%s", ctx.Token)
+
+ dataPubKey, err := ioutil.ReadFile(keyFile + ".pub")
+ assert.NoError(t, err)
+ req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateKeyOption{
+ Title: keyname,
+ Key: string(dataPubKey),
+ })
+ if ctx.ExpectedCode != 0 {
+ ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
+ return
+ }
+ resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
+ var publicKey api.PublicKey
+ DecodeJSON(t, resp, &publicKey)
+ if len(callback) > 0 {
+ callback[0](t, publicKey)
+ }
+ }
+}
+
+func doAPIDeleteUserKey(ctx APITestContext, keyID int64) func(*testing.T) {
+ return func(t *testing.T) {
+ urlStr := fmt.Sprintf("/api/v1/user/keys/%d?token=%s", keyID, ctx.Token)
+
+ req := NewRequest(t, "DELETE", urlStr)
+ if ctx.ExpectedCode != 0 {
+ ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
+ return
+ }
+ ctx.Session.MakeRequest(t, req, http.StatusNoContent)
+ }
+}
+
+func doAPICreateDeployKey(ctx APITestContext, keyname, keyFile string, readOnly bool) func(*testing.T) {
+ return func(t *testing.T) {
+ urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/keys?token=%s", ctx.Username, ctx.Reponame, ctx.Token)
+
+ dataPubKey, err := ioutil.ReadFile(keyFile + ".pub")
+ assert.NoError(t, err)
+ req := NewRequestWithJSON(t, "POST", urlStr, api.CreateKeyOption{
+ Title: keyname,
+ Key: string(dataPubKey),
+ ReadOnly: readOnly,
+ })
+
+ if ctx.ExpectedCode != 0 {
+ ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
+ return
+ }
+ ctx.Session.MakeRequest(t, req, http.StatusCreated)
+ }
+}
diff --git a/integrations/deploy_key_push_test.go b/integrations/deploy_key_push_test.go
deleted file mode 100644
index 8b3d665629..0000000000
--- a/integrations/deploy_key_push_test.go
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package integrations
-
-import (
- "fmt"
- "io/ioutil"
- "log"
- "net/http"
- "net/url"
- "os"
- "os/exec"
- "path/filepath"
- "testing"
- "time"
-
- "code.gitea.io/git"
-
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/sdk/gitea"
- "github.com/stretchr/testify/assert"
-)
-
-func createEmptyRepository(username, reponame string) func(*testing.T) {
- return func(t *testing.T) {
- session := loginUser(t, username)
- token := getTokenForLoggedInUser(t, session)
- req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos?token="+token, &api.CreateRepoOption{
- AutoInit: false,
- Description: "Temporary empty repo",
- Name: reponame,
- Private: false,
- })
- session.MakeRequest(t, req, http.StatusCreated)
- }
-}
-
-func createDeployKey(username, reponame, keyname, keyFile string, readOnly bool) func(*testing.T) {
- return func(t *testing.T) {
- session := loginUser(t, username)
- token := getTokenForLoggedInUser(t, session)
- urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/keys?token=%s", username, reponame, token)
-
- dataPubKey, err := ioutil.ReadFile(keyFile + ".pub")
- assert.NoError(t, err)
- req := NewRequestWithJSON(t, "POST", urlStr, api.CreateKeyOption{
- Title: keyname,
- Key: string(dataPubKey),
- ReadOnly: readOnly,
- })
- session.MakeRequest(t, req, http.StatusCreated)
- }
-}
-
-func initTestRepository(dstPath string) func(*testing.T) {
- return func(t *testing.T) {
- // Init repository in dstPath
- assert.NoError(t, git.InitRepository(dstPath, false))
- assert.NoError(t, ioutil.WriteFile(filepath.Join(dstPath, "README.md"), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s", dstPath)), 0644))
- assert.NoError(t, git.AddChanges(dstPath, true))
- signature := git.Signature{
- Email: "test@example.com",
- Name: "test",
- When: time.Now(),
- }
- assert.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{
- Committer: &signature,
- Author: &signature,
- Message: "Initial Commit",
- }))
- }
-}
-
-func pushTestRepository(dstPath, username, reponame string, u url.URL, keyFile string) func(*testing.T) {
- return func(t *testing.T) {
- //Setup remote link
- u.Scheme = "ssh"
- u.User = url.User("git")
- u.Host = fmt.Sprintf("%s:%d", setting.SSH.ListenHost, setting.SSH.ListenPort)
-
- //Setup ssh wrapper
- os.Setenv("GIT_SSH_COMMAND",
- "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i "+
- filepath.Join(setting.AppWorkPath, keyFile))
- os.Setenv("GIT_SSH_VARIANT", "ssh")
-
- log.Printf("Adding remote: %s\n", u.String())
- _, err := git.NewCommand("remote", "add", "origin", u.String()).RunInDir(dstPath)
- assert.NoError(t, err)
-
- log.Printf("Pushing to: %s\n", u.String())
- _, err = git.NewCommand("push", "-u", "origin", "master").RunInDir(dstPath)
- assert.NoError(t, err)
- }
-}
-
-func checkRepositoryEmptyStatus(username, reponame string, isEmpty bool) func(*testing.T) {
- return func(t *testing.T) {
- session := loginUser(t, username)
- token := getTokenForLoggedInUser(t, session)
- urlStr := fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", username, reponame, token)
-
- req := NewRequest(t, "GET", urlStr)
- resp := session.MakeRequest(t, req, http.StatusOK)
-
- var repository api.Repository
- DecodeJSON(t, resp, &repository)
-
- assert.Equal(t, isEmpty, repository.Empty)
- }
-}
-
-func deleteRepository(username, reponame string) func(*testing.T) {
- return func(t *testing.T) {
- session := loginUser(t, username)
- token := getTokenForLoggedInUser(t, session)
- urlStr := fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", username, reponame, token)
-
- req := NewRequest(t, "DELETE", urlStr)
- session.MakeRequest(t, req, http.StatusNoContent)
- }
-}
-
-func TestPushDeployKeyOnEmptyRepo(t *testing.T) {
- onGiteaRun(t, testPushDeployKeyOnEmptyRepo)
-}
-
-func testPushDeployKeyOnEmptyRepo(t *testing.T, u *url.URL) {
- reponame := "deploy-key-empty-repo-1"
- username := "user2"
- u.Path = fmt.Sprintf("%s/%s.git", username, reponame)
- keyname := fmt.Sprintf("%s-push", reponame)
-
- t.Run("CreateEmptyRepository", createEmptyRepository(username, reponame))
- t.Run("CheckIsEmpty", checkRepositoryEmptyStatus(username, reponame, true))
-
- //Setup the push deploy key file
- keyFile := filepath.Join(setting.AppDataPath, keyname)
- err := exec.Command("ssh-keygen", "-f", keyFile, "-t", "rsa", "-N", "").Run()
- assert.NoError(t, err)
- defer os.RemoveAll(keyFile)
- defer os.RemoveAll(keyFile + ".pub")
-
- t.Run("CreatePushDeployKey", createDeployKey(username, reponame, keyname, keyFile, false))
-
- // Setup the testing repository
- dstPath, err := ioutil.TempDir("", "repo-tmp-deploy-key-empty-repo-1")
- assert.NoError(t, err)
- defer os.RemoveAll(dstPath)
-
- t.Run("InitTestRepository", initTestRepository(dstPath))
- t.Run("SSHPushTestRepository", pushTestRepository(dstPath, username, reponame, *u, keyFile))
-
- log.Println("Done Push")
- t.Run("CheckIsNotEmpty", checkRepositoryEmptyStatus(username, reponame, false))
-
- t.Run("DeleteRepository", deleteRepository(username, reponame))
-}
diff --git a/integrations/git_helper_for_declarative_test.go b/integrations/git_helper_for_declarative_test.go
new file mode 100644
index 0000000000..572abe95a2
--- /dev/null
+++ b/integrations/git_helper_for_declarative_test.go
@@ -0,0 +1,127 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package integrations
+
+import (
+ "context"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "net/url"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "testing"
+ "time"
+
+ "code.gitea.io/git"
+ "code.gitea.io/gitea/modules/setting"
+ "github.com/Unknwon/com"
+ "github.com/stretchr/testify/assert"
+)
+
+func withKeyFile(t *testing.T, keyname string, callback func(string)) {
+ keyFile := filepath.Join(setting.AppDataPath, keyname)
+ err := exec.Command("ssh-keygen", "-f", keyFile, "-t", "rsa", "-N", "").Run()
+ assert.NoError(t, err)
+
+ //Setup ssh wrapper
+ os.Setenv("GIT_SSH_COMMAND",
+ "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i "+
+ filepath.Join(setting.AppWorkPath, keyFile))
+ os.Setenv("GIT_SSH_VARIANT", "ssh")
+
+ callback(keyFile)
+
+ defer os.RemoveAll(keyFile)
+ defer os.RemoveAll(keyFile + ".pub")
+}
+
+func createSSHUrl(gitPath string, u *url.URL) *url.URL {
+ u2 := *u
+ u2.Scheme = "ssh"
+ u2.User = url.User("git")
+ u2.Host = fmt.Sprintf("%s:%d", setting.SSH.ListenHost, setting.SSH.ListenPort)
+ u2.Path = gitPath
+ return &u2
+}
+
+func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) {
+ prepareTestEnv(t)
+ s := http.Server{
+ Handler: mac,
+ }
+
+ u, err := url.Parse(setting.AppURL)
+ assert.NoError(t, err)
+ listener, err := net.Listen("tcp", u.Host)
+ assert.NoError(t, err)
+
+ defer func() {
+ ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
+ s.Shutdown(ctx)
+ cancel()
+ }()
+
+ go s.Serve(listener)
+ //Started by config go ssh.Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
+
+ callback(t, u)
+}
+
+func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) {
+ return func(t *testing.T) {
+ assert.NoError(t, git.Clone(u.String(), dstLocalPath, git.CloneRepoOptions{}))
+ assert.True(t, com.IsExist(filepath.Join(dstLocalPath, "README.md")))
+ }
+}
+
+func doGitCloneFail(dstLocalPath string, u *url.URL) func(*testing.T) {
+ return func(t *testing.T) {
+ assert.Error(t, git.Clone(u.String(), dstLocalPath, git.CloneRepoOptions{}))
+ assert.False(t, com.IsExist(filepath.Join(dstLocalPath, "README.md")))
+ }
+}
+
+func doGitInitTestRepository(dstPath string) func(*testing.T) {
+ return func(t *testing.T) {
+ // Init repository in dstPath
+ assert.NoError(t, git.InitRepository(dstPath, false))
+ assert.NoError(t, ioutil.WriteFile(filepath.Join(dstPath, "README.md"), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s", dstPath)), 0644))
+ assert.NoError(t, git.AddChanges(dstPath, true))
+ signature := git.Signature{
+ Email: "test@example.com",
+ Name: "test",
+ When: time.Now(),
+ }
+ assert.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{
+ Committer: &signature,
+ Author: &signature,
+ Message: "Initial Commit",
+ }))
+ }
+}
+
+func doGitAddRemote(dstPath, remoteName string, u *url.URL) func(*testing.T) {
+ return func(t *testing.T) {
+ _, err := git.NewCommand("remote", "add", remoteName, u.String()).RunInDir(dstPath)
+ assert.NoError(t, err)
+ }
+}
+
+func doGitPushTestRepository(dstPath, remoteName, branch string) func(*testing.T) {
+ return func(t *testing.T) {
+ _, err := git.NewCommand("push", "-u", remoteName, branch).RunInDir(dstPath)
+ assert.NoError(t, err)
+ }
+}
+
+func doGitPushTestRepositoryFail(dstPath, remoteName, branch string) func(*testing.T) {
+ return func(t *testing.T) {
+ _, err := git.NewCommand("push", "-u", remoteName, branch).RunInDir(dstPath)
+ assert.Error(t, err)
+ }
+}
diff --git a/integrations/git_test.go b/integrations/git_test.go
index 96d39e0519..a372525864 100644
--- a/integrations/git_test.go
+++ b/integrations/git_test.go
@@ -5,25 +5,17 @@
package integrations
import (
- "context"
"crypto/rand"
"fmt"
"io/ioutil"
- "net"
- "net/http"
"net/url"
"os"
- "os/exec"
"path/filepath"
"testing"
"time"
"code.gitea.io/git"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/sdk/gitea"
- "github.com/Unknwon/com"
"github.com/stretchr/testify/assert"
)
@@ -32,160 +24,86 @@ const (
bigSize = 128 * 1024 * 1024 //128Mo
)
-func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) {
- prepareTestEnv(t)
- s := http.Server{
- Handler: mac,
- }
-
- u, err := url.Parse(setting.AppURL)
- assert.NoError(t, err)
- listener, err := net.Listen("tcp", u.Host)
- assert.NoError(t, err)
-
- defer func() {
- ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
- s.Shutdown(ctx)
- cancel()
- }()
-
- go s.Serve(listener)
- //Started by config go ssh.Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
-
- callback(t, u)
+func TestGit(t *testing.T) {
+ onGiteaRun(t, testGit)
}
-func TestGit(t *testing.T) {
- onGiteaRun(t, func(t *testing.T, u *url.URL) {
- u.Path = "user2/repo1.git"
+func testGit(t *testing.T, u *url.URL) {
+ username := "user2"
+ baseAPITestContext := NewAPITestContext(t, username, "repo1")
- t.Run("HTTP", func(t *testing.T) {
- dstPath, err := ioutil.TempDir("", "repo-tmp-17")
- assert.NoError(t, err)
- defer os.RemoveAll(dstPath)
- t.Run("Standard", func(t *testing.T) {
- t.Run("CloneNoLogin", func(t *testing.T) {
- dstLocalPath, err := ioutil.TempDir("", "repo1")
- assert.NoError(t, err)
- defer os.RemoveAll(dstLocalPath)
- err = git.Clone(u.String(), dstLocalPath, git.CloneRepoOptions{})
- assert.NoError(t, err)
- assert.True(t, com.IsExist(filepath.Join(dstLocalPath, "README.md")))
- })
+ u.Path = baseAPITestContext.GitPath()
- t.Run("CreateRepo", func(t *testing.T) {
- session := loginUser(t, "user2")
- token := getTokenForLoggedInUser(t, session)
- req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos?token="+token, &api.CreateRepoOption{
- AutoInit: true,
- Description: "Temporary repo",
- Name: "repo-tmp-17",
- Private: false,
- Gitignores: "",
- License: "WTFPL",
- Readme: "Default",
- })
- session.MakeRequest(t, req, http.StatusCreated)
- })
+ t.Run("HTTP", func(t *testing.T) {
+ httpContext := baseAPITestContext
+ httpContext.Reponame = "repo-tmp-17"
- u.Path = "user2/repo-tmp-17.git"
- u.User = url.UserPassword("user2", userPassword)
- t.Run("Clone", func(t *testing.T) {
- err = git.Clone(u.String(), dstPath, git.CloneRepoOptions{})
- assert.NoError(t, err)
- assert.True(t, com.IsExist(filepath.Join(dstPath, "README.md")))
- })
+ dstPath, err := ioutil.TempDir("", httpContext.Reponame)
+ assert.NoError(t, err)
+ defer os.RemoveAll(dstPath)
+ t.Run("Standard", func(t *testing.T) {
+ ensureAnonymousClone(t, u)
- t.Run("PushCommit", func(t *testing.T) {
- t.Run("Little", func(t *testing.T) {
- commitAndPush(t, littleSize, dstPath)
- })
- t.Run("Big", func(t *testing.T) {
- commitAndPush(t, bigSize, dstPath)
- })
- })
- })
- t.Run("LFS", func(t *testing.T) {
- t.Run("PushCommit", func(t *testing.T) {
- //Setup git LFS
- _, err = git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath)
- assert.NoError(t, err)
- _, err = git.NewCommand("lfs").AddArguments("track", "data-file-*").RunInDir(dstPath)
- assert.NoError(t, err)
- err = git.AddChanges(dstPath, false, ".gitattributes")
- assert.NoError(t, err)
+ t.Run("CreateRepo", doAPICreateRepository(httpContext, false))
- t.Run("Little", func(t *testing.T) {
- commitAndPush(t, littleSize, dstPath)
- })
- t.Run("Big", func(t *testing.T) {
- commitAndPush(t, bigSize, dstPath)
- })
+ u.Path = httpContext.GitPath()
+ u.User = url.UserPassword(username, userPassword)
+
+ t.Run("Clone", doGitClone(dstPath, u))
+
+ t.Run("PushCommit", func(t *testing.T) {
+ t.Run("Little", func(t *testing.T) {
+ commitAndPush(t, littleSize, dstPath)
})
- t.Run("Locks", func(t *testing.T) {
- lockTest(t, u.String(), dstPath)
+ t.Run("Big", func(t *testing.T) {
+ commitAndPush(t, bigSize, dstPath)
})
})
})
- t.Run("SSH", func(t *testing.T) {
- //Setup remote link
- u.Scheme = "ssh"
- u.User = url.User("git")
- u.Host = fmt.Sprintf("%s:%d", setting.SSH.ListenHost, setting.SSH.ListenPort)
- u.Path = "user2/repo-tmp-18.git"
+ t.Run("LFS", func(t *testing.T) {
+ t.Run("PushCommit", func(t *testing.T) {
+ //Setup git LFS
+ _, err = git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath)
+ assert.NoError(t, err)
+ _, err = git.NewCommand("lfs").AddArguments("track", "data-file-*").RunInDir(dstPath)
+ assert.NoError(t, err)
+ err = git.AddChanges(dstPath, false, ".gitattributes")
+ assert.NoError(t, err)
- //Setup key
- keyFile := filepath.Join(setting.AppDataPath, "my-testing-key")
- err := exec.Command("ssh-keygen", "-f", keyFile, "-t", "rsa", "-N", "").Run()
- assert.NoError(t, err)
- defer os.RemoveAll(keyFile)
- defer os.RemoveAll(keyFile + ".pub")
-
- session := loginUser(t, "user1")
- keyOwner := models.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User)
- token := getTokenForLoggedInUser(t, session)
- urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", keyOwner.Name, token)
-
- dataPubKey, err := ioutil.ReadFile(keyFile + ".pub")
- assert.NoError(t, err)
- req := NewRequestWithValues(t, "POST", urlStr, map[string]string{
- "key": string(dataPubKey),
- "title": "test-key",
+ t.Run("Little", func(t *testing.T) {
+ commitAndPush(t, littleSize, dstPath)
+ })
+ t.Run("Big", func(t *testing.T) {
+ commitAndPush(t, bigSize, dstPath)
+ })
})
- session.MakeRequest(t, req, http.StatusCreated)
+ t.Run("Locks", func(t *testing.T) {
+ lockTest(t, u.String(), dstPath)
+ })
+ })
+ })
+ t.Run("SSH", func(t *testing.T) {
+ sshContext := baseAPITestContext
+ sshContext.Reponame = "repo-tmp-18"
+ keyname := "my-testing-key"
+ //Setup key the user ssh key
+ withKeyFile(t, keyname, func(keyFile string) {
+ t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key", keyFile))
- //Setup ssh wrapper
- os.Setenv("GIT_SSH_COMMAND",
- "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i "+
- filepath.Join(setting.AppWorkPath, keyFile))
- os.Setenv("GIT_SSH_VARIANT", "ssh")
+ //Setup remote link
+ sshURL := createSSHUrl(sshContext.GitPath(), u)
//Setup clone folder
- dstPath, err := ioutil.TempDir("", "repo-tmp-18")
+ dstPath, err := ioutil.TempDir("", sshContext.Reponame)
assert.NoError(t, err)
defer os.RemoveAll(dstPath)
t.Run("Standard", func(t *testing.T) {
- t.Run("CreateRepo", func(t *testing.T) {
- session := loginUser(t, "user2")
- token := getTokenForLoggedInUser(t, session)
- req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos?token="+token, &api.CreateRepoOption{
- AutoInit: true,
- Description: "Temporary repo",
- Name: "repo-tmp-18",
- Private: false,
- Gitignores: "",
- License: "WTFPL",
- Readme: "Default",
- })
- session.MakeRequest(t, req, http.StatusCreated)
- })
+ t.Run("CreateRepo", doAPICreateRepository(sshContext, false))
+
//TODO get url from api
- t.Run("Clone", func(t *testing.T) {
- _, err = git.NewCommand("clone").AddArguments(u.String(), dstPath).Run()
- assert.NoError(t, err)
- assert.True(t, com.IsExist(filepath.Join(dstPath, "README.md")))
- })
+ t.Run("Clone", doGitClone(dstPath, sshURL))
+
//time.Sleep(5 * time.Minute)
t.Run("PushCommit", func(t *testing.T) {
t.Run("Little", func(t *testing.T) {
@@ -217,10 +135,20 @@ func TestGit(t *testing.T) {
lockTest(t, u.String(), dstPath)
})
})
+
})
+
})
}
+func ensureAnonymousClone(t *testing.T, u *url.URL) {
+ dstLocalPath, err := ioutil.TempDir("", "repo1")
+ assert.NoError(t, err)
+ defer os.RemoveAll(dstLocalPath)
+ t.Run("CloneAnonymous", doGitClone(dstLocalPath, u))
+
+}
+
func lockTest(t *testing.T, remote, repoPath string) {
_, err := git.NewCommand("remote").AddArguments("set-url", "origin", remote).RunInDir(repoPath) //TODO add test ssh git-lfs-creds
assert.NoError(t, err)
diff --git a/integrations/ssh_key_test.go b/integrations/ssh_key_test.go
new file mode 100644
index 0000000000..9ad43ae226
--- /dev/null
+++ b/integrations/ssh_key_test.go
@@ -0,0 +1,217 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package integrations
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "os"
+ "path/filepath"
+ "testing"
+ "time"
+
+ "code.gitea.io/git"
+ api "code.gitea.io/sdk/gitea"
+ "github.com/stretchr/testify/assert"
+)
+
+func doCheckRepositoryEmptyStatus(ctx APITestContext, isEmpty bool) func(*testing.T) {
+ return doAPIGetRepository(ctx, func(t *testing.T, repository api.Repository) {
+ assert.Equal(t, isEmpty, repository.Empty)
+ })
+}
+
+func doAddChangesToCheckout(dstPath, filename string) func(*testing.T) {
+ return func(t *testing.T) {
+ assert.NoError(t, ioutil.WriteFile(filepath.Join(dstPath, filename), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s at time: %v", dstPath, time.Now())), 0644))
+ assert.NoError(t, git.AddChanges(dstPath, true))
+ signature := git.Signature{
+ Email: "test@example.com",
+ Name: "test",
+ When: time.Now(),
+ }
+ assert.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{
+ Committer: &signature,
+ Author: &signature,
+ Message: "Initial Commit",
+ }))
+ }
+}
+
+func TestPushDeployKeyOnEmptyRepo(t *testing.T) {
+ onGiteaRun(t, testPushDeployKeyOnEmptyRepo)
+}
+
+func testPushDeployKeyOnEmptyRepo(t *testing.T, u *url.URL) {
+ // OK login
+ ctx := NewAPITestContext(t, "user2", "deploy-key-empty-repo-1")
+ keyname := fmt.Sprintf("%s-push", ctx.Reponame)
+ u.Path = ctx.GitPath()
+
+ t.Run("CreateEmptyRepository", doAPICreateRepository(ctx, true))
+
+ t.Run("CheckIsEmpty", doCheckRepositoryEmptyStatus(ctx, true))
+
+ withKeyFile(t, keyname, func(keyFile string) {
+ t.Run("CreatePushDeployKey", doAPICreateDeployKey(ctx, keyname, keyFile, false))
+
+ // Setup the testing repository
+ dstPath, err := ioutil.TempDir("", "repo-tmp-deploy-key-empty-repo-1")
+ assert.NoError(t, err)
+ defer os.RemoveAll(dstPath)
+
+ t.Run("InitTestRepository", doGitInitTestRepository(dstPath))
+
+ //Setup remote link
+ sshURL := createSSHUrl(ctx.GitPath(), u)
+
+ t.Run("AddRemote", doGitAddRemote(dstPath, "origin", sshURL))
+
+ t.Run("SSHPushTestRepository", doGitPushTestRepository(dstPath, "origin", "master"))
+
+ t.Run("CheckIsNotEmpty", doCheckRepositoryEmptyStatus(ctx, false))
+
+ t.Run("DeleteRepository", doAPIDeleteRepository(ctx))
+ })
+}
+
+func TestKeyOnlyOneType(t *testing.T) {
+ onGiteaRun(t, testKeyOnlyOneType)
+}
+
+func testKeyOnlyOneType(t *testing.T, u *url.URL) {
+ // Once a key is a user key we cannot use it as a deploy key
+ // If we delete it from the user we should be able to use it as a deploy key
+ reponame := "ssh-key-test-repo"
+ username := "user2"
+ u.Path = fmt.Sprintf("%s/%s.git", username, reponame)
+ keyname := fmt.Sprintf("%s-push", reponame)
+
+ // OK login
+ ctx := NewAPITestContext(t, username, reponame)
+
+ otherCtx := ctx
+ otherCtx.Reponame = "ssh-key-test-repo-2"
+
+ failCtx := ctx
+ failCtx.ExpectedCode = http.StatusUnprocessableEntity
+
+ t.Run("CreateRepository", doAPICreateRepository(ctx, false))
+ t.Run("CreateOtherRepository", doAPICreateRepository(otherCtx, false))
+
+ withKeyFile(t, keyname, func(keyFile string) {
+ var userKeyPublicKeyID int64
+ t.Run("KeyCanOnlyBeUser", func(t *testing.T) {
+ dstPath, err := ioutil.TempDir("", ctx.Reponame)
+ assert.NoError(t, err)
+ defer os.RemoveAll(dstPath)
+
+ sshURL := createSSHUrl(ctx.GitPath(), u)
+
+ t.Run("FailToClone", doGitCloneFail(dstPath, sshURL))
+
+ t.Run("CreateUserKey", doAPICreateUserKey(ctx, keyname, keyFile, func(t *testing.T, publicKey api.PublicKey) {
+ userKeyPublicKeyID = publicKey.ID
+ }))
+
+ t.Run("FailToAddReadOnlyDeployKey", doAPICreateDeployKey(failCtx, keyname, keyFile, true))
+
+ t.Run("FailToAddDeployKey", doAPICreateDeployKey(failCtx, keyname, keyFile, false))
+
+ t.Run("Clone", doGitClone(dstPath, sshURL))
+
+ t.Run("AddChanges", doAddChangesToCheckout(dstPath, "CHANGES1.md"))
+
+ t.Run("Push", doGitPushTestRepository(dstPath, "origin", "master"))
+
+ t.Run("DeleteUserKey", doAPIDeleteUserKey(ctx, userKeyPublicKeyID))
+ })
+
+ t.Run("KeyCanBeAnyDeployButNotUserAswell", func(t *testing.T) {
+ dstPath, err := ioutil.TempDir("", ctx.Reponame)
+ assert.NoError(t, err)
+ defer os.RemoveAll(dstPath)
+
+ sshURL := createSSHUrl(ctx.GitPath(), u)
+
+ t.Run("FailToClone", doGitCloneFail(dstPath, sshURL))
+
+ // Should now be able to add...
+ t.Run("AddReadOnlyDeployKey", doAPICreateDeployKey(ctx, keyname, keyFile, true))
+
+ t.Run("Clone", doGitClone(dstPath, sshURL))
+
+ t.Run("AddChanges", doAddChangesToCheckout(dstPath, "CHANGES2.md"))
+
+ t.Run("FailToPush", doGitPushTestRepositoryFail(dstPath, "origin", "master"))
+
+ otherSSHURL := createSSHUrl(otherCtx.GitPath(), u)
+ dstOtherPath, err := ioutil.TempDir("", otherCtx.Reponame)
+ assert.NoError(t, err)
+ defer os.RemoveAll(dstOtherPath)
+
+ t.Run("AddWriterDeployKeyToOther", doAPICreateDeployKey(otherCtx, keyname, keyFile, false))
+
+ t.Run("CloneOther", doGitClone(dstOtherPath, otherSSHURL))
+
+ t.Run("AddChangesToOther", doAddChangesToCheckout(dstOtherPath, "CHANGES3.md"))
+
+ t.Run("PushToOther", doGitPushTestRepository(dstOtherPath, "origin", "master"))
+
+ t.Run("FailToCreateUserKey", doAPICreateUserKey(failCtx, keyname, keyFile))
+ })
+
+ t.Run("DeleteRepositoryShouldReleaseKey", func(t *testing.T) {
+ otherSSHURL := createSSHUrl(otherCtx.GitPath(), u)
+ dstOtherPath, err := ioutil.TempDir("", otherCtx.Reponame)
+ assert.NoError(t, err)
+ defer os.RemoveAll(dstOtherPath)
+
+ t.Run("DeleteRepository", doAPIDeleteRepository(ctx))
+
+ t.Run("FailToCreateUserKeyAsStillDeploy", doAPICreateUserKey(failCtx, keyname, keyFile))
+
+ t.Run("MakeSureCloneOtherStillWorks", doGitClone(dstOtherPath, otherSSHURL))
+
+ t.Run("AddChangesToOther", doAddChangesToCheckout(dstOtherPath, "CHANGES3.md"))
+
+ t.Run("PushToOther", doGitPushTestRepository(dstOtherPath, "origin", "master"))
+
+ t.Run("DeleteOtherRepository", doAPIDeleteRepository(otherCtx))
+
+ t.Run("RecreateRepository", doAPICreateRepository(ctx, false))
+
+ t.Run("CreateUserKey", doAPICreateUserKey(ctx, keyname, keyFile, func(t *testing.T, publicKey api.PublicKey) {
+ userKeyPublicKeyID = publicKey.ID
+ }))
+
+ dstPath, err := ioutil.TempDir("", ctx.Reponame)
+ assert.NoError(t, err)
+ defer os.RemoveAll(dstPath)
+
+ sshURL := createSSHUrl(ctx.GitPath(), u)
+
+ t.Run("Clone", doGitClone(dstPath, sshURL))
+
+ t.Run("AddChanges", doAddChangesToCheckout(dstPath, "CHANGES1.md"))
+
+ t.Run("Push", doGitPushTestRepository(dstPath, "origin", "master"))
+ })
+
+ t.Run("DeleteUserKeyShouldRemoveAbilityToClone", func(t *testing.T) {
+ dstPath, err := ioutil.TempDir("", ctx.Reponame)
+ assert.NoError(t, err)
+ defer os.RemoveAll(dstPath)
+
+ sshURL := createSSHUrl(ctx.GitPath(), u)
+
+ t.Run("DeleteUserKey", doAPIDeleteUserKey(ctx, userKeyPublicKeyID))
+
+ t.Run("FailToClone", doGitCloneFail(dstPath, sshURL))
+ })
+ })
+}
diff --git a/models/repo.go b/models/repo.go
index 5e96a4e931..873fd407fb 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -1756,6 +1756,17 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
return ErrRepoNotExist{repoID, uid, "", ""}
}
+ // Delete Deploy Keys
+ deployKeys, err := listDeployKeys(sess, repo.ID)
+ if err != nil {
+ return fmt.Errorf("listDeployKeys: %v", err)
+ }
+ for _, dKey := range deployKeys {
+ if err := deleteDeployKey(sess, doer, dKey.ID); err != nil {
+ return fmt.Errorf("deleteDeployKeys: %v", err)
+ }
+ }
+
if cnt, err := sess.ID(repoID).Delete(&Repository{}); err != nil {
return err
} else if cnt != 1 {
@@ -1898,6 +1909,12 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
}
if err = sess.Commit(); err != nil {
+ if len(deployKeys) > 0 {
+ // We need to rewrite the public keys because the commit failed
+ if err2 := RewriteAllPublicKeys(); err2 != nil {
+ return fmt.Errorf("Commit: %v SSH Keys: %v", err, err2)
+ }
+ }
return fmt.Errorf("Commit: %v", err)
}
diff --git a/models/ssh_key.go b/models/ssh_key.go
index 90c0f04b78..a7dced841d 100644
--- a/models/ssh_key.go
+++ b/models/ssh_key.go
@@ -51,7 +51,7 @@ type PublicKey struct {
ID int64 `xorm:"pk autoincr"`
OwnerID int64 `xorm:"INDEX NOT NULL"`
Name string `xorm:"NOT NULL"`
- Fingerprint string `xorm:"NOT NULL"`
+ Fingerprint string `xorm:"INDEX NOT NULL"`
Content string `xorm:"TEXT NOT NULL"`
Mode AccessMode `xorm:"NOT NULL DEFAULT 2"`
Type KeyType `xorm:"NOT NULL DEFAULT 1"`
@@ -350,7 +350,6 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
func checkKeyFingerprint(e Engine, fingerprint string) error {
has, err := e.Get(&PublicKey{
Fingerprint: fingerprint,
- Type: KeyTypeUser,
})
if err != nil {
return err
@@ -401,12 +400,18 @@ func AddPublicKey(ownerID int64, name, content string, LoginSourceID int64) (*Pu
return nil, err
}
- if err := checkKeyFingerprint(x, fingerprint); err != nil {
+ sess := x.NewSession()
+ defer sess.Close()
+ if err = sess.Begin(); err != nil {
+ return nil, err
+ }
+
+ if err := checkKeyFingerprint(sess, fingerprint); err != nil {
return nil, err
}
// Key name of same user cannot be duplicated.
- has, err := x.
+ has, err := sess.
Where("owner_id = ? AND name = ?", ownerID, name).
Get(new(PublicKey))
if err != nil {
@@ -415,12 +420,6 @@ func AddPublicKey(ownerID int64, name, content string, LoginSourceID int64) (*Pu
return nil, ErrKeyNameAlreadyUsed{ownerID, name}
}
- sess := x.NewSession()
- defer sess.Close()
- if err = sess.Begin(); err != nil {
- return nil, err
- }
-
key := &PublicKey{
OwnerID: ownerID,
Name: name,
@@ -519,7 +518,7 @@ func UpdatePublicKeyUpdated(id int64) error {
}
// deletePublicKeys does the actual key deletion but does not update authorized_keys file.
-func deletePublicKeys(e *xorm.Session, keyIDs ...int64) error {
+func deletePublicKeys(e Engine, keyIDs ...int64) error {
if len(keyIDs) == 0 {
return nil
}
@@ -728,24 +727,28 @@ func AddDeployKey(repoID int64, name, content string, readOnly bool) (*DeployKey
accessMode = AccessModeWrite
}
- pkey := &PublicKey{
- Fingerprint: fingerprint,
- Mode: accessMode,
- Type: KeyTypeDeploy,
- }
- has, err := x.Get(pkey)
- if err != nil {
- return nil, err
- }
-
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return nil, err
}
- // First time use this deploy key.
- if !has {
+ pkey := &PublicKey{
+ Fingerprint: fingerprint,
+ }
+ has, err := sess.Get(pkey)
+ if err != nil {
+ return nil, err
+ }
+
+ if has {
+ if pkey.Type != KeyTypeDeploy {
+ return nil, ErrKeyAlreadyExist{0, fingerprint, ""}
+ }
+ } else {
+ // First time use this deploy key.
+ pkey.Mode = accessMode
+ pkey.Type = KeyTypeDeploy
pkey.Content = content
pkey.Name = name
if err = addKey(sess, pkey); err != nil {
@@ -763,8 +766,12 @@ func AddDeployKey(repoID int64, name, content string, readOnly bool) (*DeployKey
// GetDeployKeyByID returns deploy key by given ID.
func GetDeployKeyByID(id int64) (*DeployKey, error) {
+ return getDeployKeyByID(x, id)
+}
+
+func getDeployKeyByID(e Engine, id int64) (*DeployKey, error) {
key := new(DeployKey)
- has, err := x.ID(id).Get(key)
+ has, err := e.ID(id).Get(key)
if err != nil {
return nil, err
} else if !has {
@@ -775,11 +782,15 @@ func GetDeployKeyByID(id int64) (*DeployKey, error) {
// GetDeployKeyByRepo returns deploy key by given public key ID and repository ID.
func GetDeployKeyByRepo(keyID, repoID int64) (*DeployKey, error) {
+ return getDeployKeyByRepo(x, keyID, repoID)
+}
+
+func getDeployKeyByRepo(e Engine, keyID, repoID int64) (*DeployKey, error) {
key := &DeployKey{
KeyID: keyID,
RepoID: repoID,
}
- has, err := x.Get(key)
+ has, err := e.Get(key)
if err != nil {
return nil, err
} else if !has {
@@ -802,7 +813,19 @@ func UpdateDeployKey(key *DeployKey) error {
// DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed.
func DeleteDeployKey(doer *User, id int64) error {
- key, err := GetDeployKeyByID(id)
+ sess := x.NewSession()
+ defer sess.Close()
+ if err := sess.Begin(); err != nil {
+ return err
+ }
+ if err := deleteDeployKey(sess, doer, id); err != nil {
+ return err
+ }
+ return sess.Commit()
+}
+
+func deleteDeployKey(sess Engine, doer *User, id int64) error {
+ key, err := getDeployKeyByID(sess, id)
if err != nil {
if IsErrDeployKeyNotExist(err) {
return nil
@@ -812,11 +835,11 @@ func DeleteDeployKey(doer *User, id int64) error {
// Check if user has access to delete this key.
if !doer.IsAdmin {
- repo, err := GetRepositoryByID(key.RepoID)
+ repo, err := getRepositoryByID(sess, key.RepoID)
if err != nil {
return fmt.Errorf("GetRepositoryByID: %v", err)
}
- has, err := IsUserRepoAdmin(repo, doer)
+ has, err := isUserRepoAdmin(sess, repo, doer)
if err != nil {
return fmt.Errorf("GetUserRepoPermission: %v", err)
} else if !has {
@@ -824,12 +847,6 @@ func DeleteDeployKey(doer *User, id int64) error {
}
}
- sess := x.NewSession()
- defer sess.Close()
- if err = sess.Begin(); err != nil {
- return err
- }
-
if _, err = sess.ID(key.ID).Delete(new(DeployKey)); err != nil {
return fmt.Errorf("delete deploy key [%d]: %v", key.ID, err)
}
@@ -851,13 +868,17 @@ func DeleteDeployKey(doer *User, id int64) error {
}
}
- return sess.Commit()
+ return nil
}
// ListDeployKeys returns all deploy keys by given repository ID.
func ListDeployKeys(repoID int64) ([]*DeployKey, error) {
+ return listDeployKeys(x, repoID)
+}
+
+func listDeployKeys(e Engine, repoID int64) ([]*DeployKey, error) {
keys := make([]*DeployKey, 0, 5)
- return keys, x.
+ return keys, e.
Where("repo_id = ?", repoID).
Find(&keys)
}
diff --git a/modules/private/key.go b/modules/private/key.go
index 86d0a730d1..1c6511846b 100644
--- a/modules/private/key.go
+++ b/modules/private/key.go
@@ -32,6 +32,31 @@ func UpdateDeployKeyUpdated(keyID int64, repoID int64) error {
return nil
}
+// GetDeployKey check if repo has deploy key
+func GetDeployKey(keyID, repoID int64) (*models.DeployKey, error) {
+ reqURL := setting.LocalURL + fmt.Sprintf("api/internal/repositories/%d/keys/%d", repoID, keyID)
+ log.GitLogger.Trace("GetDeployKey: %s", reqURL)
+
+ resp, err := newInternalRequest(reqURL, "GET").Response()
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ switch resp.StatusCode {
+ case 404:
+ return nil, nil
+ case 200:
+ var dKey models.DeployKey
+ if err := json.NewDecoder(resp.Body).Decode(&dKey); err != nil {
+ return nil, err
+ }
+ return &dKey, nil
+ default:
+ return nil, fmt.Errorf("Failed to get deploy key: %s", decodeJSONError(resp).Err)
+ }
+}
+
// HasDeployKey check if repo has deploy key
func HasDeployKey(keyID, repoID int64) (bool, error) {
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/repositories/%d/has-keys/%d", repoID, keyID)
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 653d34c931..2d32fac9c7 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -419,7 +419,7 @@ ssh_helper = Need help? Have a look at GitHub's guide to Need help? Have a look at GitHub's guide about GPG.
add_new_key = Add SSH Key
add_new_gpg_key = Add GPG Key
-ssh_key_been_used = This SSH key is already added to your account.
+ssh_key_been_used = This SSH key has already been added to the server.
ssh_key_name_used = An SSH key with same name is already added to your account.
gpg_key_id_used = A public GPG key with same ID already exists.
gpg_no_key_email_found = This GPG key is not usable with any email address associated with your account.
diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go
index 2caca887aa..2ee1ce0098 100644
--- a/routers/api/v1/repo/key.go
+++ b/routers/api/v1/repo/key.go
@@ -159,6 +159,8 @@ func HandleCheckKeyStringError(ctx *context.APIContext, err error) {
// HandleAddKeyError handle add key error
func HandleAddKeyError(ctx *context.APIContext, err error) {
switch {
+ case models.IsErrDeployKeyAlreadyExist(err):
+ ctx.Error(422, "", "This key has already been added to this repository")
case models.IsErrKeyAlreadyExist(err):
ctx.Error(422, "", "Key content has been used as non-deploy key")
case models.IsErrKeyNameAlreadyUsed(err):
diff --git a/routers/private/internal.go b/routers/private/internal.go
index ec2281c5c5..ee6e1274c3 100644
--- a/routers/private/internal.go
+++ b/routers/private/internal.go
@@ -82,6 +82,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/repositories/:repoid/keys/:keyid/update", UpdateDeployKey)
m.Get("/repositories/:repoid/user/:userid/checkunituser", CheckUnitUser)
m.Get("/repositories/:repoid/has-keys/:keyid", HasDeployKey)
+ m.Get("/repositories/:repoid/keys/:keyid", GetDeployKey)
m.Get("/repositories/:repoid/wiki/init", InitWiki)
m.Post("/push/update", PushUpdate)
m.Get("/protectedbranch/:pbid/:userid", CanUserPush)
diff --git a/routers/private/key.go b/routers/private/key.go
index 9cc1165780..ee22f6ac48 100644
--- a/routers/private/key.go
+++ b/routers/private/key.go
@@ -72,6 +72,24 @@ func GetUserByKeyID(ctx *macaron.Context) {
ctx.JSON(200, user)
}
+//GetDeployKey chainload to models.GetDeployKey
+func GetDeployKey(ctx *macaron.Context) {
+ repoID := ctx.ParamsInt64(":repoid")
+ keyID := ctx.ParamsInt64(":keyid")
+ dKey, err := models.GetDeployKeyByRepo(keyID, repoID)
+ if err != nil {
+ if models.IsErrDeployKeyNotExist(err) {
+ ctx.JSON(404, []byte("not found"))
+ return
+ }
+ ctx.JSON(500, map[string]interface{}{
+ "err": err.Error(),
+ })
+ return
+ }
+ ctx.JSON(200, dKey)
+}
+
//HasDeployKey chainload to models.HasDeployKey
func HasDeployKey(ctx *macaron.Context) {
repoID := ctx.ParamsInt64(":repoid")
diff --git a/routers/repo/setting.go b/routers/repo/setting.go
index 5e9e24f9f2..4fb74f6cfa 100644
--- a/routers/repo/setting.go
+++ b/routers/repo/setting.go
@@ -622,6 +622,9 @@ func DeployKeysPost(ctx *context.Context, form auth.AddKeyForm) {
case models.IsErrDeployKeyAlreadyExist(err):
ctx.Data["Err_Content"] = true
ctx.RenderWithErr(ctx.Tr("repo.settings.key_been_used"), tplDeployKeys, &form)
+ case models.IsErrKeyAlreadyExist(err):
+ ctx.Data["Err_Content"] = true
+ ctx.RenderWithErr(ctx.Tr("settings.ssh_key_been_used"), tplDeployKeys, &form)
case models.IsErrKeyNameAlreadyUsed(err):
ctx.Data["Err_Title"] = true
ctx.RenderWithErr(ctx.Tr("repo.settings.key_name_used"), tplDeployKeys, &form)