* 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>tags/v1.21.12.1
| @@ -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") | ||||
| @@ -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. | ||||
| @@ -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") | ||||
| @@ -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? | ||||
| @@ -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 | ||||
| @@ -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 | ||||