From 9ca5c60020b4f533e7d29af7dcf4c2a8ac27657c Mon Sep 17 00:00:00 2001 From: Gitea Date: Tue, 12 Jun 2018 16:39:30 -0400 Subject: [PATCH 01/13] Add ability to delete a token Fix #4234 --- routers/api/v1/api.go | 1 + routers/api/v1/user/app.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index eec55cac673c7..831300d69f3be 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -302,6 +302,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/tokens", func() { m.Combo("").Get(user.ListAccessTokens). Post(bind(api.CreateAccessTokenOption{}), user.CreateAccessToken) + m.Combo("/:id").Delete(user.DeleteAccessToken) }, reqBasicAuth()) }) }) diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index fc4118649c068..0dc1fbeba60d8 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -1,4 +1,5 @@ // 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 // license that can be found in the LICENSE file. @@ -74,3 +75,39 @@ func CreateAccessToken(ctx *context.APIContext, form api.CreateAccessTokenOption Sha1: t.Sha1, }) } + +// DeleteAccessToken delete access tokens +func DeleteAccessToken(ctx *context.APIContext) { + // swagger:operation DELETE /users/{username}/tokens/{token} user userCreateToken + // --- + // summary: Create an access token + // consumes: + // - application/json + // 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: string + // 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) +} From 4ec4dc88b026f7fd8608ad10ee2d43bc71a86dc4 Mon Sep 17 00:00:00 2001 From: Gitea Date: Tue, 12 Jun 2018 16:46:45 -0400 Subject: [PATCH 02/13] add swagger file --- public/swagger.v1.json | 50 ++++++++++++++++++++++++++++++++++++++ routers/api/v1/user/app.go | 2 +- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/public/swagger.v1.json b/public/swagger.v1.json index 1c381a8297e75..baebb059bd058 100644 --- a/public/swagger.v1.json +++ b/public/swagger.v1.json @@ -5432,11 +5432,61 @@ "name": "username", "in": "path", "required": true + }, + { + "type": "int", + "description": "token to be deleted", + "name": "token", + "in": "path", + "required": true } ], "responses": { "200": { "$ref": "#/responses/AccessToken" + }, + "204": { + "$ref": "#/responses/empty" + } + } + } + }, + "/users/{username}/tokens/{token}": { + "delete": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "user" + ], + "summary": "Create an access token", + "operationId": "userCreateToken", + "parameters": [ + { + "type": "string", + "x-go-name": "Name", + "description": "username of user", + "name": "username", + "in": "path", + "required": true + }, + { + "type": "int", + "description": "token to be deleted", + "name": "token", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/AccessToken" + }, + "204": { + "$ref": "#/responses/empty" } } } diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index 0dc1fbeba60d8..e65b96f7c656b 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -94,7 +94,7 @@ func DeleteAccessToken(ctx *context.APIContext) { // - name: token // in: path // description: token to be deleted - // type: string + // type: int // required: true // responses: // "204": From 8654ad7a7763fc759d64979e07ab9cc6c1a510e9 Mon Sep 17 00:00:00 2001 From: Gitea Date: Tue, 12 Jun 2018 16:56:15 -0400 Subject: [PATCH 03/13] resolve swagger issues --- public/swagger.v1.json | 21 ++------------------- routers/api/v1/user/app.go | 6 ++---- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/public/swagger.v1.json b/public/swagger.v1.json index baebb059bd058..e1e0c0c93a7b9 100644 --- a/public/swagger.v1.json +++ b/public/swagger.v1.json @@ -5432,30 +5432,17 @@ "name": "username", "in": "path", "required": true - }, - { - "type": "int", - "description": "token to be deleted", - "name": "token", - "in": "path", - "required": true } ], "responses": { "200": { "$ref": "#/responses/AccessToken" - }, - "204": { - "$ref": "#/responses/empty" } } } }, "/users/{username}/tokens/{token}": { "delete": { - "consumes": [ - "application/json" - ], "produces": [ "application/json" ], @@ -5463,18 +5450,17 @@ "user" ], "summary": "Create an access token", - "operationId": "userCreateToken", + "operationId": "userDeleteAccessToken", "parameters": [ { "type": "string", - "x-go-name": "Name", "description": "username of user", "name": "username", "in": "path", "required": true }, { - "type": "int", + "type": "integer", "description": "token to be deleted", "name": "token", "in": "path", @@ -5482,9 +5468,6 @@ } ], "responses": { - "200": { - "$ref": "#/responses/AccessToken" - }, "204": { "$ref": "#/responses/empty" } diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index e65b96f7c656b..68c53cae448b0 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -78,11 +78,9 @@ func CreateAccessToken(ctx *context.APIContext, form api.CreateAccessTokenOption // DeleteAccessToken delete access tokens func DeleteAccessToken(ctx *context.APIContext) { - // swagger:operation DELETE /users/{username}/tokens/{token} user userCreateToken + // swagger:operation DELETE /users/{username}/tokens/{token} user userDeleteAccessToken // --- // summary: Create an access token - // consumes: - // - application/json // produces: // - application/json // parameters: @@ -94,7 +92,7 @@ func DeleteAccessToken(ctx *context.APIContext) { // - name: token // in: path // description: token to be deleted - // type: int + // type: integer // required: true // responses: // "204": From e211c284520b2fdfa23c912167d4ebb1276a9b86 Mon Sep 17 00:00:00 2001 From: Gitea Date: Tue, 12 Jun 2018 17:16:06 -0400 Subject: [PATCH 04/13] correct api description --- public/swagger.v1.json | 2 +- routers/api/v1/user/app.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/swagger.v1.json b/public/swagger.v1.json index e1e0c0c93a7b9..07d9cf5389e8f 100644 --- a/public/swagger.v1.json +++ b/public/swagger.v1.json @@ -5449,7 +5449,7 @@ "tags": [ "user" ], - "summary": "Create an access token", + "summary": "delete an access token", "operationId": "userDeleteAccessToken", "parameters": [ { diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index 68c53cae448b0..b597aae43e7ac 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -80,7 +80,7 @@ func CreateAccessToken(ctx *context.APIContext, form api.CreateAccessTokenOption func DeleteAccessToken(ctx *context.APIContext) { // swagger:operation DELETE /users/{username}/tokens/{token} user userDeleteAccessToken // --- - // summary: Create an access token + // summary: delete an access token // produces: // - application/json // parameters: From 112ec99322c6006ac89408550be3d090d9545a0d Mon Sep 17 00:00:00 2001 From: Gitea Date: Tue, 12 Jun 2018 19:06:50 -0400 Subject: [PATCH 05/13] add ID as an output for list API --- public/swagger.v1.json | 4 ++++ routers/api/v1/user/app.go | 2 ++ vendor/code.gitea.io/sdk/gitea/user_app.go | 1 + 3 files changed, 7 insertions(+) diff --git a/public/swagger.v1.json b/public/swagger.v1.json index 07d9cf5389e8f..019f6d5d11ba9 100644 --- a/public/swagger.v1.json +++ b/public/swagger.v1.json @@ -7512,6 +7512,10 @@ "AccessToken": { "description": "AccessToken represents a API access token.", "headers": { + "id": { + "type": "integer", + "format": "int64" + }, "name": { "type": "string" }, diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index b597aae43e7ac..71635a9f02642 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -39,6 +39,7 @@ func ListAccessTokens(ctx *context.APIContext) { apiTokens[i] = &api.AccessToken{ Name: tokens[i].Name, Sha1: tokens[i].Sha1, + ID: tokens[i].ID, } } ctx.JSON(200, &apiTokens) @@ -73,6 +74,7 @@ func CreateAccessToken(ctx *context.APIContext, form api.CreateAccessTokenOption ctx.JSON(201, &api.AccessToken{ Name: t.Name, Sha1: t.Sha1, + ID: t.ID, }) } diff --git a/vendor/code.gitea.io/sdk/gitea/user_app.go b/vendor/code.gitea.io/sdk/gitea/user_app.go index 08e98513ee07f..cc1f5270fa5f6 100644 --- a/vendor/code.gitea.io/sdk/gitea/user_app.go +++ b/vendor/code.gitea.io/sdk/gitea/user_app.go @@ -22,6 +22,7 @@ func BasicAuthEncode(user, pass string) string { type AccessToken struct { Name string `json:"name"` Sha1 string `json:"sha1"` + ID int64 `json:"id"` } // AccessTokenList represents a list of API access token. From b91a9690f6200de5c7fd95652200b6c6f6018620 Mon Sep 17 00:00:00 2001 From: Gitea Date: Thu, 21 Jun 2018 12:40:23 -0400 Subject: [PATCH 06/13] update code.gitea.io/sdk with changes --- Gopkg.lock | 2 +- vendor/code.gitea.io/sdk/gitea/user_app.go | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 6551354a0948e..6bb0b8619e7a3 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -11,7 +11,7 @@ branch = "master" name = "code.gitea.io/sdk" packages = ["gitea"] - revision = "b2308e3f700875a3642a78bd3f6e5db8ef6f974d" + revision = "ec80752c9512cf07fc62ddc42565118183743942" [[projects]] name = "github.com/PuerkitoBio/goquery" diff --git a/vendor/code.gitea.io/sdk/gitea/user_app.go b/vendor/code.gitea.io/sdk/gitea/user_app.go index cc1f5270fa5f6..d3bfce971bad4 100644 --- a/vendor/code.gitea.io/sdk/gitea/user_app.go +++ b/vendor/code.gitea.io/sdk/gitea/user_app.go @@ -20,9 +20,9 @@ func BasicAuthEncode(user, pass string) string { // AccessToken represents a API access token. // swagger:response AccessToken type AccessToken struct { + ID int64 `json:"id"` Name string `json:"name"` Sha1 string `json:"sha1"` - ID int64 `json:"id"` } // AccessTokenList represents a list of API access token. @@ -55,3 +55,9 @@ func (c *Client) CreateAccessToken(user, pass string, opt CreateAccessTokenOptio "Authorization": []string{"Basic " + BasicAuthEncode(user, pass)}}, 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 +} From f426f13c8a7c90c278beecfd469779e855da5fc3 Mon Sep 17 00:00:00 2001 From: techknowlogick Date: Thu, 21 Jun 2018 12:42:30 -0400 Subject: [PATCH 07/13] ID first --- routers/api/v1/user/app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index 71635a9f02642..216190b0f0b93 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -37,9 +37,9 @@ func ListAccessTokens(ctx *context.APIContext) { apiTokens := make([]*api.AccessToken, len(tokens)) for i := range tokens { apiTokens[i] = &api.AccessToken{ + ID: tokens[i].ID, Name: tokens[i].Name, Sha1: tokens[i].Sha1, - ID: tokens[i].ID, } } ctx.JSON(200, &apiTokens) From 255ccc94fbb17051a1b08f6a9d74b1d1fafe830e Mon Sep 17 00:00:00 2001 From: Matti Ranta Date: Fri, 6 Jul 2018 12:11:38 -0400 Subject: [PATCH 08/13] add some tests --- integrations/api_token_test.go | 47 ++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 integrations/api_token_test.go diff --git a/integrations/api_token_test.go b/integrations/api_token_test.go new file mode 100644 index 0000000000000..955a9070eb1b6 --- /dev/null +++ b/integrations/api_token_test.go @@ -0,0 +1,47 @@ +// 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" + + api "code.gitea.io/sdk/gitea" + + "github.com/stretchr/testify/assert" +) + +// TestAPICreateAndDeleteToken tests that token that was just created can be deleted +func TestAPICreateAndDeleteToken(t *testing.T) { + prepareTestEnv(t) + session := loginUser(t, "user1") + + req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ + "name": "test-key-1", + }) + resp := session.MakeRequest(t, req, http.StatusCreated) + + // api.AccessToken + var newAccessToken api.AccessToken + DecodeJSON(t, resp, &newAccessToken) + models.AssertExistsAndLoadBean(t, &models.AccessToken{ + ID: newAccessToken.ID, + Name: newAccessToken.Title, + Sha1: newAccessToken.Sha1 + }) + + req = NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%d", newAccessToken.ID) + session.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) + session := loginUser(t, "user1") + + req := NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%d", models.NonexistentID) + session.MakeRequest(t, req, http.StatusNotFound) +} \ No newline at end of file From ae4f22ab9d9020cc62195db515f4459c25a993f0 Mon Sep 17 00:00:00 2001 From: Matti Ranta Date: Fri, 6 Jul 2018 12:18:08 -0400 Subject: [PATCH 09/13] fix syntax --- integrations/api_token_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrations/api_token_test.go b/integrations/api_token_test.go index 955a9070eb1b6..79291667d4634 100644 --- a/integrations/api_token_test.go +++ b/integrations/api_token_test.go @@ -29,7 +29,7 @@ func TestAPICreateAndDeleteToken(t *testing.T) { models.AssertExistsAndLoadBean(t, &models.AccessToken{ ID: newAccessToken.ID, Name: newAccessToken.Title, - Sha1: newAccessToken.Sha1 + Sha1: newAccessToken.Sha1, }) req = NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%d", newAccessToken.ID) From b47947a15f189ccf32400521191815ae070dd7ae Mon Sep 17 00:00:00 2001 From: Matti Ranta Date: Fri, 6 Jul 2018 12:20:18 -0400 Subject: [PATCH 10/13] make fmt --- integrations/api_token_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrations/api_token_test.go b/integrations/api_token_test.go index 79291667d4634..893941c91b135 100644 --- a/integrations/api_token_test.go +++ b/integrations/api_token_test.go @@ -44,4 +44,4 @@ func TestAPIDeleteMissingToken(t *testing.T) { req := NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%d", models.NonexistentID) session.MakeRequest(t, req, http.StatusNotFound) -} \ No newline at end of file +} From 52f139a5c161aa0bb5021bf3f1c6d9f67b73ac6b Mon Sep 17 00:00:00 2001 From: Matti Ranta Date: Fri, 6 Jul 2018 12:25:54 -0400 Subject: [PATCH 11/13] missing import --- integrations/api_token_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/integrations/api_token_test.go b/integrations/api_token_test.go index 893941c91b135..d87907a19c8f7 100644 --- a/integrations/api_token_test.go +++ b/integrations/api_token_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + "code.gitea.io/gitea/models" api "code.gitea.io/sdk/gitea" "github.com/stretchr/testify/assert" @@ -18,7 +19,7 @@ func TestAPICreateAndDeleteToken(t *testing.T) { prepareTestEnv(t) session := loginUser(t, "user1") - req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ + req := NewRequestWithValues(t, "POST", "/api/v1/users/user1/tokens", map[string]string{ "name": "test-key-1", }) resp := session.MakeRequest(t, req, http.StatusCreated) From 1afda067ca47edfba82c3169a7a7e2b7a2ef9df8 Mon Sep 17 00:00:00 2001 From: Matti Ranta Date: Fri, 6 Jul 2018 13:05:47 -0400 Subject: [PATCH 12/13] remove import --- integrations/api_token_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/integrations/api_token_test.go b/integrations/api_token_test.go index d87907a19c8f7..5db5b631ae6fe 100644 --- a/integrations/api_token_test.go +++ b/integrations/api_token_test.go @@ -10,8 +10,6 @@ import ( "code.gitea.io/gitea/models" api "code.gitea.io/sdk/gitea" - - "github.com/stretchr/testify/assert" ) // TestAPICreateAndDeleteToken tests that token that was just created can be deleted @@ -29,7 +27,7 @@ func TestAPICreateAndDeleteToken(t *testing.T) { DecodeJSON(t, resp, &newAccessToken) models.AssertExistsAndLoadBean(t, &models.AccessToken{ ID: newAccessToken.ID, - Name: newAccessToken.Title, + Name: newAccessToken.Name, Sha1: newAccessToken.Sha1, }) From be1687f44d2953426f6db1ae7c5bdcf1bc92c8dc Mon Sep 17 00:00:00 2001 From: Matti Ranta Date: Fri, 6 Jul 2018 14:30:39 -0400 Subject: [PATCH 13/13] tokens api can only auth via basic auth --- integrations/api_token_test.go | 18 +++++++++++------- integrations/integration_test.go | 5 +++++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/integrations/api_token_test.go b/integrations/api_token_test.go index 5db5b631ae6fe..2520f356b7fa7 100644 --- a/integrations/api_token_test.go +++ b/integrations/api_token_test.go @@ -15,32 +15,36 @@ import ( // TestAPICreateAndDeleteToken tests that token that was just created can be deleted func TestAPICreateAndDeleteToken(t *testing.T) { prepareTestEnv(t) - session := loginUser(t, "user1") + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User) - req := NewRequestWithValues(t, "POST", "/api/v1/users/user1/tokens", map[string]string{ + req := NewRequestWithJSON(t, "POST", "/api/v1/users/user1/tokens", map[string]string{ "name": "test-key-1", }) - resp := session.MakeRequest(t, req, http.StatusCreated) + req = AddBasicAuthHeader(req, user.Name) + resp := MakeRequest(t, req, http.StatusCreated) - // api.AccessToken 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) - session.MakeRequest(t, req, http.StatusNoContent) + 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) - session := loginUser(t, "user1") + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User) req := NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%d", models.NonexistentID) - session.MakeRequest(t, req, http.StatusNotFound) + req = AddBasicAuthHeader(req, user.Name) + MakeRequest(t, req, http.StatusNotFound) } diff --git a/integrations/integration_test.go b/integrations/integration_test.go index 664290cc9da32..a1e66ffdfdfce 100644 --- a/integrations/integration_test.go +++ b/integrations/integration_test.go @@ -256,6 +256,11 @@ func NewRequestWithBody(t testing.TB, method, urlStr string, body io.Reader) *ht return request } +func AddBasicAuthHeader(request *http.Request, username string) *http.Request { + request.SetBasicAuth(username, userPassword) + return request +} + const NoExpectedStatus = -1 func MakeRequest(t testing.TB, req *http.Request, expectedStatus int) *httptest.ResponseRecorder {