Browse Source

token: disallow multiple tokens with same name (#5820)

* api/v1: don't allow multiple tokens with same name

Fail with 422 Unprocessable Entity if the token name
already exist

ref: https://github.com/gogs/gogs/issues/5587

* Move new token error type to models/errors/token

* Remove "useless" ListAccessTokensByName function

* Add an i18n entry for token_name_exists
Frode Aannevik 5 years ago
parent
commit
ffbb0f6a60
5 changed files with 44 additions and 5 deletions
  1. 1 0
      conf/locale/locale_en-US.ini
  2. 16 0
      models/errors/token.go
  3. 15 3
      models/token.go
  4. 6 1
      routes/api/v1/user/app.go
  5. 6 1
      routes/user/setting.go

+ 1 - 0
conf/locale/locale_en-US.ini

@@ -374,6 +374,7 @@ delete_token = Delete
 access_token_deletion = Personal Access Token Deletion
 access_token_deletion_desc = Delete this personal access token will remove all related accesses of application. Do you want to continue?
 delete_token_success = Personal access token has been removed successfully! Don't forget to update your application as well.
+token_name_exists = Token with same name already exists.
 
 orgs.none = You are not a member of any organizations.
 orgs.leave_title = Leave organization

+ 16 - 0
models/errors/token.go

@@ -0,0 +1,16 @@
+package errors
+
+import "fmt"
+
+type AccessTokenNameAlreadyExist struct {
+	Name string
+}
+
+func IsAccessTokenNameAlreadyExist(err error) bool {
+	_, ok := err.(AccessTokenNameAlreadyExist)
+	return ok
+}
+
+func (err AccessTokenNameAlreadyExist) Error() string {
+	return fmt.Sprintf("access token already exist [name: %s]", err.Name)
+}

+ 15 - 3
models/token.go

@@ -5,12 +5,13 @@
 package models
 
 import (
+	"fmt"
 	"time"
 
 	"github.com/go-xorm/xorm"
-	gouuid "github.com/satori/go.uuid"
-
+	"github.com/gogs/gogs/models/errors"
 	"github.com/gogs/gogs/pkg/tool"
+	gouuid "github.com/satori/go.uuid"
 )
 
 // AccessToken represents a personal access token.
@@ -47,10 +48,21 @@ func (t *AccessToken) AfterSet(colName string, _ xorm.Cell) {
 	}
 }
 
+func isAccessTokenNameExist(uid int64, name string) (bool, error) {
+    return x.Where("uid=?", uid).And("name=?", name).Get(&AccessToken{})
+}
+
 // NewAccessToken creates new access token.
 func NewAccessToken(t *AccessToken) error {
 	t.Sha1 = tool.SHA1(gouuid.NewV4().String())
-	_, err := x.Insert(t)
+	has, err := isAccessTokenNameExist(t.UID, t.Name)
+	if err != nil {
+		return fmt.Errorf("IsAccessTokenNameExists: %v", err)
+	} else if has {
+		return errors.AccessTokenNameAlreadyExist{t.Name}
+	}
+
+	_, err = x.Insert(t)
 	return err
 }
 

+ 6 - 1
routes/api/v1/user/app.go

@@ -10,6 +10,7 @@ import (
 	api "github.com/gogs/go-gogs-client"
 
 	"github.com/gogs/gogs/models"
+	"github.com/gogs/gogs/models/errors"
 	"github.com/gogs/gogs/pkg/context"
 )
 
@@ -33,7 +34,11 @@ func CreateAccessToken(c *context.APIContext, form api.CreateAccessTokenOption)
 		Name: form.Name,
 	}
 	if err := models.NewAccessToken(t); err != nil {
-		c.ServerError("NewAccessToken", err)
+		if errors.IsAccessTokenNameAlreadyExist(err) {
+			c.Error(http.StatusUnprocessableEntity, "", err)
+		} else {
+			c.ServerError("NewAccessToken", err)
+		}
 		return
 	}
 	c.JSON(http.StatusCreated, &api.AccessToken{t.Name, t.Sha1})

+ 6 - 1
routes/user/setting.go

@@ -607,7 +607,12 @@ func SettingsApplicationsPost(c *context.Context, f form.NewAccessToken) {
 		Name: f.Name,
 	}
 	if err := models.NewAccessToken(t); err != nil {
-		c.ServerError("NewAccessToken", err)
+		if errors.IsAccessTokenNameAlreadyExist(err) {
+			c.Flash.Error(c.Tr("settings.token_name_exists"))
+			c.SubURLRedirect("/user/settings/applications")
+		} else {
+			c.ServerError("NewAccessToken", err)
+		}
 		return
 	}