import (
"fmt"
"net/http"
+ "net/url"
"testing"
+ "github.com/stretchr/testify/assert"
+
"code.gitea.io/gitea/models"
api "code.gitea.io/sdk/gitea"
)
Mode: models.AccessModeWrite,
})
}
+
+func TestCreateUserKey(t *testing.T) {
+ prepareTestEnv(t)
+ user := models.AssertExistsAndLoadBean(t, &models.User{Name: "user1"}).(*models.User)
+
+ session := loginUser(t, "user1")
+ token := url.QueryEscape(getTokenForLoggedInUser(t, session))
+ keysURL := fmt.Sprintf("/api/v1/user/keys?token=%s", token)
+ keyType := "ssh-rsa"
+ keyContent := "AAAAB3NzaC1yc2EAAAADAQABAAABAQCyTiPTeHJl6Gs5D1FyHT0qTWpVkAy9+LIKjctQXklrePTvUNVrSpt4r2exFYXNMPeA8V0zCrc3Kzs1SZw3jWkG3i53te9onCp85DqyatxOD2pyZ30/gPn1ZUg40WowlFM8gsUFMZqaH7ax6d8nsBKW7N/cRyqesiOQEV9up3tnKjIB8XMTVvC5X4rBWgywz7AFxSv8mmaTHnUgVW4LgMPwnTWo0pxtiIWbeMLyrEE4hIM74gSwp6CRQYo6xnG3fn4yWkcK2X2mT9adQ241IDdwpENJHcry/T6AJ8dNXduEZ67egnk+rVlQ2HM4LpymAv9DAAFFeaQK0hT+3aMDoumV"
+ rawKeyBody := api.CreateKeyOption{
+ Title: "test-key",
+ Key: keyType + " " + keyContent,
+ }
+ req := NewRequestWithJSON(t, "POST", keysURL, rawKeyBody)
+ resp := session.MakeRequest(t, req, http.StatusCreated)
+
+ var newPublicKey api.PublicKey
+ DecodeJSON(t, resp, &newPublicKey)
+ models.AssertExistsAndLoadBean(t, &models.PublicKey{
+ ID: newPublicKey.ID,
+ OwnerID: user.ID,
+ Name: rawKeyBody.Title,
+ Content: rawKeyBody.Key,
+ Mode: models.AccessModeWrite,
+ })
+
+ // Search by fingerprint
+ fingerprintURL := fmt.Sprintf("/api/v1/user/keys?token=%s&fingerprint=%s", token, newPublicKey.Fingerprint)
+
+ req = NewRequest(t, "GET", fingerprintURL)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+
+ var fingerprintPublicKeys []api.PublicKey
+ DecodeJSON(t, resp, &fingerprintPublicKeys)
+ assert.Equal(t, newPublicKey.Fingerprint, fingerprintPublicKeys[0].Fingerprint)
+ assert.Equal(t, newPublicKey.ID, fingerprintPublicKeys[0].ID)
+ assert.Equal(t, user.ID, fingerprintPublicKeys[0].Owner.ID)
+
+ fingerprintURL = fmt.Sprintf("/api/v1/users/%s/keys?token=%s&fingerprint=%s", user.Name, token, newPublicKey.Fingerprint)
+
+ req = NewRequest(t, "GET", fingerprintURL)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+
+ DecodeJSON(t, resp, &fingerprintPublicKeys)
+ assert.Equal(t, newPublicKey.Fingerprint, fingerprintPublicKeys[0].Fingerprint)
+ assert.Equal(t, newPublicKey.ID, fingerprintPublicKeys[0].ID)
+ assert.Equal(t, user.ID, fingerprintPublicKeys[0].Owner.ID)
+
+ // Fail search by fingerprint
+ fingerprintURL = fmt.Sprintf("/api/v1/user/keys?token=%s&fingerprint=%sA", token, newPublicKey.Fingerprint)
+
+ req = NewRequest(t, "GET", fingerprintURL)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+
+ DecodeJSON(t, resp, &fingerprintPublicKeys)
+ assert.Len(t, fingerprintPublicKeys, 0)
+
+ // Fail searching for wrong users key
+ fingerprintURL = fmt.Sprintf("/api/v1/users/%s/keys?token=%s&fingerprint=%s", "user2", token, newPublicKey.Fingerprint)
+ req = NewRequest(t, "GET", fingerprintURL)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+
+ DecodeJSON(t, resp, &fingerprintPublicKeys)
+ assert.Len(t, fingerprintPublicKeys, 0)
+
+ // Now login as user 2
+ session2 := loginUser(t, "user2")
+ token2 := url.QueryEscape(getTokenForLoggedInUser(t, session2))
+
+ // Should find key even though not ours, but we shouldn't know whose it is
+ fingerprintURL = fmt.Sprintf("/api/v1/user/keys?token=%s&fingerprint=%s", token2, newPublicKey.Fingerprint)
+ req = NewRequest(t, "GET", fingerprintURL)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+
+ DecodeJSON(t, resp, &fingerprintPublicKeys)
+ assert.Equal(t, newPublicKey.Fingerprint, fingerprintPublicKeys[0].Fingerprint)
+ assert.Equal(t, newPublicKey.ID, fingerprintPublicKeys[0].ID)
+ assert.Nil(t, fingerprintPublicKeys[0].Owner)
+
+ // Should find key even though not ours, but we shouldn't know whose it is
+ fingerprintURL = fmt.Sprintf("/api/v1/users/%s/keys?token=%s&fingerprint=%s", user.Name, token2, newPublicKey.Fingerprint)
+
+ req = NewRequest(t, "GET", fingerprintURL)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+
+ DecodeJSON(t, resp, &fingerprintPublicKeys)
+ assert.Equal(t, newPublicKey.Fingerprint, fingerprintPublicKeys[0].Fingerprint)
+ assert.Equal(t, newPublicKey.ID, fingerprintPublicKeys[0].ID)
+ assert.Nil(t, fingerprintPublicKeys[0].Owner)
+
+ // Fail when searching for key if it is not ours
+ fingerprintURL = fmt.Sprintf("/api/v1/users/%s/keys?token=%s&fingerprint=%s", "user2", token2, newPublicKey.Fingerprint)
+ req = NewRequest(t, "GET", fingerprintURL)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+
+ DecodeJSON(t, resp, &fingerprintPublicKeys)
+ assert.Len(t, fingerprintPublicKeys, 0)
+}
"code.gitea.io/gitea/modules/util"
"github.com/Unknwon/com"
+ "github.com/go-xorm/builder"
"github.com/go-xorm/xorm"
"golang.org/x/crypto/ssh"
)
return key, nil
}
+// SearchPublicKey returns a list of public keys matching the provided arguments.
+func SearchPublicKey(uid int64, fingerprint string) ([]*PublicKey, error) {
+ keys := make([]*PublicKey, 0, 5)
+ cond := builder.NewCond()
+ if uid != 0 {
+ cond = cond.And(builder.Eq{"owner_id": uid})
+ }
+ if fingerprint != "" {
+ cond = cond.And(builder.Eq{"fingerprint": fingerprint})
+ }
+ return keys, x.Where(cond).Find(&keys)
+}
+
// ListPublicKeys returns a list of public keys belongs to given user.
func ListPublicKeys(uid int64) ([]*PublicKey, error) {
keys := make([]*PublicKey, 0, 5)
Where("repo_id = ?", repoID).
Find(&keys)
}
+
+// SearchDeployKeys returns a list of deploy keys matching the provided arguments.
+func SearchDeployKeys(repoID int64, keyID int64, fingerprint string) ([]*DeployKey, error) {
+ keys := make([]*DeployKey, 0, 5)
+ cond := builder.NewCond()
+ if repoID != 0 {
+ cond = cond.And(builder.Eq{"repo_id": repoID})
+ }
+ if keyID != 0 {
+ cond = cond.And(builder.Eq{"key_id": keyID})
+ }
+ if fingerprint != "" {
+ cond = cond.And(builder.Eq{"fingerprint": fingerprint})
+ }
+ return keys, x.Where(cond).Find(&keys)
+}
// ToDeployKey convert models.DeployKey to api.DeployKey
func ToDeployKey(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.CreatedUnix.AsTime(),
- ReadOnly: true, // All deploy keys are read-only.
+ ID: key.ID,
+ KeyID: key.KeyID,
+ Key: key.Content,
+ Fingerprint: key.Fingerprint,
+ URL: apiLink + com.ToStr(key.ID),
+ Title: key.Name,
+ Created: key.CreatedUnix.AsTime(),
+ ReadOnly: key.Mode == models.AccessModeRead, // All deploy keys are read-only.
}
}
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/"
}
// 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"
- keys, err := models.ListDeployKeys(ctx.Repo.Repository.ID)
+ 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
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)
}
apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
- ctx.JSON(200, convert.ToDeployKey(apiLink, key))
+ 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
"code.gitea.io/gitea/routers/api/v1/repo"
)
+// appendPrivateInformation appends the owner and key type information to api.PublicKey
+func appendPrivateInformation(apiKey *api.PublicKey, key *models.PublicKey, defaultUser *models.User) (*api.PublicKey, error) {
+ if key.Type == models.KeyTypeDeploy {
+ apiKey.KeyType = "deploy"
+ } else if key.Type == models.KeyTypeUser {
+ apiKey.KeyType = "user"
+
+ if defaultUser.ID == key.OwnerID {
+ apiKey.Owner = defaultUser.APIFormat()
+ } else {
+ user, err := models.GetUserByID(key.OwnerID)
+ if err != nil {
+ return apiKey, err
+ }
+ apiKey.Owner = user.APIFormat()
+ }
+ } else {
+ apiKey.KeyType = "unknown"
+ }
+ apiKey.ReadOnly = key.Mode == models.AccessModeRead
+ return apiKey, nil
+}
+
// GetUserByParamsName get user by name
func GetUserByParamsName(ctx *context.APIContext, name string) *models.User {
user, err := models.GetUserByName(ctx.Params(name))
return setting.AppURL + "api/v1/user/keys/"
}
-func listPublicKeys(ctx *context.APIContext, uid int64) {
- keys, err := models.ListPublicKeys(uid)
+func listPublicKeys(ctx *context.APIContext, user *models.User) {
+ var keys []*models.PublicKey
+ var err error
+
+ fingerprint := ctx.Query("fingerprint")
+ username := ctx.Params("username")
+
+ if fingerprint != "" {
+ // Querying not just listing
+ if username != "" {
+ // Restrict to provided uid
+ keys, err = models.SearchPublicKey(user.ID, fingerprint)
+ } else {
+ // Unrestricted
+ keys, err = models.SearchPublicKey(0, fingerprint)
+ }
+ } else {
+ // Use ListPublicKeys
+ keys, err = models.ListPublicKeys(user.ID)
+ }
+
if err != nil {
ctx.Error(500, "ListPublicKeys", err)
return
apiKeys := make([]*api.PublicKey, len(keys))
for i := range keys {
apiKeys[i] = convert.ToPublicKey(apiLink, keys[i])
+ if ctx.User.IsAdmin || ctx.User.ID == keys[i].OwnerID {
+ apiKeys[i], _ = appendPrivateInformation(apiKeys[i], keys[i], user)
+ }
}
ctx.JSON(200, &apiKeys)
// swagger:operation GET /user/keys user userCurrentListKeys
// ---
// summary: List the authenticated user's public keys
+ // parameters:
+ // - name: fingerprint
+ // in: query
+ // description: fingerprint of the key
+ // type: string
// produces:
// - application/json
// responses:
// "200":
// "$ref": "#/responses/PublicKeyList"
- listPublicKeys(ctx, ctx.User.ID)
+ listPublicKeys(ctx, ctx.User)
}
// ListPublicKeys list the given user's public keys
// description: username of user
// type: string
// required: true
+ // - name: fingerprint
+ // in: query
+ // description: fingerprint of the key
+ // type: string
// responses:
// "200":
// "$ref": "#/responses/PublicKeyList"
if ctx.Written() {
return
}
- listPublicKeys(ctx, user.ID)
+ listPublicKeys(ctx, user)
}
// GetPublicKey get a public key
}
apiLink := composePublicKeysAPILink()
- ctx.JSON(200, convert.ToPublicKey(apiLink, key))
+ apiKey := convert.ToPublicKey(apiLink, key)
+ if ctx.User.IsAdmin || ctx.User.ID == key.OwnerID {
+ apiKey, _ = appendPrivateInformation(apiKey, key, ctx.User)
+ }
+ ctx.JSON(200, apiKey)
}
// CreateUserPublicKey creates new public key to given user by ID.
return
}
apiLink := composePublicKeysAPILink()
- ctx.JSON(201, convert.ToPublicKey(apiLink, key))
+ apiKey := convert.ToPublicKey(apiLink, key)
+ if ctx.User.IsAdmin || ctx.User.ID == key.OwnerID {
+ apiKey, _ = appendPrivateInformation(apiKey, key, ctx.User)
+ }
+ ctx.JSON(201, apiKey)
}
// CreatePublicKey create one public key for me
"name": "repo",
"in": "path",
"required": true
+ },
+ {
+ "type": "integer",
+ "description": "the key_id to search for",
+ "name": "key_id",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "fingerprint of the key",
+ "name": "fingerprint",
+ "in": "query"
}
],
"responses": {
],
"summary": "List the authenticated user's public keys",
"operationId": "userCurrentListKeys",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "fingerprint of the key",
+ "name": "fingerprint",
+ "in": "query"
+ }
+ ],
"responses": {
"200": {
"$ref": "#/responses/PublicKeyList"
"name": "username",
"in": "path",
"required": true
+ },
+ {
+ "type": "string",
+ "description": "fingerprint of the key",
+ "name": "fingerprint",
+ "in": "query"
}
],
"responses": {