mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-08 05:02:38 +09:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b612ce42e | ||
|
|
1d8e56e6bb | ||
|
|
57ab65d922 | ||
|
|
3ac4a7fab8 | ||
|
|
253efbcb51 | ||
|
|
c8f061e15b | ||
|
|
7f7c451de4 | ||
|
|
b0b574f805 | ||
|
|
d269179523 | ||
|
|
6416f06508 | ||
|
|
1a8ab63dda | ||
|
|
477b4de0d1 | ||
|
|
849c85a2ec | ||
|
|
731275247d | ||
|
|
022634aa75 |
@@ -211,7 +211,7 @@ pipeline:
|
|||||||
branch: [ master ]
|
branch: [ master ]
|
||||||
|
|
||||||
static:
|
static:
|
||||||
image: karalabe/xgo-latest:latest
|
image: techknowlogick/xgo:latest
|
||||||
pull: true
|
pull: true
|
||||||
environment:
|
environment:
|
||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
TAGS: bindata sqlite sqlite_unlock_notify
|
||||||
|
|||||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -4,6 +4,22 @@ This changelog goes through all the changes that have been made in each release
|
|||||||
without substantial changes to our git log; to see the highlights of what has
|
without substantial changes to our git log; to see the highlights of what has
|
||||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||||
|
|
||||||
|
## [1.7.2](https://github.com/go-gitea/gitea/releases/tag/v1.7.2) - 2019-02-14
|
||||||
|
* BUGFIXES
|
||||||
|
* Remove all CommitStatus when a repo is deleted (#5940) (#5941)
|
||||||
|
* Fix notifications on pushing with deploy keys by setting hook environment variables (#5935) (#5944)
|
||||||
|
* Silence console logger in gitea serv (#5887) (#5943)
|
||||||
|
* Handle milestone webhook events for issues and PR (#5947) (#5955)
|
||||||
|
* Show user who created the repository instead of the organization in action feed (#5948) (#5956)
|
||||||
|
* Fix ssh deploy and user key constraints (#1357) (#5939) (#5966)
|
||||||
|
* Fix bug when deleting a linked account will removed all (#5989) (#5990)
|
||||||
|
* Fix empty ssh key importing in ldap (#5984) (#6009)
|
||||||
|
* Fix metrics auth token detection (#6006) (#6017)
|
||||||
|
* Create repository on organisation by default on its dashboard (#6026) (#6048)
|
||||||
|
* Make sure labels are actually returned in API (#6053) (#6059)
|
||||||
|
* Switch to more recent build of xgo (#6070) (#6072)
|
||||||
|
* In basic auth check for tokens before call UserSignIn (#5725) (#6083)
|
||||||
|
|
||||||
## [1.7.1](https://github.com/go-gitea/gitea/releases/tag/v1.7.1) - 2019-01-31
|
## [1.7.1](https://github.com/go-gitea/gitea/releases/tag/v1.7.1) - 2019-01-31
|
||||||
* SECURITY
|
* SECURITY
|
||||||
* Disable redirect for i18n (#5910) (#5916)
|
* Disable redirect for i18n (#5910) (#5916)
|
||||||
|
|||||||
22
cmd/serv.go
22
cmd/serv.go
@@ -70,6 +70,7 @@ func checkLFSVersion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setup(logPath string) {
|
func setup(logPath string) {
|
||||||
|
log.DelLogger("console")
|
||||||
setting.NewContext()
|
setting.NewContext()
|
||||||
checkLFSVersion()
|
checkLFSVersion()
|
||||||
log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath))
|
log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath))
|
||||||
@@ -233,23 +234,30 @@ func runServ(c *cli.Context) error {
|
|||||||
|
|
||||||
// Check deploy key or user key.
|
// Check deploy key or user key.
|
||||||
if key.Type == models.KeyTypeDeploy {
|
if key.Type == models.KeyTypeDeploy {
|
||||||
if key.Mode < requestedMode {
|
// Now we have to get the deploy key for this repo
|
||||||
fail("Key permission denied", "Cannot push with deployment key: %d", key.ID)
|
deployKey, err := private.GetDeployKey(key.ID, repo.ID)
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this deploy key belongs to current repository.
|
|
||||||
has, err := private.HasDeployKey(key.ID, repo.ID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fail("Key access denied", "Failed to access internal api: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
|
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)
|
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.
|
// Update deploy key activity.
|
||||||
if err = private.UpdateDeployKeyUpdated(key.ID, repo.ID); err != nil {
|
if err = private.UpdateDeployKeyUpdated(key.ID, repo.ID); err != nil {
|
||||||
fail("Internal error", "UpdateDeployKey: %v", err)
|
fail("Internal error", "UpdateDeployKey: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Deploy keys aren't really the owner of the repo pushing changes
|
||||||
|
// however we don't have good way of representing deploy keys in hook.go
|
||||||
|
// so for now use the owner
|
||||||
|
os.Setenv(models.EnvPusherName, username)
|
||||||
|
os.Setenv(models.EnvPusherID, fmt.Sprintf("%d", repo.OwnerID))
|
||||||
} else {
|
} else {
|
||||||
user, err = private.GetUserByKeyID(key.ID)
|
user, err = private.GetUserByKeyID(key.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
152
integrations/api_helper_for_declarative_test.go
Normal file
152
integrations/api_helper_for_declarative_test.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
127
integrations/git_helper_for_declarative_test.go
Normal file
127
integrations/git_helper_for_declarative_test.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,25 +5,17 @@
|
|||||||
package integrations
|
package integrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/git"
|
"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"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,69 +24,32 @@ const (
|
|||||||
bigSize = 128 * 1024 * 1024 //128Mo
|
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) {
|
func TestGit(t *testing.T) {
|
||||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
onGiteaRun(t, testGit)
|
||||||
u.Path = "user2/repo1.git"
|
}
|
||||||
|
|
||||||
|
func testGit(t *testing.T, u *url.URL) {
|
||||||
|
username := "user2"
|
||||||
|
baseAPITestContext := NewAPITestContext(t, username, "repo1")
|
||||||
|
|
||||||
|
u.Path = baseAPITestContext.GitPath()
|
||||||
|
|
||||||
t.Run("HTTP", func(t *testing.T) {
|
t.Run("HTTP", func(t *testing.T) {
|
||||||
dstPath, err := ioutil.TempDir("", "repo-tmp-17")
|
httpContext := baseAPITestContext
|
||||||
|
httpContext.Reponame = "repo-tmp-17"
|
||||||
|
|
||||||
|
dstPath, err := ioutil.TempDir("", httpContext.Reponame)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
defer os.RemoveAll(dstPath)
|
defer os.RemoveAll(dstPath)
|
||||||
t.Run("Standard", func(t *testing.T) {
|
t.Run("Standard", func(t *testing.T) {
|
||||||
t.Run("CloneNoLogin", func(t *testing.T) {
|
ensureAnonymousClone(t, u)
|
||||||
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")))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("CreateRepo", func(t *testing.T) {
|
t.Run("CreateRepo", doAPICreateRepository(httpContext, false))
|
||||||
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)
|
|
||||||
})
|
|
||||||
|
|
||||||
u.Path = "user2/repo-tmp-17.git"
|
u.Path = httpContext.GitPath()
|
||||||
u.User = url.UserPassword("user2", userPassword)
|
u.User = url.UserPassword(username, userPassword)
|
||||||
t.Run("Clone", func(t *testing.T) {
|
|
||||||
err = git.Clone(u.String(), dstPath, git.CloneRepoOptions{})
|
t.Run("Clone", doGitClone(dstPath, u))
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.True(t, com.IsExist(filepath.Join(dstPath, "README.md")))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("PushCommit", func(t *testing.T) {
|
t.Run("PushCommit", func(t *testing.T) {
|
||||||
t.Run("Little", func(t *testing.T) {
|
t.Run("Little", func(t *testing.T) {
|
||||||
@@ -128,64 +83,27 @@ func TestGit(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
t.Run("SSH", func(t *testing.T) {
|
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 remote link
|
//Setup remote link
|
||||||
u.Scheme = "ssh"
|
sshURL := createSSHUrl(sshContext.GitPath(), u)
|
||||||
u.User = url.User("git")
|
|
||||||
u.Host = fmt.Sprintf("%s:%d", setting.SSH.ListenHost, setting.SSH.ListenPort)
|
|
||||||
u.Path = "user2/repo-tmp-18.git"
|
|
||||||
|
|
||||||
//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",
|
|
||||||
})
|
|
||||||
session.MakeRequest(t, req, http.StatusCreated)
|
|
||||||
|
|
||||||
//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 clone folder
|
//Setup clone folder
|
||||||
dstPath, err := ioutil.TempDir("", "repo-tmp-18")
|
dstPath, err := ioutil.TempDir("", sshContext.Reponame)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
defer os.RemoveAll(dstPath)
|
defer os.RemoveAll(dstPath)
|
||||||
|
|
||||||
t.Run("Standard", func(t *testing.T) {
|
t.Run("Standard", func(t *testing.T) {
|
||||||
t.Run("CreateRepo", func(t *testing.T) {
|
t.Run("CreateRepo", doAPICreateRepository(sshContext, false))
|
||||||
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)
|
|
||||||
})
|
|
||||||
//TODO get url from api
|
//TODO get url from api
|
||||||
t.Run("Clone", func(t *testing.T) {
|
t.Run("Clone", doGitClone(dstPath, sshURL))
|
||||||
_, err = git.NewCommand("clone").AddArguments(u.String(), dstPath).Run()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.True(t, com.IsExist(filepath.Join(dstPath, "README.md")))
|
|
||||||
})
|
|
||||||
//time.Sleep(5 * time.Minute)
|
//time.Sleep(5 * time.Minute)
|
||||||
t.Run("PushCommit", func(t *testing.T) {
|
t.Run("PushCommit", func(t *testing.T) {
|
||||||
t.Run("Little", 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)
|
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) {
|
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
|
_, err := git.NewCommand("remote").AddArguments("set-url", "origin", remote).RunInDir(repoPath) //TODO add test ssh git-lfs-creds
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|||||||
217
integrations/ssh_key_test.go
Normal file
217
integrations/ssh_key_test.go
Normal file
@@ -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))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -35,8 +35,8 @@ import (
|
|||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
"github.com/go-xorm/builder"
|
"github.com/go-xorm/builder"
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
"github.com/mcuadros/go-version"
|
version "github.com/mcuadros/go-version"
|
||||||
"gopkg.in/ini.v1"
|
ini "gopkg.in/ini.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var repoWorkingPool = sync.NewExclusivePool()
|
var repoWorkingPool = sync.NewExclusivePool()
|
||||||
@@ -1346,14 +1346,14 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err
|
|||||||
|
|
||||||
if err = watchRepo(e, doer.ID, repo.ID, true); err != nil {
|
if err = watchRepo(e, doer.ID, repo.ID, true); err != nil {
|
||||||
return fmt.Errorf("watchRepo: %v", err)
|
return fmt.Errorf("watchRepo: %v", err)
|
||||||
} else if err = newRepoAction(e, u, repo); err != nil {
|
} else if err = newRepoAction(e, doer, repo); err != nil {
|
||||||
return fmt.Errorf("newRepoAction: %v", err)
|
return fmt.Errorf("newRepoAction: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateRepository creates a repository for the user/organization u.
|
// CreateRepository creates a repository for the user/organization.
|
||||||
func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err error) {
|
func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err error) {
|
||||||
if !doer.IsAdmin && !u.CanCreateRepo() {
|
if !doer.IsAdmin && !u.CanCreateRepo() {
|
||||||
return nil, ErrReachLimitOfRepo{u.MaxRepoCreation}
|
return nil, ErrReachLimitOfRepo{u.MaxRepoCreation}
|
||||||
@@ -1743,6 +1743,17 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
|
|||||||
return ErrRepoNotExist{repoID, uid, "", ""}
|
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 {
|
if cnt, err := sess.ID(repoID).Delete(&Repository{}); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if cnt != 1 {
|
} else if cnt != 1 {
|
||||||
@@ -1774,6 +1785,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
|
|||||||
&Webhook{RepoID: repoID},
|
&Webhook{RepoID: repoID},
|
||||||
&HookTask{RepoID: repoID},
|
&HookTask{RepoID: repoID},
|
||||||
&Notification{RepoID: repoID},
|
&Notification{RepoID: repoID},
|
||||||
|
&CommitStatus{RepoID: repoID},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return fmt.Errorf("deleteBeans: %v", err)
|
return fmt.Errorf("deleteBeans: %v", err)
|
||||||
}
|
}
|
||||||
@@ -1884,6 +1896,12 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err = sess.Commit(); err != nil {
|
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)
|
return fmt.Errorf("Commit: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ type PublicKey struct {
|
|||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
OwnerID int64 `xorm:"INDEX NOT NULL"`
|
OwnerID int64 `xorm:"INDEX NOT NULL"`
|
||||||
Name string `xorm:"NOT NULL"`
|
Name string `xorm:"NOT NULL"`
|
||||||
Fingerprint string `xorm:"NOT NULL"`
|
Fingerprint string `xorm:"INDEX NOT NULL"`
|
||||||
Content string `xorm:"TEXT NOT NULL"`
|
Content string `xorm:"TEXT NOT NULL"`
|
||||||
Mode AccessMode `xorm:"NOT NULL DEFAULT 2"`
|
Mode AccessMode `xorm:"NOT NULL DEFAULT 2"`
|
||||||
Type KeyType `xorm:"NOT NULL DEFAULT 1"`
|
Type KeyType `xorm:"NOT NULL DEFAULT 1"`
|
||||||
@@ -350,7 +350,6 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
|
|||||||
func checkKeyFingerprint(e Engine, fingerprint string) error {
|
func checkKeyFingerprint(e Engine, fingerprint string) error {
|
||||||
has, err := e.Get(&PublicKey{
|
has, err := e.Get(&PublicKey{
|
||||||
Fingerprint: fingerprint,
|
Fingerprint: fingerprint,
|
||||||
Type: KeyTypeUser,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -401,12 +400,18 @@ func AddPublicKey(ownerID int64, name, content string, LoginSourceID int64) (*Pu
|
|||||||
return nil, err
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key name of same user cannot be duplicated.
|
// Key name of same user cannot be duplicated.
|
||||||
has, err := x.
|
has, err := sess.
|
||||||
Where("owner_id = ? AND name = ?", ownerID, name).
|
Where("owner_id = ? AND name = ?", ownerID, name).
|
||||||
Get(new(PublicKey))
|
Get(new(PublicKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -415,12 +420,6 @@ func AddPublicKey(ownerID int64, name, content string, LoginSourceID int64) (*Pu
|
|||||||
return nil, ErrKeyNameAlreadyUsed{ownerID, name}
|
return nil, ErrKeyNameAlreadyUsed{ownerID, name}
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
|
||||||
defer sess.Close()
|
|
||||||
if err = sess.Begin(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
key := &PublicKey{
|
key := &PublicKey{
|
||||||
OwnerID: ownerID,
|
OwnerID: ownerID,
|
||||||
Name: name,
|
Name: name,
|
||||||
@@ -519,7 +518,7 @@ func UpdatePublicKeyUpdated(id int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// deletePublicKeys does the actual key deletion but does not update authorized_keys file.
|
// 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 {
|
if len(keyIDs) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -728,24 +727,28 @@ func AddDeployKey(repoID int64, name, content string, readOnly bool) (*DeployKey
|
|||||||
accessMode = AccessModeWrite
|
accessMode = AccessModeWrite
|
||||||
}
|
}
|
||||||
|
|
||||||
pkey := &PublicKey{
|
|
||||||
Fingerprint: fingerprint,
|
|
||||||
Mode: accessMode,
|
|
||||||
Type: KeyTypeDeploy,
|
|
||||||
}
|
|
||||||
has, err := x.Get(pkey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
defer sess.Close()
|
defer sess.Close()
|
||||||
if err = sess.Begin(); err != nil {
|
if err = sess.Begin(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
// First time use this deploy key.
|
||||||
if !has {
|
pkey.Mode = accessMode
|
||||||
|
pkey.Type = KeyTypeDeploy
|
||||||
pkey.Content = content
|
pkey.Content = content
|
||||||
pkey.Name = name
|
pkey.Name = name
|
||||||
if err = addKey(sess, pkey); err != nil {
|
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.
|
// GetDeployKeyByID returns deploy key by given ID.
|
||||||
func GetDeployKeyByID(id int64) (*DeployKey, error) {
|
func GetDeployKeyByID(id int64) (*DeployKey, error) {
|
||||||
|
return getDeployKeyByID(x, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDeployKeyByID(e Engine, id int64) (*DeployKey, error) {
|
||||||
key := new(DeployKey)
|
key := new(DeployKey)
|
||||||
has, err := x.ID(id).Get(key)
|
has, err := e.ID(id).Get(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} 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.
|
// GetDeployKeyByRepo returns deploy key by given public key ID and repository ID.
|
||||||
func GetDeployKeyByRepo(keyID, repoID int64) (*DeployKey, error) {
|
func GetDeployKeyByRepo(keyID, repoID int64) (*DeployKey, error) {
|
||||||
|
return getDeployKeyByRepo(x, keyID, repoID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDeployKeyByRepo(e Engine, keyID, repoID int64) (*DeployKey, error) {
|
||||||
key := &DeployKey{
|
key := &DeployKey{
|
||||||
KeyID: keyID,
|
KeyID: keyID,
|
||||||
RepoID: repoID,
|
RepoID: repoID,
|
||||||
}
|
}
|
||||||
has, err := x.Get(key)
|
has, err := e.Get(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
@@ -802,7 +813,19 @@ func UpdateDeployKey(key *DeployKey) error {
|
|||||||
|
|
||||||
// DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed.
|
// DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed.
|
||||||
func DeleteDeployKey(doer *User, id int64) error {
|
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 err != nil {
|
||||||
if IsErrDeployKeyNotExist(err) {
|
if IsErrDeployKeyNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
@@ -812,11 +835,11 @@ func DeleteDeployKey(doer *User, id int64) error {
|
|||||||
|
|
||||||
// Check if user has access to delete this key.
|
// Check if user has access to delete this key.
|
||||||
if !doer.IsAdmin {
|
if !doer.IsAdmin {
|
||||||
repo, err := GetRepositoryByID(key.RepoID)
|
repo, err := getRepositoryByID(sess, key.RepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetRepositoryByID: %v", err)
|
return fmt.Errorf("GetRepositoryByID: %v", err)
|
||||||
}
|
}
|
||||||
has, err := IsUserRepoAdmin(repo, doer)
|
has, err := isUserRepoAdmin(sess, repo, doer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetUserRepoPermission: %v", err)
|
return fmt.Errorf("GetUserRepoPermission: %v", err)
|
||||||
} else if !has {
|
} 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 {
|
if _, err = sess.ID(key.ID).Delete(new(DeployKey)); err != nil {
|
||||||
return fmt.Errorf("delete deploy key [%d]: %v", key.ID, err)
|
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.
|
// ListDeployKeys returns all deploy keys by given repository ID.
|
||||||
func ListDeployKeys(repoID int64) ([]*DeployKey, error) {
|
func ListDeployKeys(repoID int64) ([]*DeployKey, error) {
|
||||||
|
return listDeployKeys(x, repoID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listDeployKeys(e Engine, repoID int64) ([]*DeployKey, error) {
|
||||||
keys := make([]*DeployKey, 0, 5)
|
keys := make([]*DeployKey, 0, 5)
|
||||||
return keys, x.
|
return keys, e.
|
||||||
Where("repo_id = ?", repoID).
|
Where("repo_id = ?", repoID).
|
||||||
Find(&keys)
|
Find(&keys)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1461,11 +1461,14 @@ func synchronizeLdapSSHPublicKeys(usr *User, s *LoginSource, SSHPublicKeys []str
|
|||||||
// Get Public Keys from LDAP and skip duplicate keys
|
// Get Public Keys from LDAP and skip duplicate keys
|
||||||
var ldapKeys []string
|
var ldapKeys []string
|
||||||
for _, v := range SSHPublicKeys {
|
for _, v := range SSHPublicKeys {
|
||||||
ldapKey := strings.Join(strings.Split(v, " ")[:2], " ")
|
sshKeySplit := strings.Split(v, " ")
|
||||||
|
if len(sshKeySplit) > 1 {
|
||||||
|
ldapKey := strings.Join(sshKeySplit[:2], " ")
|
||||||
if !util.ExistsInSlice(ldapKey, ldapKeys) {
|
if !util.ExistsInSlice(ldapKey, ldapKeys) {
|
||||||
ldapKeys = append(ldapKeys, ldapKey)
|
ldapKeys = append(ldapKeys, ldapKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if Public Key sync is needed
|
// Check if Public Key sync is needed
|
||||||
if util.IsEqualSlice(giteaKeys, ldapKeys) {
|
if util.IsEqualSlice(giteaKeys, ldapKeys) {
|
||||||
|
|||||||
@@ -160,6 +160,10 @@ func getSlackIssuesPayload(p *api.IssuePayload, slack *SlackMeta) (*SlackPayload
|
|||||||
text = fmt.Sprintf("[%s] Issue labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||||
case api.HookIssueSynchronized:
|
case api.HookIssueSynchronized:
|
||||||
text = fmt.Sprintf("[%s] Issue synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Issue synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||||
|
case api.HookIssueMilestoned:
|
||||||
|
text = fmt.Sprintf("[%s] Issue milestoned: #%s %s", p.Repository.FullName, titleLink, senderLink)
|
||||||
|
case api.HookIssueDemilestoned:
|
||||||
|
text = fmt.Sprintf("[%s] Issue milestone cleared: #%s %s", p.Repository.FullName, titleLink, senderLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SlackPayload{
|
return &SlackPayload{
|
||||||
@@ -312,6 +316,10 @@ func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*S
|
|||||||
text = fmt.Sprintf("[%s] Pull request labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||||
case api.HookIssueSynchronized:
|
case api.HookIssueSynchronized:
|
||||||
text = fmt.Sprintf("[%s] Pull request synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
text = fmt.Sprintf("[%s] Pull request synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||||
|
case api.HookIssueMilestoned:
|
||||||
|
text = fmt.Sprintf("[%s] Pull request milestoned: #%s %s", p.Repository.FullName, titleLink, senderLink)
|
||||||
|
case api.HookIssueDemilestoned:
|
||||||
|
text = fmt.Sprintf("[%s] Pull request milestone cleared: #%s %s", p.Repository.FullName, titleLink, senderLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SlackPayload{
|
return &SlackPayload{
|
||||||
|
|||||||
@@ -135,15 +135,56 @@ func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool)
|
|||||||
if len(baHead) > 0 {
|
if len(baHead) > 0 {
|
||||||
auths := strings.Fields(baHead)
|
auths := strings.Fields(baHead)
|
||||||
if len(auths) == 2 && auths[0] == "Basic" {
|
if len(auths) == 2 && auths[0] == "Basic" {
|
||||||
|
var u *models.User
|
||||||
|
|
||||||
uname, passwd, _ := base.BasicAuthDecode(auths[1])
|
uname, passwd, _ := base.BasicAuthDecode(auths[1])
|
||||||
|
|
||||||
u, err := models.UserSignIn(uname, passwd)
|
// Check if username or password is a token
|
||||||
|
isUsernameToken := len(passwd) == 0 || passwd == "x-oauth-basic"
|
||||||
|
// Assume username is token
|
||||||
|
authToken := uname
|
||||||
|
if !isUsernameToken {
|
||||||
|
// Assume password is token
|
||||||
|
authToken = passwd
|
||||||
|
}
|
||||||
|
token, err := models.GetAccessTokenBySHA(authToken)
|
||||||
|
if err == nil {
|
||||||
|
if isUsernameToken {
|
||||||
|
u, err = models.GetUserByID(token.UID)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(4, "GetUserByID: %v", err)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
u, err = models.GetUserByName(uname)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(4, "GetUserByID: %v", err)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
if u.ID != token.UID {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
token.UpdatedUnix = util.TimeStampNow()
|
||||||
|
if err = models.UpdateAccessToken(token); err != nil {
|
||||||
|
log.Error(4, "UpdateAccessToken: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !models.IsErrAccessTokenNotExist(err) && !models.IsErrAccessTokenEmpty(err) {
|
||||||
|
log.Error(4, "GetAccessTokenBySha: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if u == nil {
|
||||||
|
u, err = models.UserSignIn(uname, passwd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !models.IsErrUserNotExist(err) {
|
if !models.IsErrUserNotExist(err) {
|
||||||
log.Error(4, "UserSignIn: %v", err)
|
log.Error(4, "UserSignIn: %v", err)
|
||||||
}
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx.Data["IsApiToken"] = true
|
ctx.Data["IsApiToken"] = true
|
||||||
return u, true
|
return u, true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,31 @@ func UpdateDeployKeyUpdated(keyID int64, repoID int64) error {
|
|||||||
return nil
|
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
|
// HasDeployKey check if repo has deploy key
|
||||||
func HasDeployKey(keyID, repoID int64) (bool, error) {
|
func HasDeployKey(keyID, repoID int64) (bool, error) {
|
||||||
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/repositories/%d/has-keys/%d", repoID, keyID)
|
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/repositories/%d/has-keys/%d", repoID, keyID)
|
||||||
|
|||||||
@@ -413,7 +413,7 @@ ssh_helper = <strong>Need help?</strong> Have a look at GitHub's guide to <a hre
|
|||||||
gpg_helper = <strong>Need help?</strong> Have a look at GitHub's guide <a href="%s">about GPG</a>.
|
gpg_helper = <strong>Need help?</strong> Have a look at GitHub's guide <a href="%s">about GPG</a>.
|
||||||
add_new_key = Add SSH Key
|
add_new_key = Add SSH Key
|
||||||
add_new_gpg_key = Add GPG 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.
|
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_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.
|
gpg_no_key_email_found = This GPG key is not usable with any email address associated with your account.
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ func ListIssueLabels(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := issue.LoadAttributes(); err != nil {
|
||||||
|
ctx.Error(500, "LoadAttributes", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
apiLabels := make([]*api.Label, len(issue.Labels))
|
apiLabels := make([]*api.Label, len(issue.Labels))
|
||||||
for i := range issue.Labels {
|
for i := range issue.Labels {
|
||||||
apiLabels[i] = issue.Labels[i].APIFormat()
|
apiLabels[i] = issue.Labels[i].APIFormat()
|
||||||
|
|||||||
@@ -159,6 +159,8 @@ func HandleCheckKeyStringError(ctx *context.APIContext, err error) {
|
|||||||
// HandleAddKeyError handle add key error
|
// HandleAddKeyError handle add key error
|
||||||
func HandleAddKeyError(ctx *context.APIContext, err error) {
|
func HandleAddKeyError(ctx *context.APIContext, err error) {
|
||||||
switch {
|
switch {
|
||||||
|
case models.IsErrDeployKeyAlreadyExist(err):
|
||||||
|
ctx.Error(422, "", "This key has already been added to this repository")
|
||||||
case models.IsErrKeyAlreadyExist(err):
|
case models.IsErrKeyAlreadyExist(err):
|
||||||
ctx.Error(422, "", "Key content has been used as non-deploy key")
|
ctx.Error(422, "", "Key content has been used as non-deploy key")
|
||||||
case models.IsErrKeyNameAlreadyUsed(err):
|
case models.IsErrKeyNameAlreadyUsed(err):
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func Metrics(ctx *context.Context) {
|
|||||||
promhttp.Handler().ServeHTTP(ctx.Resp, ctx.Req.Request)
|
promhttp.Handler().ServeHTTP(ctx.Resp, ctx.Req.Request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
header := ctx.Header().Get("Authorization")
|
header := ctx.Req.Header.Get("Authorization")
|
||||||
if header == "" {
|
if header == "" {
|
||||||
ctx.Error(401)
|
ctx.Error(401)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
|||||||
m.Post("/repositories/:repoid/keys/:keyid/update", UpdateDeployKey)
|
m.Post("/repositories/:repoid/keys/:keyid/update", UpdateDeployKey)
|
||||||
m.Get("/repositories/:repoid/user/:userid/checkunituser", CheckUnitUser)
|
m.Get("/repositories/:repoid/user/:userid/checkunituser", CheckUnitUser)
|
||||||
m.Get("/repositories/:repoid/has-keys/:keyid", HasDeployKey)
|
m.Get("/repositories/:repoid/has-keys/:keyid", HasDeployKey)
|
||||||
|
m.Get("/repositories/:repoid/keys/:keyid", GetDeployKey)
|
||||||
m.Get("/repositories/:repoid/wiki/init", InitWiki)
|
m.Get("/repositories/:repoid/wiki/init", InitWiki)
|
||||||
m.Post("/push/update", PushUpdate)
|
m.Post("/push/update", PushUpdate)
|
||||||
m.Get("/protectedbranch/:pbid/:userid", CanUserPush)
|
m.Get("/protectedbranch/:pbid/:userid", CanUserPush)
|
||||||
|
|||||||
@@ -72,6 +72,24 @@ func GetUserByKeyID(ctx *macaron.Context) {
|
|||||||
ctx.JSON(200, user)
|
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
|
//HasDeployKey chainload to models.HasDeployKey
|
||||||
func HasDeployKey(ctx *macaron.Context) {
|
func HasDeployKey(ctx *macaron.Context) {
|
||||||
repoID := ctx.ParamsInt64(":repoid")
|
repoID := ctx.ParamsInt64(":repoid")
|
||||||
|
|||||||
@@ -113,24 +113,24 @@ func HTTP(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
authUser, err = models.UserSignIn(authUsername, authPasswd)
|
// Check if username or password is a token
|
||||||
if err != nil {
|
|
||||||
if !models.IsErrUserNotExist(err) {
|
|
||||||
ctx.ServerError("UserSignIn error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if authUser == nil {
|
|
||||||
isUsernameToken := len(authPasswd) == 0 || authPasswd == "x-oauth-basic"
|
isUsernameToken := len(authPasswd) == 0 || authPasswd == "x-oauth-basic"
|
||||||
|
|
||||||
// Assume username is token
|
// Assume username is token
|
||||||
authToken := authUsername
|
authToken := authUsername
|
||||||
|
|
||||||
if !isUsernameToken {
|
if !isUsernameToken {
|
||||||
// Assume password is token
|
// Assume password is token
|
||||||
authToken = authPasswd
|
authToken = authPasswd
|
||||||
|
}
|
||||||
|
// Assume password is a token.
|
||||||
|
token, err := models.GetAccessTokenBySHA(authToken)
|
||||||
|
if err == nil {
|
||||||
|
if isUsernameToken {
|
||||||
|
authUser, err = models.GetUserByID(token.UID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetUserByID", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
authUser, err = models.GetUserByName(authUsername)
|
authUser, err = models.GetUserByName(authUsername)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrUserNotExist(err) {
|
if models.IsErrUserNotExist(err) {
|
||||||
@@ -140,37 +140,37 @@ func HTTP(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
if authUser.ID != token.UID {
|
||||||
|
|
||||||
// Assume password is a token.
|
|
||||||
token, err := models.GetAccessTokenBySHA(authToken)
|
|
||||||
if err != nil {
|
|
||||||
if models.IsErrAccessTokenNotExist(err) || models.IsErrAccessTokenEmpty(err) {
|
|
||||||
ctx.HandleText(http.StatusUnauthorized, "invalid credentials")
|
|
||||||
} else {
|
|
||||||
ctx.ServerError("GetAccessTokenBySha", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if isUsernameToken {
|
|
||||||
authUser, err = models.GetUserByID(token.UID)
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("GetUserByID", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if authUser.ID != token.UID {
|
|
||||||
ctx.HandleText(http.StatusUnauthorized, "invalid credentials")
|
ctx.HandleText(http.StatusUnauthorized, "invalid credentials")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
token.UpdatedUnix = util.TimeStampNow()
|
token.UpdatedUnix = util.TimeStampNow()
|
||||||
if err = models.UpdateAccessToken(token); err != nil {
|
if err = models.UpdateAccessToken(token); err != nil {
|
||||||
ctx.ServerError("UpdateAccessToken", err)
|
ctx.ServerError("UpdateAccessToken", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, err = models.GetTwoFactorByUID(authUser.ID)
|
if !models.IsErrAccessTokenNotExist(err) && !models.IsErrAccessTokenEmpty(err) {
|
||||||
|
log.Error(4, "GetAccessTokenBySha: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if authUser == nil {
|
||||||
|
// Check username and password
|
||||||
|
authUser, err = models.UserSignIn(authUsername, authPasswd)
|
||||||
|
if err != nil {
|
||||||
|
if !models.IsErrUserNotExist(err) {
|
||||||
|
ctx.ServerError("UserSignIn error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if authUser == nil {
|
||||||
|
ctx.HandleText(http.StatusUnauthorized, "invalid credentials")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = models.GetTwoFactorByUID(authUser.ID)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO: This response should be changed to "invalid credentials" for security reasons once the expectation behind it (creating an app token to authenticate) is properly documented
|
// TODO: This response should be changed to "invalid credentials" for security reasons once the expectation behind it (creating an app token to authenticate) is properly documented
|
||||||
ctx.HandleText(http.StatusUnauthorized, "Users with two-factor authentication enabled cannot perform HTTP/HTTPS operations via plain username and password. Please create and use a personal access token on the user settings page")
|
ctx.HandleText(http.StatusUnauthorized, "Users with two-factor authentication enabled cannot perform HTTP/HTTPS operations via plain username and password. Please create and use a personal access token on the user settings page")
|
||||||
|
|||||||
@@ -581,6 +581,9 @@ func DeployKeysPost(ctx *context.Context, form auth.AddKeyForm) {
|
|||||||
case models.IsErrDeployKeyAlreadyExist(err):
|
case models.IsErrDeployKeyAlreadyExist(err):
|
||||||
ctx.Data["Err_Content"] = true
|
ctx.Data["Err_Content"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.settings.key_been_used"), tplDeployKeys, &form)
|
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):
|
case models.IsErrKeyNameAlreadyUsed(err):
|
||||||
ctx.Data["Err_Title"] = true
|
ctx.Data["Err_Title"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.settings.key_name_used"), tplDeployKeys, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.settings.key_name_used"), tplDeployKeys, &form)
|
||||||
|
|||||||
@@ -34,11 +34,16 @@ func Security(ctx *context.Context) {
|
|||||||
|
|
||||||
// DeleteAccountLink delete a single account link
|
// DeleteAccountLink delete a single account link
|
||||||
func DeleteAccountLink(ctx *context.Context) {
|
func DeleteAccountLink(ctx *context.Context) {
|
||||||
if _, err := models.RemoveAccountLink(ctx.User, ctx.QueryInt64("loginSourceID")); err != nil {
|
id := ctx.QueryInt64("id")
|
||||||
|
if id <= 0 {
|
||||||
|
ctx.Flash.Error("Account link id is not given")
|
||||||
|
} else {
|
||||||
|
if _, err := models.RemoveAccountLink(ctx.User, id); err != nil {
|
||||||
ctx.Flash.Error("RemoveAccountLink: " + err.Error())
|
ctx.Flash.Error("RemoveAccountLink: " + err.Error())
|
||||||
} else {
|
} else {
|
||||||
ctx.Flash.Success(ctx.Tr("settings.remove_account_link_success"))
|
ctx.Flash.Success(ctx.Tr("settings.remove_account_link_success"))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx.JSON(200, map[string]interface{}{
|
ctx.JSON(200, map[string]interface{}{
|
||||||
"redirect": setting.AppSubURL + "/user/settings/security",
|
"redirect": setting.AppSubURL + "/user/settings/security",
|
||||||
|
|||||||
@@ -44,12 +44,14 @@
|
|||||||
<div v-show="tab === 'repos'" class="ui tab active list dashboard-repos">
|
<div v-show="tab === 'repos'" class="ui tab active list dashboard-repos">
|
||||||
<h4 class="ui top attached header">
|
<h4 class="ui top attached header">
|
||||||
{{.i18n.Tr "home.my_repos"}} <span class="ui grey label">${reposTotalCount}</span>
|
{{.i18n.Tr "home.my_repos"}} <span class="ui grey label">${reposTotalCount}</span>
|
||||||
|
{{if or (not .ContextUser.IsOrganization) .IsOrganizationOwner}}
|
||||||
<div class="ui right">
|
<div class="ui right">
|
||||||
<a class="poping up" :href="suburl + '/repo/create'" data-content="{{.i18n.Tr "new_repo"}}" data-variation="tiny inverted" data-position="left center">
|
<a class="poping up" :href="suburl + '/repo/create{{if .ContextUser.IsOrganization}}?org={{.ContextUser.ID}}{{end}}'" data-content="{{.i18n.Tr "new_repo"}}" data-variation="tiny inverted" data-position="left center">
|
||||||
<i class="plus icon"></i>
|
<i class="plus icon"></i>
|
||||||
<span class="sr-only">{{.i18n.Tr "new_repo"}}</span>
|
<span class="sr-only">{{.i18n.Tr "new_repo"}}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
{{end}}
|
||||||
</h4>
|
</h4>
|
||||||
<div class="ui attached secondary segment repos-search">
|
<div class="ui attached secondary segment repos-search">
|
||||||
<div class="ui fluid icon input" :class="{loading: isLoading}">
|
<div class="ui fluid icon input" :class="{loading: isLoading}">
|
||||||
|
|||||||
Reference in New Issue
Block a user