Fix #26234 close #26323 close #27040 --------- Co-authored-by: silverwind <me@silverwind.io>tags/v1.21.0-rc0
@@ -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 | |||
func reqAdmin() func(ctx *context.APIContext) { | |||
return func(ctx *context.APIContext) { | |||
@@ -910,7 +920,7 @@ func Routes() *web.Route { | |||
m.Combo("").Get(user.ListAccessTokens). | |||
Post(bind(api.CreateAccessTokenOption{}), reqToken(), user.CreateAccessToken) | |||
m.Combo("/{id}").Delete(reqToken(), user.DeleteAccessToken) | |||
}, reqBasicOrRevProxyAuth()) | |||
}, reqSelfOrAdmin(), reqBasicOrRevProxyAuth()) | |||
m.Get("/activities/feeds", user.ListUserActivityFeeds) | |||
}, context_service.UserAssignmentAPI()) |
@@ -43,8 +43,10 @@ func ListAccessTokens(ctx *context.APIContext) { | |||
// responses: | |||
// "200": | |||
// "$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) | |||
if err != nil { | |||
@@ -95,11 +97,13 @@ func CreateAccessToken(ctx *context.APIContext) { | |||
// "$ref": "#/responses/AccessToken" | |||
// "400": | |||
// "$ref": "#/responses/error" | |||
// "403": | |||
// "$ref": "#/responses/forbidden" | |||
form := web.GetForm(ctx).(*api.CreateAccessTokenOption) | |||
t := &auth_model.AccessToken{ | |||
UID: ctx.Doer.ID, | |||
UID: ctx.ContextUser.ID, | |||
Name: form.Name, | |||
} | |||
@@ -153,6 +157,8 @@ func DeleteAccessToken(ctx *context.APIContext) { | |||
// responses: | |||
// "204": | |||
// "$ref": "#/responses/empty" | |||
// "403": | |||
// "$ref": "#/responses/forbidden" | |||
// "404": | |||
// "$ref": "#/responses/notFound" | |||
// "422": | |||
@@ -164,7 +170,7 @@ func DeleteAccessToken(ctx *context.APIContext) { | |||
if tokenID == 0 { | |||
tokens, err := auth_model.ListAccessTokens(ctx, auth_model.ListAccessTokensOptions{ | |||
Name: token, | |||
UserID: ctx.Doer.ID, | |||
UserID: ctx.ContextUser.ID, | |||
}) | |||
if err != nil { | |||
ctx.Error(http.StatusInternalServerError, "ListAccessTokens", err) |
@@ -16359,6 +16359,9 @@ | |||
"responses": { | |||
"200": { | |||
"$ref": "#/responses/AccessTokenList" | |||
}, | |||
"403": { | |||
"$ref": "#/responses/forbidden" | |||
} | |||
} | |||
}, | |||
@@ -16396,6 +16399,9 @@ | |||
}, | |||
"400": { | |||
"$ref": "#/responses/error" | |||
}, | |||
"403": { | |||
"$ref": "#/responses/forbidden" | |||
} | |||
} | |||
} | |||
@@ -16430,6 +16436,9 @@ | |||
"204": { | |||
"$ref": "#/responses/empty" | |||
}, | |||
"403": { | |||
"$ref": "#/responses/forbidden" | |||
}, | |||
"404": { | |||
"$ref": "#/responses/notFound" | |||
}, |
@@ -40,6 +40,29 @@ func TestAPIDeleteMissingToken(t *testing.T) { | |||
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 { | |||
category auth_model.AccessTokenScopeCategory | |||
level auth_model.AccessTokenScopeLevel |