pull/7787/merge
George S. Baugh 2025-03-20 05:07:43 +08:00 committed by GitHub
commit 624eec0707
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 46 additions and 1 deletions

View File

@ -189,7 +189,8 @@ func RegisterRoutes(m *macaron.Macaron) {
accessTokensHandler := user.NewAccessTokensHandler(user.NewAccessTokensStore())
m.Combo("").
Get(accessTokensHandler.List()).
Post(bind(api.CreateAccessTokenOption{}), accessTokensHandler.Create())
Post(bind(api.CreateAccessTokenOption{}), accessTokensHandler.Create()).
Delete(bind(api.DeleteAccessTokenOption{}), accessTokensHandler.Delete())
}, reqBasicAuth())
})
})

View File

@ -59,6 +59,37 @@ func (h *AccessTokensHandler) Create() macaron.Handler {
}
}
// Deletes the provided token identified by SHA1
// This prevents anyone from deleting everything in list(), as the identifiers in list() are not the actual SHA1.
func (h *AccessTokensHandler) Delete() macaron.Handler {
//TODO make the latter arg api.DeleteAccessTokenOption
return func(c *context.APIContext, form api.DeleteAccessTokenOption) {
// We need the ID of the token to delete it.
existing, err := h.store.GetBySHA1(c.Req.Context(), form.Sha1);
if err != nil {
if database.IsErrAccessTokenNotExist(err) {
c.ErrorStatus(http.StatusUnprocessableEntity, err)
} else {
c.Error(err, "list tokens in delete prep")
}
return
}
// We need the User ID and the Token ID to delete it.
err = h.store.DeleteAccessToken(c.Req.Context(), c.User.ID, existing.ID)
if err != nil {
// Always possible that we race, TOCTOU
if database.IsErrAccessTokenNotExist(err) {
c.ErrorStatus(http.StatusUnprocessableEntity, err)
} else {
c.Error(err, "delete access token")
}
return
}
c.JSON(http.StatusOK, &api.AccessToken{Name: existing.Name})
}
}
// AccessTokensStore is the data layer carrier for user access tokens API
// endpoints. This interface is meant to abstract away and limit the exposure of
// the underlying data layer to the handler through a thin-wrapper.
@ -69,6 +100,10 @@ type AccessTokensStore interface {
CreateAccessToken(ctx gocontext.Context, userID int64, name string) (*database.AccessToken, error)
// ListAccessTokens returns all access tokens belongs to given user.
ListAccessTokens(ctx gocontext.Context, userID int64) ([]*database.AccessToken, error)
// Get a token by SHA1 (used to find a tok to delete it)
GetBySHA1(ctx gocontext.Context, Sha1 string) (*database.AccessToken, error)
// Delete a given token
DeleteAccessToken(ctx gocontext.Context, userID int64, tokenID int64) error
}
type accessTokensStore struct{}
@ -86,3 +121,12 @@ func (*accessTokensStore) CreateAccessToken(ctx gocontext.Context, userID int64,
func (*accessTokensStore) ListAccessTokens(ctx gocontext.Context, userID int64) ([]*database.AccessToken, error) {
return database.Handle.AccessTokens().List(ctx, userID)
}
// Note: the possibility, though remote, of SHA1 collissions could be made far less likely via providing a user as well.
func (*accessTokensStore) GetBySHA1(ctx gocontext.Context, Sha1 string) (*database.AccessToken, error) {
return database.Handle.AccessTokens().GetBySHA1(ctx, Sha1)
}
func (*accessTokensStore) DeleteAccessToken(ctx gocontext.Context, userID int64, tokenID int64) error {
return database.Handle.AccessTokens().DeleteByID(ctx, userID, tokenID)
}