mirror of https://github.com/go-gitea/gitea.git
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>
This commit is contained in:
parent
980ef24251
commit
ad5c43ae5d
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue