Unknwon 9 years ago
parent
commit
f455125d4d
11 changed files with 220 additions and 39 deletions
  1. 1 1
      README.md
  2. 7 0
      cmd/web.go
  3. 1 1
      gogs.go
  4. 28 0
      models/error.go
  5. 51 25
      models/publickey.go
  6. 2 2
      models/webhook.go
  7. 7 3
      routers/api/v1/repo_hooks.go
  8. 115 0
      routers/api/v1/repo_keys.go
  9. 6 5
      routers/repo/setting.go
  10. 1 1
      routers/user/setting.go
  11. 1 1
      templates/.VERSION

+ 1 - 1
README.md

@@ -5,7 +5,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra
 
 ![](public/img/gogs-large-resize.png)
 
-##### Current version: 0.7.17 Beta
+##### Current version: 0.7.18 Beta
 
 <table>
     <tr>

+ 7 - 0
cmd/web.go

@@ -238,6 +238,13 @@ func runWeb(ctx *cli.Context) {
 					m.Patch("/hooks/:id:int", bind(api.EditHookOption{}), v1.EditRepoHook)
 					m.Get("/raw/*", middleware.RepoRef(), v1.GetRepoRawFile)
 					m.Get("/archive/*", v1.GetRepoArchive)
+
+					m.Group("/keys", func() {
+						m.Combo("").Get(v1.ListRepoDeployKeys).
+							Post(bind(api.CreateDeployKeyOption{}), v1.CreateRepoDeployKey)
+						m.Combo("/:id").Get(v1.GetRepoDeployKey).
+							Delete(v1.DeleteRepoDeploykey)
+					})
 				}, middleware.ApiRepoAssignment())
 			}, middleware.ApiReqToken())
 

+ 1 - 1
gogs.go

@@ -17,7 +17,7 @@ import (
 	"github.com/gogits/gogs/modules/setting"
 )
 
-const APP_VER = "0.7.17.1118 Beta"
+const APP_VER = "0.7.18.1118 Beta"
 
 func init() {
 	runtime.GOMAXPROCS(runtime.NumCPU())

+ 28 - 0
models/error.go

@@ -114,6 +114,19 @@ func (err ErrUserHasOrgs) Error() string {
 //  |____|   |____/|___  /____/__|\___  > |____|__ \___  > ____|
 //                     \/             \/          \/   \/\/
 
+type ErrKeyUnableVerify struct {
+	Result string
+}
+
+func IsErrKeyUnableVerify(err error) bool {
+	_, ok := err.(ErrKeyUnableVerify)
+	return ok
+}
+
+func (err ErrKeyUnableVerify) Error() string {
+	return fmt.Sprintf("Unable to verify key content [result: %s]", err.Result)
+}
+
 type ErrKeyNotExist struct {
 	ID int64
 }
@@ -155,6 +168,21 @@ func (err ErrKeyNameAlreadyUsed) Error() string {
 	return fmt.Sprintf("public key already exists [owner_id: %d, name: %s]", err.OwnerID, err.Name)
 }
 
+type ErrDeployKeyNotExist struct {
+	ID     int64
+	KeyID  int64
+	RepoID int64
+}
+
+func IsErrDeployKeyNotExist(err error) bool {
+	_, ok := err.(ErrDeployKeyNotExist)
+	return ok
+}
+
+func (err ErrDeployKeyNotExist) Error() string {
+	return fmt.Sprintf("Deploy key does not exist [id: %d, key_id: %d, repo_id: %d]", err.ID, err.KeyID, err.RepoID)
+}
+
 type ErrDeployKeyAlreadyExist struct {
 	KeyID  int64
 	RepoID int64

+ 51 - 25
models/publickey.go

@@ -32,10 +32,6 @@ const (
 	_TPL_PUBLICK_KEY = `command="%s serv key-%d --config='%s'",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s` + "\n"
 )
 
-var (
-	ErrKeyUnableVerify = errors.New("Unable to verify public key")
-)
-
 var sshOpLocker = sync.Mutex{}
 var SSHPath string // SSH directory.
 
@@ -211,7 +207,7 @@ func CheckPublicKeyString(content string) (_ string, err error) {
 
 	sshKeygenOutput := strings.Split(stdout, " ")
 	if len(sshKeygenOutput) < 4 {
-		return content, ErrKeyUnableVerify
+		return content, ErrKeyUnableVerify{stdout}
 	}
 
 	// Check if key type and key size match.
@@ -523,6 +519,7 @@ type DeployKey struct {
 	RepoID            int64 `xorm:"UNIQUE(s) INDEX"`
 	Name              string
 	Fingerprint       string
+	Content           string    `xorm:"-"`
 	Created           time.Time `xorm:"CREATED"`
 	Updated           time.Time // Note: Updated must below Created for AfterSet.
 	HasRecentActivity bool      `xorm:"-"`
@@ -537,6 +534,16 @@ func (k *DeployKey) AfterSet(colName string, _ xorm.Cell) {
 	}
 }
 
+// GetContent gets associated public key content.
+func (k *DeployKey) GetContent() error {
+	pkey, err := GetPublicKeyByID(k.KeyID)
+	if err != nil {
+		return err
+	}
+	k.Content = pkey.Content
+	return nil
+}
+
 func checkDeployKey(e Engine, keyID, repoID int64, name string) error {
 	// Note: We want error detail, not just true or false here.
 	has, err := e.Where("key_id=? AND repo_id=?", keyID, repoID).Get(new(DeployKey))
@@ -557,18 +564,19 @@ func checkDeployKey(e Engine, keyID, repoID int64, name string) error {
 }
 
 // addDeployKey adds new key-repo relation.
-func addDeployKey(e *xorm.Session, keyID, repoID int64, name, fingerprint string) (err error) {
-	if err = checkDeployKey(e, keyID, repoID, name); err != nil {
-		return err
+func addDeployKey(e *xorm.Session, keyID, repoID int64, name, fingerprint string) (*DeployKey, error) {
+	if err := checkDeployKey(e, keyID, repoID, name); err != nil {
+		return nil, err
 	}
 
-	_, err = e.Insert(&DeployKey{
+	key := &DeployKey{
 		KeyID:       keyID,
 		RepoID:      repoID,
 		Name:        name,
 		Fingerprint: fingerprint,
-	})
-	return err
+	}
+	_, err := e.Insert(key)
+	return key, err
 }
 
 // HasDeployKey returns true if public key is a deploy key of given repository.
@@ -578,39 +586,52 @@ func HasDeployKey(keyID, repoID int64) bool {
 }
 
 // AddDeployKey add new deploy key to database and authorized_keys file.
-func AddDeployKey(repoID int64, name, content string) (err error) {
-	if err = checkKeyContent(content); err != nil {
-		return err
+func AddDeployKey(repoID int64, name, content string) (*DeployKey, error) {
+	if err := checkKeyContent(content); err != nil {
+		return nil, err
 	}
 
-	key := &PublicKey{
+	pkey := &PublicKey{
 		Content: content,
 		Mode:    ACCESS_MODE_READ,
 		Type:    KEY_TYPE_DEPLOY,
 	}
-	has, err := x.Get(key)
+	has, err := x.Get(pkey)
 	if err != nil {
-		return err
+		return nil, err
 	}
 
 	sess := x.NewSession()
 	defer sessionRelease(sess)
 	if err = sess.Begin(); err != nil {
-		return err
+		return nil, err
 	}
 
 	// First time use this deploy key.
 	if !has {
-		if err = addKey(sess, key); err != nil {
-			return nil
+		if err = addKey(sess, pkey); err != nil {
+			return nil, fmt.Errorf("addKey: %v", err)
 		}
 	}
 
-	if err = addDeployKey(sess, key.ID, repoID, name, key.Fingerprint); err != nil {
-		return err
+	key, err := addDeployKey(sess, pkey.ID, repoID, name, pkey.Fingerprint)
+	if err != nil {
+		return nil, fmt.Errorf("addDeployKey: %v", err)
 	}
 
-	return sess.Commit()
+	return key, sess.Commit()
+}
+
+// GetDeployKeyByID returns deploy key by given ID.
+func GetDeployKeyByID(id int64) (*DeployKey, error) {
+	key := new(DeployKey)
+	has, err := x.Id(id).Get(key)
+	if err != nil {
+		return nil, err
+	} else if !has {
+		return nil, ErrDeployKeyNotExist{id, 0, 0}
+	}
+	return key, nil
 }
 
 // GetDeployKeyByRepo returns deploy key by given public key ID and repository ID.
@@ -619,8 +640,13 @@ func GetDeployKeyByRepo(keyID, repoID int64) (*DeployKey, error) {
 		KeyID:  keyID,
 		RepoID: repoID,
 	}
-	_, err := x.Get(key)
-	return key, err
+	has, err := x.Get(key)
+	if err != nil {
+		return nil, err
+	} else if !has {
+		return nil, ErrDeployKeyNotExist{0, keyID, repoID}
+	}
+	return key, nil
 }
 
 // UpdateDeployKey updates deploy key information.

+ 2 - 2
models/webhook.go

@@ -178,8 +178,8 @@ func GetActiveWebhooksByRepoID(repoID int64) (ws []*Webhook, err error) {
 	return ws, err
 }
 
-// GetWebhooksByRepoId returns all webhooks of repository.
-func GetWebhooksByRepoId(repoID int64) (ws []*Webhook, err error) {
+// GetWebhooksByRepoID returns all webhooks of repository.
+func GetWebhooksByRepoID(repoID int64) (ws []*Webhook, err error) {
 	err = x.Find(&ws, &Webhook{RepoID: repoID})
 	return ws, err
 }

+ 7 - 3
routers/api/v1/repo_hooks.go

@@ -44,9 +44,9 @@ func ToApiHook(repoLink string, w *models.Webhook) *api.Hook {
 
 // https://github.com/gogits/go-gogs-client/wiki/Repositories#list-hooks
 func ListRepoHooks(ctx *middleware.Context) {
-	hooks, err := models.GetWebhooksByRepoId(ctx.Repo.Repository.ID)
+	hooks, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID)
 	if err != nil {
-		ctx.APIError(500, "GetWebhooksByRepoId", err)
+		ctx.APIError(500, "GetWebhooksByRepoID", err)
 		return
 	}
 
@@ -127,7 +127,11 @@ func CreateRepoHook(ctx *middleware.Context, form api.CreateHookOption) {
 func EditRepoHook(ctx *middleware.Context, form api.EditHookOption) {
 	w, err := models.GetWebhookByID(ctx.ParamsInt64(":id"))
 	if err != nil {
-		ctx.APIError(500, "GetWebhookById", err)
+		if models.IsErrWebhookNotExist(err) {
+			ctx.Error(404)
+		} else {
+			ctx.APIError(500, "GetWebhookById", err)
+		}
 		return
 	}
 

+ 115 - 0
routers/api/v1/repo_keys.go

@@ -0,0 +1,115 @@
+// Copyright 2015 The Gogs 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 v1
+
+import (
+	"fmt"
+
+	"github.com/Unknwon/com"
+
+	api "github.com/gogits/go-gogs-client"
+
+	"github.com/gogits/gogs/models"
+	"github.com/gogits/gogs/modules/middleware"
+	"github.com/gogits/gogs/modules/setting"
+)
+
+func ToApiDeployKey(apiLink string, key *models.DeployKey) *api.DeployKey {
+	return &api.DeployKey{
+		ID:       key.ID,
+		Key:      key.Content,
+		URL:      apiLink + com.ToStr(key.ID),
+		Title:    key.Name,
+		Created:  key.Created,
+		ReadOnly: true, // All deploy keys are read-only.
+	}
+}
+
+func composeDeployKeysAPILink(repoPath string) string {
+	return setting.AppUrl + "api/v1/repos/" + repoPath + "/keys/"
+}
+
+// https://github.com/gogits/go-gogs-client/wiki/Repositories---Deploy-Keys#list-deploy-keys
+func ListRepoDeployKeys(ctx *middleware.Context) {
+	keys, err := models.ListDeployKeys(ctx.Repo.Repository.ID)
+	if err != nil {
+		ctx.Handle(500, "ListDeployKeys", err)
+		return
+	}
+
+	apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
+	apiKeys := make([]*api.DeployKey, len(keys))
+	for i := range keys {
+		if err = keys[i].GetContent(); err != nil {
+			ctx.APIError(500, "GetContent", err)
+			return
+		}
+		apiKeys[i] = ToApiDeployKey(apiLink, keys[i])
+	}
+
+	ctx.JSON(200, &apiKeys)
+}
+
+// https://github.com/gogits/go-gogs-client/wiki/Repositories---Deploy-Keys#get-a-deploy-key
+func GetRepoDeployKey(ctx *middleware.Context) {
+	key, err := models.GetDeployKeyByID(ctx.ParamsInt64(":id"))
+	if err != nil {
+		if models.IsErrDeployKeyNotExist(err) {
+			ctx.Error(404)
+		} else {
+			ctx.Handle(500, "GetDeployKeyByID", err)
+		}
+		return
+	}
+
+	if err = key.GetContent(); err != nil {
+		ctx.APIError(500, "GetContent", err)
+		return
+	}
+
+	apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
+	ctx.JSON(200, ToApiDeployKey(apiLink, key))
+}
+
+// https://github.com/gogits/go-gogs-client/wiki/Repositories---Deploy-Keys#add-a-new-deploy-key
+func CreateRepoDeployKey(ctx *middleware.Context, form api.CreateDeployKeyOption) {
+	content, err := models.CheckPublicKeyString(form.Key)
+	if err != nil {
+		if models.IsErrKeyUnableVerify(err) {
+			ctx.APIError(422, "", "Unable to verify key content")
+		} else {
+			ctx.APIError(422, "", fmt.Errorf("Invalid key content: %v", err))
+		}
+		return
+	}
+
+	key, err := models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content)
+	if err != nil {
+		ctx.Data["HasError"] = true
+		switch {
+		case models.IsErrKeyAlreadyExist(err):
+			ctx.APIError(422, "", "Key content has been used as non-deploy key")
+		case models.IsErrKeyNameAlreadyUsed(err):
+			ctx.APIError(422, "", "Key title has been used")
+		default:
+			ctx.APIError(500, "AddDeployKey", err)
+		}
+		return
+	}
+
+	key.Content = content
+	apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
+	ctx.JSON(201, ToApiDeployKey(apiLink, key))
+}
+
+// https://github.com/gogits/go-gogs-client/wiki/Repositories---Deploy-Keys#remove-a-deploy-key
+func DeleteRepoDeploykey(ctx *middleware.Context) {
+	if err := models.DeleteDeployKey(ctx.ParamsInt64(":id")); err != nil {
+		ctx.APIError(500, "DeleteDeployKey", err)
+		return
+	}
+
+	ctx.Status(204)
+}

+ 6 - 5
routers/repo/setting.go

@@ -259,9 +259,9 @@ func Webhooks(ctx *middleware.Context) {
 	ctx.Data["BaseLink"] = ctx.Repo.RepoLink
 	ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://github.com/gogits/go-gogs-client/wiki/Repositories---Webhooks")
 
-	ws, err := models.GetWebhooksByRepoId(ctx.Repo.Repository.ID)
+	ws, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID)
 	if err != nil {
-		ctx.Handle(500, "GetWebhooksByRepoId", err)
+		ctx.Handle(500, "GetWebhooksByRepoID", err)
 		return
 	}
 	ctx.Data["Webhooks"] = ws
@@ -684,7 +684,7 @@ func DeployKeysPost(ctx *middleware.Context, form auth.AddSSHKeyForm) {
 
 	content, err := models.CheckPublicKeyString(form.Content)
 	if err != nil {
-		if err == models.ErrKeyUnableVerify {
+		if models.IsErrKeyUnableVerify(err) {
 			ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key"))
 		} else {
 			ctx.Data["HasError"] = true
@@ -695,7 +695,8 @@ func DeployKeysPost(ctx *middleware.Context, form auth.AddSSHKeyForm) {
 		}
 	}
 
-	if err = models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content); err != nil {
+	key, err := models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content)
+	if err != nil {
 		ctx.Data["HasError"] = true
 		switch {
 		case models.IsErrKeyAlreadyExist(err):
@@ -711,7 +712,7 @@ func DeployKeysPost(ctx *middleware.Context, form auth.AddSSHKeyForm) {
 	}
 
 	log.Trace("Deploy key added: %d", ctx.Repo.Repository.ID)
-	ctx.Flash.Success(ctx.Tr("repo.settings.add_key_success", form.Title))
+	ctx.Flash.Success(ctx.Tr("repo.settings.add_key_success", key.Name))
 	ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys")
 }
 

+ 1 - 1
routers/user/setting.go

@@ -286,7 +286,7 @@ func SettingsSSHKeysPost(ctx *middleware.Context, form auth.AddSSHKeyForm) {
 
 	content, err := models.CheckPublicKeyString(form.Content)
 	if err != nil {
-		if err == models.ErrKeyUnableVerify {
+		if models.IsErrKeyUnableVerify(err) {
 			ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key"))
 		} else {
 			ctx.Flash.Error(ctx.Tr("form.invalid_ssh_key", err.Error()))

+ 1 - 1
templates/.VERSION

@@ -1 +1 @@
-0.7.17.1118 Beta
+0.7.18.1118 Beta