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 <6543@obermui.de>

Co-authored-by: Lanre Adelowo <yo@lanre.wtf>
release/v1.15
6543 2020-04-13 21:02:48 +02:00 committed by GitHub
parent 980ef24251
commit ad5c43ae5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 71 additions and 1 deletions

View File

@ -330,14 +330,18 @@ func loginUserWithPassword(t testing.TB, userName, password string) *TestSession
return session return session
} }
//token has to be unique this counter take care of
var tokenCounter int64
func getTokenForLoggedInUser(t testing.TB, session *TestSession) string { func getTokenForLoggedInUser(t testing.TB, session *TestSession) string {
t.Helper() t.Helper()
tokenCounter++
req := NewRequest(t, "GET", "/user/settings/applications") req := NewRequest(t, "GET", "/user/settings/applications")
resp := session.MakeRequest(t, req, http.StatusOK) resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body) doc := NewHTMLParser(t, resp.Body)
req = NewRequestWithValues(t, "POST", "/user/settings/applications", map[string]string{ req = NewRequestWithValues(t, "POST", "/user/settings/applications", map[string]string{
"_csrf": doc.GetCSRF(), "_csrf": doc.GetCSRF(),
"name": "api-testing-token", "name": fmt.Sprintf("api-testing-token-%d", tokenCounter),
}) })
resp = session.MakeRequest(t, req, http.StatusFound) resp = session.MakeRequest(t, req, http.StatusFound)
req = NewRequest(t, "GET", "/user/settings/applications") req = NewRequest(t, "GET", "/user/settings/applications")

View File

@ -77,6 +77,11 @@ func GetAccessTokenBySHA(token string) (*AccessToken, error) {
return nil, ErrAccessTokenNotExist{token} return nil, ErrAccessTokenNotExist{token}
} }
// AccessTokenByNameExists checks if a token name has been used already by a user.
func AccessTokenByNameExists(token *AccessToken) (bool, error) {
return x.Table("access_token").Where("name = ?", token.Name).And("uid = ?", token.UID).Exist()
}
// ListAccessTokens returns a list of access tokens belongs to given user. // ListAccessTokens returns a list of access tokens belongs to given user.
func ListAccessTokens(uid int64, listOptions ListOptions) ([]*AccessToken, error) { func ListAccessTokens(uid int64, listOptions ListOptions) ([]*AccessToken, error) {
sess := x. sess := x.

View File

@ -27,6 +27,42 @@ func TestNewAccessToken(t *testing.T) {
assert.Error(t, NewAccessToken(invalidToken)) assert.Error(t, NewAccessToken(invalidToken))
} }
func TestAccessTokenByNameExists(t *testing.T) {
name := "Token Gitea"
assert.NoError(t, PrepareTestDatabase())
token := &AccessToken{
UID: 3,
Name: name,
}
// Check to make sure it doesn't exists already
exist, err := AccessTokenByNameExists(token)
assert.NoError(t, err)
assert.False(t, exist)
// Save it to the database
assert.NoError(t, NewAccessToken(token))
AssertExistsAndLoadBean(t, token)
// This token must be found by name in the DB now
exist, err = AccessTokenByNameExists(token)
assert.NoError(t, err)
assert.True(t, exist)
user4Token := &AccessToken{
UID: 4,
Name: name,
}
// Name matches but different user ID, this shouldn't exists in the
// database
exist, err = AccessTokenByNameExists(user4Token)
assert.NoError(t, err)
assert.False(t, exist)
}
func TestGetAccessTokenBySHA(t *testing.T) { func TestGetAccessTokenBySHA(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
token, err := GetAccessTokenBySHA("d2c6c1ba3890b309189a8e618c72a162e4efbf36") token, err := GetAccessTokenBySHA("d2c6c1ba3890b309189a8e618c72a162e4efbf36")

View File

@ -517,6 +517,7 @@ new_token_desc = Applications using a token have full access to your account.
token_name = Token Name token_name = Token Name
generate_token = Generate Token generate_token = Generate Token
generate_token_success = Your new token has been generated. Copy it now as it will not be shown again. generate_token_success = Your new token has been generated. Copy it now as it will not be shown again.
generate_token_name_duplicate = <strong>%s</strong> has been used as an application name already. Please use a new one.
delete_token = Delete delete_token = Delete
access_token_deletion = Delete Access Token access_token_deletion = Delete Access Token
access_token_deletion_desc = Deleting a token will revoke access to your account for applications using it. Continue? access_token_deletion_desc = Deleting a token will revoke access to your account for applications using it. Continue?

View File

@ -6,6 +6,7 @@
package user package user
import ( import (
"errors"
"net/http" "net/http"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
@ -89,6 +90,17 @@ func CreateAccessToken(ctx *context.APIContext, form api.CreateAccessTokenOption
UID: ctx.User.ID, UID: ctx.User.ID,
Name: form.Name, Name: form.Name,
} }
exist, err := models.AccessTokenByNameExists(t)
if err != nil {
ctx.InternalServerError(err)
return
}
if exist {
ctx.Error(http.StatusBadRequest, "AccessTokenByNameExists", errors.New("access token name has been used already"))
return
}
if err := models.NewAccessToken(t); err != nil { if err := models.NewAccessToken(t); err != nil {
ctx.Error(http.StatusInternalServerError, "NewAccessToken", err) ctx.Error(http.StatusInternalServerError, "NewAccessToken", err)
return return

View File

@ -43,6 +43,18 @@ func ApplicationsPost(ctx *context.Context, form auth.NewAccessTokenForm) {
UID: ctx.User.ID, UID: ctx.User.ID,
Name: form.Name, Name: form.Name,
} }
exist, err := models.AccessTokenByNameExists(t)
if err != nil {
ctx.ServerError("AccessTokenByNameExists", err)
return
}
if exist {
ctx.Flash.Error(ctx.Tr("settings.generate_token_name_duplicate", t.Name))
ctx.Redirect(setting.AppSubURL + "/user/settings/applications")
return
}
if err := models.NewAccessToken(t); err != nil { if err := models.NewAccessToken(t); err != nil {
ctx.ServerError("NewAccessToken", err) ctx.ServerError("NewAccessToken", err)
return return