mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	[API] Delete Token accept names too (#12366)
* Delete Token accept names too * better description Co-authored-by: zeripath <art27@cantab.net> Co-authored-by: Lauris BH <lauris@nix.lv>
This commit is contained in:
		@@ -37,6 +37,19 @@ func TestAPICreateAndDeleteToken(t *testing.T) {
 | 
			
		||||
	MakeRequest(t, req, http.StatusNoContent)
 | 
			
		||||
 | 
			
		||||
	models.AssertNotExistsBean(t, &models.AccessToken{ID: newAccessToken.ID})
 | 
			
		||||
 | 
			
		||||
	req = NewRequestWithJSON(t, "POST", "/api/v1/users/user1/tokens", map[string]string{
 | 
			
		||||
		"name": "test-key-2",
 | 
			
		||||
	})
 | 
			
		||||
	req = AddBasicAuthHeader(req, user.Name)
 | 
			
		||||
	resp = MakeRequest(t, req, http.StatusCreated)
 | 
			
		||||
	DecodeJSON(t, resp, &newAccessToken)
 | 
			
		||||
 | 
			
		||||
	req = NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%s", newAccessToken.Name)
 | 
			
		||||
	req = AddBasicAuthHeader(req, user.Name)
 | 
			
		||||
	MakeRequest(t, req, http.StatusNoContent)
 | 
			
		||||
 | 
			
		||||
	models.AssertNotExistsBean(t, &models.AccessToken{ID: newAccessToken.ID})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TestAPIDeleteMissingToken ensures that error is thrown when token not found
 | 
			
		||||
 
 | 
			
		||||
@@ -82,16 +82,27 @@ func AccessTokenByNameExists(token *AccessToken) (bool, error) {
 | 
			
		||||
	return x.Table("access_token").Where("name = ?", token.Name).And("uid = ?", token.UID).Exist()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListAccessTokensOptions contain filter options
 | 
			
		||||
type ListAccessTokensOptions struct {
 | 
			
		||||
	ListOptions
 | 
			
		||||
	Name   string
 | 
			
		||||
	UserID int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListAccessTokens returns a list of access tokens belongs to given user.
 | 
			
		||||
func ListAccessTokens(uid int64, listOptions ListOptions) ([]*AccessToken, error) {
 | 
			
		||||
	sess := x.
 | 
			
		||||
		Where("uid=?", uid).
 | 
			
		||||
		Desc("id")
 | 
			
		||||
func ListAccessTokens(opts ListAccessTokensOptions) ([]*AccessToken, error) {
 | 
			
		||||
	sess := x.Where("uid=?", opts.UserID)
 | 
			
		||||
 | 
			
		||||
	if listOptions.Page != 0 {
 | 
			
		||||
		sess = listOptions.setSessionPagination(sess)
 | 
			
		||||
	if len(opts.Name) != 0 {
 | 
			
		||||
		sess = sess.Where("name=?", opts.Name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		tokens := make([]*AccessToken, 0, listOptions.PageSize)
 | 
			
		||||
	sess = sess.Desc("id")
 | 
			
		||||
 | 
			
		||||
	if opts.Page != 0 {
 | 
			
		||||
		sess = opts.setSessionPagination(sess)
 | 
			
		||||
 | 
			
		||||
		tokens := make([]*AccessToken, 0, opts.PageSize)
 | 
			
		||||
		return tokens, sess.Find(&tokens)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -83,7 +83,7 @@ func TestGetAccessTokenBySHA(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestListAccessTokens(t *testing.T) {
 | 
			
		||||
	assert.NoError(t, PrepareTestDatabase())
 | 
			
		||||
	tokens, err := ListAccessTokens(1, ListOptions{})
 | 
			
		||||
	tokens, err := ListAccessTokens(ListAccessTokensOptions{UserID: 1})
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	if assert.Len(t, tokens, 2) {
 | 
			
		||||
		assert.Equal(t, int64(1), tokens[0].UID)
 | 
			
		||||
@@ -92,14 +92,14 @@ func TestListAccessTokens(t *testing.T) {
 | 
			
		||||
		assert.Contains(t, []string{tokens[0].Name, tokens[1].Name}, "Token B")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tokens, err = ListAccessTokens(2, ListOptions{})
 | 
			
		||||
	tokens, err = ListAccessTokens(ListAccessTokensOptions{UserID: 2})
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	if assert.Len(t, tokens, 1) {
 | 
			
		||||
		assert.Equal(t, int64(2), tokens[0].UID)
 | 
			
		||||
		assert.Equal(t, "Token A", tokens[0].Name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tokens, err = ListAccessTokens(100, ListOptions{})
 | 
			
		||||
	tokens, err = ListAccessTokens(ListAccessTokensOptions{UserID: 100})
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Empty(t, tokens)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,9 @@ package user
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
	"code.gitea.io/gitea/modules/context"
 | 
			
		||||
@@ -41,7 +43,7 @@ func ListAccessTokens(ctx *context.APIContext) {
 | 
			
		||||
	//   "200":
 | 
			
		||||
	//     "$ref": "#/responses/AccessTokenList"
 | 
			
		||||
 | 
			
		||||
	tokens, err := models.ListAccessTokens(ctx.User.ID, utils.GetListOptions(ctx))
 | 
			
		||||
	tokens, err := models.ListAccessTokens(models.ListAccessTokensOptions{UserID: ctx.User.ID, ListOptions: utils.GetListOptions(ctx)})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "ListAccessTokens", err)
 | 
			
		||||
		return
 | 
			
		||||
@@ -128,15 +130,44 @@ func DeleteAccessToken(ctx *context.APIContext) {
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// - name: token
 | 
			
		||||
	//   in: path
 | 
			
		||||
	//   description: token to be deleted
 | 
			
		||||
	//   type: integer
 | 
			
		||||
	//   format: int64
 | 
			
		||||
	//   description: token to be deleted, identified by ID and if not available by name
 | 
			
		||||
	//   type: string
 | 
			
		||||
	//   required: true
 | 
			
		||||
	// responses:
 | 
			
		||||
	//   "204":
 | 
			
		||||
	//     "$ref": "#/responses/empty"
 | 
			
		||||
	//   "422":
 | 
			
		||||
	//     "$ref": "#/responses/error"
 | 
			
		||||
 | 
			
		||||
	token := ctx.Params(":id")
 | 
			
		||||
	tokenID, _ := strconv.ParseInt(token, 0, 64)
 | 
			
		||||
 | 
			
		||||
	if tokenID == 0 {
 | 
			
		||||
		tokens, err := models.ListAccessTokens(models.ListAccessTokensOptions{
 | 
			
		||||
			Name:   token,
 | 
			
		||||
			UserID: ctx.User.ID,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ctx.Error(http.StatusInternalServerError, "ListAccessTokens", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch len(tokens) {
 | 
			
		||||
		case 0:
 | 
			
		||||
			ctx.NotFound()
 | 
			
		||||
			return
 | 
			
		||||
		case 1:
 | 
			
		||||
			tokenID = tokens[0].ID
 | 
			
		||||
		default:
 | 
			
		||||
			ctx.Error(http.StatusUnprocessableEntity, "DeleteAccessTokenByID", fmt.Errorf("multible matches for token name '%s'", token))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if tokenID == 0 {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "Invalid TokenID", nil)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tokenID := ctx.ParamsInt64(":id")
 | 
			
		||||
	if err := models.DeleteAccessTokenByID(tokenID, ctx.User.ID); err != nil {
 | 
			
		||||
		if models.IsErrAccessTokenNotExist(err) {
 | 
			
		||||
			ctx.NotFound()
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,7 @@ func DeleteApplication(ctx *context.Context) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func loadApplicationsData(ctx *context.Context) {
 | 
			
		||||
	tokens, err := models.ListAccessTokens(ctx.User.ID, models.ListOptions{})
 | 
			
		||||
	tokens, err := models.ListAccessTokens(models.ListAccessTokensOptions{UserID: ctx.User.ID})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.ServerError("ListAccessTokens", err)
 | 
			
		||||
		return
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,7 @@ func loadSecurityData(ctx *context.Context) {
 | 
			
		||||
		ctx.Data["RequireU2F"] = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tokens, err := models.ListAccessTokens(ctx.User.ID, models.ListOptions{})
 | 
			
		||||
	tokens, err := models.ListAccessTokens(models.ListAccessTokensOptions{UserID: ctx.User.ID})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.ServerError("ListAccessTokens", err)
 | 
			
		||||
		return
 | 
			
		||||
 
 | 
			
		||||
@@ -10635,9 +10635,8 @@
 | 
			
		||||
            "required": true
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "integer",
 | 
			
		||||
            "format": "int64",
 | 
			
		||||
            "description": "token to be deleted",
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "token to be deleted, identified by ID and if not available by name",
 | 
			
		||||
            "name": "token",
 | 
			
		||||
            "in": "path",
 | 
			
		||||
            "required": true
 | 
			
		||||
@@ -10646,6 +10645,9 @@
 | 
			
		||||
        "responses": {
 | 
			
		||||
          "204": {
 | 
			
		||||
            "$ref": "#/responses/empty"
 | 
			
		||||
          },
 | 
			
		||||
          "422": {
 | 
			
		||||
            "$ref": "#/responses/error"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user