Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix token endpoints ignore specified account #27080

Merged
merged 12 commits into from
Sep 18, 2023
12 changes: 11 additions & 1 deletion routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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())
Expand Down
12 changes: 9 additions & 3 deletions routers/api/v1/user/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
}

Expand Down Expand Up @@ -153,6 +157,8 @@ func DeleteAccessToken(ctx *context.APIContext) {
// responses:
// "204":
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
// "422":
Expand All @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions templates/swagger/v1_json.tmpl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions tests/integration/api_token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down