| @@ -11,7 +11,7 @@ | |||||
| branch = "master" | branch = "master" | ||||
| name = "code.gitea.io/sdk" | name = "code.gitea.io/sdk" | ||||
| packages = ["gitea"] | packages = ["gitea"] | ||||
| revision = "b2308e3f700875a3642a78bd3f6e5db8ef6f974d" | |||||
| revision = "ec80752c9512cf07fc62ddc42565118183743942" | |||||
| [[projects]] | [[projects]] | ||||
| name = "github.com/PuerkitoBio/goquery" | name = "github.com/PuerkitoBio/goquery" | ||||
| @@ -0,0 +1,50 @@ | |||||
| // Copyright 2018 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 ( | |||||
| "net/http" | |||||
| "testing" | |||||
| "code.gitea.io/gitea/models" | |||||
| api "code.gitea.io/sdk/gitea" | |||||
| ) | |||||
| // TestAPICreateAndDeleteToken tests that token that was just created can be deleted | |||||
| func TestAPICreateAndDeleteToken(t *testing.T) { | |||||
| prepareTestEnv(t) | |||||
| user := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User) | |||||
| req := NewRequestWithJSON(t, "POST", "/api/v1/users/user1/tokens", map[string]string{ | |||||
| "name": "test-key-1", | |||||
| }) | |||||
| req = AddBasicAuthHeader(req, user.Name) | |||||
| resp := MakeRequest(t, req, http.StatusCreated) | |||||
| var newAccessToken api.AccessToken | |||||
| DecodeJSON(t, resp, &newAccessToken) | |||||
| models.AssertExistsAndLoadBean(t, &models.AccessToken{ | |||||
| ID: newAccessToken.ID, | |||||
| Name: newAccessToken.Name, | |||||
| Sha1: newAccessToken.Sha1, | |||||
| UID: user.ID, | |||||
| }) | |||||
| req = NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%d", newAccessToken.ID) | |||||
| req = AddBasicAuthHeader(req, user.Name) | |||||
| MakeRequest(t, req, http.StatusNoContent) | |||||
| models.AssertNotExistsBean(t, &models.AccessToken{ID: newAccessToken.ID}) | |||||
| } | |||||
| // TestAPIDeleteMissingToken ensures that error is thrown when token not found | |||||
| func TestAPIDeleteMissingToken(t *testing.T) { | |||||
| prepareTestEnv(t) | |||||
| user := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User) | |||||
| req := NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%d", models.NonexistentID) | |||||
| req = AddBasicAuthHeader(req, user.Name) | |||||
| MakeRequest(t, req, http.StatusNotFound) | |||||
| } | |||||
| @@ -256,6 +256,11 @@ func NewRequestWithBody(t testing.TB, method, urlStr string, body io.Reader) *ht | |||||
| return request | return request | ||||
| } | } | ||||
| func AddBasicAuthHeader(request *http.Request, username string) *http.Request { | |||||
| request.SetBasicAuth(username, userPassword) | |||||
| return request | |||||
| } | |||||
| const NoExpectedStatus = -1 | const NoExpectedStatus = -1 | ||||
| func MakeRequest(t testing.TB, req *http.Request, expectedStatus int) *httptest.ResponseRecorder { | func MakeRequest(t testing.TB, req *http.Request, expectedStatus int) *httptest.ResponseRecorder { | ||||
| @@ -5441,6 +5441,39 @@ | |||||
| } | } | ||||
| } | } | ||||
| }, | }, | ||||
| "/users/{username}/tokens/{token}": { | |||||
| "delete": { | |||||
| "produces": [ | |||||
| "application/json" | |||||
| ], | |||||
| "tags": [ | |||||
| "user" | |||||
| ], | |||||
| "summary": "delete an access token", | |||||
| "operationId": "userDeleteAccessToken", | |||||
| "parameters": [ | |||||
| { | |||||
| "type": "string", | |||||
| "description": "username of user", | |||||
| "name": "username", | |||||
| "in": "path", | |||||
| "required": true | |||||
| }, | |||||
| { | |||||
| "type": "integer", | |||||
| "description": "token to be deleted", | |||||
| "name": "token", | |||||
| "in": "path", | |||||
| "required": true | |||||
| } | |||||
| ], | |||||
| "responses": { | |||||
| "204": { | |||||
| "$ref": "#/responses/empty" | |||||
| } | |||||
| } | |||||
| } | |||||
| }, | |||||
| "/version": { | "/version": { | ||||
| "get": { | "get": { | ||||
| "produces": [ | "produces": [ | ||||
| @@ -7479,6 +7512,10 @@ | |||||
| "AccessToken": { | "AccessToken": { | ||||
| "description": "AccessToken represents a API access token.", | "description": "AccessToken represents a API access token.", | ||||
| "headers": { | "headers": { | ||||
| "id": { | |||||
| "type": "integer", | |||||
| "format": "int64" | |||||
| }, | |||||
| "name": { | "name": { | ||||
| "type": "string" | "type": "string" | ||||
| }, | }, | ||||
| @@ -302,6 +302,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Group("/tokens", func() { | m.Group("/tokens", func() { | ||||
| m.Combo("").Get(user.ListAccessTokens). | m.Combo("").Get(user.ListAccessTokens). | ||||
| Post(bind(api.CreateAccessTokenOption{}), user.CreateAccessToken) | Post(bind(api.CreateAccessTokenOption{}), user.CreateAccessToken) | ||||
| m.Combo("/:id").Delete(user.DeleteAccessToken) | |||||
| }, reqBasicAuth()) | }, reqBasicAuth()) | ||||
| }) | }) | ||||
| }) | }) | ||||
| @@ -1,4 +1,5 @@ | |||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Copyright 2018 The Gitea Authors. All rights reserved. | |||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
| @@ -36,6 +37,7 @@ func ListAccessTokens(ctx *context.APIContext) { | |||||
| apiTokens := make([]*api.AccessToken, len(tokens)) | apiTokens := make([]*api.AccessToken, len(tokens)) | ||||
| for i := range tokens { | for i := range tokens { | ||||
| apiTokens[i] = &api.AccessToken{ | apiTokens[i] = &api.AccessToken{ | ||||
| ID: tokens[i].ID, | |||||
| Name: tokens[i].Name, | Name: tokens[i].Name, | ||||
| Sha1: tokens[i].Sha1, | Sha1: tokens[i].Sha1, | ||||
| } | } | ||||
| @@ -72,5 +74,40 @@ func CreateAccessToken(ctx *context.APIContext, form api.CreateAccessTokenOption | |||||
| ctx.JSON(201, &api.AccessToken{ | ctx.JSON(201, &api.AccessToken{ | ||||
| Name: t.Name, | Name: t.Name, | ||||
| Sha1: t.Sha1, | Sha1: t.Sha1, | ||||
| ID: t.ID, | |||||
| }) | }) | ||||
| } | } | ||||
| // DeleteAccessToken delete access tokens | |||||
| func DeleteAccessToken(ctx *context.APIContext) { | |||||
| // swagger:operation DELETE /users/{username}/tokens/{token} user userDeleteAccessToken | |||||
| // --- | |||||
| // summary: delete an access token | |||||
| // produces: | |||||
| // - application/json | |||||
| // parameters: | |||||
| // - name: username | |||||
| // in: path | |||||
| // description: username of user | |||||
| // type: string | |||||
| // required: true | |||||
| // - name: token | |||||
| // in: path | |||||
| // description: token to be deleted | |||||
| // type: integer | |||||
| // required: true | |||||
| // responses: | |||||
| // "204": | |||||
| // "$ref": "#/responses/empty" | |||||
| tokenID := ctx.ParamsInt64(":id") | |||||
| if err := models.DeleteAccessTokenByID(tokenID, ctx.User.ID); err != nil { | |||||
| if models.IsErrAccessTokenNotExist(err) { | |||||
| ctx.Status(404) | |||||
| } else { | |||||
| ctx.Error(500, "DeleteAccessTokenByID", err) | |||||
| } | |||||
| return | |||||
| } | |||||
| ctx.Status(204) | |||||
| } | |||||
| @@ -20,6 +20,7 @@ func BasicAuthEncode(user, pass string) string { | |||||
| // AccessToken represents a API access token. | // AccessToken represents a API access token. | ||||
| // swagger:response AccessToken | // swagger:response AccessToken | ||||
| type AccessToken struct { | type AccessToken struct { | ||||
| ID int64 `json:"id"` | |||||
| Name string `json:"name"` | Name string `json:"name"` | ||||
| Sha1 string `json:"sha1"` | Sha1 string `json:"sha1"` | ||||
| } | } | ||||
| @@ -54,3 +55,9 @@ func (c *Client) CreateAccessToken(user, pass string, opt CreateAccessTokenOptio | |||||
| "Authorization": []string{"Basic " + BasicAuthEncode(user, pass)}}, | "Authorization": []string{"Basic " + BasicAuthEncode(user, pass)}}, | ||||
| bytes.NewReader(body), t) | bytes.NewReader(body), t) | ||||
| } | } | ||||
| // DeleteAccessToken delete token with key id | |||||
| func (c *Client) DeleteAccessToken(user string, keyID int64) error { | |||||
| _, err := c.getResponse("DELETE", fmt.Sprintf("/user/%s/tokens/%d", user, keyID), nil, nil) | |||||
| return err | |||||
| } | |||||