Skip to content

Commit ad5c43a

Browse files
6543adelowo
andauthored
Reject duplicate AccessToken names (#10994)
* make sure duplicate token names cannot be used * add check to api routes too * add @lunny s suggestion * fix & don't forget User.ID * AccessTokenByNameExists() return error too * unique token for each test * fix lint Signed-off-by: 6543 <[email protected]> Co-authored-by: Lanre Adelowo <[email protected]>
1 parent 980ef24 commit ad5c43a

File tree

6 files changed

+71
-1
lines changed

6 files changed

+71
-1
lines changed

integrations/integration_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,14 +330,18 @@ func loginUserWithPassword(t testing.TB, userName, password string) *TestSession
330330
return session
331331
}
332332

333+
//token has to be unique this counter take care of
334+
var tokenCounter int64
335+
333336
func getTokenForLoggedInUser(t testing.TB, session *TestSession) string {
334337
t.Helper()
338+
tokenCounter++
335339
req := NewRequest(t, "GET", "/user/settings/applications")
336340
resp := session.MakeRequest(t, req, http.StatusOK)
337341
doc := NewHTMLParser(t, resp.Body)
338342
req = NewRequestWithValues(t, "POST", "/user/settings/applications", map[string]string{
339343
"_csrf": doc.GetCSRF(),
340-
"name": "api-testing-token",
344+
"name": fmt.Sprintf("api-testing-token-%d", tokenCounter),
341345
})
342346
resp = session.MakeRequest(t, req, http.StatusFound)
343347
req = NewRequest(t, "GET", "/user/settings/applications")

models/token.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ func GetAccessTokenBySHA(token string) (*AccessToken, error) {
7777
return nil, ErrAccessTokenNotExist{token}
7878
}
7979

80+
// AccessTokenByNameExists checks if a token name has been used already by a user.
81+
func AccessTokenByNameExists(token *AccessToken) (bool, error) {
82+
return x.Table("access_token").Where("name = ?", token.Name).And("uid = ?", token.UID).Exist()
83+
}
84+
8085
// ListAccessTokens returns a list of access tokens belongs to given user.
8186
func ListAccessTokens(uid int64, listOptions ListOptions) ([]*AccessToken, error) {
8287
sess := x.

models/token_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,42 @@ func TestNewAccessToken(t *testing.T) {
2727
assert.Error(t, NewAccessToken(invalidToken))
2828
}
2929

30+
func TestAccessTokenByNameExists(t *testing.T) {
31+
32+
name := "Token Gitea"
33+
34+
assert.NoError(t, PrepareTestDatabase())
35+
token := &AccessToken{
36+
UID: 3,
37+
Name: name,
38+
}
39+
40+
// Check to make sure it doesn't exists already
41+
exist, err := AccessTokenByNameExists(token)
42+
assert.NoError(t, err)
43+
assert.False(t, exist)
44+
45+
// Save it to the database
46+
assert.NoError(t, NewAccessToken(token))
47+
AssertExistsAndLoadBean(t, token)
48+
49+
// This token must be found by name in the DB now
50+
exist, err = AccessTokenByNameExists(token)
51+
assert.NoError(t, err)
52+
assert.True(t, exist)
53+
54+
user4Token := &AccessToken{
55+
UID: 4,
56+
Name: name,
57+
}
58+
59+
// Name matches but different user ID, this shouldn't exists in the
60+
// database
61+
exist, err = AccessTokenByNameExists(user4Token)
62+
assert.NoError(t, err)
63+
assert.False(t, exist)
64+
}
65+
3066
func TestGetAccessTokenBySHA(t *testing.T) {
3167
assert.NoError(t, PrepareTestDatabase())
3268
token, err := GetAccessTokenBySHA("d2c6c1ba3890b309189a8e618c72a162e4efbf36")

options/locale/locale_en-US.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ new_token_desc = Applications using a token have full access to your account.
517517
token_name = Token Name
518518
generate_token = Generate Token
519519
generate_token_success = Your new token has been generated. Copy it now as it will not be shown again.
520+
generate_token_name_duplicate = <strong>%s</strong> has been used as an application name already. Please use a new one.
520521
delete_token = Delete
521522
access_token_deletion = Delete Access Token
522523
access_token_deletion_desc = Deleting a token will revoke access to your account for applications using it. Continue?

routers/api/v1/user/app.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package user
77

88
import (
9+
"errors"
910
"net/http"
1011

1112
"code.gitea.io/gitea/models"
@@ -89,6 +90,17 @@ func CreateAccessToken(ctx *context.APIContext, form api.CreateAccessTokenOption
8990
UID: ctx.User.ID,
9091
Name: form.Name,
9192
}
93+
94+
exist, err := models.AccessTokenByNameExists(t)
95+
if err != nil {
96+
ctx.InternalServerError(err)
97+
return
98+
}
99+
if exist {
100+
ctx.Error(http.StatusBadRequest, "AccessTokenByNameExists", errors.New("access token name has been used already"))
101+
return
102+
}
103+
92104
if err := models.NewAccessToken(t); err != nil {
93105
ctx.Error(http.StatusInternalServerError, "NewAccessToken", err)
94106
return

routers/user/setting/applications.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,18 @@ func ApplicationsPost(ctx *context.Context, form auth.NewAccessTokenForm) {
4343
UID: ctx.User.ID,
4444
Name: form.Name,
4545
}
46+
47+
exist, err := models.AccessTokenByNameExists(t)
48+
if err != nil {
49+
ctx.ServerError("AccessTokenByNameExists", err)
50+
return
51+
}
52+
if exist {
53+
ctx.Flash.Error(ctx.Tr("settings.generate_token_name_duplicate", t.Name))
54+
ctx.Redirect(setting.AppSubURL + "/user/settings/applications")
55+
return
56+
}
57+
4658
if err := models.NewAccessToken(t); err != nil {
4759
ctx.ServerError("NewAccessToken", err)
4860
return

0 commit comments

Comments
 (0)