mirror of https://github.com/gogs/gogs.git
Add DELETE /users/$USER/tokens
Arguments: SHA1 of token. This allows useful workflows like applications which can build and then delete a token for its exclusive use during a session. Requires that a PR to go-gogs-client merges first. Currently you can test that it works by cloning my branch of go-gogs-client, or picking the following commit: https://github.com/teodesian/go-gogs-client/ commit/4488fbb10902b695e7df8c76fa5d6edf5394e0eb and running: go mod init && go mod tidy and finally going back in to this repo to do: go mod edit -replace=github.com/gogs/go-gogs-client=$CLONEDIR go mod tidy go build -o gogs I have tested this endpoint on a local install and verified it works. Beyond that, no automated tests have been added.pull/7787/head
parent
0965db473f
commit
a9db2abd98
|
@ -189,7 +189,8 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
accessTokensHandler := user.NewAccessTokensHandler(user.NewAccessTokensStore())
|
accessTokensHandler := user.NewAccessTokensHandler(user.NewAccessTokensStore())
|
||||||
m.Combo("").
|
m.Combo("").
|
||||||
Get(accessTokensHandler.List()).
|
Get(accessTokensHandler.List()).
|
||||||
Post(bind(api.CreateAccessTokenOption{}), accessTokensHandler.Create())
|
Post(bind(api.CreateAccessTokenOption{}), accessTokensHandler.Create()).
|
||||||
|
Delete(bind(api.DeleteAccessTokenOption{}), accessTokensHandler.Delete())
|
||||||
}, reqBasicAuth())
|
}, reqBasicAuth())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -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
|
// AccessTokensStore is the data layer carrier for user access tokens API
|
||||||
// endpoints. This interface is meant to abstract away and limit the exposure of
|
// endpoints. This interface is meant to abstract away and limit the exposure of
|
||||||
// the underlying data layer to the handler through a thin-wrapper.
|
// 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)
|
CreateAccessToken(ctx gocontext.Context, userID int64, name string) (*database.AccessToken, error)
|
||||||
// ListAccessTokens returns all access tokens belongs to given user.
|
// ListAccessTokens returns all access tokens belongs to given user.
|
||||||
ListAccessTokens(ctx gocontext.Context, userID int64) ([]*database.AccessToken, error)
|
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{}
|
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) {
|
func (*accessTokensStore) ListAccessTokens(ctx gocontext.Context, userID int64) ([]*database.AccessToken, error) {
|
||||||
return database.Handle.AccessTokens().List(ctx, userID)
|
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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue