Skip to content

Commit ab55ca7

Browse files
Add ability to delete a token (#4235)
Fix #4234
1 parent 1675fc4 commit ab55ca7

File tree

7 files changed

+138
-1
lines changed

7 files changed

+138
-1
lines changed

Gopkg.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

integrations/api_token_test.go

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2018 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package integrations
6+
7+
import (
8+
"net/http"
9+
"testing"
10+
11+
"code.gitea.io/gitea/models"
12+
api "code.gitea.io/sdk/gitea"
13+
)
14+
15+
// TestAPICreateAndDeleteToken tests that token that was just created can be deleted
16+
func TestAPICreateAndDeleteToken(t *testing.T) {
17+
prepareTestEnv(t)
18+
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
19+
20+
req := NewRequestWithJSON(t, "POST", "/api/v1/users/user1/tokens", map[string]string{
21+
"name": "test-key-1",
22+
})
23+
req = AddBasicAuthHeader(req, user.Name)
24+
resp := MakeRequest(t, req, http.StatusCreated)
25+
26+
var newAccessToken api.AccessToken
27+
DecodeJSON(t, resp, &newAccessToken)
28+
models.AssertExistsAndLoadBean(t, &models.AccessToken{
29+
ID: newAccessToken.ID,
30+
Name: newAccessToken.Name,
31+
Sha1: newAccessToken.Sha1,
32+
UID: user.ID,
33+
})
34+
35+
req = NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%d", newAccessToken.ID)
36+
req = AddBasicAuthHeader(req, user.Name)
37+
MakeRequest(t, req, http.StatusNoContent)
38+
39+
models.AssertNotExistsBean(t, &models.AccessToken{ID: newAccessToken.ID})
40+
}
41+
42+
// TestAPIDeleteMissingToken ensures that error is thrown when token not found
43+
func TestAPIDeleteMissingToken(t *testing.T) {
44+
prepareTestEnv(t)
45+
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
46+
47+
req := NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%d", models.NonexistentID)
48+
req = AddBasicAuthHeader(req, user.Name)
49+
MakeRequest(t, req, http.StatusNotFound)
50+
}

integrations/integration_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,11 @@ func NewRequestWithBody(t testing.TB, method, urlStr string, body io.Reader) *ht
256256
return request
257257
}
258258

259+
func AddBasicAuthHeader(request *http.Request, username string) *http.Request {
260+
request.SetBasicAuth(username, userPassword)
261+
return request
262+
}
263+
259264
const NoExpectedStatus = -1
260265

261266
func MakeRequest(t testing.TB, req *http.Request, expectedStatus int) *httptest.ResponseRecorder {

public/swagger.v1.json

+37
Original file line numberDiff line numberDiff line change
@@ -5441,6 +5441,39 @@
54415441
}
54425442
}
54435443
},
5444+
"/users/{username}/tokens/{token}": {
5445+
"delete": {
5446+
"produces": [
5447+
"application/json"
5448+
],
5449+
"tags": [
5450+
"user"
5451+
],
5452+
"summary": "delete an access token",
5453+
"operationId": "userDeleteAccessToken",
5454+
"parameters": [
5455+
{
5456+
"type": "string",
5457+
"description": "username of user",
5458+
"name": "username",
5459+
"in": "path",
5460+
"required": true
5461+
},
5462+
{
5463+
"type": "integer",
5464+
"description": "token to be deleted",
5465+
"name": "token",
5466+
"in": "path",
5467+
"required": true
5468+
}
5469+
],
5470+
"responses": {
5471+
"204": {
5472+
"$ref": "#/responses/empty"
5473+
}
5474+
}
5475+
}
5476+
},
54445477
"/version": {
54455478
"get": {
54465479
"produces": [
@@ -7479,6 +7512,10 @@
74797512
"AccessToken": {
74807513
"description": "AccessToken represents a API access token.",
74817514
"headers": {
7515+
"id": {
7516+
"type": "integer",
7517+
"format": "int64"
7518+
},
74827519
"name": {
74837520
"type": "string"
74847521
},

routers/api/v1/api.go

+1
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ func RegisterRoutes(m *macaron.Macaron) {
302302
m.Group("/tokens", func() {
303303
m.Combo("").Get(user.ListAccessTokens).
304304
Post(bind(api.CreateAccessTokenOption{}), user.CreateAccessToken)
305+
m.Combo("/:id").Delete(user.DeleteAccessToken)
305306
}, reqBasicAuth())
306307
})
307308
})

routers/api/v1/user/app.go

+37
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright 2014 The Gogs Authors. All rights reserved.
2+
// Copyright 2018 The Gitea Authors. All rights reserved.
23
// Use of this source code is governed by a MIT-style
34
// license that can be found in the LICENSE file.
45

@@ -36,6 +37,7 @@ func ListAccessTokens(ctx *context.APIContext) {
3637
apiTokens := make([]*api.AccessToken, len(tokens))
3738
for i := range tokens {
3839
apiTokens[i] = &api.AccessToken{
40+
ID: tokens[i].ID,
3941
Name: tokens[i].Name,
4042
Sha1: tokens[i].Sha1,
4143
}
@@ -72,5 +74,40 @@ func CreateAccessToken(ctx *context.APIContext, form api.CreateAccessTokenOption
7274
ctx.JSON(201, &api.AccessToken{
7375
Name: t.Name,
7476
Sha1: t.Sha1,
77+
ID: t.ID,
7578
})
7679
}
80+
81+
// DeleteAccessToken delete access tokens
82+
func DeleteAccessToken(ctx *context.APIContext) {
83+
// swagger:operation DELETE /users/{username}/tokens/{token} user userDeleteAccessToken
84+
// ---
85+
// summary: delete an access token
86+
// produces:
87+
// - application/json
88+
// parameters:
89+
// - name: username
90+
// in: path
91+
// description: username of user
92+
// type: string
93+
// required: true
94+
// - name: token
95+
// in: path
96+
// description: token to be deleted
97+
// type: integer
98+
// required: true
99+
// responses:
100+
// "204":
101+
// "$ref": "#/responses/empty"
102+
tokenID := ctx.ParamsInt64(":id")
103+
if err := models.DeleteAccessTokenByID(tokenID, ctx.User.ID); err != nil {
104+
if models.IsErrAccessTokenNotExist(err) {
105+
ctx.Status(404)
106+
} else {
107+
ctx.Error(500, "DeleteAccessTokenByID", err)
108+
}
109+
return
110+
}
111+
112+
ctx.Status(204)
113+
}

vendor/code.gitea.io/sdk/gitea/user_app.go

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)