Fix token endpoints ignore specified account (#27080)

Fix #26234
close #26323
close #27040

---------

Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
CaiCandong 2023-09-18 08:21:15 +08:00 committed by GitHub
parent 8531ca0837
commit f93ee5937b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 52 additions and 4 deletions

View file

@ -367,6 +367,16 @@ func reqOwner() func(ctx *context.APIContext) {
} }
} }
// reqSelfOrAdmin doer should be the same as the contextUser or site admin
func reqSelfOrAdmin() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) {
if !ctx.IsUserSiteAdmin() && ctx.ContextUser != ctx.Doer {
ctx.Error(http.StatusForbidden, "reqSelfOrAdmin", "doer should be the site admin or be same as the contextUser")
return
}
}
}
// reqAdmin user should be an owner or a collaborator with admin write of a repository, or site admin // reqAdmin user should be an owner or a collaborator with admin write of a repository, or site admin
func reqAdmin() func(ctx *context.APIContext) { func reqAdmin() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) { return func(ctx *context.APIContext) {
@ -910,7 +920,7 @@ func Routes() *web.Route {
m.Combo("").Get(user.ListAccessTokens). m.Combo("").Get(user.ListAccessTokens).
Post(bind(api.CreateAccessTokenOption{}), reqToken(), user.CreateAccessToken) Post(bind(api.CreateAccessTokenOption{}), reqToken(), user.CreateAccessToken)
m.Combo("/{id}").Delete(reqToken(), user.DeleteAccessToken) m.Combo("/{id}").Delete(reqToken(), user.DeleteAccessToken)
}, reqBasicOrRevProxyAuth()) }, reqSelfOrAdmin(), reqBasicOrRevProxyAuth())
m.Get("/activities/feeds", user.ListUserActivityFeeds) m.Get("/activities/feeds", user.ListUserActivityFeeds)
}, context_service.UserAssignmentAPI()) }, context_service.UserAssignmentAPI())

View file

@ -43,8 +43,10 @@ func ListAccessTokens(ctx *context.APIContext) {
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/AccessTokenList" // "$ref": "#/responses/AccessTokenList"
// "403":
// "$ref": "#/responses/forbidden"
opts := auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID, ListOptions: utils.GetListOptions(ctx)} opts := auth_model.ListAccessTokensOptions{UserID: ctx.ContextUser.ID, ListOptions: utils.GetListOptions(ctx)}
count, err := auth_model.CountAccessTokens(ctx, opts) count, err := auth_model.CountAccessTokens(ctx, opts)
if err != nil { if err != nil {
@ -95,11 +97,13 @@ func CreateAccessToken(ctx *context.APIContext) {
// "$ref": "#/responses/AccessToken" // "$ref": "#/responses/AccessToken"
// "400": // "400":
// "$ref": "#/responses/error" // "$ref": "#/responses/error"
// "403":
// "$ref": "#/responses/forbidden"
form := web.GetForm(ctx).(*api.CreateAccessTokenOption) form := web.GetForm(ctx).(*api.CreateAccessTokenOption)
t := &auth_model.AccessToken{ t := &auth_model.AccessToken{
UID: ctx.Doer.ID, UID: ctx.ContextUser.ID,
Name: form.Name, Name: form.Name,
} }
@ -153,6 +157,8 @@ func DeleteAccessToken(ctx *context.APIContext) {
// responses: // responses:
// "204": // "204":
// "$ref": "#/responses/empty" // "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
// "404": // "404":
// "$ref": "#/responses/notFound" // "$ref": "#/responses/notFound"
// "422": // "422":
@ -164,7 +170,7 @@ func DeleteAccessToken(ctx *context.APIContext) {
if tokenID == 0 { if tokenID == 0 {
tokens, err := auth_model.ListAccessTokens(ctx, auth_model.ListAccessTokensOptions{ tokens, err := auth_model.ListAccessTokens(ctx, auth_model.ListAccessTokensOptions{
Name: token, Name: token,
UserID: ctx.Doer.ID, UserID: ctx.ContextUser.ID,
}) })
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "ListAccessTokens", err) ctx.Error(http.StatusInternalServerError, "ListAccessTokens", err)

View file

@ -16359,6 +16359,9 @@
"responses": { "responses": {
"200": { "200": {
"$ref": "#/responses/AccessTokenList" "$ref": "#/responses/AccessTokenList"
},
"403": {
"$ref": "#/responses/forbidden"
} }
} }
}, },
@ -16396,6 +16399,9 @@
}, },
"400": { "400": {
"$ref": "#/responses/error" "$ref": "#/responses/error"
},
"403": {
"$ref": "#/responses/forbidden"
} }
} }
} }
@ -16430,6 +16436,9 @@
"204": { "204": {
"$ref": "#/responses/empty" "$ref": "#/responses/empty"
}, },
"403": {
"$ref": "#/responses/forbidden"
},
"404": { "404": {
"$ref": "#/responses/notFound" "$ref": "#/responses/notFound"
}, },

View file

@ -40,6 +40,29 @@ func TestAPIDeleteMissingToken(t *testing.T) {
MakeRequest(t, req, http.StatusNotFound) MakeRequest(t, req, http.StatusNotFound)
} }
// TestAPIGetTokensPermission ensures that only the admin can get tokens from other users
func TestAPIGetTokensPermission(t *testing.T) {
defer tests.PrepareTestEnv(t)()
// admin can get tokens for other users
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
req := NewRequestf(t, "GET", "/api/v1/users/user2/tokens")
req = AddBasicAuthHeader(req, user.Name)
MakeRequest(t, req, http.StatusOK)
// non-admin can get tokens for himself
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
req = NewRequestf(t, "GET", "/api/v1/users/user2/tokens")
req = AddBasicAuthHeader(req, user.Name)
MakeRequest(t, req, http.StatusOK)
// non-admin can't get tokens for other users
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
req = NewRequestf(t, "GET", "/api/v1/users/user2/tokens")
req = AddBasicAuthHeader(req, user.Name)
MakeRequest(t, req, http.StatusForbidden)
}
type permission struct { type permission struct {
category auth_model.AccessTokenScopeCategory category auth_model.AccessTokenScopeCategory
level auth_model.AccessTokenScopeLevel level auth_model.AccessTokenScopeLevel