forgejo/routers/api/v1/repo/key.go
zeripath 00533d3870 Keys API changes (#4960)
* Add private information to the deploy keys api

This commit adds more information to the deploy keys to allow for back
reference in to the main keys list. It also adds information about the
repository that the key is referring to.

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Add private information to the user keys API

This adjusts the keys API to give out private information to user keys if
the current user is the owner or an admin.

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Add ability to search keys by fingerprint

This commit adds the functionality to search ssh-keys by fingerprint of
the ssh-key. Deploy keys per repository can also be searched. There is
no current clear API point to allow search of all deploy keys by
fingerprint or keyID.

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Add integration test
2018-10-31 23:40:49 -04:00

250 lines
6.8 KiB
Go

// 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 repo
import (
"fmt"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/routers/api/v1/convert"
api "code.gitea.io/sdk/gitea"
)
// appendPrivateInformation appends the owner and key type information to api.PublicKey
func appendPrivateInformation(apiKey *api.DeployKey, key *models.DeployKey, repository *models.Repository) (*api.DeployKey, error) {
apiKey.ReadOnly = key.Mode == models.AccessModeRead
if repository.ID == key.RepoID {
apiKey.Repository = repository.APIFormat(key.Mode)
} else {
repo, err := models.GetRepositoryByID(key.RepoID)
if err != nil {
return apiKey, err
}
apiKey.Repository = repo.APIFormat(key.Mode)
}
return apiKey, nil
}
func composeDeployKeysAPILink(repoPath string) string {
return setting.AppURL + "api/v1/repos/" + repoPath + "/keys/"
}
// ListDeployKeys list all the deploy keys of a repository
func ListDeployKeys(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/keys repository repoListKeys
// ---
// summary: List a repository's keys
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: key_id
// in: query
// description: the key_id to search for
// type: integer
// - name: fingerprint
// in: query
// description: fingerprint of the key
// type: string
// responses:
// "200":
// "$ref": "#/responses/DeployKeyList"
var keys []*models.DeployKey
var err error
fingerprint := ctx.Query("fingerprint")
keyID := ctx.QueryInt64("key_id")
if fingerprint != "" || keyID != 0 {
keys, err = models.SearchDeployKeys(ctx.Repo.Repository.ID, keyID, fingerprint)
} else {
keys, err = models.ListDeployKeys(ctx.Repo.Repository.ID)
}
if err != nil {
ctx.Error(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.Error(500, "GetContent", err)
return
}
apiKeys[i] = convert.ToDeployKey(apiLink, keys[i])
if ctx.User.IsAdmin || ((ctx.Repo.Repository.ID == keys[i].RepoID) && (ctx.User.ID == ctx.Repo.Owner.ID)) {
apiKeys[i], _ = appendPrivateInformation(apiKeys[i], keys[i], ctx.Repo.Repository)
}
}
ctx.JSON(200, &apiKeys)
}
// GetDeployKey get a deploy key by id
func GetDeployKey(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/keys/{id} repository repoGetKey
// ---
// summary: Get a repository's key by id
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: id
// in: path
// description: id of the key to get
// type: integer
// format: int64
// required: true
// responses:
// "200":
// "$ref": "#/responses/DeployKey"
key, err := models.GetDeployKeyByID(ctx.ParamsInt64(":id"))
if err != nil {
if models.IsErrDeployKeyNotExist(err) {
ctx.Status(404)
} else {
ctx.Error(500, "GetDeployKeyByID", err)
}
return
}
if err = key.GetContent(); err != nil {
ctx.Error(500, "GetContent", err)
return
}
apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
apiKey := convert.ToDeployKey(apiLink, key)
if ctx.User.IsAdmin || ((ctx.Repo.Repository.ID == key.RepoID) && (ctx.User.ID == ctx.Repo.Owner.ID)) {
apiKey, _ = appendPrivateInformation(apiKey, key, ctx.Repo.Repository)
}
ctx.JSON(200, apiKey)
}
// HandleCheckKeyStringError handle check key error
func HandleCheckKeyStringError(ctx *context.APIContext, err error) {
if models.IsErrSSHDisabled(err) {
ctx.Error(422, "", "SSH is disabled")
} else if models.IsErrKeyUnableVerify(err) {
ctx.Error(422, "", "Unable to verify key content")
} else {
ctx.Error(422, "", fmt.Errorf("Invalid key content: %v", err))
}
}
// HandleAddKeyError handle add key error
func HandleAddKeyError(ctx *context.APIContext, err error) {
switch {
case models.IsErrKeyAlreadyExist(err):
ctx.Error(422, "", "Key content has been used as non-deploy key")
case models.IsErrKeyNameAlreadyUsed(err):
ctx.Error(422, "", "Key title has been used")
default:
ctx.Error(500, "AddKey", err)
}
}
// CreateDeployKey create deploy key for a repository
func CreateDeployKey(ctx *context.APIContext, form api.CreateKeyOption) {
// swagger:operation POST /repos/{owner}/{repo}/keys repository repoCreateKey
// ---
// summary: Add a key to a repository
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/CreateKeyOption"
// responses:
// "201":
// "$ref": "#/responses/DeployKey"
content, err := models.CheckPublicKeyString(form.Key)
if err != nil {
HandleCheckKeyStringError(ctx, err)
return
}
key, err := models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content, form.ReadOnly)
if err != nil {
HandleAddKeyError(ctx, err)
return
}
key.Content = content
apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
ctx.JSON(201, convert.ToDeployKey(apiLink, key))
}
// DeleteDeploykey delete deploy key for a repository
func DeleteDeploykey(ctx *context.APIContext) {
// swagger:operation DELETE /repos/{owner}/{repo}/keys/{id} repository repoDeleteKey
// ---
// summary: Delete a key from a repository
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: id
// in: path
// description: id of the key to delete
// type: integer
// format: int64
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
if err := models.DeleteDeployKey(ctx.User, ctx.ParamsInt64(":id")); err != nil {
if models.IsErrKeyAccessDenied(err) {
ctx.Error(403, "", "You do not have access to this key")
} else {
ctx.Error(500, "DeleteDeployKey", err)
}
return
}
ctx.Status(204)
}