Skip to content

Commit

Permalink
Backport #5250 on v1.6: Fix Issue 5249 and protect /api/v1/admin rout…
Browse files Browse the repository at this point in the history
…es with CSRF token (#5272)

* Add CSRF checking to reqToken and place CSRF in the post for deadline creation

Fixes #5226, #5249

* /api/v1/admin/users routes should have reqToken middleware
  • Loading branch information
zeripath authored and techknowlogick committed Nov 4, 2018
1 parent f95c966 commit c0bbbdd
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 10 deletions.
10 changes: 5 additions & 5 deletions integrations/api_admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ func TestAPIAdminCreateAndDeleteSSHKey(t *testing.T) {
OwnerID: keyOwner.ID,
})

req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d?token="+token,
keyOwner.Name, newPublicKey.ID)
req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d?token=%s",
keyOwner.Name, newPublicKey.ID, token)
session.MakeRequest(t, req, http.StatusNoContent)
models.AssertNotExistsBean(t, &models.PublicKey{ID: newPublicKey.ID})
}
Expand All @@ -51,7 +51,7 @@ func TestAPIAdminDeleteMissingSSHKey(t *testing.T) {
session := loginUser(t, "user1")

token := getTokenForLoggedInUser(t, session)
req := NewRequestf(t, "DELETE", "/api/v1/admin/users/user1/keys/%d?token="+token, models.NonexistentID)
req := NewRequestf(t, "DELETE", "/api/v1/admin/users/user1/keys/%d?token=%s", models.NonexistentID, token)
session.MakeRequest(t, req, http.StatusNotFound)
}

Expand All @@ -73,8 +73,8 @@ func TestAPIAdminDeleteUnauthorizedKey(t *testing.T) {

session = loginUser(t, normalUsername)
token = getTokenForLoggedInUser(t, session)
req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d?token="+token,
adminUsername, newPublicKey.ID)
req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d?token=%s",
adminUsername, newPublicKey.ID, token)
session.MakeRequest(t, req, http.StatusForbidden)
}

Expand Down
3 changes: 2 additions & 1 deletion integrations/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ func TestGit(t *testing.T) {

session := loginUser(t, "user1")
keyOwner := models.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User)
urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys", keyOwner.Name)
token := getTokenForLoggedInUser(t, session)
urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", keyOwner.Name, token)

dataPubKey, err := ioutil.ReadFile(keyFile + ".pub")
assert.NoError(t, err)
Expand Down
13 changes: 13 additions & 0 deletions modules/context/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"fmt"
"strings"

"github.com/go-macaron/csrf"

"code.gitea.io/git"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
Expand Down Expand Up @@ -97,6 +99,17 @@ func (ctx *APIContext) SetLinkHeader(total, pageSize int) {
}
}

// RequireCSRF requires a validated a CSRF token
func (ctx *APIContext) RequireCSRF() {
headerToken := ctx.Req.Header.Get(ctx.csrf.GetHeaderName())
formValueToken := ctx.Req.FormValue(ctx.csrf.GetFormName())
if len(headerToken) > 0 || len(formValueToken) > 0 {
csrf.Validate(ctx.Context.Context, ctx.csrf)
} else {
ctx.Context.Error(401)
}
}

// APIContexter returns apicontext as macaron middleware
func APIContexter() macaron.Handler {
return func(c *Context) {
Expand Down
4 changes: 4 additions & 0 deletions public/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2590,6 +2590,10 @@ function updateDeadline(deadlineString) {
data: JSON.stringify({
'due_date': realDeadline,
}),
headers: {
'X-Csrf-Token': csrf,
'X-Remote': true,
},
contentType: 'application/json',
type: 'POST',
success: function () {
Expand Down
12 changes: 8 additions & 4 deletions routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,15 @@ func repoAssignment() macaron.Handler {

// Contexter middleware already checks token for user sign in process.
func reqToken() macaron.Handler {
return func(ctx *context.Context) {
if true != ctx.Data["IsApiToken"] {
ctx.Error(401)
return func(ctx *context.APIContext) {
if true == ctx.Data["IsApiToken"] {
return
}
if ctx.IsSigned {
ctx.RequireCSRF()
return
}
ctx.Context.Error(401)
}
}

Expand Down Expand Up @@ -627,7 +631,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo)
})
})
}, reqAdmin())
}, reqToken(), reqAdmin())

m.Group("/topics", func() {
m.Get("/search", repo.TopicSearch)
Expand Down

0 comments on commit c0bbbdd

Please sign in to comment.